StrongForth's implementation of the ANS Forth Programming-Tools word set is provided as source code in blocks 80 to 95. A small number of these words is already included in the core and in the library.
Name | Word Set | Location | Comment |
---|---|---|---|
.S | TOOLS | Library | |
? | TOOLS | Block 81 | |
DUMP | TOOLS | Blocks 82 to 88 | 12 overloaded versions. Uses .HEX, .BYTE and .ADDR. |
SEE | TOOLS | Blocks 92 to 98 | Uses several new words. |
WORDS | TOOLS | Blocks 80 to 81 | Extended semantics. Uses .DIAGRAM and . for items of data type DEFINITION. |
;CODE | TOOLS EXT | Block 91 | Uses (;CODE). |
AHEAD | TOOLS EXT | Library | |
ASSEMBLER | TOOLS EXT | None | This word has been moved to the Search-Order word set. |
BYE | TOOLS EXT | Core | |
CODE | TOOLS EXT | Core | |
CS-PICK | TOOLS EXT | None | This word is not available in StrongForth. |
CS-ROLL | TOOLS EXT | None | This word is not available in StrongForth. |
EDITOR | TOOLS EXT | None | This word has been moved to the Search-Order word set. |
FORGET | TOOLS EXT | None | This word is not available in StrongForth. |
STATE | TOOLS EXT | Core | |
[ELSE] | TOOLS EXT | Block 89 | |
[IF] | TOOLS EXT | Block 90 | |
[THEN] | TOOLS EXT | Block 90 |
The complete Programming-Tools word set becomes available after compiling it into the dictionary with
80 94 THRU
Block 95 contains the source code of the floating-point extension for SEE. This block has to be loaded after loading blocks 80 to 94 and the Floating-Point word set.
StrongForth's version of WORDS has an extended semantics with respect to the ANS Forth specification. If the parse area is not empty after WORDS, only words with the specified name are displayed. This is quite useful for finding all overloaded versions of a word:
WORDS . . ( DEFINITION -- ) . ( DATA-TYPE -- ) . ( FLAG -- ) . ( CHARACTER -- ) . ( SIGNED -- ) . ( SINGLE -- ) . ( SIGNED-DOUBLE -- ) . ( DOUBLE -- ) OK
This example reveals another word, that is included in StrongForth's Programming-Tools word set. . for items of data type DEFINITION is actually used by WORDS itself to display a definition including it's stack diagram.
Most of the semantics of WORDS is actually implemented in (WORDS), a word that displays the words of a given word list. As long as the Search-Order word set is not included, WORDS always displays the contents of the FORTH-WORDLIST word list:
: (WORDS) ( WID -- ) LAST PARSE-WORD LOCALS| COUNT ADDR | BEGIN DUP 0= INVERT WHILE COUNT IF DUP ADDR COUNT ROT NAME COMPARE 0= ELSE TRUE THEN IF DUP . CR THEN PREV REPEAT DROP ; : WORDS ( -- ) FORTH-WORDLIST (WORDS) ;
The Search-Order word list contains a redefinition of WORDS that executes (WORDS) for the word list on top of the present search order.
? is actually a very simple word. In many ANS Forth systems, it is simply implemented like this:
: ? ( a-addr -- ) \ ANS Forth @ . ;
But this doesn't work in StrongForth:
: ? ( ADDRESS -- ) @ . ; @ ? undefined word ADDRESS
All versions of @ that are provided by StrongForth expect the address of a specific data type on the stack. Our first approach micht look like this:
: ? ( DATA -> SINGLE -- ) @ . ; OK BASE ? 10 OK
This works fine, but it has a couple of serious drawbacks. First, we need a separate version of ? for each kind of address (DATA, CONST, CODE, PORT and FAR-ADDRESS). If we add the character addresses, we already get 10 different versions of ?. Second, all items will be displayed as unsigned single-precision numbers, even items of data type SIGNED, FLAG, CHARACTER, and DOUBLE. ? wouldn't care about the different overloaded versions of . that display all data nicely according to their data type. That is, unless we decide again to overload ? by defining separate versions for all overloaded versions of .. But with 8 different versions of . and 10 different kinds of addresses, we'd have to define 80 (!) overloaded versions of ?. No way.
But there's a simple solution. Only one version of ?, that covers all those 80 combinations, plus all future kinds of addresses and all future versions of .. Here it is:
: ? ( -- ) POSTPONE @ POSTPONE . ; IMMEDIATE
The only drawback is that this version of ? compiles two tokens instead of one. But as a compensation, it executes a little bit faster at runtime.
StrongForth provides 12 overloaded versions of DUMP in order to cover all combinations of the data size (single-cell, double-cell and character size) with the DATA, CONST, CODE and FAR-ADDRESS addresses:
DUMP ( CFAR-ADDRESS UNSIGNED -- ) DUMP ( CCODE UNSIGNED -- ) DUMP ( CCONST UNSIGNED -- ) DUMP ( CDATA UNSIGNED -- ) DUMP ( FAR-ADDRESS -> DOUBLE UNSIGNED -- ) DUMP ( CODE -> DOUBLE UNSIGNED -- ) DUMP ( CONST -> DOUBLE UNSIGNED -- ) DUMP ( DATA -> DOUBLE UNSIGNED -- ) DUMP ( FAR-ADDRESS UNSIGNED -- ) DUMP ( CODE UNSIGNED -- ) DUMP ( CONST UNSIGNED -- ) DUMP ( DATA UNSIGNED -- )
The versions of DUMP for character addresses display 16 2-digit hexadecimal numbers per line. The double-cell versions display 4 8-digit hexadecimal numbers per line. All other addresses are assumed to refer to single-cell data, which are displayed as 8 4-digit hexadecimal numbers per line. Here are some examples:
85 BLOCK 64 DUMP 05EC: 5C 20 50 52 4F 47 52 41 4D 4D 49 4E 47 2D 54 4F 05FC: 4F 4C 53 3A 20 44 55 4D 50 20 20 20 20 20 20 20 060C: 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 061C: 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 OK ' DUMP >CODE 20 DUMP 2282: 14BC 0088 0080 00FC 023E 0074 0236 0000 2292: 018A 021C 0036 0074 0212 0010 00FC 0236 22A2: 0000 0182 0074 0090 OK DTP@ 5 DUMP 0140: 061E0000 061E0000 062A0000 06160000 0150: 063A0000 OK
.BYTE and .HEX are used within DUMP for displaying one number as 2, 4 and 8 hexadecimal digits. Additionally, .ADDR displays an address as a 4-digit hexadecimal number with a trailing colon:
.BYTE ( SINGLE -- ) .HEX ( SINGLE -- ) .HEX ( DOUBLE -- ) .ADDR ( SINGLE -- )
Colon definitions can be decompiled and displayed with SEE. Other definitions, like code definitions, constants, variables and values, may easily be included by extending the semantics of SEE. SEE traverses the virtual code of a colon definition, displaying the names of the words that are assiciated with the compiled execution tokens. It further recognizes tokens that have a parameter, like (R@) or BRANCH. The parameters are displayed as signed numbers, because they are either branch offsets or return stack offsets:
: TEST1 ( SIGNED -- 1ST ) DUP 0< IF 2* 1- THEN ; OK SEE TEST1 : TEST1 ( SIGNED -- 1ST ) DUP 0< 0BRANCH 6 2* 1- ; OK
A new line begins after each conditional or unconditional branch. The tokens of LIT, DLIT, SLIT and FLIT (if the floating-point extension has been loaded) are handled in a special way. Instead of displaying the names of these tokens, SEE directly displays the assumed source code:
: TEST2 100699. 533 UM/MOD . DROP " EURO" TYPE ; OK SEE TEST2 : TEST2 ( -- ) 100699 533 UM/MOD . DROP " EURO" TYPE ; OK
Any attempt to apply SEE to words defined with VARIABLE, CONSTANT, VALUE, CODE or CREATE will fail. But it is possible to see deferred definitions, because deferred definitions are actually a kind of colon definitions:
SEE BASE SEE BASE ? is not a colon definition SEE SOURCE DEFER SOURCE ( -- CDATA -> CHARACTER UNSIGNED ) :NONAME ( -- CDATA -> CHARACTER UNSIGNED ) BLK @ 0BRANCH 14 BLK @ BLOCK C/B BRANCH 22 SOURCE-ID 0BRANCH 10 SOURCE-SPEC SPLIT BRANCH 8 TIB #TIB @ ; IS SOURCE OK
Once the Floating-Point word set has been loaded, it becomes necessary to extend the implementation of SEE. The extension allows displaying floating-point literals and makes sure that the parameters that are attached to the tokens of the new versions of (LOOP) and (+LOOP) are being considered. The extension has to be loaded after loading the Programming-Tools and the Floating-Point word sets:
899 LOAD \ load floating-point extension for SEE
SEE uses a lookup table to detect all tokens that need a special treatment in order to be displayed correctly. Examples are the tokens of LIT, EXIT, BRANCH and all other tokens that consume a parameter when executed as virtual machine code. If you define a new word whose token needs a special treatment, you should add a new entry to the lookup table, which consists of the token and the word that performs the special treatment:
' new-word SEEN-BY treatment SEE,
The extension of SEE for the Floating-Point word set adds 8 entries to the lookup table in this way.
Of course, the implementation of SEE leaves many opportunities for improvements. Inclusion of other non colon definitions, recognising conditionals and loops, indenting according to nested control structures, and other things are possible. On the other hand, the availability of the original source code makes these efforts mostly obsolete.
Three words specified by ANS Forth are not implemented in StrongForth. CS-PICK and CS-ROLL had to be left out for the same reason as PICK and ROLL: StrongForth's data type system does not allow defining words with ambiguous stack diagrams at compile time. They are obsolete anyway, because StrongForth does not need a separate control-flow stack. Items of data type CONTROL-FLOW, which are processed by conditionals and loop structures, are simply kept on the data stack.
The third word from the Programming-Tools word set that is not available in StrongForth is FORGET. ANS Forth declares this word as obsolete, because it does not work reliably. It is actually specified for backward compatibility only. Its semantics is now covered by MARKER.
The implementations of [IF], [ELSE] and [THEN] are identical to those suggested in section A.15 of the ANS Forth specification. Some syntactical differences are caused by words that are not available in StrongForth, like WORD, 2DUP, 2DROP and S".
EDITOR and ASSEMBLER are not available as part of the Programming-Tools word set. These two words are included in the Search-Order word set instead, because their usage only makes sense when the Search-Order word set has been loaded. If you intend to use separate word sets for the assembler and the editor, you need to load the Search-Order word set before loading the assembler and the editor.
The definition of ;CODE is quite similar to the definition of DOES>. Both create defining words, and both compile a separate runtime portion ((;CODE) and (DOES)). An example of how to use ;CODE is included in the documentation of the StrongForth assembler. However, there's one thing that is worth to be noted about ;CODE in StrongForth. It's the way how the stack diagrams of the defined words are being compiled. There are actually three possible ways:
Buts whatever you chose is best for a specific defining word, make sure to apply only one of these techniques. Any mixture will most likely lead to completely unpredictable results.
Dr. Stephan Becher - November 28th, 2007