added command line processing
[rrq/rrqforth.git] / rrqforth.asm
1 ; This is a forth interpreter for x86_64 (elf64)
2         format elf64 executable
3         entry main
4
5 include 'machine.asm'
6
7 ;;; ============================================================
8
9         segment readable writable executable
10         
11 ;;; ========================================
12 ;;; These are the core "execution semantics" words, which are placed
13 ;;; first so as to remain at the same binary address at successive
14 ;;; compilations, which is helful for declaring special debugging gdb
15 ;;; aliases.
16 ;;; 
17 ;;; The DO* words are declared as "variables" to provide their
18 ;;; assembled address when used in FORTH.
19 ;;;
20 ;;; The register context at entry to an "execution semantcs" code
21 ;;; snippets is:
22 ;;; rax = cfa* of word to execute
23 ;;; rsi = cell* in the calling definition, after calling cell
24 ;;; rsp = data stack pointer
25 ;;; rbp = return stack pointer
26 ;;; 
27
28 previous_word = 0       ; Used for chaining the words
29
30         WORD p_dofasm,'doFASM',dovariable
31         ;; Execution semantics for assembly words.
32 dofasm:
33         add rax,8
34         jmp rax
35
36         WORD p_doforth,'doFORTH',dovariable ;
37         ;; Execution semantics for FORTH defition word.
38 doforth:
39         pushr rsi
40         lea rsi, [rax+8]        ; rsi = the DFA of the rax word
41         next
42
43         WORD p_dodoes,'doDOES',dovariable
44         ;; Execution semantics for DOES>
45         ;; [cfa-8] holds the adjustment offset ("does offset")
46 dodoes:
47         pushr rsi
48         lea rsi, [rax+8]        ; rsi = the DFA of the rax word
49         add rsi,qword [rax-8]   ; adjust rsi by the "does offset'
50         next
51
52         WORD p_dovariable,'doVARIABLE',dovariable
53         ;; Execution semantics for a variable ( -- addr )
54         ;; rax points to CFA field
55 dovariable:
56         lea rax, [rax+8]        ; rsi = the DFA of the rax word
57         push rax
58         next
59
60         WORD p_dovalue,'doVALUE',dovariable
61         ;; Execution semantics for a value constant ( -- v )
62         ;; rax points to CFA field
63 dovalue:
64         lea rax, [rax+8]        ; rsi = the DFA of the rax word
65         push qword [rax]
66         next
67
68         WORD p_dostring,'doSTRING',dovariable
69         ;; Execution semantics for a string constant ( -- addr n )
70         ;; rax points to CFA field
71 dostring:
72         lea rax, [rax+8]        ; rsi = the DFA of the rax word
73         pushpname rax
74         next
75
76         WORD p_calltrace,'[calltrace]',dovalue
77         ;; Common call point for debugging
78         ;; rax = cfa of called word
79         ;; rsi = cell* of next forth word
80         ;; [$rsp] = from where the call was
81         ret
82
83 include 'syscalls.asm'
84
85 ;;; ========================================
86 ;;; The stacks are placed here.
87         
88         ;segment readable writable
89
90         WORD return_stack,'RETURN-STACK',dovariable
91         ;; The return stack
92         BLOCK RS_TOP
93         rb 1048576              ; 1 Mb return stack
94 RS_TOP:                         ; The initial rbp
95         
96 last_system_word:
97         WORD data_stack,'DATA-STACK',dovariable
98         ;; The data stack
99         BLOCK DS_TOP
100         rb 1048576              ; 1 Mb data stack
101 DS_TOP:                         ; The initial rsp
102
103
104 ;;; ========================================
105 ;;; Core execution control words
106
107         ;segment readable executable
108
109 include 'signals.asm'
110
111 ;;; At fasm compilation: reset previous_word to make a new word list
112 ;;; Words above belong to the SYSTEM wordlist, and the following
113 ;;; belong to the FORTH wordlist.
114 previous_word = 0
115
116         WORD p_system,'SYSTEM',dovariable
117         ;; ( -- dfa )
118         ;; The SYSTEM word list
119         dq last_system_word     ; tfa of last SYSTEM word
120         dq p_forth_DFA          ; dfa of successor word list
121
122         WORD inline_code,'[ASM]',fasm
123         ;; ( -- )
124         ;; This transitions execution into inline assembler in the
125         ;; calling word defintion. Note that it stops advancing rsi;
126         ;; code should use FORTH macro to reenter forth execution, or
127         ;; exit to the calling definition via "jmp exit".
128         jmp qword rsi
129
130         WORD p_execute,'EXECUTE',fasm
131         ;; ( cfa -- )
132         ;; Execute the word
133         pop rax
134         jmp qword [rax]         ; goto code of that FORTH word (64 bit jump)
135         
136         WORD p_exit, 'EXIT',fasm
137         ;; ( -- ) ( R: addr -- )
138         ;; Returns execution to the calling definition as per the
139         ;; return stack.
140 exit:
141         popr rsi
142         next
143
144         ;; TERMINATE0 terminates the program with code 0
145         ;; ( -- )
146         WORD p_terminate, 'TERMINATE0',fasm
147         pop rdx
148 terminate_special:
149         mov eax,60
150         syscall
151
152 ;;; ========================================
153 ;;; Core extension(s)
154
155         ;segment readable writable executable
156         
157 include 'control.asm'
158 include 'wordlists.asm'
159 include 'memory.asm'
160 include 'stack.asm'
161 include 'math.asm'
162 include 'logic.asm'
163 include 'stdio.asm'
164 include 'temp.asm'
165 include 'compile.asm'
166
167         WORD p_program_version,'PROGRAM_VERSION',dostring
168         STRING 'RRQ Forth version 0.1 - 2021-05-22',10
169
170         WORD p_stdin,'STDIN',dovalue
171         ;; Initialised to hold a STREAM for fd 0
172         dq 0
173
174         WORD p_args,'MAIN-ARGS',dovalue
175         ;; Pointer to initial argument block
176         dq 0       ; *(int argc,(char*[argc]) argv)
177
178         WORD p_verboseQ,'VERBOSE?',dovariable
179         ;; Flags whether the running is in verbose mode ot not
180         dq 0       ; 
181
182         WORD p_lparen,'(',fasm,IMMEDIATE
183         pushr rsi
184 p_lparen_loop:
185         DOFORTH  p_input, p_get, p_read_word
186         pop rax
187         pop rbx
188         cmp rax,0 ; end of stream
189         je p_lparen_exit
190         cmp rax,1
191         jne p_lparen_loop
192         push rbx
193         push p_lparen_rparen
194         push 1
195         DOFORTH p_strncmp
196         pop rax
197         cmp rax,0
198         jne p_lparen_loop
199 p_lparen_exit:
200         popr rsi
201         next
202 p_lparen_rparen: db ')',0
203
204 ;;; ******** The main entry point. ********
205 ;;; This could be set up as a WORD but it isn't
206
207 main:   
208         ;; Initial rsp points to the arguments block of size (64 bits)
209         ;; followed by the argument pointers.
210         mov qword [p_args_DFA],rsp
211         mov rbp,RS_TOP
212         call p_setup_signals_DFA
213         call main_is_verbose
214         mov qword [p_verboseQ_DFA],rdx
215         jmp p_quit_DFA          ; QUIT
216
217         ;; Subroutine to check the command line for a "-v"
218         ;; return boolean in rdx
219         ;; implementation for that 2 byte asciiz string
220 main_is_verbose_data:
221         db '-v',0
222
223 main_is_verbose:
224         mov rbx,qword [p_args_DFA] ; Pointer to main arguments
225         mov r8,qword [rbx]         ; r8 = count of pointers
226         xor rdx,rdx
227         cld
228 main_is_verbose_next:
229         dec r8
230         jl main_is_not_verbose
231         add rbx,8
232         mov rsi,qword [rbx]
233         mov rdi,main_is_verbose_data
234 main_is_verbose_loop:
235         cmpsb
236         jne main_is_verbose_next
237         cmp byte[rsi-1],0
238         jne main_is_verbose_loop
239         not rdx
240 main_is_not_verbose:
241         ret
242
243         WORD p_process_args_var,'PROCESS-ARGS-VAR',dovariable
244         ;; ( -- a )
245         ;; Two cells for iterating and load the main args
246 p_process_args_ptr: dq 0
247 p_process_args_end: dq 0
248         
249         WORD p_process_args,'PROCESS-ARGS',fasm
250         pushr rsi
251         mov rax,qword [p_args_DFA] ; args*
252         mov rbx,qword [rax] ; count
253         shl rbx,3
254         add rax,8
255         add rbx,rax ; end
256         mov qword [p_process_args_end],rbx
257         add rax,8
258         mov qword [p_process_args_ptr],rax
259 p_process_args_loop:
260         mov rax,qword [p_process_args_ptr]
261         cmp rax,qword [p_process_args_end]
262         jge p_process_args_done
263         add qword [p_process_args_ptr],8
264         mov rax,qword [rax]
265         mov bl,[rax]
266         cmp bl,'-'
267         je p_process_args_loop
268         push rax
269         push rax
270         DOFORTH p_strlen, p_load_file, p_drop
271         jmp p_process_args_loop
272 p_process_args_done:
273         popr rsi
274         next
275         
276 ;;; This word is also the last word before syscalls
277 last_forth_word:
278         WORD p_quit,'QUIT',fasm
279         ;; QUIT is the program entry point ********************
280         mov rsp,DS_TOP
281         mov rbp,RS_TOP
282         cmp qword [p_stdin_DFA],0
283         jne p_quit_INITIALIZED
284         ;; Initialize STDIN
285         push 0
286         push 10000
287         DOFORTH p_stream
288         pop qword [p_stdin_DFA] ; Assign STDIN
289         DOFORTH p_process_args
290
291 p_quit_INITIALIZED:
292         ;; Setup INPUT from STDIN
293         FORTH
294         dq p_verboseQ
295         dq p_get
296         BRANCH 0,p_quit_EVAL
297         dq p_program_version
298         dq p_tell
299 p_quit_EVAL:
300         dq p_stdin, p_evaluate_stream
301         BRANCH 0,p_quit_ERROR
302         dq p_false
303         dq sys_exit
304 p_quit_ERROR:
305         dq p_literal_string
306         STRING 10,'*** Unknown word: '
307         dq p_tell
308         dq p_this_word
309         dq p_2get
310         dq p_tell
311         dq p_literal_string
312         STRING 10
313         dq p_tell
314         ENDFORTH
315         mov rbp,RS_TOP          ; reset the return stack
316         jmp p_quit_INITIALIZED
317
318 ;;; ========================================
319
320         ;segment readable writable
321         
322 heap_start:
323         rb 1048576              ; +1 Mb heap
324         rb 1048576              ; +1 Mb heap
325         rb 1048576              ; +1 Mb heap
326         rb 1048576              ; +1 Mb heap
327         rb 1048576              ; +1 Mb heap