6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Sat Apr 27, 2024 9:09 pm

All times are UTC




Post new topic Reply to topic  [ 335 posts ]  Go to page Previous  1 ... 18, 19, 20, 21, 22, 23  Next
Author Message
PostPosted: Thu Jun 22, 2023 1:09 pm 
Offline

Joined: Sun May 13, 2018 5:49 pm
Posts: 247
Is the idea here to be able to "clean up" after you've messed up a word a few times? Does the programmer keep running this version of forget until it can't find the word anymore in order to clear it all out?

When I make a mistake while defining a word, it's always in the back of my mind that I used up a bit of dictionary space, but It's never really been an issue because 32K of RAM (on my SBC) is so much RAM for Forth. The only exception is running Tali Forth 2's test suite. That DOES use more than 32K of RAM and I had to use MARKERs.

Tali Forth has MARKER instead of FORGET (we've discussed the pros and cons of each briefly elsewhere in this forum) which rewinds all of the wordlists back to a previous point in time. It looks like you are doing this for vocabularies so that you end up with almost identical effect.

How do you handle resetting the system deferred words? Do you save their previous values somewhere (although I think you'd need to save ALL of their previous values if that were the case) or just reset them back to their defaults? That seems like a good idea and I might add that to my list of improvements for Tali2. I currently don't handle that particular situation, but I envision that it would be more user friendly for users that don't understand all of the internals.


Top
 Profile  
Reply with quote  
PostPosted: Fri Jun 23, 2023 12:41 am 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 851
SamCoVT wrote:
Is the idea here to be able to "clean up" after you've messed up a word a few times? Does the programmer keep running this version of forget until it can't find the word anymore in order to clear it all out?

Sometimes I'll write experimental words to flesh out an idea. Each iteration helps work out the details to go from idea to something functional. There may be a few words with the same name after this prototyping. Some may be smudged or none may be smudged. This version of FORGET doesn't care. The latest name in the compilation vocabulary which matches the parsed string gets forgotten, as well as everything defined after that word.
As for forgetting multiple words with the same name, I do this:
FORGET <SOME.NAME> >IN OFF
When FORGET can no longer find <SOME.NAME> , it aborts.

Quote:
How do you handle resetting the system deferred words? Do you save their previous values somewhere (although I think you'd need to save ALL of their previous values if that were the case) or just reset them back to their defaults? That seems like a good idea and I might add that to my list of improvements for Tali2. I currently don't handle that particular situation, but I envision that it would be more user friendly for users that don't understand all of the internals.

Only a deferred word defined in the kernel is reset to its default value and only if its vector is above the new HERE .
This seemed like a prudent thing to do since some deferred words are used by the interpreter.
Care is required if a deferred word defined outside the kernel has its vector forgotten; however, this will not keep the Forth system from working.

Fleet Forth has a table of system deferred words and their default vectors.
Code:
LABEL START.I&F
' EMIT      >BODY ,  0 ,
' EXPECT    >BODY ,  0 ,
' KEY       >BODY ,  0 ,
' I/C       >BODY ,  0 ,
LABEL END.IORESET
' INITIAL   >BODY ,  0 ,
' ERR       >BODY ,  0 ,
' VALID?    >BODY ,  0 ,
' DR/W      >BODY ,  0 ,
' RR/W      >BODY ,  0 ,
' T&S       >BODY ,  0 ,
LABEL END.FORGET

The labels mark the start of the table and two different endpoints. IORESET also uses this table to reset EMIT EXPECT KEY and I/C . I/C isn't an I/O word. The cold and warm start routines call IORESET so this will also reset the interpreter's Interpret/Compile word if I type the Run/Stop and Restore keys.
Code:
: IORESET  ( -- )
   0
   [ END.IORESET ] LITERAL
   LABEL (IORESET)
   [ START.I&F ] LITERAL
   DO
      DUP  I @ @  U<
      IF
         I 2@ !
      THEN
   4 +LOOP
   DROP
   CLRCHN ;

For each of those four deferred words IORESET compares its vector with zero. FORGET places the new value of HERE and the address pointed to by the label END.FORGET on the data stack and branches into IORESET at the point with the label (IORESET) .

Here is where the default vectors are stored in the table.
Code:
START.I&F
" TARGET  DUP @ @ OVER 2+ ! 4 +
  END.FORGET OVER 2+ U<
  HOST >IN !"
COUNT EVALUATE  TARGET
DROP
COMMENT:
;S THIS SYSTEM HAS EVALUATE.
   THE STRING IS REINTERPRETED AS
LONG AS THE COMPARISON IS FALSE.
   IN THIS SYSTEM ANY VALUE OF >IN
GREATER OR EQUAL TO THE SIZE OF THE
TEXT STREAM ENDS INTERPRETATION.
;COMMENT

You may have noticed there is one system deferred word missing from this table. PAUSE is not included in the table because FORGET calls SINGLE to switch off multitasking.
Code:
: SINGLE  ( -- )
   ['] NOOP IS PAUSE ;

The cold start routine also calls EMPTY to reset the dictionary to its latest "empty point." EMPTY branches into the body of FORGET at the label (FORGET) .
Code:
: EMPTY  ( -- )
   FORTH DEFINITIONS
   [ 'THERE ] LITERAL @
   DUP FENCE !
// BRANCH INTO FORGET
   BRANCH [ (FORGET) , ] -;

Since the cold start routine calls both IORESET and EMPTY , it may seem that some of the deferred words are getting reset twice. EMPTY and FORGET will only reset a deferred word if its vector has been forgotten. IORESET will always reset the first four deferred words in the table.


Top
 Profile  
Reply with quote  
PostPosted: Sun Jul 09, 2023 10:42 pm 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 851

Tracing by hand is useful when working out the logic of a yet to be written word. If a word is already defined, a TRACE word is useful to help find out where something is going wrong or verify that a word is working properly. A TRACE word is also useful when a hand trace is impractical, such as when testing coroutines.
I have on more than one occasion mentioned Blazin' Forth's TRACE word. I had a version of TRACE modified to work with Fleet Forth and have enhanced it to some degree. I finally got around to writing Fleet Forth's own TRACE word inspired by Blazin' Forth, but taking advantage of Fleet Forth's >FORTH and >ASSEM . Fleet Forth's TRACE functionality, while having a look and feel similar to Blazin' Forth's TRACE , is implemented differently.
>NEXT and NEXT> are used to patch and restore NEXT .
Blazin' Forth's word >NEXT patches NEXT roughly in the middle. Fleet Forth's >NEXT patches NEXT as close to the beginning as possible. There is a primitive, BRANCH , which jumps to NEXT 2+ to save two cycles because it leaves a one in the Y-register; therefore, the earliest point NEXT can be patched, without re-implementing BRANCH , is two bytes in.
Code:
CODE >NEXT ( ADR -- )
   // POINTS NEXT TO ADR
   $4C # LDA  NEXT 2+ STA
   0 ,X LDA  NEXT 3 + STA
   1 ,X LDA  NEXT 4 + STA
   POP JMP  END-CODE

This leaves the first instruction of NEXT intact and overwrites the next two. The other instructions in NEXT are intact.
NEXT> restores NEXT . It is included in the kernel so FORGET can restore NEXT .
Code:
   LABEL NEXT
   1 # LDY
   LABEL NEXT1
   IP )Y LDA  W 1+ STA  DEY
   LABEL NEXT2
   IP )Y LDA  W STA
   1 # LDA
   SEC  IP ADC  IP STA
   CS NOT IF
      W 1- JMP
   THEN
   IP 1+ INC
   W 1- JMP  END-CODE

CODE NEXT>  ( -- )  // RESTORE NEXT
   2 # LDY
   BEGIN
      NEXT2 ,Y LDA
      NEXT1 ,Y STA   DEY
   0< UNTIL
   NEXT JMP  END-CODE

Two variables, <IP and IP> , are used to check if IP is in the address range for a TRACE .
TRACE gets a name from the text stream and uses AFIND to find where that word ends.
AFIND takes an address, any address, and finds the nearest link fields below and above that address. It checks all vocabularies. It's reasonably fast because it uses a primitive to search a vocabulary for the nearest link fields above and below. Fleet Forth already uses this word for some components of SEE .
Code:
: TRACE  ( ++ )
   NEXT>
   ' AFIND NIP  IP> ! <IP !
   STEP >NEXT ;

STEP is a SUBR , a subroutine in Fleet Forth. It returns its PFA just like a VARIABLE . TRACE stores the beginning and end of a word in <IP and IP> respectively and patches NEXT to jump to the body of STEP .
Code:
SUBR STEP
   <IP LDA  IP CMP
   <IP 1+ LDA  IP 1+ SBC
   CS NOT IF
      IP> LDA  IP CMP
      IP> 1+ LDA  IP 1+ SBC
   CS NOT ELIF
   BEGIN  CS-SWAP
      IP )Y LDA  W 1+ STA
      NEXT 6 + JMP
   THEN
   >FORTH
   NEXT>  R@ HLD @ >R +PAD
   CR .STEP
   KEY DUP ASCII" {RUN/STOP}" =
   IF  DROP NOTRACE  THEN
   ASCII" {CONTROL-P}" =
   IF
      CON OFF
      BEGIN
         CR ." P? "  QUERY INTERPRET
         STATE @ 0= IF  ."  OK" THEN
         CON @
      UNTIL
   THEN
   -PAD  R> HLD !  RECURSE >NEXT
   >ASSEM
   INY
   0= UNTIL  END-CODE

The following tests if IP is in the range for a TRACE .
Code:
   <IP LDA  IP CMP
   <IP 1+ LDA  IP 1+ SBC
   CS NOT IF
      IP> LDA  IP CMP
      IP> 1+ LDA  IP 1+ SBC
   CS NOT ELIF

BEGIN CS-SWAP just marks the destination for a backward jump and places that information under the control flow data for the IF ELIF THEN structure.
When IP is not in range, NEXT needs to run to completion. Since the Y-register is not altered by the comparison, it still holds a value of one. The following does the work of the overwritten instructions in NEXT and jumps to the rest of NEXT .
Code:
      IP )Y LDA  W 1+ STA
      NEXT 6 + JMP

When IP is in range, the rest of STEP runs.
Code:
   >FORTH
   NEXT>  R@ HLD @ >R +PAD
   CR .STEP
   KEY DUP ASCII" {RUN/STOP}" =
   IF  DROP NOTRACE  THEN

STEP shifts to high level Forth. NEXT> switches off tracing. The top of the return stack is fetched. This will be the value of IP pushed there by the shift to high level Forth. The current value of HLD is pushed on the return stack and the gap between HERE and PAD is altered.
Code:
: +PAD
   $FF ?MEM
   [ ' PAD >BODY 4 + ] LITERAL C! ;

This allows tracing pictured numeric output words.
.STEP displays the information such as the name of the word about to be executed and the contents of the data stack, for this step. .STEP is a DEFERred word so it can be set to an appropriate display word for the given trace.
Code:
DEFER .STEP
: (.ST1)  ( IP -- )
   @ .NAME TAB .S ;
' (.ST1) IS .STEP
: (.ST2)  ( IP -- )
   (.ST1)  CR TAB .AS ;
: (.ST3)  ( IP -- )
   (.ST2)
   2R> .RS 2>R ;

(.ST2) would be useful when tracing a word which uses the auxiliary stack and (.ST3) would be useful when tracing coroutine words since it shows the contents of the return stack.
{RUN/STOP} and {CONTROL-P} are where the RUN/STOP and CONTROL-P keys are used on the Commodore 64. They do not display properly in a print dump.
If the RUN/STOP key is pressed, tracing stops. STEP calls NOTRACE which restores NEXT , restores the gap between HERE and PAD then QUITs. Before NOTRACE runs, NEXT is already restored, but NOTRACE can also be used at the command line.
Code:
: -PAD
   $55
   BRANCH [ ' +PAD >BODY 5 + , ] -;

: NOTRACE
   NEXT>  -PAD
   ." TRACING OFF"  QUIT -;

If the Control-P key combination is pressed, a mini quit loop runs. This is useful for checking other data or changing some of the items on the return stack.
Code:
   ASCII" {CONTROL-P}" =
   IF
      CON OFF
      BEGIN
         CR ." P? "  QUERY INTERPRET
         STATE @ 0= IF  ."  OK" THEN
         CON @
      UNTIL
   THEN

The mini quit loop runs until the word CONT is entered.
Code:
VARIABLE CON
: CONT   CON ON ;

If a word being traced is about to branch out to another word, the trace can be changed to that word.
An example is FORGET . It branches into IORESET . When the trace shows that the branch to IORESET is to be executed, I can press Control-P. While in the mini quit loop I can type the following to resume tracing where FORGET branches.
Code:
TRACE IORESET
CONT

Code:
   -PAD  R> HLD !  RECURSE >NEXT
   >ASSEM
   INY
   0= UNTIL  END-CODE

STEP restores the gap between HERE and PAD and restores the previous value of HLD .
Although RECURSE is used, STEP is not recursive. RECURSE compiles STEP which places its PFA on the data stack. This value is used to patch NEXT so tracing can continue. >ASSEM shifts the word to low level code. In the process of shifting to low level >ASSEM performs the function of EXIT by pulling the top value from the return stack and storing it in IP . At this point the Y-register has a value of zero. It is incremented followed by a jump to the address marked by BEGIN .
Code:
   BEGIN  CS-SWAP
      IP )Y LDA  W 1+ STA
      NEXT 6 + JMP


There is another tool I like from Blazin' Forth, WATCH . WATCH watches two consecutive memory locations (16 bits) such as a VARIABLE , VALUE or other location which is getting changed unexpectedly. Here is Fleet Forth's implementation.
Code:
: WATCH ( ADR -- )
   DUP @ EYE !
   // PATCH LOOK
   DUP LOOK 1+ !
   1+ LOOK 9 + !
   LOOK >NEXT ;

WATCH stores the original 16 bit value from the address on the stack in the variable EYE . It then patches the SUBRoutine LOOK with the address.
Code:
VARIABLE EYE
SUBR LOOK
   TRUE LDA EYE CMP
   0= IF
      TRUE LDA EYE 1+ CMP
      0= IF
         IP )Y LDA  W 1+ STA
         NEXT 6 + JMP
      THEN
   THEN
   INY
   BEGIN
      NEXT 7 + ,Y LDA
      NEXT 2+ ,Y STA  DEY
   0< UNTIL
   >FORTH
   .RS  TRUE
   ABORT" WATCHED MEMORY ALTERED" -;

The following tests if the memory locations have been altered.
Code:
   TRUE LDA EYE CMP
   0= IF
      TRUE LDA EYE 1+ CMP
      0= IF

At this point the Y-register has a value of one. If the memory under observation has not been changed, go back to NEXT the same as STEP
Code:
         IP )Y LDA  W 1+ STA
         NEXT 6 + JMP

