The coolest words

Topics relating to various Forth models on the 6502, 65816, and related microprocessors and microcontrollers.
JimBoyd
Posts: 931
Joined: 05 May 2017

Re: The coolest words

Post by JimBoyd »

GARTHWILSON wrote:

The CO word above defined as a secondary would have very limited usefulness if any though, because your return address gets changed. I have RSWAP defined in my '816 Forth as:

Code: Select all

       HEADER "RSWAP", NOT_IMMEDIATE   ; Swaps the two top return-stack cells.
RSWAP: PRIMITIVE
       LDA   1,S
       STA   N
       LDA   3,S
       STA   1,S
       LDA   N
       STA   3,S
       GO_NEXT
 ;-------------------
which executes much, much faster and doesn't mess up where it comes back to for the next instruction.

The purpose of CO is to swap the return addresses. It's a co-routine word. If I had RSWAP in my system, I would define CO like this:

Code: Select all

: CO
   RSWAP ;

If I wanted to make CO a primitive, since Fleet Forth is an ITC Forth, I would have to write it to swap the top of the return stack with IP.
Here are two words used for a trivial example:

Code: Select all

: COROUTINE1 
   BEGIN
      CR ." ROUTINE ONE HERE!"
      CO DONE?
   UNTIL ;
: COROUTINE2
   BEGIN
      CR ." SECOND COROUTINE!"
      CO DONE?
   UNTIL ;

And a portion of the system log showing their execution:

Code: Select all

' COROUTINE1 >BODY >R COROUTINE2 
SECOND COROUTINE!
SECOND COROUTINE!
ROUTINE ONE HERE!
SECOND COROUTINE!
ROUTINE ONE HERE!
SECOND COROUTINE!
ROUTINE ONE HERE!
SECOND COROUTINE!
ROUTINE ONE HERE!

or they can be launched another way:

Code: Select all

: ROUTINES
   COROUTINE1  COROUTINE2 ;

And a sample run:

Code: Select all

ROUTINES 
ROUTINE ONE HERE!
SECOND COROUTINE!
SECOND COROUTINE!
ROUTINE ONE HERE!
SECOND COROUTINE!
ROUTINE ONE HERE!
SECOND COROUTINE!
ROUTINE ONE HERE!
SECOND COROUTINE!
ROUTINE ONE HERE!

A trivial example to be sure.
Here is something useful. Fleet Forth has the word RB ( restore base ). Among other places, it is used in the definition of NUMBER? .

Code: Select all

: RB  ( -- )
   BASE @ R> 2>R  CO
   R> BASE ! ;

The behavior of a word like RB is fairly straight forward. perform the part before CO right now. Perform the part after CO when this word's caller exits.
Here is a walkthrough of how RB works:

Code: Select all

   BASE @ R> 2>R

The value in BASE is fetched then the return address of RB's caller is moved from the return stack to the data stack. Since 2>R preserves the order of the values moved to the return stack, after 2>R executes, the value of BASE will be tucked under the return address of RB's caller on the return stack.

Code: Select all

: CO   2R> SWAP 2>R ;

When at the start of CO , there are three values of immediate interest on the return stack. The value of BASE , the return address of RB's caller, and the return address of RB .

Code: Select all

   2R> SWAP 2>R ;

CO swaps the two return addresses. When CO exits, it exits into RB's caller at the point just after RB .

When RB's caller finishes and exits, it exits into RB just after CO .

Code: Select all

   R> BASE ! ;

The original value of BASE saved to the return stack by RB is now the top value of the return stack. It is pulled off the return stack and stored back in BASE . RB exits into the word that called RB's caller.

Another useful place for CO is in the definition of WITH-WORDS , a handy utility word in Fleet Forth.
WITH-WORDS works like this:

Code: Select all

: NAMEX  <DO THIS ONCE> WITH-WORDS <DO THIS ONCE FOR EACH WORD IN THE CONTEXT VOCABULARY> ;

The thread of Forth after WITH-WORDS gets executed once for each word in the context vocabulary. The only values on the data stack when that thread executes are whatever was on the data stack prior to the execution of WITH-WORDS and the NFA of the word for which this thread is executed.
User avatar
barrym95838
Posts: 2056
Joined: 30 Jun 2013
Location: Sacramento, CA, USA

Re: The coolest words

Post by barrym95838 »

JimBoyd wrote:
Here is something useful. Fleet Forth has the word RB ( restore base ). Among other places, it is used in the definition of NUMBER? .

Code: Select all

: RB  ( -- )
   BASE @ R> 2>R  CO
   R> BASE ! ;
