Skip to the content.

Meta Programming

gbforth is a Forth cross-compiler. It runs on your computer (the host system) while it will generate a program that will be executed on a Game Boy (the target system).

This imposes some restrictions compared to other Forth environments.

The most fundamental limitation is that you cannot interpret code that is intended to be executed on the target. For instance, the following code will not work:

: square dup * ;

\ We can't run SQUARE on the host!
: f [ 10 square ]L ;

However, you are still able to get the execution token (XT) of a word by using ' as follows:

: square dup * ;

\ Push the target address of SQUARE to the stack
' square

We can characterize code based on 2 criteria:

Since it does not make sense for a computation in the target system to act on the host system, we are left with 3 meaningful combinations:

Colon definitions

when: run-time, where: target system

This is the most common kind of code that you will write. It executes on the target and allows you to read/write variables, display to screen and all you would like to do with your device.

: foo
  %11100100 $FF47 ! ;

The words in the definition of foo would execute at run-time, storing a value to an address on the target.

gbforth does not provide a run-time Forth system, so for example, you can’t create new words (CREATE), and you can’t read words from the input stream (PARSE). You can, however, switch to interpreter mode (using [ and ]) and do those things at compile-time.

Top-level code and interpretation

when: compile-time, where: target system

In this mode, you can run words like CREATE, CONSTANT, VARIABLE and other defining and parsing Forth words.

CREATE bar 2 cells allot

In this code, CREATE is executed during the compilation of the program. Likewise, 2 cells allot will allocate 2 cells on the target system memory.

Meta colon definitions

As described above, the word : switches to colon definition / compilation mode for code that is meant to be executed on the target system.

However, that introduces a challenge. Sometimes we want to build new abstractions on top of interpreting words. To do that, we can use the word :m, which allows us to create a word that can be executed on the host, but will still act on target structures.

For example, we can define this enumerations facility like:

:m begin-enum 0 ;
:m enum 1+ dup constant ;
:m end-enum drop ;

This code, unlike ordinary colon definitions, can be executed in interpreter mode, so we can use it like:

begin-enum
  enum DARKER
  enum DARK
  enum LIGHT
  enum LIGHTER
end-enum

This mode is intended to be as intuitive as possible, but it still imposes some important limitations. For example, you cannot allocate memory in the host system, as the words allot, here and so on act on the target data structures.

To use even more advanced scenarios, you will want to switch to host definitions.

Host definitions

when: compile-time, where: host system

Sometimes you’ll want to write code that is executed on the host system, and operates on data in the host as well.

That is useful in advanced scenarios where, for example, you would like to allocate some memory for some compile-time computations, but that data does not need to be available at run-time at all.

Summary

Context Execution Data
Colon definition : Run-time (target) Target (ROM / RAM)
Top-level Compile-time (host) Target (ROM)
[host] Compile-time (host) Host