1 /* A minimal FORTH interpreter for Linux / i386 systems. -*- asm -*-
2 * By Richard W.M. Jones <rich@annexia.org>
4 * gcc -m32 -nostdlib -static -Wl,-Ttext,0 -o jonesforth jonesforth.S
7 #include <asm-i386/unistd.h>
15 /* Macros to deal with the return stack. */
22 /* ELF entry point. */
27 mov $return_stack,%ebp // Initialise the return stack.
29 mov $cold_start,%esi // Initialise interpreter.
30 NEXT // Run interpreter!
33 cold_start: // High-level code without a codeword.
36 /* DOCOL - the interpreter! */
40 lea -4(%ebp),%ebp // push %esi on to the return stack
43 addl $4,%eax // %eax points to codeword, so make
44 movl %eax,%esi // %esi point to first data word
47 /*----------------------------------------------------------------------
48 * Fixed sized buffers for everything.
52 /* FORTH return stack. */
53 #define RETURN_STACK_SIZE 8192
55 .space RETURN_STACK_SIZE
58 /* Space for user-defined words. */
59 #define USER_DEFS_SIZE 16384
69 /*----------------------------------------------------------------------
70 * Built-in words defined the long way.
75 // Store the chain of links.
78 .macro defcode name, namelen, flags=0, label
82 .byte \flags+\namelen // flags + length byte
83 .ascii "\name" // the name
91 1: // assembler code follows
94 .macro defword name, namelen, flags=0, label
98 .byte \flags+\namelen // flags + length byte
99 .ascii "\name" // the name
104 .int DOCOL // codeword - the interpreter
105 // list of word pointers follow
108 /* COLD must not return (ie. must not call EXIT). */
109 defword "COLD",4,,COLD
110 .int KEY,ECHO,RDROP,COLD
112 defcode "EXIT",4,,EXIT
113 movl (%ebp),%esi // pop return stack into %esi
118 pop %ebx // address to store at
119 pop %eax // data to store there
120 mov %eax,(%ebx) // store it
124 pop %ebx // address to fetch
125 mov (%ebx),%eax // fetch it
126 push %eax // push value onto stack
129 defcode "STATE",5,,STATE
133 defcode "HERE",4,,HERE
137 defcode "LATEST",6,,LATEST
142 pop %eax // pop parameter stack into %eax
143 lea -4(%ebp),%ebp // push %eax on to return stack
147 defcode "R>",2,,FROMR
148 mov (%ebp),%eax // pop top of return stack to %eax
150 push %eax // and push on to parameter stack
153 #if 0 /* This definition is wrong. */
155 mov %(ebp),%eax // copy (don't pop) top of return stack to %eax
156 push %eax // and push on to parameter stack
160 defcode "RSP@",4,,RSPFETCH
164 defcode "RSP!",4,,RSPSTORE
168 defcode "RDROP",5,,RDROP
169 lea 4(%ebp),%ebp // pop the return stack
183 mov $0,%ebx // out of input, exit (0)
187 defcode "ECHO",4,,ECHO
188 mov $1,%ebx // 1st param: stdout
190 // write needs the address of the byte to write
193 mov $2f,%ecx // 2nd param: address
195 mov $1,%edx // 3rd param: nbytes = 1
197 mov $__NR_write,%eax // write syscall
203 2: .space 1 // scratch used by ECHO
206 pop %eax // duplicate top of stack
211 defcode "DROP",3,,DROP
212 pop %eax // drop top of stack
215 defcode "SWAP",4,,SWAP
216 pop %eax // swap top of stack
224 call nextword // get next word, the procedure name
225 // The next word is returned in %ebx and has length %ecx bytes.
227 // Save the current value of VOCAB.
231 // Change VOCAB to point to our new word's header (at LATEST).
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
239 mov %ebx,%esi // Copy the string.
241 // Round up to the next multiple of 4 so that the link pointer
245 pop %eax // Link pointer, points to old VOCAB.
248 // Write the codeword, which for user-defined words is always a
249 // pointer to the FORTH indirect threaded interpreter.
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).
258 movl $1,v_state // go into compiling mode
261 defcode ";",1,F_IMMED,SEMICOLON
266 defcode SYSEXIT,7,,SYSEXIT
271 /*----------------------------------------------------------------------
272 * Variables containing the interpreter's state.
278 .int 0 // 0 = immediate, non-zero = compiling
280 .int N_SYSEXIT // last word in the dictionary
282 .int user_defs_start // pointer to next space for user definition or current compiled def
284 /*----------------------------------------------------------------------
285 * Input buffer & initial input.
291 \\ Define some constants \n\
300 : CR '\\n' ECHO ; \n\