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 /* ELF entry point. */
20 mov $return_stack,%ebp // Initialise the return stack.
22 mov $cold_start,%esi // Initialise interpreter.
23 NEXT // Run interpreter!
26 cold_start: // High-level code without a codeword.
29 /* DOCOL - the interpreter! */
32 lea -4(%ebp),%ebp // push %esi on to the return stack
35 addl $4,%eax // %eax points to codeword, so make
36 movl %eax,%esi // %esi point to first data word
41 /*----------------------------------------------------------------------
42 * Fixed sized buffers for everything.
46 /* FORTH return stack. */
47 #define RETURN_STACK_SIZE 8192
49 .space RETURN_STACK_SIZE
52 /* Space for user-defined words. */
53 #define USER_DEFS_SIZE 16384
63 /*----------------------------------------------------------------------
64 * Built-in words defined the long way.
69 // Store the chain of links.
72 .macro defcode name, namelen, flags=0, label
76 .byte \flags+\namelen // flags + length byte
77 .ascii "\name" // the name
84 1: // assembler code follows
87 .macro defword name, namelen, flags=0, label
91 .byte \flags+\namelen // flags + length byte
92 .ascii "\name" // the name
97 .int DOCOL // codeword - the interpreter
98 // list of word pointers follow
101 defword "COLD",4,,COLD
102 .int KEY,KEY,SWAP,ECHO,ECHO,RDROP,COLD
105 pop %eax // duplicate top of stack
110 defcode "DROP",3,,DROP
111 pop %eax // drop top of stack
114 defcode "SWAP",4,,SWAP
115 pop %eax // swap top of stack
132 mov $0,%ebx // out of input, exit (0)
136 defcode "ECHO",4,,ECHO
137 mov $1,%ebx // 1st param: stdout
139 // write needs the address of the byte to write
142 mov $2f,%ecx // 2nd param: address
144 mov $1,%edx // 3rd param: nbytes = 1
146 mov $__NR_write,%eax // write syscall
152 2: .space 1 // scratch used by ECHO
154 defcode "RDROP",5,,RDROP
155 lea 4(%ebp),%ebp // pop the return stack
159 defcode "R>",2,,FROMR
164 call nextword // get next word, the procedure name
165 // The next word is returned in %ebx and has length %ecx bytes.
167 // Save the current value of VOCAB.
171 // Change VOCAB to point to our new word's header (at LATEST).
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
179 mov %ebx,%esi // Copy the string.
181 // Round up to the next multiple of 4 so that the link pointer
185 pop %eax // Link pointer, points to old VOCAB.
188 // Write the codeword, which for user-defined words is always a
189 // pointer to the FORTH indirect threaded interpreter.
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).
198 movl $1,v_state // go into compiling mode
201 defcode ";",1,F_IMMED,SEMICOLON
206 defcode SYSEXIT,7,,SYSEXIT
211 /*----------------------------------------------------------------------
212 * Variables containing the interpreter's state.
218 .int 0 // 0 = immediate, 1 = compiling
220 .int N_SYSEXIT // last word in the dictionary
222 .int user_defs_start // pointer to next space for user definition or current compiled def
224 /*----------------------------------------------------------------------
225 * Input buffer & initial input.
231 \\ Define some constants \n\
240 : CR '\\n' ECHO ; \n\