If the memory under observation has been altered, NEXT must be restored before shifting to high level to avoid "infinite recursion."
Code:
   INY
   BEGIN
      NEXT 7 + ,Y LDA
      NEXT 2+ ,Y STA  DEY
   0< UNTIL

With NEXT restored, LOOK shifts to high level. The high level section displays the return stack with .RS and aborts with the message "WATCHED MEMORY ALTERED."
The session log of the test.
Code:
 OK
VARIABLE NONESUCH  OK
$1234 NONESUCH !  OK
NONESUCH WATCH  OK
: TEST
    1 NONESUCH +! ;  OK
: TEST2   TEST ;  OK
: TEST3   TEST2 ;  OK
TEST3
22464 LOOK
23515 TEST
23531 TEST2
23547 TEST3
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

TEST3
^^^^^
WATCHED MEMORY ALTERED



Top
 Profile  
Reply with quote  
PostPosted: Tue Jul 18, 2023 8:12 pm 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 851

I wish I had Fleet Forth's new TRACE back when I presented coroutines in Fleet Forth.
.RS was rewritten to support an improvement to (.ST3) which removes another artifact of the trace from the display of the return stack.
Code:
: .RS  ( -- )
   RP0 @ RP@
   SETWIDTH
   DO
      CR I 1+ @ DUP U.W
      2- AFIND 2PICK U< 0= OVER 0<>
      AND AND NIP ?DUP
      IF
         L>NAME ID.
      THEN
      2
   +LOOP -;
   ' CR >BODY  HERE 2- @ 2- !

