I have gotten round to implementing the ADD A,r set of opcodes on my Z80 core. I had a bit of confusion about the carry and overflow flags which I think I've nailed, but I wanted to put it to the community to check that I'm right.
Basically, from what I can see, the ALU in the Z80 doesn't care about signed/unsigned operations, it just adds bits. This means that if two 8-bit values are added together and cause a 9-bit value as a result of their addition, the carry flag will be set. This includes adding two negative two's complement numbers, for example -20 (11101100) and -40 (11011000), as although the result is -60 (11000100), the result is actually a 9-bit value 1 1100 0100. This surely means if adding two negative two's complement values, the carry flag will always be set, even when there is no overflow condition - am I right?
Secondly, I decided that to detect an overflow in this instruction, I would XOR bit 7 of both operands, and if the result was 10000000, then there is definitely no overflow - if the result of this is 00000000 then there could be an overflow as the signs are the same, and I would therefore XOR bit 7 of the result of the addition with bit 7 of either operand, and if the result of this is 10000000 then an overflow has occurred and I set the P/V overflow flag. Am I right here also?
Sorry for such a convoluted question, I'm pretty sure I'm right but I need to know before I carry on with countless more instructions based on this logic. Many thanks.
The bits of the result are obtained from the truncated sum of unsigned integers. The add instruction doesn't care about the sign here nor does it care about your own interpretation of the integers as signed or unsigned. It just adds as if the numbers were unsigned.
The carry flag (or borrow in case of subtraction) is that non-existent 9th bit from the addition of the 8-bit unsigned integers. Effectively, this flag signifies an overflow/underflow for add/sub of unsigned integers. Again, add doesn't care about the signs here at all, it just adds as if the numbers were unsigned.
Adding two negative 2's complement numbers will result in setting of the carry flag to 1, correct.
The overflow flag shows whether or not there's been an overflow/underflow for add/sub of signed integers. To set the overflow flag the instruction treats the numbers as signed (just like it treats them as unsigned for the carry flag and the 8 bits of the result).
The idea behind setting the overflow flag is simple. Suppose you sign-extend your 8-bit signed integers to 9 bits, that is, just copy the 7th bit to an extra, 8th bit. An overflow/underflow will occur if the 9-bit sum/difference of these 9-bit signed integers has different values in bits 7 and 8, meaning that the addition/subtraction has lost the result's sign in the 7th bit and used it for the result's magnitude, or, in other words, the 8 bits can't accommodate the sign bit and such a large magnitude.
Now, bit 7 of the result can differ from the imaginary sign bit 8 if and only if the carry into bit 7 and the carry into bit 8 (=carry out of bit 7) are different. That's because we start with the addends having bit 7=bit 8 and only different carry-ins into them can affect them in the result in different ways.
So overflow flag = carry-out flag XOR carry from bit 6 into bit 7.
Both my and your ways of calculating the overflow flag are correct. In fact, both are described in the Z80 CPU User's Manual in section "Z80 Status Indicator Flags".
Here's how you can emulate most of the ADC instruction in C, where you don't have direct access to the CPU's flags and can't take full advantage of the emulating CPU's ADC instruction:
Output:
You can change
#if 0
to#if 1
to use the sign-comparison-based method for overflow calculation. The result will be the same. At first glance it's a bit surprising that the sign-based method takes care of the carry-in too.Please note that by using my method in which I calculate all carry-ins into bits 0 through 7, you also get for free the value of the
half-carry
flag (carry from bit 3 to bit 4) that's needed for theDAA
instruction.EDIT: I've added a function for subtraction with borrow (SBC/SBB instruction) and results for it.
Another way to see this which is maybe easier to understand. When performing a sum: