6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Sat Nov 23, 2024 12:01 pm

All times are UTC




Post new topic Reply to topic  [ 354 posts ]  Go to page Previous  1 ... 9, 10, 11, 12, 13, 14, 15 ... 24  Next
Author Message
PostPosted: Sun Mar 21, 2021 9:01 pm 
Offline

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

Although Fleet Forth's editor is based on the editor presented in Forth Dimensions volume 3 issue 3, there are some differences.
WHERE is in the kernel, takes no parameters, and is used by (ABORT") .
(SPREAD) is named SPREAD because it's not just used by other words, I also use it directly.
Some of the words not directly used by the programmer are different or have different names.
The version of MATCH in the article returns a flag and a cursor offset while the version in Fleet Forth returns the flag on top.

In both versions, MATCH does not skip over spaces. The search string might contain spaces. MATCH takes the address and length of a string to search and an address and length of a string to find. MATCH starts at the beginning of the string to search and progresses through it one byte at a time until it finds a match or it has made it through the string.
There is a cursor variable, R# . When editing a screen, the starting address and length for the string to be searched are calculated using the value of R# and the offset returned by MATCH is added to R# .
Here is the source for Fleet Forth's MATCH
Code:
: MATCH  ( ADR1 LEN1 ADR2 LEN2 -- OFFSET FLAG )
   2OVER BOUNDS
   ?DO
      2DUP I TEXT=
      IF
         NIP ROT I SWAP - +
         TUCK U< 0=
         UNLOOP EXIT
      THEN
   LOOP
   2DROP NIP FALSE ;

TEXT= is a code word that takes an address and a count (length) and another address. It returns TRUE if the strings at both addresses are identical over the length specified by the count.
As long as a match is not found and the search space is not exhausted, the DO LOOP executes five words that are all code words (primitives).
2DUP , I , TEXT= , ?BRANCH ( compiled by IF ), and (LOOP) ( compiled by LOOP ). The words in the IF THEN structure calculate the cursor offset. a TRUE flag is returned only if the cursor offset is not larger than LEN1, otherwise a FALSE flag is returned. The loop parameters are discarded and MATCH exits at this point. If the DO LOOP runs to completion, the search string was not found and a cursor offset equal to LEN1 and a FALSE flag are returned.
Another difference is how MATCH is used. In both versions, R# is used to find the line and position on the current line to start the search. In both versions, the search performed by the word TILL is limited to the current line.
There are two other search words. F finds the next occurrence of the search string in the screen. S finds all occurrences of the search string in a range of screens. Well, all but pathological cases. Consider this screen:
Code:
0 FH LIST
SCR# 8100
0:
1:
2:
3:
4:
5:
6:
7:    DUPDUPDUP
8:
9:
A:
B:
C:
D:
E:
F:
 OK

When searching for the string DUP , all three are found.
Code:
1 FH S DUP
   DUP^DUPDUP                                                     7 8100
   DUPDUP^DUP                                                     7 8100
   DUPDUPDUP^                                                     7 8100 
OK

Notice that I did not say "the word DUP" but rather "the string DUP" . These search words know nothing of Forth words or space delimited parsing. They are editing tools looking for one string within another.
When searching for the string DUPDUP , only one occurrence is found.
Code:
1 FH S DUPDUP
   DUPDUP^DUP                                                     7 8100 
OK

With Fleet Forth's editor and the one in the article, F uses the word SEEK ( spelled (SEEK) in the article ) to find the search string. While both versions search from the current cursor position, the version in the article uses the cursor variable, R# , to calculate the address of the cursor position and the remaining length of that line. If the string is not found, the next line is searched from the beginning, each line searched until the string is found or there are no more lines to search. Fleet Forth's version of SEEK uses the cursor variable, R# , to calculate the address of the cursor position and the remaining length of the entire screen.
The version of S in the article also searches for the search string one line at a time whereas the version in Fleet Forth searches the entire screen.


Top
 Profile  
Reply with quote  
PostPosted: Sun Mar 21, 2021 10:58 pm 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8545
Location: Southern California
That's neat! The only additional thing I could wish for is wildcards, which I have on my HP-71 hand-held computer but I haven't had much use for in the way I use Forth. By "wildcards," I mean for example that the search string could specify that it has to start with one thing and end with another but not specify what's in the middle, or you could specify for example that you only want the string if it's at the beginning of a line, or other such things. That's something even my wonderful MultiEdit editor on the PC doesn't have.

_________________
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  
PostPosted: Thu Mar 25, 2021 2:27 am 
Offline

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

That sounds interesting but I haven't needed wildcards so I didn't try to implement them. The editor really is quite simple. Here is my find and replace function.
Code:
: FR
   BEGIN F R AGAIN ; -2 ALLOT

Unlike S , F ( find) and R ( replace) are limited to one screen at a time. If F can't find the string it aborts. I'm changing that to QUIT so it will not clear the data and auxiliary stacks.
It is used like this:
Code:
FR <STRING1>^<STRING2>

As long as I didn't do anything to alter the contents of the find and insert buffers I can perform the same search and replace on other screens just by typing FR .
The C64 has a screen editor. If I scroll up to a line and hit the enter key, the entire line is read in by EXPECT as if I had typed it. This made it easy to add a screen editor for screens as opposed to just a line editor.
Code:
HEX
EDITOR DEFINITIONS
: XED  ( N -- )
   CREATE C, DOES>  ( -- )
   C@ C/L * R# !
   0 TEXT C/L PAD C!
   IBUF BUFMOVE (R)
   QUIT ; -2 ALLOT

 0 XED 0:    1 XED 1:    2 XED 2:
 3 XED 3:    4 XED 4:    5 XED 5:
 6 XED 6:    7 XED 7:    8 XED 8:
 9 XED 9:   0A XED A:   0B XED B:
0C XED C:   0D XED D:   0E XED E:
0F XED F:

I modified LIST to always show the line numbers in hexadecimal so I only need sixteen XED child words.
Code:
HEX
VOCABULARY EDITOR
VARIABLE R#
: LINE  ( LINE# -- LADR CNT )
   0F OVER U< ABORT" OFF SCREEN"
   C/L *  SCR @ BLOCK +  C/L ;
: LIST  ( SCR -- )
   RB EDITOR  R# OFF DUP SCR !
   CR ." SCR# " U. HEX  10 0
   DO
      CR I 1 .R ." : "
      I LINE -TRAILING QTYPE
      DONE? ?LEAVE
   LOOP
   CR ;



Top
 Profile  
Reply with quote  
PostPosted: Thu Mar 25, 2021 2:57 am 
Offline

Joined: Sun Apr 26, 2020 3:08 am
Posts: 357
I haven't seen much opportunity to use wildcards yet, either. Even under ML programming, the only use I have for wildcards is when cataloging a disk. And even then, I combine the wildcard <search by name> with, <search by type>, <search by date>, <search by filesize>, etc...

As of yet, I don't have many volume or disk utilities in Forth.


Top
 Profile  
Reply with quote  
PostPosted: Sat Apr 03, 2021 12:17 am 
Offline

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

I discussed the structure of Fleet Forth's vocabularies here.
In a recent modification to my metacompiler, I took advantage of that structure. The following words are defined exactly the same in the main ( host ) assembler vocabulary and the target assembler vocabulary:
Code:
TABLE     MODE      .A        X)
)Y        #         )         MEM
,X        ,Y        Z         OFFSET
VS        CS        0=        0<
>=        NOT

