In order to understand the calling sequence, a few words must be said first about the format of the stack.
The stack pointer (sp = AR7) points to a word containing the stack pointer of its caller. Immediately before this word are two words of linkage (at offsets -2, and -1) that are used by routines CALLED by the current routine. These three words (offsets -2, -1 and 0 from the stack pointer) are collectively referred to as the "linkage area".
Arguments to a routine are located at positive offsets from the stack pointer. The first argument is at offset 1, the next at offset 2, and so on. Furthermore, each successive argument may be located by adding one (or whatever the length of the argument) from a pointer to the previous argument (i.e. they occupy increasing core addresses.) If the argument requires double word alignment, a word of filler may be allocated.
Following the arguments are N words of display, where N is the nesting level of the routine. The display is double word aligned. Following the display are local variables and compiler temporary variables. After this is the space that will be used as a linkage area by called routines. This is followed by the space where arguments to these routines are placed. There may be more compiler temporaries after this, and finally the stack frame ends with another "secondary linkage area".
Thus the stack space required by a routine is given by
Extra padding words are added, if needed, before any items requiring even or odd word alignment. The stack frame length is always rounded up to an even number.
The offset to the linkage area of the callee is not fixed and varies with the amount of temporary space currently in use. This is most obvious when a function must be called while the arguments to another function are being evaluated. In this case, the space occupied by already evaluated arguments is considered to be active temporary, and the arguments and linkage for the second call are placed after this. The stack frame size calculation is done for the maximum stack usage.
The stack pointer is stored in address register AR7. It is often referred to simply as "sp". It points to the stack frame of the currently active routine. The stack grows upward away from zero. AR7 always contains an odd value, so argument 1 is aligned on an even word boundary.
For a call, the arguments are computed and stored in the argument area, except for the first two arguments (or first two words of arguments if the first argument is longer than one word). The first two words are passed in the A and Q registers, although space is allocated in the argument area for the callee to save them. During the call sequence, ar7 is advanced to point to the linkage area preceding the arguments of the called routine, and the previous contents of the stack pointer are stored in that linkage area.
Note therefore, that the stack frame of the callee overlaps that of the caller from the argument space on.
A standard call to a Pascal routine "x" which has three arguments, whose values are given by "a", "b" and "c", could be written
lda c,,ar7 Fill in args sta m+3,,ar7 ldq b,,ar7 lda a,,ar7 eax7 display,,ar7 Load display pointer tsx1 x zero m,n
where "n" gives the number of words of arguments passed to "x", (in this case, n=3), and "m" (which is always even) gives the amount to advance the stack pointer to point to the new stack frame. That is, "m" is the amount of stack that is private to the caller, not overlapped with the next frame.
At the entry to any Pascal routine, the routine must execute some stack setup code, to reserve sufficient stack for it to run in, and to save the previous environment. As this code is invariant, it has been arranged that it be called as a small subroutine from the Pascal routine. The amount of stack space required varies of course from routine to routine, so a word containing the stack frame size is placed following the call to the entry routine. The frame size is always an even number of words. The value must be at least eight (8) to allow for a display, and the primary and secondary linkage areas.
The entry routine is called ".entry" and is invoked as follows.
symref .entry x tsx0 .entry zero fsize,dpoint
where "fsize" is the number of words of stack required and "dpoint" is a pointer (possibly zero) to optional debug information. Normally, "fsize" is not used by the entry routine, but a pointer to it is stored in the linkage area. It is used together with the current stack pointer to find the true end of the stack. This may be used to check for stack overflow, to initialize the stack after the arguments for debugging purposes, or by an asynchronous operation such as the break handler to find some free stack space to work with.
The called function must also make a display describing its active scope. On entry, X7 will contain a pointer to a copy of the display for the statically enclosing procedure. Except in the case of a passed procedure, this will be the stack frame of the caller. The new display may be produced as follows.
awdx ,x7,ar0 Point to enclosing stack frame mlr (1),(1) Copy display adsc9 0,,n*4-4,ar0 adsc9 d,,n*4-4,ar7 sar7 d+n-1,,ar7 Put current frame into display
where "n" is the nesting level of the procedure, and "d" is the offset to the callee's display. In the case where "n" equals one (i.e. there is no enclosing procedure) the copy code (awdx, mlr) is not generated.
To leave a function normally and return to the calling routine, another small routine is invoked. It is called ".retrn". This pops the stack and restores the values of various registers. It is invoked by
symref .retrn tra .retrn
If there is to be a return value, it is loaded into the Q register (if integer), the AQ register (if double-word integer), or the EAQ if floating-point.
If the procedure exit is via a goto, the following code is generated.
lda d+n-1,,ar7 Get stack pointer from display eaq label Location to jump to tsx0 .goto Routine to unwind stack zero m,2
where "n" is the nesting level of the procedure containing the label, and "d" is the offset to the display of the current procedure. It would be possible to just load the stack pointer and jump to the label, but this would not spring traps set to do such things as closing tempfiles opened in between.
Throughout the above discussion only one-word objects have been considered. Objects that occupy several words are handled similarly; the offset for the following object is simply larger by the appropriate amount. The same comment applies to double-precision objects (which must be properly aligned).
Since the offset from an address register is only a 15 bit field, this imposes a limit of 16384 words on the size of a stack frame. Therefore the sum total of all the variables, temporaries and arguments needed by a procedure or a function must be less than this. If larger arrays are needed, they must either be statically allocated globally, or allocated dynamically via NEW.
The Pascal stack is compatible with the stack frame used by the B language. For a more complete discussion of the stack, including the use of the secondary linkage area, see "expl b environment".
Copyright © 1996, Thinkage Ltd.