The DIV
instruction is a bit tricky, and to understand it, you need to look carefully at the documentation.
First, notice that your code is doing a 32-bit division. The way I know that is you have DIV ten
, and ten
is a DWORD (since you've declared it with DD
). A DWORD is 32 bits.
From the above-linked documentation, we can see that a 32-bit division divides EDX:EAX
by the operand (in this case, ten
). It stores the quotient in EAX
, and the remainder in EDX
.
Okay, so wait a minute—what is EDX:EAX
? This is a notation for a way to store a 64-bit value using two 32-bit registers. The high DWORD is in EDX
, and the low DWORD is in EAX
. Combine them together, and you get a 64-bit value.
Hopefully now you see why your solution of zeroing EDX
worked, and why you were getting the wrong result without doing it. The division was actually implicitly using the EDX
register as part of the dividend, so if it contains garbage, the result of the division will be wrong.
And now that you understand the problem, you can memorize this simple rule: whenever you do a 32-bit division, using the DIV
instruction, always pre-zero the EDX
register. The idiomatic and most efficient way to zero out a register is, of course, with the XOR
instruction.
Note that similar problem occurs with signed division (IDIV
), except that, in this case, you don't simply want to zero out EDX
. You need to extend the value in EAX
in a way that respects the sign bit. The CDQ
instruction does this for you: it sign-extends EAX
into EDX:EAX
, ready for an IDIV
. Another simple rule to learn—CDQ
should virtually always precede IDIV
.
A final bit of free optimization advice:
CMP EAX, 0
is equivalent to:
TEST EAX, EAX
but the latter assembles to fewer bytes of code (because it doesn't use an immediate operand) and is faster in many cases (both because of its smaller size and the fact that it is more likely to macro-fuse with the subsequent branch instruction, e.g. JE
).
So another rule (isn't assembly programming fun?): when you are testing to see whether a register is 0, use TEST
to bitwise-AND the register with itself.
The only time you would use CMP xxx, 0
is when you want to see if a memory location is 0 (without first loading it into a register).