Assembler and Disassembler
The Mops' PowerPC assembler and disassembler were originally written by
Xan Gregg for Power MacForth. They've been
adapted for use in the Mops environment as modules. The source files are
"pasmMod.txt
" and "disasm
". The
syntax is traditional Forth postfix-style.
Assembler colon definitions
You write a code definition thus:
`:ppc_code someName`\
` </nowiki><var><ppc instructions></var><nowiki>`\
`;ppc_code`\
This will create a normal header for someName
in the
dictionary, then align the CDP to a 4-byte boundary (as required for
PowerPC code), and compile the code. Note that if you tick
someName
, the resulting xt on the stack will be the
address of the first instruction, minus 2. The extra 2 bytes are
used internally (by Mops) as flags. Remember to add 2 if you need the
actual address of the first instruction. If, however, you use
EXECUTE
, you pass the xt in the normal way.
We don't provide a full rundown of the assembly syntax here; however
the comments in the source files are fairly extensive. So if you're the
type of person who might want to write assembly language, you'll
probably be able to figure out what to do! �especially as the file
"test pasm
" in the 'Module Source' folder has a
definition containing all the PowerPC instructions supported by the
assembler. Remember that it's a Forth-style postfix assembler. We also
give a short example at the end of this section, and there are a number
of code definitions in the source files in 'PPC source', especially
the file "setup
".
We don't provide any way of writing a method with the assembler. This
should never be necessary for performance, and if you really need access
to machine-level features, you can write a
:ppc\_code
word and call it from
your method.
Accessing the dictionary
If you need to get the address of an item in the code or data area of the dictionary, you can do it with this syntax:
r0 ' <var>someWord</var> <var>dicaddr,</var>
This generates an addi
instruction, using the
appropriate base register and offset to place the address of the
location you want into a register. We used r0
in this
example, but you could have used any free register. For a location in
the data area, remember that ticking the name of the item won't get you
there�you have to do something like this:
r0 ' <var>myValue</var> >body <var>dicaddr,</var>
You can execute any code you like between naming the register and
putting dicaddr,
. You are in execution mode, and can
execute any Mops words, so long as you leave just one item on the stack.
One proviso is that at the moment, the target address must be within 32k
bytes distance from where the base register points, as otherwise the
address can't be generated with a single addi
instruction. If the assembler gives you an out-of-range error on a line
with dicaddr,
this is probably the reason.
Executing colon definitions from code
In a word, don't. Mops colon definitions will expect various numbers of their parameters in registers when they're called, and the algorithm for working this out is quite complex (and might even change!). So you should really only use code definitions for words that don't call any other words.
Executing other code definitions from code
There's no problem with doing this, since you have full control of the registers. Remembering that the first instruction of a code definition starts two bytes after where the xt points, you call another code definition this way:
' <var>someWord</var> 2+ bl,
The Assembler automatically converts the absolute address of the first
instruction of someWord
, which we generate with
'someWord 2+
', to the offset that is required by the
bl
instruction.
Assembler source
The assembler-related source files are all in the 'Module source' folder. They are:
pasmMod.txt the assembler
disasm the disassembler (loaded by "pasmMod.txt
")
test pasm a big test definition with all the PPC instructions
vectors pasm test another big test definition with all the AltiVec instructions
On entry to a code definition, the top-of-stack item is in register
r4
, the second item is in r3
, and the
next stack item is in memory, pointed to by the data stack pointer,
r18
. You can do whatever you like with the stack
(within reason), but on exit from your definition you must observe this
same convention.
Within the definition, you can use registers
r5
-12
freely for whatever you like
without having to save or restore them. You can also use
r23
-31
, but as Mops uses these for
locals, they might contain values in use by the calling code. So you
will need to save and restore any of these that you use.
The same applies to the floating point stack. On entry to a code
definition, the top-of-stack item is in register fr2
,
the second item is in fr1
, and the next stack item is
in memory, pointed to by the FP stack pointer, r19
. You
can use fr0
and
fr3
-13
without saving or restoring
them. fr14
-31
are used for FP locals,
so you can use them if you save and restore them.
PowerPC register usage
Here's a summary of the register usage in the PowerMops runtime environment:
`r0` scratch
r1
system stack pointer (leave it alone, normally)
r2
RTOC (Table Of Contents pointer -- leave alone)
r3
initially, second stack cell
r4
initially, top stack cell
r5
scratch
r6
scratch
r7
scratch
r8
scratch
r9
scratch
r10
scratch
r11
scratch, also used in system calls
r12
ditto
r13
base address of main dic code area
r14
base address of main dic data area
r15
base address of current module's code area
r16
base address of current module's data area
r17
return stack pointer (points to top cell)
r18
data stack pointer (points to top memory cell)
r19
floating point stack pointer (points to top memory cell)
r20
base address of current object
r21
loop counter I
r22
limit value for DO
...LOOP
r23
can use if you save and restore
r24
ditto
r25
ditto
r26
ditto
r27
ditto
r28
ditto
r29
ditto
r30
ditto
r31
ditto
fr0
scratch
fr1
initially, second stack cell
fr2
initially, top stack cell
fr3
scratch
fr4
scratch
fr5
scratch
fr6
scratch
fr7
scratch
fr8
scratch
fr9
scratch
fr10
scratch
fr11
scratch
fr12
scratch
fr13
scratch
fr14
can use if you save and restore
fr15
ditto
fr16
ditto
fr17
ditto
fr18
ditto
fr19
ditto
fr20
ditto
fr21
ditto
fr22
ditto
fr23
ditto
fr24
ditto
fr25
ditto
fr26
ditto
fr27
ditto
fr28
ditto
fr29
ditto
fr30
ditto
fr31
ditto
Example
Finally, here's a short example, from the file
"pnuc1
" (in 'PPC source'). This is the definition
of PICK
.
`:ppc_code PICK`\
` r4 0 cmpi, \is it 0 pick?`\
` eq if,`\
` r4 r3 r3 or, \yes - copy TOS`\
` else,`\
` r5 r4 2 0 29 rlwinm, \no- mult index by 4 by left shift`\
` r5 r5 -4 addi, \and subtract 4 to get SP offset`\
` r4 r18 r5 lwzx, \grab the cell`\
` then,`\
` blr, \and return.`\
`;ppc_code`\
Note that PICK
doesn't push or pop from the data
stack, but simply replaces the top cell (in r4
) with
the stack cell that it fetches. Also, since it doesn't call any other
routine, there's no need to save and restore the return address which
is in the link register on entry. If we had needed to save and restore
the link register, we would have put the instructions
r0 mflr, \save lr on return stack
r0 -4 rRP stwu,
at the beginning, and
r0 mtlr, \restore lr
at the end before the blr,
.
Note also that we need to special-case the '0 pick
'
case, since the desired cell isn't in the memory part of the stack, but
is already in r3
.
For an example of a much longer code definition, have a look at
(EX)
in the file "setup
" (in 'PPC
source').
Warning: If you are already familiar with the old 68k Mops Assembler, note that you must write instructions at the end of your code routine so that it will return. The PowerPC Assembler doesn't do this for you, since it can't always know where your return address is. (It might not necessarily be in the link register at that point�you might have put it in the count register or have saved it on the return stack or somewhere else.)
Disassembler
The disassembler will disassemble PowerPC code from a range of addresses you specify, dumping its output to the Mops window.
You call the disassembler with one of the following words:
disasm\_word
someWord
Disassembles the word someWord
.
disasm\_xt
( xt \-- )
Disassembles the word with the given xt.
disasm\_rng
( from to \-- )
Disassembles within the given address range.
disasm\_cnt
class="STACK" nowrap | ( from \#inst \-- )
Starts at 'from', disassembles the given number of instructions.
disasm
( from \-- )
Starts at 'from', keeps going till you hit a key or until a blr
instruction is seen (which normally comes at the end of a definition).