Page 302 - Introduction to Microcontrollers Architecture, Programming, and Interfacing of The Motorola 68HC12
P. 302
9,5 Procedure Calls and Arguments 279
9.5 Procedure Calls and Arguments
A C procedure is generally called using a JSR orBSR instruction, with the input
arguments pushed on the stack. The return value of a function is generally left in
accumulator D. If the input argument is a vector, or an "&" sign appears before the
name, then the address is passed on the stack, using call-by-name; otherwise the data
itself is pushed on the stack, using call-by-value. However, the rightmost argument is
passed into a function through a register. The function might push this register value
inside it, as a local variable. Passing one argument this way improves efficiency, because
even if it is pushed on the stack inside the function itself, its code therein appears just
once in a program, rather than each time the subroutine is called. As an example, the
procedure power can be called by main( ) in Figure 9.14a. Figure 9.14b shows the
calling procedure's assembly language.
Figure 9.15 shows the stack within the procedure that was called; its assembly
language is shown Figure 9.14c. The while loop requires the test at the end of the loop
and a branch at the beginning of the while loop to that test program sequence. Observe
from Figure 9.14c that call-by-value argument j is generally in accumulator A. The test
requires checking the argument j before it is decremented, so the instruction PS HA
saves j, the DEC instruction decrements j, and the BNE instruction tests the value
obtained by the LDAB 1, SP+ instruction.
The EMUL instruction multiplies the value in D by the value in Y. We passed the
address of argument i to power, merely to show how call-by-name can be handled. It
was pushed on the stack. Note from Figure 9.15 that this address is at 2 , SP. The int
value at that location can be read into index register Y by LDY 2, SP LDD 0, Y and
the other multiplier, a local variable, is read into Y by the TFR X, Y. The data are
multiplied and the result stored in the local variable using TFR D, X. Note that the final
returned value is passed in accumulator D.
It is also possible to pull the return address and deallocate the procedure's arguments
at the end of the procedure before returning to the main program. This is similar to the
passing of the rightmost argument in a register. In some sense these optimization
techniques are just minor modifications. However, they can improve static efficiency. If a
procedure is called from ten different places in the main program, then putting push and
pull instructions within the called procedure removes these instructions from ten places
in the calling sequence and puts only one copy in the called procedure. Moreover, the
technique of putting the first input argument in accumulator D works especially well for
small procedures with only one argument; we may not need to save the argument on the
stack at all, merely use the value in accumulator D. However, the last technique of
pulling the program counter and balancing the stack inside the called procedure has a
significant limitation. It is not possible to have a procedure with an arbitrary number of
arguments when the called procedure removes the same number of bytes from the stack
whenever it is called. The C printf procedure allows an arbitrary number of arguments,
so it would not be able to pull the program counter and balancing the stack inside it.