Packed BCD keeps two decimal digits in one byte, one per four bits: the number 38 is stored as 38h. Adding two such bytes with a plain ADD gives a binary sum, which is often not valid BCD: 38h + 45h = 7Dh. DAA right after the ADD (or ADC) on AL turns it into the correct packed result, here 83h.

Exactly what it does

First, if the low digit of AL is greater than 9 or AF is 1, DAA adds 6 to AL and sets AF. Then, if AL is now greater than 9Fh or CF is 1, it adds 60h to AL and sets CF. A step that is not needed clears its flag. CF = 1 therefore means the decimal sum passed 99 and a 1 carries into the next pair of digits. SF, ZF and PF are set from the final AL; OF is undefined.

AddBinary sumAfter DAACF
38h + 45h7Dh83h0
19h + 01h1Ah20h0
75h + 48hBDh23h1 (123)
09h + 09h12h (AF = 1)18h0
Prints 83.
MOV AL, 38h ; packed BCD 38
ADD AL, 45h ; AL = 7Dh, not BCD
DAA ; AL = 83h: packed 83
MOV AH, AL
MOV CL, 4
SHR AH, CL ; AH = high digit
AND AL, 0Fh ; AL = low digit
OR AX, 3030h ; both digits -> characters
MOV BX, AX
MOV DL, BH
MOV AH, 02h ; DOS: print DL
INT 21h
MOV DL, BL
MOV AH, 02h
INT 21h

Longer numbers

A packed number with more digits is a string of such bytes. Add them from the lowest byte up, with ADC so the carry from the previous DAA is included, and DAA after every byte.

Pitfalls

DAA works only on AL and only straight after an addition into AL, since it reads AF and CF from that addition. Both inputs must be valid packed BCD. Use INC AL with care: it does not set CF, so for a decimal counter write ADD AL, 1 then DAA.

Later CPUs

Unchanged in 16- and 32-bit code; not available in 64-bit mode.