Skip to content

14.Debugger_SEE

Matteo Vitturi edited this page Jun 23, 2024 · 9 revisions

SEE debug word

Used in the form SEE cccc

it will print how the word cccc is defined along with NFA, CFA, PFA data. If cccc is a “colon-definition” the result will show a simplified de-compilation of it.

For example, the word TYPE is a “colon-definition” and is defined as follow:

: TYPE( a n -- ) \ Send to current output channel n characters starting at address a. 
    BOUNDS 
    ?DO 
        I C@ EMIT 
    LOOP
; 

If you give

see type 

it will print

Nfa: E7CA 84
Lfa: E7CF 7C0 LEAVE
Cfa: 2C2B 26EF
BOUNDS (?DO) +12 I C@ EMIT (LOOP) -8 EXIT ok

The first line shows TYPE Name Field Address ($E7CA) followed by $84 that is the counter byte of a 4-bytes length name. The counter byte always has the most significant bit set, that is $80 added to $04 giving $84. The second line is the Link Field Address ($E7CF) which holds a heap-pointer $07C0 to LEAVE’s NFA that in this case happens to be the previous definition in the dictionary. You will notice these two fields (NFA and LFA) are stored above address $E000 (i.e. MMU7) and they are effectively stored in one of the extended 8K-RAM pages. Assuming you launched dot-command .vForth, the third line is the Code Field Address ($2C2B) that contains the actual machine code to be run which in this case is a “CALL” to the ENTER routine of any colon-definition, located at $26EF. The compiled code slightly differs from the source, e.g. LOOP from the source is compiled as (LOOP) followed by the displacement to the beginning of the loop itself.

Another example: the word NIP, that removes the second element from Stack, isn’t a colon-definition, isn’t a colon-definition, but a low-level definition coded in machine-code as follow:

CODE NIP ( n1 n2 -- n2 )  \ Drop the second element on the stack.
        POP      HL|      \ Get n2 from stack 
        EX(SP)HL          \ Swap it with n1 and discard it
        NEXT              \ Jump to next instruction
    C; 

And if you give

see nip

it will print

Nfa: E2EE 83
Lfa: E2F2 2E5 DROP
Cfa: 25CE DDE3
25CE E1 E3 DD E9 FD 02 E1 F1  ac]i} aq 

In this case, since NIP is a low-level definition, the PFA part is shown as hex DUMP and no "PFA" part, it's a real machine-code routine. This is an example of use of the ASSEMBLER built-in vocabulary available via NEEDS ASSEMBLER. Again, the first line shows NIP’s NFA ($E2EE in this case) and $83, the count-byte, that indicates a 3-bytes length defi- nition name. The second line is NIP’s LFA ($E2F2) that contains a heap-pointer 02E5 to DROP’s NFA, the previous definition in dictionary. The third line is NIP’s CFA ($25CE) which content is the machine-code routine itself. Examining the subsequent DUMP you should be able to recognize E1 for POP HL, E3 for EX(SP),HL and DD E9 for JP(IX) back to the inner interpreter address that is compiled by NEXT Assembler definition. The four bytes that follows – FD 02 E1 F1 – are the beginning of the subsequent definition compiled in dictionary (TUCK in this case): Try $02FD FAR 2+ 8 DUMP to inspect in HEAP its NFA.

Another example is the definition IF a colon-definition that compiles a conditional branching in the program flow, defined as follows:

: IF ( -- a 2 ) \ compile-time
    COMPILE 0BRANCH
    HERE 0 , 2
;
IMMEDIATE

with the following output:

Nfa: EC5C C2
Lfa: EC5F C53 BACK
Cfa: 3DEE 26EF
COMPILE 0BRANCH HERE 0 , 2 EXIT ok

In this case, since IF is an IMMEDIATE definition, the NFA length-byte is $C2 instead $82.