Not your run of the mill control flow

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

Re: Not your run of the mill control flow

Post by JimBoyd »


As promised, here is what GEN does.
I already said GEN was a generator. Given an input of N, it will return the numbers from 0 to N-1; however, the way GEN does this is by placing a number on the data stack and executing the Forth threaded code fragment following GEN in the word where it appears. It runs this threaded code fragment once for each number it returns.
Two more words from M. L. Gassanenko are used in the definition of GEN , the pair PRO and CONT .

Code: Select all

: PRO  ( -- )
   R> R> >L  ENTER  L> DROP ;
: CONT  ( -- )
   L> >R R@ ENTER R> >L ;

M. L. Gassanenko refers to PRO as a prolog-like procedure prologue.
I've previously shown ENTER in this thread; nonetheless, here is the source for ENTER .

Code: Select all

: ENTER  ( T-ADR -- )  >R ;

ENTER takes the address of a fragment of threaded code and runs the threaded code fragment at that address. When that threaded code fragment exits, control is returned to the fragment which executed ENTER .
>L and L> are words to move a number to and from what M. L. Gassanenko refers to as a continuation stack.
Here is the source for GEN .

Code: Select all

: GEN  ( U1 -- U2 )
   PRO  0 ?DO  I CONT  LOOP ;

And the source for TEST .

Code: Select all

: TEST
   5 GEN . ;

PRO pulls two addresses off the return stack. The second address, in this case an address in the body of TEST, is placed on the continuation stack. The first address pulled from the return stack, an address in the body of GEN , is used as a parameter for ENTER to transfer control back to GEN . Control is returned back to PRO when GEN exits. PRO removes and drops the address it previously placed on the continuation stack then exits, transferring control back to the word which called TEST .
In the word GEN , CONT pulls the address off the continuation stack. This address is in TEST just past GEN . It saves a copy of this address on the return stack and transfers control to that portion of TEST which is after GEN . Control is returned to CONT when TEST exits. CONT moves that address from the return stack back to the continuation stack. The threaded code after GEN in the word TEST will run for each number returned by GEN .

Back to the brain teaser.

Code: Select all

: GENTEST  ( -- )
   2 GEN CR .
   4 GEN CR 2 .R
   3 GEN CR 3 .R ;

The first occurrence of GEN will return two numbers. It will cause this fragment of Forth to run for each of them.

Code: Select all

         CR .
   4 GEN CR 2 .R
   3 GEN CR 3 .R ;

Each time it runs the next occurrence of GEN will cause this fragment of Forth to run four times.

Code: Select all

         CR 2 .R
   3 GEN CR 3 .R ;

Each time this fragment runs the last occurrence of GEN will cause this final fragment of Forth from the word GENTEST to run three times.

Code: Select all

         CR 3 .R ;

That last fragment runs a total of 24 times each time GENTEST is run.

Words which manipulate return addresses are notoriously difficult to use in a definite loop, where the parameters are kept on the return stack. The pair of words PRO and CONT get around this. PRO gets to the addresses it needs because it is used before any words which place data on the return stack. CONT can get to the address it needs because it is no longer on the return stack.

M. L. Gassanenko gives the example of the word STACK , which non-destructively returns the numbers on the data stack similar to how GEN returns a range of numbers. Many Forth systems have the word .S to non-destructively display the contents of the data stack; however, the stack contents could be displayed as signed or unsigned numbers. This version shows the stack contents as signed numbers:

Code: Select all

: .S   STACK . ;

while this version shows the stack contents as unsigned numbers:

Code: Select all

: U.S   STACK U. ;


My Forth has an auxiliary stack. I defined the continuation stack words >L and L> as aliases for the auxiliary stack words.

Code: Select all

: >L   >A ;
: L>   A> ;

In the word CONT

Code: Select all

   L> >R R@

is functionally equivalent to

Code: Select all

   L> DUP >R

or on my system

Code: Select all

   L> DUP>R

so I redefined CONT

Code: Select all

: CONT
   L> DUP>R ENTER R> >L ;

I wondered why CONT was defined this way and not simply:

Code: Select all

: CONT
   L@ ENTER ;

At first I thought CONT removed the address from the continuation stack to permit something like the word GEN2 .

Code: Select all

: GEN  ( U -- )
   PRO  0 ?DO  I CONT  LOOP ;
: GEN2  ( U -- )
   PRO  GEN  0 ?DO  I CONT  LOOP ;

However, a word like GEN2 could be defined like this:

Code: Select all

: GEN2  ( U -- )
   GEN  PRO  0 ?DO  I CONT  LOOP ;

A word like GEN2 is not even necessary.

Code: Select all

: TESTA  ( U -- )
   GEN GEN . ;
