ECE-1021 

Bit Banging

(Last Mod: 27 November 2010 21:38:39 )

ECE-1021 Home



Objectives


Prerequisites


Overview

As noted in the module on bits and bytes, bit banging refers to the process of working at the level of individual bits, particularly when they are embedded within a larger data object, such as a byte or multi-byte value, even when the processor may not allow direct access to individual bits. Under these conditions, we must develop techniques that provide a means of reading, setting, and clearing individual bits without affecting, or being affected by, other bits within the larger value.

The techniques developed in this module assume you understand and are comfortable with the properties of the logic operators introduced in the Boolean Logic module. In particular, you need to understand the useful properties of these operators when acting on a data bit and a control bit.


Bitwise Operators

For the purposes of this module, we will assume that we are working with a collection of bits that make up one eight-bit byte. We want to treat each bit as an independent logical variable, even though we will assume that we can neither read nor write to these bits separately but must instead do so to the collection as a whole. We will further assume that our processor supports some form of "bitwise" operators. Bitwise operators are simply operators that determine the state of each bit in the result based only on the corresponding bits in the operands.

The C language supports the four logical operations discussed in the module on Boolean Logic, namely the NOT, AND, OR, and XOR, on a bitwise basis. It also provides two bit shifting operators and has abbreviated assignment versions of all of these operators except the NOT operator.

Since the topics in this module transcend any particular language, we will not use the syntax of the C language and will, instead, use the generic operators as presented in the Boolean Logic module. Translating these commands to the corresponding C syntax is trivially straight-forward as will be illustrated in several examples.

Because we are working with the bits in our multi-bit value in two ways (bitwise logical values and multi-bit integer values), we have to be sure that those two ways are compatible. Here we are assuming that the value stored in a bit is zero if it is FALSE and one if it is TRUE. If this weren't the case, we could still accomplish our goal, it would just be more complicated.


Reading Bits

If we want to know the state of a particular bit within a multi-bit value, we can use a bitwise-AND to "mask off" all of the bits we are not interested in and the resulting multi-bit value will be a zero (meaning that all bits are cleared) if and only if the bits we didn't mask off are cleared. Conversely, if any of the bits we didn't mask off are set, the resulting multi-bit value will be something other than zero.

Example 1: Determine the state of data bit 6 (D6) within the 8-bit value A.

MASK = 0100 0000 b    (Mask off everything except D6)

RESULT = (A) AND (MASK)

If RESULT is zero, then we know that D6 was cleared. If it is anything other than zero (which, in this case, would be a value of 64 assuming a pure binary interpretation), then D6 was set.

If D6 is CLR D7 D6 D5 D4 D3 D2 D1 D0
PRESENT VALUE F F F T T F T T
MASK F T F F F F F F
RESULT = 0 0 0 0 0 0 0 0 0

 

If D6 is SET D7 D6 D5 D4 D3 D2 D1 D0
PRESENT VALUE F T F T T F T T
MASK F T F F F F F F
RESULT = 64 0 1 0 0 0 0 0 0

C Code Fragment:

char A, MASK, RESULT;

 

MASK = 0x40; /* 0100 0000b */;

RESULT = A & MASK;


Clearing Bits

If we want to clear a particular bit or bits within a multi-bit value, we can use the bitwise-AND to force the target bit(s) to become zero while leaving our bit mask transparent to all of the other bits.

Example 2: Clear both bits D7 and D1 within the 8-bit value A.

MASK = 1000 0010 b    (Mask with D7 and D1 set)

A = (A) AND (NOT (MASK))

CLR D6 and D1 D7 D6 D5 D4 D3 D2 D1 D0
PRESENT VALUE F F F T T F T T
MASK F T F F F F T F
NOT (MASK) T F T T T T F T
NEW VALUE F F F T T F F T

C Code Fragment:

char A, MASK;

 

MASK = 0x82;  /* 1000 0010b               */

A &= ~MASK;   /* Same as A = A & (~MASK); */


Setting Bits

If we want to set a particular bit or bits within a multi-bit value, we can use the bitwise-OR to force the target bit(s) to become one while leaving our bit mask transparent to all of the other bits.

Example 3: Set both bits D2 and D0 within the 8-bit value A.

MASK = 0000 0101 b    (Mask with D2 and D0 set)

A = (A) OR (MASK)

SET D2 and D0 D7 D6 D5 D4 D3 D2 D1 D0
PRESENT VALUE F F F T T F T T
MASK F F F F F T F T
NEW VALUE F F F T T T T T

C Code Fragment:

char A, MASK;

 

MASK = 0x05;  /* 0000 0101b            */

A |= MASK;    /* Same as A = A | MASK; */


Toggling Bits

If we want to set a toggle a particular bit or bits within a multi-bit value, we can use the bitwise-XOR to force the target bit(s) to become inverted while leaving our bit mask transparent to all of the other bits.

Example 4: Toggle both bits D4 and D4 within the 8-bit value A.

MASK = 0011 0000 b    (Mask with D5 and D4 set)

A = (A) XOR (MASK)

TOGGLE D5 and D4 D7 D6 D5 D4 D3 D2 D1 D0
PRESENT VALUE F F F T T F T T
MASK F F T T F F F F
NEW VALUE F F T F T F T T

C Code Fragment:

char A, MASK;

 

MASK = 0x30;  /* 0011 0000b            */

A ^= MASK;    /* Same as A = A ^ MASK; */


Generating Bit Masks in C

In all of the above examples, the bit masks were implemented as literal constants. While this is adequate for many situations, we also need to be able to generate bit masks on the fly. Conceptually, we can do this by starting with a bit mask that only has one end bit set and then shift that bit the proper number of time in order to position it were we want to.

In C, we have the bit shift operators << and >> which shift the bits in an integer the specified number of places to the left and right respectively. For our purposes here, we must be careful to only shift bits to the left unless we are using an unsigned integer data type. In the case of signed integers, the value that is shifted into the newly vacated leftmost bit when we perform a right-shift is implementation defined due to what is known as "sign extension" issues. With the left-shift operator we are guaranteed that the value shifted into the newly vacated rightmost bit will be a zero.

Hence, to generate a mask for data bit n (where n starts a zero) we can use the following statement:

MASK = 1 << n;

If we want to generate a mask with multiple bits set, we simply create the first one and then perform a running bitwise-OR with the successive individual bit masks.

Example 5: Clear bits j, k, and m within in the 8-bit value A.

char A, MASK;

int j, k, m;

 

MASK  = 1 << j;  /* Set bit j in the MASK      */

MASK |= 1 << k;  /* Also set bit k in the MASK */

MASK |= 1 << m;  /* Also set bit m in the MASK */

A &= ~MASK;      /* Clear bits j and k         */


Bit Banging without Bitwise Operators

Although we assumed that we had access to the necessary bitwise operators, we do not have to have them in order to perform bit banging. We could still accomplish our goals using the arithmetic operators, but the process is much more involved and will not dealt with in this course.