Added FORTH interpreter and older versions.
[jonesforth.git] / jonesforth.S.2
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,ECHO,RDROP,COLD
103
104         defcode "KEY",3,,KEY
105         mov (currkey),%ebx
106         cmp (bufftop),%ebx
107         jge 1f
108         xor %eax,%eax
109         mov (%ebx),%al
110         push %ax
111         inc %ebx
112         mov %ebx,(currkey)
113         NEXT
114 1:
115         mov $0,%ebx             // out of input, exit (0)
116         mov $__NR_exit,%eax
117         int $0x80
118
119         defcode "ECHO",4,,ECHO
120         mov $1,%ebx             // 1st param: stdout
121
122         // write needs the address of the byte to write
123         pop %eax
124         mov %al,(_echo_tmp)
125         mov $_echo_tmp,%ecx     // 2nd param: address
126
127         mov $1,%edx             // 3rd param: nbytes = 1
128
129         mov $__NR_write,%eax    // write syscall
130         int $0x80
131
132         NEXT
133
134         .bss
135 _echo_tmp: .space 1
136
137         defcode "RDROP",5,,RDROP
138         lea 4(%ebp),%ebp        // pop the return stack
139         NEXT
140
141 #if 0
142         defcode "R>",2,,FROMR
143
144         defcode ">R",2,,TOR
145
146         defcode ":",1,,COLON
147         call nextword           // get next word, the procedure name
148         // The next word is returned in %ebx and has length %ecx bytes.
149
150         // Save the current value of VOCAB.
151         mov v_vocab,%eax
152         push %eax
153
154         // Change VOCAB to point to our new word's header (at LATEST).
155         mov v_latest,%edi
156         mov %edi,v_vocab
157
158         // We'll start by writing the word's header at LATEST; the header
159         // is just length byte, the word itself, link pointer.
160         mov %ecx,(%edi)         // Length byte
161         inc %edi
162         mov %ebx,%esi           // Copy the string.
163         rep movsb
164         // Round up to the next multiple of 4 so that the link pointer
165         // is aligned.
166         or $3,%edi
167         inc %edi
168         pop %eax                // Link pointer, points to old VOCAB.
169         mov %eax,(%edi)
170         add $4,%edi
171         // Write the codeword, which for user-defined words is always a
172         // pointer to the FORTH indirect threaded interpreter.
173         movl $DOCOL,(%edi)
174         add $4,%edi
175
176         // Finally, update LATEST.  As we go along compiling, we'll be
177         // writing compiled codewords to the LATEST pointer (and moving
178         // it along each time).
179         mov %edi,v_latest
180
181         movl $1,v_state         // go into compiling mode
182         ret
183
184         defcode ";",1,F_IMMED,SEMICOLON
185         // XXX
186
187 #endif
188
189         defcode SYSEXIT,7,,SYSEXIT
190         pop %ebx
191         mov $__NR_exit,%eax
192         int $0x80
193
194 /*----------------------------------------------------------------------
195  * Variables containing the interpreter's state.
196  */
197         .data
198
199         .align 4
200 v_state:
201         .int 0          // 0 = immediate, 1 = compiling
202 v_vocab:
203         .int N_SYSEXIT  // last word in the dictionary
204 v_latest:
205         .int user_defs_start    // pointer to next space for user definition or current compiled def
206
207 /*----------------------------------------------------------------------
208  * Input buffer & initial input.
209  */
210         .data
211         .align 4096
212 buffer:
213         .ascii "TEST OF READING WORDS   1 2 3"
214
215 _initbufftop:
216         .align 4096
217 buffend:
218
219 currkey:
220         .int buffer
221 bufftop:
222         .int _initbufftop