在汇编中添加两个三字数字

Adding two three-word numbers in assembly

这是我目前所做的:

.MODEL SMALL
.STACK 64 
.DATA
X DW 5463h,0F8A8h,0D37Eh
Y DW 87DEh,3408h,92C2h
Result DW 3 DUP(0) ;Trying to add X, Y with the result being here.

.Code
MAIN PROC FAR 
MOV AX,@Data
MOV DS,AX 

;Adding X,Y:
;Getting Pointers Ready
LEA SI,X
LEA DI,Y
LEA BP,Result
MOV CX,3 ; Counter
CLC ;Clearing the carry
PushF ;We don't want the flag to be affected from the next two operations
;Looping three times to Add Word by Word 
WordAdd:
MOV AX,[SI+4] ;We will iterate from the least significant word (4) to the most significant word (0)
MOV BX,[DI+4]
PopF ;Retaining the flag register
ADC AX,BX
PushF
MOV [BP+4],AX
Sub BP,2
Sub SI,2
Sub DI,2
Loop WordAdd

MOV Ax,[BP]
MOV BX,[BP+2]
MOV CX,[BP+4]

MAIN ENDP 
END MAIN

不知道为什么最后AX,BX,CX都是0000,虽然我每次调试都确保加法正确并放入AX,但我想不通为什么。

至少有 2 个错误:

a) 最后一个 pushf 永远不会弹出(正如 Nate Eldredge 在评论中提到的那样)。这可能会导致程序在返回其调用者时崩溃。

b) 你的顺序错了。 80x86 是 little-endian,你正在做“最重要的词”。这将使进位错误(如果它是十进制数字而不是 16 位字,它就像“090 + 010 = 001”)。

注意,因为你知道总会有3个单词,而且因为3个很小,所以最好不要循环。这可能看起来像:

MOV AX,[X]
MOV BX,[X+2]
MOV CX,[X+4]
ADD AX,[Y]
ADC BX,[Y+2]
ADC CX,[Y+4]

另请注意,所有现代 80x86 CPU(自 1985 年的 80386 以来)都允许您在 16 位代码中使用 32 位指令。这使您可以做更多类似的事情:

MOV EAX,[X]
MOV BX,[X+4]
ADD EAX,[Y]
ADC BX,[Y+4]

您的代码正确计算了总和并将其存储在 Result 的三个单词中,采用您不寻常的混合字节顺序。但是,请注意 BP 中的地址:您开始将它指向 Result 并在循环的每次迭代中将其递减 2。由于您将循环中的单词存储到 [BP+4],因此您得到了正确的单词,但在循环之后 BP 指向 Result-6。因此,您对 AX, BX, CX 的加载分别取自 Result-6Result-4Result-2,它们不是存储总和的位置。

一些更一般的建议:

  • 您当前的设计需要分散在整个代码中的各种魔法偏移量,例如 [BP+4][BP+6] 等。这些都取决于您的数组大小,因此您实际上是在大约 10 个地方对该大小进行硬编码。想一想当您想要修改您的程序以添加四字数字时的痛苦,或者天堂禁止,一个长度要在运行时确定。所以你可能会考虑避免这种情况的设计。例如,如果您从 BP 开始指向最后一个元素而不是第一个元素。

  • 正如 Brendan 所说,您的混合字节顺序是非常规的,可能会使其他程序员感到困惑。颠倒顺序也会简化您的代码,因为您可以向前而不是向后遍历数组。

  • 当您可以简单地执行 ADC AX, [DI+4] 时,像 MOV BX,[DI+4] / ADC AX,BX 这样的序列是多余的。 CPU 的设计者为了让每条指令都可用于一个内存操作数而费尽周折;不妨好好利用一下。

  • LEA SI, X 可以用 MOV SI, OFFSET X 代替,后者做同样的事情,但少使用一个字节。其他 LEA 也一样,只是加载一个常量地址。

  • 请记住,使用 BP 的内存引用隐含地相对于堆栈段 SS。这对于 DSSS 相等的 SMALL 模型来说很好,但是如果你切换代码模型,你会想知道为什么你的代码没有写入 Result 即使BP“显然”指向它。

  • 您在循环中使用了四个不同的寄存器 (BP, SI, DI, CX),它们基本上都只是跟踪循环索引。看看你能不能把它减少到一个。您可以通过执行 MOV AX, [X+BX] MOV DX, [Y+BX].

    之类的操作,将单个寄存器用作所有数组的偏移量
  • 如果您最终要转向使用现代 x86 CPUs,LOOP 指令是一个坏习惯,因为它优化不佳并且不鼓励使用它.自己做递减和条件跳转实际上在这样的机器上更有效率,而且它还让你可以自由地使用任何寄存器而不是被绑定到 CX.

  • 同样,PUSHF/POPF 技巧也是一个坏习惯,因为这些指令在现代 CPUs 上相当昂贵,因为它们可能会操纵中断标志。尝试设计循环的其余部分,以便在迭代之间保持进位标志完好无损。 INCDEC 指令在这里很好,因为它们不修改它;像这样的循环正是它们被设计成那样的原因。

  • 大写您的助记符并始终如一地注册姓名。有些人喜欢全大写,有些人喜欢全小写,但没有人喜欢混合大小写。