[futurebasic] Re: Inline Assembly - assem. newbie Question

Message: < previous - next > : Reply : Subscribe : Cleanse
Home   : December 1997 : Group Archive : Group : All Groups

From: Rick Brown <rbrown@...>
Date: Thu, 04 Dec 1997 20:28:26 -0600
Wilcox wrote:
> Well, I figured that much out, but I don't see how to pass parameters with
> the toolbox calls. Such as, in the toolbox call DebuggStr, it takes one
> parameter, a string. How would I pass Debuggstr something like "FOOBAR"?
> Would I MOVE "FOOBAR" into the a6, or a7, or what?
> What about functions with >1 parameters?

Parameters are generally passed in one of two ways.  In some of the
older (but still widely used) Toolbox routines, you pass parameters by
loading them into one or more of the sixteen "CPU registers," which are
called A0 through A7 and D0 through D7 (actually, A5, A6 and A7 are
reserved for other purposes, so you're really limited to 13 registers to
use in this way.  In practice, you rarely use more than three or four of
them).  Each CPU register can hold up to 4 bytes.  Routines that work
this way are called "Register-based routines."  NEWHANDLE is an example
of such a routine.  To call it in assembler, you store the desired size
into D0, then execute the "NewHandle trap," which means you put $A122 as
the next machine language "instruction".  NewHandle then executes, and
it puts the result code into the lower half of D0 (this will be zero if
all went well) and puts the handle itself into A0 (this will be NONzero
if all went well).  As you probably know, you can use MOVE instructions
to copy numbers from FB variables into CPU registers, and vice-versa.

Other Toolbox routines are called "stack-based routines."  In such
routines, you store input and output values in a certain area of RAM
called the "stack."  You never have to worry about where the stack is,
nor where within the stack you should put your parameters, because one
of the CPU registers (A7, to be exact) always acts as a "stack pointer,"
and contains the address of the current location in the stack where the
next parameter should be put into.

In stack-based routines, you specify an input parameter by "pushing" it
onto the stack.  That means: you first subtract either 2 or 4 from the
current value of A7 (depending on the size (2 or 4 bytes) of the
parameter that you're passing), and then store your parameter at the new
location that A7 now points to.  All of this can be done with a single
machine-language instruction.

You "push" all the parameters onto the stack in this way.  They must be
"pushed" in the same order in which they appear in the routine's
description in Inside Macintosh.

If the (stack-based) routine is a "function"--that is, if it returns a
value--then there's an additional requirement: _before_ you push any
parameters onto the stack, you must subtract 2 or 4 bytes (depending on
the size of the returned value) from the stack pointer.  You can do this
with a CLR.W -(A7) or a CLR.L -(A7) command, or by explicitly
subtracting 2 or 4 from A7.  Moving the stack pointer in this way
"leaves room" on the stack for the returned result.

Once you've done all this, you code the "trap" for the Toolbox routine
(sometimes it's also necessary to store a "selector" value, usually into
D0.  This allows a single "trap number" to handle many, many different
routines, each of which responds to a different selector value).  The
Toolbox routine knows where to find the parameters simply by looking at
the value of A7.  If the routine is a function and hence returns a
value, then the return value will be "on the stack" when the routine
finishes.  You retrieve this value by "popping" it off the stack; this
means, read the 2 or 4 bytes at the location that A7 points to, then add
2 or 4 to A7 (so that it points to a new location).  Again, this can all
be done with a single machine-language instruction.

Notice that parameters are _always_ 2 bytes or 4 bytes long.  In cases
where you need to "pass" something longer, like a string, what you
should pass is the objects _address_ (i.e., a pointer to the object's
location in memory), which is always 4 bytes.

In stack based routines, you _must_ push and pop the exactly correct
number of bytes onto and off of the stack (for example, you're not
allowed to just "ignore" the return value and fail to pop it off the
stack).  The reason is that pushing and popping always adjusts the value
of the stack pointer, and the stack pointer _must_ be adjusted by the
right amount.  If it's pointing to the wrong address when you exit the
LOCAL FN, a system error could easily result.

Here's an example using DebugStr:

LOCAL FN myDebugStr(stringPtr&)
`  MOVE.L ^stringPtr&,-(A7)    ;push stringPtr& onto stack
`  _debugStr    ;can also be coded as: DC.W $ABFF

You could call this as follows:

msg$ = "This is a message."
FN myDebugStr(@msg$)

Hope this helps.
- Rick