: TESTB  ( U -- )
   GEN2 . ;

So, why the lengthier definition for CONT ? Any thoughts on this?
User avatar
GARTHWILSON
Forum Moderator
Posts: 8773
Joined: 30 Aug 2002
Location: Southern California
Contact:

Re: Not your run of the mill control flow

Post by GARTHWILSON »

JimBoyd wrote:
ENTER takes the address of a fragment of threaded code and runs the threaded code fragment at that address. When that threaded code fragment exits, control is returned to the fragment which executed ENTER .
I can't take the time to understand everything above, but this part looks neat, allowing you to enter a word somewhere other than at the beginning, something I've kind of wanted at times, like this:

Code: Select all

: SOME_WORD
   do_stuff
   BEGIN
       do_more_stuff
       yada-yada       <---Optionally enter here
       Condition?
   UNTIL
   etc.             ;

You'd have to use [ here ] (and maybe store it before the ] ) so you'd have the address of where to enter.  Hopefully I'm understanding it right.  Chalk up another one for Forth's unexpected flexibility.
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?
JimBoyd
Posts: 931
Joined: 05 May 2017

Re: Not your run of the mill control flow

Post by JimBoyd »

GARTHWILSON wrote:
JimBoyd wrote:
ENTER takes the address of a fragment of threaded code and runs the threaded code fragment at that address. When that threaded code fragment exits, control is returned to the fragment which executed ENTER .
I can't take the time to understand everything above, but this part looks neat, allowing you to enter a word somewhere other than at the beginning, something I've kind of wanted at times, like this:

Code: Select all

: SOME_WORD
   do_stuff
   BEGIN
       do_more_stuff
       yada-yada       <---Optionally enter here
       Condition?
   UNTIL
   etc.             ;

You'd have to use [ here ] (and maybe store it before the ] ) so you'd have the address of where to enter.  Hopefully I'm understanding it right.  Chalk up another one for Forth's unexpected flexibility.

Code: Select all

: SOME_WORD
   do_stuff
   BEGIN
       do_more_stuff
       [ HERE >A ]     \ Save address on auxiliary stack
       yada-yada       <---Optionally enter here
       Condition?
   UNTIL
   etc.             ;
: SOME_OTHER_WORD
      [ A> ] LITERAL ENTER ;

Could also be:

Code: Select all

: SOME_WORD
   do_stuff
   BEGIN
       do_more_stuff
       [ HERE >A ]     \ Save address on auxiliary stack
       yada-yada       <---Optionally enter here
       Condition?
   UNTIL
   etc.             ;
: SOME_OTHER_WORD
      BRANCH [ A> , ] -;

Unless this is desired:

Code: Select all

: SOME_WORD
   do_stuff
   BEGIN
       do_more_stuff
       [ HERE >A ]     \ Save address on auxiliary stack
       yada-yada       <---Optionally enter here
       Condition?
   UNTIL
   etc.             ;
: SOME_OTHER_WORD
      [ A> ] LITERAL ENTER  \ Execute last part of SOME_WORD
      do_yet_more_stuff ;   \ and return to here.

Of course BRANCH can not be used from Forth's command line. ENTER can.
JimBoyd
Posts: 931
Joined: 05 May 2017

Re: Not your run of the mill control flow

Post by JimBoyd »

Dr Jefyll wrote:
... my own preference would be...

Code: Select all

?EXIT   \ pop top of stack. exit if true.
?0EXIT  \ pop top of stack. exit if false.
... because they both conditionally execute based on TOS (which gets popped), and that's a scenario I associate with the question mark. And the 0 gets added if you wanna reverse the sense.

-- Jeff
What about

Code: Select all

?EXIT   \ pop top of stack. exit if true.
?STAY   \ pop top of stack. stay if true. (exit if false.)

User avatar
Dr Jefyll
Posts: 3525
Joined: 11 Dec 2009
Location: Ontario, Canada
Contact:

Re: Not your run of the mill control flow

Post by Dr Jefyll »

You're proposing that the opposite-sense equivalent of ?EXIT would be called ?STAY ? I guess I don't object to that.

To me, ?EXIT suggests that the related but opposite word would be ?0EXIT (just as the opposite of = is 0=). But your idea & mine both seem plausible.

Anecdotally -- and changing the subject to words which unconditionally exit -- I dimly recall one such word which I concocted decades ago. And I remember feeling a little smug about the name I came up with, which at the time struck me as both meaningful and faintly humorous: OUST :mrgreen:

-- Jeff
In 1988 my 65C02 got six new registers and 44 new full-speed instructions!
https://laughtonelectronics.com/Arcana/ ... mmary.html
Post Reply