The body of a Fleet Forth VOCABULARY has three fields, a pointer to the link field of the latest word in the vocabulary, a pointer to the vocabulary's parent vocabulary, and as a link in the VOC-LINK chain. The first word defined in a vocabulary has a link field with a value of zero.
To avoid redefining the words shown above, I made sure they were the first words defined in the original ASSEMBLER vocabulary and modified the metacompiler's ASSEMBLER vocabulary before it had any words.
Code:
SCR# 51
// TIE IN WITH FLEET FORTH ASSEMBLER
TARGET VOCABULARY ASSEMBLER
LATEST NAME> >BODY
HOST ASSEMBLER ' NOT >LINK SWAP!

The metacompiler's assembler is chained to the main assembler at this point and the first word defined in the metacompiler's ASSEMBLER vocabulary will be linked to the word NOT in the original ASSEMBLER vocabulary; therefore the words shown above are searched as part of the metacompiler's vocabulary. Since Fleet Forth's vocabularies do not have a fake name in the vocabulary, but a link to the parent vocabulary, a search in the metacompiler's assembler does not immediately continue in the main Forth vocabulary. The search progresses from the metacompiler's assembler to the target Forth vocabulary on down to the host ( or main) Forth vocabulary.
Code:
ASSEMBLER \ metacompiler's assembler vocabulary
FORTH     \ target Forth vocabulary
SHADOW    \ for constants that "shadow" variables in the target
META      \ metacompiler aliases for C@ @ C! ! etc.
FORTH     \ original Forth vocabulary



Top
 Profile  
Reply with quote  
PostPosted: Mon Apr 12, 2021 11:39 pm 
Offline

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

