Computer Organization and Design Fundamentals by David Tarnoff is now available!

Although the set of notes you have requested is presented below, it has not been maintained since January, 2003. All of the information in these notes has been included in an on-line text titled Computer Organization and Design Fundamentals. The book is available in three formats, two of which are free electronic downloads. Please visit one of the following links in order to access the format you prefer.

Thank you for your interest in this textbook. Please feel free to e-mail me at tarnoff etsu.edu if you have any questions or comments.

-Dave Tarnoff

Bitwise Operations


Topic: Some of the assembly language operations need to be elaborated.

Reading: I didn't see anything in the book that would help us out here.


At the computational level, you as programmers stick to things like multiplication, addition, etc. But many times, you may need to examine or manipulate the individual bits within a number.

Huh?

Let me try to explain this with an example. Can we figure out a quick way to determine if a number is odd or not? "Sure," you say, "divide the number by two, chop off the decimal, then multiply by two. If it doesn't equal the original number, then it was odd."

But I said quick.

Examine binary numbers for a moment. Notice, with the examples below, that odd binary numbers have a one in the least significant bit position while even numbers have a zero.

Decimal 35 = 0 0 1 0 0 0 1 1
Decimal 124 = 0 1 1 1 1 1 0 0
Decimal 93 = 0 1 0 1 1 1 0 1
Decimal 30 = 0 0 0 1 1 1 1 0

If you need to do this really fast, then it might benefit you to know how to isolate that bit. And there are ways to do that.

Okay, so you say that this is a ridiculous example. It might be. Let's take a look at IP addresses. IP addresses are made up of a group of 4 bytes, usually displayed in decimal in the form:

129.69.23.15

Each number separated by the periods can be a value from 0 to 255, i.e., one byte a piece. Part of this number, a group of consecutive bits starting with the most significant bit, represents an identification for the subnet of which this address belongs. For example, the subnet for ETSU's network is the first 24 bits. So how do we isolate these bits from the rest of the four-byte number?

Bitwise Operations

Sometimes, operations such as these are called "bit twiddling", although the more professional term is bitwise operations. These terms refer to the setting, clearing, or toggling of individual bits within a binary number. To do this, processors are capable of performing logical operations on the individual pairs of bits within two binary numbers. The bits are paired up by matching their bit position.

As an example, the bitwise AND on the binary values 01101011 and 11011010 is shown below.

Value 1 =
Value 2 =
Resulting bitwise AND =
0 1 1 0 1 0 1 1
1 1 0 1 1 0 1 0
0 1 0 0 1 0 1 0

The following is a discussion of the three types of "bit twiddling": clearing, setting, and toggling.

Clearing/Masking Bits

Clearing individual bits, also known as masking, uses the bitwise logical AND to clear individual bits while leaving the other bits untouched. To do this, AND the original value with a binary value of the same size with 1's in all the positions to be left untouched and 0's in all the positions to clear. The example below uses this operation to clear bit positions 3, 4, and 7 of the binary value 11010110. (Note that bit position 3 is already cleared, and therefore this operation should have no affect on that bit position.)

Original value =
Masking value =
New value w/bits 3, 4, & 7 cleared =
1 1 0 1 0 1 1 0
0 1 1 0 0 1 1 1
0 1 0 0 0 1 1 0

Note that the operation left bit positions 0, 1, 2, 5, and 6 untouched.

There are probably two reactions from this: what is he talking about or so what. To explain further, let's go back to our original examples. First, let's mask the least significant bit of a binary value to see if it is odd or even. This means we would clear all bits except for the last bit. If the result is a zero, the number was even. If the result is one, the number was odd. The zero or one result can be checked simply by looking at the processor's zero flag.

Decimal 35 =
Masking LSB =
Non-zero result means 35 is odd =
0 0 1 0 0 0 1 1
0 0 0 0 0 0 0 1
0 0 0 0 0 0 0 1
Decimal 124 =
Masking LSB =
Zero result means 124 is even =
0 1 1 1 1 1 0 0
0 0 0 0 0 0 0 1
0 0 0 0 0 0 0 0

This is a great deal faster than that dividing by two, truncating, multiplying by two, then comparing. Let's take a look at the subnet masking example for IP addresses.

IP address 129.69.23.15 =
Subnet mask 255.255.255.0 =
Subnet address =
10000001.01000101.00010111.00001111
11111111.11111111.11111111.00000000
10000001.01000101.00010111.00000000

Therefore, the subnet is equal to 129.69.23.0.

