我有这两个代码片段:
mov ax, word [wNum2]
cwd
div word [wNum3]
mov [wAns16], dx
movzx eax, word [wNum2]
;cwd
div word [wNum3]
mov [wAns16], edx
第一个给出正确的答案,第二个给出的答案会相差一百左右,除非我取消注释
cwd
。
我的问题是,我认为
movzx
会将我的一切归零,这将使 cwd
变得多余。我是否完全误解了它们的工作原理?有人可以引导我完成这些代码片段吗?
裸露的结果可以是等价的,也可以是不等价的——这取决于值。 CWD状态
的描述通过符号扩展将寄存器 AX、EAX 或 RAX 中操作数的大小加倍(取决于操作数大小),并将结果分别存储到寄存器 DX:AX、EDX:EAX 或 RDX:RAX 中。 CWD 指令将 AX 寄存器中的值的符号(位 15)复制到 DX 寄存器中的每个位位置。
因此,如果
AX
中的值低于 32,767(15 位最大值),则其结果相当于 MOVZX
(零扩展)和 MOVSX
(符号扩展)。但如果该值更大,则only相当于MOVSX
。通常 MOVZX
与 DIV
(无符号除法)结合使用,MOVSX
与 IDIV
(有符号除法)结合使用。
但是仍然存在结果存储在哪里的问题:
CWD
将 32 位结果存储在两个 16 位寄存器 DX:AX
中,而 MOV?X
指令将其存储在 32 位寄存器 EAX
中。
DIV
指令产生影响。代码的第一部分使用 DX:AX
中的 32 位值作为输入,而第二种方法假设 EAX
是 16 位 DIV
的输入:
F7 /6 DIV r/m16 M Valid Valid Unsigned divide DX:AX by r/m16, with result stored in AX ← Quotient, DX ← Remainder.
这使得结果不可预测,因为
DX
未定义,并且 EAX
的上半部分在除法中未使用。
不,MOVZX 是零扩展,而不是符号。 CWD 将 AX 符号扩展为 DX:AX(就像您在 IDIV 之前想要的那样,而不是 DIV)。
movSx eax, word [wNum2]
是执行 mov ax,mem
+ CWDE 的更有效方法,而不是 CWD。 (如果已知您的输入在被视为有符号时为非负,则符号和零扩展会执行相同的操作)。
cltq 在汇编中做什么? 有一个 cbw/cwde/cdqe 和等效的 movsx 指令表,以及 cwd/cdq/cqo 做什么(以及等效的 mov/sar)。
在无符号之前,这些都不是你想要的
div
:使用xor edx,edx
将DX归零,DX是32/16 => 16位除法的高半输入。
另请参阅 我们何时以及为何使用 mul/div 来签署扩展和使用 cdq?
为了避免写入部分寄存器产生错误依赖性,在最新的 CPU 上,最有效的方法是执行 movzx 加载,将 16 位值放入 AX 中,而不合并到 RAX/EAX 的先前值中。类似地,异或归零(通常?)不被识别为部分寄存器上的归零习惯用法,因此即使您只想读取
的低半部分,您也需要 32 位操作数大小 movzx eax, word [wNum2] ; zere extend only to avoid false dep from merging into EAX
xor edx, edx ; high half dividend = DX = 0
div word [wNum3]
mov [wAns16], dx ; store remainder from DX, not EDX
您的代码将 32 位 EDX 存储到
[wAns16]
中可能是一个错误,假设在您踩到它后面的任何内容之前只有 2 个字节的空间。