daa
The daa instruction is used to adjust the content of the AL register after that register is used to perform the addition of two packed BCDs. The CPU uses the following logic:
CF_old = CF IF (al AND 0Fh > 9) or (the Auxilliary Flag is set) al = al+6 CF = CF or CF_old AF set ENDIF IF (al > 99h) or (Carry Flag is set) al = al + 60h CF set ENDIFAlthough this instruction should be used immediately after the addition instruction, it could be used later as long as no other intervening instruction would have changed the AF or CF flags (such as a mov instruction).
Example 1:
mov al,38h ;packed decimal "38"
add al,45h ;add packed decimal "45"
daa ;AL = 7Dh -> 83h (with CF clear = 83 packed decimal)
AL CF AF
; after addition 7Dh clear clear
; daa 1st part (al+6) 83h clear set (because al AND 0Fh > 9)
; daa 2nd part (nothing) 83h clear set (al !> 99h && CF clear)
Example 2:
mov al,88h ;packed decimal "88"
add al,74h ;add packed decimal "74"
daa ;AL = 0FCh -> 62h (with CF set = 162 packed decimal)
AL CF AF
; after addition 0FCh clear clear
; daa 1st part (al+6) 02h set set (because al AND 0Fh > 9)
; daa 2nd part (al+60h) 62h set set (because CF set)
Example 3:
mov al,47h ;packed decimal "47"
add al,69h ;add packed decimal "69"
daa ;AL = 0B0h -> 16h (with CF set = 116 packed decimal)
AL CF AF
; after addition 0B0h clear set
; daa 1st part (al+6) 0B6h clear set (because AF set)
; daa 2nd part (al+60h) 16h set set (because AL > 99h)
The resulting Carry Flag must always be used with the next addition, or inserted as an additional digit following the addition of the last packed digits.
The following code is an example of adding two large packed BCD numbers in "string" order. Code is also added to convert the resulting packed BCD sum to a null-terminated string without any leading "0" character.
.data
answer db 16 dup(0)
num1size dd 7
num2size dd 8
num1 db 78h,96h,19h,03h,21h,38h,55h ; 78961903213855 packed
num2 db 07h,27h,52h,83h,61h,84h,68h,15h ;727528361846815 packed
txtbuf db 32 dup(0)
.code
start:
mov ebx,offset answer
mov edi,offset num1
mov esi,offset num2
mov ecx,num1size
mov edx,num2size
add edi,ecx ;must start with least significant digits
add esi,edx ; idem
.if ecx < edx
xchg esi,edi ;use EDI to point to longest number
xchg ecx,edx ;use ECX to hold largest count of packed bytes
.endif
clc ;start with CF clear
@@:
dec edi
dec esi
mov al,[edi]
adc al,[esi]
daa ;decimal adjust addition result
mov [ebx],al ;store it
inc ebx
dec ecx
dec edx
jnz @B ;process all bytes of smallest number
pushfd ;keep current flags
;the next instruction would modify them
or ecx,ecx ;is longest number also completed?
jz lastcarry
popfd ;retrieve flags
@@:
dec edi
mov al,[edi]
adc al,0
daa
mov [ebx],al
inc ebx
dec ecx
jnz @B ;process remaining bytes of longest number
pushfd
lastcarry:
popfd ;retrieve the last flags for the CF
jnc @F ;skip if no last Carry
mov byte ptr[ebx],1
inc ebx ;EBX now points to byte after most significant digit
@@:
mov ecx,ebx
mov edx,offset answer
sub ecx,edx ;ECX = number of packed bytes in answer
mov edi,offset txtbuf
dec ebx
; The next few instructions are for processing the very first byte of the
; packed BCD if the high nibble is 0, and thus prevent a leading 0
; in the converted string.
mov al,[ebx]
.if al < 10 ;if high nibble = 0
add al,30h ;convert single digit to ASCII
stosb ;store only 1 digit
jmp nextbyte
.endif
@@:
movzx eax,byte ptr[ebx]
ror ax,4 ;shift most significant nibble to least significant
;while least significant nibble transfers to AH
ror ah,4 ;do same now with AH which contained
;the least significant nibble of AL in upper 4 bits
add ax,3030h ;convert both digits to ASCII
stosw ;store both digits
;the more significant of the two having been kept in AL will
;get stored ahead of the less significant one in the string
nextbyte:
dec ebx
dec ecx
jnz @B
mov [edi],cl
; The ASCII representation of the packed BCD sum is now available as a
; null-terminated string at the txtbuf address.
