Re: The coolest words
Posted: Tue Mar 23, 2021 7:51 pm
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
;-------------------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.