'SPACE' -> BL (standard FORTH word)
[jonesforth.git] / jonesforth.f
index b05b64c..711cf85 100644 (file)
@@ -2,7 +2,7 @@
 \      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).
 \      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.f,v 1.5 2007-09-26 22:20:52 rich Exp $
+\      $Id: jonesforth.f,v 1.11 2007-09-29 23:13:45 rich Exp $
 \
 \      The first part of this tutorial is in jonesforth.S.  Get if from http://annexia.org/forth
 \
 \
 \      The first part of this tutorial is in jonesforth.S.  Get if from http://annexia.org/forth
 \
 : MOD /MOD DROP ;
 
 \ Define some character constants
 : MOD /MOD DROP ;
 
 \ Define some character constants
-: '\n'   10 ;
-: 'SPACE' 32 ;
+: '\n' 10 ;
+: BL   32 ; \ BL (BLank) is a standard FORTH word for space.
 
 \ CR prints a carriage return
 : CR '\n' EMIT ;
 
 \ SPACE prints a space
 
 \ CR prints a carriage return
 : CR '\n' EMIT ;
 
 \ SPACE prints a space
-: SPACE 'SPACE' EMIT ;
+: SPACE BL EMIT ;
 
 \ DUP, DROP are defined in assembly for speed, but this is how you might define them
 \ in FORTH.  Notice use of the scratch variables _X and _Y.
 
 \ DUP, DROP are defined in assembly for speed, but this is how you might define them
 \ in FORTH.  Notice use of the scratch variables _X and _Y.
 ;
 
 \ A few more character constants defined the same way as above.
 ;
 
 \ A few more character constants defined the same way as above.
