PD release.
[jonesforth.git] / jonesforth.S.4
1 /* A minimal FORTH interpreter for Linux / i386 systems. -*- asm -*-
2  * By Richard W.M. Jones <rich@annexia.org>
3  *
4  * gcc -m32 -nostdlib -static -Wl,-Ttext,0 -o jonesforth jonesforth.S
5  */
6
7 #include <asm-i386/unistd.h>
8
9 /* NEXT macro. */
10         .macro NEXT
11         lodsl
12         jmp *(%eax)
13         .endm
14
15 /* Macros to deal with the return stack. */
16         .macro PUSHRSP reg
17         .endm
18
19         .macro POPRSP reg
20         .endm
21
22 /* ELF entry point. */
23         .text
24         .globl _start
25 _start:
26         cld
27         mov $return_stack,%ebp  // Initialise the return stack.
28
29         mov $cold_start,%esi    // Initialise interpreter.
30         NEXT                    // Run interpreter!
31
32         .section .rodata
33 cold_start:                     // High-level code without a codeword.
34         .int COLD
35
36 /* DOCOL - the interpreter! */
37         .text
38         .align 4
39 DOCOL:
40         lea -4(%ebp),%ebp       // push %esi on to the return stack
41         movl %esi,(%ebp)
42
43         addl $4,%eax            // %eax points to codeword, so make
44         movl %eax,%esi          // %esi point to first data word
45         NEXT
46
47 /*----------------------------------------------------------------------
48  * Fixed sized buffers for everything.
49  */
50         .bss
51
52 /* FORTH return stack. */
53 #define RETURN_STACK_SIZE 8192
54         .align 4096
55         .space RETURN_STACK_SIZE
56 return_stack:
57
58 /* Space for user-defined words. */
59 #define USER_DEFS_SIZE 16384
60         .align 4096
61 user_defs_start:
62         .space USER_DEFS_SIZE
63
64
65
66
67
68
69 /*----------------------------------------------------------------------
70  * Built-in words defined the long way.
71  */
72 #define F_IMMED 0x80
73 #define F_HIDDEN 0x40
74
75         // Store the chain of links.
76         .set link,0
77
78         .macro defcode name, namelen, flags=0, label
79         .section .rodata
80 N_\label :
81         .align 4
82         .byte \flags+\namelen   // flags + length byte
83         .ascii "\name"          // the name
84         .align 4
85         .int link               // link
86         .set link,N_\label
87 \label :
88         .int 1f                 // codeword
89         .text
90         .align 4
91 1:                              // assembler code follows
92         .endm
93
94         .macro defword name, namelen, flags=0, label
95         .section .rodata
96 N_\label :
97         .align 4
98         .byte \flags+\namelen   // flags + length byte
99         .ascii "\name"          // the name
100         .align 4
101         .int link               // link
102         .set link,N_\label
103 \label :
104         .int DOCOL              // codeword - the interpreter
105         // list of word pointers follow
106         .endm
107
108         /* COLD must not return (ie. must not call EXIT). */
109         defword "COLD",4,,COLD
110         .int KEY,ECHO,RDROP,COLD
111
112         defcode "EXIT",4,,EXIT
113         movl (%ebp),%esi        // pop return stack into %esi
114         lea 4(%ebp),%ebp
115         NEXT
116
117         defcode "!",1,,STORE
118         pop %ebx                // address to store at
119         pop %eax                // data to store there
120         mov %eax,(%ebx)         // store it
121         NEXT
122
123         defcode "@",1,,FETCH
124         pop %ebx                // address to fetch
125         mov (%ebx),%eax         // fetch it
126         push %eax               // push value onto stack
127         NEXT
128
129         defcode "STATE",5,,STATE
130         push $v_state
131         NEXT
132
133         defcode "HERE",4,,HERE
134         push $v_here
135         NEXT
136
137         defcode "LATEST",6,,LATEST
138         push $v_latest
139         NEXT
140
141         defcode ">R",2,,TOR
142         pop %eax                // pop parameter stack into %eax
143         lea -4(%ebp),%ebp       // push %eax on to return stack
144         movl %eax,(%ebp)
145         NEXT
146
147         defcode "R>",2,,FROMR
148         mov (%ebp),%eax         // pop top of return stack to %eax
149         lea 4(%ebp),%ebp
150         push %eax               // and push on to parameter stack
151         NEXT
152
153 #if 0 /* This definition is wrong. */
154         defcode "R",1,,R
155         mov %(ebp),%eax         // copy (don't pop) top of return stack to %eax
156         push %eax               // and push on to parameter stack
157         NEXT
158 #endif
159
160         defcode "RSP@",4,,RSPFETCH
161         push %ebp
162         NEXT
163
164         defcode "RSP!",4,,RSPSTORE
165         pop %ebp
166         NEXT
167
168         defcode "RDROP",5,,RDROP
169         lea 4(%ebp),%ebp        // pop the return stack
170         NEXT
171
172         defcode "KEY",3,,KEY
173         mov (currkey),%ebx
174         cmp (bufftop),%ebx
175         jge 1f
176         xor %eax,%eax
177         mov (%ebx),%al
178         push %eax
179         inc %ebx
180         mov %ebx,(currkey)
181         NEXT
182 1:
183         mov $0,%ebx             // out of input, exit (0)
184         mov $__NR_exit,%eax
185         int $0x80
186
187         defcode "ECHO",4,,ECHO
188         mov $1,%ebx             // 1st param: stdout
189
190         // write needs the address of the byte to write
191         pop %eax
192         mov %al,(2f)
193         mov $2f,%ecx            // 2nd param: address
194
195         mov $1,%edx             // 3rd param: nbytes = 1
196
197         mov $__NR_write,%eax    // write syscall
198         int $0x80
199
200         NEXT
201
202         .bss
203 2:      .space 1                // scratch used by ECHO
204
205         defcode "DUP",3,,DUP
206         pop %eax                // duplicate top of stack
207         push %eax
208         push %eax
209         NEXT
210
211         defcode "DROP",3,,DROP
212         pop %eax                // drop top of stack
213         NEXT
214
215         defcode "SWAP",4,,SWAP
216         pop %eax                // swap top of stack
217         pop %ebx
218         push %eax
219         push %ebx
220         NEXT
221
222 #if 0
223         defcode ":",1,,COLON
224         call nextword           // get next word, the procedure name
225         // The next word is returned in %ebx and has length %ecx bytes.
226
227         // Save the current value of VOCAB.
228         mov v_vocab,%eax
229         push %eax
230
231         // Change VOCAB to point to our new word's header (at LATEST).
232         mov v_latest,%edi
233         mov %edi,v_vocab
234
235         // We'll start by writing the word's header at LATEST; the header
236         // is just length byte, the word itself, link pointer.
237         mov %ecx,(%edi)         // Length byte
238         inc %edi
239         mov %ebx,%esi           // Copy the string.
240         rep movsb
241         // Round up to the next multiple of 4 so that the link pointer
242         // is aligned.
243         or $3,%edi
244         inc %edi
245         pop %eax                // Link pointer, points to old VOCAB.
246         mov %eax,(%edi)
247         add $4,%edi
248         // Write the codeword, which for user-defined words is always a
249         // pointer to the FORTH indirect threaded interpreter.
250         movl $DOCOL,(%edi)
251         add $4,%edi
252
253         // Finally, update LATEST.  As we go along compiling, we'll be
254         // writing compiled codewords to the LATEST pointer (and moving
255         // it along each time).
256         mov %edi,v_latest
257
258         movl $1,v_state         // go into compiling mode
259         ret
260
261         defcode ";",1,F_IMMED,SEMICOLON
262         // XXX
263
264 #endif
265
266         defcode SYSEXIT,7,,SYSEXIT
267         pop %ebx
268         mov $__NR_exit,%eax
269         int $0x80
270
271 /*----------------------------------------------------------------------
272  * Variables containing the interpreter's state.
273  */
274         .data
275
276         .align 4
277 v_state:
278         .int 0          // 0 = immediate, non-zero = compiling
279 v_latest:
280         .int N_SYSEXIT  // last word in the dictionary
281 v_here:
282         .int user_defs_start    // pointer to next space for user definition or current compiled def
283
284 /*----------------------------------------------------------------------
285  * Input buffer & initial input.
286  */
287         .data
288         .align 4096
289 buffer:
290         .ascii "                \n\
291 \\ Define some constants        \n\
292 : '\\n'   10 ;                  \n\
293 : ')'     41 ;                  \n\
294 : 'space' 32 ;                  \n\
295 : '\"'    34 ;                  \n\
296 : '-'     45 ;                  \n\
297 : '0'     48 ;                  \n\
298                                 \n\
299 \\ CR command                   \n\
300 : CR '\\n' ECHO ;               \n\
301 "
302
303 _initbufftop:
304         .align 4096
305 buffend:
306
307 currkey:
308         .int buffer
309 bufftop:
310         .int _initbufftop