/* A sometimes minimal FORTH compiler and tutorial for Linux / i386 systems. -*- asm -*-
By Richard W.M. Jones <rich@annexia.org> http://annexia.org/forth
This is PUBLIC DOMAIN (see public domain release statement below).
- $Id: jonesforth.S,v 1.29 2007-09-24 00:37:01 rich Exp $
+ $Id: jonesforth.S,v 1.33 2007-09-26 22:47:49 rich Exp $
gcc -m32 -nostdlib -static -Wl,-Ttext,0 -o jonesforth jonesforth.S
*/
- .set JONES_VERSION,29
+ .set JONES_VERSION,32
/*
INTRODUCTION ----------------------------------------------------------------------
over every other element in a list of numbers? You can add it to the language. What
about an operator which pulls in variables directly from a configuration file and makes
them available as FORTH variables? Or how about adding Makefile-like dependencies to
- the language? No problem in FORTH. This concept isn't common in programming languages,
+ the language? No problem in FORTH. How about modifying the FORTH compiler to allow
+ complex inlining strategies -- simple. This concept isn't common in programming languages,
but it has a name (in fact two names): "macros" (by which I mean LISP-style macros, not
the lame C preprocessor) and "domain specific languages" (DSLs).
This code draws heavily on the design of LINA FORTH (http://home.hccnet.nl/a.w.m.van.der.horst/lina.html)
by Albert van der Horst. Any similarities in the code are probably not accidental.
- Also I used this document (http://ftp.funet.fi/pub/doc/IOCCC/1992/buzzard.2.design) which really
- defies easy explanation.
+ Some parts of this FORTH are also based on this IOCCC entry from 1992:
+ http://ftp.funet.fi/pub/doc/IOCCC/1992/buzzard.2.design.
+ I was very proud when Sean Barrett, the original author of the IOCCC entry, commented in the LtU thread
+ http://lambda-the-ultimate.org/node/2452#comment-36818 about this FORTH.
+
+ And finally I'd like to acknowledge the (possibly forgotten?) authors of ARTIC FORTH because their
+ original program which I still have on original cassette tape kept nagging away at me all these years.
+ http://en.wikipedia.org/wiki/Artic_Software
PUBLIC DOMAIN ----------------------------------------------------------------------
/*
BUILT-IN WORDS ----------------------------------------------------------------------
- Remember our dictionary entries (headers). Let's bring those together with the codeword
+ Remember our dictionary entries (headers)? Let's bring those together with the codeword
and data words to see how : DOUBLE DUP + ; really looks in memory.
pointer to previous word
push %eax // ignore overflow
NEXT
- defcode "/",1,,DIV
- xor %edx,%edx
- pop %ebx
- pop %eax
- idivl %ebx
- push %eax // push quotient
- NEXT
+/*
+ In this FORTH, only /MOD is primitive. Later we will define the / and MOD words in
+ terms of the primitive /MOD.
+*/
- defcode "MOD",3,,MOD
+ defcode "/MOD",4,,DIVMOD
xor %edx,%edx
pop %ebx
pop %eax
idivl %ebx
push %edx // push remainder
+ push %eax // push quotient
NEXT
defcode "=",1,,EQU // top two words are equal?
1: pushl $1
NEXT
- defcode "0<",2,,ZLT
+ defcode "0<",2,,ZLT // comparisons with 0
pop %eax
test %eax,%eax
jl 1f
1: pushl $1
NEXT
- defcode "AND",3,,AND
+ defcode "AND",3,,AND // bitwise AND
pop %eax
andl %eax,(%esp)
NEXT
- defcode "OR",2,,OR
+ defcode "OR",2,,OR // bitwise OR
pop %eax
orl %eax,(%esp)
NEXT
- defcode "XOR",3,,XOR
+ defcode "XOR",3,,XOR // bitwise XOR
pop %eax
xorl %eax,(%esp)
NEXT
- defcode "INVERT",6,,INVERT // this is the FORTH bitwise "NOT" function
+ defcode "INVERT",6,,INVERT // this is the FORTH bitwise "NOT" function (cf. NEGATE)
notl (%esp)
NEXT
| addr of EXIT |
+------------------+
- And NEXT just completes the job by, well in this case just by calling DOUBLE again :-)
+ And NEXT just completes the job by, well, in this case just by calling DOUBLE again :-)
LITERALS ----------------------------------------------------------------------
subl %eax,(%ebx) // add it
NEXT
-/* ! and @ (STORE and FETCH) store 32-bit words. It's also useful to be able to read and write bytes.
- * I don't know whether FORTH has these words, so I invented my own, called !b and @b.
- * Byte-oriented operations only work on architectures which permit them (i386 is one of those).
- * UPDATE: writing a byte to the dictionary pointer is called C, in FORTH.
+/*
+ ! and @ (STORE and FETCH) store 32-bit words. It's also useful to be able to read and write bytes
+ so we also define standard words C@ and C!.
+
+ Byte-oriented operations only work on architectures which permit them (i386 is one of those).
*/
- defcode "!b",2,,STOREBYTE
+
+ defcode "C!",2,,STOREBYTE
pop %ebx // address to store at
pop %eax // data to store there
movb %al,(%ebx) // store it
NEXT
- defcode "@b",2,,FETCHBYTE
+ defcode "C@",2,,FETCHBYTE
pop %ebx // address to fetch
xor %eax,%eax
movb (%ebx),%al // fetch it
DOCOL Pointer to DOCOL.
F_IMMED The IMMEDIATE flag's actual value.
F_HIDDEN The HIDDEN flag's actual value.
- F_LENMASK The length mask.
+ F_LENMASK The length mask in the flags/len byte.
*/
.macro defconst name, namelen, flags=0, label, value
5: .space 32
/*
- . (also called DOT) prints the top of the stack as an integer in the current BASE.
-*/
-
- defcode ".",1,,DOT
- pop %eax // Get the number to print into %eax
- call _DOT // Easier to do this recursively ...
- NEXT
-_DOT:
- mov var_BASE,%ecx // Get current BASE
-1:
- cmp %ecx,%eax // %eax < BASE? If so jump to print immediately.
- jb 2f
- xor %edx,%edx // %edx:%eax / %ecx -> quotient %eax, remainder %edx
- idivl %ecx
- pushl %edx // Print quotient (top half) first ...
- call _DOT
- popl %eax // ... then loop to print remainder
- jmp 1b
-2: // %eax < BASE so print immediately.
- movl $digits,%edx
- addl %eax,%edx
- movb (%edx),%al // Note top bits are already zero.
- call _EMIT
- ret
- .section .rodata
-digits: .ascii "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-
-/*
- Almost the opposite of DOT (but not quite), SNUMBER parses a numeric string such as one returned
- by WORD and pushes the number on the parameter stack.
+ As well as reading in words we'll need to read in numbers and for that we are using a function
+ called SNUMBER. This parses a numeric string such as one returned by WORD and pushes the
+ number on the parameter stack.
This function does absolutely no error checking, and in particular the length of the string
must be >= 1 bytes, and should contain only digits 0-9. If it doesn't you'll get random results.