The behavior of a word like RB is fairly straight forward. perform the part before CO right now. Perform the part after CO when this word's caller exits.
That's too cool for school!
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)
SamCoVT
Posts: 344
Joined: 13 May 2018

Re: The coolest words

Post by SamCoVT »

I think RB and CO are my two new favorite words.
I've always saved and restored the base myself in words that needed to change it, but that solution is a lot more elegant. I especially enjoy how simple both words are. RB is also, by far, the most practical use of co-routines I have seen.
JimBoyd
Posts: 931
Joined: 05 May 2017

Re: The coolest words

Post by JimBoyd »


I cover coroutines in greater depth here.
JimBoyd
Posts: 931
Joined: 05 May 2017

Re: The coolest words

Post by JimBoyd »


I tested CO and RB on Durex Forth for the C64. Durex Forth is an STC Forth. Both words needed a slight redefinition because Durex Forth didn't have 2>R and 2R> .

Code: Select all

: CO   R> R> SWAP >R >R ;
: RB   R> BASE @ >R >R  CO  R> BASE ! ;

These and the GREET HELLO examples worked as expected.
JimBoyd
Posts: 931
Joined: 05 May 2017

Re: The coolest words

Post by JimBoyd »

CO in my Forth has been a primitive for some time. Here is an excerpt from my Forth kernel's source. It was built with a metacompiler.
The three sections of code below are presented in the reverse order they appear in the kernel source. I felt this would make the source for CO easier to follow.

Code: Select all

CODE CO  (  R: ADR1 -- ADR2 )
         ( IP: ADR2 -- ADR1 )
   PLA  TAY  PLA
   (CO) JMP  END-CODE

   LABEL (CO)    ( IN THE BODY OF >FORTH)
   N STA
   IP 1+ LDA  PHA
   IP    LDA  PHA
   N LDA  IP STY
   ' EXIT @ 4 + JMP  ( THIS JMP)

   ( JUMPS HERE)
   IP 1+ STA
   ( FALLS INTO NEXT)

The following hand translation is provided for those not familiar or comfortable with RPN assembly.

Code: Select all

CODE CO  (  R: ADR1 -- ADR2 )
         ( IP: ADR2 -- ADR1 )
   PLA
   TAY
   PLA
   JMP (CO)
   END-CODE

   LABEL (CO)    ( IN THE BODY OF >FORTH)
   STA N
   LDA IP+1
   PHA
   LDA IP
   PHA
   LDA N
   STY IP
   JMP EXIT4   ( THIS JMP)

   ( JUMPS HERE)
   LABEL EXIT4
   STA IP+1
   ( FALLS INTO NEXT)

As I mentioned previously, CO swaps IP with the top of the return stack.
By taking advantage of what was already in another primitive, the SUBRoutine >FORTH , the primitive version of CO is smaller than the high level version.
JimBoyd
Posts: 931
Joined: 05 May 2017

Re: The coolest words

Post by JimBoyd »

In my humble opinion, another cool word is FH , from here, in Leo Brodie's "Thinking Forth."

Code: Select all

: FH  \  ( offset -- offset-block )  "from here"  
   BLK @  ?DUP 0= IF  SCR @  THEN  + ;

When used in a block which is loading, FH will add the block number in BLK to the number on the data stack. This allows relative loading.
Here is an example from my system loader.

Code: Select all

SCR# 2 
// LOAD BLOCK
DECIMAL
CR .( DONE?      )         2 FH LOAD
CR .( THRU --> <-- ;S )    3 FH LOAD
CR .( ASSEMBLER  )   4 FH 16 FH THRU
CR .( UTILITIES  )  17 FH 25 FH THRU
CR .( SF EDITOR  )  26 FH 38 FH THRU
CR .( SCREEN ED  )        39 FH LOAD
CR .( AUX STACK  )  45 FH 48 FH THRU
CR .( MORE UTILITIES AND EXTENSIONS)
CR                  49 FH 80 FH THRU
CR .( VIRTUAL MEM ) 81 FH 86 FH THRU
CR .( CONDITIONAL COMP )  91 FH LOAD
CR .( AFIND       ) 92 FH 93 FH THRU
CR .( FILES (ERR)   94 FH 95 FH THRU
CR .( MULTITASKER) 96 FH 100 FH THRU

This screen of source is on block 2. When the phrase 2 FH LOAD is interpreted, the screen on block 4 is loaded.
When not loading (BLK is zero), FH adds the screen number in SCR to the number on the stack. When editing this same screen on block 2, typing 3 FH LIST will list the screen with the source for THRU , --> , <-- and ;S on block 5.
Post Reply