In addition to .S to display the contents of the data stack and .AS to display the contents of the auxiliary stack, Fleet Forth also has .RS to display the contents of the return stack. These three words can aid in tracking down errors.
Here is the source for Fleet Forth's ABORT , the word executed by (ABORT") .
Code:
: ABORT  ( -- )
   SINGLE ERR SP! AP!
   ['] LIT (IS) WHERE
   QUIT ; -2 ALLOT

SINGLE switches off multitasking, setting the deferred word PAUSE to execute the word NOOP , a no op.
SP! and AP! clear the data stack and the auxiliary stack.
The line
Code:
   ['] LIT (IS) WHERE

re enables WHERE .
-2 ALLOT reclaims the memory used by EXIT , as there is no returning from QUIT .
ERR is a deferred word normally set to the no op, NOOP . To help with troubleshooting, ERR can be set to a word like (ERR) .
Code:
: (ERR)
   CR ." DATA: " .S
   CR ."  AUX: " .AS
   CR ."  RET:"  .RS ;

This will display all three stacks when an error is encountered.

I recently redefined .RS . The old version worked well enough. Here is a test case:
Code:
: INNER
   1 0  DO  .RS  LOOP ;
: OUTER
   1 0  DO  INNER  LOOP ;
: WRAPPER
   OUTER ;

Executing WRAPPER produced something like the following:
Code:
WRAPPER
5807 .RS
7FFF
8001
580B
5823 INNER
7FFF
8001
5827
5839 OUTER
21A0 EXECUTE
21EB INTERPRET
 OK

This does show the nesting to the word .RS , but it is not accurate and I desired that accuracy for some of my Forth experiments.
The address 21EB is not in INTERPRET , but in QUIT . Do-colon places the address 21EB on the return stack when QUIT executes INTERPRET , just as the address 21A0 is placed on the return stack when WRAPPER is executed. The improved .RS uses the word AFIND to show which word contains a given address.
Code:
AFIND  ( ADDR -- ADDR LINK1 LINK2 )

ADDR is the address in question. LINK1 is the closest link address to ADDR from below and LINK2 is the closest link address to ADDR from above.
For each value on the return stack, .RS displays it as an unsigned number. If, as an address, it is within the dictionary, the name of the word containing that address is displayed.
Here is the result of using the new .RS with the above test case (I've added lowercase comments to the session log):
Code:
WRAPPER
5807 INNER
7FFF
8001
580B INNER      \ one of the DO LOOP parameters in INNER
5823 OUTER
7FFF
8001
5827 OUTER      \ one of the DO LOOP parameters in OUTER
5839 WRAPPER
21A0 INTERPRET
21EB QUIT
 OK

Although the address 580B is in the word INNER , it is not placed there by executing another word. It is the first DO LOOP parameter placed on the return stack. It is the branch address used by any LEAVE or ?LEAVE ( of which there are none in this test ) and used by LOOP and +LOOP when a DO LOOP terminates through LOOP or +LOOP.


Top
 Profile  
Reply with quote  
PostPosted: Fri Apr 16, 2021 11:11 pm 
Offline

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

I made a change to Fleet Forth's LINELOAD , which is used by LOAD . LINELOAD takes a line number and a screen number as parameters. It loads a given screen starting at the line specified.
I read somewhere (I don't remember where) a recommendation that LOAD should set BASE to decimal prior to loading a screen. I've given this some thought and can not really see a down side. As it is, I have been in the habit of specifying the number base at the start of a screen. With this modification, I will not have to specify the number base if I am using decimal for a particular screen.
Here is the source for Fleet Forth's LINELOAD and LOAD
Code:
: LINELOAD  ( LINE# SCR# -- )
   ?DUP 0=
   ABORT" CAN'T LOAD 0"
   RB DECIMAL
   BLK 2@ 2>R
   BLK !  C/L * >IN !
   INTERPRET  2R> BLK 2! ;
: LOAD  ( U -- )
   0 SWAP LINELOAD ;

This line:
Code:
   RB DECIMAL

Sets the base to decimal and also causes LINELOAD and LOAD to revert to the number base in use prior to loading a screen.


Top
 Profile  
Reply with quote  
PostPosted: Sat Apr 17, 2021 12:56 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8545
Location: Southern California
I think it's a bit short-sighted for someone to say decimal should be the default. In my own uses, it make sense to keep it in hex most of the time, and change to decimal or binary only temporarily and in limited places.

_________________
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  
PostPosted: Sat Apr 17, 2021 2:12 am 
Offline

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

The idea isn't so much to have decimal as a default, but to avoid having no default. This change doesn't change anything for screens where I still set the base to hexadecimal on line one since having LOAD set a particular base doesn't preclude changing the base while loading the screen. I usually use line zero for a comment that the word INDEX displays and line one to set the base. On the screens that will compile the same in hexadecimal or decimal, it is tempting to not specify the base. Without a default of some kind, it may be necessary to specify the number base for each screen since they can be loaded individually for testing purposes.
With the other change, RB causes LOAD to restore base to the value it had prior to loading a screen. Base can be changed in a given screen (possibly multiple times). After a screen is loaded, base is restored to what it was before I loaded that screen.
As an example, suppose I'm working in binary to check out some information that makes more sense to view in binary and I have some bit manipulation words defined on screen 250. No base was set in the source because that screen will load just fine in hexadecimal or decimal. It just won't load correctly in binary. While working in binary, I can load these words by typing:
#250 LOAD ( or $FA LOAD ) without leaving binary and I'm right back in binary after the screen loads.
I suppose this change to LOAD has to do with my recent change to NUMBER? . Before that change, If I was working in binary and wanted to load screen number 250, I would have to change to decimal (or hexadecimal) to load it, since that would be more convenient and easier to remember than typing:
11111010 LOAD


There is one caveat: If there is an error while loading a screen, Fleet Forth will be left in whatever base was in use until I next change base. This is no different than before. RB can't do anything about that.


Top
 Profile  
Reply with quote  
PostPosted: Sat Apr 17, 2021 2:20 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8545
Location: Southern California
I guess it would be a little strange to have screens numbered in something other than decimal.

_________________
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  
PostPosted: Sat Apr 17, 2021 2:36 am 
Offline

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

Funny you should say that. Part of the reason I chose decimal as the default is that I'd been thinking a little too much in hexadecimal when working on Fleet Forth. When I would print a range of screens to a print dump file, I was in the habit of printing them in hexadecimal.


Top
 Profile  
Reply with quote  
PostPosted: Sat Apr 17, 2021 5:55 am 
Offline

Joined: Sun Apr 26, 2020 3:08 am
Posts: 357
GARTHWILSON wrote:
I guess it would be a little strange to have screens numbered in something other than decimal.

I find it stranger to have screens numbered in decimal. But that's just because I wrote some words based on screens being multiples of 16 (0-$F). Also, being on a hard drive and not dealing with floppies really helps. Screen files can be created to be multiples of $50 (80), $100 (256), $200 (512), up to $8000 (32768) screens at a time, which is the Prodos max file size on the Apple II.

Personally, I just find it easier to count in hexadecimal.


Top
 Profile  
Reply with quote  
PostPosted: Thu Apr 22, 2021 2:32 am 
Offline

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

Whether listing in hexadecimal, decimal, or some other base, the base used to list is independent of the base used for loading.
The advantages of my modifications, for me, are:
1) I don't have to specify the base in the source of a screen. If I need a number in hexadecimal, I can prefix the number with the character $.
2) If I need to load some tools, whatever base I'm working in is automatically restored after the load.


Top
 Profile  
Reply with quote  
PostPosted: Thu Apr 22, 2021 2:58 am 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 895
barrym95838 wrote:
I'm not properly equipped to give any meaningful critiques, but I'm very happy to tag along and try to learn a thing or two.


You don't need to let that stop you. Although I eventually went with AYPUSH (which has the high byte in Y) as well as keeping PUSH , your suggestion about PUSHAY got me thinking in that direction.


Top
 Profile  
Reply with quote  
PostPosted: Tue Apr 27, 2021 12:16 am 
Offline

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

Coroutines in Fleet Forth, an ITC Forth.
I discussed coroutines here. This is a more thorough and, I hope, more complete presentation of coroutines in Fleet Forth.
Coroutines are two routines which take turns executing. This is not the same as mutual recursion. Two (or more) routines which are mutually recursive execute one another from the beginning, causing the return stack to grow until the condition is met to not recurse. One place I've seen mutual recursion in Forth is turtle graphics. Mutual recursion in Forth can be achieved with deferred words.
Code:
DEFER LBRANCH
: RBRANCH   \ draw right branch and sub-branches of tree
   ... RECURSE ... ;
   ... LBRANCH ... ;
: (LBRANCH)   \ draw left branch and sub-branches of tree
   ... RECURSE ... ;
   ... RBRANCH ... ;
(LBRANCH) IS LBRANCH

In Fleet Forth, the word RECURSE causes the CFA of the word being defined to be compiled.
Note that comments to the end of line in Fleet Forth use the word // since the Commodore 64 lacks a backslash character. The use of the backslash indicates comments which were added to the listing or session log, although in this case, this isn't an actual listing as the ellipses indicate code not shown.

As coroutines switch back and forth, each coroutine resumes where it left off. If two coroutines are infinite loops, they behave like a primitive multitasking system with two tasks that share stacks and run forever. I have yet to think of a good use for such coroutines where a cooperative round robin multitasker wouldn't work better. If anyone knows of such a case, I would like to know about it. Coroutines which are not infinite loops are, in my opinion, far more interesting.

Fleet Forth's coroutine word is CO . CO can be written as a primitive (a code word) or in high level Forth; either way, it swaps the top value on the return stack with the contents of IP , Forth's interpretive pointer. When a high level Forth word executes, do-colon pushes the current contents of IP onto the return stack and places the address of the body of the high level word being executed in IP. EXIT , the word used to return from a high level Forth word to the word which executed it, pulls the top address off the return stack and stores it in IP. When CO is defined as a high level Forth word, do-colon and EXIT move the contents of IP to and from the return stack so all that is necessary, in the body of CO , is to swap the top two items on the return stack.
Code:
: CO   2R> SWAP 2>R ;

Here is a brief example.
Code:
: GREETINGS
   CR ." GREETINGS!"      CO
   CR ." HOW DO YOU DO?"  CO
   CR ." GOODBYE."        ;
: HELLO
   GREETINGS
   CR TAB ." HELLO THERE!"       CO
   CR TAB ." AS WELL AS I CAN."  CO
   CR TAB ." SO LONG." ;

Here is an excerpt of a session log showing the execution of the word HELLO
Code:
GREETINGS!
          HELLO THERE!
HOW DO YOU DO?
          AS WELL AS I CAN.
GOODBYE.
          SO LONG.

Notice that HELLO executes GREETINGS and when GREETINGS exits it exits to HELLO . HELLO exits to whatever executed it.

Here is a slightly different version.
Code:
: GREETINGS
   CR ." GREETINGS!"      CO
   CR ." HOW DO YOU DO?"  CO
   CR ." GOODBYE."        CO
   CR ." THANK YOU."      ;
: HELLO
   GREETINGS
   CR TAB ." HELLO THERE!"        CO
   CR TAB ." AS WELL AS I CAN."   CO
   CR TAB ." BYE AND TAKE CARE."  ;

and an excerpt from the session log where HELLO is executed.
Code:
GREETINGS!
          HELLO THERE!
HOW DO YOU DO?
          AS WELL AS I CAN.
GOODBYE.
          BYE AND TAKE CARE.
THANK YOU.

Once again, HELLO executes GREETINGS . This time, HELLO exits to GREETINGS which exits to INTERPRET .


One coroutine might be an infinite loop. here is an example.
Code:
// DECISIONS DECISIONS
: ANSWER
   BEGIN
      CR TAB ." MAYBE." CO
      CR TAB ." YES!"   CO
      CR TAB ." NO."    CO
   AGAIN ;
: QUESTION
   CR ." WILL THIS WORK?" ANSWER
   CR ." ARE YOU SURE?"       CO
   CR ." REALLY?"             CO
   CR ." CAN YOU BE SERIOUS?" CO
   CR ." FOR THIS PROJECT?"   CO
   CR ." ARE YOU QUALIFIED?"  CO
   CR ." KNOCK IT OFF!"       CO
   R> DROP ;

And the session log.
Code:
WILL THIS WORK?
          MAYBE.
ARE YOU SURE?
          YES!
REALLY?
          NO.
CAN YOU BE SERIOUS?
          MAYBE.
FOR THIS PROJECT?
          YES!
ARE YOU QUALIFIED?
          NO.
KNOCK IT OFF!
          MAYBE.

In this case, when the word QUESTION is finished, it must drop from the return stack the address of the other coroutine before QUESTION exits. What happens if it doesn't? When QUESTION exits, it will exit into ANSWER where ANSWER left off. ANSWER will, on the next execution of CO switch with INTERPRET . When interpret exits, it will exit into ANSWER . When ANSWER switches again, it will be with QUIT . QUIT is an infinite loop which clears the return stack at the start of the loop. That will be the end of the coroutine switching.

Although some words in Fleet Forth can start out as high level then transition to low level and vice versa, the high level portions of mixed level words can use coroutines and even be a coroutine to another word.

Looking back at the second example with GREETINGS and HELLO , the number of coroutine switching in the two words is different. This causes the word which executed the other to be the one which exits to the other. Here is a simplified example.
Code:
// COROUTINE DEMO 1 COROUTINE
: GREETINGS
   CR ." GREETINGS!"        CO
   CR ." AS WELL AS I CAN." ;
: HELLO
   GREETINGS
   CR TAB ." HI! HOW DO YOU DO?" ;

And the result of executing HELLO .
Code:
GREETINGS!
          HI! HOW DO YOU DO?
AS WELL AS I CAN.

Only the word GREETINGS uses the coroutine word, CO . HELLO executes GREETINGS but also exits to GREETINGS. The part of GREETINGS before CO is executed, then GREETINGS switches with HELLO . The part of GREETINGS after CO is executed when HELLO exits. This can be used to advantage. One example is the word RB , the word which causes the word executing it to cause the value of BASE to be restored to what it was prior to the execution of RB .
Code:
: RB  ( -- )
   BASE @ R> 2>R  CO
   R> BASE ! ;

The only tricky thing here is to tuck the value of BASE under the return address.

It is possible to craft even more complex words, but care must be exercised. In the following examples, the word (ERR) displays the contents of all of Fleet Forth's stacks.

For each iteration of its loop, the word PROTO will cause execution to pass to the Forth thread which is the body of the word TEST . TEST exits to PROTO which conditionally continues the loop.
Code:
: TEST
   CR ." TESTING." (ERR) ;
: PROTO
   BEGIN
      ['] TEST >BODY >R CO
      DONE?
   UNTIL
   (ERR)
   CR ." PROTO FINISHED." ;

And here is an excerpt of a session log showing the execution of PROTO .
Code:
TESTING.
DATA: EMPTY
 AUX: EMPTY
 RET:
5610 (ERR)
57AF TEST
57C7 PROTO
21A0 INTERPRET
21EB QUIT

TESTING.
DATA: EMPTY
 AUX: EMPTY
 RET:
5610 (ERR)
57AF TEST
57C7 PROTO
21A0 INTERPRET
21EB QUIT

DATA: EMPTY
 AUX: EMPTY
 RET:
5610 (ERR)
57CF PROTO
21A0 INTERPRET
21EB QUIT

PROTO FINISHED.

This variation of PROTO will cause execution to pass to the Forth thread which is the portion of TEST which occurs after PROTO .
Code:
: PROTO
   BEGIN
      R@ >R CO
      DONE?
   UNTIL
   (ERR)  R> DROP
   CR ." PROTO FINISHED." ;
: TEST
   PROTO  CR ." TESTING." (ERR) ;

Notice this time TEST is executed directly, rather than PROTO . When the loop in PROTO finishes, there is an address of a Forth thread on the return stack. It points into the body of TEST just past where PROTO occurs in the definition of TEST . It must be dropped or that portion of TEST will be executed again. This would not matter for this trivial example, however it will be important later.
Here is an excerpt of a session log where TEST is executed.
Code:
TESTING.
DATA: EMPTY
 AUX: EMPTY
 RET:
5610 (ERR)
5783 TEST
5745 PROTO
5774 TEST
21A0 INTERPRET
21EB QUIT

TESTING.
DATA: EMPTY
 AUX: EMPTY
 RET:
5610 (ERR)
5783 TEST
5745 PROTO
5774 TEST
21A0 INTERPRET
21EB QUIT

DATA: EMPTY
 AUX: EMPTY
 RET:
5610 (ERR)
574D PROTO
5774 TEST
21A0 INTERPRET
21EB QUIT

PROTO FINISHED.

The following version of PROTO has a value which is incremented each time through the loop. A copy is kept on the return stack while another copy is left on the data stack for the thread of Forth from TEST to use.
Code:
: PROTO  ( -- N )
   0
   BEGIN
      1+
      DUP R@ 2>R CO R>
      DONE?
   UNTIL
   (ERR)
   R> DROP
   CR ." OUT OF PROTO LOOP"
   CR ." LOOPED " . ." TIMES" CR ;
: TEST
   0  PROTO  + (ERR) ;

And here is an excerpt of a session log where TEST is executed.
Code:
DATA:    1
 AUX: EMPTY
 RET:
5610 (ERR)
579C TEST
574B PROTO
   1
5798 TEST
21A0 INTERPRET
21EB QUIT

DATA:    3
 AUX: EMPTY
 RET:
5610 (ERR)
579C TEST
574B PROTO
   2
5798 TEST
21A0 INTERPRET
21EB QUIT

DATA:    6
 AUX: EMPTY
 RET:
5610 (ERR)
579C TEST
574B PROTO
   3
5798 TEST
21A0 INTERPRET
21EB QUIT

DATA:    6    3
 AUX: EMPTY
 RET:
5610 (ERR)
5755 PROTO
5798 TEST
21A0 INTERPRET
21EB QUIT

OUT OF PROTO LOOP
LOOPED 3 TIMES

TEST , or any other word which has PROTO , can not use the coroutine word CO after PROTO . If TEST had CO after PROTO , it would be like the other examples with coroutines in that TEST would switch to PROTO without exiting to it. The thread address would be left on the stack and PROTO would pull it from the return stack and use it as the value which is being incremented. The actual value being incremented would be used as the thread of Forth to switch with PROTO . This would most likely cause a crash when PROTO executes CO , depending on the value.
There is nothing preventing TEST from using CO before PROTO or using another word which uses CO , so long as TEST exits to PROTO rather than switching with it.
Code:
: AFTER-.   CO  TAB U. (ERR) ;
: TEST2   PROTO  AFTER-. ;

Here is an excerpt of a session log where TEST2 is executed.
Code:
          1
DATA: EMPTY
 AUX: EMPTY
 RET:
5610 (ERR)
57B4 AFTER-.
574B PROTO
   1
57C4 TEST2
21A0 INTERPRET
21EB QUIT
          2
DATA: EMPTY
 AUX: EMPTY
 RET:
5610 (ERR)
57B4 AFTER-.
574B PROTO
   2
57C4 TEST2
21A0 INTERPRET
21EB QUIT
          3
DATA: EMPTY
 AUX: EMPTY
 RET:
5610 (ERR)
57B4 AFTER-.
574B PROTO
   3
57C4 TEST2
21A0 INTERPRET
21EB QUIT

DATA:    3
 AUX: EMPTY
 RET:
5610 (ERR)
5755 PROTO
57C4 TEST2
21A0 INTERPRET
21EB QUIT

OUT OF PROTO LOOP
LOOPED 3 TIMES

Here is another example:
Code:
: TEST3
   PROTO  CR U. HELLO ;

And the session log excerpt where TEST3 is executed.
Code:
1
GREETINGS!
          HELLO THERE!
HOW DO YOU DO?
          AS WELL AS I CAN.
GOODBYE.
          BYE AND TAKE CARE.
THANK YOU.
2
GREETINGS!
          HELLO THERE!
HOW DO YOU DO?
          AS WELL AS I CAN.
GOODBYE.
          BYE AND TAKE CARE.
THANK YOU.
3
GREETINGS!
          HELLO THERE!
HOW DO YOU DO?
          AS WELL AS I CAN.
GOODBYE.
          BYE AND TAKE CARE.
THANK YOU.
DATA:    3
 AUX: EMPTY
 RET:
5610 (ERR)
5755 PROTO
58AE TEST3
21A0 INTERPRET
21EB QUIT

OUT OF PROTO LOOP
LOOPED 3 TIMES

As these examples show, TEST can have other coroutine words in its body after PROTO as long as TEST does not switch with PROTO , it must exit to PROTO because of the way PROTO works.

A few modifications to PROTO will change it into a simpler version of WITH-WORDS .
1) Use the value of CONTEXT and follow the link fields rather than increment the value.
2) Test for the end of a vocabulary and exit the loop by way of WHILE .
3) convert the copy of the value left on the data stack from the LFA to the NFA before CO is executed.
Code:
: WITH-WORDS  ( -- NFA )
   CONTEXT @
   BEGIN
      @ ?DUP
   WHILE
      DUP R@ 2>R  L>NAME CO
      R>
   REPEAT
   R> DROP ;

Here is the prototype for WORDS .
Code:
: WORDS  ( -- )
   WITH-WORDS  ID. SPACE ;

Here is a variation on Albert van der Horst's FOR-WORDS and WORDS which have been modified to work better with Fleet Forth.
Code:
// ALBERT'S FOR-WORDS
CODE CO-EXIT  ( R: ADR -- )
   PLA  PLA  NEXT JMP  END-CODE
: FOR-WORDS
   BEGIN
      @ ?DUP
   WHILE
      DUP CO
   REPEAT
   CO-EXIT ;
: WORDS
   CONTEXT @ FOR-WORDS
   BEGIN
      L>NAME ID. SPACE CO
   AGAIN ;

CO-EXIT ( exit the other coroutine) is just another name for RDROP , but the name CO-EXIT is more descriptive of what's happening in this case.
If both WITH-WORDS and FOR-WORDS used CO-EXIT ( or if both used the phrase R> DROP ), the combined size with with each one's respective version of WORDS would be the same, excluding the difference in name sizes. Actually, Albert's version could be made slightly smaller by reclaiming the two bytes used by EXIT , since that version of WORDS is an infinite loop.
Nevertheless, WITH-WORDS and the version of WORDS which uses it is a cleaner implementation. This version of WORDS is not itself a coroutine word. The only parameters on the data stack for the part of WORDS after WITH-WORDS is the NFA of a word in the context vocabulary. Words can be defined to use WITH-WORDS to:
Count the words in a vocabulary:
Code:
: #WORDS  ( -- N )
   0 WITH-WORDS  DROP 1+ ;

Or find the size of all the headers in a vocabulary:
Code:
: HEADERS  ( -- N )
   0 WITH-WORDS
   C@ $1F AND   \ get the size
   1+           \ add 1 for count byte
   2+           \ add 2 for size of link field
   + ;

The general rule for using WITH-WORDS in a word:
1) The part of a word which is before WITH-WORDS is executed once.
2) The part of a word after WITH-WORDS is executed once for each word in the context vocabulary.
3) The net affect WITH-WORDS has on the data stack is to leave the address of a word's NFA for use by that portion of threaded code which follows WITH-WORDS in a definition.
Here is a sample of the output of the simple version of WORDS presented so far:
Code:
WORDS WITH-WORDS ELAPSED CLEAR.TIME TIME TIME! .TIME SEXTAL BCD>INT (ERR)
                         .
                         .
                         .
