X-Git-Url: https://git.rrq.au/?a=blobdiff_plain;f=main.asm;h=cbf8a139d6e4df42f6cfc6ab17b432b39a0efbe3;hb=623eb650651ba4105e4e91239c52b04ed0e5cd85;hp=28cd430df50123520fb322304c3c765e84d788d6;hpb=393bd34286ed22c677fbbc70c5a3e70aa655c09f;p=rrq%2Fjonasforth.git diff --git a/main.asm b/main.asm index 28cd430..cbf8a13 100644 --- a/main.asm +++ b/main.asm @@ -59,8 +59,14 @@ macro forth label, name { dq docol } + + segment readable executable +entry main + +include "impl.asm" + main: cld ; Clear direction flag so LODSQ does the right thing. mov rbp, return_stack_top ; Initialize return stack @@ -95,37 +101,15 @@ forth_asm LIT, 'LIT' ;; dictionary entry for that word. If no such word exists, return 0. forth_asm FIND, 'FIND' mov [.rsi], rsi - pop [.search_length] - pop [.search_buffer] - ;; RSI contains the entry we are currently looking at + pop [find.search_length] + pop [find.search_buffer] mov rsi, [latest_entry] ; Start with the last added word + call find + push rsi -.loop: - movzx rcx, byte [rsi + 8] ; Length of word being looked at - cmp rcx, [.search_length] - jne .next ; If the words don't have the same length, we have the wrong word - - ;; Otherwise, we need to compare strings - lea rdx, [rsi + 8 + 1] ; Location of character being compared in entry - mov rdi, [.search_buffer] ; Location of character being compared in search buffer -.compare_char: - mov al, [rdx] - mov ah, [rdi] - cmp al, ah - jne .next ; They don't match; try again - inc rdx ; These characters match; look at the next ones - inc rdi - loop .compare_char - - jmp .found ; They match! We are done. - -.next: - mov rsi, [rsi] ; Look at the previous entry - cmp rsi, 0 - jnz .loop ; If there is no previous word, exit and return 0 - -.found: + mov rsi, [.rsi] + next push rsi mov rsi, [.rsi] @@ -200,98 +184,25 @@ forth SPACE, 'SPACE' ;; size. The pointer is valid until the next call to READ_WORD. forth_asm READ_WORD, 'READ-WORD' mov [.rsi], rsi - mov [.rax], rax - -.skip_whitespace: - ;; Read characters into .char_buffer until one of them is not whitespace. - mov rax, 0 - mov rdi, 0 - mov rsi, .char_buffer - mov rdx, 1 - syscall - - cmp [.char_buffer], ' ' - je .skip_whitespace - cmp [.char_buffer], $A - je .skip_whitespace - -.alpha: - ;; We got a character that wasn't whitespace. Now read the actual word. - mov [.length], 0 - -.read_alpha: - mov al, [.char_buffer] - movzx rbx, [.length] - mov rsi, .buffer - add rsi, rbx - mov [rsi], al - inc [.length] - - mov rax, 0 - mov rdi, 0 - mov rsi, .char_buffer - mov rdx, 1 - syscall - cmp [.char_buffer], ' ' - je .end - cmp [.char_buffer], $A - jne .read_alpha - -.end: - push .buffer - movzx rax, [.length] - push rax + call read_word + push rdi ; Buffer + push rdx ; Length mov rsi, [.rsi] - mov rax, [.rax] - next ;; Takes a string on the stack and replaces it with the decimal number that the ;; string represents. forth_asm PARSE_NUMBER, 'PARSE-NUMBER' - pop [.length] ; Length - pop rdi ; String pointer - mov r8, 0 ; Result - - ;; Add (10^(rcx-1) * parse_char(rdi[length - rcx])) to the accumulated value - ;; for each rcx. - mov rcx, [.length] -.loop: - ;; First, calcuate 10^(rcx - 1) - mov rax, 1 - - mov r9, rcx - .exp_loop: - dec r9 - jz .break - mov rbx, 10 - mul rbx - jmp .exp_loop - .break: - - ;; Now, rax = 10^(rcx - 1). - - ;; We need to calulate the value of the character at rdi[length - rcx]. - mov rbx, rdi - add rbx, [.length] - sub rbx, rcx - movzx rbx, byte [rbx] - sub rbx, '0' - - ;; Multiply this value by rax to get (10^(rcx-1) * parse_char(rdi[length - rcx])), - ;; then add this to the result. - mul rbx - - ;; Add that value to r8 - add r8, rax - - dec rcx - jnz .loop + pop [parse_number.length] ; Length + pop [parse_number.buffer] ; String pointer - push r8 + push rsi + call parse_number + pop rsi + push rax ; Result next forth READ_NUMBER, 'READ-NUMBER' @@ -331,6 +242,55 @@ forth HELLO, 'HELLO' dq NEWLINE dq EXIT +;; Duplicate a pair of elements. +forth_asm PAIRDUP, '2DUP' + pop rbx + pop rax + push rax + push rbx + push rax + push rbx + next + +;; Swap the top two elements on the stack. +forth_asm SWAP, 'SWAP' + pop rax + pop rbx + push rax + push rbx + next + +;; Remove the top element from the stack. +forth_asm DROP, 'DROP' + add rsp, 8 + next + +;; The INTERPRET word reads and interprets user input. It's behavior depends on +;; the current STATE. It provides special handling for integers. (TODO) +forth INTERPRET, 'INTERPRET' + ;; Read word + dq READ_WORD + dq PAIRDUP + ;; Stack is (word length word length). + dq FIND ; Try to find word + dq DUP_ + dq ZBRANCH, 8 * 8 ; Check if word is found + + ;; Word is found, execute it + dq TCFA + ;; Stack is (word length addr) + dq SWAP, DROP + dq SWAP, DROP + ;; Stack is (addr) + dq EXEC + dq EXIT + + ;; No word is found, assume it is an integer literal + ;; Stack is (word length addr) + dq DROP + dq PARSE_NUMBER + dq EXIT + ;; .U prints the value on the stack as an unsigned integer in hexadecimal. forth_asm DOTU, '.U' mov [.length], 0 @@ -392,39 +352,60 @@ forth_asm DOTU, '.U' pop rsi next +;; Takes a value and an address, and stores the value at the given address. +forth_asm PUT, '!' + pop rbx ; Address + pop rax ; Value + mov [rbx], rax + next + +;; Takes an address and returns the value at the given address. +forth_asm GET, '@' + pop rax + mov rax, [rax] + push rax + next + +;; Get the location of the STATE variable. It can be set with '!' and read with +;; '@'. +forth STATE, 'STATE' + dq LIT, var_STATE + dq EXIT + +;; Get the location of the LATEST variable. It can be set with '!' and read with +;; '@'. +forth LATEST, 'LATEST' + dq LIT, latest_entry + dq EXIT + +;; Get the location at which compiled words are expected to be added. This +;; pointer is usually modified automatically when calling ',', but we can also +;; read it manually with 'HERE'. +forth HERE, 'HERE' + dq LIT, here + dq EXIT + forth MAIN, 'MAIN' dq HELLO - dq READ_WORD, FIND, TCFA, EXEC - dq BRANCH, -8 * 5 + dq INTERPRET + dq BRANCH, -8 * 2 dq TERMINATE segment readable writable +;; The LATEST variable holds a pointer to the word that was last added to the +;; dictionary. This pointer is updated as new words are added, and its value is +;; used by FIND to look up words. latest_entry dq initial_latest_entry -SPACE_string db 'SPACE' -.length = $ - SPACE_string -HELLO_string db 'HELLO' -.length = $ - HELLO_string -DOTU_string db '.U' -.length = $ - DOTU_string -HELLA_string db 'HELLA' -.length = $ - HELLA_string - - -you_typed_string db 'You typed: ' -.length = $ - you_typed_string +;; The STATE variable is 0 when the interpreter is executing, and non-zero when +;; it is compiling. +var_STATE dq 0 -FIND.search_length dq ? -FIND.search_buffer dq ? FIND.rsi dq ? READ_WORD.rsi dq ? -READ_WORD.rax dq ? -READ_WORD.max_size = $FF -READ_WORD.buffer rb READ_WORD.max_size -READ_WORD.length db ? -READ_WORD.char_buffer db ? +READ_WORD.rbp dq ? DOTU.chars db '0123456789ABCDEF' DOTU.buffer rq 16 ; 64-bit number has no more than 16 digits in hex @@ -432,7 +413,9 @@ DOTU.rbuffer rq 16 DOTU.length dq ? DOTU.printed_length dq ? -PARSE_NUMBER.length dq ? +;; Reserve space for compiled words, accessed through HERE. +here dq here_top +here_top rq $2000 ;; Return stack rq $2000