1 format ELF64 executable
3 ;; The code in this macro is placed at the end of each Forth word. When we are
4 ;; executing a definition, this code is what causes execution to resume at the
5 ;; next word in that definition.
7 ;; RSI points to the address of the definition of the next word to execute.
8 lodsq ; Load value at RSI into RAX and increment RSI
9 ;; Now RAX contains the location of the next word to execute. The first 8
10 ;; bytes of this word is the address of the codeword, which is what we want
12 jmp qword [rax] ; Jump to the codeword of the current word
15 ;; pushr and popr work on the return stack, whose location is stored in the
26 segment readable executable
29 cld ; Clear direction flag so LODSQ does the right thing.
30 mov rbp, return_stack_top ; Initialize return stack
37 ;; The codeword is the code that will be executed at the beginning of a forth
38 ;; word. It needs to save the old RSI and update it to point to the next word to
41 pushr rsi ; Save old value of RSI on return stack; we will continue execution there after we are done executing this word
42 lea rsi, [rax + 8] ; RAX currently points to the address of the codeword, so we want to continue at RAX+8
43 next ; Execute word pointed to by RSI
45 ;; This word is called at the end of a Forth definition. It just needs to
46 ;; restore the old value of RSI (saved by 'docol') and resume execution.
53 ;; LIT is a special word that reads the next "word pointer" and causes it to be
54 ;; placed on the stack rather than executed.
62 ;; 0BRANCH is the fundamental mechanism for branching. If the top of the stack
63 ;; is zero, we jump by the given offset. 0BRANCH is given the offset as an
64 ;; integer after the word.
68 ;; Compare top of stack to see if we should branch
73 add rsi, [rsi] ; [RSI], which is the next word, contains the offset; we add this to the instruction pointer.
74 next ; Then, we can just continue execution as normal
76 add rsi, 8 ; We need to skip over the next word, which contains the offset.
79 ;; Expects a character on the stack and prints it to standard output.
95 ;; Prints a newline to standard output.
102 ;; Read a word from standard input and push it onto the stack as a pointer and a
103 ;; size. The pointer is valid until the next call to READ_WORD.
111 ;; Read characters into .char_buffer until one of them is not whitespace.
114 mov rsi, .char_buffer
118 cmp [.char_buffer], ' '
120 cmp [.char_buffer], $A
124 ;; We got a character that wasn't whitespace. Now read the actual word.
128 mov al, [.char_buffer]
137 mov rsi, .char_buffer
141 cmp [.char_buffer], ' '
143 cmp [.char_buffer], $A
156 ;; Takes a string (in the form of a pointer and a length on the stack) and
157 ;; prints it to standard output.
174 ;; Exit the program cleanly.
195 push you_typed_string
196 push you_typed_string.length
214 dq LIT, you_typed_string
215 dq LIT, you_typed_string.length
222 segment readable writable
224 you_typed_string db 'You typed: '
225 .length = $ - you_typed_string
229 READ_WORD.max_size = $FF
230 READ_WORD.buffer rb READ_WORD.max_size
231 READ_WORD.length db ?
232 READ_WORD.char_buffer db ?