.RS is one cell smaller and the first three words in .RS are now
Code:
RP0 @ RP@

which makes the improved (.ST3) slightly smaller.
Code:
: (.ST3)  ( IP -- )
   (.ST2)  RP0 @ RP@ 2+ 2+
   BRANCH [ ' .RS >BODY 6 + , ] -;

(.ST4) adds a few carriage returns for clarity.
Code:
: (.ST4)  ( IP -- )
   [ ' CR >BODY ] LITERAL >R
   CR (.ST2) CR  RP0 @ RP@ 6 +
   BRANCH [ ' .RS >BODY 6 + , ] -;

The following coroutine words were used as a test of TRACE using (.ST3) for the step display.
Code:
: CO1
   BEGIN  1+ DUP . CO  AGAIN ;
: CO2
   CR 0 CO1
   BEGIN
      1- DUP . CO DONE?
   UNTIL
   R> 2DROP ;

This is part of a session log from the trace.
Code:
' (.ST3) IS .STEP  OK
TRACE CO2  OK
' CO1 <IP !  OK
CO2
CR          EMPTY
            EMPTY
23511 CO2
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT


0           EMPTY
            EMPTY
23513 CO2
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

CO1             0
            EMPTY
23515 CO2
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

1+              0
            EMPTY
23489 CO1
23517 CO2
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

DUP             1
            EMPTY
23491 CO1
23517 CO2
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

.               1     1
            EMPTY
23493 CO1
23517 CO2
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT
1
CO              1
            EMPTY
23495 CO1
23517 CO2
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

1-              1
            EMPTY
23517 CO2
23497 CO1
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

DUP             0
            EMPTY
23519 CO2
23497 CO1
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

.               0     0
            EMPTY
23521 CO2
23497 CO1
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT
0
CO              0
            EMPTY
23523 CO2
23497 CO1
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

BRANCH          0
            EMPTY
23497 CO1
23525 CO2
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT
TRACING OFF
CONSOLE

Code:
TRACE CO2
sets <IP and IP> to the beginning and end of CO2 .
Code:
' CO1 <IP !
sets <IP to the beginning of CO1 .
CO1 is defined before CO2 ; therefore, both words will be traced. If any words had been defined between CO1 and CO2 they would also have been traced when executed.
CO1 and CO2 can be seen trading places in IP and on the top of the return stack every time CO is executed.

what the trace looks like using (.ST1) .
Code:
' (.ST1) IS .STEP  OK
TRACE CO2  OK
' CO1 <IP !  OK
CO2
CR          EMPTY

0           EMPTY
CO1             0
1+              0
DUP             1
.               1     1 1
CO              1
1-              1
DUP             0
.               0     0 0
CO              0
BRANCH          0 TRACING OFF
CONSOLE

An excerpt from the session log with comments added.
Code:
.               0     0   \ Word to be executed and data stack contents.
            EMPTY         \ Auxiliary stack contents.
23521 CO2                 \ Contents of IP.
23497 CO1                 \ These four lines show
 8557 (I/C)               \ the contents of
 8629 INTERPRET           \ the return stack.
 8724 QUIT                \
0                         \ Value displayed by . (dot)

Jut to be clear, that period is not an intrinsic part of the display when using (.ST3) . It is the Forth word dot.
Note that the word shown in the TRACE has not been executed yet. The display shows the contents of the stacks and IP prior to execution.
If CO2 were executed from the command line, the information shown during the trace is exactly what would be in IP and the stacks.
Unlike a hand trace, TRACE can trace a word which stores items on the return stack. Using (.ST3) for the step display word makes it easier to follow what is happening when tracing more complex coroutine words such as WITH-WORDS .
Code:
: WITH-WORDS  ( -- NFA )
   CONTEXT @
   AHEAD
   BEGIN
      DUP R@ 2>R L>NAME CO R>
   CS-SWAP THEN
      @ DUP 0= DONE? OR
   UNTIL
   R> 2DROP ;

A simple word using WITH-WORDS was defined to demonstrate tracing WITH-WORDS .
Code:
: #WORDS  ( -- N )
   0 WITH-WORDS  DROP 1+ ;

A testbed vocabulary was created and two words defined. This is an excerpt from the session log showing the trace of WITH-WORDS
Code:
TRACE WITH-WORDS  OK
#WORDS
CONTEXT       0
          EMPTY
16950 WITH-WORDS
23572 #WORDS
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

@             0  2590
          EMPTY
16952 WITH-WORDS
23572 #WORDS
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

BRANCH        0 23592
          EMPTY
16954 WITH-WORDS
23572 #WORDS
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

@             0 23592
          EMPTY
16970 WITH-WORDS
23572 #WORDS
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

DUP           0 23615
          EMPTY
16972 WITH-WORDS
23572 #WORDS
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

0=            0 23615 23615
          EMPTY
16974 WITH-WORDS
23572 #WORDS
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

DONE?         0 23615     0
          EMPTY
16976 WITH-WORDS
23572 #WORDS
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

OR            0 23615     0     0
          EMPTY
16978 WITH-WORDS
23572 #WORDS
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

?BRANCH       0 23615     0
          EMPTY
16980 WITH-WORDS
23572 #WORDS
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

DUP           0 23615
          EMPTY
16958 WITH-WORDS
23572 #WORDS
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

R@            0 23615 23615
          EMPTY
16960 WITH-WORDS
23572 #WORDS
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

2>R           0 23615 23615 23572
          EMPTY
16962 WITH-WORDS
23572 #WORDS
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

L>NAME        0 23615
          EMPTY
16964 WITH-WORDS
23572 #WORDS
23615 FOOBAR
23572 #WORDS
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

CO            0 23617
          EMPTY
16966 WITH-WORDS
23572 #WORDS
23615 FOOBAR
23572 #WORDS
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

R>            1
          EMPTY
16968 WITH-WORDS
23615 FOOBAR
23572 #WORDS
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

@             1 23615
          EMPTY
16970 WITH-WORDS
23572 #WORDS
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

DUP           1 23600
          EMPTY
16972 WITH-WORDS
23572 #WORDS
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

0=            1 23600 23600
          EMPTY
16974 WITH-WORDS
23572 #WORDS
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

DONE?         1 23600     0
          EMPTY
16976 WITH-WORDS
23572 #WORDS
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

OR            1 23600     0     0
          EMPTY
16978 WITH-WORDS
23572 #WORDS
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

?BRANCH       1 23600     0
          EMPTY
16980 WITH-WORDS
23572 #WORDS
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

DUP           1 23600
          EMPTY
16958 WITH-WORDS
23572 #WORDS
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

R@            1 23600 23600
          EMPTY
16960 WITH-WORDS
23572 #WORDS
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

2>R           1 23600 23600 23572
          EMPTY
16962 WITH-WORDS
23572 #WORDS
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

L>NAME        1 23600
          EMPTY
16964 WITH-WORDS
23572 #WORDS
23600 TESTBED
23572 #WORDS
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

CO            1 23602
          EMPTY
16966 WITH-WORDS
23572 #WORDS
23600 TESTBED
23572 #WORDS
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

R>            2
          EMPTY
16968 WITH-WORDS
23600 TESTBED
23572 #WORDS
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

@             2 23600
          EMPTY
16970 WITH-WORDS
23572 #WORDS
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

DUP           2     0
          EMPTY
16972 WITH-WORDS
23572 #WORDS
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

0=            2     0     0
          EMPTY
16974 WITH-WORDS
23572 #WORDS
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

DONE?         2     0 65535
          EMPTY
16976 WITH-WORDS
23572 #WORDS
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

OR            2     0 65535     0
          EMPTY
16978 WITH-WORDS
23572 #WORDS
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

?BRANCH       2     0 65535
          EMPTY
16980 WITH-WORDS
23572 #WORDS
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

R>            2     0
          EMPTY
16984 WITH-WORDS
23572 #WORDS
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

2DROP         2     0 23572
          EMPTY
16986 WITH-WORDS
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

EXIT          2
          EMPTY
16988 WITH-WORDS
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT
 OK
. 2  OK

And an excerpt showing the trace of #WORDS
Code:
TRACE #WORDS  OK
.S EMPTY  OK
#WORDS
0         EMPTY
          EMPTY
23568 #WORDS
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

WITH-WORDS              0
          EMPTY
23570 #WORDS
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

DROP          0 23617
          EMPTY
23572 #WORDS
16968 WITH-WORDS
23615 FOOBAR
23572 #WORDS
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

1+            0
          EMPTY
23574 #WORDS
16968 WITH-WORDS
23615 FOOBAR
23572 #WORDS
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

EXIT          1
          EMPTY
23576 #WORDS
16968 WITH-WORDS
23615 FOOBAR
23572 #WORDS
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

DROP          1 23602
          EMPTY
23572 #WORDS
16968 WITH-WORDS
23600 TESTBED
23572 #WORDS
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

1+            1
          EMPTY
23574 #WORDS
16968 WITH-WORDS
23600 TESTBED
23572 #WORDS
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

EXIT          2
          EMPTY
23576 #WORDS
16968 WITH-WORDS
23600 TESTBED
23572 #WORDS
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT
 OK
. 2  OK
CONSOLE


There is one caveat. Never trace STEP !
STEP will only run during a trace. Executing STEP returns its PFA ; therefore, the following is harmless.
Code:
TRACE STEP

However, addresses can be stored in <IP and IP> to trace a range of words. If STEP is within that range, when one of the words in that range is traced, STEP will try to trace itself recursively.

WITH-WORDS and RB are two coroutine words which don't strictly behave as coroutines. Rather than switching places with another coroutine, each of these is a creative use of the coroutine word CO . There is another Fleet Forth word, IN-NAME , which searches all the names of the CONTEXT VOCABULARY for a sub string. It uses the coroutine words AFTER-DROP and WITH-WORDS .
Code:
: AFTER-DROP  ( N -- )
   CO DROP ;

(.ST3) really shines when analyzing the execution of a word like IN-NAME or even Fleet Forth's version of EVALUATE .
The source.
Code:
: EVALUATE  ( ADR CNT -- )
   TIB #TIB @ 2>R
   LIT [ >MARK ] ENTER
   0 0 LIT
   [ ' LINELOAD >BODY DUP TRUE
     " LOAD 0" COUNT MATCH ?HUH
     + , ]
   ENTER
   2R>
   [ >RESOLVE ]
   #TIB ! (IS) TIB ;

The de-compilation.
Code:
SEE EVALUATE
EVALUATE
 16390  2761 TIB
 16392  2623 #TIB
 16394  3593 @
 16396  4768 2>R
 16398  3286 LIT 16416
 16402 13953 ENTER
 16404  3325 0
 16406  3325 0
 16408  3286 LIT 11109
 16412 13953 ENTER
 16414  4791 2R>
 16416  2623 #TIB
 16418  2230 !
 16420  7834 (IS)
 16422  2761 TIB
 16424  2480 EXIT
36
 OK

And the log of the test run.
Code:
 OK
: MESSAGE   ." NOTHING TO SEE HERE!" ;  OK
" CR MESSAGE CR TAB MESSAGE"  OK
.S 23609  OK
COUNT EVALUATE
NOTHING TO SEE HERE!
          NOTHING TO SEE HERE! OK
SEE .STEP
.STEP DEFERED TO (.ST3) 22251  OK
TRACE EVALUATE  OK
.S EMPTY  OK
PAD COUNT EVALUATE
TIB       23610    25
          EMPTY
16390 EVALUATE
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

#TIB      23610    25   679
          EMPTY
16392 EVALUATE
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

@         23610    25   679  2625
          EMPTY
16394 EVALUATE
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

2>R       23610    25   679    18
          EMPTY
16396 EVALUATE
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

LIT       23610    25
          EMPTY
16398 EVALUATE
   18
  679
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

ENTER     23610    25 16416
          EMPTY
16402 EVALUATE
   18
  679
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

#TIB      23610    25
          EMPTY
16416 EVALUATE
16404 EVALUATE
   18
  679
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

!         23610    25  2625
          EMPTY
16418 EVALUATE
16404 EVALUATE
   18
  679
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

(IS)      23610
          EMPTY
16420 EVALUATE
16404 EVALUATE
   18
  679
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

EXIT      EMPTY
          EMPTY
16424 EVALUATE
16404 EVALUATE
   18
  679
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

0         EMPTY
          EMPTY
16404 EVALUATE
   18
  679
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

0             0
          EMPTY
16406 EVALUATE
   18
  679
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

LIT           0     0
          EMPTY
16408 EVALUATE
   18
  679
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

ENTER         0     0 11109
          EMPTY
16412 EVALUATE
   18
  679
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

NOTHING TO SEE HERE!
          NOTHING TO SEE HERE!
2R>       EMPTY
          EMPTY
16414 EVALUATE
   18
  679
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

#TIB        679    18
          EMPTY
16416 EVALUATE
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

!           679    18  2625
          EMPTY
16418 EVALUATE
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

(IS)        679
          EMPTY
16420 EVALUATE
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT

EXIT      EMPTY
          EMPTY
16424 EVALUATE
 8557 (I/C)
 8629 INTERPRET
 8724 QUIT
 OK


In summary: Fleet Forth's TRACE is, in my opinion, a cleaner implementation of TRACE than the one from Blazin' Forth. The biggest improvement is also the simplest. The use of a DEFERred word to display the information for a step. A custom display word can be written to display the most relevant information for a given trace.


Top
 Profile  
Reply with quote  
PostPosted: Fri Jul 21, 2023 11:21 pm 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 851

I have noticed the occasional programming challenge on this forum. Here is a programming challenge in Forth.
While improving some feature in Fleet Forth, I wrote a test word and the results were as expected. The word begins like this:
Code:
: TEST  ( LO HI -- )
   1+ SWAP
   DO

and end like this:
Code:
   LOOP ;

Here is a session log showing interactive use of the test word. I typed TEST (as well as its two parameters) and I typed RESET . Everything else is Fleet Forth's response.
Code:
0 5 TEST
HEAD:   0
TAIL:   1^
        2^^
        3^^^
        4^
        5^^ OK
0 5 TEST
        0^^^
        1^
        2^^
        3^^^
        4^
        5^^ OK
0 19 TEST
        0^^^
        1^
        2^^
        3^^^
        4^
        5^^
        6^^^
        7^
        8^^
        9^^^
       10^
       11^^
       12^^^
       13^
       14^^
       15^^^
       16^
       17^^
       18^^^
       19^ OK
20 39 TEST
       20^^
       21^^^
       22^
       23^^
       24^^^
       25^
       26^^
       27^^^
       28^
       29^^
       30^^^
       31^
       32^^
       33^^^
       34^
       35^^
       36^^^
       37^
       38^^
       39^^^ OK
-10 10 TEST
      -10^
       -9^^
       -8^^^
       -7^
       -6^^
       -5^^^
       -4^
       -3^^
       -2^^^
       -1^
        0^^
        1^^^
        2^
        3^^
        4^^^
        5^
        6^^
        7^^^
        8^
        9^^
       10^^^ OK
RESET  OK
-10 10 TEST
HEAD: -10
TAIL:  -9^
       -8^^
       -7^^^
       -6^
       -5^^
       -4^^^
       -3^
       -2^^
       -1^^^
        0^
        1^^
        2^^^
        3^
        4^^
        5^^^
        6^
        7^^
        8^^^
        9^
       10^^ OK
0 5 TEST
        0^^^
        1^
        2^^
        3^^^
        4^
        5^^ OK
RESET  OK
0 5 TEST
HEAD:   0
TAIL:   1^
        2^^
        3^^^
        4^
        5^^ OK

Can any of my fellow Forthwrites deduce a likely body for the DO LOOP in TEST ?


Top
 Profile  
Reply with quote  
PostPosted: Sat Jul 22, 2023 6:33 am 
Online
User avatar

Joined: Sun Jun 30, 2013 10:26 pm
Posts: 1927
Location: Sacramento, CA, USA
Inside the DO loop you appear to have I . and a variable or other persistent cell that you're using for the caret count and resetting to 1 if it hits 4 after incrementing. RESET seems to return the variable to 0, which also triggers the HEAD: TAIL: response when TEST sees it. That's about as far as my feeble limits can carry me at present. Am I nipping at the wrong tire?

_________________
Got a kilobyte lying fallow in your 65xx's memory map? Sprinkle some VTL02C on it and see how it grows on you!

Mike B. (about me) (learning how to github)


Top
 Profile  
Reply with quote  
PostPosted: Sun Jul 23, 2023 8:49 pm 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 851

First, thank you for your response.
I stated in my post that I wrote the test word while improving some feature in Fleet Forth; therefore, it's going to be more generic than just using a counter.
Not too long ago I read about how coroutines do not play well with DO LOOPs. Consider two coroutine words which trade places with each other as the currently running word, the norm for coroutines. If one of the coroutine words has a BEGIN loop, it can launch the other coroutine word before entering the BEGIN loop and can remove the other coroutine word after leaving the BEGIN loop. When one of the coroutine words has a DO LOOP , anything which alters the return stack within the body of the loop has to restore it before the next iteration or before the loop terminates. Parameters can not be passed into the loop on the return stack. Not portably. To launch the second coroutine word in a loop requires knowing if this is the first pass through the loop. A VARIABLE can be used for this, but the variable would need reset before entering the loop to insure the coroutine word is launched. The difficult problem is removing the other coroutine word from the return stack on the last pass through the loop.
I realized a halting colon definition does not have this problem. Neither does Leo Brodie's DOER/MAKE .
The source for halting colon definitions was improved to add resetting a halting colon definition.
Code:
: H:
   :  ( -- ADR TRUE )
      HERE 2+ ,  [ HERE 2+ >A ]
   DOES>  ( -- )
      @ >R ;
: (HALT)  ( R: ADR -- )
   R@ 2+  R> @ >BODY ! ;
: ?H: ( CFA -- )
   @ [ A> ] LITERAL <>
   ABORT" NON HALTING WORD" ;
: HALT   LATEST NAME> ?H:
   COMPILE (HALT) [COMPILE] RECURSE
   ; IMMEDIATE
: HRESET  ( CFA -- )
   DUP ?H: >BODY DUP 2+ SWAP! ;

Here is the source for TEST and RESET
Code:
: TEST  ( LO HI -- )
   1+ SWAP
   DO
     STREAM  I 3 .R  ARROWS
   LOOP ;
: RESET
   ['] ARROWS HRESET
   ['] STREAM HRESET ;

STREAM and ARROWS are both halting colon definitions.
Code:
H: STREAM  ( N -- )
   CR ." HEAD: " HALT
   CR ." TAIL: "
   BEGIN
      HALT
      CR 6 SPACES
   AGAIN -;
H: ARROWS  ( -- )
   BEGIN
      HALT  ." ^"
      HALT  ." ^^"
      HALT  ." ^^^"
   AGAIN -;

As I mentioned in the post on halting colon definitions, HALT is like an EXIT which also tells the halting colon definition where to resume the next time it is run.
A halting colon definition isn't as efficient as a coroutine word. One cell is required to compile CO. HALT is an immediate word which compiles two cells. A halting colon definition can be used in places where a coroutine word can't. On the other hand, coroutines can be used where a halting colon definition can't. One example is RB . A word which calls RB can, without problems, call another word which calls RB because RB saves its state on the return stack. A halting colon definition saves its state (where it should resume) in its own parameter field.

I also mentioned in that post:

JimBoyd wrote:

If round robin multitasking will not be used, to avoid splitting the stacks into smaller areas for each task, PAUSE can be vectored to a 'run to completion' word. There are two caveats. Any word PAUSE is vectored to must leave the stacks as they were when it began. It, and all the words it executes, must not execute PAUSE to avoid runaway recursion.


If this run to completion word will not be run from the command line, SINGLE can be used to set PAUSE to NOOP , a no-op, at the beginning of the word and PAUSE can be restored at the end.
Example run to completion word with halting colon definitions and a normal colon definition.
Code:
H: HCD1   <DO SOME STUFF> ;
H: HCD2   <DO OTHER STUFF> ;
H: HCD3   <DO DIFFERENT STUFF> ;
: NCD  <DO ONE THING> ;
: BACKGROUND
   SINGLE
   HCD1 HCD2 HCD3 NCD
   LIT RECURSE IS PAUSE ;
' BACKGROUND IS PAUSE

Actually, running BACKGROUND from the command line would run it and install it as the (simulated) multitasker.
NCD , the Normal Colon Definition, does the same thing each time BACKGROUND runs.


Top
 Profile  
Reply with quote  
PostPosted: Thu Jul 27, 2023 8:34 pm 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 851

A few posts back I wrote about Fleet Forth's new TRACE .
I like TRACE and my STC version of Fleet Forth will support it and other tools, such as WATCH , which depend on patching NEXT .
The STC version of Fleet Forth is on a back burner for now. I don't have the uninterrupted spans of free time to work out some of the technical details. When I resume, there will be two versions of the STC version of Fleet Forth. One will have primitives which end with RTS . The other will have primitives which end with a jump to NEXT . This NEXT will just be RTS with two additional bytes. Other than that, both versions will be identical and built from the same source.
This will allow me to have a version with a NEXT which can be patched with a jump to a trace routine or other diagnostic tool and still have an STC version running at top speed. A word which is not working as it should, for whatever reason, can be compiled and traced on the slower STC Fleet Forth. This will also allow supporting WATCH to keep an eye on a memory cell which gets changed unexpectedly.


Top
 Profile  
Reply with quote  
PostPosted: Tue Aug 01, 2023 7:15 pm 
Offline

Joined: Sun May 13, 2018 5:49 pm
Posts: 247
JimBoyd wrote:

... there will be two versions of the STC version of Fleet Forth. One will have primitives which end with RTS . The other will have primitives which end with a jump to NEXT . This NEXT will just be RTS with two additional bytes. Other than that, both versions will be identical and built from the same source.
This will allow me to have a version with a NEXT which can be patched with a jump to a trace routine or other diagnostic tool and still have an STC version running at top speed. A word which is not working as it should, for whatever reason, can be compiled and traced on the slower STC Fleet Forth. This will also allow supporting WATCH to keep an eye on a memory cell which gets changed unexpectedly.
That's a neat idea. I like how that leaves the diagnostic routine up to the end user. Does the diagnostic routine, that would be patched into NEXT, have to be written in assembly so it doesn't try to diagnose itself? I normally end up running my STC routines in a simulator when I need to debug at a low level, but your method would work on real hardware as well, which I could see being useful for debugging "driver" words that access hardware.


Top
 Profile  
Reply with quote  
PostPosted: Tue Aug 01, 2023 9:46 pm 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 851
SamCoVT wrote:
Does the diagnostic routine, that would be patched into NEXT, have to be written in assembly so it doesn't try to diagnose itself?

Yes. In an ITC Forth, when NEXT is patched, the jump has to go to low level code. This low level code can not shift to high level Forth for the test without causing infinite recursion.
The same is true for an STC Forth where words no longer end with an RTS , but end with a jump to a dummy NEXT , a routine with an RTS followed by two unused bytes.
The diagnostic routine will also start out as assembly for the speed. 64Forth by T. J. Zimmer had a TRACE which worked differently. It's TRACE would begin tracing the word to be traced immediately. With the version of TRACE I presented, the word to be traced doesn't get traced until it is executed. The word to be traced can be traced interactively or in actual use in another word. Until this word is executed, the low level portion of the diagnostic routine does nothing out of the ordinary, just test if a condition has been met and continue.
Only when the specified condition has been met, such as IP , or the return address for STC Forth, within range for a TRACE , will the high level portion run. The rest of the time the slowdown should be kept to a minimum.
Also notice when STEP shifts to high level, the value in IP changes from an address within the traced word to an address within the high level portion of STEP . The word being traced might be called when the high level portion of STEP runs, so the first thing it does is switch tracing off with NEXT> .
Here is STEP .
Code:
SUBR STEP
   <IP LDA  IP CMP
   <IP 1+ LDA  IP 1+ SBC
   CS NOT IF
      IP> LDA  IP CMP
      IP> 1+ LDA  IP 1+ SBC
   CS NOT ELIF
   BEGIN  CS-SWAP
      IP )Y LDA  W 1+ STA
      NEXT 6 + JMP
   THEN
   >FORTH
   NEXT>  R@ HLD @ >R +PAD
   CR .STEP
   KEY DUP ASCII" {RUN/STOP}" =
   IF  DROP NOTRACE  THEN
   ASCII" {CONTROL-P}" =
   IF
      CON OFF
      BEGIN
         CR ." P? "  QUERY INTERPRET
         STATE @ 0= IF  ."  OK" THEN
         CON @
      UNTIL
   THEN
   -PAD  R> HLD !  RECURSE >NEXT
   >ASSEM
   INY
   0= UNTIL  END-CODE

LOOK , used by WATCH , checks for a changed memory location, rather than an address in IP ; therefore, it restores NEXT before the shift to high level.
Code:
SUBR LOOK
   TRUE LDA EYE CMP
   0= IF
      TRUE LDA EYE 1+ CMP
      0= IF
         IP )Y LDA  W 1+ STA
         NEXT 6 + JMP
      THEN
   THEN
   INY
   BEGIN
      NEXT 7 + ,Y LDA
      NEXT 2+ ,Y STA  DEY
   0< UNTIL
   >FORTH
   .RS  TRUE
   ABORT" WATCHED MEMORY ALTERED" -;

The purpose of WATCH is to look for a pair of memory locations, such as a VARIABLE or VALUE , which is getting changed unexpectedly. It will display the contents of the return stack when the memory contents change and abort with the message "WATCHED MEMORY ALTERED." The pair of memory locations under observation may not get altered right away, so it's a good idea if the portion of LOOK which runs the test is as fast as possible.
The source for TRACE and WATCH.


Top
 Profile  
Reply with quote  
PostPosted: Tue Aug 01, 2023 10:43 pm 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 851
JimBoyd wrote:
.STEP is a DEFERred word so it can be set to an appropriate display word for the given trace.
Code:
DEFER .STEP
: (.ST1)  ( IP -- )
   @ .NAME TAB .S ;
' (.ST1) IS .STEP
: (.ST2)  ( IP -- )
   (.ST1)  CR TAB .AS ;
: (.ST3)  ( IP -- )
   (.ST2)
   2R> .RS 2>R ;

(.ST2) would be useful when tracing a word which uses the auxiliary stack and (.ST3) would be useful when tracing coroutine words since it shows the contents of the return stack.
If the auxiliary and return stacks do not need to be shown but a halting colon definition or a word with branches is to be traced, (.ST5) could be used. It shows the value of IP with the step data shown by (.ST1) .
Code:
: (.ST5)  ( IP -- )
   DUP U. (.ST1) ;

Tracing TEST from the example with halting colon definitions.
Code:
RESET  OK
0 5 TEST
23744 1+      0     5
23746 SWAP              0     6
23748 (DO)              6     0
23752 STREAM        EMPTY
HEAD:
23754 I   EMPTY
23756 3       0
23758 .R      0     3   0
23760 ARROWS        EMPTY
23762 (LOOP)        EMPTY
23752 STREAM        EMPTY
TAIL:
23754 I   EMPTY
23756 3       1
23758 .R      1     3   1
23760 ARROWS        EMPTY ^
23762 (LOOP)        EMPTY
23752 STREAM        EMPTY
     
23754 I   EMPTY
23756 3       2
23758 .R      2     3   2
23760 ARROWS        EMPTY ^^
23762 (LOOP)        EMPTY
23752 STREAM        EMPTY
     
23754 I   EMPTY
23756 3       3
23758 .R      3     3   3
23760 ARROWS        EMPTY ^^^
23762 (LOOP)        EMPTY
23752 STREAM        EMPTY
     
23754 I   EMPTY
23756 3       4
23758 .R      4     3   4
23760 ARROWS        EMPTY ^
23762 (LOOP)        EMPTY
23752 STREAM        EMPTY
     
23754 I   EMPTY
23756 3       5
23758 .R      5     3   5
23760 ARROWS        EMPTY ^^
23762 (LOOP)        EMPTY
23766 EXIT          EMPTY  OK

The stack display looks a little ragged because (.ST5) calls (.ST1) which uses TAB . The tab size can be increased or something like the technique in (.ST6) can be used.
Code:
: (.ST6)  ( IP -- )
   DUP U. @ .NAME SPACE
   16 CHARS @ - SPACES .S ;

CHARS is a variable. It's value is the number of characters displayed since the last carriage return or page. In Fleet Forth, if the parameter for SPACES is zero or negative then SPACES will display zero spaces.
Code:
' (.ST6) IS .STEP  OK
RESET  OK
0 5 TEST
23744 1+            0     5
23746 SWAP          0     6
23748 (DO)          6     0
23752 STREAM    EMPTY
HEAD:
23754 I         EMPTY
23756 3             0
23758 .R            0     3   0
23760 ARROWS    EMPTY
23762 (LOOP)    EMPTY
23752 STREAM    EMPTY
TAIL:
23754 I         EMPTY
23756 3             1
23758 .R            1     3   1
23760 ARROWS    EMPTY ^
23762 (LOOP)    EMPTY
23752 STREAM    EMPTY
     
23754 I         EMPTY
23756 3             2
23758 .R            2     3   2
23760 ARROWS    EMPTY ^^
23762 (LOOP)    EMPTY
23752 STREAM    EMPTY
     
23754 I         EMPTY
23756 3             3
23758 .R            3     3   3
23760 ARROWS    EMPTY ^^^
23762 (LOOP)    EMPTY
23752 STREAM    EMPTY
     
23754 I         EMPTY
23756 3             4
23758 .R            4     3   4
23760 ARROWS    EMPTY ^
23762 (LOOP)    EMPTY
23752 STREAM    EMPTY
     
23754 I         EMPTY
23756 3             5
23758 .R            5     3   5
23760 ARROWS    EMPTY ^^
23762 (LOOP)    EMPTY
23766 EXIT      EMPTY  OK

The disassembly for TEST .
Code:
SEE TEST
TEST
 23744  4873 1+
 23746  5162 SWAP
 23748  2317 (DO) 23766
 23752 23642 STREAM
 23754  2425 I
 23756  2699 3
 23758  7570 .R
 23760 23698 ARROWS
 23762  2197 (LOOP) 23752
 23766  2480 EXIT
24
 OK



Top
 Profile  
Reply with quote  
PostPosted: Tue Aug 15, 2023 11:46 pm 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 851

Forth source kept in blocks (called screens) is usually formatted as 16 lines of 64 characters. I've heard of one Forth system which showed a screen as 25 lines of 40 characters. To the interpreter, a screen is just a text stream of 1024 bytes.
The Commodore 64's 40 column screen makes it tempting to go with the 25 line by 40 character option (even with the inability to edit those last 24 bytes) but source from such a system would not display properly on a system displaying a screen as 16 lines of 64 characters and vice versa.
I posted the following about five years ago.
JimBoyd wrote:
Garth suggested an editing keyhole into the source.
I should be able to modify the editor to show a 36 column window into the block so I can see and edit just part of a source block without line wrapping on the C64 screen. Since it will be easier to add comments, there won't be so much wasted space.

I kept putting it off, until now. Here is my initial attempt.
First, I should mention that Fleet Forth's EXPECT uses the Commodore 64's screen editor. This means that I can cursor up to a line of text on the screen and press the return key. The line of text will be received by EXPECT just as if it had been typed.
SCR is a variable in the EDITOR vocabulary. It is set by LIST and used by all the editor words for the current block to edit.
The words 0: through F: in the EDITOR vocabulary are the child words of XED . 0: reads the entire text input buffer and copies the first 64 bytes (after 0: and a space) to line zero of the current block.
There are some new versions of LIST in the FORTH vocabulary and L in the EDITOR vocabulary.
LIST and L display the entire screen.
<LIST and <L display the first 36 characters of each line.
LIST> and L> display the last 36 characters of each line.
Each version of L fetches the current screen (for editing) from the variable SCR and calls its respective version of LIST .
Code:
: L   SCR @ LIST ;
: <L   SCR @ <LIST ;
: L>   SCR @ LIST> ;


I added more comments to one screen for the assembler. This is what each version of LIST shows.
Code:
0 FH LIST
SCR# 2056  1 8
0: // SYSTEM CONSTANTS
1:
2: $85 CONSTANT N   $8D CONSTANT XSAVE  // THESE CONSTANTS WERE
3: $8E CONSTANT UP  $FB CONSTANT IP     // SET BY DESIGN
4: $FE CONSTANT W                       //
5:       ' NOOP @ CONSTANT NEXT         // THESE CONSTANTS DEPENDED
6: ' FILL @ 3 + @ CONSTANT SETUP        // ON THE LOCATIONS OF
7:      ' 2DROP @ CONSTANT POPTWO       // VARIOUS PRIMITIVES
8:      POPTWO 2+ CONSTANT POP          // IN THE SYSTEM
9:       ' 1 @ 2+ CONSTANT APUSH        //
A:       APUSH 2+ CONSTANT AYPUSH       //
B:      AYPUSH 2+ CONSTANT AYPUT        //
C:
D:
E:
F:
 OK
<L
SCR# 2056  1 8
0[ // SYSTEM CONSTANTS
1[
2[ $85 CONSTANT N   $8D CONSTANT XSAVE
3[ $8E CONSTANT UP  $FB CONSTANT IP
4[ $FE CONSTANT W
5[       ' NOOP @ CONSTANT NEXT
6[ ' FILL @ 3 + @ CONSTANT SETUP
7[      ' 2DROP @ CONSTANT POPTWO
8[      POPTWO 2+ CONSTANT POP
9[       ' 1 @ 2+ CONSTANT APUSH
A[       APUSH 2+ CONSTANT AYPUSH
B[      AYPUSH 2+ CONSTANT AYPUT
C[
D[
E[
F[
 OK
L>
SCR# 2056  1 8
0]
1]
2] T XSAVE  // THESE CONSTANTS WERE
3] T IP     // SET BY DESIGN
4]          //
5]          // THESE CONSTANTS DEPENDED
6] P        // ON THE LOCATIONS OF
7] WO       // VARIOUS PRIMITIVES
8]          // IN THE SYSTEM
9] H        //
A] SH       //
B] T        //
C]
D]
E]
F]
 OK

This is what it looks like on the Commodore 64.
Attachment:
original LIST.png
original LIST.png [ 23.09 KiB | Viewed 14150 times ]

Attachment:
begin LIST.png
begin LIST.png [ 22.75 KiB | Viewed 14150 times ]

Attachment:
end LIST.png
end LIST.png [ 20.29 KiB | Viewed 14150 times ]

Notice each version of LIST appends a different character to the line number. This is to support the new screen editing words.
The words 0[ through F[ only alter the first 36 characters of a line and the words 0] through F] only alter the last 36 characters of a line.
The original version of LIST which shows everything on each line is acceptable and the version which shows the first 36 characters of each line is acceptable. The version which shows the last 36 characters on a line doesn't show enough of the beginning of each line. It's almost like editing the end of the line blind. The block could initially be displayed with the original LIST and the source typed in using the screen editing words 0: through F: . The other versions of LIST and the screen editing words 0[ through F[ and 0] through F] could be used to edit the existing source.


Top
 Profile  
Reply with quote  
PostPosted: Sun Aug 20, 2023 8:50 pm 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 851

I'm not satisfied with the editor extension I mentioned in my last post.
My current screen editor, one I've used for years, uses a modified LIST which displays all the line numbers as hexadecimal numbers, regardless of base, with a colon appended to the number. These are editor words which parse the rest of the line where they occur and copy that text to the corresponding line of the current screen, the block number stored in SCR .
Code:
SCR# 2056  1 8
0: // SYSTEM CONSTANTS
1:
2: $85 CONSTANT N   $8D CONSTANT XSAVE
3: $8E CONSTANT UP  $FB CONSTANT IP
4: $FE CONSTANT W
5:       ' NOOP @ CONSTANT NEXT
6: ' FILL @ 3 + @ CONSTANT SETUP
7:      ' 2DROP @ CONSTANT POPTWO
8:      POPTWO 2+ CONSTANT POP
9:       ' 1 @ 2+ CONSTANT APUSH
A:       APUSH 2+ CONSTANT AYPUSH
B:      AYPUSH 2+ CONSTANT AYPUT
C:
D:
E:
F:

Fleet Forth's EXPECT uses the Commodore 64's screen editor. I can scroll up to a line, alter that line or not and press the return key. That line will be what EXPECT returns.
A very plain and simple screen editor. It does have it's limitations. This is what it looks like if a line (including line number, colon and space) is longer than (or even equal to) 40 characters.
Code:
SCR# 2083  1 35
0: // S  DISPLAYS ALL OCCURRENCES OF TEX
T FROM SCR TO BLK#-1
1: : S  ( BLK# -- )
2:    >FBUF  SCR @ OVER U< IF
3:       SCR @ TUCK
4:       DO
5:          I SCR ! R# OFF
6:          BEGIN
7:             SEEK
8:          WHILE
9:             POINT SCR @ U.
A:          REPEAT
B:       LOOP
C:    THEN
D:    SCR ! ;
E:
F:

Sure, the editor words will still work because the Commodore 64 links the two physical lines for the comment line into one logical line. If there are too many long lines the top of the listing will scroll off the screen.
LIST includes ?DONE so the listing can be stopped before all the lines are displayed. This makes it possible to edit the top lines if there are many long lines.
There is another problem with long lines. If the long lines are in a definition, they make the source more difficult to follow. That line wrapping makes it harder to notice the source indentation at a glance.

As I said, my current screen editor is the one I've been using for years. I remember one forum member mentioning although my source is in blocks, it has a very vertical format with a great deal of white space to the right. The Commodore 64's 40 column screen is the reason.

In spite of my dissatisfaction with my latest editor extension mentioned in the previous post, I like the idea of a secondary set of screen editor words which use an edit window into the text; however, I would like an edit window which can scroll. I'd also like to keep the editor extension simple. I do not at this time want to duplicate the functionality of 64Forth's screen editor.

I did find a solution which seems satisfying. Only time and use will tell. A modification to EXPECT enhances it further.


Top
 Profile  
Reply with quote  
PostPosted: Sun Aug 20, 2023 10:03 pm 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 851

I may have given Fleet Forth's EXPECT nonstandard behavior.
The Forth-83 Standard states the following concerning EXPECT .
Code:
          9.5.2     EXPECT

          Control characters may be processed to allow system dependent
          editing of the characters prior to receipt.  Therefore, a
          Standard Program may not anticipated that control characters can
          be received.

It doesn't seem to say anything about intercepting function keys; however, I thought such a capability would be useful. In Fleet Forth, EXPECT is a DEFERred word. It is normally set to (EXPECT) .
The high level portion of (EXPECT) reads the keyboard and echoes the characters back to the screen. If a carriage return is detected in the keyboard buffer, it is not read. Control flow passes to the low level portion of (EXPECT) which uses the Commodore 64's screen editor, kernal routine CHRIN , $FFCF.
The word which echoes keys to the screen is DEMIT . It is a primitive which uses the same C64 kernal routines as (EMIT) . As the leading 'D' in the name implies, it is normally used to send a character to a disk drive, but can also send a character to the screen. The difference with (EMIT) , the vector for EMIT , is DEMIT does not update the variables CHARS and LINES . These variables are used for 'print formatting'. They keep track of the number of characters emitted since the last carriage return or page and the number of lines since the last page.
DEMIT was replaced with the following:
Code:
         7 OVER #133 - U<   \ is this not a function key?
         IF                 \ if it is not
            DEMIT           \ echo to the screen as usual
         ELSE               \ else
            #63 +  4 /MOD SWAP 2* +   \ convert PETSCII code
            " F0" TUCK 2+ C!          \ insert digit character into string
            CONTEXT @ (FIND)          \ and search CONTEXT vocabulary
            IF  DUP EXECUTE  THEN     \ if found, execute
            DROP                       
         THEN

The search of the CONTEXT vocabulary will include the parent vocabularies.

The Commodore 64's function keys are a little different. There are only four actual function keys: F1, F3, F5 and F7. F2 is obtained with shift-F1, F4 with shift-F3 and so on.
The PETSCII codes are as follows:
Code:
F1  133
F3  134
F5  135
F7  136
F2  137
F4  138
F6  139
F8  140

in function key order:
Code:
F1  133
F2  137
F3  134
F4  138
F5  135
F6  139
F7  136
F8  140

This line:
Code:
            #63 +  4 /MOD SWAP 2* +   \ convert PETSCII code

performs the following conversion:
Code:
133 => 49
137 => 50
134 => 51
138 => 52
135 => 53
139 => 54
136 => 55
140 => 56

An eight byte look-up table could have been used, but this was smaller.

If function key F1 is pressed and there is no word by that name, it's the same as if the function key was not pressed. If the name exists, it is executed.

I've notice the function key names could be confused with hexadecimal numbers. I've gotten too used to using $F1 for a hexadecimal number. I could change this line:
Code:
   " F0" TUCK 2+ C!          \ insert digit character into string

to this:
Code:
   " FKEY0" TUCK 5 + C!          \ insert digit character into string

Or something similar.
Code:
   " FKEY0" [ HERE 1- ] SWAP LITERAL C! \ insert digit character into string

or even
Code:
   [ HERE 2+ ] $FFFF    \ compile a literal
   C!
   " FKEY0"  [ HERE 1- SWAP! ]  \ patch literal with address of
                                \ last character in string.

To use function key F1 with this change there would need to be a word with the name FKEY1 .


Top
 Profile  
Reply with quote  
PostPosted: Mon Aug 21, 2023 12:49 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8428
Location: Southern California
I've only used code page 437, which clearly the C64 doesn't use.  I haven't thought about how I'd use the C64 character set (including function keys), but CP437 is sure nice for the special characters, drawing boxes, and the Greek letters we use all the time in engineering (since that's what I'm always using Forth for).  The Epson dot-matrix impact printers, which Epson is still making about nine models of, use the same character set, so I use it with the workbench computer too, although they don't display correctly in its small LCD unless I make custom characters in the LCD, which I've done before but is kind of a lot of overhead.  My exception to the 437-only use is my HP-71B hand-held computer which defaults to the ROMAN8 set which is kind of similar but has different byte values for the special characters; but after the HP Thinkjet printer I was using with it went down and I started using the Epson printer, I loaded the CP437 character set into the HP71 computer so things would display correctly in its LCD.  Here are a couple of examples of my uses of the box-drawing characters:
Attachment:
MultiEdit2.jpg
MultiEdit2.jpg [ 72.02 KiB | Viewed 14070 times ]

Attachment:
MultiEdit3a.gif
MultiEdit3a.gif [ 74.57 KiB | Viewed 14070 times ]

(although that last one was for a PIC16 project, not 6502).  CP437 also lets me have Forth word names like ±½°C or 10KΩ or put Φ2 in the comments.

_________________
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 335 posts ]  Go to page Previous  1 ... 18, 19, 20, 21, 22, 23  Next

All times are UTC


Who is online

Users browsing this forum: No registered users and 12 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to: