Some useful bit functions


#1

uLisp is designed for use with microcontrollers in applications that often involve manipulating individual bits, such as the bits in an I/O port, or the data being transferred using a serial protocol.

There are already several bitwise operations included in uLisp, but this article describes how to define some other functions that might be useful in typical microcontroller applications.

Counting the 1-bits: popcount

A useful bit function is popcount, which returns the number of 1-bits in the binary representation of its argument.

Here’s a simple recursive definition for 32-bit platforms:

(defun popcount (n)
  (cond
   ((zerop n) 0)
   (t (+ (logand n 1) (popcount (logand (ash n -1) #x7FFFFFFF))))))

For example:

> (popcount #b01010011)
4

This is similar to the function logcount provided in Common Lisp; the difference is that logcount returns the number of 0-bits if the argument is negative. It would be simple to extend popcount to provide this.

One possible application is counting the number of lit segments on a seven-segment display, to compensate for dimming due to the higher current through a current-limiting resistor.

Finding the length of a binary number: bitlength

Another useful function is bitlength, which returns the number of bits needed to express the binary representation of its argument, excluding leading 0-bits.

Here’s a recursive definition for 32-bit platforms:

(defun bitlength (n)
  (if (zerop n) 0 (1+ (bitlength (logand (ash n -1) #x7FFFFFFF)))))

For example:

> (bitlength #b01010011)
7

This is similar to the Common Lisp function integer-length, except that for negative arguments integer-length returns the number of bits needed to express its argument excluding leading 1-bits. It would be simple to extend bitlength to provide this.

Reversing the order of the bits: reversebits

The final function, reversebits, does what it says: reverses the order of the bits in its argument.

Here’s a function that achieves this:

(defun reversebits (n &optional (width 32))
  (let ((r 0))
    (dotimes (x width r)
      (setq r (logior (ash r 1) (logand n 1)))
      (setq n (ash n -1)))))

The second parameter specifies the width of the bit field. For example:

> (format t "~b" (reversebits #b01010011 8))
11001010 

One possible application is interfacing between protocols that work with an MSB-first (most significant bit first) order, and ones that use an LSB-first (least significant bit first) order.