Bit fiddeling with Emacs

Posted on November 27, 2022
Tags: howto, emacs

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!