\ @(#) trace.fth 98/01/28 1.2\ TRACE ( <name> -- , trace pForth word )\\ Single step debugger.\ TRACE ( i*x <name> -- , setup trace for Forth word )\ S ( -- , step over )\ SM ( many -- , step over many times )\ SD ( -- , step down )\ G ( -- , go to end of word )\ GD ( n -- , go down N levels from current level, stop at end of this level )\\ This debugger works by emulating the inner interpreter of pForth.\ It executes code and maintains a separate return stack for the\ program under test. Thus all primitives that operate on the return\ stack, such as DO and R> must be trapped. Local variables must\ also be handled specially. Several state variables are also\ saved and restored to establish the context for the program being\ tested.\\ Copyright 1997 Phil Burk\\ Modifications:\ 19990930 John Providenza - Fixed stack bugs in GDanew task-trace.fth: SPACE.TO.COLUMN ( col -- ) out @ - spaces;: IS.PRIMITIVE? ( xt -- flag , true if kernel primitive ) ['] first_colon <;0 value TRACE_IP \ instruction pointer0 value TRACE_LEVEL \ level of descent for inner interpreter0 value TRACE_LEVEL_MAX \ maximum level of descentprivate{\ use fake return stack128 cells constant TRACE_RETURN_SIZE \ size of return stack in bytescreate TRACE-RETURN-STACK TRACE_RETURN_SIZE 16 + allotvariable TRACE-RSP: TRACE.>R ( n -- ) trace-rsp @ cell- dup trace-rsp ! ! ; \ *(--rsp) = n: TRACE.R> ( -- n ) trace-rsp @ dup @ swap cell+ trace-rsp ! ; \ n = *rsp++: TRACE.R@ ( -- n ) trace-rsp @ @ ; ; \ n = *rsp: TRACE.RPICK ( index -- n ) cells trace-rsp @ + @ ; ; \ n = rsp[index]: TRACE.0RP ( -- n ) trace-return-stack trace_return_size + 8 + trace-rsp ! ;: TRACE.RDROP ( -- ) cell trace-rsp +! ;: TRACE.RCHECK ( -- , abort if return stack out of range ) trace-rsp @ trace-return-stack u< abort" TRACE return stack OVERFLOW!" trace-rsp @ trace-return-stack trace_return_size + 12 + u> abort" TRACE return stack UNDERFLOW!";\ save and restore several state variables10 cells constant TRACE_STATE_SIZEcreate TRACE-STATE-1 TRACE_STATE_SIZE allotcreate TRACE-STATE-2 TRACE_STATE_SIZE allotvariable TRACE-STATE-PTR: TRACE.SAVE++ ( addr -- , save next thing ) @ trace-state-ptr @ ! cell trace-state-ptr +!;: TRACE.SAVE.STATE ( -- ) state trace.save++ hld trace.save++ base trace.save++;: TRACE.SAVE.STATE1 ( -- , save normal state ) trace-state-1 trace-state-ptr ! trace.save.state;: TRACE.SAVE.STATE2 ( -- , save state of word being debugged ) trace-state-2 trace-state-ptr ! trace.save.state;: TRACE.RESTORE++ ( addr -- , restore next thing ) trace-state-ptr @ @ swap ! cell trace-state-ptr +!;: TRACE.RESTORE.STATE ( -- ) state trace.restore++ hld trace.restore++ base trace.restore++;: TRACE.RESTORE.STATE1 ( -- ) trace-state-1 trace-state-ptr ! trace.restore.state;: TRACE.RESTORE.STATE2 ( -- ) trace-state-2 trace-state-ptr ! trace.restore.state;\ The implementation of these pForth primitives is specific to pForth.variable TRACE-LOCALS-PTR \ point to top of local frame\ create a return stack frame for NUM local variables: TRACE.(LOCAL.ENTRY) ( x0 x1 ... xn n -- ) { num | lp -- } trace-locals-ptr @ trace.>r trace-rsp @ trace-locals-ptr ! trace-rsp @ num cells - trace-rsp ! \ make room for locals trace-rsp @ -> lp num 0 DO lp ! cell +-> lp \ move data into locals frame on return stack LOOP;: TRACE.(LOCAL.EXIT) ( -- ) trace-locals-ptr @ trace-rsp ! trace.r> trace-locals-ptr !;: TRACE.(LOCAL@) ( l# -- n , fetch from local frame ) trace-locals-ptr @ swap cells - @;: TRACE.(1_LOCAL@) ( -- n ) 1 trace.(local@) ;: TRACE.(2_LOCAL@) ( -- n ) 2 trace.(local@) ;: TRACE.(3_LOCAL@) ( -- n ) 3 trace.(local@) ;: TRACE.(4_LOCAL@) ( -- n ) 4 trace.(local@) ;: TRACE.(5_LOCAL@) ( -- n ) 5 trace.(local@) ;: TRACE.(6_LOCAL@) ( -- n ) 6 trace.(local@) ;: TRACE.(7_LOCAL@) ( -- n ) 7 trace.(local@) ;: TRACE.(8_LOCAL@) ( -- n ) 8 trace.(local@) ;: TRACE.(LOCAL!) ( n l# -- , store into local frame ) trace-locals-ptr @ swap cells - !;: TRACE.(1_LOCAL!) ( -- n ) 1 trace.(local!) ;: TRACE.(2_LOCAL!) ( -- n ) 2 trace.(local!) ;: TRACE.(3_LOCAL!) ( -- n ) 3 trace.(local!) ;: TRACE.(4_LOCAL!) ( -- n ) 4 trace.(local!) ;: TRACE.(5_LOCAL!) ( -- n ) 5 trace.(local!) ;: TRACE.(6_LOCAL!) ( -- n ) 6 trace.(local!) ;: TRACE.(7_LOCAL!) ( -- n ) 7 trace.(local!) ;: TRACE.(8_LOCAL!) ( -- n ) 8 trace.(local!) ;: TRACE.(LOCAL+!) ( n l# -- , store into local frame ) trace-locals-ptr @ swap cells - +!;: TRACE.(?DO) { limit start ip -- ip' } limit start = IF ip @ +-> ip \ BRANCH ELSE start trace.>r limit trace.>r cell +-> ip THEN ip;: TRACE.(LOOP) { ip | limit indx -- ip' } trace.r> -> limit trace.r> 1+ -> indx limit indx = IF cell +-> ip ELSE indx trace.>r limit trace.>r ip @ +-> ip THEN ip;: TRACE.(+LOOP) { delta ip | limit indx oldindx -- ip' } trace.r> -> limit trace.r> -> oldindx oldindx delta + -> indx\ /* Do indices cross boundary between LIMIT-1 and LIMIT ? */\ if( ( (OldIndex - Limit) & ((Limit-1) - NewIndex) & 0x80000000 ) ||\ ( (NewIndex - Limit) & ((Limit-1) - OldIndex) & 0x80000000 ) ) oldindx limit - limit 1- indx - AND $ 80000000 AND indx limit - limit 1- oldindx - AND $ 80000000 AND OR IF cell +-> ip ELSE indx trace.>r limit trace.>r ip @ +-> ip THEN ip;: TRACE.CHECK.IP { ip -- } ip ['] first_colon u< ip here u> OR IF ." TRACE - IP out of range = " ip .hex cr abort THEN;: TRACE.SHOW.IP { ip -- , print name and offset } ip code> >name dup id. name> >code ip swap - ." +" .;: TRACE.SHOW.STACK { | mdepth -- } base @ >r ." <" base @ decimal 1 .r ." :" depth 1 .r ." > " r> base ! depth 5 min -> mdepth depth mdepth - IF ." ... " \ if we don't show entire stack THEN mdepth 0 ?DO mdepth i 1+ - pick . \ show numbers in current base LOOP;: TRACE.SHOW.NEXT { ip -- } >newline ip trace.check.ip\ show word name and offset ." << " ip trace.show.ip 16 space.to.column\ show data stack trace.show.stack 40 space.to.column ." ||" trace_level 2* spaces ip code@ cell +-> ip\ show primitive about to be executed dup .xt space\ trap any primitives that are followed by inline data CASE ['] (LITERAL) OF ip @ . ENDOF ['] (ALITERAL) OF ip a@ . ENDOF[ exists? (FLITERAL) [IF] ] ['] (FLITERAL) OF ip f@ f. ENDOF[ [THEN] ] ['] BRANCH OF ip @ . ENDOF ['] 0BRANCH OF ip @ . ENDOF ['] (.") OF ip count type .' "' ENDOF ['] (C") OF ip count type .' "' ENDOF ['] (S") OF ip count type .' "' ENDOF ENDCASE 65 space.to.column ." >> ";: TRACE.DO.PRIMITIVE { ip xt | oldhere -- ip' , perform code at ip } xt CASE 0 OF -1 +-> trace_level trace.r> -> ip ENDOF \ EXIT ['] (CREATE) OF ip cell- body_offset + ENDOF ['] (LITERAL) OF ip @ cell +-> ip ENDOF ['] (ALITERAL) OF ip a@ cell +-> ip ENDOF[ exists? (FLITERAL) [IF] ] ['] (FLITERAL) OF ip f@ 1 floats +-> ip ENDOF[ [THEN] ] ['] BRANCH OF ip @ +-> ip ENDOF ['] 0BRANCH OF 0= IF ip @ +-> ip ELSE cell +-> ip THEN ENDOF ['] >R OF trace.>r ENDOF ['] R> OF trace.r> ENDOF ['] R@ OF trace.r@ ENDOF ['] RDROP OF trace.rdrop ENDOF ['] 2>R OF trace.>r trace.>r ENDOF ['] 2R> OF trace.r> trace.r> ENDOF ['] 2R@ OF trace.r@ 1 trace.rpick ENDOF ['] i OF 1 trace.rpick ENDOF ['] j OF 3 trace.rpick ENDOF ['] (LEAVE) OF trace.rdrop trace.rdrop ip @ +-> ip ENDOF ['] (LOOP) OF ip trace.(loop) -> ip ENDOF ['] (+LOOP) OF ip trace.(+loop) -> ip ENDOF ['] (DO) OF trace.>r trace.>r ENDOF ['] (?DO) OF ip trace.(?do) -> ip ENDOF ['] (.") OF ip count type ip count + aligned -> ip ENDOF ['] (C") OF ip ip count + aligned -> ip ENDOF ['] (S") OF ip count ip count + aligned -> ip ENDOF ['] (LOCAL.ENTRY) OF trace.(local.entry) ENDOF ['] (LOCAL.EXIT) OF trace.(local.exit) ENDOF ['] (LOCAL@) OF trace.(local@) ENDOF ['] (1_LOCAL@) OF trace.(1_local@) ENDOF ['] (2_LOCAL@) OF trace.(2_local@) ENDOF ['] (3_LOCAL@) OF trace.(3_local@) ENDOF ['] (4_LOCAL@) OF trace.(4_local@) ENDOF ['] (5_LOCAL@) OF trace.(5_local@) ENDOF ['] (6_LOCAL@) OF trace.(6_local@) ENDOF ['] (7_LOCAL@) OF trace.(7_local@) ENDOF ['] (8_LOCAL@) OF trace.(8_local@) ENDOF ['] (LOCAL!) OF trace.(local!) ENDOF ['] (1_LOCAL!) OF trace.(1_local!) ENDOF ['] (2_LOCAL!) OF trace.(2_local!) ENDOF ['] (3_LOCAL!) OF trace.(3_local!) ENDOF ['] (4_LOCAL!) OF trace.(4_local!) ENDOF ['] (5_LOCAL!) OF trace.(5_local!) ENDOF ['] (6_LOCAL!) OF trace.(6_local!) ENDOF ['] (7_LOCAL!) OF trace.(7_local!) ENDOF ['] (8_LOCAL!) OF trace.(8_local!) ENDOF ['] (LOCAL+!) OF trace.(local+!) ENDOF >r xt EXECUTE r> ENDCASE ip;: TRACE.DO.NEXT { ip | xt oldhere -- ip' , perform code at ip } ip trace.check.ip\ set context for word under test trace.save.state1 here -> oldhere trace.restore.state2 oldhere 256 + dp !\ get execution token ip code@ -> xt cell +-> ip\ execute token xt is.primitive? IF \ primitive ip xt trace.do.primitive -> ip ELSE \ secondary trace_level trace_level_max < IF ip trace.>r \ threaded execution 1 +-> trace_level xt codebase + -> ip ELSE \ treat it as a primitive ip xt trace.do.primitive -> ip THEN THEN\ restore original context trace.rcheck trace.save.state2 trace.restore.state1 oldhere dp ! ip;: TRACE.NEXT { ip | xt -- ip' } trace_level 0> IF ip trace.do.next -> ip THEN trace_level 0> IF ip trace.show.next ELSE trace-stack on ." Finished." cr THEN ip;}private: TRACE ( i*x <name> -- i*x , setup trace environment ) ' dup is.primitive? IF drop ." Sorry. You can't trace a primitive." cr ELSE 1 -> trace_level trace_level -> trace_level_max trace.0rp >code -> trace_ip trace_ip trace.show.next trace-stack off trace.save.state2 THEN;: s ( -- , step over ) trace_level -> trace_level_max trace_ip trace.next -> trace_ip;: sd ( -- , step down ) trace_level 1+ -> trace_level_max trace_ip trace.next -> trace_ip;: sm ( many -- , step many times ) trace_level -> trace_level_max 0 ?DO trace_ip trace.next -> trace_ip LOOP;defer trace.user ( IP -- stop? )' 0= is trace.user: gd { more_levels | stop_level -- } here what's trace.user u< \ has it been forgotten? IF ." Resetting TRACE.USER !!!" cr ['] 0= is trace.user THEN more_levels 0< more_levels 10 > or \ 19990930 - OR was missing IF ." GD level out of range (0-10), = " more_levels . cr ELSE trace_level more_levels + -> trace_level_max trace_level 1- -> stop_level BEGIN trace_ip trace.user \ call deferred user word ?dup \ leave flag for UNTIL \ 19990930 - was DUP IF ." TRACE.USER returned " dup . ." so stopping execution." cr ELSE trace_ip trace.next -> trace_ip trace_level stop_level > not THEN UNTIL THEN;: g ( -- , execute until end of word ) 0 gd;: TRACE.HELP ( -- ) ." TRACE ( i*x <name> -- , setup trace for Forth word )" cr ." S ( -- , step over )" cr ." SM ( many -- , step over many times )" cr ." SD ( -- , step down )" cr ." G ( -- , go to end of word )" cr ." GD ( n -- , go down N levels from current level," cr ." stop at end of this level )" cr;privatize0 [IF]variable var1100 var1 !: FOO dup IF 1 + . THEN 77 var1 @ + . ;: ZOO 29 foo 99 22 + . ;: ROO 92 >r 1 r@ + . r> . ;: MOO c" hello" count type ." This is a message." cr s" another message" type cr;: KOO 7 FOO ." DONE" ;: TR.DO 4 0 DO i . LOOP ;: TR.?DO 0 ?DO i . LOOP ;: TR.LOC1 { aa bb } aa bb + . ;: TR.LOC2 789 >r 4 5 tr.loc1 r> . ;[THEN]
\ @(#) trace.fth 98/01/28 1.2
\ TRACE ( <name> -- , trace pForth word )
\
\ Single step debugger.
\ TRACE ( i*x <name> -- , setup trace for Forth word )
\ S ( -- , step over )
\ SM ( many -- , step over many times )
\ SD ( -- , step down )
\ G ( -- , go to end of word )
\ GD ( n -- , go down N levels from current level, stop at end of this level )
\
\ This debugger works by emulating the inner interpreter of pForth.
\ It executes code and maintains a separate return stack for the
\ program under test. Thus all primitives that operate on the return
\ stack, such as DO and R> must be trapped. Local variables must
\ also be handled specially. Several state variables are also
\ saved and restored to establish the context for the program being
\ tested.
\
\ Copyright 1997 Phil Burk
\
\ Modifications:
\ 19990930 John Providenza - Fixed stack bugs in GD
anew task-trace.fth
: SPACE.TO.COLUMN ( col -- )
out @ - spaces
;
: IS.PRIMITIVE? ( xt -- flag , true if kernel primitive )
['] first_colon <
;
0 value TRACE_IP \ instruction pointer
0 value TRACE_LEVEL \ level of descent for inner interpreter
0 value TRACE_LEVEL_MAX \ maximum level of descent
private{
\ use fake return stack
128 cells constant TRACE_RETURN_SIZE \ size of return stack in bytes
create TRACE-RETURN-STACK TRACE_RETURN_SIZE 16 + allot
variable TRACE-RSP
: TRACE.>R ( n -- ) trace-rsp @ cell- dup trace-rsp ! ! ; \ *(--rsp) = n
: TRACE.R> ( -- n ) trace-rsp @ dup @ swap cell+ trace-rsp ! ; \ n = *rsp++
: TRACE.R@ ( -- n ) trace-rsp @ @ ; ; \ n = *rsp
: TRACE.RPICK ( index -- n ) cells trace-rsp @ + @ ; ; \ n = rsp[index]
: TRACE.0RP ( -- n ) trace-return-stack trace_return_size + 8 + trace-rsp ! ;
: TRACE.RDROP ( -- ) cell trace-rsp +! ;
: TRACE.RCHECK ( -- , abort if return stack out of range )
trace-rsp @ trace-return-stack u<
abort" TRACE return stack OVERFLOW!"
trace-rsp @ trace-return-stack trace_return_size + 12 + u>
abort" TRACE return stack UNDERFLOW!"
;
\ save and restore several state variables
10 cells constant TRACE_STATE_SIZE
create TRACE-STATE-1 TRACE_STATE_SIZE allot
create TRACE-STATE-2 TRACE_STATE_SIZE allot
variable TRACE-STATE-PTR
: TRACE.SAVE++ ( addr -- , save next thing )
@ trace-state-ptr @ !
cell trace-state-ptr +!
;
: TRACE.SAVE.STATE ( -- )
state trace.save++
hld trace.save++
base trace.save++
;
: TRACE.SAVE.STATE1 ( -- , save normal state )
trace-state-1 trace-state-ptr !
trace.save.state
;
: TRACE.SAVE.STATE2 ( -- , save state of word being debugged )
trace-state-2 trace-state-ptr !
trace.save.state
;
: TRACE.RESTORE++ ( addr -- , restore next thing )
trace-state-ptr @ @ swap !
cell trace-state-ptr +!
;
: TRACE.RESTORE.STATE ( -- )
state trace.restore++
hld trace.restore++
base trace.restore++
;
: TRACE.RESTORE.STATE1 ( -- )
trace-state-1 trace-state-ptr !
trace.restore.state
;
: TRACE.RESTORE.STATE2 ( -- )
trace-state-2 trace-state-ptr !
trace.restore.state
;
\ The implementation of these pForth primitives is specific to pForth.
variable TRACE-LOCALS-PTR \ point to top of local frame
\ create a return stack frame for NUM local variables
: TRACE.(LOCAL.ENTRY) ( x0 x1 ... xn n -- ) { num | lp -- }
trace-locals-ptr @ trace.>r
trace-rsp @ trace-locals-ptr !
trace-rsp @ num cells - trace-rsp ! \ make room for locals
trace-rsp @ -> lp
num 0
DO
lp !
cell +-> lp \ move data into locals frame on return stack
LOOP
;
: TRACE.(LOCAL.EXIT) ( -- )
trace-locals-ptr @ trace-rsp !
trace.r> trace-locals-ptr !
;
: TRACE.(LOCAL@) ( l# -- n , fetch from local frame )
trace-locals-ptr @ swap cells - @
;
: TRACE.(1_LOCAL@) ( -- n ) 1 trace.(local@) ;
: TRACE.(2_LOCAL@) ( -- n ) 2 trace.(local@) ;
: TRACE.(3_LOCAL@) ( -- n ) 3 trace.(local@) ;
: TRACE.(4_LOCAL@) ( -- n ) 4 trace.(local@) ;
: TRACE.(5_LOCAL@) ( -- n ) 5 trace.(local@) ;
: TRACE.(6_LOCAL@) ( -- n ) 6 trace.(local@) ;
: TRACE.(7_LOCAL@) ( -- n ) 7 trace.(local@) ;
: TRACE.(8_LOCAL@) ( -- n ) 8 trace.(local@) ;
: TRACE.(LOCAL!) ( n l# -- , store into local frame )
trace-locals-ptr @ swap cells - !
;
: TRACE.(1_LOCAL!) ( -- n ) 1 trace.(local!) ;
: TRACE.(2_LOCAL!) ( -- n ) 2 trace.(local!) ;
: TRACE.(3_LOCAL!) ( -- n ) 3 trace.(local!) ;
: TRACE.(4_LOCAL!) ( -- n ) 4 trace.(local!) ;
: TRACE.(5_LOCAL!) ( -- n ) 5 trace.(local!) ;
: TRACE.(6_LOCAL!) ( -- n ) 6 trace.(local!) ;
: TRACE.(7_LOCAL!) ( -- n ) 7 trace.(local!) ;
: TRACE.(8_LOCAL!) ( -- n ) 8 trace.(local!) ;
: TRACE.(LOCAL+!) ( n l# -- , store into local frame )
trace-locals-ptr @ swap cells - +!
;
: TRACE.(?DO) { limit start ip -- ip' }
limit start =
IF
ip @ +-> ip \ BRANCH
ELSE
start trace.>r
limit trace.>r
cell +-> ip
THEN
ip
;
: TRACE.(LOOP) { ip | limit indx -- ip' }
trace.r> -> limit
trace.r> 1+ -> indx
limit indx =
IF
cell +-> ip
ELSE
indx trace.>r
limit trace.>r
ip @ +-> ip
THEN
ip
;
: TRACE.(+LOOP) { delta ip | limit indx oldindx -- ip' }
trace.r> -> limit
trace.r> -> oldindx
oldindx delta + -> indx
\ /* Do indices cross boundary between LIMIT-1 and LIMIT ? */
\ if( ( (OldIndex - Limit) & ((Limit-1) - NewIndex) & 0x80000000 ) ||
\ ( (NewIndex - Limit) & ((Limit-1) - OldIndex) & 0x80000000 ) )
oldindx limit - limit 1- indx - AND $ 80000000 AND
indx limit - limit 1- oldindx - AND $ 80000000 AND OR
IF
cell +-> ip
ELSE
indx trace.>r
limit trace.>r
ip @ +-> ip
THEN
ip
;
: TRACE.CHECK.IP { ip -- }
ip ['] first_colon u<
ip here u> OR
IF
." TRACE - IP out of range = " ip .hex cr
abort
THEN
;
: TRACE.SHOW.IP { ip -- , print name and offset }
ip code> >name dup id.
name> >code ip swap - ." +" .
;
: TRACE.SHOW.STACK { | mdepth -- }
base @ >r
." <" base @ decimal 1 .r ." :"
depth 1 .r ." > "
r> base !
depth 5 min -> mdepth
depth mdepth -
IF
." ... " \ if we don't show entire stack
THEN
mdepth 0
?DO
mdepth i 1+ - pick . \ show numbers in current base
LOOP
;
: TRACE.SHOW.NEXT { ip -- }
>newline
ip trace.check.ip
\ show word name and offset
." << "
ip trace.show.ip
16 space.to.column
\ show data stack
trace.show.stack
40 space.to.column ." ||"
trace_level 2* spaces
ip code@
cell +-> ip
\ show primitive about to be executed
dup .xt space
\ trap any primitives that are followed by inline data
CASE
['] (LITERAL) OF ip @ . ENDOF
['] (ALITERAL) OF ip a@ . ENDOF
[ exists? (FLITERAL) [IF] ]
['] (FLITERAL) OF ip f@ f. ENDOF
[ [THEN] ]
['] BRANCH OF ip @ . ENDOF
['] 0BRANCH OF ip @ . ENDOF
['] (.") OF ip count type .' "' ENDOF
['] (C") OF ip count type .' "' ENDOF
['] (S") OF ip count type .' "' ENDOF
ENDCASE
65 space.to.column ." >> "
;
: TRACE.DO.PRIMITIVE { ip xt | oldhere -- ip' , perform code at ip }
xt
CASE
0 OF -1 +-> trace_level trace.r> -> ip ENDOF \ EXIT
['] (CREATE) OF ip cell- body_offset + ENDOF
['] (LITERAL) OF ip @ cell +-> ip ENDOF
['] (ALITERAL) OF ip a@ cell +-> ip ENDOF
[ exists? (FLITERAL) [IF] ]
['] (FLITERAL) OF ip f@ 1 floats +-> ip ENDOF
[ [THEN] ]
['] BRANCH OF ip @ +-> ip ENDOF
['] 0BRANCH OF 0= IF ip @ +-> ip ELSE cell +-> ip THEN ENDOF
['] >R OF trace.>r ENDOF
['] R> OF trace.r> ENDOF
['] R@ OF trace.r@ ENDOF
['] RDROP OF trace.rdrop ENDOF
['] 2>R OF trace.>r trace.>r ENDOF
['] 2R> OF trace.r> trace.r> ENDOF
['] 2R@ OF trace.r@ 1 trace.rpick ENDOF
['] i OF 1 trace.rpick ENDOF
['] j OF 3 trace.rpick ENDOF
['] (LEAVE) OF trace.rdrop trace.rdrop ip @ +-> ip ENDOF
['] (LOOP) OF ip trace.(loop) -> ip ENDOF
['] (+LOOP) OF ip trace.(+loop) -> ip ENDOF
['] (DO) OF trace.>r trace.>r ENDOF
['] (?DO) OF ip trace.(?do) -> ip ENDOF
['] (.") OF ip count type ip count + aligned -> ip ENDOF
['] (C") OF ip ip count + aligned -> ip ENDOF
['] (S") OF ip count ip count + aligned -> ip ENDOF
['] (LOCAL.ENTRY) OF trace.(local.entry) ENDOF
['] (LOCAL.EXIT) OF trace.(local.exit) ENDOF
['] (LOCAL@) OF trace.(local@) ENDOF
['] (1_LOCAL@) OF trace.(1_local@) ENDOF
['] (2_LOCAL@) OF trace.(2_local@) ENDOF
['] (3_LOCAL@) OF trace.(3_local@) ENDOF
['] (4_LOCAL@) OF trace.(4_local@) ENDOF
['] (5_LOCAL@) OF trace.(5_local@) ENDOF
['] (6_LOCAL@) OF trace.(6_local@) ENDOF
['] (7_LOCAL@) OF trace.(7_local@) ENDOF
['] (8_LOCAL@) OF trace.(8_local@) ENDOF
['] (LOCAL!) OF trace.(local!) ENDOF
['] (1_LOCAL!) OF trace.(1_local!) ENDOF
['] (2_LOCAL!) OF trace.(2_local!) ENDOF
['] (3_LOCAL!) OF trace.(3_local!) ENDOF
['] (4_LOCAL!) OF trace.(4_local!) ENDOF
['] (5_LOCAL!) OF trace.(5_local!) ENDOF
['] (6_LOCAL!) OF trace.(6_local!) ENDOF
['] (7_LOCAL!) OF trace.(7_local!) ENDOF
['] (8_LOCAL!) OF trace.(8_local!) ENDOF
['] (LOCAL+!) OF trace.(local+!) ENDOF
>r xt EXECUTE r>
ENDCASE
ip
;
: TRACE.DO.NEXT { ip | xt oldhere -- ip' , perform code at ip }
ip trace.check.ip
\ set context for word under test
trace.save.state1
here -> oldhere
trace.restore.state2
oldhere 256 + dp !
\ get execution token
ip code@ -> xt
cell +-> ip
\ execute token
xt is.primitive?
IF \ primitive
ip xt trace.do.primitive -> ip
ELSE \ secondary
trace_level trace_level_max <
IF
ip trace.>r \ threaded execution
1 +-> trace_level
xt codebase + -> ip
ELSE
\ treat it as a primitive
ip xt trace.do.primitive -> ip
THEN
THEN
\ restore original context
trace.rcheck
trace.save.state2
trace.restore.state1
oldhere dp !
ip
;
: TRACE.NEXT { ip | xt -- ip' }
trace_level 0>
IF
ip trace.do.next -> ip
THEN
trace_level 0>
IF
ip trace.show.next
ELSE
trace-stack on
." Finished." cr
THEN
ip
;
}private
: TRACE ( i*x <name> -- i*x , setup trace environment )
' dup is.primitive?
IF
drop ." Sorry. You can't trace a primitive." cr
ELSE
1 -> trace_level
trace_level -> trace_level_max
trace.0rp
>code -> trace_ip
trace_ip trace.show.next
trace-stack off
trace.save.state2
THEN
;
: s ( -- , step over )
trace_level -> trace_level_max
trace_ip trace.next -> trace_ip
;
: sd ( -- , step down )
trace_level 1+ -> trace_level_max
trace_ip trace.next -> trace_ip
;
: sm ( many -- , step many times )
trace_level -> trace_level_max
0
?DO
trace_ip trace.next -> trace_ip
LOOP
;
defer trace.user ( IP -- stop? )
' 0= is trace.user
: gd { more_levels | stop_level -- }
here what's trace.user u< \ has it been forgotten?
IF
." Resetting TRACE.USER !!!" cr
['] 0= is trace.user
THEN
more_levels 0<
more_levels 10 >
or \ 19990930 - OR was missing
IF
." GD level out of range (0-10), = " more_levels . cr
ELSE
trace_level more_levels + -> trace_level_max
trace_level 1- -> stop_level
BEGIN
trace_ip trace.user \ call deferred user word
?dup \ leave flag for UNTIL \ 19990930 - was DUP
IF
." TRACE.USER returned " dup . ." so stopping execution." cr
ELSE
trace_ip trace.next -> trace_ip
trace_level stop_level > not
THEN
UNTIL
THEN
;
: g ( -- , execute until end of word )
0 gd
;
: TRACE.HELP ( -- )
." TRACE ( i*x <name> -- , setup trace for Forth word )" cr
." S ( -- , step over )" cr
." SM ( many -- , step over many times )" cr
." SD ( -- , step down )" cr
." G ( -- , go to end of word )" cr
." GD ( n -- , go down N levels from current level," cr
." stop at end of this level )" cr
;
privatize
0 [IF]
variable var1
100 var1 !
: FOO dup IF 1 + . THEN 77 var1 @ + . ;
: ZOO 29 foo 99 22 + . ;
: ROO 92 >r 1 r@ + . r> . ;
: MOO c" hello" count type
." This is a message." cr
s" another message" type cr
;
: KOO 7 FOO ." DONE" ;
: TR.DO 4 0 DO i . LOOP ;
: TR.?DO 0 ?DO i . LOOP ;
: TR.LOC1 { aa bb } aa bb + . ;
: TR.LOC2 789 >r 4 5 tr.loc1 r> . ;
[THEN]