popr rsi
next
-;; Read a word from standard input and push it onto the stack as a pointer and a
-;; size. The pointer is valid until the next call to READ_WORD.
+;; Read a word and push it onto the stack as a pointer and a size. The pointer
+;; is valid until the next call to READ_WORD.
forth_asm READ_WORD, 'READ-WORD'
+ ;; Are we reading from user input or from the input buffer?
+ cmp [input_buffer], 0
+ jne .from_buffer
+
+ ;; Reading user input
mov [.rsi], rsi
call read_word
mov rsi, [.rsi]
next
-;; Read a word from a buffer. Expects (buffer buffer-length) on the stack.
-;; Updates buffer and buffer-length, such that the word has been removed from
-;; the buffer. Appends (word-buffer word-buffer-length) to the stack.
-forth_asm POP_WORD, 'POP-WORD'
- pushr rsi
+.from_buffer:
+ ;; Reading from buffer
+ mov [.rsi], rsi
- pop rcx ; Length
- pop rsi ; Buffer
+ mov rsi, [input_buffer]
+ mov rcx, [input_buffer_length]
call pop_word
- push rsi ; Updated buffer
- push rcx ; Length of updated buffer
- push rdi ; Word buffer
- push rdx ; Length of word buffer
+ mov [input_buffer], rsi ; Updated buffer
+ mov [input_buffer_length], rcx ; Length of updated buffer
+ push rdi ; Word buffer
+ push rdx ; Length of word buffer
- popr rsi
+ mov rsi, [.rsi]
next
;; Takes a string on the stack and replaces it with the decimal number that the
push rdx ; a % b
next
-;; Read user input until next " character is found. Push a string containing the
+;; Read input until next " character is found. Push a string containing the
;; input on the stack as (buffer length). Note that the buffer is only valid
-;; until the next call to S" and that no more than 255 character can be read.
+;; until the next call to S" and that no more than 255 characters can be read.
forth_asm READ_STRING, 'S"'
+ ;; If the input buffer is set, we should read from there instead.
+ cmp [input_buffer], 0
+ jne read_string_buffer
+
push rsi
mov [.length], 0
next
+read_string_buffer:
+ push rsi
+
+ ;; We borrow READ_STRING's buffer. They won't mind.
+ mov [READ_STRING.length], 0
+
+.read_char:
+ mov rbx, [input_buffer]
+ mov al, [rbx]
+ cmp al, '"'
+ je .done
+
+ mov rdx, READ_STRING.buffer
+ add rdx, [READ_STRING.length]
+ mov [rdx], al
+ inc [READ_STRING.length]
+
+ inc [input_buffer]
+ dec [input_buffer_length]
+
+ jmp .read_char
+
+.done:
+ pop rsi
+
+ ;; Skip closing "
+ inc [input_buffer]
+ dec [input_buffer_length]
+
+ push READ_STRING.buffer
+ push [READ_STRING.length]
+
+ next
+
;; CREATE inserts a new header in the dictionary, and updates LATEST so that it
;; points to the header. To compile a word, the user can then call ',' to
;; continue to append data after the header.
next
forth MAIN, 'MAIN'
+ dq SYSCODE
+ dq INTERPRET_STRING
dq INTERPRET
dq BRANCH, -8 * 2
dq TERMINATE
dq LIT, sysf.len
dq EXIT
+forth INPUT_BUFFER, 'INPUT-BUFFER'
+ dq LIT, input_buffer
+ dq EXIT
+
+forth INPUT_LENGTH, 'INPUT-LENGTH'
+ dq LIT, input_buffer_length
+ dq EXIT
+
segment readable writable
;; The LATEST variable holds a pointer to the word that was last added to the
;; it is compiling.
var_STATE dq 0
+;; The interpreter can read either from standard input or from a buffer. When
+;; input-buffer is set (non-null), words like READ-WORD and S" will use this
+;; buffer instead of reading user input.
+input_buffer dq 0
+input_buffer_length dq 0
+
FIND.rsi dq ?
READ_WORD.rsi dq ?