Computer Systems Org I - Prof. Grishman

Lecture 22 - Nov. 22, 2004

Putting C and LC-3 together:  generating code for functions with pointers

In this class and the next, we will consider how C functions involving pointers would be translated into LC-3 code.

In the textbook, P&P describe conventions for passing parameters which are typical for C implementations, in which the parameters are placed on the stack before calling the function.  This allows for an unlimited number of parameters.  For our examples, we will use a simpler calling convention, in which the parameters are passed in registers.  If there is one parameter, it is passed in R1;  if two parameters, in R1 and R2, etc.  Furthermore, we will agree that, if the function returns a value, it is returned in R0.

Function calls are made using the JSR (jump to subroutine) instruction.  This saves the return address in R7;  when the function finishes, it returns with a RET (return) instruction, which simply jumps to the address in R7.

The identity function

We will start with a function which takes one argument and returns it, just to see how function call and return work.

    int a, b;
    main () {
       b = 5;
       a = dup(b);
    }
    int dup (int x) {
       return x;
    }


in LC-3 assembler:

         .ORIG x3000
main     AND   R1, R1, 0        ; b = 5
         ADD   R1, R1, 5
         ST    R1, b
         JSR   dup              ; a = dup(b)  [note value of b is already in R1]
         ST    R0, a
         HALT
         ; function dup (x)
dup      ADD   R0, R1, 0        ; return value = x
         RET
a        .FILL 0                ; static variables
b        .FILL 0
         .END

The dereferencing function

Next we will try something just a tiny bit more complex:  it is passed the address of a variable, and returns its value.  This will be our simplest example of pointer manipulation.  Dereferencing (getting the contents of a memory location, given its address) is implemented by the LDR instruction.

    int a, b;
    main () {
       b = 5;
       a = deref(&b);
    }
    int
deref (int* x) {
       return *x;
    }


in LC-3 assembler:

         .ORIG x3000
main     AND   R1, R1, 0        ; b = 5
         ADD   R1, R1, 5
         ST    R1, b
         LEA   R1, b            ; a = deref(&b)
         JSR   deref
         ST    R0, a
         HALT
         ; function
deref (x)
deref    LDR   R0, R1, 0        ; return value = x
         RET
a        .FILL 0                ; static variables
b        .FILL 0
         .END

The swap function

Now we will consider our first 'real' C example ... the swap function we did when we introducted pointers, discussed on page 432 of P&P.  Note that dereferencing on the left side of an assignment (storing into a memory location, given its address) corresponds to the STR instruction.

    int a, b;
    main () {
       a = 5;  b = 10;
       swap (&a, &b);
    }
    void
swap (int* x, int* y) {
       int temp;
       temp = *x;
       *x = *y;
       *y = temp;
    }


in LC-3 assembler:

         .ORIG x3000
main     AND   R1, R1, 0        ; a = 5
         ADD   R1, R1, 5
         ST    R1, a
         ADD   R1, R1, 15       ; b = 20
         ST    R1, b
         LEA   R1, a            ; swap(&a, &b)
         LEA   R2, b
         JSR   swap
         HALT
         ; function swap
(int* x, int* y)
         ;                        on entry
         ;                        R1 = x = &a
         ;                        R2 = y = &b
swap     LDR   R3, R1, 0        ; R3 = *x = a
         LDR   R4, R2, 0        ; R4 = *y = b
         STR   R3, R2, 0        ; *y = *x [b = a]
         STR   R4, R1, 0        ; *x = *y [a = b]
         RET
a        .FILL 0                ; static variables
b        .FILL 0
        
.END