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 on the stack and replaces it with the decimal number that the
162 ;; string represents.
166 pop [.length] ; Length
167 pop rdi ; String pointer
170 ;; Add (10^(rcx-1) * parse_char(rdi[length - rcx])) to the accumulated value
174 ;; First, calcuate 10^(rcx - 1)
186 ;; Now, rax = 10^(rcx - 1).
188 ;; We need to calulate the value of the character at rdi[length - rcx].
192 movzx rbx, byte [rbx]
195 ;; Multiply this value by rax to get (10^(rcx-1) * parse_char(rdi[length - rcx])),
196 ;; then add this to the result.
199 ;; Add that value to r8
215 ;; Takes a string (in the form of a pointer and a length on the stack) and
216 ;; prints it to standard output.
233 ;; Exit the program cleanly.
254 push you_typed_string
255 push you_typed_string.length
269 ;; .U prints the value on the stack as an unsigned integer in hexadecimal.
274 mov [.printed_length], 1
275 pop rax ; RAX = value to print
276 push rsi ; Save value of RSI
278 ;; We start by constructing the buffer to print in reverse
283 div rbx ; Put remainer in RDX and quotient in RAX
285 ;; Place the appropriate character in the buffer
294 ;; .printed_length is the number of characters that we ulitmately want to
295 ;; print. If we have printed a non-zero character, then we should update
298 je .skip_updating_real_length
300 mov [.printed_length], rbx
301 .skip_updating_real_length:
306 ;; Flip buffer around, since it is currently reversed
307 mov rcx, [.printed_length]
315 add rdi, [.printed_length]
325 mov rdx, [.printed_length]
328 ;; Restore RSI and continue execution
335 dq READ_NUMBER, DOTU, NEWLINE
339 segment readable writable
341 you_typed_string db 'You typed: '
342 .length = $ - you_typed_string
346 READ_WORD.max_size = $FF
347 READ_WORD.buffer rb READ_WORD.max_size
348 READ_WORD.length db ?
349 READ_WORD.char_buffer db ?
351 DOTU.chars db '0123456789ABCDEF'
352 DOTU.buffer rq 16 ; 64-bit number has no more than 16 digits in hex
355 DOTU.printed_length dq ?
357 PARSE_NUMBER.length dq ?