f82824cca8d56bf183689c237762aefc1be41c88
[rrq/jonasforth.git] / notes.md
1 FASM:
2 - https://flatassembler.net/docs.php?article=fasmg (Introduction)
3 - https://flatassembler.net/docs.php?article=fasmg_manual (Manual)
4 - https://flatassembler.net/docs.php?article=manual (Other manual)
5
6 JONESFORTH:
7 - https://github.com/nornagon/jonesforth/blob/master/jonesforth.S
8
9 # Notes on implementation
10
11 This is my summary of the most important parts of
12 https://raw.githubusercontent.com/nornagon/jonesforth/master/jonesforth.S.
13
14 ## Dictionary
15
16 In Forth, words are stored in a dictionary. The dictionary is a linked list whose entries look like this:
17
18     +------------------------+--------+---------- - - - - +----------- - - - -
19     | LINK POINTER           | LENGTH/| NAME              | DEFINITION
20     |                        | FLAGS  |                   |
21     +--- (4 bytes) ----------+- byte -+- n bytes  - - - - +----------- - - - -
22
23 For example, DOUBLE and QUADRUPLE may be stored like this:
24
25       pointer to previous word
26        ^
27        |
28     +--|------+---+---+---+---+---+---+---+---+------------- - - - -
29     | LINK    | 6 | D | O | U | B | L | E | 0 | (definition ...)
30     +---------+---+---+---+---+---+---+---+---+------------- - - - -
31        ^       len                         padding
32        |
33     +--|------+---+---+---+---+---+---+---+---+---+---+---+---+------------- - - - -
34     | LINK    | 9 | Q | U | A | D | R | U | P | L | E | 0 | 0 | (definition ...)
35     +---------+---+---+---+---+---+---+---+---+---+---+---+---+------------- - - - -
36        ^       len                                     padding
37        |
38        |
39     LATEST
40
41 The Forth variable LATEST contains a pointer to the most recently defined word.
42
43 ## Threaded code
44
45 In a typical Forth interpreter, code is stored in a peculiar way. (This way of
46 storing code is primarily motivated by space contraints on early systems.)
47
48 The definition of a word is stored as a sequence of memory adresses of each of
49 the words making up that definition. (At the end of a compiled definition, there
50 is also some extra code that causes execution to continue in the correct way.)
51
52 We use a register (ESI) to store a reference to the next index of the
53 word (inside a definition) that we are executing. Then, in order to execute a
54 word, we just jump to whatever address is pointed to by ESI. The code for
55 updating ESI and continuing execution is stored at the end of each subroutine.
56
57 Of course, this approach only works if each of the words that we are executing
58 is defined in assembly, but we also want to be able to execute Forth words!
59
60 We get around this problem by adding a "codeword" to the beginning of any
61 compiled subroutine. This codeword is a pointer to the intrepreter to run the
62 given function. In order to run such functions, we actually need two jumps when
63 executing: In order to execute a word, we jump to the address at the location
64 pointed to by the address in ESI.
65
66 ## Definitions
67
68 What does the codeword of a Forth word contain? It needs to save the old value
69 of ESI (so that we can resume execution of whatever outer definition we are
70 executing at the time) and set the new version of ESI to point to the first word
71 in the inner definition.
72
73 The stack where the values of ESI are stored is called the "return stack". We
74 will use EBP for the return stack.
75
76 As mentioned, whenever we finish executing a Forth word, we will need to
77 continue execution in the manner described in the previous section. When the
78 word being executed is itself written in Forth, we need to pop the old value of
79 ESI that we saved at the beginning of the definition before doing this.
80
81 Thus, the actual data for a word in a dictionary will look something like this:
82
83       pointer to previous word
84        ^
85        |
86     +--|------+---+---+---+---+---+---+---+---+------------+------------+------------+------------+
87     | LINK    | 6 | D | O | U | B | L | E | 0 | DOCOL      | DUP        | +          | EXIT       |
88     +---------+---+---+---+---+---+---+---+---+------------+--|---------+------------+------------+
89        ^       len                         pad  codeword      |
90        |                                                      V
91       LINK in next word                            points to codeword of DUP
92
93 Here, DOCOL (the codeword) is address of the simple interpreter described above,
94 while EXIT a word (implemented in assembly) that takes care of popping ESI and
95 continuing execution. Note that DOCOL, DUP, + and EXIT are all stored as
96 addresses which point to codewords.
97
98 ## Literals
99
100 Literals are handled in a special way. There is a word in Forth, called LIT,
101 implemented in assembly. When executed, this word looks at the next Forth
102 instruction (i.e. the value of ESI), and places that on the stack as a literal,
103 and then manipulates ESI to skip over the literal value.
104
105 ## Built-in variables
106
107 * **STATE** -- Is the interpreter executing code (0) or compiling a word (non-zero)?
108 * **LATEST** -- Points to the latest (most recently defined) word in the dictionary.
109 * **HERE** -- Points to the next free byte of memory.  When compiling, compiled words go here.
110 * **S0** -- Stores the address of the top of the parameter stack.
111 * **BASE** -- The current base for printing and reading numbers.