17

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.

9

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.

  • 4
    As Leo B alludes to in his answer (and made explicit in comments) the DAA instruction examines the H bit to perform BCD arithmetic. – TripeHound Sep 12 '17 at 12:57
  • @TripeHound Well yes, but that doesn't mean it needs to be exposed to the user.. – JeremyP Sep 13 '17 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? – user3570736 Sep 13 '17 at 12:32
  • 2
    @JeremyP It needs to be exposed so that it can be saved & restored through interrupts, etc. – Wayne Conrad Oct 5 '17 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. – Tommy Oct 7 '17 at 2:39
31

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
  • 2
    the 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... – Spektre Sep 9 '17 at 7:46
  • @Spektre Since you put "undefined" in quotes, have you been able to find the documentation on this instruction? – NieDzejkob Sep 9 '17 at 11:09
  • 2
    @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 mine daa 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; } – Spektre Sep 9 '17 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. – Spektre Sep 9 '17 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 pass ZEXALL. . – JeremyP Sep 11 '17 at 14:34
4

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.

  • 4
    Trivia 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. – Tommy Sep 10 '17 at 17:04
  • 1
    The MC68000 doesn’t have anything like a half-carry flag – tofro May 4 '18 at 19:38
  • @tofro : It's bit 5 of the SR. – Eric Towers May 4 '18 at 23:07
  • 1
    The "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. – tofro May 4 '18 at 23:17
  • @tofro : Bit 5: CVZNXH. – Eric Towers May 4 '18 at 23:19
4

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.

  • 2
    That can be done in 5 bytes and 18 cycles: CP 10; SBC 69h; DAA – George Phillips May 3 '18 at 23:23
  • Not 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 the DAA (save it with either EX AF,AF' or PUSH 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. – Toby Speight Mar 27 at 15:54
3

The reason there needs to be a visible "half carry" bit is a combination of two factors.

  1. BCD support
  2. 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.

  • 1
    I 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. May 4 '18 at 1:19
  • @LeoB.: That doesn't mean there haven't been processors that have failed to do such things. – supercat May 4 '18 at 1:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.