S2 calculator engine in S2 assembly
language
The engine now is working. You can see all the details in the
source. It includes the main program (S2 simulator) in C and the
shell which call the "interpreter" in S2 assembly language to run the
calculator. The package is s2cal.zip
The main function to run the calculator in C is "mainCal()" (in s2c.c)
which is a simple execution loop. The interpreter file (in S2 assembly
language) is "s2cal.txt". You can inspect
how I write the calculator engine.
The first part of eval() tests the token (the main loop is already read
one token and put it into r1) and jumps to the corresponding
routines. The first line "push r31 r29" saves the link register
to the system stack in order to allow recursive call of eval(). Eval()
calls evalnext() and evaln() which will call eval() itself.
Before return, eval() must pop the link register. Two trap
functions are introduced:
- trap 3 read a token from the
inputline, returns the token (1..18) in r1
- trap 4 do load object
file. I will not explain its detail here. Please refer to
the C source.
Here is the eval function of the calculator:
;;
register convention
;; r31 stack pointer
(system)
;; r30 display
;; r29 link register
;; r28 return value
;; r27 evaluation stack
(calculator)
:eval
;; assume one token in r1
push r31
r29 << -- save link register
eq r2 r1 #tkNum
jt r2 xNum
eq r2 r1 #tkAdd
jt r2
xAdd << -- jump to
action routine
eq r2 r1 #tkSub
jt r2 xSub
eq r2 r1 #tkMul
jt r2 xMul
eq r2 r1 #tkDiv
jt r2 xDiv
eq r2 r1 #tkF0
jt r2 xF0
...
:return
pop r31
r29 << -- restore link
register
ret r29
The routine to handle each operator (such as add) will evaluate the
next token by calling read() (trap 3) and eval it, then applies the
operator. Here is the example of "add". Add is an infix operator, the
calculator has already reads one argument and its value has already
been on the evaluation stack (pointed by r27). The action of xAdd
is to read and evaluate next token and do "add". The evalnxt()
does the read and evaluate the next token. The call to fAdd "jal
r29 fAdd" calls the routine to perform the "add" operation.
:xAdd
;; get another arg then add
jal r29 evalnxt
jal r29 fAdd
push r27
r28 ;; push result
jmp return
:evalnxt
;; read and eval next token
push r31 r29
trap
3 <<
-- read a token, return value in r28
mv r1
r28 << -- put token to
r1
jal r29
eval << -- recursive call to eval()
pop r31 r29
ret r29
:fAdd
push r31 r1
push r31 r2
pop r27 r2
pop r27 r1
add r28 r1 r2
pop r31 r2
pop r31 r1
ret r29
"fAdd" is written according to the rule of writing a module for the
calculator:
1) It does not use any global
variable.
2) It uses only the registers.
3) It saves and restores all registers it used.
4) The operator takes arguments from the evaluation stack
(pointed by r27).
5) It returns the result in r28.
The calculator itself takes care of scanning the input line, providing
the arguments to the operator (in the evaluation stack), and pushing
the result to the evaluation stack (so that the next operations can
continue correctly). The "calculation" (or user-defined) routine
performs the operation.
The dynamic load the special function object (f0.obj, f1.obj
...) is now working (10 Feb 2007). One example is included in the
S2 calculator package (f1.obj). It is implemented as an identitfy
function. It just returns its argument.
How to run the calculator
Try the following:
c:>cal
>33 + 22 - 1 =
54
>ld f1.obj
>f1 10 =
10
>20 + f1 11 =
31
>
The basic four functions (+ - * /) are working. Here is how the
function f1 for demonstration is written:
;; f1.txt -> f1.obj
.code 1000
:f1
;; identity function
pop r27 r28 ;;
move from eval stack to r27
ret r29
.end
Please note
You don't have to understand how the calculator work in order to submit
your project. You can write your special function without knowing
the detail of the calculator. You just follow the "protocol" (or the
rule) that I describe in the document (How to write assembly language
for special functions, in the webpage "project2"). I explain all these
details for educational purpose.
last update 10 Feb 2007