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.
