PD release.
[jonesforth.git] / jonesforth.S.3
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 /* ELF entry point. */
16         .text
17         .globl _start
18 _start:
19         cld
20         mov $return_stack,%ebp  // Initialise the return stack.
21
22         mov $cold_start,%esi    // Initialise interpreter.
23         NEXT                    // Run interpreter!
24
25         .section .rodata
26 cold_start:                     // High-level code without a codeword.
27         .int COLD
28
29 /* DOCOL - the interpreter! */
30         .text
31 DOCOL:
32         lea -4(%ebp),%ebp       // push %esi on to the return stack
33         movl %esi,(%ebp)
34
35         addl $4,%eax            // %eax points to codeword, so make
36         movl %eax,%esi          // %esi point to first data word
37         NEXT
38
39
40
41 /*----------------------------------------------------------------------
42  * Fixed sized buffers for everything.
43  */
44         .bss
45
46 /* FORTH return stack. */
47 #define RETURN_STACK_SIZE 8192
48         .align 4096
49         .space RETURN_STACK_SIZE
50 return_stack:
51
52 /* Space for user-defined words. */
53 #define USER_DEFS_SIZE 16384
54         .align 4096
55 user_defs_start:
56         .space USER_DEFS_SIZE
57
58
59
60
61
62
63 /*----------------------------------------------------------------------
64  * Built-in words defined the long way.
65  */
66 #define F_IMMED 0x80
67 #define F_HIDDEN 0x40
68
69         // Store the chain of links.
70         .set link,0
71
72         .macro defcode name, namelen, flags=0, label
73         .section .rodata
74 N_\label :
75         .align 4
76         .byte \flags+\namelen   // flags + length byte
77         .ascii "\name"          // the name
78         .align 4
79         .int link               // link
80         .set link,N_\label
81 \label :
82         .int 1f                 // codeword
83         .text
84 1:                              // assembler code follows
85         .endm
86
87         .macro defword name, namelen, flags=0, label
88         .section .rodata
89 N_\label :
90         .align 4
91         .byte \flags+\namelen   // flags + length byte
92         .ascii "\name"          // the name
93         .align 4
94         .int link               // link
95         .set link,N_\label
96 \label :
97         .int DOCOL              // codeword - the interpreter
98         // list of word pointers follow
99         .endm
100
101         defword "COLD",4,,COLD
102         .int KEY,KEY,SWAP,ECHO,ECHO,RDROP,COLD
103
104         defcode "DUP",3,,DUP
105         pop %eax                // duplicate top of stack
106         push %eax
107         push %eax
108         NEXT
109
110         defcode "DROP",3,,DROP
111         pop %eax                // drop top of stack
112         NEXT
113
114         defcode "SWAP",4,,SWAP
115         pop %eax                // swap top of stack
116         pop %ebx
117         push %eax
118         push %ebx
119         NEXT
120
121         defcode "KEY",3,,KEY
122         mov (currkey),%ebx
123         cmp (bufftop),%ebx
124         jge 1f
125         xor %eax,%eax
126         mov (%ebx),%al
127         push %eax
128         inc %ebx
129         mov %ebx,(currkey)
130         NEXT
131 1:
132         mov $0,%ebx             // out of input, exit (0)
133         mov $__NR_exit,%eax
134         int $0x80
135
136         defcode "ECHO",4,,ECHO
137         mov $1,%ebx             // 1st param: stdout
138
139         // write needs the address of the byte to write
140         pop %eax
141         mov %al,(2f)
142         mov $2f,%ecx            // 2nd param: address
143
144         mov $1,%edx             // 3rd param: nbytes = 1
145
146         mov $__NR_write,%eax    // write syscall
147         int $0x80
148
149         NEXT
150
151         .bss
152 2:      .space 1                // scratch used by ECHO
153
154         defcode "RDROP",5,,RDROP
155         lea 4(%ebp),%ebp        // pop the return stack
156         NEXT
157
158 #if 0
159         defcode "R>",2,,FROMR
160
161         defcode ">R",2,,TOR
162
163         defcode ":",1,,COLON
164         call nextword           // get next word, the procedure name
165         // The next word is returned in %ebx and has length %ecx bytes.
166
167         // Save the current value of VOCAB.
168         mov v_vocab,%eax
169         push %eax
170
171         // Change VOCAB to point to our new word's header (at LATEST).
172         mov v_latest,%edi
173         mov %edi,v_vocab
174
175         // We'll start by writing the word's header at LATEST; the header
176         // is just length byte, the word itself, link pointer.
177         mov %ecx,(%edi)         // Length byte
178         inc %edi
179         mov %ebx,%esi           // Copy the string.
180         rep movsb
181         // Round up to the next multiple of 4 so that the link pointer
182         // is aligned.
183         or $3,%edi
184         inc %edi
185         pop %eax                // Link pointer, points to old VOCAB.
186         mov %eax,(%edi)
187         add $4,%edi
188         // Write the codeword, which for user-defined words is always a
189         // pointer to the FORTH indirect threaded interpreter.
190         movl $DOCOL,(%edi)
191         add $4,%edi
192
193         // Finally, update LATEST.  As we go along compiling, we'll be
194         // writing compiled codewords to the LATEST pointer (and moving
195         // it along each time).
196         mov %edi,v_latest
197
198         movl $1,v_state         // go into compiling mode
199         ret
200
201         defcode ";",1,F_IMMED,SEMICOLON
202         // XXX
203
204 #endif
205
206         defcode SYSEXIT,7,,SYSEXIT
207         pop %ebx
208         mov $__NR_exit,%eax
209         int $0x80
210
211 /*----------------------------------------------------------------------
212  * Variables containing the interpreter's state.
213  */
214         .data
215
216         .align 4
217 v_state:
218         .int 0          // 0 = immediate, 1 = compiling
219 v_vocab:
220         .int N_SYSEXIT  // last word in the dictionary
221 v_latest:
222         .int user_defs_start    // pointer to next space for user definition or current compiled def
223
224 /*----------------------------------------------------------------------
225  * Input buffer & initial input.
226  */
227         .data
228         .align 4096
229 buffer:
230         .ascii "                \n\
231 \\ Define some constants        \n\
232 : '\\n'   10 ;                  \n\
233 : ')'     41 ;                  \n\
234 : 'space' 32 ;                  \n\
235 : '\"'    34 ;                  \n\
236 : '-'     45 ;                  \n\
237 : '0'     48 ;                  \n\
238                                 \n\
239 \\ CR command                   \n\
240 : CR '\\n' ECHO ;               \n\
241 "
242
243 _initbufftop:
244         .align 4096
245 buffend:
246
247 currkey:
248         .int buffer
249 bufftop:
250         .int _initbufftop