汇编语言AAA指令:调整ADD或ADC指令的二进制运算结果

在 32 位模式下,AAA ( 加法后的 ASCII 调整 ) 指令调整 ADD 或 ADC 指令的二进制运算结果。设两个 ASCII 数字相加,其二进制结果存放在 AL 中,则 AAA 将 AL 转换为两个非压缩十进制数字存入 AH 和 AL。一旦成为非压缩格式,通过将 AH 和 AL 与 30h 进 OR 运算,很容易就能把它们转换为 ASCII 码。

下例展示了如何用 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 十进制数值相加。由于每次数字相加的进位标志位都要传递到更高位,因此,过程的实现要比想象的更复杂一些。下面的伪代码中,acc 代表的是一个 8 位的累加寄存器:

esi (index) = length of first_number - 1
edi (index) = length of first_number
ecx = length of first_number
set carry value to 0
Loop
    acc = first_number[esi]
    add previous carry to acc
    save carry in carry1
    acc += second_number[esi]
    OR the carry with carry1
    sum[edi] = acc
    dec edi
Until ecx == 0
Store last carry digit in sum

进位值必须总是被转换为 ASCII 码。将进位值与第一个操作数相加时,就需要用 AAA 来调整结果。程序清单如下:
; ASCII Addition                      (ASCII_add.asm)
; 对有隐含固定小数点的串执行 ASCII 运算。

INCLUDE Irvine32.inc

DECIMAL_OFFSET = 5                            ; 距离右侧的偏移量
.data
decimal_one BYTE "100123456789765"            ; 1001234567.89765
decimal_two BYTE "900402076502015"            ; 9004020765.02015
sum BYTE (SIZEOF decimal_one + 1) DUP(0),0

.code
main PROC

; 从最后一个数字开始

    mov    esi,SIZEOF decimal_one - 1
    mov    edi,SIZEOF decimal_one
    mov    ecx,SIZEOF decimal_one
    mov    bh,0                       ; 进位值清零

L1:    mov    ah,0                    ; 执行加法前清除AH
    mov    al,decimal_one[esi]        ; 取第一个数字
    add    al,bh                      ; 加上之前的进位值
    aaa                               ; 调整和数 (AH = 进位值)
    mov    bh,ah                      ; 将进位保存到 carry1
    or    bh,30h                      ; 将其转化为 ASCII 码
    add    al,decimal_two[esi]        ; 加第二个数字
    aaa                               ; 调整和数 (AH = 进位值)
    or    bh,ah                       ; 将进位值 carry1 进行 OR 运算
    or    bh,30h                      ; 将其转换为 ASCII 码
    or    al,30h                      ; 将 AL 转换为 ASCII 码
    mov    sum[edi],al                ; 将 AL 保存到 sum
    dec    esi                        ; 后退一个数字
    dec    edi
    loop    L1
    mov    sum[edi],bh                ; 保存最后的进位值

; 显示和数字符串

    mov    edx,OFFSET sum
    call    WriteString
    call    Crlf
   
    exit
main ENDP
END main
程序输出如下所示,和数没有显示十进制小数点:

1000 5255 3329 1780