b05b64cba2d0b2012b8f07aca2f5011285e8e8ed
[jonesforth.git] / jonesforth.f
1 \ -*- text -*-
2 \       A sometimes minimal FORTH compiler and tutorial for Linux / i386 systems. -*- asm -*-
3 \       By Richard W.M. Jones <rich@annexia.org> http://annexia.org/forth
4 \       This is PUBLIC DOMAIN (see public domain release statement below).
5 \       $Id: jonesforth.f,v 1.5 2007-09-26 22:20:52 rich Exp $
6 \
7 \       The first part of this tutorial is in jonesforth.S.  Get if from http://annexia.org/forth
8 \
9 \       PUBLIC DOMAIN ----------------------------------------------------------------------
10 \
11 \       I, the copyright holder of this work, hereby release it into the public domain. This applies worldwide.
12 \
13 \       In case this is not legally possible, I grant any entity the right to use this work for any purpose,
14 \       without any conditions, unless such conditions are required by law.
15 \
16 \       SETTING UP ----------------------------------------------------------------------
17 \
18 \       Let's get a few housekeeping things out of the way.  Firstly because I need to draw lots of
19 \       ASCII-art diagrams to explain concepts, the best way to look at this is using a window which
20 \       uses a fixed width font and is at least this wide:
21 \
22 \<------------------------------------------------------------------------------------------------------------------------>
23 \
24 \       Secondly make sure TABS are set to 8 characters.  The following should be a vertical
25 \       line.  If not, sort out your tabs.
26 \
27 \       |
28 \       |
29 \       |
30 \
31 \       Thirdly I assume that your screen is at least 50 characters high.
32 \
33 \       START OF FORTH CODE ----------------------------------------------------------------------
34 \
35 \       We've now reached the stage where the FORTH system is running and self-hosting.  All further
36 \       words can be written as FORTH itself, including words like IF, THEN, .", etc which in most
37 \       languages would be considered rather fundamental.
38 \
39 \       Some notes about the code:
40 \
41 \       I use indenting to show structure.  The amount of whitespace has no meaning to FORTH however
42 \       except that you must use at least one whitespace character between words, and words themselves
43 \       cannot contain whitespace.
44 \
45 \       FORTH is case-sensitive.  Use capslock!
46
47 \ The primitive word /MOD (DIVMOD) leaves both the quotient and the remainder on the stack.  (On
48 \ i386, the idivl instruction gives both anyway).  Now we can define the / and MOD in terms of /MOD
49 \ and a few other primitives.
50 : / /MOD SWAP DROP ;
51 : MOD /MOD DROP ;
52
53 \ Define some character constants
54 : '\n'   10 ;
55 : 'SPACE' 32 ;
56
57 \ CR prints a carriage return
58 : CR '\n' EMIT ;
59
60 \ SPACE prints a space
61 : SPACE 'SPACE' EMIT ;
62
63 \ DUP, DROP are defined in assembly for speed, but this is how you might define them
64 \ in FORTH.  Notice use of the scratch variables _X and _Y.
65 \ : DUP _X ! _X @ _X @ ;
66 \ : DROP _X ! ;
67
68 \ The 2... versions of the standard operators work on pairs of stack entries.  They're not used
69 \ very commonly so not really worth writing in assembler.  Here is how they are defined in FORTH.
70 : 2DUP OVER OVER ;
71 : 2DROP DROP DROP ;
72
73 \ More standard FORTH words.
74 : 2* 2 * ;
75 : 2/ 2 / ;
76
77 \ NEGATE leaves the negative of a number on the stack.
78 : NEGATE 0 SWAP - ;
79
80 \ Standard words for booleans.
81 : TRUE  1 ;
82 : FALSE 0 ;
83 : NOT   0= ;
84
85 \ LITERAL takes whatever is on the stack and compiles LIT <foo>
86 : LITERAL IMMEDIATE
87         ' LIT ,         \ compile LIT
88         ,               \ compile the literal itself (from the stack)
89         ;
90
91 \ Now we can use [ and ] to insert literals which are calculated at compile time.  (Recall that
92 \ [ and ] are the FORTH words which switch into and out of immediate mode.)
93 \ Within definitions, use [ ... ] LITERAL anywhere that '...' is a constant expression which you
94 \ would rather only compute once (at compile time, rather than calculating it each time your word runs).
95 : ':'
96         [               \ go into immediate mode (temporarily)
97         CHAR :          \ push the number 58 (ASCII code of colon) on the parameter stack
98         ]               \ go back to compile mode
99         LITERAL         \ compile LIT 58 as the definition of ':' word
100 ;
101
102 \ A few more character constants defined the same way as above.
103 : '(' [ CHAR ( ] LITERAL ;
104 : ')' [ CHAR ) ] LITERAL ;
105 : '"' [ CHAR " ] LITERAL ;
106 : 'A' [ CHAR A ] LITERAL ;
107 : '0' [ CHAR 0 ] LITERAL ;
108 : '-' [ CHAR - ] LITERAL ;
109 : '.' [ CHAR . ] LITERAL ;
110
111 \ While compiling, '[COMPILE] word' compiles 'word' if it would otherwise be IMMEDIATE.
112 : [COMPILE] IMMEDIATE
113         WORD            \ get the next word
114         FIND            \ find it in the dictionary
115         >CFA            \ get its codeword
116         ,               \ and compile that
117 ;
118
119 \ RECURSE makes a recursive call to the current word that is being compiled.
120 \
121 \ Normally while a word is being compiled, it is marked HIDDEN so that references to the
122 \ same word within are calls to the previous definition of the word.  However we still have
123 \ access to the word which we are currently compiling through the LATEST pointer so we
124 \ can use that to compile a recursive call.
125 : RECURSE IMMEDIATE
126         LATEST @        \ LATEST points to the word being compiled at the moment
127         >CFA            \ get the codeword
128         ,               \ compile it
129 ;
130
131 \ So far we have defined only very simple definitions.  Before we can go further, we really need to
132 \ make some control structures, like IF ... THEN and loops.  Luckily we can define arbitrary control
133 \ structures directly in FORTH.
134 \
135 \ Please note that the control structures as I have defined them here will only work inside compiled
136 \ words.  If you try to type in expressions using IF, etc. in immediate mode, then they won't work.
137 \ Making these work in immediate mode is left as an exercise for the reader.
138
139 \ condition IF true-part THEN rest
140 \       -- compiles to: --> condition 0BRANCH OFFSET true-part rest
141 \       where OFFSET is the offset of 'rest'
142 \ condition IF true-part ELSE false-part THEN
143 \       -- compiles to: --> condition 0BRANCH OFFSET true-part BRANCH OFFSET2 false-part rest
144 \       where OFFSET if the offset of false-part and OFFSET2 is the offset of rest
145
146 \ IF is an IMMEDIATE word which compiles 0BRANCH followed by a dummy offset, and places
147 \ the address of the 0BRANCH on the stack.  Later when we see THEN, we pop that address
148 \ off the stack, calculate the offset, and back-fill the offset.
149 : IF IMMEDIATE
150         ' 0BRANCH ,     \ compile 0BRANCH
151         HERE @          \ save location of the offset on the stack
152         0 ,             \ compile a dummy offset
153 ;
154
155 : THEN IMMEDIATE
156         DUP
157         HERE @ SWAP -   \ calculate the offset from the address saved on the stack
158         SWAP !          \ store the offset in the back-filled location
159 ;
160
161 : ELSE IMMEDIATE
162         ' BRANCH ,      \ definite branch to just over the false-part
163         HERE @          \ save location of the offset on the stack
164         0 ,             \ compile a dummy offset
165         SWAP            \ now back-fill the original (IF) offset
166         DUP             \ same as for THEN word above
167         HERE @ SWAP -
168         SWAP !
169 ;
170
171 \ BEGIN loop-part condition UNTIL
172 \       -- compiles to: --> loop-part condition 0BRANCH OFFSET
173 \       where OFFSET points back to the loop-part
174 \ This is like do { loop-part } while (condition) in the C language
175 : BEGIN IMMEDIATE
176         HERE @          \ save location on the stack
177 ;
178
179 : UNTIL IMMEDIATE
180         ' 0BRANCH ,     \ compile 0BRANCH
181         HERE @ -        \ calculate the offset from the address saved on the stack
182         ,               \ compile the offset here
183 ;
184
185 \ BEGIN loop-part AGAIN
186 \       -- compiles to: --> loop-part BRANCH OFFSET
187 \       where OFFSET points back to the loop-part
188 \ In other words, an infinite loop which can only be returned from with EXIT
189 : AGAIN IMMEDIATE
190         ' BRANCH ,      \ compile BRANCH
191         HERE @ -        \ calculate the offset back
192         ,               \ compile the offset here
193 ;
194
195 \ BEGIN condition WHILE loop-part REPEAT
196 \       -- compiles to: --> condition 0BRANCH OFFSET2 loop-part BRANCH OFFSET
197 \       where OFFSET points back to condition (the beginning) and OFFSET2 points to after the whole piece of code
198 \ So this is like a while (condition) { loop-part } loop in the C language
199 : WHILE IMMEDIATE
200         ' 0BRANCH ,     \ compile 0BRANCH
201         HERE @          \ save location of the offset2 on the stack
202         0 ,             \ compile a dummy offset2
203 ;
204
205 : REPEAT IMMEDIATE
206         ' BRANCH ,      \ compile BRANCH
207         SWAP            \ get the original offset (from BEGIN)
208         HERE @ - ,      \ and compile it after BRANCH
209         DUP
210         HERE @ SWAP -   \ calculate the offset2
211         SWAP !          \ and back-fill it in the original location
212 ;
213
214 \ FORTH allows ( ... ) as comments within function definitions.  This works by having an IMMEDIATE
215 \ word called ( which just drops input characters until it hits the corresponding ).
216 : ( IMMEDIATE
217         1               \ allowed nested parens by keeping track of depth
218         BEGIN
219                 KEY             \ read next character
220                 DUP '(' = IF    \ open paren?
221                         DROP            \ drop the open paren
222                         1+              \ depth increases
223                 ELSE
224                         ')' = IF        \ close paren?
225                                 1-              \ depth decreases
226                         THEN
227                 THEN
228         DUP 0= UNTIL            \ continue until we reach matching close paren, depth 0
229         DROP            \ drop the depth counter
230 ;
231
232 (
233         From now on we can use ( ... ) for comments.
234
235         In FORTH style we can also use ( ... -- ... ) to show the effects that a word has on the
236         parameter stack.  For example:
237
238         ( n -- )        means that the word consumes an integer (n) from the parameter stack.
239         ( b a -- c )    means that the word uses two integers (a and b, where a is at the top of stack)
240                                 and returns a single integer (c).
241         ( -- )          means the word has no effect on the stack
242 )
243
244 ( With the looping constructs, we can now write SPACES, which writes n spaces to stdout. )
245 : SPACES        ( n -- )
246         BEGIN
247                 DUP 0>          ( while n > 0 )
248         WHILE
249                 SPACE           ( print a space )
250                 1-              ( until we count down to 0 )
251         REPEAT
252         DROP
253 ;
254
255 ( Standard words for manipulating BASE. )
256 : DECIMAL ( -- ) 10 BASE ! ;
257 : HEX ( -- ) 16 BASE ! ;
258
259 (
260         The standard FORTH word . (DOT) is very important.  It takes the number at the top
261         of the stack and prints it out.  However first I'm going to implement some lower-level
262         FORTH words:
263
264         U.R     ( u width -- )  which prints an unsigned number, padded to a certain width
265         U.      ( u -- )        which prints an unsigned number
266         .R      ( n width -- )  which prints a signed number, padded to a certain width.
267
268         For example:
269                 -123 6 .R
270         will print out these characters:
271                 <space> <space> - 1 2 3
272
273         In other words, the number padded left to a certain number of characters.
274
275         The full number is printed even if it is wider than width, and this is what allows us to
276         define the ordinary functions U. and . (we just set width to zero knowing that the full
277         number will be printed anyway).
278
279         Another wrinkle of . and friends is that they obey the current base in the variable BASE.
280         BASE can be anything in the range 2 to 36.
281
282         While we're defining . &c we can also define .S which is a useful debugging tool.  This
283         word prints the current stack (non-destructively) from top to bottom.
284 )
285
286 ( This is the underlying recursive definition of U. )
287 : U.            ( u -- )
288         BASE @ /MOD     ( width rem quot )
289         DUP 0<> IF      ( if quotient <> 0 then )
290                 RECURSE         ( print the quotient )
291         ELSE
292                 DROP            ( drop the zero quotient )
293         THEN
294
295         ( print the remainder )
296         DUP 10 < IF
297                 '0'             ( decimal digits 0..9 )
298         ELSE
299                 10 -            ( hex and beyond digits A..Z )
300                 'A'
301         THEN
302         +
303         EMIT
304 ;
305
306 (
307         FORTH word .S prints the contents of the stack.  It doesn't alter the stack.
308         Very useful for debugging.
309 )
310 : .S            ( -- )
311         DSP@            ( get current stack pointer )
312         BEGIN
313                 DUP S0 @ <
314         WHILE
315                 DUP @ U.        ( print the stack element )
316                 SPACE
317                 4+              ( move up )
318         REPEAT
319         DROP
320 ;
321
322 ( This word returns the width (in characters) of an unsigned number in the current base )
323 : UWIDTH        ( u -- width )
324         BASE @ /        ( rem quot )
325         DUP 0<> IF      ( if quotient <> 0 then )
326                 RECURSE 1+      ( return 1+recursive call )
327         ELSE
328                 DROP            ( drop the zero quotient )
329                 1               ( return 1 )
330         THEN
331 ;
332
333 : U.R           ( u width -- )
334         SWAP            ( width u )
335         DUP             ( width u u )
336         UWIDTH          ( width u uwidth )
337         -ROT            ( u uwidth width )
338         SWAP -          ( u width-uwidth )
339         ( At this point if the requested width is narrower, we'll have a negative number on the stack.
340           Otherwise the number on the stack is the number of spaces to print.  But SPACES won't print
341           a negative number of spaces anyway, so it's now safe to call SPACES ... )
342         SPACES
343         ( ... and then call the underlying implementation of U. )
344         U.
345 ;
346
347 (
348         .R prints a signed number, padded to a certain width.  We can't just print the sign
349         and call U.R because we want the sign to be next to the number ('-123' instead of '-  123').
350 )
351 : .R            ( n width -- )
352         SWAP            ( width n )
353         DUP 0< IF
354                 NEGATE          ( width u )
355                 1               ( save a flag to remember that it was negative | width n 1 )
356                 ROT             ( 1 width u )
357                 SWAP            ( 1 u width )
358                 1-              ( 1 u width-1 )
359         ELSE
360                 0               ( width u 0 )
361                 ROT             ( 0 width u )
362                 SWAP            ( 0 u width )
363         THEN
364         SWAP            ( flag width u )
365         DUP             ( flag width u u )
366         UWIDTH          ( flag width u uwidth )
367         -ROT            ( flag u uwidth width )
368         SWAP -          ( flag u width-uwidth )
369
370         SPACES          ( flag u )
371         SWAP            ( u flag )
372
373         IF                      ( was it negative? print the - character )
374                 '-' EMIT
375         THEN
376
377         U.
378 ;
379
380 ( Finally we can define word . in terms of .R, with a trailing space. )
381 : . 0 .R SPACE ;
382
383 ( The real U., note the trailing space. )
384 : U. U. SPACE ;
385
386 ( ? fetches the integer at an address and prints it. )
387 : ? @ . ;
388
389 ( c a b WITHIN returns true if a <= c and c < b )
390 : WITHIN
391         ROT             ( b c a )
392         OVER            ( b c a c )
393         <= IF
394                 > IF            ( b c -- )
395                         TRUE
396                 ELSE
397                         FALSE
398                 THEN
399         ELSE
400                 2DROP           ( b c -- )
401                 FALSE
402         THEN
403 ;
404
405 ( DEPTH returns the depth of the stack. )
406 : DEPTH         ( -- n )
407         S0 @ DSP@ -
408         4-                      ( adjust because S0 was on the stack when we pushed DSP )
409 ;
410
411 (
412         ALIGNED takes an address and rounds it up (aligns it) to the next 4 byte boundary.
413 )
414 : ALIGNED       ( addr -- addr )
415         3 + 3 INVERT AND        ( (addr+3) & ~3 )
416 ;
417
418 (
419         ALIGN aligns the HERE pointer, so the next word appended will be aligned properly.
420 )
421 : ALIGN HERE @ ALIGNED HERE ! ;
422
423 (
424         S" string" is used in FORTH to define strings.  It leaves the address of the string and
425         its length on the stack, with the address at the top.  The space following S" is the normal
426         space between FORTH words and is not a part of the string.
427
428         This is tricky to define because it has to do different things depending on whether
429         we are compiling or in immediate mode.  (Thus the word is marked IMMEDIATE so it can
430         detect this and do different things).
431
432         In compile mode we append
433                 LITSTRING <string length> <string rounded up 4 bytes>
434         to the current word.  The primitive LITSTRING does the right thing when the current
435         word is executed.
436
437         In immediate mode there isn't a particularly good place to put the string, but in this
438         case we put the string at HERE (but we _don't_ change HERE).  This is meant as a temporary
439         location, likely to be overwritten soon after.
440 )
441 : S" IMMEDIATE          ( -- len addr )
442         STATE @ IF      ( compiling? )
443                 ' LITSTRING ,   ( compile LITSTRING )
444                 HERE @          ( save the address of the length word on the stack )
445                 0 ,             ( dummy length - we don't know what it is yet )
446                 BEGIN
447                         KEY             ( get next character of the string )
448                         DUP '"' <>
449                 WHILE
450                         HERE @ C!       ( store the character in the compiled image )
451                         1 HERE +!       ( increment HERE pointer by 1 byte )
452                 REPEAT
453                 DROP            ( drop the double quote character at the end )
454                 DUP             ( get the saved address of the length word )
455                 HERE @ SWAP -   ( calculate the length )
456                 4-              ( subtract 4 (because we measured from the start of the length word) )
457                 SWAP !          ( and back-fill the length location )
458                 ALIGN           ( round up to next multiple of 4 bytes for the remaining code )
459         ELSE            ( immediate mode )
460                 HERE @          ( get the start address of the temporary space )
461                 BEGIN
462                         KEY
463                         DUP '"' <>
464                 WHILE
465                         OVER C!         ( save next character )
466                         1+              ( increment address )
467                 REPEAT
468                 DROP            ( drop the final " character )
469                 HERE @ -        ( calculate the length )
470                 HERE @          ( push the start address )
471         THEN
472 ;
473
474 (
475         ." is the print string operator in FORTH.  Example: ." Something to print"
476         The space after the operator is the ordinary space required between words and is not
477         a part of what is printed.
478
479         In immediate mode we just keep reading characters and printing them until we get to
480         the next double quote.
481
482         In compile mode we use S" to store the string, then add EMITSTRING afterwards:
483                 LITSTRING <string length> <string rounded up to 4 bytes> EMITSTRING
484
485         It may be interesting to note the use of [COMPILE] to turn the call to the immediate
486         word S" into compilation of that word.  It compiles it into the definition of .",
487         not into the definition of the word being compiled when this is running (complicated
488         enough for you?)
489 )
490 : ." IMMEDIATE          ( -- )
491         STATE @ IF      ( compiling? )
492                 [COMPILE] S"    ( read the string, and compile LITSTRING, etc. )
493                 ' EMITSTRING ,  ( compile the final EMITSTRING )
494         ELSE
495                 ( In immediate mode, just read characters and print them until we get
496                   to the ending double quote. )
497                 BEGIN
498                         KEY
499                         DUP '"' = IF
500                                 DROP    ( drop the double quote character )
501                                 EXIT    ( return from this function )
502                         THEN
503                         EMIT
504                 AGAIN
505         THEN
506 ;
507
508 (
509         In FORTH, global constants and variables are defined like this:
510
511         10 CONSTANT TEN         when TEN is executed, it leaves the integer 10 on the stack
512         VARIABLE VAR            when VAR is executed, it leaves the address of VAR on the stack
513
514         Constants can be read but not written, eg:
515
516         TEN . CR                prints 10
517
518         You can read a variable (in this example called VAR) by doing:
519
520         VAR @                   leaves the value of VAR on the stack
521         VAR @ . CR              prints the value of VAR
522         VAR ? CR                same as above, since ? is the same as @ .
523
524         and update the variable by doing:
525
526         20 VAR !                sets VAR to 20
527
528         Note that variables are uninitialised (but see VALUE later on which provides initialised
529         variables with a slightly simpler syntax).
530
531         How can we define the words CONSTANT and VARIABLE?
532
533         The trick is to define a new word for the variable itself (eg. if the variable was called
534         'VAR' then we would define a new word called VAR).  This is easy to do because we exposed
535         dictionary entry creation through the CREATE word (part of the definition of : above).
536         A call to CREATE TEN leaves the dictionary entry:
537
538                                    +--- HERE
539                                    |
540                                    V
541         +---------+---+---+---+---+
542         | LINK    | 3 | T | E | N |
543         +---------+---+---+---+---+
544                    len
545
546         For CONSTANT we can continue by appending DOCOL (the codeword), then LIT followed by
547         the constant itself and then EXIT, forming a little word definition that returns the
548         constant:
549
550         +---------+---+---+---+---+------------+------------+------------+------------+
551         | LINK    | 3 | T | E | N | DOCOL      | LIT        | 10         | EXIT       |
552         +---------+---+---+---+---+------------+------------+------------+------------+
553                    len              codeword
554
555         Notice that this word definition is exactly the same as you would have got if you had
556         written : TEN 10 ;
557
558         Note for people reading the code below: DOCOL is a constant word which we defined in the
559         assembler part which returns the value of the assembler symbol of the same name.
560 )
561 : CONSTANT
562         CREATE          ( make the dictionary entry (the name follows CONSTANT) )
563         DOCOL ,         ( append DOCOL (the codeword field of this word) )
564         ' LIT ,         ( append the codeword LIT )
565         ,               ( append the value on the top of the stack )
566         ' EXIT ,        ( append the codeword EXIT )
567 ;
568
569 (
570         VARIABLE is a little bit harder because we need somewhere to put the variable.  There is
571         nothing particularly special about the 'user definitions area' (the area of memory pointed
572         to by HERE where we have previously just stored new word definitions).  We can slice off
573         bits of this memory area to store anything we want, so one possible definition of
574         VARIABLE might create this:
575
576            +--------------------------------------------------------------+
577            |                                                              |
578            V                                                              |
579         +---------+---------+---+---+---+---+------------+------------+---|--------+------------+
580         | <var>   | LINK    | 3 | V | A | R | DOCOL      | LIT        | <addr var> | EXIT       |
581         +---------+---------+---+---+---+---+------------+------------+------------+------------+
582                              len              codeword
583
584         where <var> is the place to store the variable, and <addr var> points back to it.
585
586         To make this more general let's define a couple of words which we can use to allocate
587         arbitrary memory from the user definitions area.
588
589         First ALLOT, where n ALLOT allocates n bytes of memory.  (Note when calling this that
590         it's a very good idea to make sure that n is a multiple of 4, or at least that next time
591         a word is compiled that HERE has been left as a multiple of 4).
592 )
593 : ALLOT         ( n -- addr )
594         HERE @ SWAP     ( here n )
595         HERE +!         ( adds n to HERE, after this the old value of HERE is still on the stack )
596 ;
597
598 (
599         Second, CELLS.  In FORTH the phrase 'n CELLS ALLOT' means allocate n integers of whatever size
600         is the natural size for integers on this machine architecture.  On this 32 bit machine therefore
601         CELLS just multiplies the top of stack by 4.
602 )
603 : CELLS ( n -- n ) 4 * ;
604
605 (
606         So now we can define VARIABLE easily in much the same way as CONSTANT above.  Refer to the
607         diagram above to see what the word that this creates will look like.
608 )
609 : VARIABLE
610         1 CELLS ALLOT   ( allocate 1 cell of memory, push the pointer to this memory )
611         CREATE          ( make the dictionary entry (the name follows VARIABLE) )
612         DOCOL ,         ( append DOCOL (the codeword field of this word) )
613         ' LIT ,         ( append the codeword LIT )
614         ,               ( append the pointer to the new memory )
615         ' EXIT ,        ( append the codeword EXIT )
616 ;
617
618 (
619         VALUEs are like VARIABLEs but with a simpler syntax.  You would generally use them when you
620         want a variable which is read often, and written infrequently.
621
622         20 VALUE VAL    creates VAL with initial value 20
623         VAL             pushes the value directly on the stack
624         30 TO VAL       updates VAL, setting it to 30
625
626         Notice that 'VAL' on its own doesn't return the address of the value, but the value itself,
627         making values simpler and more obvious to use than variables (no indirection through '@').
628         The price is a more complicated implementation, although despite the complexity there is no
629         performance penalty at runtime.
630
631         A naive implementation of 'TO' would be quite slow, involving a dictionary search each time.
632         But because this is FORTH we have complete control of the compiler so we can compile TO more
633         efficiently, turning:
634                 TO VAL
635         into:
636                 LIT <addr> !
637         and calculating <addr> (the address of the value) at compile time.
638
639         Now this is the clever bit.  We'll compile our value like this:
640
641         +---------+---+---+---+---+------------+------------+------------+------------+
642         | LINK    | 3 | V | A | L | DOCOL      | LIT        | <value>    | EXIT       |
643         +---------+---+---+---+---+------------+------------+------------+------------+
644                    len              codeword
645
646         where <value> is the actual value itself.  Note that when VAL executes, it will push the
647         value on the stack, which is what we want.
648
649         But what will TO use for the address <addr>?  Why of course a pointer to that <value>:
650
651                 code compiled   - - - - --+------------+------------+------------+-- - - - -
652                 by TO VAL                 | LIT        | <addr>     | !          |
653                                 - - - - --+------------+-----|------+------------+-- - - - -
654                                                              |
655                                                              V
656         +---------+---+---+---+---+------------+------------+------------+------------+
657         | LINK    | 3 | V | A | L | DOCOL      | LIT        | <value>    | EXIT       |
658         +---------+---+---+---+---+------------+------------+------------+------------+
659                    len              codeword
660
661         In other words, this is a kind of self-modifying code.
662
663         (Note to the people who want to modify this FORTH to add inlining: values defined this
664         way cannot be inlined).
665 )
666 : VALUE         ( n -- )
667         CREATE          ( make the dictionary entry (the name follows VALUE) )
668         DOCOL ,         ( append DOCOL )
669         ' LIT ,         ( append the codeword LIT )
670         ,               ( append the initial value )
671         ' EXIT ,        ( append the codeword EXIT )
672 ;
673
674 : TO IMMEDIATE  ( n -- )
675         WORD            ( get the name of the value )
676         FIND            ( look it up in the dictionary )
677         >DFA            ( get a pointer to the first data field (the 'LIT') )
678         4+              ( increment to point at the value )
679         STATE @ IF      ( compiling? )
680                 ' LIT ,         ( compile LIT )
681                 ,               ( compile the address of the value )
682                 ' ! ,           ( compile ! )
683         ELSE            ( immediate mode )
684                 !               ( update it straightaway )
685         THEN
686 ;
687
688 ( x +TO VAL adds x to VAL )
689 : +TO IMMEDIATE
690         WORD            ( get the name of the value )
691         FIND            ( look it up in the dictionary )
692         >DFA            ( get a pointer to the first data field (the 'LIT') )
693         4+              ( increment to point at the value )
694         STATE @ IF      ( compiling? )
695                 ' LIT ,         ( compile LIT )
696                 ,               ( compile the address of the value )
697                 ' +! ,          ( compile +! )
698         ELSE            ( immediate mode )
699                 +!              ( update it straightaway )
700         THEN
701 ;
702
703 (
704         ID. takes an address of a dictionary entry and prints the word's name.
705
706         For example: LATEST @ ID. would print the name of the last word that was defined.
707 )
708 : ID.
709         4+              ( skip over the link pointer )
710         DUP C@          ( get the flags/length byte )
711         F_LENMASK AND   ( mask out the flags - just want the length )
712
713         BEGIN
714                 DUP 0>          ( length > 0? )
715         WHILE
716                 SWAP 1+         ( addr len -- len addr+1 )
717                 DUP C@          ( len addr -- len addr char | get the next character)
718                 EMIT            ( len addr char -- len addr | and print it)
719                 SWAP 1-         ( len addr -- addr len-1    | subtract one from length )
720         REPEAT
721         2DROP           ( len addr -- )
722 ;
723
724 (
725         'WORD word FIND ?HIDDEN' returns true if 'word' is flagged as hidden.
726
727         'WORD word FIND ?IMMEDIATE' returns true if 'word' is flagged as immediate.
728 )
729 : ?HIDDEN
730         4+              ( skip over the link pointer )
731         C@              ( get the flags/length byte )
732         F_HIDDEN AND    ( mask the F_HIDDEN flag and return it (as a truth value) )
733 ;
734 : ?IMMEDIATE
735         4+              ( skip over the link pointer )
736         C@              ( get the flags/length byte )
737         F_IMMED AND     ( mask the F_IMMED flag and return it (as a truth value) )
738 ;
739
740 (
741         WORDS prints all the words defined in the dictionary, starting with the word defined most recently.
742         However it doesn't print hidden words.
743
744         The implementation simply iterates backwards from LATEST using the link pointers.
745 )
746 : WORDS
747         LATEST @        ( start at LATEST dictionary entry )
748         BEGIN
749                 DUP 0<>         ( while link pointer is not null )
750         WHILE
751                 DUP ?HIDDEN NOT IF      ( ignore hidden words )
752                         DUP ID.         ( but if not hidden, print the word )
753                 THEN
754                 SPACE
755                 @               ( dereference the link pointer - go to previous word )
756         REPEAT
757         DROP
758         CR
759 ;
760
761 (
762         So far we have only allocated words and memory.  FORTH provides a rather primitive method
763         to deallocate.
764
765         'FORGET word' deletes the definition of 'word' from the dictionary and everything defined
766         after it, including any variables and other memory allocated after.
767
768         The implementation is very simple - we look up the word (which returns the dictionary entry
769         address).  Then we set HERE to point to that address, so in effect all future allocations
770         and definitions will overwrite memory starting at the word.  We also need to set LATEST to
771         point to the previous word.
772
773         Note that you cannot FORGET built-in words (well, you can try but it will probably cause
774         a segfault).
775
776         XXX: Because we wrote VARIABLE to store the variable in memory allocated before the word,
777         in the current implementation VARIABLE FOO FORGET FOO will leak 1 cell of memory.
778 )
779 : FORGET
780         WORD FIND       ( find the word, gets the dictionary entry address )
781         DUP @ LATEST !  ( set LATEST to point to the previous word )
782         HERE !          ( and store HERE with the dictionary address )
783 ;
784
785 (
786         DUMP is used to dump out the contents of memory, in the 'traditional' hexdump format.
787 )
788 : DUMP          ( addr len -- )
789         BASE @ ROT              ( save the current BASE at the bottom of the stack )
790         HEX                     ( and switch the hexadecimal mode )
791
792         BEGIN
793                 DUP 0>          ( while len > 0 )
794         WHILE
795                 OVER 8 .R       ( print the address )
796                 SPACE
797
798                 ( print up to 16 words on this line )
799                 2DUP            ( addr len addr len )
800                 1- 15 AND 1+    ( addr len addr linelen )
801                 BEGIN
802                         DUP 0>          ( while linelen > 0 )
803                 WHILE
804                         SWAP            ( addr len linelen addr )
805                         DUP C@          ( addr len linelen addr byte )
806                         2 .R SPACE      ( print the byte )
807                         1+ SWAP 1-      ( addr len linelen addr -- addr len addr+1 linelen-1 )
808                 REPEAT
809                 2DROP           ( addr len )
810
811                 ( print the ASCII equivalents )
812                 2DUP 1- 15 AND 1+ ( addr len addr linelen )
813                 BEGIN
814                         DUP 0>          ( while linelen > 0)
815                 WHILE
816                         SWAP            ( addr len linelen addr )
817                         DUP C@          ( addr len linelen addr byte )
818                         DUP 32 128 WITHIN IF    ( 32 <= c < 128? )
819                                 EMIT
820                         ELSE
821                                 DROP '.' EMIT
822                         THEN
823                         1+ SWAP 1-      ( addr len linelen addr -- addr len addr+1 linelen-1 )
824                 REPEAT
825                 2DROP           ( addr len )
826                 CR
827
828                 DUP 1- 15 AND 1+ ( addr len linelen )
829                 DUP             ( addr len linelen linelen )
830                 ROT             ( addr linelen len linelen )
831                 -               ( addr linelen len-linelen )
832                 ROT             ( len-linelen addr linelen )
833                 +               ( len-linelen addr+linelen )
834                 SWAP            ( addr-linelen len-linelen )
835         REPEAT
836
837         2DROP                   ( restore stack )
838         BASE !                  ( restore saved BASE )
839 ;
840
841 ( Finally print the welcome prompt. )
842 ." JONESFORTH VERSION " VERSION . CR
843 ." OK "