GARTHWILSON wrote:
Martin_H wrote:
This might be an embarrassing admission, but does Forth usually contain a run time trace and profile feature? I've never used one.
This was one of the things I had in mind when I implemented an indirect jump to NEXT in my '816 Forth; but have never used the trace. The idea is that if you want to start/stop a trace, you just put the address of a different (more complex) version of NEXT in variable NEXTadr which you use as the address for the indirect jump. Then JMP NEXT becomes JMP (NEXTadr). You could invent versions of NEXT on the fly to meet special needs.
Blazin' Forth had
TRACE as part of the system loader ( Blazin' Forth included a disk with the system loader in source form on Forth blocks). There was a code word
>NEXT that took an address from the stack and patched
NEXT with a jump to that address. The new
NEXT would be something like:
Code:
1 # LDY,
IP )Y LDA, W 1+ STA, DEY,
IP )Y LDA, W STA,
<some.address> JMP,
There was also the code word
NEXT> to restore
NEXT .
Here is the code from the Blazin' Forth system loader.
Code:
SCR #19
0 ( UTILITIES -- DEBUGGING: RP@ UNRAVEL ?PARAMS SDBJUN85)
1 CODE RP@ ( -- ADDR RETURN STACK 1- )
2 XSAVE STX, TSX, TXA, PHA, 1 # LDA, XSAVE LDX, PUSH JMP, END-CODE
3
4 : ID? ( TRUE IF VALID NAME)
5 DUP @ [ ' : @ ] LITERAL =
6 SWAP @ C@ [ ' FORTH @ C@ ] LITERAL = OR ;
SCR #20
0 ( UTILITIES -- DEBUGGING: ;NEXT NEXT> >NEXT SDBJUN85)
1 HEX
2 VARIABLE 'TRACE VARIABLE <IP VARIABLE IP>
3
4 CODE ;NEXT ( EXIT THEN NEXT )
5 ( EXIT) PLA, IP STA, PLA, IP 1+ STA,
6 ( GET W) PLA, W STA, PLA, W 1+ STA,
7 ( NEXT) CLC, IP LDA, 2 # ADC, IP STA, CS
8 IF, IP 1+ INC, THEN, 0 # LDY, W 1- JMP, END-CODE
9
10 CODE NEXT> ( RESTORE NEXT)
11 018 # LDA, NEXT 0B + STA, 0A5 # LDA, NEXT 0C + STA,
12 084 # LDA, NEXT 0D + STA, NEXT JMP, END-CODE
13
14
15 DECIMAL
SCR #21
0 ( UTILITIES - DEBUGGING: >NEXT <IP> ID.L SDBJUL85)
1 HEX
2 CODE >NEXT ( N -- POINT NEXT TO N)
3 04C # LDA, NEXT 0B + STA, BOT LDA, NEXT 0C + STA,
4 BOT 1+ LDA, NEXT 0D + STA, POP JMP, END-CODE
5
6 CREATE <IP> ASSEMBLER
7 <IP LDA, IP CMP, <IP 1+ LDA, IP 1+ SBC, CS NOT
8 IF, IP> LDA, IP CMP, IP> 1+ LDA, IP 1+ SBC, CS
9 IF, DEX, DEX, IP LDA, BOT STA, IP 1+ LDA, BOT 1+ STA,
10 W 1+ LDA, PHA, W LDA, PHA,
11 'TRACE LDA, W STA, 'TRACE 1+ LDA, W 1+ STA, W 1- JMP,
12 THEN, THEN, ' ;NEXT @ 0C + JMP, DECIMAL
13
14 : ID.L ( CFA LEN -- )
15 OVER >NAME DUP ID. SPACE ROT 1- - + SPACES ;
SCR #22
0 ( UTILITIES - DEBUGGERS: 'EXIT STEP TRACE CONT SDBJUL85)
1 : 'EXIT ( CFA -- LAST WORD)
2 BEGIN 1+ DUP @ ['] EXIT = UNTIL ;
3 VARIABLE CON
4 : STEP ( CFA -- )
5 NEXT> CR @ 10 ID.L .S SPACE KEY CONTROL "{CONTROL P}" OVER = IF
6 CON OFF DROP CR ." P? "
7 BEGIN QUERY RUN CR ." P? " CON @ UNTIL
8 THEN CONTROL "{RUN/STOP}" OVER = IF
9 DROP CR ." TRACING OFF" QUIT
10 THEN DROP <IP> >NEXT ;NEXT ; ' STEP 'TRACE !
11
12 : TRACE NEXT> ' DUP ID? NOT ABORT" CAN'T TRACE"
13 DUP <IP ! 'EXIT IP> ! <IP> >NEXT ;
14 : NOTRACE NEXT> ." TRACING OFF" QUIT ;
15 : CONT CON ON 0 ;
I was able to get this to work with Fleet Forth, but I had to change one thing.
Code:
084 # LDA,
Was changed to:
Code:
IP # LDA,
Because Blazin' Forth's
IP is at $84-$85 and Fleet Forth's is at $FB-$FC. Besides, using the constant for
IP makes it a little more portable.
TRACE was followed by the name of a word to trace but did not initiate a trace, tracing occurred when
IP was between two addresses stored in
<IP and
IP> so the word was traced whenever it was executed until tracing was turned off.
Blazin' Forth used
ID? to determine if a word was one that could be traced. It used
'EXIT to find where the word to be traced ended. Since my decompiler does a better job at determining where a word ends and low level code in the trace range is harmless, I put
TRACE in its own block and modified the load screen slightly:
Code:
SCR# 1
0: ( BLAZIN' FORTH 85 SYSTEM LOADER )
1: VIEW OFF
2: DECIMAL
3: CR 13 LOAD
4: CR 15 22 THRU .// UTILITIES
5: " SAD" FIND NIP 9 AND 23 LINELOAD
6:
7: CR 50 57 THRU .// STRINGS
8: CR 58 70 THRU .// SID SUPPORT
9: CR 71 104 THRU .// VIC SUPPORT
10:
11: VIEW ON .( LOCATE ENABLED) CR
SCR# 23
0: // TRACE
1: DECIMAL
2: : TRACE
3: NEXT> ' DUP ID? NOT
4: ABORT" CAN'T TRACE"
5: DUP <IP ! 'EXIT IP> !
6: <IP> >NEXT ;
7: ;S
8: // IF THE DECOMPILER IS AVAILABLE
9: // HERE IS A BETTER TRACE
10: : TRACE
11: NEXT> SAD OFF SEE
12: SAD @ ?DUP 0=
13: ABORT" CAN'T TRACE"
14: 2- <IP ! EAD @ IP> !
15: <IP> >NEXT ;
;S stops loading of a block ( it will also stop interpretation/compilation of text from the keyboard). it is an alias for
EXIT . It is actually a 'code word' with its
CFA pointing to the body of
EXIT , it has no body of its own.
SAD is a variable that holds the starting address of the latest decompiled word ( the address of its parameter field or body).
EAD holds the ending address, or rather the address just past the end of the latest decompiled word.
Code:
EAD @ SAD @ - .
Gives the size of the latest decompiled word's body.
Oh and where there is a
// , just think backslash (
\ ). The Commodore 64 has no backslash key.
[Edit: Fixed the source code. This source is from a print dump and the print dump functionality does not support the Commodore 64's quote mode. Where the source shows {CONTROL P} , press the C64's control key and the P key. Where it shows {RUN/STOP} , press the C64's run/stop key.]