When working with embedded systems I found more and more use for all types of bit manipulations, e.g. fixed point arithmetic, bit shifting, two’s complement, etc.. For example, I often need to convert register addresses or convert sensor data from and to hexadecimal representation.
In need of a good calculator tool to aid these use cases, of course, I came across Emacs Calc (again!), and finally started to really integrate Calc in my workflow.
Here I start a little collection of Emacs functionalities that were very helpful in learning and daily work, programming in bash, C/C++, and Emacs Lisp.
Integer to binary representation
defun int-to-binary-string (i)
("convert an integer into it's binary representation in string format"
;; https://stackoverflow.com/questions/20568684/converting-number-to-base-2-binary-string-representation
let ((res ""))
(not (= i 0))
(while (setq res (concat (if (= 1 (logand i 1)) "1" "0") res))
(setq i (lsh i -1)))
(if (string= res "")
(setq res "0"))
( res))
Integer to hex representation
Here is a function from Xah Lee http://xahlee.info/emacs/emacs/elisp_converting_hex_decimal.html showing the hex number at point in decimal.
defun xah-show-hexadecimal-value ()
("Prints the decimal value of a hexadecimal string under cursor.
Samples of valid input:
ffff → 65535
0xffff → 65535
#xffff → 65535
FFFF → 65535
0xFFFF → 65535
#xFFFF → 65535
more test cases
64*0xc8+#x12c 190*0x1f4+#x258
100 200 300 400 500 600
URL `http://xahlee.info/emacs/emacs/elisp_converting_hex_decimal.html'
Version 2020-02-17"
(interactive )let ($inputStr $tempStr $p1 $p2 )
(if (region-active-p)
(progn
(setq $p1 (region-beginning))
(setq $p2 (region-end)))
(progn
(
(save-excursion"0123456789abcdefABCDEF#x")
(skip-chars-backward setq $p1 (point))
("0123456789abcdefABCDEF#x" )
(skip-chars-forward setq $p2 (point)))))
(setq $inputStr (buffer-substring-no-properties $p1 $p2))
(let ((case-fold-search nil))
(setq $tempStr (replace-regexp-in-string "\\`0x" "" $inputStr )) ; C, Perl, …
(setq $tempStr (replace-regexp-in-string "\\`#x" "" $tempStr )) ; elisp …
(setq $tempStr (replace-regexp-in-string "\\`#" "" $tempStr )) ; CSS …
(
)"input 「%s」, Hexadecimal 「%s」 is 「%d」" $inputStr $tempStr (string-to-number $tempStr 16)))) (message
First, I started with using simple functions inserting hex numbers after asking for user input.
defun mnd/bits-to-hex (bitstring)
("Insert a hex number given a bitstring of 0s and 1s."
"sEnter bit string (e.g. \"010110101\"): ")
(interactive format "0x%X" (string-to-number bitstring 2))))
(insert (
defun mnd/convert-integer (i)
("Show integer representations for input in minibuffer."
"nEnter number (ex. 5, #xFF, #b01011: ")
(interactive let ((dec (number-to-string i))
(format "%X" i))
(hex (format "%o" i))
(oct (
(bin (int-to-binary-string i)))"dec 「%s」, hex 「%s」, oct 「%s」, bin 「%s」" dec hex oct bin)))
(message
defun mnd/insert-integer-in-representation (i)
("Ask for specific integer representation of input and insert string."
"nEnter number (ex. 5, #xFF, #b01011): ")
(interactive
(insert"Which representation? " '("dec" "hex" "oct" "bin"))
(pcase (completing-read "dec" (number-to-string i))
("hex" (format "%X" i))
("oct" (format "%o" i))
("bin" (int-to-binary-string i))
(
(_ (number-to-string i)) )))
Twos complement
To convert the byte pattern for an integer to its two’s complement interpretation the following function can be used (see Two’s complement - Wikipedia).
defun twos (nbits i)
("Compute the twos complement of integer i for a nbits word size."
let* ((bitdepth (expt 2 nbits))
(- (/ bitdepth 2) 1)))
(maxval2s ("nbits = %i\ni = %i\nbitdetph = %i\nmaxval2s = %i" nbits i bitdepth maxval2s)
(message if (> i maxval2s) (- i bitdepth) i)))
(
defun binary-string-not (s)
("x" "1" (s-replace "1" "0" (s-replace "0" "x" s)))) (s-replace
Enter Calc
The integrated calculator in Emacs is great for the above (and many many other) use cases (see this blog post from Dr. Florian Adamsky).
full-calc
I like to enter calc with: “M-x full-calc” In the calculator window one can simply enter numbers (by default in decimal representation) and use the following commands to change the radix number:
- /“d 2”/ to binary, with universal argument (C-u) to twos complement
- /“d 6”/ to hexadecimal
- /“d 8”/ to octal
- /“d <n>”/ any other
- /“d 0”/ switch back to decimal
(In doom-emacs using evil-mode it is capital letter “D”, “d” deletes from the RPN stack.)
To enter integer of different number representations one can prepend the input with “<radix-number>#…”, for example:
- 2#010101
- 16#FF
- 8#16
quick-calc
With /“M-x quick-calc”/ one can solve simple one-liner calculation in the minibuffer. To directly insert the result in the buffer, one can pass the universal argument (C-u).
For example: “C-u M-x quick-calc RET 16#FF RET” inserts the hex number FF in the current radix representation into the buffer, which solve a similar use case as the above function mnd/insert-integer-in-representation
.
calc in emacs lisp code
In the calc manual I have read how calc can be used in Emacs Lisp code. Here is a simple example function for similar radix conversions.
defun mnd/integer-to-binary-calc-string (i &optional nbits twos-complement-mode)
("Convert integer to calc's binary representation."
let ($res)
(when (null nbits)
(setq nbits 8))
(
(calc-wrappert)
(calc-leading-zeros
(calc-word-size nbits)2 twos-complement-mode)
(calc-radix setq $res (calc-eval i))
(
)
$res ))
TODO other useful calc functions
As I use and learn more commands that aid my calc workflow— calc-grab
or embedded-calc
functionalities come to mind—I will describe them here at a later time.
In org-mode spreadsheets
Exemplary use of calc in an org table using some of the above functions:
|-----+--------+----------+-----------+------------------|
| int | bits | twos int | twos bits | signed twos bits |
|-----+--------+----------+-----------+------------------|
| 0 | 2#000 | 0 | 2##000 | 2#000 |
| 1 | 2#001 | 1 | 2##001 | 2#001 |
| 2 | 2#010 | 2 | 2##010 | 2#010 |
| 3 | 2#011 | 3 | 2##011 | 2#011 |
| 4 | 2#100 | -4 | 2##100 | -2#100 |
| 5 | 2#101 | -3 | 2##101 | -2#011 |
| 6 | 2#110 | -2 | 2##110 | -2#010 |
| 7 | 2#111 | -1 | 2##111 | -2#001 |
|-----+--------+----------+-----------+------------------|
| 8 | 2#1000 | 0 | 2##000 | 2#000 |
| / | | <r> | <r> | <r> |
|-----+--------+----------+-----------+------------------|
#+TBLFM: $1=@#-2::$2='(mnd/integer-to-binary-calc-string $1 3)::$3='(twos 3 $1);N::$4='(mnd/integer-to-binary-calc-string $3 3 t)::$5='(mnd/integer-to-binary-calc-string $3 3)::
Conclusion
I had a specific itch: number representation conversion for embedded software development. With its already integrated calc tool, once again, I could use Emacs to scratch this itch. In addition, it was possible to customize the functionality such that it exactly fits my preferred workflow!