aas
The aas instruction is used to adjust the content of the AL register after that register has been used to perform the subtraction of two unpacked BCDs. The CPU uses the following logic:
IF (al AND 0Fh > 9) or (the Auxilliary Flag is set) al = al-6 ah = ah-1 CF set ENDIF al = al AND 0Fh
(The Auxilliary Flag is set whenever there is a carry of the lower 4 bits from a subtraction.)
Although 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 flag (such as a mov instruction).
Example1: mov al,7 sub al,2 ;al = 5, AF clear, CF clear aas ;al = 5, ah is unchanged, CF clear
Example2: mov al,3 sub al,6 ;al = -3 = 0FDh, al AND 0Fh = 0Dh > 9, AF set, CF set aas ;al = al-6 = -9 = 0F7h AND 0Fh = 7, ah=ah-1, CF set
Example3: mov al,2 sub al,9 ;al = -7 = 0F9h, al AND 0Fh = 9 <= 9, AF set, CF set aas ;al = al-6 = -13 = 0F3h AND 0Fh = 3, ah=ah-1, CF setNote: Because only the lower 4 bits of AL are retained, it is thus possible to subtract the ASCII values of numerical digits directly without the need to convert them to their binary values beforehand.
Example4: mov al,"7" ;37h sub al,"9" ;al = 37h-39h = 0FEh AND 0Fh = 0Eh > 9, AF set, CF set aas ;al = 0FEh-6 = 0F8h AND 0Fh = 8, ah = ah-1, CF set
The subtraction of two large numbers requires more preparation than the simple addition of similar numbers. The smaller of the two numbers must always be subtracted from the larger one to get a correct answer. When the larger one has to be subtracted from the smaller one, a "-" sign must then be inserted in front of the answer.
Furthermore, the sign of each number should be checked as a first step. For instance, the subtraction of a negative number from a positive one should be handled by addition of the digits.
The simple subtraction of two numbers is still relatively straightforward. Repeated subtractions is also one of the few ways that large numbers can be divided.
The next example will show how such division can be coded. The two large ASCII numbers (num1/num2) are assumed to be valid integers (numerical digits only) and their digits in the "string" order. They will already have been placed in memory buffers of adequate size. Their individual size will also be already in their respective memory variable. The buffer of the numerator will be used to store the result of the subtractions since that result must be used repeatedly. A third buffer will be used to store the answer of the division also in the "string" order.
The code also shows how a required number of fractional digits can be included in the answer.
.data
num1add dd ?
num2add dd ?
size1 dd 21
size2 dd 11
decimal dd 5 ;number of decimal places required in answer
prenum1 db 0 ;to avoid extra code
num1 db "491756380472816275825",11 dup(0)
prenum2 db 0 ;to avoid extra code
num2 db "19932850157",13 dup(0)
answer db 32 dup(?)
.code
; EDX will be used to hold the count of repeating cycles
; ECX will be used for various counters
; ESI will be used to point to the current digit of the numerator string
; EDI will be used to point to the current digit of the denominator string
; EBX will be used to point to the current digit of the answer
; Both ASCII numbers first have to be converted to binary for this operation
; Convert numerator
mov edi,offset num1
mov ecx,size1
@@:
mov al,[edi]
and al,0Fh
stosb
dec ecx
jnz @B
; If some decimal places must also be computed for the answer, an equivalent
; number of trailing 0's must be appended to the numerator.
xor eax,eax
mov ecx,decimal
rep stosb
; Convert denominator
mov edi,offset num2
mov ecx,size2
@@:
mov al,[edi]
and al,0Fh
stosb
dec ecx
jnz @B
; Leading 0's in the numerator are immaterial. However, extra leading 0's
; in the denominator must be removed and its size adjusted accordingly
mov edi,offset num2
xor eax,eax
mov ecx,size2
repz scasb ;find first non-zero byte
inc ecx ;correction for first non-zero byte
mov size2,ecx
dec edi ;back to first non-zero digit
dec edi ;will allow one necessary leading 0
mov num2add,edi ;address of first working byte
mov esi,offset prenum1
mov num1add,esi ;to keep track of the position of the most
;significant digit
mov ebx,offset answer
; The following will essentially define the number of digits in the
; integer portion of the answer
mov edx,size1
sub edx,size2
jnc @F
zero_integer:
mov byte ptr[ebx],"0" ;denominator would be larger than numerator
inc ebx
jmp do_decimals
@@:
inc edx
; The following code will avoid leading 0's in the answer
; It will also detect a smaller numerator due to leading 0's
@@:
mov esi,num1add
mov edi,num2add
mov ecx,size2
inc ecx
repz cmpsb
jnc outerloop
inc num1add
dec edx
jz zero_integer
jmp @B
outerloop:
mov byte ptr[ebx],"0" ;initialize each byte to ASCII 0
;this should only be done if the answer
;is to be displayed immediately
innerloop:
;check if denominator can be subtracted from numerator
mov esi,num1add
mov edi,num2add
mov ecx,size2
inc ecx
repz cmpsb
jc num2larger
mov ecx,size2
mov esi,num1add
add esi,ecx ;points to least significant digit
;for subtracting denominator
mov edi,num2add
add edi,ecx ;points to end of denominator
inc ecx ;may need to subtract one more digit
clc ;clear CF to start subtraction
@@:
mov al,[esi]
sbb al,[edi]
aas
mov [esi],al
dec esi
dec edi
dec ecx
jnz @B
inc byte ptr[ebx] ;increment digit of answer
jmp innerloop ;repeat subtraction
num2larger:
inc num1add
inc ebx
dec edx
jnz outerloop
; At this point, the integer portion of the answer is ready for display.
; If a number of decimal places would be required as specified by the
; decimal variable, those could continue to be computed with the
; following code.
do_decimals:
mov edx,decimal
or edx,edx
jz exit
mov decimal,0 ;to skip the second time around
mov byte ptr[ebx],"." ;insert decimal delimiter
inc ebx
jmp outerloop
exit:
mov byte ptr[edi],0
; The answer is now ready for display as a null-terminated string
The above code provides a truncated answer. Additional instructions could be added to provide a rounded answer.
Additional instructions could also be added to cater for input containing decimal fractions. The possibilities are only left to the imagination of the programmer.
