Assembly movzx和cwd-它们可以互换吗?
我有以下两段代码片段:Assembly movzx和cwd-它们可以互换吗?,assembly,x86,Assembly,X86,我有以下两段代码片段: mov ax, word [wNum2] cwd div word [wNum3] mov [wAns16], dx 第一个生成正确的答案,第二个将给出100左右的答案,除非我取消注释cwd 我的问题是,我认为movzx会为我将一切归零,这将使cwd不再需要。我是否完全误解了它们的工作原理?有人能带我看一下这些代码片段吗?简单的结果可能是等价的,也可能是不等价的,这取决于值。对国家的描述 通过符号扩展将寄存器AX、EAX或RAX中的操作数大小加倍(取决于操作数大小),并
mov ax, word [wNum2]
cwd
div word [wNum3]
mov [wAns16], dx
第一个生成正确的答案,第二个将给出100左右的答案,除非我取消注释cwd
我的问题是,我认为movzx会为我将一切归零,这将使cwd不再需要。我是否完全误解了它们的工作原理?有人能带我看一下这些代码片段吗?简单的结果可能是等价的,也可能是不等价的,这取决于值。对国家的描述 通过符号扩展将寄存器AX、EAX或RAX中的操作数大小加倍(取决于操作数大小),并将结果分别存储在寄存器DX:AX、EDX:EAX或RDX:RAX中。CWD指令将AX寄存器中值的符号(位15)复制到DX寄存器中的每个位位置 因此,如果
AX
中的值低于32767(最大15位),则其结果相当于MOVZX
(零扩展)和MOVSX
(符号扩展)。但是,如果该值更大,它将只相当于MOVSX
。通常MOVZX
将与DIV
(无符号除法)结合使用,MOVSX
将与IDIV
(有符号除法)结合使用
但仍然存在将结果存储在何处的问题:CWD
将32位结果存储在两个16位寄存器DX:AX
,而MOV?X
指令将其存储在32位寄存器EAX
中
这会对以下说明产生影响。代码的第一部分使用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的更有效的方法。(如果已知输入在被视为有符号时是非负的,则符号和零扩展做相同的事情)
有一个cbw/cwde/cdqe和等效movsx指令表,以及cwd/cdq/cqo的功能(和等效mov/sar)
在无符号div
之前,这些东西都不是您想要的:使用xor edx,edx
将DX(32/16=>16位除法的高半输入)归零
另见
为了避免写入部分寄存器的错误依赖性,在最新的CPU上,最有效的方法是执行movzx加载,只将16位值加载到AX中,而不合并到以前的RAX/EAX值中。类似地,xor归零(通常)不被认为是部分寄存器上的一种归零习惯用法,因此即使只读取数据的下半部分,也需要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]
中的代码可能是一个bug,假设在您踩下它之后的任何东西之前只有2字节的空间。您可能应该提到,您通常希望在div
之前进行零扩展(即xor EDX,EDX
),并且只在idiv
之前进行符号扩展。和
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