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

z6psavjg  于 2023-11-19  发布在  其他
关注(0)|答案(2)|浏览(155)

我有两个代码片段:

mov   ax, word [wNum2]
cwd
div   word [wNum3]
mov   [wAns16], dx

个字符
第一个产生正确的答案,第二个将给我一个给予答案,这是一百左右,除非我取消注解cwd
我的问题是,我认为movzx会为我清零所有内容,这将使cwd变得多余。我是否完全误解了它们的工作原理?有人能告诉我这些代码片段吗?

qncylg1j

qncylg1j1#

裸结果可以是等价的,也可以不是--这取决于值。
通过符号扩展将寄存器AX、EAX或RAX(取决于操作数大小)中的操作数大小加倍,并将结果分别存储在寄存器DX:AX、EDX:EAX或RDX:RAX中。CWD指令将AX寄存器中的值的符号(位15)复制到DX寄存器中的每个位位置。
所以如果AX中的值小于或等于32767(15位MAX),其结果相当于MOVZX(零延伸)和MOVSX(sign extend)。但如果该值大于32,通常MOVZXDIV(无符号除法)结合使用,MOVSXIDIV(有符号除法)结合使用。
但仍存在将结果存储在何处的问题:
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的高半部分在除法中未使用。

tp5buhyn

tp5buhyn2#

不,MOVZX是零扩展,而不是符号。CWD符号扩展AX到DX:AX(就像你在IDIV之前想要的那样,而不是DIV)。
movSx eax, word [wNum2]是执行mov ax,mem + CWDE 的更有效的方法,而不是CWD。(如果您的输入在被视为有符号时是非负的,则符号和零扩展做同样的事情)。
What does cltq do in assembly?有一个cbw/cwde/cdqe和等效的movsx指令的表,以及cwd/cdq/cqo做什么(和等效的mov/sar)。

**在无符号div**之前,这些都不是你想要的:使用xor edx,edx将DX置零,32/16 => 16位除法的高半输入。

参见When and why do we sign extend and use cdq with mul/div?
为了避免写入部分寄存器时产生的假依赖,在最新的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]中可能是一个错误,假设在您访问它之后的任何内容之前只有2个字节的空间。

相关问题