30 August 2009

7. Subtraction and Division

These are easy. The instruction to subtract on x86 is sub. It is used like the add instruction. The only difference is that with subtraction, the order of the operands matters.
10-3 <> 3-10

The x86 instruction idiv is rather clunky. The format of a division is dividend/divisor. idiv requires that the dividend is twice as large as the division. So if the divisor is a long, then the dividend must be a quad. And also, the dividend must reside in eax. Now eax doesn't actually hold a quad, so in this case, idiv requires that one part of the quad is in edx and the other part is in eax!
The result is always placed in eax. Additionally, the remainder is placed in edx.
Also we have to be sure to get the order of the operands correct here as well, since 10/2 is not the same as 2/10.

Let's make a new grammar with the new operators:

mulop ::= * | /
addop ::= + | -
mulexp ::= <value> {<mulop> <value>}
addexp ::= <mulexp> {<addop> <mulexp> }
expression ::= <addexp>

As you can see, + and - have the same precedence, so does * and /.

Make a copy of mul.pb (call it subdiv.pb). Adding subtraction to the code is done like this. I also replaced all occurences of ecx with ebp to make the generated assembly code easier to read. Ecx looks too much like eax. Contrary to what some people believe, ebp is not some kind of special register. It is a general-purpose register just like ecx.
Procedure AddExp()
; addexp ::= <mulexp> {<addop> <mulexp> }
Protected Operation.c
MulExp()
While Look = '+' Or Look = '-'
Operation = Look
GetLookWhite()
Emit("push eax")
MulExp()
Emit("pop ebp")
Select Operation
Case '+': Emit("add eax, ebp")
Case '-': Emit("sub eax, ebp")
Emit("neg eax")
EndSelect
Wend
EndProcedure

We store the operator in a variable to preserve it across the calls to GetLookWhite() and Mulexp(). Then we negate the result after subtracting to get the correct sign. We have to do this because we are subtracting ebp from eax instead of the other way around (which is what we really want).
We could replace
sub  eax, ebp
neg eax

with
sub  ebp, eax
mov eax, ebp

but simply negating the result is faster.

Try the code now. Mix addition, subtraction and multiplication, and test the resulting code to see if it's correct.

Now for division. Here is the code:

Procedure MulExp()
; mulexp ::= <value> {<mulop> <value>}
Protected Operation.c
Value()
While Look = '*' Or Look = '/'
Operation = Look
GetLookWhite()
Emit("push eax")
Value()
Emit("pop ebp")
Select Operation
Case '*': Emit("imul eax, ebp")
Case '/': Emit("xchg eax, ebp")
Emit("cdq") ; expands eax to edx:eax
Emit("idiv ebp")
EndSelect
Wend
EndProcedure


With division, we also get the operands in the reverse order. However, we can't just negate the result. While -(10-2) equals 2-10, -(10/2) does not equal 2/10. So we have to exchange the operands before doing the division. We do that with the xchg instruction.
I should mention here that you must never use the xchg instruction with a memory operand (unless you are coding a mutex). This is because xchg with memory "asserts a bus lock". This just means it's very slow. So multiple mov instructions are faster. But with registers only, it's fine.
The cdq (convert dword to quad) instruction makes a quad out of the value stored in eax. Or technically, it sign extends eax to edx:eax. Which becomes the input of the idiv instruction.
If you ever find yourself doing idiv edx, you're doing it wrong. Because edx:eax is already the input, so you'll get a nonsense result.
Finally, idiv ebp divides edx:eax by ebp and leaves the result in eax. Also, it leaves the remainder of the division in edx. (Example: the remainder of 3/2 is 1. And of 5/2, also 1.)

The operator for getting the remainder is called modulo, and is usually % or mod. PB uses %, so we'll do that, too.

Here is the full code, including addition, subtraction, multiplication, division and modulo:
; subdiv.pb
IncludeFile "..\base2.pb"

Procedure Value()
; value ::= <integer>
Emit("mov eax, " + GetInteger())
EndProcedure

Procedure MulExp()
; mulexp ::= <value> {<mulop> <value>}
Protected Operation.c
Value()
While Look = '*' Or Look = '/' Or Look = '%'
Operation = Look
GetLookWhite()
Emit("push eax")
Value()
Emit("pop ebp")
Select Operation
Case '*': Emit("imul eax, ebp")
Case '/', '%'
Emit("xchg eax, ebp")
Emit("cdq") ; expands eax to edx:eax
Emit("idiv ebp")
EndSelect
If Operation = '%'
Emit("mov eax, edx")
EndIf
Wend
EndProcedure

Procedure AddExp()
; addexp ::= <mulexp> {<addop> <mulexp> }
Protected Operation.c
MulExp()
While Look = '+' Or Look = '-'
Operation = Look
GetLookWhite()
Emit("push eax")
MulExp()
Emit("pop ebp")
Select Operation
Case '+': Emit("add eax, ebp")
Case '-': Emit("sub eax, ebp")
Emit("neg eax")
EndSelect
Wend
EndProcedure

Procedure Expression()
; expression ::= <AddExp>
AddExp()

If Look <> 0
Expected("End of line")
EndIf
EndProcedure

Init()
Expression()
Input()

No comments: