[comp.sys.acorn] Saving the old pc in APCS || Shareware comments

csuwr@warwick.ac.uk (Derek Hunter) (03/27/91)

Why, in the APCS section of the PRMs, does the example code suggest
 saving the /old/ pc onto the stack?

#  Mov   ip,sp			\ This is all from memory
#  StmFD sp!,{a1-a4}		\  please forgive mistakes.
#  StmFD sp!,{v1-v6,fp,ip,lr,pc}  
#  Sub   fp,ip,#20
#
# ... other bits
#
#  LdmDB fp,{v1-v6,fp,sp,pc}^

Does LdmXX Rn,{...,pc}^ restore the flags from the next word, and not
 from the pc word, or is this a sneaky way of setting up some traceback
 block-type-thing (If so, where is the traceback word that describes which
 registers have been saved)?

Also, if we can access the parameters relative to fp, why the distinction
 with the local variables? (If an answer to this involves stack segmentation,
 please feel free. I was confused by that concept.)

It's a shame that Basic attempts to interpret sp! as the first half of
 a word indirection, rather than noting that it's inside assembly, and
 checking sp! type things for a following comma.

			Derek Hunter.

  +------------------------------------------------------------------------+
  | Can someone who has actually done it, mail me (or post here) a piece   |
  | describing the trouble that initiating shareware involves - amount of  |
  | time spent in distributing upgrades to registered users, maintainance, |
  | answering fan/hate mail, evaluating bugfixes etc.            Ta.       |
  +------------------------------------------------------------------------+
.------------------------------------+---------------------------------------.
|  `Osmosis is an absorbing hobby.'  |  Derek Hunter csuwr@cu.warwick.ac.uk  |

john@acorn.co.uk (John Bowler) (03/27/91)

In article <+?R&L0#@warwick.ac.uk> csuwr@warwick.ac.uk (Derek Hunter) writes:
>Why, in the APCS section of the PRMs, does the example code suggest
> saving the /old/ pc onto the stack?
>
>#  Mov   ip,sp			\ This is all from memory
>#  StmFD sp!,{a1-a4}		\  please forgive mistakes.
>#  StmFD sp!,{v1-v6,fp,ip,lr,pc}  

old?  The lr value is saved to allow the function to return...  I
assume what you mean is the *current* pc (ie the last register in
the above instruction).

This is done because the pc points to the STMFD which saved it +
12 bytes.  So you can find the code which made the stack frame, so
you can find which registers were saved (and you know where, from
the FP) so you can reclaim callee save registers (v1-v6) and hence
find the register values in the *caller*, so you can write a stack
traceback, (ie unwind the whole stack and find callee save register
values in every function on the stack), so you can debug your
programs.

This is the only thing which APCS does to support debuggers...
The instruction sequence is not mandated, instead [fp], when the
relevant bits have been cleared, points *12 bytes beyond* a word
which looks remarkably like an STMDB instruction.  Ok, we admit
it, you have to use that instruction sequence :-)

>#  Sub   fp,ip,#20
>#
># ... other bits
>#
>#  LdmDB fp,{v1-v6,fp,sp,pc}^
>
>Does LdmXX Rn,{...,pc}^ restore the flags from the next word, and not
> from the pc word,

DB == decrement before.  The registers are loaded from fp-4, so the
pc value is skipped, and the saved lr value gets put into the pc.

>                   or is this a sneaky way of setting up some traceback
> block-type-thing

Exactly.

>                   (If so, where is the traceback word that describes which
> registers have been saved)?

In the code :-)

>Also, if we can access the parameters relative to fp, why the distinction
> with the local variables? (If an answer to this involves stack segmentation,
> please feel free. I was confused by that concept.)

It's totally horrible (but, on the other hand, it does work).  Only
the callee necessarily knows the number of local variables (because
of separate compilation), therefore only the callee can check to
see if there is enough room on the stack.  If there isn't the callee
has to call rather a lot of code (including malloc) to get some
more stack; something has to save registers for this code to run.
It is convenient to save the registers on the stack (actually, it
is almost impossible to save them statically in a multi-threaded
world), so they are saved on the ``old'' stack (ie the current
stack segment when the callee is entered).  The local variables,
however, are on the new stack.

Now, the stack extension code doesn't know how many arguments the function
has, because:-

i) The function doesn't tell it.
ii) The function may be a varargs one, in which case it doesn't even
know itself.

So it cannot copy *all* the arguments onto the new stack, although
it could copy the stack frame and saved arguments.  Rather than
doing this copying (which isn't required) the stack frame is left
on the old stack (plus some junk from the stack extension code)
and the local variables are on the new stack.  Since there is no
necessary relationship between these pieces of store the code needs
two pointers; one (the fp) to get at the arguments and stack frame
(for procedure return) and the other (the sp) to get at locals.

As if this isn't bad enough the stack extension code can fix up
the callee stack frame so that control returns to the extension
code when the callee attempts to return.  Then the extension code
can free the stack frame if desirable.

John Bowler