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,ECHO,RDROP,COLD
115 mov $0,%ebx // out of input, exit (0)
119 defcode "ECHO",4,,ECHO
120 mov $1,%ebx // 1st param: stdout
122 // write needs the address of the byte to write
125 mov $_echo_tmp,%ecx // 2nd param: address
127 mov $1,%edx // 3rd param: nbytes = 1
129 mov $__NR_write,%eax // write syscall
137 defcode "RDROP",5,,RDROP
138 lea 4(%ebp),%ebp // pop the return stack
142 defcode "R>",2,,FROMR
147 call nextword // get next word, the procedure name
148 // The next word is returned in %ebx and has length %ecx bytes.
150 // Save the current value of VOCAB.
154 // Change VOCAB to point to our new word's header (at LATEST).
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
162 mov %ebx,%esi // Copy the string.
164 // Round up to the next multiple of 4 so that the link pointer
168 pop %eax // Link pointer, points to old VOCAB.
171 // Write the codeword, which for user-defined words is always a
172 // pointer to the FORTH indirect threaded interpreter.
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).
181 movl $1,v_state // go into compiling mode
184 defcode ";",1,F_IMMED,SEMICOLON
189 defcode SYSEXIT,7,,SYSEXIT
194 /*----------------------------------------------------------------------
195 * Variables containing the interpreter's state.
201 .int 0 // 0 = immediate, 1 = compiling
203 .int N_SYSEXIT // last word in the dictionary
205 .int user_defs_start // pointer to next space for user definition or current compiled def
207 /*----------------------------------------------------------------------
208 * Input buffer & initial input.
213 .ascii "TEST OF READING WORDS 1 2 3"