+: ';' [ CHAR ; ] LITERAL ;
 : '(' [ CHAR ( ] LITERAL ;
 : ')' [ CHAR ) ] LITERAL ;
 : '"' [ CHAR " ] LITERAL ;
 : '(' [ CHAR ( ] LITERAL ;
 : ')' [ CHAR ) ] LITERAL ;
 : '"' [ CHAR " ] LITERAL ;
        ,               \ compile it
 ;
 
        ,               \ compile it
 ;
 
+\      CONTROL STRUCTURES ----------------------------------------------------------------------
+\
 \ So far we have defined only very simple definitions.  Before we can go further, we really need to
 \ make some control structures, like IF ... THEN and loops.  Luckily we can define arbitrary control
 \ structures directly in FORTH.
 \ So far we have defined only very simple definitions.  Before we can go further, we really need to
 \ make some control structures, like IF ... THEN and loops.  Luckily we can define arbitrary control
 \ structures directly in FORTH.
        SWAP !          \ and back-fill it in the original location
 ;
 
        SWAP !          \ and back-fill it in the original location
 ;
 
+\      COMMENTS ----------------------------------------------------------------------
+\
 \ FORTH allows ( ... ) as comments within function definitions.  This works by having an IMMEDIATE
 \ word called ( which just drops input characters until it hits the corresponding ).
 : ( IMMEDIATE
 \ FORTH allows ( ... ) as comments within function definitions.  This works by having an IMMEDIATE
 \ word called ( which just drops input characters until it hits the corresponding ).
 : ( IMMEDIATE
 (
        From now on we can use ( ... ) for comments.
 
 (
        From now on we can use ( ... ) for comments.
 
+       STACK NOTATION ----------------------------------------------------------------------
+
        In FORTH style we can also use ( ... -- ... ) to show the effects that a word has on the
        parameter stack.  For example:
 
        In FORTH style we can also use ( ... -- ... ) to show the effects that a word has on the
        parameter stack.  For example:
 
        ( -- )          means the word has no effect on the stack
 )
 
        ( -- )          means the word has no effect on the stack
 )
 
+( Some more complicated stack examples, showing the stack notation. )
+: NIP ( x y -- y ) SWAP DROP ;
+: TUCK ( x y -- y x y ) DUP ROT ;
+: PICK ( x_u ... x_1 x_0 u -- x_u ... x_1 x_0 x_u )
+       1+              ( add one because of 'u' on the stack )
+       4 *             ( multiply by the word size )
+       DSP@ +          ( add to the stack pointer )
+       @               ( and fetch )
+;
+
 ( With the looping constructs, we can now write SPACES, which writes n spaces to stdout. )
 : SPACES       ( n -- )
        BEGIN
 ( With the looping constructs, we can now write SPACES, which writes n spaces to stdout. )
 : SPACES       ( n -- )
        BEGIN
 : HEX ( -- ) 16 BASE ! ;
 
 (
 : HEX ( -- ) 16 BASE ! ;
 
 (
+       PRINTING NUMBERS ----------------------------------------------------------------------
+
        The standard FORTH word . (DOT) is very important.  It takes the number at the top
        of the stack and prints it out.  However first I'm going to implement some lower-level
        FORTH words:
        The standard FORTH word . (DOT) is very important.  It takes the number at the top
        of the stack and prints it out.  However first I'm going to implement some lower-level
        FORTH words:
 ( This is the underlying recursive definition of U. )
 : U.           ( u -- )
        BASE @ /MOD     ( width rem quot )
 ( This is the underlying recursive definition of U. )
 : U.           ( u -- )
        BASE @ /MOD     ( width rem quot )
-       DUP 0<> IF      ( if quotient <> 0 then )
+       ?DUP IF                 ( if quotient <> 0 then )
                RECURSE         ( print the quotient )
                RECURSE         ( print the quotient )
-       ELSE
-               DROP            ( drop the zero quotient )
        THEN
 
        ( print the remainder )
        THEN
 
        ( print the remainder )
 ( This word returns the width (in characters) of an unsigned number in the current base )
 : UWIDTH       ( u -- width )
        BASE @ /        ( rem quot )
 ( This word returns the width (in characters) of an unsigned number in the current base )
 : UWIDTH       ( u -- width )
        BASE @ /        ( rem quot )
-       DUP 0<> IF      ( if quotient <> 0 then )
+       ?DUP IF         ( if quotient <> 0 then )
                RECURSE 1+      ( return 1+recursive call )
        ELSE
                RECURSE 1+      ( return 1+recursive call )
        ELSE
-               DROP            ( drop the zero quotient )
                1               ( return 1 )
        THEN
 ;
                1               ( return 1 )
        THEN
 ;
 : U. U. SPACE ;
 
 ( ? fetches the integer at an address and prints it. )
 : U. U. SPACE ;
 
 ( ? fetches the integer at an address and prints it. )
-: ? @ . ;
+: ? ( addr -- ) @ . ;
 
 ( c a b WITHIN returns true if a <= c and c < b )
 : WITHIN
 
 ( c a b WITHIN returns true if a <= c and c < b )
 : WITHIN
 : ALIGN HERE @ ALIGNED HERE ! ;
 
 (
 : ALIGN HERE @ ALIGNED HERE ! ;
 
 (
+       STRINGS ----------------------------------------------------------------------
+
        S" string" is used in FORTH to define strings.  It leaves the address of the string and
        S" string" is used in FORTH to define strings.  It leaves the address of the string and
-       its length on the stack, with the address at the top.  The space following S" is the normal
+       its length on the stack, (length at the top of stack).  The space following S" is the normal
        space between FORTH words and is not a part of the string.
 
        This is tricky to define because it has to do different things depending on whether
        space between FORTH words and is not a part of the string.
 
        This is tricky to define because it has to do different things depending on whether
        case we put the string at HERE (but we _don't_ change HERE).  This is meant as a temporary
        location, likely to be overwritten soon after.
 )
        case we put the string at HERE (but we _don't_ change HERE).  This is meant as a temporary
        location, likely to be overwritten soon after.
 )
-: S" IMMEDIATE         ( -- len addr )
+: S" IMMEDIATE         ( -- addr len )
        STATE @ IF      ( compiling? )
                ' LITSTRING ,   ( compile LITSTRING )
                HERE @          ( save the address of the length word on the stack )
        STATE @ IF      ( compiling? )
                ' LITSTRING ,   ( compile LITSTRING )
                HERE @          ( save the address of the length word on the stack )
                DROP            ( drop the final " character )
                HERE @ -        ( calculate the length )
                HERE @          ( push the start address )
                DROP            ( drop the final " character )
                HERE @ -        ( calculate the length )
                HERE @          ( push the start address )
+               SWAP            ( addr len )
        THEN
 ;
 
        THEN
 ;
 
        In immediate mode we just keep reading characters and printing them until we get to
        the next double quote.
 
        In immediate mode we just keep reading characters and printing them until we get to
        the next double quote.
 
-       In compile mode we use S" to store the string, then add EMITSTRING afterwards:
-               LITSTRING <string length> <string rounded up to 4 bytes> EMITSTRING
+       In compile mode we use S" to store the string, then add TELL afterwards:
+               LITSTRING <string length> <string rounded up to 4 bytes> TELL
 
        It may be interesting to note the use of [COMPILE] to turn the call to the immediate
        word S" into compilation of that word.  It compiles it into the definition of .",
 
        It may be interesting to note the use of [COMPILE] to turn the call to the immediate
        word S" into compilation of that word.  It compiles it into the definition of .",
 : ." IMMEDIATE         ( -- )
        STATE @ IF      ( compiling? )
                [COMPILE] S"    ( read the string, and compile LITSTRING, etc. )
 : ." IMMEDIATE         ( -- )
        STATE @ IF      ( compiling? )
                [COMPILE] S"    ( read the string, and compile LITSTRING, etc. )
-               ' EMITSTRING ,  ( compile the final EMITSTRING )
+               ' TELL ,        ( compile the final TELL )
        ELSE
                ( In immediate mode, just read characters and print them until we get
                  to the ending double quote. )
        ELSE
                ( In immediate mode, just read characters and print them until we get
                  to the ending double quote. )
 ;
 
 (
 ;
 
 (
+       CONSTANTS AND VARIABLES ----------------------------------------------------------------------
+
        In FORTH, global constants and variables are defined like this:
 
        10 CONSTANT TEN         when TEN is executed, it leaves the integer 10 on the stack
        In FORTH, global constants and variables are defined like this:
 
        10 CONSTANT TEN         when TEN is executed, it leaves the integer 10 on the stack
 ;
 
 (
 ;
 
 (
+       VALUES ----------------------------------------------------------------------
+
        VALUEs are like VARIABLEs but with a simpler syntax.  You would generally use them when you
        want a variable which is read often, and written infrequently.
 
        VALUEs are like VARIABLEs but with a simpler syntax.  You would generally use them when you
        want a variable which is read often, and written infrequently.
 
 ;
 
 (
 ;
 
 (
+       PRINTING THE DICTIONARY ----------------------------------------------------------------------
+
        ID. takes an address of a dictionary entry and prints the word's name.
 
        For example: LATEST @ ID. would print the name of the last word that was defined.
        ID. takes an address of a dictionary entry and prints the word's name.
 
        For example: LATEST @ ID. would print the name of the last word that was defined.
 : WORDS
        LATEST @        ( start at LATEST dictionary entry )
        BEGIN
 : WORDS
        LATEST @        ( start at LATEST dictionary entry )
        BEGIN
-               DUP 0<>         ( while link pointer is not null )
+               ?DUP            ( while link pointer is not null )
        WHILE
                DUP ?HIDDEN NOT IF      ( ignore hidden words )
                        DUP ID.         ( but if not hidden, print the word )
        WHILE
                DUP ?HIDDEN NOT IF      ( ignore hidden words )
                        DUP ID.         ( but if not hidden, print the word )
                SPACE
                @               ( dereference the link pointer - go to previous word )
        REPEAT
                SPACE
                @               ( dereference the link pointer - go to previous word )
        REPEAT
-       DROP
        CR
 ;
 
 (
        CR
 ;
 
 (
+       FORGET ----------------------------------------------------------------------
+
        So far we have only allocated words and memory.  FORTH provides a rather primitive method
        to deallocate.
 
        So far we have only allocated words and memory.  FORTH provides a rather primitive method
        to deallocate.
 
 ;
 
 (
 ;
 
 (
+       DUMP ----------------------------------------------------------------------
+
        DUMP is used to dump out the contents of memory, in the 'traditional' hexdump format.
        DUMP is used to dump out the contents of memory, in the 'traditional' hexdump format.
+
+       Notice that the parameters to DUMP (address, length) are compatible with string words
+       such as WORD and S".
 )
 : DUMP         ( addr len -- )
        BASE @ ROT              ( save the current BASE at the bottom of the stack )
 )
 : DUMP         ( addr len -- )
        BASE @ ROT              ( save the current BASE at the bottom of the stack )
        BEGIN
                DUP 0>          ( while len > 0 )
        WHILE
        BEGIN
                DUP 0>          ( while len > 0 )
        WHILE
-               OVER 8 .R       ( print the address )
+               OVER 8 U.R      ( print the address )
                SPACE
 
                ( print up to 16 words on this line )
                SPACE
 
                ( print up to 16 words on this line )
        BASE !                  ( restore saved BASE )
 ;
 
        BASE !                  ( restore saved BASE )
 ;
 
-( Finally print the welcome prompt. )
+(
+       CASE ----------------------------------------------------------------------
+
+       CASE...ENDCASE is how we do switch statements in FORTH.  There is no generally
+       agreed syntax for this, so I've gone for the syntax mandated by the ISO standard
+       FORTH (ANS-FORTH).
+
+       ( some value on the stack )
+       CASE
+       test1 OF ... ENDOF
+       test2 OF ... ENDOF
+       testn OF ... ENDOF
+       ... ( default case )
+       ENDCASE
+
+       The CASE statement tests the value on the stack by comparing it for equality with
+       test1, test2, ..., testn and executes the matching piece of code within OF ... ENDOF.
+       If none of the test values match then the default case is executed.  Inside the ... of
+       the default case, the value is still at the top of stack (it is implicitly DROP-ed
+       by ENDCASE).  When ENDOF is executed it jumps after ENDCASE (ie. there is no "fall-through"
+       and no need for a break statement like in C).
+
+       The default case may be omitted.  In fact the tests may also be omitted so that you
+       just have a default case, although this is probably not very useful.
+
+       An example (assuming that 'q', etc. are words which push the ASCII value of the letter
+       on the stack):
+
+       0 VALUE QUIT
+       0 VALUE SLEEP
+       KEY CASE
+               'q' OF 1 TO QUIT ENDOF
+               's' OF 1 TO SLEEP ENDOF
+               ( default case: )
+               ." Sorry, I didn't understand key <" DUP EMIT ." >, try again." CR
+       ENDCASE
+
+       (In some versions of FORTH, more advanced tests are supported, such as ranges, etc.
+       Other versions of FORTH need you to write OTHERWISE to indicate the default case.
+       As I said above, this FORTH tries to follow the ANS FORTH standard).
+
+       The implementation of CASE...ENDCASE is somewhat non-trivial.  I'm following the
+       implementations from here:
+       http://www.uni-giessen.de/faq/archiv/forthfaq.case_endcase/msg00000.html
+
+       The general plan is to compile the code as a series of IF statements:
+
+       CASE                            (push 0 on the immediate-mode parameter stack)
+       test1 OF ... ENDOF              test1 OVER = IF DROP ... ELSE
+       test2 OF ... ENDOF              test2 OVER = IF DROP ... ELSE
+       testn OF ... ENDOF              testn OVER = IF DROP ... ELSE
+       ... ( default case )            ...
+       ENDCASE                         DROP THEN [THEN [THEN ...]]
+
+       The CASE statement pushes 0 on the immediate-mode parameter stack, and that number
+       is used to count how many THEN statements we need when we get to ENDCASE so that each
+       IF has a matching THEN.  The counting is done implicitly.  If you recall from the
+       implementation above of IF, each IF pushes a code address on the immediate-mode stack,
+       and these addresses are non-zero, so by the time we get to ENDCASE the stack contains
+       some number of non-zeroes, followed by a zero.  The number of non-zeroes is how many
+       times IF has been called, so how many times we need to match it with THEN.
+
+       This code uses [COMPILE] so that we compile calls to IF, ELSE, THEN instead of
+       actually calling them while we're compiling the words below.
+
+       As is the case with all of our control structures, they only work within word
+       definitions, not in immediate mode.
+)
+: CASE IMMEDIATE
+       0               ( push 0 to mark the bottom of the stack )
+;
+
+: OF IMMEDIATE
+       ' OVER ,        ( compile OVER )
+       ' = ,           ( compile = )
+       [COMPILE] IF    ( compile IF )
+       ' DROP ,        ( compile DROP )
+;
+
+: ENDOF IMMEDIATE
+       [COMPILE] ELSE  ( ENDOF is the same as ELSE )
+;
+
+: ENDCASE IMMEDIATE
+       ' DROP ,        ( compile DROP )
+
+       ( keep compiling THEN until we get to our zero marker )
+       BEGIN
+               ?DUP
+       WHILE
+               [COMPILE] THEN
+       REPEAT
+;
+
+(
+       DECOMPILER ----------------------------------------------------------------------
+
+       CFA> is the opposite of >CFA.  It takes a codeword and tries to find the matching
+       dictionary definition.
+
+       In this FORTH this is not so easy.  In fact we have to search through the dictionary
+       because we don't have a convenient back-pointer (as is often the case in other versions
+       of FORTH).
+
+       This word returns 0 if it doesn't find a match.
+)
+: CFA>
+       LATEST @        ( start at LATEST dictionary entry )
+       BEGIN
+               ?DUP            ( while link pointer is not null )
+       WHILE
+               DUP >CFA        ( cfa curr curr-cfa )
+               2 PICK          ( cfa curr curr-cfa cfa )
+               = IF            ( found a match? )
+                       NIP             ( leave curr dictionary entry on the stack )
+                       EXIT            ( and return from the function )
+               THEN
+               @               ( follow link pointer back )
+       REPEAT
+       DROP            ( restore stack )
+       0               ( sorry, nothing found )
+;
+
+(
+       SEE decompiles a FORTH word.
+
+       We search for the dictionary entry of the word, then search again for the next
+       word (effectively, the end of the compiled word).  This results in two pointers:
+
+       +---------+---+---+---+---+------------+------------+------------+------------+
+       | LINK    | 3 | T | E | N | DOCOL      | LIT        | 10         | EXIT       |
+       +---------+---+---+---+---+------------+------------+------------+------------+
+        ^                                                                             ^
+        |                                                                             |
+       Start of word                                                         End of word
+
+       With this information we can have a go at decompiling the word.  We need to
+       recognise "meta-words" like LIT, LITSTRING, BRANCH, etc. and treat those separately.
+)
+: SEE
+       WORD FIND       ( find the dictionary entry to decompile )
+
+       ( Now we search again, looking for the next word in the dictionary.  This gives us
+         the length of the word that we will be decompiling.  (Well, mostly it does). )
+       HERE @          ( address of the end of the last compiled word )
+       LATEST @        ( word last curr )
+       BEGIN
+               2 PICK          ( word last curr word )
+               OVER            ( word last curr word curr )
+               <>              ( word last curr word<>curr? )
+       WHILE                   ( word last curr )
+               NIP             ( word curr )
+               DUP @           ( word curr prev (which becomes: word last curr) )
+       REPEAT
+
+       DROP            ( at this point, the stack is: start-of-word end-of-word )
+       SWAP            ( end-of-word start-of-word )
+
+       ( begin the definition with : NAME [IMMEDIATE] )
+       ':' EMIT SPACE DUP ID. SPACE
+       DUP ?IMMEDIATE IF ." IMMEDIATE " THEN
+
+       >DFA            ( get the data address, ie. points after DOCOL | end-of-word start-of-data )
+
+       ( now we start decompiling until we hit the end of the word )
+       BEGIN           ( end start )
+               2DUP >
+       WHILE
+               DUP @           ( end start codeword )
+
+               CASE
+               ' LIT OF                ( is it LIT ? )
+                       4 + DUP @               ( get next word which is the integer constant )
+                       .                       ( and print it )
+               ENDOF
+               ' LITSTRING OF          ( is it LITSTRING ? )
+                       [ CHAR S ] LITERAL EMIT '"' EMIT SPACE ( print S"<space> )
+                       4 + DUP @               ( get the length word )
+                       SWAP 4 + SWAP           ( end start+4 length )
+                       2DUP TELL               ( print the string )
+                       '"' EMIT SPACE          ( finish the string with a final quote )
+                       + ALIGNED               ( end start+4+len, aligned )
+                       4 -                     ( because we're about to add 4 below )
+               ENDOF
+               ' 0BRANCH OF            ( is it 0BRANCH ? )
+                       ." 0BRANCH ( "
+                       4 + DUP @               ( print the offset )
+                       .
+                       ." ) "
+               ENDOF
+               ' BRANCH OF             ( is it BRANCH ? )
+                       ." BRANCH ( "
+                       4 + DUP @               ( print the offset )
+                       .
+                       ." ) "
+               ENDOF
+               ' ' OF                  ( is it ' (TICK) ? )
+                       [ CHAR ' ] LITERAL EMIT SPACE
+                       4 + DUP @               ( get the next codeword )
+                       CFA>                    ( and force it to be printed as a dictionary entry )
+                       ID. SPACE
+               ENDOF
+               ' EXIT OF               ( is it EXIT? )
+                       ( We expect the last word to be EXIT, and if it is then we don't print it
+                         because EXIT is normally implied by ;.  EXIT can also appear in the middle
+                         of words, and then it needs to be printed. )
+                       2DUP                    ( end start end start )
+                       4 +                     ( end start end start+4 )
+                       <> IF                   ( end start | we're not at the end )
+                               ." EXIT "
+                       THEN
+               ENDOF
+                                       ( default case: )
+                       DUP                     ( in the default case we always need to DUP before using )
+                       CFA>                    ( look up the codeword to get the dictionary entry )
+                       ID. SPACE               ( and print it )
+               ENDCASE
+
+               4 +             ( end start+4 )
+       REPEAT
+
+       ';' EMIT CR
+
+       2DROP           ( restore stack )
+;
+
+(
+       C STRINGS ----------------------------------------------------------------------
+
+       FORTH strings are represented by a start address and length kept on the stack or in memory.
+
+       Most FORTHs don't handle C strings, but we need them in order to access the process arguments
+       and environment left on the stack by the Linux kernel.
+
+       The main function we need is STRLEN which works out the length of a C string.  DUP STRLEN is
+       a common idiom which 'converts' a C string into a FORTH string.  (For example, DUP STRLEN TELL
+       prints a C string).
+)
+
+(
+       Z" .." is like S" ..." except that the string is terminated by an ASCII NUL character.
+
+       To make it more like a C string, at runtime Z" just leaves the address of the string
+       on the stack (not address & length as with S").  To implement this we need to add the
+       extra NUL to the string and also a DROP instruction afterwards.  Apart from that the
+       implementation just a modified S".
+)
+: Z" IMMEDIATE
+       STATE @ IF      ( compiling? )
+               ' LITSTRING ,   ( compile LITSTRING )
+               HERE @          ( save the address of the length word on the stack )
+               0 ,             ( dummy length - we don't know what it is yet )
+               BEGIN
+                       KEY             ( get next character of the string )
+                       DUP '"' <>
+               WHILE
+                       HERE @ C!       ( store the character in the compiled image )
+                       1 HERE +!       ( increment HERE pointer by 1 byte )
+               REPEAT
+               0 HERE @ C!     ( add the ASCII NUL byte )
+               1 HERE +!
+               DROP            ( drop the double quote character at the end )
+               DUP             ( get the saved address of the length word )
+               HERE @ SWAP -   ( calculate the length )
+               4-              ( subtract 4 (because we measured from the start of the length word) )
+               SWAP !          ( and back-fill the length location )
+               ALIGN           ( round up to next multiple of 4 bytes for the remaining code )
+               ' DROP ,        ( compile DROP (to drop the length) )
+       ELSE            ( immediate mode )
+               HERE @          ( get the start address of the temporary space )
+               BEGIN
+                       KEY
+                       DUP '"' <>
+               WHILE
+                       OVER C!         ( save next character )
+                       1+              ( increment address )
+               REPEAT
+               DROP            ( drop the final " character )
+               0 SWAP C!       ( store final ASCII NUL )
+               HERE @          ( push the start address )
+       THEN
+;
+
+( STRLEN returns the length of a C string )
+: STRLEN       ( str -- len )
+       DUP             ( save start address )
+       BEGIN
+               DUP C@ 0<>      ( zero byte found? )
+       WHILE
+               1+
+       REPEAT
+
+       SWAP -          ( calculate the length )
+;
+
+(
+       STRNCMP compares two strings up to a length.  As with C's strncmp it returns 0 if they
+       are equal, or a number > 0 or < 0 indicating their order.
+)
+: STRNCMP      ( str1 str2 len -- eq? )
+       BEGIN
+               ?DUP
+       WHILE
+               ROT             ( len str1 str2 )
+               DUP C@          ( len str1 str2 char2 )
+               2 PICK C@       ( len str1 str2 char2 char1 )
+               OVER            ( len str1 str2 char2 char1 char2 )
+               -               ( len str1 str2 char2 char1-char2 )
+
+               ?DUP IF         ( strings not the same at this position? )
+                       NIP             ( len str1 str2 diff )
+                       ROT             ( len diff str1 str2 )
+                       DROP DROP       ( len diff )
+                       NIP             ( diff )
+                       EXIT
+               THEN
+
+               0= IF           ( characters are equal, but is this the end of the C string? )
+                       DROP DROP DROP
+                       0
+                       EXIT
+               THEN
+
+               1+              ( len str1 str2+1 )
+               ROT             ( str2+1 len str1 )
+               1+ ROT          ( str1+1 str2+1 len )
+               1-              ( str1+1 str2+1 len-1 )
+       REPEAT
+
+       2DROP           ( restore stack )
+       0               ( equal )
+;
+
+(
+       THE ENVIRONMENT ----------------------------------------------------------------------
+
+       Linux makes the process arguments and environment available to us on the stack.
+
+       The top of stack pointer is saved by the early assembler code when we start up in the FORTH
+       variable S0, and starting at this pointer we can read out the command line arguments and the
+       environment.
+
+       Starting at S0, S0 itself points to argc (the number of command line arguments).
+
+       S0+4 points to argv[0], S0+8 points to argv[1] etc up to argv[argc-1].
+
+       argv[argc] is a NULL pointer.
+
+       After that the stack contains environment variables, a set of pointers to strings of the
+       form NAME=VALUE and on until we get to another NULL pointer.
+
+       The first word that we define, ARGC, pushes the number of command line arguments (note that
+       as with C argc, this includes the name of the command).
+)
+: ARGC
+       S0 @ @
+;
+
+(
+       n ARGV gets the nth command line argument.
+
+       For example to print the command name you would do:
+               0 ARGV TELL CR
+)
+: ARGV ( n -- str u )
+       1+ CELLS S0 @ + ( get the address of argv[n] entry )
+       @               ( get the address of the string )
+       DUP STRLEN      ( and get its length / turn it into a FORTH string )
+;
+
+(
+       ENVIRON returns the address of the first environment string.  The list of strings ends
+       with a NULL pointer.
+
+       For example to print the first string in the environment you could do:
+               ENVIRON @ DUP STRLEN TELL
+)
+: ENVIRON      ( -- addr )
+       ARGC            ( number of command line parameters on the stack to skip )
+       2 +             ( skip command line count and NULL pointer after the command line args )
+       CELLS           ( convert to an offset )
+       S0 @ +          ( add to base stack address )
+;
+
+(
+       SYSTEM CALLS ----------------------------------------------------------------------
+
+       Some wrappers around Linux system calls
+)
+
+( BYE exits by calling the Linux exit(2) syscall. )
+: BYE          ( -- )
+       0
+       0
+       0               ( return code (0) )
+       SYS_EXIT        ( system call number )
+       SYSCALL3
+;
+
+(
+       OPEN, CREAT and CLOSE are just like the Linux syscalls open(2), creat(2) and close(2).
+
+       Notice that they take C strings and may return error codes (-errno).
+)
+: OPEN         ( mode flags c-pathname -- ret )
+       SYS_OPEN
+       SYSCALL3
+;
+
+: CREAT                ( mode c-pathname -- ret )
+       0 ROT
+       SYS_CREAT
+       SYSCALL3
+;
+
+: CLOSE                ( fd -- ret )
+       0 ROT 0 ROT
+       SYS_CLOSE
+       SYSCALL3
+;
+
+( READ and WRITE system calls. )
+: READ         ( len buffer fd -- ret )
+       SYS_READ
+       SYSCALL3
+;      
+
+: WRITE                ( len buffer fd -- ret )
+       SYS_WRITE
+       SYSCALL3
+;      
+
+(
+       ANS FORTH ----------------------------------------------------------------------
+
+       From this point we're trying to fill in the missing parts of the ISO standard, commonly
+       referred to as ANS FORTH.
+
+       http://www.taygeta.com/forth/dpans.html
+       http://www.taygeta.com/forth/dpansf.htm (list of words)
+)
+
+( C, writes a byte at the HERE pointer. )
+: C, HERE @ C! 1 HERE +! ;
+
+
+
+
+
+
+
+
+
+(
+       NOTES ----------------------------------------------------------------------
+
+       DOES> isn't possible to implement with this FORTH because we don't have a separate
+       data pointer.
+)
+
+(
+       WELCOME MESSAGE ----------------------------------------------------------------------
+
+       Print the version and OK prompt.
+)
+
 ." JONESFORTH VERSION " VERSION . CR
 ." OK "
 ." JONESFORTH VERSION " VERSION . CR
 ." OK "