572e58a31e56ff4b10f45345896c7d6fc1a7475f
[rrq/jonasforth.git] / README.md
1 # Building and running
2
3 You can run JONASFORTH inside QEMU or on real hardware. If you want to run
4 inside QEMU, you should have the following dependencies installed (assuming
5 Arch Linux):
6
7     $ pacman -S qemu ovmf
8
9 Then, to run a UEFI shell inside QEMU, run:
10
11     $ make qemu
12
13 JONASFORTH will be available as `main` on `FS0:`. Thus, to run it, you can run
14 the following command inside the UEFI shell:
15
16     Shell> fs0:main
17     Ready.
18     S" Hello, World!" TELL
19     Hello World!
20
21 (Try typing in the code in `example.f` for something a little more
22 interesting.)
23
24 ## Running on real hardware
25
26 Making the program run on physical hardware is pretty easy. Just create a
27 FAT32-formatted USB drive, and copy `out/main` to it. Then, you can execute the
28 program in the same way that you did from inside QEMU, assuming your system
29 comes with a UEFI shell built-in.
30
31 If your system doesn't have a UEFI shell, then you can copy the executable to
32 `\EFI\BOOT\BOOTx64.EFI` on the USB drive. Then, the system should be able to
33 boot from the USB drive and directly into JONASFORTH. The way to do this is a
34 little bit different depending on the exact firmware, but most firmwares will
35 have some way to enter a boot menu where you can select the USB drive. You may
36 need to disable Secure Boot first.
37
38 To format a USB drive as FAT32, you can run
39
40     # mkfs.vfat -F32 /dev/sdx
41
42 with `/dev/sdx` replaced by the path of your USB drive. Then mount the drive,
43 and copy `out/main` to `\EFI\BOOT\BOOTx64.EFI`:
44
45     $ mkdir mnt
46     # mount /dev/sdx mnt
47     $ mkdir -p mnt/EFI/BOOT
48     $ make out/main
49     $ cp out/main mnt/EFI/BOOT/BOOTx64.EFI
50
51 Now you should be able to boot directly from the USB drive.
52
53 # Notes on implementation
54
55 The implementation is based on
56 [JONESFORTH](https://raw.githubusercontent.com/nornagon/jonesforth/master/jonesforth.S).
57 This is my summary of the most important parts.
58
59 ## Dictionary
60
61 In Forth, words are stored in a dictionary. The dictionary is a linked list
62 whose entries look like this:
63
64     +------------------------+--------+---------- - - - - +----------- - - - -
65     | LINK POINTER           | LENGTH/| NAME              | DEFINITION
66     |                        | FLAGS  |                   |
67     +--- (4 bytes) ----------+- byte -+- n bytes  - - - - +----------- - - - -
68
69 For example, DOUBLE and QUADRUPLE may be stored like this:
70
71       pointer to previous word
72        ^
73        |
74     +--|------+---+---+---+---+---+---+---+---+------------- - - - -
75     | LINK    | 6 | D | O | U | B | L | E | 0 | (definition ...)
76     +---------+---+---+---+---+---+---+---+---+------------- - - - -
77        ^       len                         padding
78        |
79     +--|------+---+---+---+---+---+---+---+---+---+---+---+---+------------- - - - -
80     | LINK    | 9 | Q | U | A | D | R | U | P | L | E | 0 | 0 | (definition ...)
81     +---------+---+---+---+---+---+---+---+---+---+---+---+---+------------- - - - -
82        ^       len                                     padding
83        |
84        |
85     LATEST
86
87 The Forth variable LATEST contains a pointer to the most recently defined word.
88
89 ## Threaded code
90
91 In a typical Forth interpreter, code is stored in a peculiar way. (This way of
92 storing code is primarily motivated by space contraints on early systems.)
93
94 The definition of a word is stored as a sequence of memory adresses of each of
95 the words making up that definition. (At the end of a compiled definition, there
96 is also some extra code that causes execution to continue in the correct way.)
97
98 We use a register (ESI) to store a reference to the next index of the
99 word (inside a definition) that we are executing. Then, in order to execute a
100 word, we just jump to whatever address is pointed to by ESI. The code for
101 updating ESI and continuing execution is stored at the end of each subroutine.
102
103 Of course, this approach only works if each of the words that we are executing
104 is defined in assembly, but we also want to be able to execute Forth words!
105
106 We get around this problem by adding a "codeword" to the beginning of any
107 compiled subroutine. This codeword is a pointer to the intrepreter to run the
108 given function. In order to run such functions, we actually need two jumps when
109 executing: In order to execute a word, we jump to the address at the location
110 pointed to by the address in ESI.
111
112 ## Definitions
113
114 What does the codeword of a Forth word contain? It needs to save the old value
115 of ESI (so that we can resume execution of whatever outer definition we are
116 executing at the time) and set the new version of ESI to point to the first word
117 in the inner definition.
118
119 The stack where the values of ESI are stored is called the "return stack". We
120 will use EBP for the return stack.
121
122 As mentioned, whenever we finish executing a Forth word, we will need to
123 continue execution in the manner described in the previous section. When the
124 word being executed is itself written in Forth, we need to pop the old value of
125 ESI that we saved at the beginning of the definition before doing this.
126
127 Thus, the actual data for a word in a dictionary will look something like this:
128
129       pointer to previous word
130        ^
131        |
132     +--|------+---+---+---+---+---+---+---+---+------------+------------+------------+------------+
133     | LINK    | 6 | D | O | U | B | L | E | 0 | DOCOL      | DUP        | +          | EXIT       |
134     +---------+---+---+---+---+---+---+---+---+------------+--|---------+------------+------------+
135        ^       len                         pad  codeword      |
136        |                                                      V
137       LINK in next word                            points to codeword of DUP
138
139 Here, DOCOL (the codeword) is address of the simple interpreter described above,
140 while EXIT a word (implemented in assembly) that takes care of popping ESI and
141 continuing execution. Note that DOCOL, DUP, + and EXIT are all stored as
142 addresses which point to codewords.
143
144 ## Literals
145
146 Literals are handled in a special way. There is a word in Forth, called LIT,
147 implemented in assembly. When executed, this word looks at the next Forth
148 instruction (i.e. the value of ESI), and places that on the stack as a literal,
149 and then manipulates ESI to skip over the literal value.
150
151 ## Built-in variables
152
153 - **STATE** -- Is the interpreter executing code (0) or compiling a word (non-zero)?
154 - **LATEST** -- Points to the latest (most recently defined) word in the dictionary.
155 - **HERE** -- Points to the next free byte of memory. When compiling, compiled words go here.
156 - **S0** -- Stores the address of the top of the parameter stack.
157 - **BASE** -- The current base for printing and reading numbers.
158
159 ## Input and lookup
160
161 `WORD` reads a word from standard input and pushes a string (in the form of an
162 address followed by the length of the string) to the stack. (It uses an internal
163 buffer that is overwritten each time it is called.)
164
165 `FIND` takes a word as parsed by `WORD` and looks it up in the dictionary. It
166 returns the address of the dictionary header of that word if it is found.
167 Otherwise, it returns 0.
168
169 `>CFA` turns a dictionary pointer into a codeword pointer. This is used when
170 compiling.
171
172 ## Compilation
173
174 The Forth word INTERPRET runs in a loop, reading in words (with WORD), looking
175 them up (with FIND), turning them into codeword pointers (with >CFA) and then
176 deciding what to do with them.
177
178 In immediate mode (when STATE is zero), the word is simply executed immediately.
179
180 In compilation mode, INTERPRET appends the codeword pointer to user memory
181 (which is at HERE). However, if a word has the immediate flag set, then it is
182 run immediately, even in compile mode.
183
184 ### Definition of `:` and `;`
185
186 The word `:` starts by reading in the new word. Then it creates a new entry for
187 that word in the dictoinary, updating the contents of `LATEST`, to which it
188 appends the word `DOCOL`. Then, it switches to compile mode.
189
190 The word `;` simply appends `EXIT` to the currently compiling definition and
191 then switches back to immediate mode.
192
193 These words rely on `,` to append words to the currently compiling definition.
194 This word simply appends some literal value to `HERE` and moves the `HERE`
195 pointer forward.
196
197 # Notes on UEFI
198
199 `JONASFORTH` is runs without an operating system, instead using the facilities
200 provided by UEFI by running as a UEFI application. (Or rather, in the future it
201 hopefully will. Right now, it uses Linux.) This section contains some notes
202 about how this functionality is implemented.
203
204 I also wrote an entire tutorial that descirbes how to write and compile a
205 "Hello, World!" UEFI application, including how to run it on real hardware,
206 which you can find here: [Getting started with bare-metal
207 assembly](https://johv.dk/blog/bare-metal-assembly-tutorial.html).
208
209 ## Packaging and testing the image
210
211 UEFI expects a UEFI application to be stored in a FAT32 file system on a
212 GPT-partitioned disk.
213
214 Luckily, QEMU has a convenient way of making a subdirectory availabe as a
215 FAT-formatted disk (see [the relevant section in the QEMU User
216 Documentation](https://qemu.weilnetz.de/doc/qemu-doc.html#disk_005fimages_005ffat_005fimages)
217 for more information):
218
219     $ qemu-sytem-x86_64 ... -hda fat:/some/directory
220
221 We use this to easily test the image in QEMU; see the Makefile for more
222 information, or just run the `qemu` target to run the program inside of QEMU
223 (of course, you must have QEMU installed for this to work):
224
225     $ make qemu
226
227 ## Interfacing with UEFI
228
229 From [OSDev Wiki](https://wiki.osdev.org/UEFI#How_to_use_UEFI):
230
231 > Traditional operating systems like Windows and Linux have an existing software
232 > architecture and a large code base to perform system configuration and device
233 > discovery. With their sophisticated layers of abstraction they don't directly
234 > benefit from UEFI. As a result, their UEFI bootloaders do little but prepare
235 > the environment for them to run.
236 >
237 > An independent developer may find more value in using UEFI to write
238 > feature-full UEFI applications, rather than viewing UEFI as a temporary
239 > start-up environment to be jettisoned during the boot process. Unlike legacy
240 > bootloaders, which typically interact with BIOS only enough to bring up the OS,
241 > a UEFI application can implement sophisticated behavior with the help of UEFI.
242 > In other words, an independent developer shouldn't be in a rush to leave
243 > "UEFI-land".
244
245 For `JONASFORTH`, I have decided to run as a UEFI application, taking advantage
246 of UEFI's features, including its text I/O features and general graphical device
247 drivers. Eventually, we would like to add some basic graphical drawing
248 capabilities to `JONASFORTH`, and it's my impression that this would be possible
249 using what is provided to us by UEFI.
250
251 A UEFI images is basically a windows EXE without symbol tables. There are three
252 types of UEFI images; we use the EFI application, which has subsystem `10`. It
253 is an x68-64 image, which has value `0x8664`.
254
255 UEFI applications use [Microsoft's 64-bit calling
256 convention](https://en.wikipedia.org/wiki/X86_calling_conventions#Microsoft_x64_calling_convention)
257 for x68-64 functions. See the linked article for a full description. Here is
258 the short version:
259
260 - Integer or pointer arguments are given in RCX, RDX, R8 and R9.
261 - Additional arguments are pushed onto the stack from right to left.
262 - Integer or pointer values are returned in RAX.
263 - An integer-sized struct is passed directly; non-integer-sized structs are passed as pointers.
264 - The caller must allocate 32 bytes of "shadow space" on the stack immediately
265   before calling the function, regardless of the number of parameters used, and
266   the caller is responsible for popping the stack afterwards.
267 - The following registers are volatile (caller-saved): RAX, RCX, RDX, R8, R9, R10, R11
268 - The following registers are nonvolatile (callee-saved): RBX, RBP, RDI, RSI, RSP, R12, R13, R14, R15
269
270 When the application is loaded, RCX contains a firmware allocated `EFI_HANDLE`
271 for the UEFI image, RDX contains a `EFI_SYSTEM_TABLE*` pointer to the EFI system
272 table and RSP contains the return address. For more infromation about how a UEFI
273 application is entered, see "4 - EFI System Table" in [the latest UEFI
274 specification as of March 2020 (PDF)](https://uefi.org/sites/default/files/resources/UEFI_Spec_2_8_A_Feb14.pdf).
275
276 **Sources:**
277
278 - [UEFI applications in detail - OSDev Wiki](https://wiki.osdev.org/UEFI#UEFI_applications_in_detail)
279 - [Microsoft x64 calling convention](https://en.wikipedia.org/wiki/X86_calling_conventions#Microsoft_x64_calling_convention)
280 - [UEFI Specifications](https://uefi.org/specifications)
281
282 ### UEFI with FASM
283
284 We might want to consider using something like this: https://wiki.osdev.org/Uefi.inc)
285
286 FASM can generate UEFI application binaries by default. Use the following
287 template to output a 64-bit UEFI application:
288
289     format pe64 dll efi
290     entry main
291
292     section '.text' code executable readable
293
294     main:
295        ;; ...
296        ret
297
298     section '.data' data readable writable
299
300     ;; ...
301
302 Use `objdump -x` to inspect the assembled application binary.
303
304 ### UEFI documentation
305
306 - [Latest specification as of March 2020 (PDF)](https://uefi.org/sites/default/files/resources/UEFI_Spec_2_8_A_Feb14.pdf)
307
308 Notable sections:
309
310 - 2\. Overview (14)
311 - 4\. EFI System Table (89)
312 - 7\. Services - Boot Services (140)
313 - 8\. Services - Runtime Services (228)
314 - 12\. Protocols - Console Support (429)
315 - 13\. Protocols - Media Access (493)
316 - Appendix B - Console (2201)
317 - Appendix D - Status Codes (2211)
318
319 ## Resources
320
321 - [UEFI - OSDev Wiki](https://wiki.osdev.org/UEFI)
322 - [Unified Extensible Firmware Interface (Wikipedia)](https://en.wikipedia.org/wiki/Unified_Extensible_Firmware_Interface)
323 - [UEFI Specifications](https://uefi.org/specifications)