The Z80 apparently had a 4-bit ALU, and computes 8-bit values in two stages. The half-carry bit preserves the carry from bits 3 to 4. Why did the designers of this chip choose to preserve that value in the flags register? Presumably, that intermediate result could have been stored (at less cost) in the Carry bit between the two halves of the addition/subtraction.
5 Answers
Why does the Z80 have a half-carry bit?
Because the ALU is only 4 bits wide. The Z80 needs to preserve the carry between bits 3 and 4.
Why did the designers of this chip choose to preserve that value in the flags register?
Good question. You can't access the H
bit directly except by saving the status register and then examining its contents, so there is no real purpose in having it visible. I suspect it was just easier or saved gates to put it in the same 8 bit register as the other user accessible flags. There are also two other undocumented flags in the status register that have less obvious uses.
Presumably, that intermediate result could have been stored (at less cost) in the Carry bit between the two halves of the addition/subtraction
Some Z80 arithmetic calculations should not affect the normal carry flag. For example address displacement calculations.
Also, the DAA
instruction needs not just the final carry to be preserved but also the carry between "hex" digits so it knows how to adjust the digits to get the proper answer. For example 1 + 1 = 2 which is fine in hex and BCD but 9 + 9 = 0x12
in hex and the addition needs to be adjusted by DAA
to be 0x18
. The only way to tell between the two cases is to know the value of the carry from the bottom hex digit to the top hex digit.
-
6As Leo B alludes to in his answer (and made explicit in comments) the
DAA
instruction examines theH
bit to perform BCD arithmetic. Commented Sep 12, 2017 at 12:57 -
@TripeHound Well yes, but that doesn't mean it needs to be exposed to the user..– JeremyPCommented Sep 13, 2017 at 9:19
-
4@JeremyP it does for debugging, or saving and restoring program state in context switches. What if an interrupt occurs between an ADD and a DAA? Commented Sep 13, 2017 at 12:32
-
2@JeremyP It needs to be exposed so that it can be saved & restored through interrupts, etc. Commented Oct 5, 2017 at 21:27
-
7@JeremyP I agree with Wayne and Jeremy; potential issue is this: user-space code adds two numbers, is about to perform a
DAA
. An interrupt occurs. The interrupt code adds two numbers at some point. If the half-carry flag isn't stored and restored, the interrupt will not have been invisible. So it's a flag just like any other flag. Which means it's stored and restored just like any other flag. Which makes it visible.– TommyCommented Oct 7, 2017 at 2:39
The intermediate carry flag, or "adjust flag", or half-carry flag is used to facilitate binary-coded decimal (BCD) arithmetic, where each decimal digit of a number is represented as a nibble (a group of 4 bits). The range of valid values for each nibble is 0 to 9 (0000 to 1001). If, after an arithmetic operation, the result contains a "non-decimal" nibble (1010 to 1111), it can be deduced from its bit pattern that a correction is necessary. However, it might also be necessary even if both nibbles of the result look "decimal".
Here's an example (from Wikipedia) why the flag is needed:
Adding the BCD values 3916 and 4816 produces 8116. This result does not have a non-decimal low nibble, but it does cause a carry out of the least significant digit (lower four bits) into the most significant digit (upper four bits). This is indicated by the CPU setting the half-carry flag. This value must also be corrected, by adding 0616 to 8116 to produce a corrected BCD result of 8716.
0011 1001 39 + 0100 1000 48 ----------- 1000 0001 81, intermediate result + 0110 06, adjustment ----------- 1000 0111 87, adjusted result
-
2the adjustment is done by
DAA
instruction which is one of the most ugly instructions in Z80 to emulate properly due to "undefined" behavior of some of the flag bits...– SpektreCommented Sep 9, 2017 at 7:46 -
@Spektre Since you put "undefined" in quotes, have you been able to find the documentation on this instruction?– MayaCommented Sep 9, 2017 at 11:09
-
3@NieDzejkob yes I did and implemented it in mine emulator ... it is in form of huge
2048x16bit
LUT table. not sure where I fount it anymore it has been some years ago but I still got the source code... From a quick search on the first line of LUT I fount this z80_lib/Tables.h but that is most likely not the source I got it from ... The code for minedaa
looks like this:void Z80::ins0_DAA (){ static int aaa; aaa=reg.r8.a+(((reg.r8.f&3)+((reg.r8.f&16)>>2))<<8); reg.r16.af=_z80_table_DAA[aaa]; return; }
– SpektreCommented Sep 9, 2017 at 11:59 -
1@LeoB. yep it is the best stuff I found on the matter. Without it I would never got with my Emulator so far. Heh haven done anything on it for 3 years (just using it time to time) till today major updates :). The tests are really long it take around 35-60 min to complete on turbo speed
~50MHz
if my memory serves well.– SpektreCommented Sep 9, 2017 at 18:09 -
1@Spektre
DAA
was the worst instruction to implement on my Z80 implementation. It took me ages to get it right but I found two pieces of goodish documentation and looking at somebody else's emulator to get it to passZEXALL
. .– JeremyPCommented Sep 11, 2017 at 14:34
The Intel 4004 and 4040 were 4-bit CPUs. In some ways, the 8008 and its successor, the 8080 behaved as if it were two 4004s glued together, although the 8008 architecture was not just two 4004s glued together. For instance, the 8008 had an extensive carry prediction chunk attached to the ALU.
Adding either BCD or 4-bit values in the 4004 could result in overflow, indicated by the carry flag. In the 8008 and its descendants, this carry is preserved as the half-carry flag. This is why there is a half-carry flag in Intel's 8080, 8086, 8088, and subsequent x86 processors, as well as the Zilog Z80 and its descendants (e.g., the eZ80, released in 2007) and its clones. That the Z80 implements its ALU as a two-pass 4-bit ALU is a little weird, but is the way it was done. This choice of ALU design is not why there is a half-carry flag; this flag is present to preserve binary compatibility with the 8080.
The Motorola 6809 also had a half-carry flag. Weirdly, it is not affected by the DAA opcode (even if the DAA must carry between the nibbles) for fixing up BCD addition via single-byte adds. Here, the half-carry bit is set by the ADDA or ADCA and thus indicates binary inter-nibble carry, not BCD inter-digit carry. (This is a way that the English Wikipedia's article on the half-carry flag is not quite applicable to the MC68000.) Nevertheless, this led to the half-carry flag in the Motorola 68000 series and descendants as well.
-
4Trivia I learnt today though: the 8008 was developed simultaneously to the 4004, sharing ideas but not as a successor. Their original parts designations, the 1201 and 1202, gave the 8008 the earlier designation and it almost beat the 4004 to market. But the 4004 team were responsible for getting themselves a more marketable part number, and the 8008 team subsequently took on the logical development of that.– TommyCommented Sep 10, 2017 at 17:04
-
1
-
-
1The "X" (Extend) bit of the SR (in case you mean that it's actually bit 4 if you start to count at 0) is no half-carry - It is a copy of the carry bit.– tofroCommented May 4, 2018 at 23:17
-
1Contrary to this answer, the 8008 did not have a half-carry flag. Also, the 8008 is not at all like two 4004s glued together; the two processors have totally different architectures. The 8008 was derived from the Datapoint 2200's architecture. Don't be misled by the similar numbers into thinking the 4004 and 8008 are at all similar. Commented Mar 28, 2019 at 5:11
Halfcarry is necessary for DAA
instruction and thus it makes sense to have it in AF
in a designated place (since it has a designated behavior).
DAA
is needed for BCD arithmetic. But it's also useful to convert a nibble into ASCII:
ADD A,90H
DAA
ADC A,40H
DAA
takes 6 bytes and 22 cycles. You cannot really do that with conditional jumps.
The halfcarry bit is not even the most obscure: there is a bit that is set when the last operation was a subtraction (or comparable) so that the DAA
instruction knows whether to adjust after an addition or a subtraction.
The 8088 (which is sort-of source code compatible to the 8080) has two different adjustment instructions for addition and subtraction, in contrast.
-
2That can be done in 5 bytes and 18 cycles: CP 10; SBC 69h; DAA Commented May 3, 2018 at 23:23
-
1Not only does it make sense to have the halfcarry in
AF
, it's necessary to have it somewhere that at can be stored if an interrupt could occur between the arithmetic and theDAA
(save it with eitherEX AF,AF'
orPUSH AF
, depending on your interrupt handler's preference). The only other alternative is to disable interrupts during decimal calculations, and that's not very palatable. Commented Mar 27, 2019 at 15:54 -
1@TobySpeight: If compatibility with the 8080's DAA instruction weren't required, it might have been cheaper for the Z80 to make DAA a prefix which would affect the behavior of a succeeding add or subtract instruction than to add the necessary signal routing to maintain the H and N signals in the register file. I know MOS had a patent on direct BCD arithmetic, but I think that only applied to its ability to process two digits simultaneously; decimal support in a 4-bit ALU would not have been particularly novel in the 1970s.– supercatCommented Oct 19, 2021 at 17:46
The reason there needs to be a visible "half carry" bit is a combination of two factors.
- BCD support
- Interrupt handling.
As other answers have pointed out the Z80 provided BCD support through an adjustment instruction run after the addition or subtraction operation (this constrasts with the 6502 that implemented it as a mode flag). This instruction needs to know whether or not there was a carry from the high nibble to the low nibble to correctly adjust the result.
But why does that flag need to be visible to the user as part of a status register rather than just an internal flag? the answer is interrupt handling.
Consider what happens if an interrupt happens between an arithmetic instruction and the corresponding DAA instruction. The interrupt must save the state of the half carry so it can be restored and correctly used by the DAA instruction after the interrupt returns.
This is why many flags that one would normally think of as "internal" are grouped together into a user-visible status register so they can be easilly saved and restored on interrupt.
-
2I think that the idea that if a flag is produced by an instruction and consumed by another instruction, then it has to be accessible for save-restore at the instruction set level in case of an interrupt is so self-evident, given the example of other condition flags, that nobody bothered to mention it.– Leo B.Commented May 4, 2018 at 1:19
-
1@LeoB.: That doesn't mean there haven't been processors that have failed to do such things.– supercatCommented May 4, 2018 at 1:52