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.