22C:18, Lecture 26, Summer 1997

Douglas W. Jones
University of Iowa Department of Computer Science

A Large Example

Up to this point, we have looked fairly informally at how a person can hand translate high-level-language code to assembly language. The time has come to take a more formal look at this problem. We will look at this problem through the perspective of a very simple example language, the language of a simple RPN calculator. This calculator has the following commands:

E - enter;
push zero on the stack.

0-9 - any digit d;
multiply the top item on the stack by 10 and add d.

+- - operators;
combine the top two items on the stack using the indicated arithmetic operator.
We will assume that, after each input, the calculator prints the contents of its stack. The following example illustrates the operation of this calculator:
input: E
stack: 0
input: E
stack: 0 0
input: 123
stack: 0 123
input: +
stack: 123
input: E 12 E 123 + E 12 E 123 -
stack: 123 135 -111
input: +
stack: 123 24
A Pascal implementation of this calculator is trivial, assuming that the ends of strings are marked by a distinctive character, endofstring:
	{ global variables }
        const stacksize = 80;
        var stack: array[0..stacksize] of integer;
            sp: integer;

	procedure initstack;
	begin
	    sp := -1;
	end

	procedure calculate(var line: string);
        var i: 0..stringsize;
        begin
            i := 0;
            while s[i] <> endofstring do begin
                case s[i] of
                'E':begin
                        sp := sp + 1;
                        stack[sp] := 0;
                    end;
                '0'..'9':
                    stack[sp] := stack[sp] * 10
                                 + (ord(s[i]) - ord('0'));
                '+':begin
                        stack[sp-1] := stack[sp-1] + stack[sp];
                        sp := sp - 1;
                    end;
                '-':begin
                        stack[sp-1] := stack[sp-1] - stack[sp];
                        sp := sp - 1;
                    end;
                end;
		i := i + 1;
            end;
        end;

	procedure printstack;
        var i: 0..stacksize;
        begin
	    for i = 0 to sp do write( stack[sp] );
	    writeln;
	end;
The above routines do no error checking, something that should be remedied in any practical implementation! The above code rests on the following main program:
        program Calculator(input,output)
        var line: string;

        begin { main program }
	    initstack;
            repeat
		write('Input:');  { output a prompt }
                readln(line);
                calculate(line);
		printstack;
            forever;
        end;
The biggest problem in translating this program to the Hawk environment is that our low-level input/output routines do not follow the model of Pascal (or C), both of which were designed in the era of printing input/output devices. So, we must explicitly move the output position around on the display screen instead of relying on the "printer carriage" to keep track of the position. In the following version of the code, we have rewritten it in terms of the Hawk system's I/O routines.
        program Calculator(input,output)
        var line: string;
	    lineno: integer;

        begin { main program }
	    sp := 0;
	    lineno := 0
            repeat
		DSPAT(lineno, 0);
		lineno := lineno + 1;
		DSPST('Input:');  { output a prompt }
                KBDGETS(line);
                calculate(line);
		DSPAT(lineno, 0);
		lineno := lineno + 1;
		printstack;
            forever;
        end;
Let's look at the printstack routine before continuing to look at the main program. This routine contained the code:
	    for i = 0 to sp do write( stack[sp] );
Note that this requires that the array addressing for stack[sp] be re evaluated with each iteration of the loop. C (and C++) allows this to be avoided by rewriting the loop as follows:
	    for (i = &stack[0]; i < &stack[sp]; i++ ) write( *i );
In English, this says, "initialize i to the address of stack[0], and then, so long as i is less than the address of stack[sp], write the integer that i points to and then increment i by the size of one integer." Note that the operator ++ in C does not mean increment by one in this context, it means increment by the size of one integer!

We can make one further improvement. Instead of declaring sp as an integer and initializing it as -1, we can declare sp as a pointer to an integer and initialize it as &stack[-1].

This lets us rewrite the above for loop as:

	    for (i = &stack[0]; i < sp; i++ ) write( *i );
Before finishing the translation of printstack to assembly language, it is important that we agree on a representation of the stack. We will use the following declarations for this:
	; include file rpnstack.h

STACKSIZE = 20	; size of the stack

	COMMON	RPN,STACKSIZE+4
PRPN	W:	RPN

; PRPN points to a record with the following format
RPNSP	=	0	; the stack pointer in the RPN stack
RPNSTK	=	4	; the first word of the stack
Our convention is that RPNSP points to the current word on the stack top, so we can initialize the stack as follows:
INITSTK:
	LOAD	R3,PRPN
	LEA	R4,R3,RPNSTK-4	; the address of stack[-1]
	STORE	R4,R3,RPNSP	; initialize the stack pointer
	JUMPS	R1
Now, we are ready to rewrite printstack in assembly language:
PRINTSTACK:	; procedure to print the stack
; 	Activation record format
;RA =	0	; return address
INDEX = 4	; loop index pointer
LIMIT = 8	; loop limit pointer
AR    = 12	; activation record size

	; no parameters, no returned result, wipes out R3-7

	STORES	R1,R2		; save return address
	LOAD	R3,PRPN
	LEA	R4,R3,RPNSTK	; the index is address of stack[0]
	LOAD	R5,R3,RPNSP	; the address of stack[sp]
	STORE	R5,R2,LIMIT	; save loop limit
PRLP:
	CMP	R4,R5		; check if loop done
	BGT	PRLPQ		;   quit if so

	LOADS	R3,R4		; get contents of stack[index]
	STORE	R4,R2,INDEX	; save loop index while printing
	LIS	R4,1
	ADDI	R2,AR		; push AR
	CALL	DSPDEC		; output the number
	LIS	" "
	CALL	DSPCH		; output blank
	ADDI	R2,-AR		; pop AR
	LOAD 	R4,R2,INDEX	; recover loop index
	LOAD 	R5,R2,LIMIT	; recover loop limit

	ADDSI	R4,4		; increment loop index
	BR	PRLP		; continue loop
PRLPQ:
	LOADS	R1,R2		; recover return address
	JUMPS	R1		; return