我在汇编386工作,我在一个循环中继续将数字除以0Ah我一直得到一个我无法理解的结果是我的代码:
MOV EAX,94Ch
MOV ten,0Ah;ten is of size DD that i've defined
XOR ECX,ECX
L1:
CMP EAX,0;check if the number is zero
JE somewhere
DIV ten
INC CL
JMP L1
当 EAX 中 17h 并且我除以 0Ah 时,我得到 CCCF 当我应该得到EAX中的 2 。 为什么会那样?
答案 0 :(得分:1)
DIV
指令有点棘手,要理解它,您需要仔细查看the documentation。
首先,请注意您的代码正在执行32位除法。我知道的方式是你有DIV ten
,而ten
是一个DWORD(因为你用DD
声明了它)。 DWORD是32位。
从上面链接的文档中,我们可以看到32位除法将EDX:EAX
除以操作数(在本例中为ten
)。它将商存储在EAX
中,余数存储在EDX
。
好的,等一下 - 什么是EDX:EAX
?这是使用两个32位寄存器存储64位值的方法的表示法。高DWORD位于EDX
,低DWORD位于EAX
。将它们组合在一起,就可以获得64位的值。
希望您现在明白为什么您的归零EDX
解决方案有效,以及为什么没有这样做会得到错误的结果。除法实际上隐含地使用EDX
寄存器作为被除数的一部分,因此如果它包含垃圾,则除法的结果将是错误的。
现在您已经理解了这个问题,您可以记住这个简单的规则:每当使用DIV
指令进行32位除法时,总是将{{ 1}}注册。归零寄存器的惯用且最有效的方法当然是with the XOR
instruction。
请注意,签名除法(IDIV
)会出现类似的问题,但在这种情况下,您不需要将EDX
清零。您需要以尊重符号位的方式扩展EDX
中的值。 CDQ
instruction为您执行此操作:它将EAX
签名延伸到EAX
,为EDX:EAX
做好准备。要学习的另一个简单规则 - IDIV
实际上应始终位于CDQ
之前。
最后一点免费优化建议:
IDIV
相当于:
CMP EAX, 0
但是后者组装到更少的字节代码(因为它不使用立即操作数)并且在许多情况下更快(因为它的尺寸较小以及它更可能与宏的融合后续分支指令,例如 TEST EAX, EAX
)。
那么另一条规则(汇编编程不是很有趣吗?):在测试寄存器是否为0时,使用JE
按位与自身进行比较。
您希望查看内存位置是否为0(不先将其加载到寄存器中)时,唯一一次使用TEST
。