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 ""))
(while (not (= i 0))
(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
(skip-chars-backward "0123456789abcdefABCDEF#x")
(setq $p1 (point))
(skip-chars-forward "0123456789abcdefABCDEF#x" )
(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 …
)
(message "input 「%s」, Hexadecimal 「%s」 is 「%d」" $inputStr $tempStr (string-to-number $tempStr 16))))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."
(interactive "sEnter bit string (e.g. \"010110101\"): ")
(insert (format "0x%X" (string-to-number bitstring 2))))
(defun mnd/convert-integer (i)
"Show integer representations for input in minibuffer."
(interactive "nEnter number (ex. 5, #xFF, #b01011: ")
(let ((dec (number-to-string i))
(hex (format "%X" i))
(oct (format "%o" i))
(bin (int-to-binary-string i)))
(message "dec 「%s」, hex 「%s」, oct 「%s」, bin 「%s」" dec hex oct bin)))
(defun mnd/insert-integer-in-representation (i)
"Ask for specific integer representation of input and insert string."
(interactive "nEnter number (ex. 5, #xFF, #b01011): ")
(insert
(pcase (completing-read "Which representation? " '("dec" "hex" "oct" "bin"))
("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))
(maxval2s (- (/ bitdepth 2) 1)))
(message "nbits = %i\ni = %i\nbitdetph = %i\nmaxval2s = %i" nbits i bitdepth maxval2s)
(if (> i maxval2s) (- i bitdepth) i)))
(defun binary-string-not (s)
(s-replace "x" "1" (s-replace "1" "0" (s-replace "0" "x" s))))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-wrapper
(calc-leading-zeros t)
(calc-word-size nbits)
(calc-radix 2 twos-complement-mode)
(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!