6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Mon Jun 03, 2024 7:44 pm

All times are UTC




Post new topic Reply to topic  [ 21 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: Re: Extra stacks
PostPosted: Sun Oct 04, 2020 8:18 pm 
Offline

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

Has anyone else implemented an extra stack ( or more) in Forth? How did it affect your programming?
I've implemented an extra stack in Fleet Forth that I mostly use for a spare data stack. I've defined the following parallels for the return stack words:
Code:
>R      >A
R>      A>
R@      A@
DUP>R   DUP>A
2>R     2>A
2R>     2A>

Since this extra stack, which I call the auxiliary stack, is rignt up against the return stack and right up against the area the C64 uses to keep track of which files are open, the words to move data to and from the auxiliary stack ( or aux stack ) test for overflow/underflow.
I usually use the aux stack to hold control flow data so I can do more with CODE words while keeping the source sane and avoiding hand calculated offsets. The aux stack words CS>A and A>CS move the control flow data on the control flow stack ( data stack ) to or from the aux stack. I also use the aux stack to hold temporary addresses to be resolved later, when I want one CODE word to branch or jump into another CODE word at a certain location.
I have also used these words in place of their return stack counterparts when defining a new word ( for my decompiler ) to test it. Once it was working, I changed the aux stack words to the faster return stack words.
It's even been helpful in hand tracing the execution of a system word I was modifying ( to make it easier to support more drive types ).
On another thread, SamCoVT mentioned:
SamCoVT wrote:
I'll also recommend avoiding >r and r> when easy/possible because they make the words harder to test. While they are sometimes the exact right tool for the job, they can only be used in word definitions while compiling.

Aux stack to the rescue!
First, some temporary redefinitions to make things a little safer, just in case:
Code:
: >R >A ;
REDEFINE: >R
 OK
: 2>R 2>A ;
REDEFINE: 2>R
 OK
: DUP>R DUP>A ;
REDEFINE: DUP>R
 OK
: R> A> ;
REDEFINE: R>
 OK
: 2R> 2A> ;
REDEFINE: 2R>
 OK
: R@ A@ ;
REDEFINE: R@
 OK

Here is modified source for one of Fleet Forth's system words:
Code:
// (DR/W)
HEX
NH 2 CONSTANT DSI
: (DR/W)  ( ADR BLK# R/WF CNT -- )
   1- SPLIT 2>R  T&S (IS) DSI
   R> 0
   ?DO
      >R  2OVER 2OVER R@ 100 SR/W
      2>R
      100 UNDER+  DSI + 2 PICK /MOD
      2R>
      ROT UNDER+  R>
   LOOP
   R> 1+ SR/W  DROP ;
' (DR/W) IS DR/W

And here is the log of tracing it by hand:
Code:
HEX  OK
2 DRIVE  OK
PAD 315 1 B/BUF  OK
.S 5934  315    1  400  OK
.AS EMPTY  OK
1- SPLIT  OK
.S 5934  315    1   FF    3  OK
2>A  OK
.S 5934  315    1  OK
T&S81  OK
.S   28 5934   24   50    0    1    1  OK
0 VALUE DSI  OK
TO DSI  OK
.S   28 5934   24   50    0    1  OK
A> 0  OK
.S   28 5934   24   50    0    1    3
   0  OK
. . 0 3  OK
>A 2OVER 2OVER A@ 100  OK
: .SRW CR . . . . . . ;  OK
.S   28 5934   24   50    0 5934   24
  50    0    1  100  OK
.A  OK
.S   28 5934   24   50    0 5934   24
  50    0    1  100    A    0  OK
D. A  OK
.AS   FF    1  OK
.S   28 5934   24   50    0 5934   24
  50    0    1  100  OK
.SRW
100 1 0 50 24 5934  OK
2>A 100 UNDER+ DSI + 2 PICK /MOD  OK
.S   28 5A34   25    0  OK
2A> ROT UNDER+ A>  OK
.S   28 5A34   25   50    0    1  OK
>A 2OVER 2OVER A@ 100 .SRW
100 1 0 50 25 5A34  OK
2>A 100 UNDER+ DSI + 2 PICK /MOD  OK
2A> ROT UNDER+ R>  OK
.S   28 5B34   26   50    0    1  OK
.AS   FF  OK
>A 2OVER 2OVER A@ 100 .SRW
100 1 0 50 26 5B34  OK
2>A 100 UNDER+ DSI + 2 PICK /MOD  OK
2A> ROT UNDER+ A>  OK
.S   28 5C34   27   50    0    1  OK
R>  OK
.S   28 5C34   27   50    0    1   FF  OK
1+  OK
.SRW DROP
100 1 0 50 27 5C34  OK
.S EMPTY  OK
CONSOLE

There is one place in the log where I inadvertently typed .A instead of .AS , placing a double on the data stack rather than displaying the contents of the aux stack. I promptly removed it and continued tracing by hand.
Had I accidentally typed >R rather than >A ( because that is what the source has ) it would have been fine thanks to the temporary redefinitions. Accidentally typing ?DO or LOOP would not have caused a problem other than clearing all stacks when it aborted with the message "FOR COMPILING".


Top
 Profile  
Reply with quote  
 Post subject: Re: Extra stacks
PostPosted: Sun Oct 04, 2020 10:17 pm 
Offline

Joined: Sun May 13, 2018 5:49 pm
Posts: 249
JimBoyd wrote:
On another thread, SamCoVT mentioned:
SamCoVT wrote:
I'll also recommend avoiding >r and r> when easy/possible because they make the words harder to test. While they are sometimes the exact right tool for the job, they can only be used in word definitions while compiling.
Aux stack to the rescue!
OK - This is pretty slick and something I wish I had in my brain earlier. For doing the kind of debugging work you show, it doesn't even have to be fast - a simple implementation using some space ALLOTted in the dictionary along with an index or pointer would work fine. Thanks for sharing!


Top
 Profile  
Reply with quote  
 Post subject: Re: Extra stacks
PostPosted: Mon Oct 05, 2020 4:03 am 
Offline

Joined: Sun Apr 26, 2020 3:08 am
Posts: 357
Instead of >r and r>, or even variables, I started using free ZP locations for temporary storage. I call it Z! and Z@, which are defined as,

: Z! 0 ! ; (or any free ZP memory)
: Z@ 0 @ ;

The advantage of using memory locations compared to >R is it doesn't have to be DUMP'd at the end, with R> DUMP, if the value is not needed.
Another use for the ZP location is the loop variable doesn't get retained when LEAVE is encountered. So I will use: I Z! LEAVE in words that contain a loop that exits prematurely.


Top
 Profile  
Reply with quote  
 Post subject: Re: Extra stacks
PostPosted: Mon Oct 05, 2020 4:07 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8453
Location: Southern California
IamRob wrote:
Another use for the ZP location is the loop variable doesn't get retained when LEAVE is encountered. So I will use: I Z! LEAVE in words that contain a loop that exits prematurely.

How 'bout just having LEAVE store the loop index in a variable, all in the one primitive so it's faster. I think I'll do that myself.

_________________
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  
 Post subject: Re: Extra stacks
PostPosted: Mon Oct 05, 2020 6:34 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8453
Location: Southern California
Jim, you have quite a few words above that are neither part of any standard I know of, nor defined above. One I'll ask about however is REDEFINE:. It appears to edit the old word to redirect execution to the new one, for secondaries that are already compiled using it, so those secondaries don't need to be recompiled.. Is that what's happening? I've had a way to do that but I like yours more.

_________________
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  
 Post subject: Re: Extra stacks
PostPosted: Mon Oct 05, 2020 8:14 pm 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 864
GARTHWILSON wrote:
Jim, you have quite a few words above that are neither part of any standard I know of, nor defined above. One I'll ask about however is REDEFINE:. It appears to edit the old word to redirect execution to the new one, for secondaries that are already compiled using it, so those secondaries don't need to be recompiled.. Is that what's happening? I've had a way to do that but I like yours more.


No. Sorry, I should have been clear about that. This section:
Code:
: >R >A ;
REDEFINE: >R
 OK
: 2>R 2>A ;
REDEFINE: 2>R
 OK
: DUP>R DUP>A ;
REDEFINE: DUP>R
 OK
: R> A> ;
REDEFINE: R>
 OK
: 2R> 2A> ;
REDEFINE: 2R>
 OK
: R@ A@ ;
REDEFINE: R@
 OK

is part of the log of the interactive session where I hand traced the word (DR/W) . I had modified it to make it easier to support the 1581 disk drive as well as the others.
"REDEFINE: >R" is a message from the system letting me know I redefined >R . It's harder to tell that from the print dump than a live session so I may change that message to something like:
"YOU REDEFINED >R"
or even
">R EXISTS"
or maybe
">R REDEFINED"
or even
">R WAS REDEFINED"

Your comment does give me an idea and I'll have to give it some thought.
As for the other words, the source is from the source for my Forth kernel.
NH sets a flag so the metacompiler compiles the next word headerless. For interactive testing in Forth, not metacompiling, I redefine NH as a no-op
Code:
: NH ;

In the log, the phrase "2 DRIVE" ( "10 DRIVE" would also work ) sets the current drive to drive 10 ( drive 8 being selected with "0 DRIVE" or "8 DRIVE" ) . Commodore 64 disk drives start at device 8 and go up from there.
SPLIT splits a cell into its low byte and high byte. It is seven bytes and is a really fast "$100 /MOD" .
Fleet Forth, like Blazin' Forth, uses direct access to drive sectors for block access ( on disks that are only supposed to be for blocks) .
T&S derives the starting track and sector from the block number.
(IS) is the primitive used by IS and TO to write the value on the data stack into the first cell of the parameter field of the following word in the definition and bump IP past said word.
SR/W is the sector read write word.
UNDER+ has the following stack diagram:
( N1 N2 N3 -- N1+N3 N2 )
(DR/W) is the vector for the deferred word DR/W , disk read write.
Either DR/W or RR/W ( ram read write ) is executed by R/W depending on the block number.
I've modified things since my latest upload. a block number of $8000 and up and RR/W is executed. RR/W sees a block number $8000 less than the actual block number.
I hope this clarifies things.


Top
 Profile  
Reply with quote  
 Post subject: Re: Extra stacks
PostPosted: Mon Oct 05, 2020 8:34 pm 
Offline

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

IamRob wrote:
Instead of >r and r>, or even variables, I started using free ZP locations for temporary storage. I call it Z! and Z@, which are defined as,

: Z! 0 ! ; (or any free ZP memory)
: Z@ 0 @ ;

One disadvantage is keeping track of which ZP locations you are using if you need more than one. The aux stack is an actual stack and my implementation is over 40 cells deep. ( memory the C64 wasn't using below screen memory ) .
Quote:
The advantage of using memory locations compared to >R is it doesn't have to be DUMP'd at the end, with R> DUMP, if the value is not needed.
Another use for the ZP location is the loop variable doesn't get retained when LEAVE is encountered. So I will use: I Z! LEAVE in words that contain a loop that exits prematurely.

Wouldn't you still need to initialize the storage with a sentinel value so you know if you left the loop prematurely? Something like 0 Z! or -1 Z! ?
In one of my system words, I leave a loop like this:
Code:
   ?DO
      DUP I >BT @ =
      IF  DROP I UNLOOP
      ELSE CS>A
   LOOP

UNLOOP discards the loop parameters and I branch out of the loop by moving the control flow data from ELSE to the aux stack. Further in the definition I resolve the ELSE by moving the control flow data back to the data stack ( the control flow stack ) like this:
Code:
   A>CS THEN

Yes, the definition was a bit long. It could not be factored into non trivial smaller parts that got used more than once though.


Top
 Profile  
Reply with quote  
 Post subject: Re: Extra stacks
PostPosted: Mon Oct 05, 2020 9:10 pm 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8453
Location: Southern California
JimBoyd wrote:
I hope this clarifies things.
Yes, that clears up a lot.

Quote:
SPLIT splits a cell into its low byte and high byte.
Yes, that one is standard. COMBINE is the complement.

_________________
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  
 Post subject: Re: Extra stacks
PostPosted: Tue Oct 06, 2020 3:51 am 
Offline

Joined: Sun Apr 26, 2020 3:08 am
Posts: 357
JimBoyd wrote:

IamRob wrote:
Instead of >r and r>, or even variables, I started using free ZP locations for temporary storage. I call it Z! and Z@, which are defined as,

: Z! 0 ! ; (or any free ZP memory)
: Z@ 0 @ ;

One disadvantage is keeping track of which ZP locations you are using if you need more than one. The aux stack is an actual stack and my implementation is over 40 cells deep. ( memory the C64 wasn't using below screen memory ) .
Quote:
The advantage of using memory locations compared to >R is it doesn't have to be DUMP'd at the end, with R> DUMP, if the value is not needed.
Another use for the ZP location is the loop variable doesn't get retained when LEAVE is encountered. So I will use: I Z! LEAVE in words that contain a loop that exits prematurely.

Wouldn't you still need to initialize the storage with a sentinel value so you know if you left the loop prematurely? Something like 0 Z! or -1 Z! ?
In one of my system words, I leave a loop like this:
Code:
   ?DO
      DUP I >BT @ =
      IF  DROP I UNLOOP
      ELSE CS>A
   LOOP

UNLOOP discards the loop parameters and I branch out of the loop by moving the control flow data from ELSE to the aux stack. Further in the definition I resolve the ELSE by moving the control flow data back to the data stack ( the control flow stack ) like this:
Code:
   A>CS THEN

Yes, the definition was a bit long. It could not be factored into non trivial smaller parts that got used more than once though.



Yes, I have to initialize Z! to zero. I have thought of implementing something like UNLOOP, which is a cleaner exit and probably faster. Is your UNLOOP a primitive or a word?


Top
 Profile  
Reply with quote  
 Post subject: Re: Extra stacks
PostPosted: Tue Oct 06, 2020 9:13 pm 
Offline

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

My UNLOOP is a primitive. It's only nine bytes.
Code:
CODE UNLOOP  ( S: -- )
             ( R: ADR N1 N2 -- )
   PLA,  PLA,
   PLA,  PLA,
   PLA,  PLA,
   NEXT JMP,
END-CODE

Following a suggestion from Garth Wilson, my do loops have three loop parameters. ADR is the address that LEAVE or ?LEAVE ( if present) and LOOP or +LOOP branch to.
UNLOOP is from the Ansi Forth standard. Note that it doesn't actually exit the word the loop is in nor does it leave the loop. It just discards the loop parameters. This is so a word can be exited from within a do loop or within a nested do loop. There needs to be an UNLOOP for each level of loop nesting.
Here are two hypothetical examples:
Code:
: SOMEWORD  ( -- N1 )
   10 0 DO
      I DO_SOMETHING_WITH_I
      I SOMETEST
      IF  I UNLOOP EXIT  THEN
   LOOP
   TRUE ;

: ANOTHERWORD  ( -- N1 N2 )
   10 0 DO
      25 0 DO
         I J DO_SOMETHING
         I J SOME_OTHER_TEST
         IF  I UNLOOP I UNLOOP EXIT  THEN
      LOOP
   LOOP
   TRUE TRUE ;



Top
 Profile  
Reply with quote  
 Post subject: Re: Extra stacks
PostPosted: Thu Oct 08, 2020 11:59 pm 
Offline

Joined: Sun May 13, 2018 5:49 pm
Posts: 249
JimBoyd wrote:
Has anyone else implemented an extra stack ( or more) in Forth? How did it affect your programming?
Well... now I have. I ended up with:
Code:
( Auxiliary Stack - 2020-10-05        SamCoVT )
( Based on 6502.org discussion.  License: CC0 )

( Create the stack.  AUXTOS points to just *after* TOS )
CREATE AUXSTACK 16 CELLS ALLOT
VARIABLE AUXTOS   AUXSTACK AUXTOS ! ( Start with empty stack. )

: .AS   ( Print the aux stack in Tali2 format )
   ( Print <#items>  at the beginning. )
   ." <"   AUXTOS @ AUXSTACK -   1 CELLS /
   0 <# #S #> TYPE ( Number with no trailing space )
   ." > "
   ( Print the stack values with TOS on the right. )
   AUXSTACK               ( Start at the bottom of the stack. )
   BEGIN DUP AUXTOS @ < WHILE
      DUP @ .   1 CELLS + ( Print and move up the stack. )
   REPEAT  DROP ;

: >A   ( S: n  A: -- S:  A: n )
   AUXTOS @ !  1 CELLS AUXTOS +! ;

: A>   ( S:  A: n -- S: n  A: )
   AUXTOS @ AUXSTACK = IF
      ." AUXSTACK EMPTY"
   ELSE
      -1 CELLS AUXTOS +! AUXTOS @ @
   THEN ;

: A@   ( S:  A: n -- S: n  A: n )
   AUXTOS @ AUXSTACK = IF
      ." AUXSTACK EMPTY"
   ELSE
      AUXTOS @   1 CELLS -   @
   THEN ;

: DUP>A   ( S: n  A: -- S: n  A: n )
   DUP >A ;

: 2>A   ( S: d  A: -- S:  A: d )
   SWAP >A >A ;

: 2A>   ( S:  A: d -- S: d  A: )
  A> A> SWAP ;
I've tested this on Tali2 and Gforth and it looks like it works. I learned the following unrelated things about forth in the process:

>= isn't a standard word?!?! Once I realized I was printing my stack backards, I also discovered that I didn't need this word and < would do just fine. I'm not sure how I didn't run into this before.

. prints a space after the number. If you don't want that, you get to learn about <# and #S and #>

I think this will be handy for when I'm working on a new word that uses >R and R> and R@, like in JimBoyd's example, and I want to try it out a line at a time. I especially like how .AS lets you see what's going on in the pretend return stack. I don't know as I'll use it outside of this use case, but it certainly looks handy for debugging words that use the return stack.

Edit - uppercased all of the forth words in the code for readability


Top
 Profile  
Reply with quote  
 Post subject: Re: Extra stacks
PostPosted: Fri Oct 09, 2020 8:34 pm 
Offline

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

How about replacing
Code:
0 <# #S #> TYPE

with
Code:
1 .R



Top
 Profile  
Reply with quote  
 Post subject: Re: Extra stacks
PostPosted: Sat Oct 10, 2020 2:50 pm 
Offline

Joined: Sun May 13, 2018 5:49 pm
Posts: 249
JimBoyd wrote:
Code:
1 .R
That's much better. I didn't realize that .R doesn't put a space after the number - that's useful to know and I think it makes the code more readable. I'm not too sad that I delved into #S and family - I understand them a little better each time I look them up and play with them.


Top
 Profile  
Reply with quote  
 Post subject: Re: Extra stacks
PostPosted: Sat Oct 31, 2020 1:16 am 
Offline

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

The return stack normally holds return addresses and DO LOOP parameters. Some Forth implementations may place the loop parameters elsewhere ( which in itself would be another stack, assuming the loops are nestable) but the latest standard indicates that the loop parameters will be on the return stack. The latest Forth standard states that a program may use the return stack for temporary storage subject to certain restrictions. The Forth-83 standard merely states that the return stack may cautiously be used for other values. An auxiliary stack may be used as freely as the data stack, without any of these restrictions. Nothing surprising there. I wonder, are we are so used to the restrictions with the return stack that we apply the same restrictions to an auxiliary stack?


Top
 Profile  
Reply with quote  
 Post subject: Re: Extra stacks
PostPosted: Sun Aug 06, 2023 9:55 pm 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 864
GARTHWILSON wrote:
Jim, you have quite a few words above that are neither part of any standard I know of, nor defined above. One I'll ask about however is REDEFINE:.

I already mentioned the string "REDEFINE:" is a message from CREATE to let me know I redefined a word. I've changed that. CREATE will now print the name of the word followed by the string "EXISTS" when there is a redefinition.
Code:
: TEST ;  OK
: TEST ;
TEST EXISTS
 OK

One point to keep in mind with these session logs is that the printout does not show a distinction between what the programmer types and the computer's response. It is a faithful printout of the text sent to the computer screen.
Attachment:
Fleet Forth word redefinition.png
Fleet Forth word redefinition.png [ 14.15 KiB | Viewed 2881 times ]



Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 21 posts ]  Go to page 1, 2  Next

All times are UTC


Who is online

Users browsing this forum: No registered users and 15 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: