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

Not your run of the mill control flow

Post by JimBoyd »


Forth has the IF ELSE THEN and BEGIN WHILE REPEAT control flow structures as well as DO LOOP's. There are even some Forths with additional control flow words such as AHEAD and ELIF . I was wondering about other methods of control flow.
PETTIL, by chitselb, has a CASE# ELSE THEN structure. It also has ?:.
I was looking at one of my CREATE DOES> words and it reminded me of a CASE statement.
The CREATE DOES> word XTTABLE bears a resemblance to a CASE statement in sofar as there are tests and actions.

Code: Select all

: XTTABLE  ( -- )
   CREATE
   DOES>  ( N -- )
      BEGIN
         2DUP @ EXECUTE 0=
      WHILE
         2+ 2+
      REPEAT
      2+ @ EXECUTE  ;
: DEFAULT  ( N -- T )
   DROP TRUE ;

XTTABLE creates a word without a parameter field. The parameter field is filled in after the child of XTTABLE is defined. The parameter field consists of tests and actions. A test word is passed the same parameter the XTTABLE word received and is expected to consume that parameter and return a flag. An XTTABLE word will keep going down the list of word pairs until a test returns a TRUE flag. When that happens, the corresponding action word is executed with the same parameter on the stack. After this action word executes, the XTTABLE word exits. Since the XTTABLE word will continue down the list of word pairs until a test returns a TRUE flag, the last test must be the default case. DEFAULT just drops the parameter and returns a TRUE flag. The words used by an XTTABLE word are compiled in this order:

Code: Select all

TEST0  ACTION0  TEST1  ACTION1 ... TESTN  ACTIONN

Here is an example.

Code: Select all

XTTABLE <SEE>  ( CFA -- ??? )
   ] ?CODE      >DIS
     ?HL        >:DIS
     ?DEFER     DDIS
     DEFAULT    CREATEDIS [

Rather than using ' SOMEWORD , , I reverse the normal order of [ and ] to compile the words used by the XTTABLE word <SEE> .
The word <SEE> is used like this:

Code: Select all

: (SEE)  ( CFA -- )
   DUP CR .NAME
   SETWIDTH <SEE> ;
: SEE  ( -- )  ' (SEE) ;

So, what other unique control methods are out there, or even the traditional ones used in unique and interesting ways?
User avatar
barrym95838
Posts: 2056
Joined: 30 Jun 2013
Location: Sacramento, CA, USA

Re: Not your run of the mill control flow

Post by barrym95838 »

JimBoyd wrote:
So, what other unique control methods are out there, or even the traditional ones used in unique and interesting ways?
Courtesy of "HAA" on usenet:

Code: Select all

FOR NEXT came from cmForth as a replacement for DO LOOP. The latter
was deemed too expensive to implement on Forth chips. Implementations
vary e.g. cmForth's iterates n+1 times for reasons peculiar to Novix.

eForth creator Bill Muench provides this diagram & test to explain how
FOR NEXT works with AFT and WHILE. Use fixed-width font to view.

\ v--------<<
\ FOR ... NEXT a1 ...
\
\ v---------------------<< >>----------v
\ FOR ... WHILE a1 ... NEXT a2 ... ELSE a3 ... THEN ...
\ >>-------------------------^
\
\ v-----------------<<
\ FOR ... AFT a1 ... THEN ... NEXT a2 ...
\ >>----------^

( test FOR-NEXT ============================================= )

: N1 ( -- ) 9 FOR R@ . NEXT ;
: N2 ( -- ) 9 FOR 5 R@ < WHILE R@ . NEXT ." X" ELSE R> DROP ." Y" THEN ;
: N3 ( -- ) 9 FOR ." X" AFT ." Y" THEN R@ . NEXT ;

Results using Bill's eForth

ok N1 9 8 7 6 5 4 3 2 1 0
ok N2 9 8 7 6 Y
ok N3 X9 Y8 Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0
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)
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 »

One of the words I added to my version of FIG Forth is NIF (short for "not if"). Predictably, it compiles a primitive which behaves like 0= IF. And NUNTIL was "not until."

These are pretty trivial, of course, but seemed worthwhile given my own relative priorities re the speed/memory tradeoff.

-- Jeff
In 1988 my 65C02 got six new registers and 44 new full-speed instructions!
https://laughtonelectronics.com/Arcana/ ... mmary.html
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 »

Dr Jefyll wrote:
One of the words I added to my version of FIG Forth is NIF (short for "not if").
Wow, looking at my own code, there are enough occurrences of 0= IF that I think that would pay for itself in memory, so there's no penalty for the faster execution. I think I would call it something else though unless "NIF" is already in common use, because it looks like a short form of "nifty" and definitely did not make me think "NOT IF." NOT in Forth XOR's the value with -1, rather than doing 0=.

I believe FOR...NEXT is in the newest standards, probably as a concession to reduce newcomers' resistance to Forth, but I do like DO...LOOP and friends. I have 32-bit equivalents too, at http://wilsonminesco.com/Forth/32DOLOOP.FTH .

Michael, can you give further explanation of what the flow is in FOR...WHILE a1...NEXT a2...ELSE a3...THEN...
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?
User avatar
barrym95838
Posts: 2056
Joined: 30 Jun 2013
Location: Sacramento, CA, USA

Re: Not your run of the mill control flow

Post by barrym95838 »

I asked the question and received that from HAA as the kindest answer, but I'm not embarrassed to admit that I still don't fully understand the answer.
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)
JimBoyd
Posts: 931
Joined: 05 May 2017

Re: Not your run of the mill control flow

Post by JimBoyd »

barrym95838 wrote:
Courtesy of "HAA" on usenet:

Monday evening I think I figured this out and wrote FOR NEXT words. The diagrams were not much help. This better represents what my FOR NEXT words do.

Code: Select all

\     v--------<<
\ FOR a0 ... NEXT a1 ...
\
\     v--------------------<<           >>------------v
\ FOR a0... WHILE a1 ... NEXT a2 ... ELSE a3 ... THEN a4 ...
\              >>-------------------------^
\
\                v--------------------<<
\ FOR a0 ... AFT a1 ... THEN a2 ... NEXT a3 ...
\             >>-------------^

This is how it works in my Forth.
First a brief refresher on the control flow words >MARK >RESOLVE <MARK <RESOLVE .
>MARK leaves an address to be resolved and reserves a cell in the dictionary while >RESOLVE resolves the forward reference.
<MARK leaves a destination address for a backward branch and <RESOLVE compiles that address in the dictionary. This is from my Forth. The (minimal) compiler security is just to make sure that a <MARK is resolved by a <RESOLVE and a >MARK is resolve by a >RESOLVE .

Code: Select all

: ?PAIRS  ( N1 N2 -- )
   <>
   ABORT" STRUCTURE MISMATCH" ;

: >MARK  ( -- >SYS )
   HERE 1  0 , ;
: >RESOLVE  ( >SYS -- )
   1 ?PAIRS  HERE SWAP ! ;

: <MARK  ( -- <SYS )
   HERE 2 ;
: <RESOLVE  ( <SYS -- )
   2 ?PAIRS  , ;

Here are the FOR NEXT words. The only new primitive is (NEXT) .

Code: Select all

' ?BRANCH @ 8 + CONSTANT 2.IP.+!
CODE (NEXT)  ( -- )
   PLA  TAY
   0= IF
      PLA
      0= IF
         2.IP.+! JMP
      THEN
      SEC  1 # SBC  PHA
   THEN
   DEY  TYA  PHA
   0 # LDY
   ' BRANCH @ JMP
   END-CODE

And the high level words.

Code: Select all

: FOR  ( -- )
       ( N -- )
   COMPILE >R  <MARK ; IMMEDIATE
: NEXT  ( -- )
   COMPILE  (NEXT)
   <RESOLVE ; IMMEDIATE
: AFT  ( -- )
   [COMPILE] CS-DROP  COMPILE BRANCH
   >MARK  <MARK
   [COMPILE] CS-SWAP ; IMMEDIATE

The FOR and NEXT loop structure is similar to a BEGIN UNTIL loop.
AFT is the unusual one. It drops the control flow data from FOR , compiles BRANCH and adds control flow data for a forward branch and backward branch. AFT then swaps the control flow data because the forward branch gets resolved before the backward branch.

Here are the test words:

Code: Select all

: N1  ( -- )
   9
   FOR
     R@ .
   NEXT ;
: N2  ( -- )
   9
   FOR
      5 R@ <
   WHILE
      R@ .
   NEXT
      ." X"
   ELSE
      R> DROP ." Y"
   THEN ;
: N3  ( -- )
   9
   FOR
      ." X"
   AFT
      ." Y"
   THEN
      R@ .
   NEXT ;

And what got compiled:

Code: Select all

SEE N1 
N1
 31296  3419 CLIT 9 
 31299  4656 >R
 31301  4740 R@
 31303  7568 .
 31305 31202 (NEXT)
 31307 31301 
 31309  2970 EXIT
15 
 OK
SEE N2 
N2
 31320  3419 CLIT 9 
 31323  4656 >R
 31325  3419 CLIT 5 
 31328  4740 R@
 31330  4448 <
 31332  2781 ?BRANCH 31352 
 31336  4740 R@
 31338  7568 .
 31340 31202 (NEXT)
 31342 31325 
 31344  7809 (.") X
 31348  3042 BRANCH 31360 
 31352  4672 R>
 31354  3031 DROP
 31356  7809 (.") Y
 31360  2970 EXIT
42 
 OK
SEE N3 
N3
 31371  3419 CLIT 9 
 31374  4656 >R
 31376  7809 (.") X
 31380  3042 BRANCH 31388 
 31384  7809 (.") Y
 31388  4740 R@
 31390  7568 .
 31392 31202 (NEXT)
 31394 31384 
 31396  2970 EXIT
27 
 OK

The address after (NEXT) is the branch address. My SEE doesn't recognize (NEXT) as a branching word, but that is not a problem.
The results match those provided.

Code: Select all

N1 9 8 7 6 5 4 3 2 1 0  OK
N2 9 8 7 6 Y OK
N3 X9 Y8 Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0  OK

[Edit: Added some clarification about my SEE .]
Last edited by JimBoyd on Mon Sep 26, 2022 11:07 pm, edited 1 time in total.
JimBoyd
Posts: 931
Joined: 05 May 2017

Re: Not your run of the mill control flow

Post by JimBoyd »

GARTHWILSON wrote:
Dr Jefyll wrote:
One of the words I added to my version of FIG Forth is NIF (short for "not if").
Wow, looking at my own code, there are enough occurrences of 0= IF that I think that would pay for itself in memory, so there's no penalty for the faster execution. I think I would call it something else though unless "NIF" is already in common use, because it looks like a short form of "nifty" and definitely did not make me think "NOT IF." NOT in Forth XOR's the value with -1, rather than doing 0=.
I think I saw it called -IF but it has been a while and I can't remember where.
JimBoyd
Posts: 931
Joined: 05 May 2017

Re: Not your run of the mill control flow

Post by JimBoyd »


I need to run some more tests on my FOR NEXT loops. I suspect the following will be true:
When nested, an inner FOR NEXT loop can be in any part of the outer loop as long as the control structures do not cross.
WHILE can even be used with a FOR ... AFT ... THEN ... NEXT loop. Because AFT places two sets of control flow data on the control flow stack (in my case, the data stack), WHILE cannot be used to branch past NEXT from between AFT and THEN . In other words, I don't think this will work:

Code: Select all

FOR a0 ... AFT a1 ... WHILE a2 ... THEN a3 ... NEXT a4 ... THEN a5 ...

Multiple WHILEs can be used. Each must have a matching THEN after NEXT.

I think my Forth's compiler security of making sure each >MARK is matched with a >RESOLVE , each <MARK is matched with a <RESOLVE and a colon or CODE is matched with a semicolon or END-CODE will cause an ABORT if these conditions are not met.
Also, if any WHILE's are used to branch out of the FOR NEXT loop, the loop parameter will still be on the return stack as shown in test word N2 .

Code: Select all

: N2  ( -- )
   9
   FOR
      5 R@ <
   WHILE
      R@ .
   NEXT
      ." X"
   ELSE
      R> DROP ." Y"
   THEN ;

I also think this might be possible, but I need to test it:

Code: Select all

               v-------------------------------------------<<    
FOR A0 ... AFT A1 ... WHILE A2 ... THEN A3... THEN A4... NEXT A5...
                         >>------------------------^
            >>--------------------------^

User avatar
barrym95838
Posts: 2056
Joined: 30 Jun 2013
Location: Sacramento, CA, USA

Re: Not your run of the mill control flow

Post by barrym95838 »

Jim, I knew you'd be the guy here to figure out how eForth's AFT works. Now all I have to do is figure out how you figured it out, then figure out a proper use for it ... :lol:
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)
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:
GARTHWILSON wrote:
Dr Jefyll wrote:
One of the words I added to my version of FIG Forth is NIF (short for "not if").
Wow, looking at my own code, there are enough occurrences of 0= IF that I think that would pay for itself in memory, so there's no penalty for the faster execution. I think I would call it something else though unless "NIF" is already in common use, because it looks like a short form of "nifty" and definitely did not make me think "NOT IF." NOT in Forth XOR's the value with -1, rather than doing 0=.
I think I saw it called -IF but it has been a while and I can't remember where.
IF0 or IF_0 would be pretty intuitive.
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?
IamRob
Posts: 357
Joined: 26 Apr 2020

Re: Not your run of the mill control flow

Post by IamRob »

GARTHWILSON wrote:
JimBoyd wrote:
GARTHWILSON wrote:
Dr Jefyll wrote:
One of the words I added to my version of FIG Forth is NIF (short for "not if").
Wow, looking at my own code, there are enough occurrences of 0= IF that I think that would pay for itself in memory, so there's no penalty for the faster execution. I think I would call it something else though unless "NIF" is already in common use, because it looks like a short form of "nifty" and definitely did not make me think "NOT IF." NOT in Forth XOR's the value with -1, rather than doing 0=.
I think I saw it called -IF but it has been a while and I can't remember where.
IF0 or IF_0 would be pretty intuitive.
How about "ZIF" or "ZEQIF"?
BruceRMcF
Posts: 388
Joined: 21 Aug 2019

Re: Not your run of the mill control flow

Post by BruceRMcF »

GARTHWILSON wrote:
JimBoyd wrote:
... I think I saw it called -IF but it has been a while and I can't remember where.
IF0 or IF_0 would be pretty intuitive.
-IF is the one that I saw previously, but I have to say that IF0 is the best combination of intuitive and fast to type for me.

I think -IF is most intuitive if you have a dictionary with a leading "-" for a number of words that invert the test for the underlying word. Otherwise I'd have a risk of reading it as taking the following action if the top of the stack is negative.

I would say that "I have FOR NEXT in xForth", but I haven't had an opportunity to work on it over the past year, and until I have a working CREATE / DOES> word pair, I don't think it's fair to say that I actually "have" xForth -- rather I have code that hopes to grow up into xForth on of these days.
JimBoyd
Posts: 931
Joined: 05 May 2017

Re: Not your run of the mill control flow

Post by JimBoyd »


What about 0IF ?
Also 0WHILE and 0UNTIL ?

Code: Select all

   ...
      BEGIN
         2DUP @ EXECUTE
      0WHILE
         2+ 2+
      REPEAT
   ...

Code: Select all

   ...
      BEGIN
         2DUP @ EXECUTE
      WHILE0
         2+ 2+
      REPEAT
   ...

Which do you think reads better?
I also have:

Code: Select all

?EXIT   \ pop top of stack. exit if true.
0EXIT   \ pop top of stack. 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 »

JimBoyd wrote:
What about 0IF ?
Also 0WHILE and 0UNTIL ?
To me, the leading zero seems good. Terse yet clear. Of course I liked the leading N too (example: NIF ), and I knew well enough that it didn't mean NOT (or "nifty"!) :P

But the leading 0 seems fine for 0IF and likewise for 0WHILE.
As for this...

Code: Select all

?EXIT   \ pop top of stack. exit if true.
0EXIT   \ pop top of stack. exit if false.
... 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
In 1988 my 65C02 got six new registers and 44 new full-speed instructions!
https://laughtonelectronics.com/Arcana/ ... mmary.html
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

I like it. Good thing I haven't released Fleet Forth version 2.0 yet.
Post Reply