The Infamous 'link' Macro
Each word (or function, in other languages) in Forth is stored as an entry in a linked list known as the dictionary. When bootstrapping a Forth from assembly, it is your responsibility to create and maintain this linked list structure. This is a tedious process and is the source of many errors when re-arranging words or defining new words; it’s incredibly easy to turn your list into a graph by mistake.
In jonesforth, an x86 implementation of Forth, the assembler supports re-assigning new values to assembler variables. This means an assember variable can be used to store the memory address of the previous entry, and we have the ability to update it or use it whenever we need to. I used this exact strategy in my incomplete x86 Forth although I used FASM instead of GAS. Unfortunately, GAS doesn’t support this feature for ARM or RISC-V targets. It may be possible to pull this off on ARM with the right set of relocation and/or relaxation parameters, but I was unable to find success with RISC-V.
I did come up with an alternative set of macros that achieves the same goal
and should be more portable than the jonesforth
solution:
1.macro this_link
2 .globl link_\+
3link_\+:
4.endm
5
6.macro prev_link
7 .int link_\+
8.endm
9
10.macro link
11 this_link
12 prev_link
13.endm
14
15this_link
These macros depend on the special value
\+
.
This value is replaced by the invocation count of the current macro during
assembly. Since we want to
link
back to the previous word in the dictionary, we need
prev_link
to resolve to 1 word before
this_link
,
which is why we call
this_link
once before defining any words.
You’ll also need to add this to your linker script to null-terminate your
dictionary, otherwise the start of your dictionary will be marked by the
address where the
link_0
label created by
this_link
was stored:
1link_0 = 0;