Skip to the content.

gbforth assembler

gbforth includes an assembler. This is useful when you want more control about how the hardware is used or you need to optimize some piece of code.

You can combine Forth and Assembler as you please. You can even write all your game in assembler, yet having the power of all the Forth at compile-time.

Introduction

gbforth’s assembler lives in a separate vocabulary, which can be accessed using the words [asm] and [endasm].

It provides a traditional postfix notation. Operands are specified first, and then the instruction. For example, the code below will move the number 42 to the register HL.

[asm]

42 # HL ld,

[endasm]

To learn more, you can have a look to the hello world example written in assembler, or the the lib/ directory.

Operands

Operands are kept in an auxiliary stack. Registers and flags push themselves to this stack, immediate values have to be pushed manually.

Immediate

You can push immediate values by using the word #. You can use prefixes to specify the base of your immediate values.

[asm]

42 # HL ld,     \ decimal
$ff # HL ld,    \ hex
%1011 # HL ld,  \ binary

[endasm]

Registers

Registers like A, B, SP, or HL push themselves to the operand stack.

Some registers also support memory indirection. In that case, the register is wrapped in square brackets: [BC], [DE], [HL], [C].

Finally, there are the operands [HL+] and [HL-]. This means that the register HL will be used to reference the memory, and then the register HL will be incremented or decremented.

Flags

Flags, like registers, push themselves to the operand stack.

There are 4 flags available to make an instruction conditional:

These flags can be used in combination with the instructions call, and jp, and jr, and ret,.

[asm]

$1234 # #NZ jp,  \ jump if last result was non-zero
#C ret,          \ return if carry flag is set

[endasm]

Memory references

Memory references can be pushed to the operand stack using the word ]*.

[asm]

$FF40 ]* A ld,  \ load value at address $FF40 into A

[endasm]

Labels

Labels can be created using the word label. These labels are simple constants, and can only be referenced after they are defined:

[asm]

label countToZero
A dec,
countToZero #NZ jp,

[endasm]

For local references however (like in the example above), you are encouraged to make use of two other concepts that the gbforth assembler provides: stack-based references, and structured control flow. These also support cases where you need to forward-reference an address (which is not possible with label).

Anonymous stack-based references

gbforth’s assembler provides word pairs to create anonymous stack-based references:

They can be used to implement simple loops or jumps, when there is no need to give a name to the reference:

[asm]

( backward reference )
here<
A dec,
<there #NZ jp,

( forward reference )
there> #C jp,
B inc,
>here

[endasm]

For cases where you need a long forward reference, you can use the word named-ref> to assign a name to the reference:

[asm]

there> #Z jp,
named-ref> longFwdJump

( ... )

longFwdJump \ replaces >here

[endasm]

Structured control flow

The assembler exposes a few words similar to control flow words available in Forth:

The words if,, until, and while, have to be combined with one of the flag operands (because they are essentially jp, instructions).

These can be used to structure the control flow without using the underlying references directly:

[asm]

begin,
  A dec,
#Z until,

#NC if,
  B inc,
else,
  B dec,
then,

[endasm]