4

我正在查看伪代码:BCD 指令的隐藏力量。以下是网站内容的片段:

那么,让我们来看看AAA是做什么的。这是等效的伪代码(来自英特尔):

   IF ((AL AND 0FH) > 9) OR (AF = 1)
    THEN
            AL = (AL + 6) AND 0FH;
            AH = (AH + 1);
            AF = 1;
            CF = 1;
    ELSE
            AF = 0;
            CF = 0;
    FI;enter code here

对于像这样的典型英特尔文档兼容使用,情况确实如此:

    mov     al,6
    add     al,9    ;al=15=0Fh
    aaa             ;al=21=15h  =>  it's in decimal!

上面的算法似乎没有给出代码中注释的结果,所以我假设这里省略了一些步骤。评论声明如下:“al=21=15h => 它是十进制的!”。

我对代码的解释如下:

  • 在加法指令之后,存储在 AL 寄存器中的值将是 15 (0Fh)。
  • 由于它大于 10,因此将向该值添加 6 并与 0Fh 进行与运算,这将导致 1s 值存储在 AL 寄存器中(“AL = (AL + 6) AND 0FH;”)。AL 寄存器的值现在应该是 05h。
  • 该算法将 1 添加到 AH 寄存器,我假设这是结转,因为我们对 AL 寄存器的 4 位进行了与运算和归零(“AH = (AH + 1);”)。

但是,没有提及在运行“AH = (AH + 1);”行之前存储在 AH 寄存器中的值。如果它被初始化为零,它只适用于 20 以下的数字。现在假设它被初始化为零,我希望结果存储为 AH=1 和 AL=5,但注释说“;al =21=15h => 是十进制的!”。似乎在执行这段代码之前和之后做了其他事情。

你能向我解释一下我在这里缺少什么吗?

另外,这里如何使用 CF/AF 标志?

  • 我知道当第 3 位有进位时设置了 AF 标志。这是否用于调整 AL 寄存器以使其存储评论中提到的 15h 值?如前所述,该算法似乎确实表明十位值存储在 AH 寄存器中,而个位值存储在 AL 寄存器中。
  • 为什么在这里设置CF?这是从 MSB 结转的,但我在这个特定的添加/转换中看不到任何结转。我希望在溢出之类的情况下设置它。
4

1 回答 1

9

看起来作者对AAA所做的事情的理解是错误的,他可能将其与DAA(加法后的十进制调整 AL)混淆了,但更糟糕的是,多年来英特尔的文档有时不正确2 3,因此增加了混乱。

我不会直接回答你的问题,但我想给你一些背景知识,让你在给出适当的文档的情况下找出你自己问题的答案。您在问题中引用的文档仅适用于早于 80286 的处理器,即使如此,它也包含有关屏蔽 AL 2底部 4 位的错误

AAA的初衷是在添加两个有效的未打包BCD 数字(ASCII09或 0x30 到 0x39)后使用,以将结果转换为有效的2 位 BCD 数字。有些人滥用AAA超出其设计目的。不幸的是,当 286 发布时,一些滥用AAA的代码被破坏了。

AAA 指令最好这样定义1

IF ((( AL and 0FH ) > 9 ) or (AF==1) 
    IF CPU<286 THEN 
        AL = AL+6  
    ELSE
        AX = AX+6
    ENDIF 
    AH = AH+1 
    CF = 1 
    AF = 1 
ELSE 
    CF = 0 
    AF = 0 
ENDIF 
AL = AL and 0Fh 

添加两个单位数字 BCD 数字时,您需要在AAA之前清除 AH 。添加两个数字的代码如下所示:

xor ah, ah        ; Clear AH
mov al, '6'       ; AL=0x36 (0x36 = ASCII '6')
add al, '9'       ; AL=0x36+0x39 (0x39 = ASCII '9') = 0x6F
aaa               ; AH=0x01, AL=0x05 thus AX=0x0105 . AH has upper digit, AL has the lower.

由于此代码使用有效值(0x30 到 0x39),因此对于支持该指令的所有处理器1 ,它将以相同的方式工作。更一般地说,只要AL中的值是 0x00 到 0xF9(包括),如果在 <286 的处理器和 286 或更高版本的处理器上运行, AAA的结果将是相同的。

如果添加三个或更多 BCD 数字,您可以使用 AH 中的值来帮助保持正确溢出的结果。


AAS 的类似问题(减法后的 ASCII 调整)

AAS指令最好这样定义1

IF ((( AL and 0FH ) > 9 ) or (AF==1) 
    IF CPU<286 THEN 
        AL = AL-6  
    ELSE
        AX = AX-6
    ENDIF 
    AH = AH-1 
    CF = 1 
    AF = 1 
ELSE 
    CF = 0 
    AF = 0 
ENDIF 
AL = AL and 0Fh 

笔记:

  • 1 AAAAAS在 x86-64 处理器上的 64 位代码中不可用。
  • 2一些 80386 和 80486 文档没有明确说明无论使用什么处理器,结果中总是清除AL的高 4 位。您从文章中引用的文档在这方面也是错误的。与AAS指令相关的问题。
  • 3一些 80286+ 文档不正确地指出AHAAA递增 6,而实际上是AX递增 6。除此之外,在这两种情况下AH也递增 1。最近的英特尔手册对此进行了正确处理,但不要提到 286 之前的处理器在这方面的工作方式不同。与AAS指令相关的问题。
于 2018-08-06T18:34:05.257 回答