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()