From: Ralph Ronnquist Date: Thu, 13 May 2021 02:25:13 +0000 (+1000) Subject: baseline X-Git-Url: https://git.rrq.au/?a=commitdiff_plain;h=9c582d87bca4bce0aa889cad0bd200ee85512f78;p=rrq%2Frrqforth.git baseline --- diff --git a/Makefile b/Makefile index 39e68f2..1e629b9 100644 --- a/Makefile +++ b/Makefile @@ -2,9 +2,13 @@ BINARY = rrqforth default: rrqforth +VFMT := "RRQ Forth version %h at %aI" +VERSION := $(shell git log -1 --pretty=format:'$(VFMT)' main.fasm) + +main.fasm: machine.fasm stdio.fasm + version: main.fasm - git log --pretty=format:"msg db 'RRQ Forth version %h at %aI'" $< > $@ - echo "\ndb 10" >> $@ + @echo "db '$(VERSION)'\ndb 10" > $@ rrqforth: main.fasm | version fasm $^ $@ diff --git a/machine.fasm b/machine.fasm new file mode 100644 index 0000000..92f51a1 --- /dev/null +++ b/machine.fasm @@ -0,0 +1,101 @@ +;;; This file define/describes the "machine" +;;; +;;; Abstract Machine: +;;; https://en.wikibooks.org/wiki/X86_Assembly/X86_Architecture +;;; +;;; General Purpose Registers ( * marks those used in syscalls ) +;;; *rax = ( -, eax = ( -, ax = ( ah, al) )) "accumulator" +;;; rbx = ( -, ebx = ( -, bx = ( bh, bl) )) "base" +;;; *rcx = ( -, ecx = ( -, cx = ( ch, cl) )) "counter" +;;; *rdx = ( -, edx = ( -, dx = ( dh, dl) )) "data" +;;; rsp = ( -, esp = ( -, sp = ( -, spl) )) "stack pointer" +;;; rbp = ( -, ebp = ( -, bp = ( -, bpl) )) "stack base pointer" +;;; *rsi = ( -, esi = ( -, si = ( -, sil) )) "source" +;;; *rdi = ( -, edi = ( -, di = ( -, dil) )) "destination" +;;; *r8 +;;; *r9 +;;; r10 +;;; *r11 +;;; r12 +;;; r13 +;;; r14 +;;; r15 +;;; clobbers rdi rsi rdx rcx r8 r9 r11 +;;; rax = syscall id +;;; +;;; Segment Registers +;;; SS "Stack Segment" +;;; CS "Code Segment" +;;; DS "Data Segment" +;;; ES "Extra Segment" +;;; FS "more Extra Segment" +;;; GS "more more Extra Segment" +;;; +;;; EFLAGS Register +;;; 0,0,0,0,0,0,0,0,0,0,ID,VIP,VIF,AC,VM,RF, +;;; 0,NT,[IOPL,IOPL],OF,DF,IF,TF,SF,ZF,0,AF,0,PF,1,CF +;;; +;;; Instruction pointer +;;; EIP +;;; +;;; Syscall allocations +;;; clobbers rdi rsi rdx rcx r8 r9 r11 +;;; rax = syscall id +;;; +;;; function calling + +;;; FORTH model +;;; rsp = data stack pointer +;;; rbp = frame pointer +;;; rdi = frame stack +;;; rsi = instruction pointer + +;;; ======================================== +;;; The next macro "moves" execution to the next FORTH instruction, +;;; using rsi as instruction pointer. + + macro next { + lodsq ; mov rax, qword [rsi] + ; add rsi,8 + jmp qword [rax] ; goto code of that FORTH word + } + +;;; ======================================== +;;; 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 qword [rbp], x + } + + macro popr x { + mov x, [rbp] + add rbp, 8 + } + +;;; ======================================== +;;; + + previous_word = 0 + + ;; Macro WORD starts a FORTH word definition in this code + macro WORD label, name, doer, flags { +label#_tfa: + ;; TFA + dq previous_word + ;; PFA +label#_word: + previous_word = label#_word + db flags + 0 + db label - $ - 1 + db name + ;; CFA = pointer to "interpreter" +label: + dq doer + } + + ;; Macro WORD_assembler begins an assembler implementation + macro WORD_assembler label, name, flags { + WORD label, name, label#_code, flags +label#_code: + } diff --git a/main.fasm b/main.fasm index dd4f30d..25e48b4 100644 --- a/main.fasm +++ b/main.fasm @@ -1,20 +1,79 @@ -; This is a program +; This is a forth interpreter format elf64 executable - entry main + entry main_code - segment readable executable - include 'version' - length = $ - msg +;;; ############################################################ +;;; The FORTH words + segment readable writable executable -main: - lea rsi,[msg] ; address of message - mov edx,length ; length od trdting +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 + + ;; 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 -fini: + ;; TERMINATE0 terminates the program with code 0 + ;; ( -- ) + WORD_assembler terminate0, 'TERMINATE0' xor edi,edi mov eax,60 syscall + + ;; EXIT ends a forth code defintion, returning to caller + ;; ( -- ) + WORD_assembler exit, '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] + 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 + next + + ;; HERE is a variable pointing to the free heap + WORD here, 'HERE', marker + dq heap_start ; initialise to first "free" data + + ;; WORDS is the list of words + WORD words, 'WORDS', marker + dq forth_tfa ; initialise to last forth word + + ;; FORTH is the last word of the VOCABULARY + WORD forth, 'FORTH', marker_get + dq forth_tfa + +heap_start: +