X-Git-Url: https://git.rrq.au/?a=blobdiff_plain;f=main.fasm;h=f06c158bd5a9879f64f804185a7292861a307d38;hb=1c48f378a2c7786735d05df33dd145c59fb49a12;hp=25e48b4f69a9aa1daf2561f2a24230d3f848a771;hpb=9c582d87bca4bce0aa889cad0bd200ee85512f78;p=rrq%2Frrqforth.git diff --git a/main.fasm b/main.fasm index 25e48b4..f06c158 100644 --- a/main.fasm +++ b/main.fasm @@ -1,79 +1,220 @@ -; This is a forth interpreter +; This is a forth interpreter for x86_64 (elf64) format elf64 executable - entry main_code + entry main + +;;; ======================================== +;;; The pushr macro pushes x onto the return stack +;;; The popr macro pops x from the return stack +macro pushr x { + sub rbp, 8 + mov [rbp], x +} + +macro popr x { + mov x, [rbp] + add rbp, 8 +} + +;;; ======================================== +;;; The next macro "moves" execution to the next FORTH instruction, +;;; using rsi as instruction pointer. It points to the doer field of a +;;; word, which points to the assembly code that implements the +;;; execution effect of the word. That doer code is entered with rsi +;;; referring to the subsequent address in the colling word, and rax +;;; referring to the doer field of the called word. + +macro next { + lodsq ; mov rax, [rsi] + add rsi,8 + jmp qword [rax] ; goto code of that FORTH word (64 bit jump) +} + +;;; ======================================== +;;; The FORTH macro transitions to inline FORTH execution. +macro FORTH { + local forthcode + mov rsi,forthcode + next + ;; align 8 +forthcode: +} + +;;; ======================================== +;;; The ENDFORTH macro transitions back to inline assembler after FORTH +;;; ======================================== +macro ENDFORTH { + dq inline_code +} + +;;; ======================================== +;;; The DOFORTH lays out a single FORTH call +;;; ======================================== +macro DOFORTH label { + FORTH + dq label + ENDFORTH +} + +;;; Macro WORD starts a FORTH word definition in this code +;;; + previous_word = 0 ; Used for chaining the words + + IMMEDIATE = 1 ; optional flag + +macro WORD label, name, doer, flags { + ;; align 8 +label#_TFA: + ;; TFA + dq previous_word + previous_word = label#_TFA + ;; PFA +label#_PFA: + db flags + 0 + db label - $ - 2 + db name + db 0 + ;; align 8 + +label#_OFF: + dq 0 ; The DOES offset. Defaults to 0. + ;; also CFA = pointer to "doer" +label: + if doer eq + dq doforth + else + if doer in + dq label#_DFA + else + dq doer + end if + end if + ;; DFA +label#_DFA: +} + +;;; ============================================================ +;;; FORTH machine model +;;; rsp = data stack pointer +;;; rbp = return stack pointer +;;; rsi = instruction pointer + +;;; ============================================================ -;;; ############################################################ -;;; The FORTH words segment readable writable executable -include 'machine.fasm' - - ;; PROGRAM_VERSION is the program version string - WORD program_version, 'PROGRAM_VERSION', marker - db length -program_version_string: - include 'version' - length = $ - program_version_string + WORD return_stack,'RS',dovariable + ;; The return stack + rb 1048576 ; 1 Mb return stack +RS_TOP: ; The initial rbp - ;; MAIN is the program entry point - ;; ( -- ) - WORD_assembler main, "MAIN" - mov rsi,program_version_string ; address of string - mov edx,length ; length of string (cheating) - mov edi,1 ; stdout - mov eax,1 ; sys_write - syscall - jmp terminate0_code + WORD data_stack,'DS',dovariable + ;; The data stack + rb 1048576 ; 1 Mb data stack +DS_TOP: ; The initial rsp - ;; TERMINATE0 terminates the program with code 0 + WORD inline_code,'[ASM]',fasm ;; ( -- ) - WORD_assembler terminate0, 'TERMINATE0' - xor edi,edi - mov eax,60 - syscall + ;; This transitions execution into inline assembler in the + ;; calling word defintion. Note that it stops advancing rsi; + ;; code should use FORTH macro to reenter forth execution, or + ;; exit to the calling definition via "jmp exit". + jmp qword rsi - ;; EXIT ends a forth code defintion, returning to caller - ;; ( -- ) - WORD_assembler exit, 'EXIT' +;;; Execution semantics for FORTH defition word +;;; At entry, rsi points into the calling definition, at the cell +;;; following the cell indicating this word, rax points to the CFA of +;;; this word. +doforth: + pushr rsi + lea rsi, [rax+8] ; rsi = the DFA of the rax word + next + + WORD p_exit, 'EXIT',fasm + ;; ( -- ) ( R: addr -- ) + ;; Returns execution to the calling definition as per the + ;; return stack. +exit: popr rsi next - - ;; MARKER is a word that pushes the address after itself, then exits - ;; ( -- p ) - WORD_assembler marker, 'MARKER' - push qword rsi - jmp exit_code - - ;; MARKER@ is a word that pushes a the value after itself, then exits - ;; ( -- v ) - WORD_assembler marker_get, 'MARKER@' - push qword [rsi] - jmp exit_code - - ;; DOFORTH begins a FORTH defintion - WORD_assembler doforth, 'DOFORTH' - pushr rsi - lea rsi, [rax + 8] + + ;; Execution semantics for a variable ( -- addr ) + ;; rax points to doer field +dovariable: + push rax+16 + next + + ;; Execution semantics for a constant ( -- v ) + ;; rax points to doer field +dovalue: + push qword [rax+16] next - ;; LIT is a word that pushes a the value after itself, then continues - ;; ( -- v ) - WORD_assembler lit, 'LIT' - push qword [rsi] - add rsi, 8 + ;; Execution semantics for a string constant ( -- addr n ) + ;; rax points to doer field +dostring: + add rax,16 + mov bl,[rax] + mov byte [rsp],bl + push rax+1 next - ;; HERE is a variable pointing to the free heap - WORD here, 'HERE', marker - dq heap_start ; initialise to first "free" data +include 'wordlists.fasm' +include 'syscalls.fasm' +include 'memory.fasm' +include 'stack.fasm' +include 'math.fasm' +include 'stdio.fasm' - ;; WORDS is the list of words - WORD words, 'WORDS', marker - dq forth_tfa ; initialise to last forth word + WORD p_program_version,'PROGRAM_VERSION',dostring + db length +program_version_string: + db 'RRQ Forth version 0.1 - 2021-05-13',10 + length = $ - program_version_string - ;; FORTH is the last word of the VOCABULARY - WORD forth, 'FORTH', marker_get - dq forth_tfa + WORD p_stdin,'STDIN',dovalue + ;; Initialised to hold a STREAM for fd 0 + dq 0 -heap_start: + WORD p_quit,'QUIT',fasm + ;; QUIT is the program entry point ******************** +main: + mov rsp,DS_TOP + mov rbp,RS_TOP + ;; Initialize STREAM STDIN + push 0 + push 10000 + DOFORTH p_stream + pop qword [p_stdin_DFA] + + ;; read a word + push qword 1 ; ( fd ) =stdout + push qword [p_stdin_DFA] + FORTH + dq p_read_word ; ( fd s n ) + dq sys_write + ENDFORTH + + push qword 1 ; stdout + push qword program_version_string ; address of string + push qword length ; length of string (cheating) + DOFORTH sys_write ; printout + pop rax ; ignore errors + + push 0 + DOFORTH sys_exit + ;; TERMINATE0 terminates the program with code 0 + ;; ( v -- ) + WORD terminate, 'TERMINATE',fasm + pop rdx +terminate_special: + mov eax,60 + syscall + +last_word: + ;; FORTH is the last word of VOCABULARY FORTH + WORD forth,'FORTH',dovalue + dq forth_TFA + dq 0 + + +heap_start: