All high level language programmers are familiar with the concept of subroutines and procedures. Here's an example from C:
#include
int fact(int);
main(int argc, char **argv)
{
int arg, ans;
if(argc < 2)
{
printf("Please re-enter with an
integer on the CL\n");
exit(0);
}
arg = atoi(argv[1]); /* convert command line arg to
integer */
ans = fact(arg); /* get it's
factorial */
printf("%d! = %d\n", arg, ans); /*
display the answer */
}
int fact(int num)
{
/* calculate factorial of num */
int ans = num;
if (num ==
0) /* 0!
is defined as 1 */
return(1);
else
while (num >
0) /* n! = n * (n - 1)! */
ans *=
--num; &nbssp; /* same as ans = ans * (num - 1) */
return(ans);
/* return answer */
}
In the above example the subroutine (or procedure) is fact(int). We could have put the fact code in the main routine, but by seperating it out we can now cut and paste the code into another program that uses factorial calculations; code re-use. Another way we could re-use the code is to put the fact subroutine (called functions in the C language) into a "library" of routines, and any program that needs to use fact can just call it up from that library. In MACRO-32, the way we used fact in the example, coul've been accomplished wih a subroutine. The library concept leads to procedures in MACRO-32.
Could've been accomplished by a subroutine, because the above example uses the same calling mechanism as MACRO-32 procedures. A subroutine is just what the name implies; a routine that's right below the main routine. In other words, a subroutine is by definition part of the same program as the calling routine; BASIC programmers are familiar with the GOSUB statement as a subroutine. A BASIC programmer calls a subroutine by making a GOSUB statment aimed at a label in his program; the label that marks the beginning of the subroutine. A RETURN statement returns the program to the line of code right after the GOSUB statment.MACRO-32 has a mechanism similar to BASIC's GOSUB statement. Such a mechanism must:
Steps 1. and 2. are accomplished with one of three statements:
These three statements all store the address of the statement after themselves on the stack. Actually, what they do is:
At the end of the subroutine is a statement, RSB (Return from SuBroutine). RSB POPs that stack into the PC. Notice we don't mess with the stack, except for storage and retreival of return addresses. If we add anything else to the stack, or pop anything else off of it, we run the risk of destroying our return mechanism. Therefore we pass arguments to the subroutine through registers, or global variables (just like in BASIC GOSUBs). ...and like GOSUBs this is very neat and efficient.
Getting back to our C example with a factorial procedure: What if we want to stick this factorial function somewhere on the system where other programs can access it as well? Obviously the above mechanism won't work anymore. What we need is a mechanism that allows two programs to communicate with each other. The two programs in question are invoked as one program, and therefore share the same program stack. So we can have our main procedure
An example is:
NUM: .BLKL
.ENTRY BEGIN ^M<>
MOVL #10, NUM ; we want 10!
PUSHAL NUM ; number to get factorial value of
CALLS #1, FACT ; #1 is a literal, telling the procedure there is 1 val on the stack
POP NUM ; put answer back into NUM
MOVL #SS_NORMAL, 0 ; we always get a good return
RET
;
.ENTRY FACT ^M
MOVL 4(AP), R2 ; don't pop since we will return on the stack
MOVL R2, R3 ; have to save the original value of num
$10: DECL R3 ; num! = num * (num -1)!
BEQL 20$ ; PSL value Z = 1, means R3 = 0
MULL R3, R2 ; R2 = R3 * R2
BRB $10 ; short jump
$20: MOVL R2, 4(AP) ; return answer
RET
.END BEGIN
We move the answer back into the same place in the stack, because that's where the calling routine expects it; we could have done this differently. However, the way we did this required we popped the stack on returning. Note that the RET instruction moves R13 (also called FP) into the PC (which is R15); the address of the next instruction before making a call.
There's another way to call, CALLG, but I'll leave that for another VIOTW.