Programming-Tools Reference

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.

WORDS

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.

DUMP

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 -- )

SEE

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.

Programming-Tools Extension Words

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:

  1. You may specify the stack diagram immediately after ;CODE. The example in the documentation of the StrongForth assembler uses this technique. Its disadvantage is that the stack diagrams of all defined words have to be exactly the same.
  2. You may compile the stack diagram explicitly between CREATE and ;CODE, using , or PARAM, to compile the data types including their attributes. This technique is useful if compiling defining words like CONSTANT and VARIABLE that create words with variable stack diagrams.
  3. You may specify the stack diagram individually for each defined word, i. e., after executing the defining word. This technique allows the greatest flexibility.

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