MOVZX 和 CWD - 它们可以互换吗?

问题描述 投票:0回答:2

我有这两个代码片段:

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
变得多余。我是否完全误解了它们的工作原理?有人可以引导我完成这些代码片段吗?

assembly x86 division instruction-set
2个回答
6
投票

裸露的结果可以是等价的,也可以是不等价的——这取决于值。 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
的上半部分在除法中未使用。


3
投票

不,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 个字节的空间。

© www.soinside.com 2019 - 2024. All rights reserved.