PD release.
[jonesforth.git] / jonesforth.S.1
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         .macro defcode name, namelen, flags=0, label, link=0
70         .section .rodata
71 N_\label :
72         .align 4
73         .byte \flags+\namelen   // flags + length byte
74         .ascii "\name"          // the name
75         .align 4
76         .int \link              // link
77 \label :
78         .int 1f                 // codeword
79         .text
80 1:                              // assembler code follows
81         .endm
82
83         .macro defword name, namelen, flags=0, label, link=0
84         .section .rodata
85 N_\label :
86         .align 4
87         .byte \flags+\namelen   // flags + length byte
88         .ascii "\name"          // the name
89         .align 4
90         .int \link              // link
91 \label :
92         .int DOCOL              // codeword - the interpreter
93         // list of word pointers follow
94         .endm
95
96         defword "COLD",4,,COLD,
97         .int KEY,ECHO,RDROP,COLD
98
99         defcode "KEY",3,,KEY,N_COLD
100         mov (currkey),%ebx
101         cmp (bufftop),%ebx
102         jge 1f
103         xor %eax,%eax
104         mov (%ebx),%al
105         push %ax
106         inc %ebx
107         mov %ebx,(currkey)
108         NEXT
109 1:
110         mov $0,%ebx             // out of input, exit (0)
111         mov $__NR_exit,%eax
112         int $0x80
113
114         defcode "ECHO",4,,ECHO,N_KEY
115         mov $1,%ebx             // 1st param: stdout
116
117         // write needs the address of the byte to write
118         pop %eax
119         mov %al,(_echo_tmp)
120         mov $_echo_tmp,%ecx     // 2nd param: address
121
122         mov $1,%edx             // 3rd param: nbytes = 1
123
124         mov $__NR_write,%eax    // write syscall
125         int $0x80
126
127         NEXT
128
129         .bss
130 _echo_tmp: .space 1
131
132         defcode "RDROP",5,,RDROP,N_ECHO
133         lea 4(%ebp),%ebp        // pop the return stack
134         NEXT
135
136 #if 0
137         defcode "R>",2,,FROMR,N_TAIL
138
139         defcode ">R",2,,TOR,N_FROMR
140
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.
144
145         // Save the current value of VOCAB.
146         mov v_vocab,%eax
147         push %eax
148
149         // Change VOCAB to point to our new word's header (at LATEST).
150         mov v_latest,%edi
151         mov %edi,v_vocab
152
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
156         inc %edi
157         mov %ebx,%esi           // Copy the string.
158         rep movsb
159         // Round up to the next multiple of 4 so that the link pointer
160         // is aligned.
161         or $3,%edi
162         inc %edi
163         pop %eax                // Link pointer, points to old VOCAB.
164         mov %eax,(%edi)
165         add $4,%edi
166         // Write the codeword, which for user-defined words is always a
167         // pointer to the FORTH indirect threaded interpreter.
168         movl $DOCOL,(%edi)
169         add $4,%edi
170
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).
174         mov %edi,v_latest
175
176         movl $1,v_state         // go into compiling mode
177         ret
178
179         defcode ";",1,F_IMMED,SEMICOLON,N_COLON
180         // XXX
181
182 #endif
183
184         defcode SYSEXIT,7,,SYSEXIT, //N_COLON
185         pop %ebx
186         mov $__NR_exit,%eax
187         int $0x80
188
189 /*----------------------------------------------------------------------
190  * Variables containing the interpreter's state.
191  */
192         .data
193
194         .align 4
195 v_state:
196         .int 0          // 0 = immediate, 1 = compiling
197 v_vocab:
198         .int N_SYSEXIT  // last word in the dictionary
199 v_latest:
200         .int user_defs_start    // pointer to next space for user definition or current compiled def
201
202 /*----------------------------------------------------------------------
203  * Input buffer & initial input.
204  */
205         .data
206         .align 4096
207 buffer:
208         .ascii "TEST OF READING WORDS   1 2 3"
209
210 _initbufftop:
211         .align 4096
212 buffend:
213
214 currkey:
215         .int buffer
216 bufftop:
217         .int _initbufftop