das
The das instruction is used to adjust the content of the AL register after that register is used to perform the subtraction 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 subtraction 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,73h ;packed decimal "73" sub al,45h ;subtract packed decimal "45" daa ;AL = 2Eh -> 28h (with CF clear = 28 packed decimal) AL CF AF ; after subtraction 2Eh clear set ; daa 1st part (al-6) 28h clear set (because al AND 0Fh > 9) ; daa 2nd part (nothing) 28h clear set (al !> 99h && CF clear)
Example 2: mov al,38h ;packed decimal "38" sub al,74h ;subtract packed decimal "74" daa ;AL = 0C4h -> 64h with borrow from next digit AL CF AF ; after subtraction 0C4h set clear ; daa 1st part (nothing) 0C4h set clear (al AND 0Fh !> 9 && AF clear) ; daa 2nd part (al-60h) 64h set clear (because CF set)
Example 3: mov al,47h ;packed decimal "47" sub al,49h ;subtract packed decimal "49" daa ;AL = 0FEh -> 98h with borrow from next digit AL CF AF ; after subtraction 0FEh set set ; daa 1st part (al-6) 0F8h set set (because al AND 0Fh > 9) ; daa 2nd part (al-60h) 98h set set (because CF set)The resulting Carry Flag must always be used with the next subtraction. There should never be any carry from the subtraction of the last packed byte; otherwise, the result would be erroneous. The smaller number must always be subtracted from the larger one and a negative sign inserted with the result if necessary.
The following code is an example of subtracting two large packed BCD numbers in "string" order, (i.e. num1 - num2). It assumes that neither number has leading 0 bytes reflected in its size. Otherwise, a longer number with such leading 0 bytes could actually be smaller than the other with fewer bytes and lead to an erroneous result.
Code is also added to convert the resulting packed BCD result to a null-terminated string without any leading "0" character. The byte count of each source number has been initialized in the data section to save coding instructions (the byte count could be obtained otherwise by several means).
.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 7h,27h,52h,83h,61h,84h,68h,15h ;727528361846815 packed signbit db ? ;0=positive, !0=negative txtbuf db 32 dup(?) .code start: mov edi,offset num1 mov esi,offset num2 mov ecx,num1size mov edx,num2size mov signbit,0 .if ecx < edx ;result would be negative negative: xchg esi,edi ;use EDI to point to longest number xchg ecx,edx ;use ECX to hold largest count of packed bytes mov signbit,1 .elseif ecx == edx push esi push edi push ecx repz cmpsb ;compare the two numbers pop ecx pop edi pop esi .if ZERO? ;both numbers are equal mov answer,0 mov txtbuf,"0" jmp exit .endif jnc negative ;num2 > num1 .endif add edi,ecx ;must start with least significant digits add esi,edx ; idem mov ebx,offset answer clc ;start with CF clear @@: dec edi dec esi mov al,[edi] sbb al,[esi] das ;decimal adjust subtraction result mov [ebx],al ;store it inc ebx dec ecx dec edx jnz @B ;process all bytes of smallest number pushfd ;keep current flags or ecx,ecx ;is longest number also completed? jz no_more popfd ;retrieve flags @@: dec edi mov al,[edi] sbb al,0 das mov [ebx],al inc ebx dec ecx jnz @B ;process remaining bytes of longest number no_more: mov ecx,ebx ;EBX points to byte following most significant one mov edx,offset answer sub ecx,edx ;ECX = number of packed bytes in answer mov edi,offset txtbuf ; The following is to convert the result to ASCII ; First insert negative sign if necessary .if signbit != 0 mov al,"-" stosb .endif ; Remove any leading packed BCD 0's @@: dec ebx mov al,[ebx] or al,al jnz @F dec ecx ;adjust count of bytes in answer jmp @B ;check next byte ; Then process separately a leading 0 in the most significant byte @@: .if al < 10 ;no high nibble add al,30h stosb jmp nextbyte .endif ; Process the other bytes @@: movzx eax,byte ptr[ebx] ror ax,4 ;shift most significant nibble to least significant ;while least significant transfers to AH ror ah,4 ;do same with AH which contains the least ;significant nibble of AL in the upper 4 bits add ax,3030h ;convert both digits to ASCII stosw ;store both characters ;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 exit: mov [edi],cl ;add string terminating 0 ; The ASCII representation of the packed BCD result is now available as a ; null-terminated string at the txtbuf address.