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;

#forth #assembly #risc-v #arm

Reply to this post by email ↪