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 .macro defcode name, namelen, flags=0, label, link=0
73 .byte \flags+\namelen // flags + length byte
74 .ascii "\name" // the name
80 1: // assembler code follows
83 .macro defword name, namelen, flags=0, label, link=0
87 .byte \flags+\namelen // flags + length byte
88 .ascii "\name" // the name
92 .int DOCOL // codeword - the interpreter
93 // list of word pointers follow
96 defword "COLD",4,,COLD,
97 .int KEY,ECHO,RDROP,COLD
99 defcode "KEY",3,,KEY,N_COLD
110 mov $0,%ebx // out of input, exit (0)
114 defcode "ECHO",4,,ECHO,N_KEY
115 mov $1,%ebx // 1st param: stdout
117 // write needs the address of the byte to write
120 mov $_echo_tmp,%ecx // 2nd param: address
122 mov $1,%edx // 3rd param: nbytes = 1
124 mov $__NR_write,%eax // write syscall
132 defcode "RDROP",5,,RDROP,N_ECHO
133 lea 4(%ebp),%ebp // pop the return stack
137 defcode "R>",2,,FROMR,N_TAIL
139 defcode ">R",2,,TOR,N_FROMR
141 defcode ":",1,,COLON,
142 call nextword // get next word, the procedure name
143 // The next word is returned in %ebx and has length %ecx bytes.
145 // Save the current value of VOCAB.
149 // Change VOCAB to point to our new word's header (at LATEST).
153 // We'll start by writing the word's header at LATEST; the header
154 // is just length byte, the word itself, link pointer.
155 mov %ecx,(%edi) // Length byte
157 mov %ebx,%esi // Copy the string.
159 // Round up to the next multiple of 4 so that the link pointer
163 pop %eax // Link pointer, points to old VOCAB.
166 // Write the codeword, which for user-defined words is always a
167 // pointer to the FORTH indirect threaded interpreter.
171 // Finally, update LATEST. As we go along compiling, we'll be
172 // writing compiled codewords to the LATEST pointer (and moving
173 // it along each time).
176 movl $1,v_state // go into compiling mode
179 defcode ";",1,F_IMMED,SEMICOLON,N_COLON
184 defcode SYSEXIT,7,,SYSEXIT, //N_COLON
189 /*----------------------------------------------------------------------
190 * Variables containing the interpreter's state.
196 .int 0 // 0 = immediate, 1 = compiling
198 .int N_SYSEXIT // last word in the dictionary
200 .int user_defs_start // pointer to next space for user definition or current compiled def
202 /*----------------------------------------------------------------------
203 * Input buffer & initial input.
208 .ascii "TEST OF READING WORDS 1 2 3"