CURRENT CONTEXT SPAN STATE >IN BLK FENCE

Here is Fleet Forth's WITH-WORDS and WORDS with all enhancements:
Code:
// WITH-WORDS WORDS
: WITH-WORDS  ( -- NFA )
   CONTEXT @
   AHEAD
   BEGIN
      DUP R@ 2>R L>NAME CO R>
   CS-SWAP THEN
      @ DUP 0= DONE? OR
   UNTIL
   R> 2DROP ;
: WORDS  ( -- )
   COLS ?CR
   WITH-WORDS DUP C@ $1F AND 1+ ?CR
   ID. TAB ;

And a sample of the output when COLS , a VALUE , is set to 70.
Code:
WORDS     WITH-WORDS          ELAPSED   CLEAR.TIME          TIME
TIME!     .TIME     SEXTAL    BCD>INT   (ERR)     FILES     IN-NAME
                         .
                         .
                         .
SPAN      STATE     >IN       BLK       FENCE     

Albert van der Horst also introduced the word AFTER-DROP , which drops an item from the data stack after the word in which it is used exits. In the case of a word which uses WITH-WORDS , If AFTER-DROP occurs before WITH-WORDS then an item is dropped from the data stack after the word which uses WITH-WORDS exits.
Code:
: AFTER-DROP
   CO DROP ;

