the-infamous-link-macro.md (2527B)
1 --- 2 title: "The Infamous 'link' Macro" 3 date: 2024-11-12T13:01:09-08:00 4 tags: [Forth, Assembly, RISC-V, ARM] 5 draft: false 6 --- 7 8 Each word (or function, in other languages) in Forth is stored as an entry in 9 a linked list known as the dictionary. When bootstrapping a Forth from 10 assembly, it is your responsibility to create and maintain this linked list 11 structure. This is a tedious process and is the source of many errors when 12 re-arranging words or defining new words; it's incredibly easy to turn your 13 list into a graph by mistake. 14 15 In 16 [jonesforth](https://rwmj.wordpress.com/2010/08/07/jonesforth-git-repository/), 17 an x86 implementation of Forth, the assembler supports re-assigning new values 18 to assembler variables. This means an assember variable can be used to store 19 the memory address of the previous entry, and we have the ability to update it 20 or use it whenever we need to. I used this exact strategy in my 21 [incomplete x86 Forth](https://git.christianermann.dev/forth/log.html) although 22 I used [FASM](https://flatassembler.net/) instead of 23 [GAS](https://wiki.osdev.org/GAS). Unfortunately, GAS doesn't support this 24 feature for ARM or RISC-V targets. It may be possible to pull this off on ARM 25 with the right set of relocation and/or relaxation parameters, but I was unable 26 to find success with RISC-V. 27 28 I did come up with an alternative set of macros that achieves the same goal 29 and should be more portable than the `jonesforth` solution: 30 ```GAS 31 .macro this_link 32 .globl link_\+ 33 link_\+: 34 .endm 35 36 .macro prev_link 37 .int link_\+ 38 .endm 39 40 .macro link 41 this_link 42 prev_link 43 .endm 44 45 this_link 46 ``` 47 48 These macros depend on the special value 49 {{<highlight GAS "hl_inline=true">}}\+{{</highlight>}}. 50 This value is replaced by the invocation count of the current macro during 51 assembly. Since we want to 52 {{<highlight GAS "hl_inline=true">}}link{{</highlight>}} 53 back to the previous word in the dictionary, we need 54 {{<highlight GAS "hl_inline=true">}}prev_link{{</highlight>}} 55 to resolve to 1 word before 56 {{<highlight GAS "hl_inline=true">}}this_link{{</highlight>}}, 57 which is why we call 58 {{<highlight GAS "hl_inline=true">}}this_link{{</highlight>}} 59 once before defining any words. 60 61 You'll also need to add this to your linker script to null-terminate your 62 dictionary, otherwise the start of your dictionary will be marked by the 63 address where the 64 {{<highlight GAS "hl_inline=true">}}link_0{{</highlight>}} 65 label created by 66 {{<highlight GAS "hl_inline=true">}}this_link{{</highlight>}} was stored: 67 ```GAS 68 link_0 = 0; 69 ``` 70