SEE is defined on some Forth systems to reconstruct ( or try to reconstruct) the source of a Forth word. This seems to me to be a cute parlor trick, especially if the Forth source is readily available.
I feel that
SEE can be a useful tool if, instead of attempting to show the source of a Forth word, it shows
what actually got compiled. Here are my criteria for a useful
SEE:
1 Display what was compiled for Forth words
and code words.
2 Recognize the built in data types (
VARIABLE ,
CONSTANT ,
2VARIABLE ,
2CONSTANT , and
USER ) and display their values.
3 Display a
DEFERed word's vector ( the word a
DEFERed word is set to execute) .
4 Transition seamlessly from decompiling/disassembling High level code to Low level and back. For example:
Code:
SEE 2CONSTANT
2CONSTANT
2F0E CREATE
2F10 ,
2F12 ,
2F14 DOES
2F16 2429 JMP ' DOES> >BODY 15 +
2F19 2@
2F1B EXIT
OK
SEE CONSTANT
CONSTANT
23CF CREATE
23D1 ,
23D3 DOES
23D5 2 # LDY
23D7 FE )Y LDA W
23D9 PHA
23DA INY
23DB FE )Y LDA W
23DD 838 JMP PUSH
OK
SEE DEFER
DEFER
23A6 CREATE
23A8 LIT 237C
23AC ,
23AE DOES
23B0 2 # LDY
23B2 FE )Y LDA W
23B4 PHA
23B5 INY
23B6 FE )Y LDA W
23B8 FF STA W 1+
23BA PLA
23BB FE STA W
23BD 0 # LDY
23BF FD JMP W 1-
OK
Notice that in these examples
DOES , the primitive compiled by
DOES> and
;CODE , sets the code field of the latest word in the current vocabulary to point to the code following
DOES and exits. These defining words do
not transition from high level code to low level, but the decompiler does.
5
SEE is just a wrapper word for
(SEE) so
(SEE) can take a CFA from the data stack.
Code:
: SEE ( -- ) ' (SEE) ;
6 Know when to
stop decompiling and when, or if, it should transition from high or low level to the other.
7 Defined with helper words
:DIS and
DIS that decompile
: ( colon) definitions and code definitions respectively. In some situations, the ones that 'trip up'
SEE , it is useful to decompile a Forth word starting on a word boundary, inside the body of the definition, with
:DIS ,or disassemble a low level word starting on an instruction boundary, with
DIS . This example is a partial disassembly of
FIND , the high level word that uses
(FIND) and
VFIND , the find primitives.
Code:
2091 :DIS
2091 CURRENT
2093 @
2095 VFIND
2097 EXIT
OK
The full disassembly of
FIND for comparison:
Code:
SEE FIND
FIND
2087 CONTEXT
2089 @
208B (FIND)
208D ?DUP
208F ?EXIT
2091 CURRENT
2093 @
2095 VFIND
2097 EXIT
OK
8 A word like
DONE? is included in the definition of the decompiler(s) which can pause the listing and wait if a key is pressed or return with a flag. It is possible
SEE will encounter a situation that makes it keep decompiling because none of the stop criteria are met, especially if it's used to examine the code of a new construct like
DOER/MAKE .
Code:
: DONE? ( -- F )
?KEY DUP 0EXIT
3 = ?DUP ?EXIT
KEY 3 = ;
DONE? is used like this:
Code:
: SOME.FORTH.WORD
BLAH BLAH BLAH
BEGIN
BLAH BLAH BLAH
TEST.CONDITION DONE? OR
UNTIL
BLAH BLAH ;
The loop in
SOME.FORTH.WORD keeps running until the test condition is true
or the C64's STOP key is pressed (
DONE? returned a TRUE, we're done. If any other key is pressed,
DONE? will wait for a key press. If the stop key is pressed, a TRUE flag is returned, otherwise FALSE is returned.
A
SEE written with these criteria in mind, along with
DUMP , a word to display memory contents, can be a useful tool when performing a 'postmortem' after an attempt to add some new feature to one's Forth fails.
Cheers,
Jim