Search code examples
assemblyx86x86-16bcdeflags

Why auxiliary flag is 0 when there is a borrow between nibbles


Testing on EMU8086, with the following code snippet:

MOV CX, 1527H
SUB CX, 44H

The emulator shows that AF is 0

   1527
 -   44
========
   14E3

When doing the subtraction by hand, we got 7 - 4 = 3, no problem here. Then 2 - 4, we then have to borrow from the next nibble. So from my understanding, AF should have been 1.


Solution

  • AF is set according to carry (or borrow) from bit #3 to bit #4. i.e. across the lowest / least-significant nibble boundary, the one in the middle of AL/BL/CL/DL, not the middle of AX. (Since each hex digit represents a nibble, carry/borrow from the lowest hex digit into the 2nd-lowest.)

    As you say 7h - 3h doesn't borrow, so AF=0.

    The description of AF as a "half-carry" flag makes sense in the context of byte operand-size, where there's only one nibble boundary within the byte, and it's half-way to the carry-out position.

    Word operand-size (and larger on 386 and x86-64) still sets AF from bit 3->4, not from the middle of the operand-size or carry between any other bit-positions.


    That's because it's intended for packed and unpacked BCD operations like DAA and AAA respectively. Note that AAA (for use after add ax, cx or whatever with 2 decimal digits unpacked into separate bytes) depends on AF detecting carry-out from the low 4 bits. There would never be carry from bit #7 to bit #8 (across the byte boundary) in that case, e.g. 0x0909 + 0x0909 produces 0x1212, with an AF-setting carry from 9+9 = 12h, but no carry from 09h + 09h = 12h at the byte boundary.

    Instead of working differently for unpacked (checking the high bits of AL), AAA uses mostly the same logic as for DAA (checking AF, and low nibble of al being > 9) - https://www.felixcloutier.com/x86/aaa#operation

    Fun fact: you can use DAS to save a couple bytes in int -> ASCII-hex conversion, along with cmp and sbb, a total hack / abuse that just happens to work because of the distance between the ASCII codes for '9' and 'A', along with DAS's conditional AL-=6 and other behaviour.