虽然 CPU 用二进制运算,但是也可以执行 ASCII 十进制串的运算。使用后者进行运算,对用户而言既便于输入也便于在控制台窗口显示,因为不用进行二进制转换。尽管 ASCII 运算执行速度比二进制运算要慢很多,但是它有两个明显的优点:1) 不必在执行运算之前转换串格式。2) 使用假设的十进制小数点,使得实数操作不会出现浮点运算的舍入误差的危险。有四类指令用于处理 ASCII 加法、减法、乘法和除法:AAA / AAS / AAM / AAD
AAA:ASCII adjust after addition。
执行加法后进行 ASCII 调整。将AX寄存器的内容转换为BCD码,但是这个内容是有条件的,必须先将AH设置0,然后AL的内容是由两个ASCII码的数字相加后的结果。如果AL存放其它不符合要求的内容,AAA指令的结果将没有意义。AAA指令的算法用C语言表达如下:
if ((and al, 0Fh) > 9 || AC == 1){
if (8088 || 8086){
al = al + 6
else
ax = ax + 6
}
ah = ah + 1
AC = 1
CY = 1
else
AC = 0
CY = 0
}
al = (and al, 0Fh)
下例展示了如何用 AAA 指令正确地实现 ASCII 数字 8 加 2。在执行加法之前,必须把 AH 清零,否则它将影响 AAA 执行的结果。最后一条指令将 AH 和 AL 转换为 ASCII 数字:
mov ah, 0
mov al, '8' ; AX = 0038h
add al, '2' ; AX = 006Ah
aaa ; AX = 0100h (结果进行 ASCII 调整)
or ax, 3030h ; AX = 3130h ='10' (转换为 ASCH 码)
使用AAA指令处理很长的ASCII码阿拉伯数字相加的例子,100123456789765 + 900402076502015。这里的难点是,虽然AAA指令能轻松地将两个ASCII的数字相加,但相加的结果可能会进位,下一位的数字相加时不要忘记把前面的进位一起算进去。
.386
.model flat, stdcall
.stack 4096
ExitProcess proto,dwExitCode:dword
INCLUDE Irvine32.inc
.data
decimal_one BYTE "100123456789765"
decimal_two BYTE "900402076502015"
sum byte 15 dup(0), 0
.code
main PROC
nop
mov esi, offset decimal_one + 14
mov edi, offset decimal_two + 14
mov ebp, offset sum + 15
mov ecx, 15
mov bh, 0
L1:
mov ah, 0
mov al, [esi]
add al, [edi]
AAA
add al, bh
AAA
or ax, 3030h
mov [ebp], al
mov bh, ah
dec ebp
dec esi
dec edi
loop L1
mov [ebp], bh
nop
invoke ExitProcess,0
main ENDP
end main
AAS (ASCII adjust after subtraction)
32 位模式下,AAS(做完减法后调整为ASCII码)指令紧随 SUB 或 SBB 指令之后,这两条指令执行两个非压缩BCD码的减法,并将结果保存到 AL 中。AAS 指令将 AL 转换为 ASCII 码的数字形式。
.386
.model flat, stdcall
.stack 4096
ExitProcess proto,dwExitCode:dword
INCLUDE Irvine32.inc
.data
.code
main PROC
nop
mov eax, 0
mov al, '8'
mov bl, '5'
sub al, bl
AAS
invoke ExitProcess,0
main ENDP
end main