Well, I don't know of any processor that uses a separate stack for the
frame pointers. See for example the instructions LINK and UNLK of the
680x0 (there are similar instructions on X86 and others). The top frame
pointer is usually kept in A6, while the stack pointer is A7=SP.
At the entry point of functions, there's an instruction:
LINK A6,#-localSpace
and at the exit of them, there are:
UNLK A6
RTN
So for functions like:
> function G(a) { c = 42; F(c, a) }
> function F(a, b) { ... don't care about what's in F }
and starting with A7=SP=0xfff0 (stacks tend to grow downward in
processors, but it makes no differences, only the offsets are
opposites):
A7: 0xfff0
A6: 0xfff8
the caller pushes 99 for the argument of G, then calls G, with a JSR G,
which pushes the return address onto the stack:
0xfff0:
0xffec: 99
0xffe8: return-address-to-caller
A7: 0xffe8
A6: 0xfff8
then G executes LINK A6,#-4 since it needs one local variable.
0xfff0:
0xffec: 99
0xffe8: return-address-to-caller
0xffe4: 0xfff8 ; old fram pointer
0xffe0: random value for c
A7: 0xffe0
A6: 0xffe4 ; G frame pointer
c:=42 This writes into the local frame at the address -4(A6):
0xfff0:
0xffec: 99
0xffe8: return-address-to-caller
0xffe4: 0xfff8 ; old fram pointer
0xffe0: 42 ; c
A7: 0xffe0
A6: 0xffe4 ; G frame pointer
F(c,a) this reads the local frame: c is in the local variables at
-4(A6), and a is in the parameters at 8(A6). The arguments are pushed
on the stack:
0xfff0:
0xffec: 99
0xffe8: return-address-to-caller
0xffe4: 0xfff8 ; old fram pointer
0xffe0: 42 ; c
0xffdc: 42
0xffd8: 99
A7: 0xffd8
A6: 0xffe4 ; G frame pointer
then F is called.
0xfff0:
0xffec: 99
0xffe8: return-address-to-caller
0xffe4: 0xfff8 ; old fram pointer
0xffe0: 42 ; c
0xffdc: 42
0xffd8: 99
0xffd4: return address into G
A7: 0xffd4
A6: 0xffe4 ; G frame pointer
So F executes LINK A6,#-n (n depending on the local storage F needs):
And so on. When F returns, it calls:
UNLK A6
which restores the stack to:\
0xfff0:
0xffec: 99
0xffe8: return-address-to-caller
0xffe4: 0xfff8 ; old fram pointer
0xffe0: 42 ; c
0xffdc: 42
0xffd8: 99
0xffd4: return address into G
A7: 0xffd4
A6: 0xffe4 ; G frame pointer
and:
RTN
which returns to G
0xfff0:
0xffec: 99
0xffe8: return-address-to-caller
0xffe4: 0xfff8 ; old fram pointer
0xffe0: 42 ; c
0xffdc: 42
0xffd8: 99
A7: 0xffd8
A6: 0xffe4 ; G frame pointer
When G returns, it executes:
UNLK A6
which restores the stack to:
0xfff0:
0xffec: 99
0xffe8: return-address-to-caller
A7: 0xffe8
A6: 0xfff8
and then:
RTN
and we're back to the caller:
0xfff0:
0xffec: 99
A7: 0xffec
A6: 0xfff8