This group performs logical operations (see my previous post on logical operations). They are similar in nature to the arithmetic group in that most operations operate on A (the accumulator), and most operations effect the flags. All operate on 8-bits, there are no instructions in this group that effect register pairs.
[http://en.wikipedia.org/wiki/George_Boole]
Boolean refers to mathematician George Boole who worked with logic in the 1800s. He
invented a series of mathematical rules governing reduction of complicated logical
expressions which are used extensively in digital logic design.
AND, OR, not (CMP), and exclusive or (XOR) are known as Boolean operations. OR and AND were explained earlier. A not instruction (the 8080 calls it CMA or complement accumulator) simply flips the bits, all 1s become 0s, and all 0s become 1s.
I think of XOR as a "difference detector". Its truth table looks like this:
x | y | result |
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 0 |
There are register, memory, and immediate forms of AND, OR, and XOR. (CMP only has a register instruction). Here is an implementation of a couple of opcodes:
case 0x2F: //CMA (not) state->a = ~state->a //Data book says CMA doesn't effect the flags break; case 0xe6: //ANI byte { uint8_t x = state->a & opcode[1]; state->cc.z = (x == 0); state->cc.s = (0x80 == (x & 0x80)); state->cc.p = parity(x, 8); state->cc.cy = 0; //Data book says ANI clears CY state->a = x; state->pc++; //for the data byte } break;
These instructions rearrange the bits in the registers. Shift right moves them right by one bit, and shift left moves them left one bit. Like this:
Shift Right(0b00010000) = 0b00001000
Shift Left (0b00000001) = 0b00000010
They might appear to have no use, but they have some tricks up their sleeves. They can be used to multiply and divide by powers of two. Take the shift left example above. 0b00000001
is decimal 1, and shifting it left makes it 0b00000010
which is decimal 2. If you shift it left again, you get 0b00000100
which is 4. Shift left again and you have multiplied by 8. This works for any number you try: 5 (0b00000101
) shifted left once equals 10 (0b00001010
). Shift left again gives 20 (0b00010100
). Shift right does the same thing for divide.
The 8080 doesn't have a multiply instruction, but you can implement it using these instructions. You get bonus points if you can figure out how. I was asked this question in a job interview once. (I got it right, although it took me a few minutes.)
These instructions rotate the accumulator and only effect the carry flag. Here are a couple:
case 0x0f: //RRC { uint8_t x = state->a; state->a = ((x & 1) << 7) | (x >> 1); state->cc.cy = (1 == (x&1)); } break; case 0x1f: //RAR { uint8_t x = state->a; state->a = (state->cc.cy << 7) | (x >> 1); state->cc.cy = (1 == (x&1)); } break;
The purpose of CMP and CPI is simply to set the flags (for a branch). They accomplish this with a subtraction that sets the flags but doesn't store the result.
Equal: If the 2 numbers are equal, the Z flag gets set, since subtracting them results in zero.
Greater than: If A is greater than the comparison, then the CY flag will be cleared (since the subtraction could be completed without a borrow).
Less than: If A is less than the comparison, CY will be set (since A had to borrow to complete the subtraction).
There are register, memory, and immediate versions of it. The implementation is simply subtract without the result stored:
case 0xfe: //CPI byte { uint8_t x = state->a - opcode[1]; state->cc.z = (x == 0); state->cc.s = (0x80 == (x & 0x80)); //It isn't clear in the data book what to do with p - had to pick state->cc.p = parity(x, 8); state->cc.cy = (state->a < opcode[1]); state->pc++; } break;
These finish out the logical group and are used to set and clear the carry flag.
← Prev: branch-group Next: io-and-special-group →Post questions or comments on Twitter @realemulator101, or if you find issues in the code, file them on the github repository.