Get rid of remaining "loose" syscalls
[rrq/jonasforth.git] / bootstrap.asm
1 ;; vim: syntax=fasm
2
3 ;; We need some basic words to be available before we can implement the actual
4 ;; interpreter. For this reason we need to write some words in assembly, even
5 ;; though they depend only on other Forth words. Such words are defined in this
6 ;; file.
7 ;;
8 ;; With these words, we can finally defined INTERPRET, and from there we'll load
9 ;; everything else from an external file.
10 ;;
11 ;; This file is included from main.asm; see that file for more information.
12
13 ;; Define a Forth word that is implemented in Forth. (The body will be a list of
14 ;; 'dq' statements.)
15 macro forth label, name, immediate {
16   header label, name, immediate
17   dq DOCOL
18 }
19
20 forth COMMA, ','
21   dq HERE, GET, PUT             ; Set the memory at the address pointed to by HERE
22   dq HERE, GET, LIT, 8, PLUS    ; Calculate new address for HERE to point to
23   dq HERE, PUT                  ; Update HERE to point to the new address
24   dq EXIT
25
26 ;; Mark the last added word as immediate.
27 forth IMMEDIATE, 'IMMEDIATE', 1
28   dq LIT, 1
29   dq LATEST, GET
30   dq LIT, 8, PLUS
31   dq PUT_BYTE
32   dq EXIT
33
34 ;; Given the address of a word, return 0 if the given word is not immediate.
35 forth IS_IMMEDIATE, 'IMMEDIATE?'
36   dq LIT, 8, PLUS
37   dq GET_BYTE
38   dq EXIT
39
40 ;; Enter immediate mode, immediately
41 forth INTO_IMMEDIATE, '[', 1
42   dq LIT, 0, STATE, PUT_BYTE
43   dq EXIT
44
45 ;; Enter compilation mode
46 forth OUTOF_IMMEDIATE, ']'
47   dq LIT, 1, STATE, PUT_BYTE
48   dq EXIT
49
50 ;; INTERPRET-WORD expects a word as a (buffer, length) pair on the stack. It
51 ;; interprets and executes the word. It's behavior depends on the current STATE.
52 ;; It provides special handling for integers.
53 forth INTERPRET_WORD, 'INTERPRET-WORD'
54   dq PAIRDUP
55   ;; Stack is (word length word length).
56   dq FIND                       ; Try to find word
57   dq DUP_
58   dq ZBRANCH, 8 * 22            ; Check if word is found
59
60   ;; - Word is found -
61
62   dq STATE, GET, ZBRANCH, 8 * 11 ; Check whether we are in compilation or immediate mode
63
64   ;; (Word found, compilation mode)
65   dq DUP_, IS_IMMEDIATE, NOT_, ZBRANCH, 8 * 6 ; If the word is immediate, continue as we would in immediate mode
66
67   ;; Otherwise, we want to compile this word
68   dq TCFA
69   dq COMMA
70   dq DROP, DROP
71   dq EXIT
72
73   ;; (Word found, immediate mode)
74   ;; Execute word
75   dq TCFA
76   ;; Stack is (word length addr)
77   dq SWAP, DROP
78   dq SWAP, DROP
79   ;; Stack is (addr)
80   dq EXEC
81   dq EXIT
82
83   ;; - No word is found, assume it is an integer literal -
84   ;; Stack is (word length addr)
85   dq DROP
86   dq PARSE_NUMBER
87
88   dq STATE, GET, ZBRANCH, 8 * 5 ; Check whether we are in compilation or immediate mode
89
90   ;; (Number, compilation mode)
91   dq LIT, LIT, COMMA
92   dq COMMA
93   dq EXIT
94
95   ;; (Number, immediate mode)
96   dq EXIT
97
98 ;; The INTERPRET word reads and interprets a single word from the user.
99 forth INTERPRET, 'INTERPRET'
100   dq READ_WORD
101   dq INTERPRET_WORD
102   dq EXIT
103
104 ;; INTERPRET_STRING is a variant of INTERPRET that reads from a string instead
105 ;; of from the user. It takes a string as a (buffer, length) pair on the stack
106 ;; and interprets the entire string, even if the string has more than one word.
107 forth INTERPRET_STRING, 'INTERPRET-STRING'
108   dq INPUT_LENGTH, PUT
109   dq INPUT_BUFFER, PUT
110
111   ;; Check if the buffer is-non-empty
112   ;; [TODO] This probably won't work for strings with whitespace at the end.
113   dq INPUT_LENGTH, GET
114   dq ZBRANCH, 8 * 5 ; to EXIT
115
116   dq READ_WORD
117
118   dq INTERPRET_WORD
119   dq BRANCH, -8 * 7 ; to INPUT-LENGTH @
120
121   dq LIT, 0, INPUT_BUFFER, PUT
122
123   dq EXIT