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.
 
                        
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 - 3hdoesn'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, cxor 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+0x0909produces0x1212, with an AF-setting carry from9+9 = 12h, but no carry from09h + 09h = 12hat the byte boundary.Instead of working differently for unpacked (checking the high bits of AL),
AAAuses mostly the same logic as for DAA (checking AF, and low nibble ofalbeing > 9) - https://www.felixcloutier.com/x86/aaa#operationFun fact: you can use
DASto 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 conditionalAL-=6and other behaviour.