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.