Next: , Previous: Assembler and Code Words, Up: Assembler and Code Words


5.26.1 Code and ;code

Gforth provides some words for defining primitives (words written in machine code), and for defining 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,1.

assembler              tools-ext       “assembler”

init-asm              gforth       “init-asm”

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.

The typical usage of these code words can be shown most easily by analogy to the equivalent high-level defining words:

     : foo                              code foo
        <high-level Forth words>              <assembler>
     ;                                  end-code
     
     : bar                              : bar
        <high-level Forth words>           <high-level Forth words>
        CREATE                             CREATE
           <high-level Forth words>           <high-level Forth words>
        DOES>                              ;code
           <high-level Forth words>           <assembler>
     ;                                  end-code

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.

In particular, ip (Forth instruction pointer) and rp (return stack pointer) may be in different places in gforth and gforth-fast, or different installations. This means that you cannot write a NEXT routine that works reliably on both versions or different installations; so for doing NEXT, I recommend jumping to ' noop >code-address, which contains nothing but a NEXT.

For general accesses to the inner interpreter's registers, the easiest solution is to use explicit register declarations (see Variables in Specified Registers) 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 Options for Code Generation Conventions) 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 to add the desired functionality to the source of Gforth. For normal words you just have to edit primitives (see Automatic Generation). Defining words (equivalent to ;CODE words, for fast defined words) may require changes in engine.c, kernel.fs, prims2x.fs, and possibly cross.fs.


Footnotes

[1] This isn't portable, because these words emit stuff in data space; it works because Gforth has unified code/data spaces. Assembler isn't likely to be portable anyway.