The header.
[jonesforth.git] / jonesforth.S
1 /*      A sometimes minimal FORTH compiler and tutorial for Linux / i386 systems. -*- asm -*-
2         By Richard W.M. Jones <rich@annexia.org> http://annexia.org/forth
3
4         gcc -m32 -nostdlib -static -Wl,-Ttext,0 -o jonesforth jonesforth.S
5
6         INTRODUCTION ----------------------------------------------------------------------
7
8         FORTH is one of those alien languages which most working programmers regard in the same
9         way as Haskell, LISP, and so on.  Something so strange that they'd rather any thoughts
10         of it just go away so they can get on with writing this paying code.  But that's wrong
11         and if you care at all about programming then you should at least understand all these
12         languages, even if you will never use them.
13
14         LISP is the ultimate high-level language, and features from LISP are being added every
15         decade to the more common languages.  But FORTH is in some ways the ultimate in low level
16         programming.  Out of the box it lacks features like dynamic memory management and even
17         strings.  In fact, at its primitive level it lacks even basic concepts like IF-statements
18         and loops.
19
20         Why then would you want to learn FORTH?  There are several very good reasons.  First
21         and foremost, FORTH is minimal.  You really can write a complete FORTH in, say, 2000
22         lines of code.  I don't just mean a FORTH program, I mean a complete FORTH operating
23         system, environment and language.  You could boot such a FORTH on a bare PC and it would
24         come up with a prompt where you could start doing useful work.  The FORTH you have here
25         isn't minimal and uses a Linux process as its 'base PC' (both for the purposes of making
26         it a good tutorial). It's possible to completely understand the system.  Who can say they
27         completely understand how Linux works, or gcc?
28
29         Secondly FORTH has a peculiar bootstrapping property.  By that I mean that after writing
30         a little bit of assembly to talk to the hardware and implement a few primitives, all the
31         rest of the language and compiler is written in FORTH itself.  Remember I said before
32         that FORTH lacked IF-statements and loops?  Well of course it doesn't really because
33         such a lanuage would be useless, but my point was rather that IF-statements and loops are
34         written in FORTH itself.
35
36         Now of course this is common in other languages as well, and in those languages we call
37         them 'libraries'.  For example in C, 'printf' is a library function written in C.  But
38         in FORTH this goes way beyond mere libraries.  Can you imagine writing C's 'if' in C?
39         And that brings me to my third reason: If you can write 'if' in FORTH, then why restrict
40         yourself to the usual if/while/for/switch constructs?  You want a construct that iterates
41         over every other element in a list of numbers?  You can add it to the language.  What
42         about an operator which pulls in variables directly from a configuration file and makes
43         them available as FORTH variables?  Or how about adding Makefile-like dependencies to
44         the language?  No problem in FORTH.  This concept isn't common in programming languages,
45         but it has a name (in fact two names): "macros" (by which I mean LISP-style macros, not
46         the lame C preprocessor) and "domain specific languages" (DSLs).
47
48         This tutorial isn't about learning FORTH as the language.  I'll point you to some references
49         you should read if you're not familiar with using FORTH.  This tutorial is about how to
50         write FORTH.  In fact, until you understand how FORTH is written, you'll have only a very
51         superficial understanding of how to use it.
52
53         So if you're not familiar with FORTH or want to refresh your memory here are some online
54         references to read:
55
56         http://en.wikipedia.org/wiki/Forth_%28programming_language%29
57
58         http://galileo.phys.virginia.edu/classes/551.jvn.fall01/primer.htm
59
60         http://wiki.laptop.org/go/Forth_Lessons
61
62         Here is another "Why FORTH?" essay: http://www.jwdt.com/~paysan/why-forth.html
63
64         SETTING UP ----------------------------------------------------------------------
65
66         Let's get a few housekeeping things out of the way.  Firstly because I need to draw lots of
67         ASCII-art diagrams to explain concepts, the best way to look at this is using a window which
68         uses a fixed width font and is at least this wide:
69
70  <------------------------------------------------------------------------------------------------------------------------>
71
72         ASSEMBLING ----------------------------------------------------------------------
73
74         If you want to actually run this FORTH, rather than just read it, you will need Linux on an
75         i386.  Linux because instead of programming directly to the hardware on a bare PC which I
76         could have done, I went for a simpler tutorial by assuming that the 'hardware' is a Linux
77         process with a few basic system calls (read, write and exit and that's about all).  i386
78         is needed because I had to write the assembly for a processor, and i386 is by far the most
79         common.  (Of course when I say 'i386', any 32- or 64-bit x86 processor will do.  I'm compiling
80         this on a 64 bit AMD Opteron).
81
82         Again, to assemble this you will need gcc and gas (the GNU assembler).  The commands to
83         assemble and run the code (save this file as 'jonesforth.S') are:
84
85         gcc -m32 -nostdlib -static -Wl,-Ttext,0 -o jonesforth jonesforth.S
86         ./jonesforth
87
88         You will see lots of 'Warning: unterminated string; newline inserted' messages from the
89         assembler.  That's just because the GNU assembler doesn't have a good syntax for multi-line
90         strings (or rather it used to, but the developers removed it!) so I've abused the syntax
91         slightly to make things readable.  Ignore these warnings.
92
93         ASSEMBLER ----------------------------------------------------------------------
94
95         (You can just skip to the next section -- you don't need to be able to read assembler to
96         follow this tutorial).
97
98         However if you do want to read the assembly code here are a few notes about gas (the GNU assembler):
99
100         (1) Register names are prefixed with '%', so %eax is the 32 bit i386 accumulator.  The registers
101             available on i386 are: %eax, %ebx, %ecx, %edx, %esi, %edi, %ebp and %esp, and most of them
102             have special purposes.
103
104         (2) Add, mov, etc. take arguments in the form SRC,DEST.  So mov %eax,%ecx moves %eax -> %ecx
105
106         (3) Constants are prefixed with '$', and you mustn't forget it!  If you forget it then it
107             causes a read from memory instead, so:
108             mov $2,%eax         moves number 2 into %eax
109             mov 2,%eax          reads the 32 bit word from address 2 into %eax (ie. most likely a mistake)
110
111         (4) gas has a funky syntax for local labels, where '1f' (etc.) means label '1:' "forwards"
112             and '1b' (etc.) means label '1:' "backwards".
113
114         (5) 'ja' is "jump if above", 'jb' for "jump if below", 'je' "jump if equal" etc.
115
116         (6) gas has a reasonably nice .macro syntax, and I use them a lot to make the code shorter and
117             less repetitive.
118
119         For more help reading the assembler, do "info gas" at the Linux prompt.
120
121         Now the tutorial starts in earnest.
122
123         THE DICTIONARY ----------------------------------------------------------------------
124
125         In FORTH as you will know, functions are called "words", as just as in other languages they
126         have a name and a definition.  Here are two FORTH words:
127
128         : DOUBLE 2 * ;                  \ name is "DOUBLE", definition is "2 *"
129         : QUADRUPLE DOUBLE DOUBLE ;     \ name is "QUADRUPLE", definition is "DOUBLE DOUBLE"
130
131         Words, both built-in ones and ones which the programmer defines later, are stored in a dictionary
132         which is just a linked list of dictionary entries.
133
134         <--- DICTIONARY ENTRY (HEADER) ----------------------->
135         +------------------------+--------+---------- - - - - +----------- - - - -
136         | LINK POINTER           | LENGTH/| NAME              | DEFINITION
137         |                        | FLAGS  |                   |
138         +--- (4 bytes) ----------+- byte -+- n bytes  - - - - +----------- - - - -
139
140         I'll come to the definition of the word later.  For now just look at the header.  The first
141         4 bytes are the link pointer.  This points back to the previous word in the dictionary, or, for
142         the first word in the dictionary it is just a NULL pointer.  Then comes a length/flags byte.
143         The length of the word can be up to 31 characters (5 bits used) and the top three bits are used
144         for various flags which I'll come to later.  This is followed by the name itself, and in this
145         implementation the name is rounded up to a multiple of 4 bytes by padding it with zero bytes.
146         That's just to ensure that the definition starts on a 32 bit boundary.
147
148         A FORTH variable called LATEST contains a pointer to the most recently defined word, in
149         other words, the head of this linked list.
150
151         DOUBLE and QUADRUPLE might look like this:
152
153           pointer to previous word
154            ^
155            |
156         +--|------+---+---+---+---+---+---+---+---+------------- - - - -
157         | LINK    | 6 | D | O | U | B | L | E | 0 | (definition ...)
158         +---------+---+---+---+---+---+---+---+---+------------- - - - -
159            ^       len                         padding
160            |
161         +--|------+---+---+---+---+---+---+---+---+---+---+---+---+------------- - - - -
162         | LINK    | 9 | Q | U | A | D | R | U | P | L | E | 0 | 0 | (definition ...)
163         +---------+---+---+---+---+---+---+---+---+---+---+---+---+------------- - - - -
164            ^       len                                     padding
165            |
166            |
167           LATEST
168
169         You shoud be able to see from this how you might implement functions to find a word in
170         the dictionary (just walk along the dictionary entries starting at LATEST and matching
171         the names until you either find a match or hit the NULL pointer at the end of the dictionary),
172         and add a word to the dictionary (create a new definition, set its LINK to LATEST, and set
173         LATEST to point to the new word).  We'll see precisely these functions implemented in
174         assembly code later on.
175
176         INDIRECT THREADED CODE ----------------------------------------------------------------------
177
178         Now we'll get to the really crucial bit in understanding FORTH, so go and get a cup of tea
179         or coffee and settle down.  It's fair to say that if you don't understand this section, then you
180         won't "get" how FORTH works, and that would be a failure on my part for not explaining it well.
181         So if after reading this section a few times you don't understand it, please email me
182         (rich@annexia.org).
183
184         
185
186
187
188
189
190
191
192
193 */
194
195 /* NEXT macro. */
196         .macro NEXT
197         lodsl
198         jmp *(%eax)
199         .endm
200
201 /* Macros to deal with the return stack. */
202         .macro PUSHRSP reg
203         lea -4(%ebp),%ebp       // push reg on to return stack
204         movl \reg,(%ebp)
205         .endm
206
207         .macro POPRSP reg
208         mov (%ebp),\reg         // pop top of return stack to reg
209         lea 4(%ebp),%ebp
210         .endm
211
212 /* ELF entry point. */
213         .text
214         .globl _start
215 _start:
216         cld
217         mov %esp,var_S0         // Store the initial data stack pointer.
218         mov $return_stack,%ebp  // Initialise the return stack.
219
220         mov $cold_start,%esi    // Initialise interpreter.
221         NEXT                    // Run interpreter!
222
223         .section .rodata
224 cold_start:                     // High-level code without a codeword.
225         .int COLD
226
227 /* DOCOL - the interpreter! */
228         .text
229         .align 4
230 DOCOL:
231         PUSHRSP %esi            // push %esi on to the return stack
232         addl $4,%eax            // %eax points to codeword, so make
233         movl %eax,%esi          // %esi point to first data word
234         NEXT
235
236 /*----------------------------------------------------------------------
237  * Fixed sized buffers for everything.
238  */
239         .bss
240
241 /* FORTH return stack. */
242 #define RETURN_STACK_SIZE 8192
243         .align 4096
244         .space RETURN_STACK_SIZE
245 return_stack:
246
247 /* Space for user-defined words. */
248 #define USER_DEFS_SIZE 16384
249         .align 4096
250 user_defs_start:
251         .space USER_DEFS_SIZE
252
253
254
255
256
257
258 /*----------------------------------------------------------------------
259  * Built-in words defined the long way.
260  */
261 #define F_IMMED 0x80
262 #define F_HIDDEN 0x20
263
264         // Store the chain of links.
265         .set link,0
266
267         .macro defcode name, namelen, flags=0, label
268         .section .rodata
269         .align 4
270         .globl name_\label
271 name_\label :
272         .int link               // link
273         .set link,name_\label
274         .byte \flags+\namelen   // flags + length byte
275         .ascii "\name"          // the name
276         .align 4
277         .globl \label
278 \label :
279         .int code_\label        // codeword
280         .text
281         .align 4
282         .globl code_\label
283 code_\label :                   // assembler code follows
284         .endm
285
286         .macro defword name, namelen, flags=0, label
287         .section .rodata
288         .align 4
289         .globl name_\label
290 name_\label :
291         .int link               // link
292         .set link,name_\label
293         .byte \flags+\namelen   // flags + length byte
294         .ascii "\name"          // the name
295         .align 4
296         .globl \label
297 \label :
298         .int DOCOL              // codeword - the interpreter
299         // list of word pointers follow
300         .endm
301
302         .macro defvar name, namelen, flags=0, label, initial=0
303         defcode \name,\namelen,\flags,\label
304         push $var_\name
305         NEXT
306         .data
307         .align 4
308 var_\name :
309         .int \initial
310         .endm
311
312         // Some easy ones, written in assembly for speed
313         defcode "DROP",4,,DROP
314         pop %eax                // drop top of stack
315         NEXT
316
317         defcode "DUP",3,,DUP
318         pop %eax                // duplicate top of stack
319         push %eax
320         push %eax
321         NEXT
322
323         defcode "SWAP",4,,SWAP
324         pop %eax                // swap top of stack
325         pop %ebx
326         push %eax
327         push %ebx
328         NEXT
329
330         defcode "OVER",4,,OVER
331         mov 4(%esp),%eax        // get the second element of stack
332         push %eax               // and push it on top
333         NEXT
334
335         defcode "ROT",3,,ROT
336         pop %eax
337         pop %ebx
338         pop %ecx
339         push %eax
340         push %ecx
341         push %ebx
342         NEXT
343
344         defcode "-ROT",4,,NROT
345         pop %eax
346         pop %ebx
347         pop %ecx
348         push %ebx
349         push %eax
350         push %ecx
351         NEXT
352
353         defcode "1+",2,,INCR
354         incl (%esp)             // increment top of stack
355         NEXT
356
357         defcode "1-",2,,DECR
358         decl (%esp)             // decrement top of stack
359         NEXT
360
361         defcode "4+",2,,INCR4
362         addl $4,(%esp)          // increment top of stack
363         NEXT
364
365         defcode "4-",2,,DECR4
366         subl $4,(%esp)          // decrement top of stack
367         NEXT
368
369         defcode "+",1,,ADD
370         pop %eax
371         addl %eax,(%esp)
372         NEXT
373
374         defcode "-",1,,SUB
375         pop %eax
376         subl %eax,(%esp)
377         NEXT
378
379         defcode "*",1,,MUL
380         pop %eax
381         pop %ebx
382         imull %ebx,%eax
383         push %eax               // ignore overflow
384         NEXT
385
386         defcode "/",1,,DIV
387         xor %edx,%edx
388         pop %ebx
389         pop %eax
390         idivl %ebx
391         push %eax               // push quotient
392         NEXT
393
394         defcode "MOD",3,,MOD
395         xor %edx,%edx
396         pop %ebx
397         pop %eax
398         idivl %ebx
399         push %edx               // push remainder
400         NEXT
401
402         defcode "=",1,,EQU      // top two words are equal?
403         pop %eax
404         pop %ebx
405         cmp %ebx,%eax
406         je 1f
407         pushl $0
408         NEXT
409 1:      pushl $1
410         NEXT
411
412         defcode "<>",2,,NEQU    // top two words are not equal?
413         pop %eax
414         pop %ebx
415         cmp %ebx,%eax
416         je 1f
417         pushl $1
418         NEXT
419 1:      pushl $0
420         NEXT
421
422         defcode "0=",2,,ZEQU    // top of stack equals 0?
423         pop %eax
424         test %eax,%eax
425         jz 1f
426         pushl $0
427         NEXT
428 1:      pushl $1
429         NEXT
430
431         defcode "AND",3,,AND
432         pop %eax
433         andl %eax,(%esp)
434         NEXT
435
436         defcode "OR",2,,OR
437         pop %eax
438         orl %eax,(%esp)
439         NEXT
440
441         defcode "INVERT",6,,INVERT
442         notl (%esp)
443         NEXT
444
445         // COLD must not return (ie. must not call EXIT).
446         defword "COLD",4,,COLD
447         // XXX reinitialisation of the interpreter
448         .int INTERPRETER        // call the interpreter loop (never returns)
449         .int LIT,1,SYSEXIT      // hmmm, but in case it does, exit(1).
450
451         defcode "EXIT",4,,EXIT
452         POPRSP %esi             // pop return stack into %esi
453         NEXT
454
455         defcode "LIT",3,,LIT
456         // %esi points to the next command, but in this case it points to the next
457         // literal 32 bit integer.  Get that literal into %eax and increment %esi.
458         // On x86, it's a convenient single byte instruction!  (cf. NEXT macro)
459         lodsl
460         push %eax               // push the literal number on to stack
461         NEXT
462
463         defcode "LITSTRING",9,,LITSTRING
464         lodsl                   // get the length of the string
465         push %eax               // push it on the stack
466         push %esi               // push the address of the start of the string
467         addl %eax,%esi          // skip past the string
468         addl $3,%esi            // but round up to next 4 byte boundary
469         andl $~3,%esi
470         NEXT
471
472         defcode "BRANCH",6,,BRANCH
473         add (%esi),%esi         // add the offset to the instruction pointer
474         NEXT
475
476         defcode "0BRANCH",7,,ZBRANCH
477         pop %eax
478         test %eax,%eax          // top of stack is zero?
479         jz code_BRANCH          // if so, jump back to the branch function above
480         lodsl                   // otherwise we need to skip the offset
481         NEXT
482
483         defcode "!",1,,STORE
484         pop %ebx                // address to store at
485         pop %eax                // data to store there
486         mov %eax,(%ebx)         // store it
487         NEXT
488
489         defcode "@",1,,FETCH
490         pop %ebx                // address to fetch
491         mov (%ebx),%eax         // fetch it
492         push %eax               // push value onto stack
493         NEXT
494
495         defcode "+!",2,,ADDSTORE
496         pop %ebx                // address
497         pop %eax                // the amount to add
498         addl %eax,(%ebx)        // add it
499         NEXT
500
501         defcode "-!",2,,SUBSTORE
502         pop %ebx                // address
503         pop %eax                // the amount to subtract
504         subl %eax,(%ebx)        // add it
505         NEXT
506
507 /* ! and @ (STORE and FETCH) store 32-bit words.  It's also useful to be able to read and write bytes.
508  * I don't know whether FORTH has these words, so I invented my own, called !b and @b.
509  * Byte-oriented operations only work on architectures which permit them (i386 is one of those).
510  * UPDATE: writing a byte to the dictionary pointer is called C, in FORTH.
511  */
512         defcode "!b",2,,STOREBYTE
513         pop %ebx                // address to store at
514         pop %eax                // data to store there
515         movb %al,(%ebx)         // store it
516         NEXT
517
518         defcode "@b",2,,FETCHBYTE
519         pop %ebx                // address to fetch
520         xor %eax,%eax
521         movb (%ebx),%al         // fetch it
522         push %eax               // push value onto stack
523         NEXT
524
525         // The STATE variable is 0 for execute mode, != 0 for compile mode
526         defvar "STATE",5,,STATE
527
528         // This points to where compiled words go.
529         defvar "HERE",4,,HERE,user_defs_start
530
531         // This is the last definition in the dictionary.
532         defvar "LATEST",6,,LATEST,name_SYSEXIT // SYSEXIT must be last in built-in dictionary
533
534         // _X, _Y and _Z are scratch variables used by standard words.
535         defvar "_X",2,,TX
536         defvar "_Y",2,,TY
537         defvar "_Z",2,,TZ
538
539         // This stores the top of the data stack.
540         defvar "S0",2,,SZ
541
542         // This stores the top of the return stack.
543         defvar "R0",2,,RZ,return_stack
544
545         defcode "DSP@",4,,DSPFETCH
546         mov %esp,%eax
547         push %eax
548         NEXT
549
550         defcode "DSP!",4,,DSPSTORE
551         pop %esp
552         NEXT
553
554         defcode ">R",2,,TOR
555         pop %eax                // pop parameter stack into %eax
556         PUSHRSP %eax            // push it on to the return stack
557         NEXT
558
559         defcode "R>",2,,FROMR
560         POPRSP %eax             // pop return stack on to %eax
561         push %eax               // and push on to parameter stack
562         NEXT
563
564         defcode "RSP@",4,,RSPFETCH
565         push %ebp
566         NEXT
567
568         defcode "RSP!",4,,RSPSTORE
569         pop %ebp
570         NEXT
571
572         defcode "RDROP",5,,RDROP
573         lea 4(%ebp),%ebp        // pop return stack and throw away
574         NEXT
575
576 #include <asm-i386/unistd.h>
577
578         defcode "KEY",3,,KEY
579         call _KEY
580         push %eax               // push return value on stack
581         NEXT
582 _KEY:
583         mov (currkey),%ebx
584         cmp (bufftop),%ebx
585         jge 1f
586         xor %eax,%eax
587         mov (%ebx),%al
588         inc %ebx
589         mov %ebx,(currkey)
590         ret
591
592 1:      // out of input; use read(2) to fetch more input from stdin
593         xor %ebx,%ebx           // 1st param: stdin
594         mov $buffer,%ecx        // 2nd param: buffer
595         mov %ecx,currkey
596         mov $buffend-buffer,%edx // 3rd param: max length
597         mov $__NR_read,%eax     // syscall: read
598         int $0x80
599         test %eax,%eax          // If %eax <= 0, then exit.
600         jbe 2f
601         addl %eax,%ecx          // buffer+%eax = bufftop
602         mov %ecx,bufftop
603         jmp _KEY
604
605 2:      // error or out of input: exit
606         xor %ebx,%ebx
607         mov $__NR_exit,%eax     // syscall: exit
608         int $0x80
609
610         defcode "EMIT",4,,EMIT
611         pop %eax
612         call _EMIT
613         NEXT
614 _EMIT:
615         mov $1,%ebx             // 1st param: stdout
616
617         // write needs the address of the byte to write
618         mov %al,(2f)
619         mov $2f,%ecx            // 2nd param: address
620
621         mov $1,%edx             // 3rd param: nbytes = 1
622
623         mov $__NR_write,%eax    // write syscall
624         int $0x80
625         ret
626
627         .bss
628 2:      .space 1                // scratch used by EMIT
629
630         defcode "WORD",4,,WORD
631         call _WORD
632         push %ecx               // push length
633         push %edi               // push base address
634         NEXT
635
636 _WORD:
637         /* Search for first non-blank character.  Also skip \ comments. */
638 1:
639         call _KEY               // get next key, returned in %eax
640         cmpb $'\\',%al          // start of a comment?
641         je 3f                   // if so, skip the comment
642         cmpb $' ',%al
643         jbe 1b                  // if so, keep looking
644
645         /* Search for the end of the word, storing chars as we go. */
646         mov $5f,%edi            // pointer to return buffer
647 2:
648         stosb                   // add character to return buffer
649         call _KEY               // get next key, returned in %al
650         cmpb $' ',%al           // is blank?
651         ja 2b                   // if not, keep looping
652
653         /* Return the word (well, the static buffer) and length. */
654         sub $5f,%edi
655         mov %edi,%ecx           // return length of the word
656         mov $5f,%edi            // return address of the word
657         ret
658
659         /* Code to skip \ comments to end of the current line. */
660 3:
661         call _KEY
662         cmpb $'\n',%al          // end of line yet?
663         jne 3b
664         jmp 1b
665
666         .bss
667         // A static buffer where WORD returns.  Subsequent calls
668         // overwrite this buffer.  Maximum word length is 32 chars.
669 5:      .space 32
670
671         defcode "EMITSTRING",10,,EMITSTRING
672         mov $1,%ebx             // 1st param: stdout
673         pop %ecx                // 2nd param: address of string
674         pop %edx                // 3rd param: length of string
675
676         mov $__NR_write,%eax    // write syscall
677         int $0x80
678
679         NEXT
680
681         defcode ".",1,,DOT
682         pop %eax                // Get the number to print into %eax
683         call _DOT               // Easier to do this recursively ...
684         NEXT
685 _DOT:
686         mov $10,%ecx            // Base 10
687 1:
688         cmp %ecx,%eax
689         jb 2f
690         xor %edx,%edx           // %edx:%eax / %ecx -> quotient %eax, remainder %edx
691         idivl %ecx
692         pushl %edx
693         call _DOT
694         popl %eax
695         jmp 1b
696 2:
697         xor %ah,%ah
698         aam $10
699         cwde
700         addl $'0',%eax
701         call _EMIT
702         ret
703
704         // Parse a number from a string on the stack -- almost the opposite of . (DOT)
705         // Note that there is absolutely no error checking.  In particular the length of the
706         // string must be >= 1 bytes.
707         defcode "SNUMBER",7,,SNUMBER
708         pop %edi
709         pop %ecx
710         call _SNUMBER
711         push %eax
712         NEXT
713 _SNUMBER:
714         xor %eax,%eax
715         xor %ebx,%ebx
716 1:
717         imull $10,%eax          // %eax *= 10
718         movb (%edi),%bl
719         inc %edi
720         subb $'0',%bl           // ASCII -> digit
721         add %ebx,%eax
722         dec %ecx
723         jnz 1b
724         ret
725
726         defcode "FIND",4,,FIND
727         pop %edi                // %edi = address
728         pop %ecx                // %ecx = length
729         call _FIND
730         push %eax
731         NEXT
732
733 _FIND:
734         push %esi               // Save %esi so we can use it in string comparison.
735
736         // Now we start searching backwards through the dictionary for this word.
737         mov var_LATEST,%edx     // LATEST points to name header of the latest word in the dictionary
738 1:
739         test %edx,%edx          // NULL pointer?  (end of the linked list)
740         je 4f
741
742         // Compare the length expected and the length of the word.
743         // Note that if the F_HIDDEN flag is set on the word, then by a bit of trickery
744         // this won't pick the word (the length will appear to be wrong).
745         xor %eax,%eax
746         movb 4(%edx),%al        // %al = flags+length field
747         andb $(F_HIDDEN|0x1f),%al // %al = name length
748         cmpb %cl,%al            // Length is the same?
749         jne 2f
750
751         // Compare the strings in detail.
752         push %ecx               // Save the length
753         push %edi               // Save the address (repe cmpsb will move this pointer)
754         lea 5(%edx),%esi        // Dictionary string we are checking against.
755         repe cmpsb              // Compare the strings.
756         pop %edi
757         pop %ecx
758         jne 2f                  // Not the same.
759
760         // The strings are the same - return the header pointer in %eax
761         pop %esi
762         mov %edx,%eax
763         ret
764
765 2:
766         mov (%edx),%edx         // Move back through the link field to the previous word
767         jmp 1b                  // .. and loop.
768
769 4:      // Not found.
770         pop %esi
771         xor %eax,%eax           // Return zero to indicate not found.
772         ret
773
774         defcode ">CFA",4,,TCFA  // DEA -> Codeword address
775         pop %edi
776         call _TCFA
777         push %edi
778         NEXT
779 _TCFA:
780         xor %eax,%eax
781         add $4,%edi             // Skip link pointer.
782         movb (%edi),%al         // Load flags+len into %al.
783         inc %edi                // Skip flags+len byte.
784         andb $0x1f,%al          // Just the length, not the flags.
785         add %eax,%edi           // Skip the name.
786         addl $3,%edi            // The codeword is 4-byte aligned.
787         andl $~3,%edi
788         ret
789
790         defcode "CHAR",4,,CHAR
791         call _WORD              // Returns %ecx = length, %edi = pointer to word.
792         xor %eax,%eax
793         movb (%edi),%al         // Get the first character of the word.
794         push %eax               // Push it onto the stack.
795         NEXT
796
797         defcode ":",1,,COLON
798
799         // Get the word and create a dictionary entry header for it.
800         call _WORD              // Returns %ecx = length, %edi = pointer to word.
801         mov %edi,%ebx           // %ebx = address of the word
802
803         movl var_HERE,%edi      // %edi is the address of the header
804         movl var_LATEST,%eax    // Get link pointer
805         stosl                   // and store it in the header.
806
807         mov %cl,%al             // Get the length.
808         orb $F_HIDDEN,%al       // Set the HIDDEN flag on this entry.
809         stosb                   // Store the length/flags byte.
810         push %esi
811         mov %ebx,%esi           // %esi = word
812         rep movsb               // Copy the word
813         pop %esi
814         addl $3,%edi            // Align to next 4 byte boundary.
815         andl $~3,%edi
816
817         movl $DOCOL,%eax        // The codeword for user-created words is always DOCOL (the interpreter)
818         stosl
819
820         // Header built, so now update LATEST and HERE.
821         // We'll be compiling words and putting them HERE.
822         movl var_HERE,%eax
823         movl %eax,var_LATEST
824         movl %edi,var_HERE
825
826         // And go into compile mode by setting STATE to 1.
827         movl $1,var_STATE
828         NEXT
829
830         defcode ",",1,,COMMA
831         pop %eax                // Code pointer to store.
832         call _COMMA
833         NEXT
834 _COMMA:
835         movl var_HERE,%edi      // HERE
836         stosl                   // Store it.
837         movl %edi,var_HERE      // Update HERE (incremented)
838         ret
839
840         defcode "HIDDEN",6,,HIDDEN
841         call _HIDDEN
842         NEXT
843 _HIDDEN:
844         movl var_LATEST,%edi    // LATEST word.
845         addl $4,%edi            // Point to name/flags byte.
846         xorb $F_HIDDEN,(%edi)   // Toggle the HIDDEN bit.
847         ret
848
849         defcode "IMMEDIATE",9,F_IMMED,IMMEDIATE
850         call _IMMEDIATE
851         NEXT
852 _IMMEDIATE:
853         movl var_LATEST,%edi    // LATEST word.
854         addl $4,%edi            // Point to name/flags byte.
855         xorb $F_IMMED,(%edi)    // Toggle the IMMED bit.
856         ret
857
858         defcode ";",1,F_IMMED,SEMICOLON
859         movl $EXIT,%eax         // EXIT is the final codeword in compiled words.
860         call _COMMA             // Store it.
861         call _HIDDEN            // Toggle the HIDDEN flag (unhides the new word).
862         xor %eax,%eax           // Set STATE to 0 (back to execute mode).
863         movl %eax,var_STATE
864         NEXT
865
866 /* This definiton of ' (TICK) is strictly cheating - it also only works in compiled code. */
867         defcode "'",1,,TICK
868         lodsl                   // Get the address of the next word and skip it.
869         pushl %eax              // Push it on the stack.
870         NEXT
871
872 /* This interpreter is pretty simple, but remember that in FORTH you can always override
873  * it later with a more powerful one!
874  */
875         defword "INTERPRETER",11,,INTERPRETER
876         .int INTERPRET,RDROP,INTERPRETER
877
878         defcode "INTERPRET",9,,INTERPRET
879         call _WORD              // Returns %ecx = length, %edi = pointer to word.
880
881         // Is it in the dictionary?
882         xor %eax,%eax
883         movl %eax,interpret_is_lit // Not a literal number (not yet anyway ...)
884         call _FIND              // Returns %eax = pointer to header or 0 if not found.
885         test %eax,%eax          // Found?
886         jz 1f
887
888         // In the dictionary.  Is it an IMMEDIATE codeword?
889         mov %eax,%edi           // %edi = dictionary entry
890         movb 4(%edi),%al        // Get name+flags.
891         push %ax                // Just save it for now.
892         call _TCFA              // Convert dictionary entry (in %edi) to codeword pointer.
893         pop %ax
894         andb $F_IMMED,%al       // Is IMMED flag set?
895         mov %edi,%eax
896         jnz 4f                  // If IMMED, jump straight to executing.
897
898         jmp 2f
899
900 1:      // Not in the dictionary (not a word) so assume it's a literal number.
901         incl interpret_is_lit
902         call _SNUMBER           // Returns the parsed number in %eax
903         mov %eax,%ebx
904         mov $LIT,%eax           // The word is LIT
905
906 2:      // Are we compiling or executing?
907         movl var_STATE,%edx
908         test %edx,%edx
909         jz 4f                   // Jump if executing.
910
911         // Compiling - just append the word to the current dictionary definition.
912         call _COMMA
913         mov interpret_is_lit,%ecx // Was it a literal?
914         test %ecx,%ecx
915         jz 3f
916         mov %ebx,%eax           // Yes, so LIT is followed by a number.
917         call _COMMA
918 3:      NEXT
919
920 4:      // Executing - run it!
921         mov interpret_is_lit,%ecx // Literal?
922         test %ecx,%ecx          // Literal?
923         jnz 5f
924
925         // Not a literal, execute it now.  This never returns, but the codeword will
926         // eventually call NEXT which will reenter the loop in INTERPRETER.
927         jmp *(%eax)
928
929 5:      // Executing a literal, which means push it on the stack.
930         push %ebx
931         NEXT
932
933         .data
934         .align 4
935 interpret_is_lit:
936         .int 0                  // Flag used to record if reading a literal
937
938         // NB: SYSEXIT must be the last entry in the built-in dictionary.
939         defcode SYSEXIT,7,,SYSEXIT
940         pop %ebx
941         mov $__NR_exit,%eax
942         int $0x80
943
944 /*----------------------------------------------------------------------
945  * Input buffer & initial input.
946  */
947         .data
948         .align 4096
949 buffer:
950         // XXX gives 'Warning: unterminated string; newline inserted' messages which you can ignore
951         .ascii "\
952 \\ Define some character constants
953 : '\\n'   10 ;
954 : 'SPACE' 32 ;
955 : '\"'    34 ;
956 : ':'     58 ;
957
958 \\ CR prints a carriage return
959 : CR '\\n' EMIT ;
960
961 \\ SPACE prints a space
962 : SPACE 'SPACE' EMIT ;
963
964 \\ Primitive . (DOT) function doesn't follow with a blank, so redefine it to behave like FORTH.
965 \\ Notice how we can trivially redefine existing functions.
966 : . . SPACE ;
967
968 \\ DUP, DROP are defined in assembly for speed, but this is how you might define them
969 \\ in FORTH.  Notice use of the scratch variables _X and _Y.
970 \\ : DUP _X ! _X @ _X @ ;
971 \\ : DROP _X ! ;
972
973 \\ The 2... versions of the standard operators work on pairs of stack entries.  They're not used
974 \\ very commonly so not really worth writing in assembler.  Here is how they are defined in FORTH.
975 : 2DUP OVER OVER ;
976 : 2DROP DROP DROP ;
977
978 \\ More standard FORTH words.
979 : 2* 2 * ;
980 : 2/ 2 / ;
981
982 \\ [ and ] allow you to break into immediate mode while compiling a word.
983 : [ IMMEDIATE           \\ define [ as an immediate word
984         0 STATE !       \\ go into immediate mode
985         ;
986
987 : ]
988         1 STATE !       \\ go back to compile mode
989         ;
990
991 \\ LITERAL takes whatever is on the stack and compiles LIT <foo>
992 : LITERAL IMMEDIATE
993         ' LIT ,         \\ compile LIT
994         ,               \\ compile the literal itself (from the stack)
995         ;
996
997 \\ condition IF true-part THEN rest
998 \\   compiles to:
999 \\ condition 0BRANCH OFFSET true-part rest
1000 \\   where OFFSET is the offset of 'rest'
1001 \\ condition IF true-part ELSE false-part THEN
1002 \\   compiles to:
1003 \\ condition 0BRANCH OFFSET true-part BRANCH OFFSET2 false-part rest
1004 \\   where OFFSET if the offset of false-part and OFFSET2 is the offset of rest
1005
1006 \\ IF is an IMMEDIATE word which compiles 0BRANCH followed by a dummy offset, and places
1007 \\ the address of the 0BRANCH on the stack.  Later when we see THEN, we pop that address
1008 \\ off the stack, calculate the offset, and back-fill the offset.
1009 : IF IMMEDIATE
1010         ' 0BRANCH ,     \\ compile 0BRANCH
1011         HERE @          \\ save location of the offset on the stack
1012         0 ,             \\ compile a dummy offset
1013 ;
1014
1015 : THEN IMMEDIATE
1016         DUP
1017         HERE @ SWAP -   \\ calculate the offset from the address saved on the stack
1018         SWAP !          \\ store the offset in the back-filled location
1019 ;
1020
1021 : ELSE IMMEDIATE
1022         ' BRANCH ,      \\ definite branch to just over the false-part
1023         HERE @          \\ save location of the offset on the stack
1024         0 ,             \\ compile a dummy offset
1025         SWAP            \\ now back-fill the original (IF) offset
1026         DUP             \\ same as for THEN word above
1027         HERE @ SWAP -
1028         SWAP !
1029 ;
1030
1031 \\ BEGIN loop-part condition UNTIL
1032 \\   compiles to:
1033 \\ loop-part condition 0BRANCH OFFSET
1034 \\   where OFFSET points back to the loop-part
1035 \\ This is like do { loop-part } while (condition) in the C language
1036 : BEGIN IMMEDIATE
1037         HERE @          \\ save location on the stack
1038 ;
1039
1040 : UNTIL IMMEDIATE
1041         ' 0BRANCH ,     \\ compile 0BRANCH
1042         HERE @ -        \\ calculate the offset from the address saved on the stack
1043         ,               \\ compile the offset here
1044 ;
1045
1046 \\ BEGIN loop-part AGAIN
1047 \\   compiles to:
1048 \\ loop-part BRANCH OFFSET
1049 \\   where OFFSET points back to the loop-part
1050 \\ In other words, an infinite loop which can only be returned from with EXIT
1051 : AGAIN IMMEDIATE
1052         ' BRANCH ,      \\ compile BRANCH
1053         HERE @ -        \\ calculate the offset back
1054         ,               \\ compile the offset here
1055 ;
1056
1057 \\ BEGIN condition WHILE loop-part REPEAT
1058 \\   compiles to:
1059 \\ condition 0BRANCH OFFSET2 loop-part BRANCH OFFSET
1060 \\   where OFFSET points back to condition (the beginning) and OFFSET2 points to after the whole piece of code
1061 \\ So this is like a while (condition) { loop-part } loop in the C language
1062 : WHILE IMMEDIATE
1063         ' 0BRANCH ,     \\ compile 0BRANCH
1064         HERE @          \\ save location of the offset2 on the stack
1065         0 ,             \\ compile a dummy offset2
1066 ;
1067
1068 : REPEAT IMMEDIATE
1069         ' BRANCH ,      \\ compile BRANCH
1070         SWAP            \\ get the original offset (from BEGIN)
1071         HERE @ - ,      \\ and compile it after BRANCH
1072         DUP
1073         HERE @ SWAP -   \\ calculate the offset2
1074         SWAP !          \\ and back-fill it in the original location
1075 ;
1076
1077 \\ With the looping constructs, we can now write SPACES, which writes n spaces to stdout.
1078 : SPACES
1079         BEGIN
1080                 SPACE   \\ print a space
1081                 1-      \\ until we count down to 0
1082                 DUP 0=
1083         UNTIL
1084 ;
1085
1086 \\ .S prints the contents of the stack.  Very useful for debugging.
1087 : .S
1088         DSP@            \\ get current stack pointer
1089         BEGIN
1090                 DUP @ .         \\ print the stack element
1091                 4+              \\ move up
1092                 DUP S0 @ 4- =   \\ stop when we get to the top
1093         UNTIL
1094         DROP
1095 ;
1096
1097 \\ DEPTH returns the depth of the stack.
1098 : DEPTH S0 @ DSP@ - ;
1099
1100 \\ .\" is the print string operator in FORTH.  Example: .\" Something to print\"
1101 \\ The space after the operator is the ordinary space required between words.
1102 \\ This is tricky to define because it has to do different things depending on whether
1103 \\ we are compiling or in immediate mode.  (Thus the word is marked IMMEDIATE so it can
1104 \\ detect this and do different things).
1105 \\ In immediate mode we just keep reading characters and printing them until we get to
1106 \\ the next double quote.
1107 \\ In compile mode we have the problem of where we're going to store the string (remember
1108 \\ that the input buffer where the string comes from may be overwritten by the time we
1109 \\ come round to running the function).  We store the string in the compiled function
1110 \\ like this:
1111 \\   LITSTRING, string length, string rounded up to 4 bytes, EMITSTRING, ...
1112 : .\" IMMEDIATE
1113         STATE @         \\ compiling?
1114         IF
1115                 ' LITSTRING ,   \\ compile LITSTRING
1116                 HERE @          \\ save the address of the length word on the stack
1117                 0 ,             \\ dummy length - we don't know what it is yet
1118                 BEGIN
1119                         KEY             \\ get next character of the string
1120                         DUP '\"' <>
1121                 WHILE
1122                         HERE @ !b       \\ store the character in the compiled image
1123                         1 HERE +!       \\ increment HERE pointer by 1 byte
1124                 REPEAT
1125                 DROP            \\ drop the double quote character at the end
1126                 DUP             \\ get the saved address of the length word
1127                 HERE @ SWAP -   \\ calculate the length
1128                 4-              \\ subtract 4 (because we measured from the start of the length word)
1129                 SWAP !          \\ and back-fill the length location
1130                 HERE @          \\ round up to next multiple of 4 bytes for the remaining code
1131                 3 +
1132                 3 INVERT AND
1133                 HERE !
1134                 ' EMITSTRING ,  \\ compile the final EMITSTRING
1135         ELSE
1136                 \\ In immediate mode, just read characters and print them until we get
1137                 \\ to the ending double quote.  Much simpler than the above code!
1138                 BEGIN
1139                         KEY
1140                         DUP '\"' = IF EXIT THEN
1141                         EMIT
1142                 AGAIN
1143         THEN
1144 ;
1145
1146 \\ While compiling, [COMPILE] WORD compiles WORD if it would otherwise be IMMEDIATE.
1147 : [COMPILE] IMMEDIATE
1148         WORD            \\ get the next word
1149         FIND            \\ find it in the dictionary
1150         >CFA            \\ get its codeword
1151         ,               \\ and compile that
1152 ;
1153
1154 \\ RECURSE makes a recursive call to the current word that is being compiled.
1155 \\ Normally while a word is being compiled, it is marked HIDDEN so that references to the
1156 \\ same word within are calls to the previous definition of the word.
1157 : RECURSE IMMEDIATE
1158         LATEST @ >CFA   \\ LATEST points to the word being compiled at the moment
1159         ,               \\ compile it
1160 ;
1161
1162 \\ ALLOT is used to allocate (static) memory when compiling.  It increases HERE by
1163 \\ the amount given on the stack.
1164 : ALLOT HERE +! ;
1165
1166
1167 \\ Finally print the welcome prompt.
1168 .\" OK \"
1169 "
1170
1171 _initbufftop:
1172         .align 4096
1173 buffend:
1174
1175 currkey:
1176         .int buffer
1177 bufftop:
1178         .int _initbufftop