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 ;; BRANCH is the fundamental mechanism for branching. BRANCH reads the next word
63 ;; as a signed integer literal and jumps by that offset.
67 add rsi, [rsi] ; [RSI], which is the next word, contains the offset; we add this to the instruction pointer.
68 next ; Then, we can just continue execution as normal
70 ;; 0BRANCH is like BRANCH, but it jumps only if the top of the stack is zero.
74 ;; Compare top of stack to see if we should branch
81 add rsi, 8 ; We need to skip over the next word, which contains the offset.
84 ;; Expects a character on the stack and prints it to standard output.
100 ;; Prints a newline to standard output.
107 ;; Read a word from standard input and push it onto the stack as a pointer and a
108 ;; size. The pointer is valid until the next call to READ_WORD.
116 ;; Read characters into .char_buffer until one of them is not whitespace.
119 mov rsi, .char_buffer
123 cmp [.char_buffer], ' '
125 cmp [.char_buffer], $A
129 ;; We got a character that wasn't whitespace. Now read the actual word.
133 mov al, [.char_buffer]
142 mov rsi, .char_buffer
146 cmp [.char_buffer], ' '
148 cmp [.char_buffer], $A
161 ;; Takes a string (in the form of a pointer and a length on the stack) and
162 ;; prints it to standard output.
179 ;; Exit the program cleanly.
200 push you_typed_string
201 push you_typed_string.length
215 ;; .U prints the value on the stack as an unsigned integer in hexadecimal.
220 pop rax ; RAX = value to print
221 push rsi ; Save value of RSI
223 ;; We start by constructing the buffer to print in reverse
228 div rbx ; Put remainer in RDX and quotient in RAX
230 ;; Place the appropriate character in the buffer
239 ;; .printed_length is the number of characters that we ulitmately want to
240 ;; print. If we have printed a non-zero character, then we should update
243 je .skip_updating_real_length
245 mov [.printed_length], rbx
246 .skip_updating_real_length:
251 ;; Flip buffer around, since it is currently reversed
252 mov rcx, [.printed_length]
260 add rdi, [.printed_length]
270 mov rdx, [.printed_length]
273 ;; Restore RSI and continue execution
280 dq LIT, 1234567890, DOTU, NEWLINE
281 dq LIT, $ABCD, DOTU, NEWLINE
282 dq LIT, $1234ABCD5678EFAB, DOTU, NEWLINE
285 segment readable writable
287 you_typed_string db 'You typed: '
288 .length = $ - you_typed_string
292 READ_WORD.max_size = $FF
293 READ_WORD.buffer rb READ_WORD.max_size
294 READ_WORD.length db ?
295 READ_WORD.char_buffer db ?
297 DOTU.chars db '0123456789ABCDEF'
298 DOTU.buffer rq 16 ; 64-bit number has no more than 16 digits in hex
301 DOTU.printed_length dq ?