assemblyx86divisioninstruction-set

MOVZX and CWD - Are they interchangeable?


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?


Solution

  • 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.