Advance Procedures

Let's think a little bit more about procedures and functions, and their implementation. Suppose we have a recursive function to compute a factorial:


int fact(i) {
    int retval;

    if (i == 1)
        retval = i;
    else
        retval = i*fact(i-1);

    return(retval);
}

(as usual, this example is contrived. Recursion is a stupid way to implement factorial; the only reason I'm using the local variable retval is so I need to allocate space for a local variable on the stack.)

Whenever we call a function we need to create a data structure to hold its data, called an activation record. Three things need to go into this record:

In the case of fact, we have one parameter (i) and one local variable (retval). We'll see what state-restoring information we need as we go....

We stack the activation records (so we need a stack pointer). Notice that we can't use the stack pointer for indexed addressing, so we also have to have one of the index registers pointing to the current activation record. This is referred to as a frame pointer.

We have a number of registers and instructions to help us manipulate this stack. First, there is a stack pointer register (sp). This register always points to the next location we'll be pushing to (so it doesn't quite point to the top of the stack). There are a number of instructions that either manipulate or make use of sp. Some of them are

lds
Load sp
psha, pshb, pshx, pshy
Push a register on the stack
pula, pulb, pulx, puly
Pop a register from the stack
tsx, tsy
Add one to sp and copy it into a register
txs, tys
Subtract one from a register and copy it into sp
jsr, rts
These are very important: jsr pushes a return address and jumps; rts pops a return address and goes to the return address (loads the PC with it).

The function call itself is a partnership between the procedure doing the calling, and the procedure being called. Each has to execute code involved in setting up an activation record, transferring control, and deleting the activation record. Of the components of an activation record defined above, we can get a picture for who does what based on asking who knows what.

Using factorial as an example, how do we do this?

  1. push parameters
  2. jsr
  3. reserve space on the stack for local variables. If we don't need much space, we can do this by a sequence of des instructions or clear an accumulator and push it a sequencial number of times on the stack.
  4. set up new frame pointer. We can do this with a tsx
  5. push onto the stack any registers we need to save in case they are corrupted during any new procedure calls.
Once things are set up, we can execute the procedure. After we're done, we can
  1. pop any registers we pushed, in reverse order
  2. get rid of space on the stack for local variables. For a small number of them, we can do this with some ins instructions.
  3. return
  4. pop the parameters; using pula, pulb, etc.

The actual HC11 code for the factorial function and program follows:


RAM     equ   $0000
STACK   equ   $00ff
EEPROM  equ   $f800
RESET   equ   $fffe
TVAL    equ   5


     org   RAM
oval rmb   1

     org   EEPROM

****
* Function Fact
*   - computes the factorial of the input
*
****
fact des               * local variable
     tsx               * set the frame pointer
if
     ldaa  4,x         * read input parameter
     cmpa  #1          * if (i == 1)
     bne   else
then
     staa  0,x         *   retval = i
     bra   ret
else
     psha              * else {
     pshx              *   save needed registers
     deca
     psha              *   put out new parameter fact call
     des               *   save place for return
     jsr   fact        *   call fact(i-1)
     pulb              *   get return
     ins
     pulx              *   restore registers
     pula
     mul               *   retval = i*fact(i-1)
     stab  0,x         * }

ret  ldab  0,x         * return( retval )
     stab  3,x
     ins               * remove local variables
     rts

****
*  Main program
*    - test the factorial function
****
main lds   #STACK      * setup stack for use
     ldab  #TVAL       * let's get factorial for the test value
     pshb
     des
     jsr   fact        * oval = fact(TVAL)
     pulb
     stab  oval        
     ins

end  bra   end

* setup the reset vector
     org   RESET
     fdb   main