【问题标题】:Assembly division by 0AH0AH 组装事业部
【发布时间】:2017-04-25 09:33:26
【问题描述】:

我在汇编 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 我应该得到 CCCF2 在 EAX 中。 为什么会这样?

【问题讨论】:

  • EDX 中的值是多少?
  • 我在循环之前将其归零
  • 我已经通过在循环中执行XOR EDX,EDX 来修复它,但为什么会发生这种情况?
  • 阅读div 的文档,上面写着。

标签: assembly hex


【解决方案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 位除法时,总是EDX 寄存器预置零.将寄存器归零的惯用且最有效的方法当然是with the XOR instruction


请注意,signed 除法 (IDIV) 也会出现类似问题,只是在这种情况下,您不想简单地将 EDX 归零。您需要以尊重符号位的方式扩展EAX 中的值。 CDQ instruction 为您执行此操作:它将EAX 符号扩展为EDX:EAX,为IDIV 做好准备。另一个需要学习的简单规则——CDQ 实际上应该总是在IDIV 之前。


最后一点免费的优化建议:

CMP EAX, 0

相当于:

TEST EAX, EAX

但后者组装成更少字节的代码(因为它不使用立即操作数)并且在许多情况下速度更快(这既是因为它的尺寸更小,而且它更可能与随后的分支指令,例如 JE)。

所以另一个规则(汇编编程不是很有趣吗?):当您测试一个寄存器是否为 0 时,使用TEST 将寄存器与自身进行按位与运算。

您唯一会使用CMP xxx, 0 的情况是您想查看内存 位置是否为0(无需先将其加载到寄存器中)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-29
    • 1970-01-01
    • 1970-01-01
    • 2020-10-22
    • 1970-01-01
    相关资源
    最近更新 更多