(Last Mod: 27 November 2010 21:38:39 )
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.
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.
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.
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;
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.
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); */
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.
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; */
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.
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; */
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.
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 */
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.