Gforth provides some words for defining primitives (words written in
machine code), and for defining the the machine-code equivalent of
DOES>
-based defining words. However, the machine-independent
nature of Gforth poses a few problems: First of all, Gforth runs on
several architectures, so it can provide no standard assembler. What's
worse is that the register allocation not only depends on the processor,
but also on the gcc
version and options used.
The words that Gforth offers encapsulate some system dependences (e.g., the
header structure), so a system-independent assembler may be used in
Gforth. If you do not have an assembler, you can compile machine code
directly with ,
and c,
.
assembler
-- tools-ext "assembler"
code
"name" -- colon-sys tools-ext "code"
end-code
colon-sys -- gforth "end-code"
;code
compilation. colon-sys1 -- colon-sys2 tools-ext "semicolon-code"
flush-icache
c-addr u -- gforth "flush-icache"
Make sure that the instruction cache of the processor (if there is
one) does not contain stale data at c_addr and u bytes
afterwards. END-CODE
performs a flush-icache
automatically. Caveat: flush-icache
might not work on your
installation; this is usually the case if direct threading is not
supported on your machine (take a look at your `machine.h') and
your machine has a separate instruction cache. In such cases,
flush-icache
does nothing instead of flushing the instruction
cache.
If flush-icache
does not work correctly, code
words
etc. will not work (reliably), either.
These words are rarely used. Therefore they reside in code.fs
,
which is usually not loaded (except flush-icache
, which is always
present). You can load them with require code.fs
.
In the assembly code you will want to refer to the inner interpreter's registers (e.g., the data stack pointer) and you may want to use other registers for temporary storage. Unfortunately, the register allocation is installation-dependent.
The easiest solution is to use explicit register declarations
(see section `Variables in Specified Registers' in GNU C Manual) for all of the inner interpreter's registers: You have to
compile Gforth with -DFORCE_REG
(configure option
--enable-force-reg
) and the appropriate declarations must be
present in the machine.h
file (see mips.h
for an example;
you can find a full list of all declarable register symbols with
grep register engine.c
). If you give explicit registers to all
variables that are declared at the beginning of engine()
, you
should be able to use the other caller-saved registers for temporary
storage. Alternatively, you can use the gcc
option
-ffixed-REG
(see section `Options for Code Generation Conventions' in GNU C Manual) to reserve a register
(however, this restriction on register allocation may slow Gforth
significantly).
If this solution is not viable (e.g., because gcc
does not allow
you to explicitly declare all the registers you need), you have to find
out by looking at the code where the inner interpreter's registers
reside and which registers can be used for temporary storage. You can
get an assembly listing of the engine's code with make engine.s
.
In any case, it is good practice to abstract your assembly code from the
actual register allocation. E.g., if the data stack pointer resides in
register $17
, create an alias for this register called sp
,
and use that in your assembly code.
Another option for implementing normal and defining words efficiently
is: adding the wanted functionality to the source of Gforth. For normal
words you just have to edit `primitives' (see section Automatic Generation), defining words (equivalent to ;CODE
words, for fast
defined words) may require changes in `engine.c', `kernal.fs',
`prims2x.fs', and possibly `cross.fs'.