The last 8 bits of the number is the host identifier, the number that identifies the computer within the subnet. Finding the host id is accomplished the same way.

IP address 129.69.23.15 =
Host ID mask 0.0.0.255 =
Host identifier =
10000001.01000101.00010111.00001111
00000000.00000000.00000000.11111111
00000000.00000000.00000000.00001111

Therefore, the host ID is equal to 0.0.0.15

Setting Bits

Setting individual bits uses the bitwise logical OR. To do this, OR the original value with a binary value of the same size with 1's in all the positions to be set and 0's in all the positions to be left untouched. As an example, the operation below sets bit positions 1, 3, and 5 of the binary value 10010110. (Note that bit position 1 is already set, hence this operation should have no affect on that bit.)

Original value =
OR'ed value =
New value w/bits 1, 3, & 5 set =
1 0 0 1 0 1 1 0
0 0 1 0 1 0 1 0
1 0 1 1 1 1 1 0

Note that the operation left bit positions 0, 2, 4, 6, and 7 untouched.

Toggling Bits

We can also toggle or switch the value of individual bits from 1 to 0 or vice versa. This is done with the XOR function. Remember from the truth table, that an XOR gate is like a programmable inverter. If A is set to zero, B is passed through to the output untouched. If A is set to one, B is inverted at the output.

A B XOR
0 0 0
0 1 1
1 0 1
1 1 0

Therefore, if we perform a bitwise XOR, then the bit positions in the mask with zeros will pass the original value through and bit positions with ones will invert the original value. The example below toggles bit positions 1, 2, 3, and 5 of the binary value 10010110 while leaving the others untouched.

Original value =
XOR'ed value =
New value w/bits 1, 2, 3, & 5 flipped =
1 0 0 1 0 1 1 0
0 0 1 0 1 1 1 0
1 0 1 1 1 0 0 0

Shifting Registers

There is another concept regarding registers that we haven't talked about yet. It is a process involving taking each of the bits of a number and shifting them either one position toward the most significant bit (a left shift), or one position toward the least significant bit (a right shift).

During a shift, there is always a bit that will be left undefined. In the case of a left shift, the LSB is undefined, and in the case of a right shift, it's the MSB. In the figure above, this undefined bit has been set to a zero. Sometimes, however, the processor will shift in the carry bit or use the bit that popped off the other end. There are usually different assembly language commands for each type of shift.

Multiplying and Dividing With Shifts

Once again, speed is of great importance with many programs. Typically, multiplication or divide commands take the longest periods to execute. Therefore, we will avoid using them if we can.

There is a simple trick we can use if we want to multiply or divide by a power of two. For example, multiply 34 by 2 and we get 68.

Binary value of 34 =
Binary value of 68 =
0 0 1 0 0 0 1 0
0 1 0 0 0 1 0 0

Notice any similarities between the binary values of 34 and 68? Sixty-eight is simply 34 shifted left once and filling in with a zero. What happens if we multiply 34 by 4? Notice that 4 is 22.

Binary value of 34 =
Binary value of 68 =
Binary value of 136 =
0 0 1 0 0 0 1 0
0 1 0 0 0 1 0 0
1 0 0 0 1 0 0 0

So, to multiply by 2, we simply do a left shift. To multiply by 2n, shift left n times.

It works the opposite way for integer division. To divide by 2n, shift right n times.

8086 Assembly Language

We will be programming in assembly language in the lab for the next two laboratory experiments, so it would be helpful for you to be introduced to the 8086 assembly language.

Like most programming languages, assembly language source code must follow a well-defined syntax and structure. The following is a description of the fields of an assembly language file.

Label Field -- A label is used to associate the memory address of a specific line of code or a memory location with a text string. (Remember, those humans like to see words, not numbers.) There are some rules it must follow:

Instruction or Opcode Field -- The instruction field contains the assembly language commands that the processor is supposed to follow to run a program. The command field must follow these rules:

Operand field -- The operand field contains the data that the assembly language instructions use to run. This includes addresses, constants, register names, etc. There is a link to a site later in this page that describes some of the guidelines operands must follow.

Comment Field -- Last of all, we have the comment field. The ways to represent comments in assembly language differ from assembler to assembler. For most assemblers, comments begin after opcode/operand with a semi-colon. The assembler ignores everything followed by the comment character until a carriage return/line feed (CR/LF).

As we discussed in class on Monday, assembly language can be very difficult to read. It is for this reason that comments are critical to the source code documentation of an assembly file.