(i.e., how to create the parameter variables and local variables for a function when it is invoked)
(i.e., how to make sure that each function invocation accesses it's own copy of the parameter and local variables and not those that belong to a different invacation...
"Destroy" = reclaim - i.e., mark the memory space as "unused"
(You need to do this or else the computer will run out of "free" (unused) memory space...)
Before creating a variable: After creating a variable on stack: +----------+ Top of stack --> +----------+ | | | new var | Top of stack --> +----------+ +----------+ | xxxxx | | xxxxx | +----------+ +----------+
Before creating a new variable, the portion of memory above the "top of stack" was not reserved.
After creating a new variable, that portion of memory has become reserved.
In fact: each invocation of a recursive routine will create its own set of parameters and local variables.
The stack looks like this when a recursive fac routine is called:
Suppose main calls fac(3): Top of stack --> +----------------------+ | local variables | | + | | return address | <--- fac(1) calls fac(0) | + | and creates local vars | parameter variables | + parameters for fac(0) | (n = 0) | +----------------------+ | local variables | | + | | return address | <--- fac(2) calls fac(1) | + | and creates local vars | parameter variables | + parameters for fac(1) | (n = 1) | +----------------------+ | local variables | | + | | return address | <--- fac(3) calls fac(2) | + | and creates local vars | parameter variables | + parameters for fac(2) | (n = 2) | +----------------------+ | local variables | | + | | return address | <--- main calls fac(3) | + | and creates local vars | parameter variables | + parameters for fac(3) | (n = 3) | +----------------------+
Take a look at this call:
main() { int r, n; r = fac(n); }When main() calls fac(n), the following events happens in this order:
If you observe the execution order, you will see that the stack will look like this when fac begins execution:
Stack when fac starts executing: Top of stack --> +----------+ | retaddr | <- retaddr pushed when main calls fac +----------+ | n | <- main pushes parameter n first +----------+ | xxxxx | +----------+
Top of stack --> +-------------+ | local vars | <- fac allocates local vars +-------------+ | retaddr | <- retaddr pushed when main calls fac +-------------+ | n | <- main pushes parameter n first +-------------+ | xxxxx | +-------------+This structure is called a stack frame
We still need to make it easy for us to access the variables.
If you take a look at a whole sequence of fac calls:
Top of stack --> +-------------+ | local vars | <--+ +-------------+ | (stack frame of fac(3)) | retaddr | | parameters & local vars +-------------+ | of fac(3) | n = 3 | <--+ +-------------+ | local vars | <--+ +-------------+ | (stack frame of fac(4)) | retaddr | | parameters & local vars +-------------+ | of fac(4) | n = 4 | <--+ +-------------+ | local vars | <--+ +-------------+ | (stack frame of fac(5)) | retaddr | | parameters & local vars +-------------+ | of fac(5) | n = 5 | <--+ +-------------+How do you find your own variables easily ?
The registers used in M68000 is usually A6 (since A7 is already used for stack pointer) to point to the stack frame. This register is called the (stack) frame pointer (fp)
Since A6 is shared between the different recursive function calls, A6 will then contain different values when a different recursive function is active. In other words, when fac(5) is active, A6 will have a certain value. When fac(5) calls fac(4), A6 will have a different value when it is running fac(4). Now, when fac(4) (eventually) returns to fac(5), the A6 value used by fac(5) must be restored ! (remember that fac(4) uses a different value for A6.
Pictorially:
fac(5) +----> fac(4) | | | | A6 points to | | A6 points to | fac(5)'s parameters | | fac(4)'s param | and local variables | | and local vars | | | +---> fac(5) calls fac(4) ----+ ..... | +<-------------------------------------+ fac(4) finishes | and returns... | Register A6 has CHANGED !Notice that register A6 would have changed when fac(4) returns back to fac(5) if fac(4) does not restore the value of A6
So the stack structure above is extending to include the frame pointer:
+----------------+ | local vars | <- fac allocates local vars +----------------+ my A6 --> | A6 of my caller| <- fac save the fp of its caller +----------------+ | retaddr | <- retaddr pushed when main calls fac +----------------+ | n | <- main pushes parameter n first +----------------+