I have these two code snippets:
mov ax, word [wNum2]
cwd
div word [wNum3]
mov [wAns16], dx
movzx eax, word [wNum2]
;cwd
div word [wNum3]
mov [wAns16], edx
The first produces the correct answer, the second will give me an answer that is a hundred or so off unless I uncomment cwd
.
My question is that I thought movzx
would zero everything out for me, and that would make cwd
redundant. Have I completely misunderstood how they work? Can someone walk me through these code snippets?
The bare result can be equivalent or not - that depends on the value. The description of CWD states
Doubles the size of the operand in register AX, EAX, or RAX (depending on the operand size) by means of sign extension and stores the result in registers DX:AX, EDX:EAX, or RDX:RAX, respectively. The CWD instruction copies the sign (bit 15) of the value in the AX register into every bit position in the DX register.
So if the value in AX
is smaller than or equal to 32,767 (15 bit MAX), the result of it is equivalent to MOVZX
(zero extend) and MOVSX
(sign extend). But if the value is bigger than 32,767 it would only be equivalent to MOVSX
. Usually MOVZX
would be used in combination with DIV
(unsigned division) and MOVSX
in combination with IDIV
(signed division).
But there remains the problem of where the result will be stored:
CWD
stores the 32-bit result in two 16-bit registers DX:AX
, while the MOV?X
instructions store it in the 32-bit register EAX
.
This has consequences on the following DIV
instruction. The first part of your code uses the 32-bit value in DX:AX
as input, while the second approach assumes EAX
to be the input of a 16-bit DIV
:
F7 /6 DIV r/m16 M Valid Valid Unsigned divide DX:AX by r/m16, with result stored in AX ← Quotient, DX ← Remainder.
which makes the result unpredictable, because DX
is undefined and the higher half of EAX
is unused in the division.