Here is a trivial example to demonstrate.
Code:
: WORDS-3DROP
   AFTER-DROP AFTER-DROP AFTER-DROP
   WITH-WORDS
   ID. SPACE ;

This word will display all the words in the context vocabulary AND drop three items from the data stack.
Not a very useful word, I know. Here is one that is:
Code:
: IN-NAME  ( ADR -- )
   AFTER-DROP
   WITH-WORDS
   2DUP
   COUNT $1F AND >HERE CLIP COUNT
   2+ TRUE UNDER+
   BL HERE C!
   ROT COUNT MATCH NIP
   IF  CR ID. EXIT  THEN
   DROP ;

IN-NAME takes the address of a counted string ( the sub string) and displays the name of every word in the context vocabulary which contains that string in its name. The drop in AFTER-DROP drops the address of the sub string at the conclusion of IN-NAME .
Here is an excerpt from a session log where I've added comments.
Code:
" AD" IN-NAME  \ typed by me. substring: 'AD'
 .ADDR
 ADMODE
 ADDRESSES
 EAD
 SAD
 ADEPTH
 AHEAD
 LOAD
 LINELOAD
 ADD
 PAD  OK
"  AD" IN-NAME \ typed by me. substring: ' AD'
 ADMODE
 ADDRESSES
 ADEPTH
 ADD  OK
" AD " IN-NAME \ typed by me. substring: 'AD '
 EAD
 SAD
 AHEAD
 LOAD
 LINELOAD
 PAD  OK

The lines with the comment "typed by me. substring: 'xxx'" were entered at Forth's command line. Everything else in this excerpt is the computer's response.


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 354 posts ]  Go to page Previous  1 ... 9, 10, 11, 12, 13, 14, 15 ... 24  Next

All times are UTC


Who is online

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