6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Thu Nov 21, 2024 10:34 am

All times are UTC




Post new topic Reply to topic  [ 23 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Thu Jan 25, 2024 12:55 am 
Offline

Joined: Sat Dec 12, 2015 7:48 pm
Posts: 145
Location: Lake Tahoe
One of my goals for Forth in PLASMA was to easily write scripts to do things like installations. PLFORTH has access to all things PLASMA so I decided to show off my rudimentary BATCH style scripts for installing the PLASMA system on a hard disk. PLASMA has grown to fill many floppies @140K so installing to a hard drive (or some such medium 800K or larger) has become necessary. It has had to be done by hand up until now. You may ask why I would choose to run Forth in an interpreted mode instead of compiling it and running it that way, and I probably couldn't give you a very good reason except it feels more *BATCHy* that way. And you can intersperse word definitions, variable declarations and such in the most unstructured way possible. The biggest challenge was writing an immediate-mode conditional. I checked out some others' solutions but I decided to not compile any code and operate on the input stream directly. Since an install script is my first test, here is a snippet of the scripts:
Code:
CONFIRM" Copy build tools?"
?EXEC
  DEST " BLD" STRCAT DROP
  " NEWDIR" DEST LOADMOD
  INSERT.FLOPPY" PLASMA.BLD"
  FILELIST " /PLASMA.BLD/BLD/PLASM /PLASMA.BLD/BLD/CODEOPT " STRCPY
  DEST STRCAT
  " COPY" SWAP LOADMOD
  FILELIST " -R /PLASMA.BLD/BLD/INC " STRCPY DEST STRCAT
  " COPY" SWAP LOADMOD

  CONFIRM" Copy sample PLASMA code?"
  ?EXEC
    FILELIST " -R /PLASMA.BLD/BLD/SAMPLES " STRCPY DEST STRCAT
    " COPY" SWAP LOADMOD
  RESUME>

  CONFIRM" Copy sample FORTH scripts?"
  ?EXEC
  INSERT.FLOPPY" PLASMA.4TH"
    FILELIST " -R /PLASMA.4TH/BLD/SCRIPTS " STRCPY DEST STRCAT
    " COPY" SWAP LOADMOD
  RESUME>

RESUME>


DEST and FILELIST are strings that get built up preceding the call to LOADMOD which takes a module name and command line string. The LOADMOD sequence is setting up the command line to pass to the COPY and NEWDIR executable modules. Usually run interactively from the command line, PLFORTH is able to programmatically build the command line parameters and then call them with the module (all things in PLASMA are modules).

Two of the interesting words here are ?EXEC and RESUME>. The idea is to conditionally execute the code between them. I also needed them to nest. I'll admit to being quite the n00b when it comes to programming in Forth, so be kind.

Here is what I came up with for these words:
Code:
: RESUME> ; ( PLACE HOLDER TO RESUME EXECUTION )
: ?EXEC ( F -- )
  NOT IF ( SKIP CODE IN BETWEEN ?EXEC AND RESUME> )
    1 >R
    BEGIN
      BL WORD FIND IF
        CASE
          ' RESUME> OF
            R> 1- ?DUP 0= IF ( EXIT IF FINAL RESUME> )
              DROP EXIT
            THEN
            >R
            ENDOF
          ' ?EXEC OF ( CHECK FOR NESTED ?EXEC )
            R> 1+ >R
            ENDOF
        ENDCASE
      ELSE
        DROP
      THEN
    AGAIN
  THEN
;


The other words are pretty small and just get input from the user to confirm whether to conditionally execute the following code or not and wait for a key press when a floppy disk needs to be swapped in. And it all works just as I envisioned.


Top
 Profile  
Reply with quote  
PostPosted: Thu Jan 25, 2024 7:22 pm 
Offline

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

Have you considered [IF] [ELSE] and [THEN]?


Top
 Profile  
Reply with quote  
PostPosted: Thu Jan 25, 2024 11:22 pm 
Offline

Joined: Sat Dec 12, 2015 7:48 pm
Posts: 145
Location: Lake Tahoe
....and just like that I screwed it up. Anyone catch where I referenced ?EXEC inside the definition of ?EXEC ? In cursory testing it worked until I said 'No' to the build tools but it still asked for the Forth sample scripts. The ' ?EXEC inside the definition actually compiled a zero so it never matched and threw the whole nested count off.

Here is the revised version that uses variables to capture the addresses of ?EXEC and RESUME> for later testing. I know how other languages refer to functions not yet defined (PLASMA uses predef), but I'm unsure how Forth handles it.

Code:
0 VARIABLE RESUMEXT
0 VARIABLE EXECXT

: RESUME> ; ( PLACE HOLDER TO RESUME EXECUTION )
' RESUME> RESUMEXT !

: ?EXEC ( F -- )
  NOT IF ( SKIP CODE IN BETWEEN ?EXEC AND RESUME> )
    1 >R
    BEGIN
      BL WORD FIND IF
        CASE
          RESUMEXT @ OF
            R> 1- ?DUP 0= IF ( EXIT IF FINAL RESUME> )
              DROP EXIT
            THEN
            >R
            ENDOF
          EXECXT @ OF ( CHECK FOR NESTED ?EXEC )
            R> 1+ >R
            ENDOF
        ENDCASE
      ELSE
        DROP
      THEN
    AGAIN
  THEN
;
' ?EXEC EXECXT !


Last edited by resman on Thu Jan 25, 2024 11:39 pm, edited 2 times in total.

Top
 Profile  
Reply with quote  
PostPosted: Thu Jan 25, 2024 11:37 pm 
Offline

Joined: Sat Dec 12, 2015 7:48 pm
Posts: 145
Location: Lake Tahoe
JimBoyd wrote:

Have you considered [IF] [ELSE] and [THEN]?


I had not. I will admit that Forth naming conventions drive me a little crazy. Bracketed, parenthesized, etc. version of other words seems to be more confusing than just picking a new name. I understand that Chuck M. went more down the path of different words for interpreted vs. compiled versions. I can't decide if that is beneficial or not. In my case, I decided on names that seem to flow better with the use case of a batch file environment, but that could just be me. I went with the '?' in front if EXEC because its a conditional, and the following '>' on RESUME to add closure but also avoid the case where a valid usage of RESUME existed, in a string for instance.

Do you feel its better to stick with (one of) the Forth standard, or go with what feels consistent with the particular usage? I'm definitely on the fence. And thanks for your time!


Top
 Profile  
Reply with quote  
PostPosted: Fri Jan 26, 2024 12:02 am 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 895
resman wrote:
I know how other languages refer to functions not yet defined (PLASMA uses predef), but I'm unsure how Forth handles it.
DEFER.
' ( tick ) is not immediate on some Forth systems. See here and here.
Referencing RESUME> should work fine. It is defined before ?EXEC .
As for ?EXEC , LATEST is a non-standard word which returns the name field of the latest word defined in the current vocabulary.
Instead of:
Code:
   ['] ?EXEC

I would have used this:
Code:
   [ LATEST NAME> ] LITERAL

and avoided the unnecessary variables.


Top
 Profile  
Reply with quote  
PostPosted: Fri Jan 26, 2024 12:07 am 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 895
resman wrote:
Do you feel its better to stick with (one of) the Forth standard, or go with what feels consistent with the particular usage? I'm definitely on the fence. And thanks for your time!
I like to stick to the Forth-83 Standard for the most part. I try to follow common usage for words not defined in the Forth-83 Standard. One example is when I had a word to discard the parameters of a DO LOOP without exiting the word containing the loop. I called this word UNDO . When I found out there was a word called UNLOOP which did the same thing, I changed the name of my Forth's UNDO to UNLOOP .


Top
 Profile  
Reply with quote  
PostPosted: Fri Jan 26, 2024 2:14 am 
Offline

Joined: Sat Dec 12, 2015 7:48 pm
Posts: 145
Location: Lake Tahoe
Thank you very much for the information on LATEST and UNLOOP! Added both to PLFORTH which nicely fixed my ?EXEC implementation and cleaned up one of my samples that exited inside three LOOPs. Much better. I'm slowly wrapping my head around the evolution of Forth to the semi-modern days.


Top
 Profile  
Reply with quote  
PostPosted: Tue Jan 30, 2024 9:50 pm 
Offline

Joined: Wed Aug 21, 2019 6:10 pm
Posts: 217
resman wrote:
JimBoyd wrote:

Have you considered [IF] [ELSE] and [THEN]?


I had not. I will admit that Forth naming conventions drive me a little crazy. Bracketed, parenthesized, etc. version of other words seems to be more confusing than just picking a new name.


One thing is to disentangle the two. Some people building Forths like to use () around names for inner routines that are not intended to be used in "userland", and/or for names of routines (either Forth or machine language stubs) that are not appropriate for the outer-interpreter to execute.

For instance, in a direct-threaded Forth, a compiled word starts with a call or jump to a hardware routine which pushes the IP and gets things set up for the inner interpreter to execute the following list of word addresses. The people who like the parentheses words might (ENTER) or (LIST) ... (list of word addresses, that is, not "print to screen") ... but I don't like that style of naming, and I'd call it doENTER or doLIST.

The bracket-y words are something else. When compiling, "... [ <name> ] ..." drops into interpreting state, executes <name>, and then goes back into compiling mode.

So if you have a word that is NOT immediate, which you would often use while compiling with [ ] brackets around, and then you make an immediate version of that word with behavior that works whether you are in compiler or interpreter state, it's a common shorthand to put brackets around it.

And that is what [IF] ... [ELSE] ... [THEN] ... are. They are extremely useful for conditional compilation, and in that context, the surrounding brackets are a visual reminder that they are going to execute, not be compiled.

They are also quite useful for batch processing. In that context, the immediacy is not an issue, but even if you mostly use them for batch processing, using them in the form that also works well when doing conditional compilation means that you already have that tool in your toolkit when needed.

And once you have them, you have multi-line comments for free, using the "0 [IF] ... [THEN]" pattern.

Quote:
I understand that Chuck M. went more down the path of different words for interpreted vs. compiled versions.


While I think that the question of the style of Forth that is most convenient to use to program for matrix networks of Forth processors is an intriguing one, most people who would be using Forth for your kind of application stick to conventional Forth.

Quote:
...
Do you feel its better to stick with (one of) the Forth standard, or go with what feels consistent with the particular usage? I'm definitely on the fence. And thanks for your time!


Note that if you are defining a word with behavior that isn't in a standard word, the most important thing is to avoid giving it the same name as a standard word, which will end up causing confusion if people are trying to refine it or port it for their own use. If you have a word that doesn't work like Forth20xx [IF] ... definitely don't CALL it [IF].

And if you are doing batch processing, you'll want more than just [IF] [ELSE] [THEN] ... you need a batch processing form of a looping constrict, for example ... so there is plenty of opportunity to name your own words your own way.


Top
 Profile  
Reply with quote  
PostPosted: Tue Jan 30, 2024 10:39 pm 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8543
Location: Southern California
BruceRMcF wrote:
resman wrote:
JimBoyd wrote:

Have you considered [IF] [ELSE] and [THEN]?


I had not. I will admit that Forth naming conventions drive me a little crazy. Bracketed, parenthesized, etc. version of other words seems to be more confusing than just picking a new name.


One thing is to disentangle the two. Some people building Forths like to use () around names for inner routines that are not intended to be used in "userland", and/or for names of routines (either Forth or machine language stubs) that are not appropriate for the outer-interpreter to execute.

For instance, in a direct-threaded Forth, a compiled word starts with a call or jump to a hardware routine which pushes the IP and gets things set up for the inner interpreter to execute the following list of word addresses. The people who like the parentheses words might (ENTER) or (LIST) ... (list of word addresses, that is, not "print to screen") ... but I don't like that style of naming, and I'd call it doENTER or doLIST.

I haven't liked the (<NAME>) thing because in the instances you might want to comment-out something with (...) and resume on the same line, the ) of the name might prematurely end the commenting.  That's a rare situation, but I have seen it.

The do<NAME> thing takes more room on the line than just putting the name in lower-case, the latter being what I grew up with, so for example do is compiled by DO, loop is compiled by LOOP, etc..  I know Jim hasn't done that because the C64 didn't have lower-case which is extra unfortunate because the editing window was so narrow, 40 characters, I believe, not even 64, let alone 80 or 132..


Quote:
Note that if you are defining a word with behavior that isn't in a standard word, the most important thing is to avoid giving it the same name as a standard word, which will end up causing confusion if people are trying to refine it or port it for their own use. If you have a word that doesn't work like Forth20xx [IF] ... definitely don't CALL it [IF].

Common usage, in effect, is a standard in itself, even if something hasn't make it into an official standards document yet.  There have been many words that fit this category over the years.  I try not to muddy things by adding yet more names for the same things.

_________________
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: Tue Jan 30, 2024 11:10 pm 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 895
GARTHWILSON wrote:
I haven't liked the (<NAME>) thing because in the instances you might want to comment-out something with (...) and resume on the same line, the ) of the name might prematurely end the commenting.  That's a rare situation, but I have seen it.

The do<NAME> thing takes more room on the line than just putting the name in lower-case, the latter being what I grew up with, so for example do is compiled by DO, loop is compiled by LOOP, etc..  I know Jim hasn't done that because the C64 didn't have lower-case which is extra unfortunate because the editing window was so narrow, 40 characters, I believe, not even 64, let alone 80 or 132..
What I have done is to give a run-time word the same name as the word which compiles it. The run-time word DO is compiled by the compiling word DO and the run-time word LOOP is compiled by the compiling word LOOP .
The source for SEE creates a pseudo vocabulary on the auxiliary stack so the runtime is found rather than the word which compiles it. Since, in Fleet Forth, a failed search of the CONTEXT vocabulary results in a search of the CURRENT vocabulary, all the words in the FORTH vocabulary can still be found; however, the ones defined where the pseudo vocabulary starts are searched first.
The run-time words are defined before the word FORTH-83 and their compile time counterparts are defined after.
By using a pseudo vocabulary I can show the source for both words named LOOP .
Code:
SEE LOOP
LOOP IMMEDIATE
 11745  8372 COMPILE
 11747  2226 LOOP
 11749  4867 2-
 11751 11524 <RESOLVE
 11753  4867 2-
 11755 11501 >RESOLVE
 11757  2472 EXIT
14
 OK
0 ' FORTH-83 >LINK 2>A AP0 @ @ CONTEXT !  OK
SEE LOOP
LOOP
  2228          PLA
  2229          TAY
  2230          INY
  2231  2299    BNE ' ?BRANCH >BODY 22 +
  2233          SEC
  2234          PLA
  2235     0  # ADC
  2237  2298    BVC ' ?BRANCH >BODY 21 +
  2239  2183 ^^ BVS ' LEAVE >BODY 2 +
13
 OK
FORTH  OK
CONSOLE



Top
 Profile  
Reply with quote  
PostPosted: Wed Jan 31, 2024 2:17 am 
Offline

Joined: Wed Aug 21, 2019 6:10 pm
Posts: 217
GARTHWILSON wrote:
... The do<NAME> thing takes more room on the line than just putting the name in lower-case, the latter being what I grew up with, so for example do is compiled by DO, loop is compiled by LOOP, etc..  I know Jim hasn't done that because the C64 didn't have lower-case which is extra unfortunate because the editing window was so narrow, 40 characters, I believe, not even 64, let alone 80 or 132..


Yes, while the Commander X16 allows 80 column text, if the same source is going to work in ASCII mode, in PETSCII Graphics mode and PETSCII Upper/Lower mode, the words should be all upper case in ASCII & PETSCII Graphics, which means they are lower case in PETSCII Upper/Lower mode.

Horses for courses.


Top
 Profile  
Reply with quote  
PostPosted: Tue Feb 06, 2024 3:10 pm 
Offline

Joined: Sat Dec 12, 2015 7:48 pm
Posts: 145
Location: Lake Tahoe
Many thanks for everyone's contributions. I started out targeting the old figForth words and as such did use the '(' ')' for the compiled version of the immediate words. As I am also targeting the Apple ][ (among other models), upper-case only and 40 columns is one of the limitations for name choices. After all the feedback (and great links - thanks Jim) I've been slowly aligning my word names with more current implementations. While writing more demo scripts to exercise different parts of the system, the need for DEFER came up. Although not a batch script, it shows off the power of DEFER/IS. I realized DEFER is nothing more than a function (word) pointer and can be used with multiple IS invocations to alter its function. To that end, it works as a nice plotting calculator - just update the DEFERed function with IS and get a new plot. Here is a simple program with a tall stack of PLASMA libraries implementing floating point through the Apple SANE library (quite extensive but very slow) and hi-res graphics:
Code:
DEFER FUNC

: PLOTFUNC
  3 HGRCOLOR
  279 0 DO
    I DUP 139 - >FPINT XSCALE FPEXT@ FP*
    FUNC
    YSCALE FPEXT@ FP* FPINT> 95 +
    OVER 1+ OVER HGRPLOT HGRPLOT
  LOOP
;

' FPSIN IS FUNC
DRAWAXIS PLOTFUNC PLOTDONE

' FPCOS IS FUNC
DRAWAXIS PLOTFUNC PLOTDONE


(yes, I realize the plots are inverted. WIP)


Attachments:
hrplotcos.png
hrplotcos.png [ 8.99 KiB | Viewed 1427 times ]
hrplotsin.png
hrplotsin.png [ 8.87 KiB | Viewed 1427 times ]
Top
 Profile  
Reply with quote  
PostPosted: Wed Feb 07, 2024 4:27 pm 
Offline

Joined: Wed Aug 21, 2019 6:10 pm
Posts: 217
One setting where DEFER can be real handy is where you have mutually recursive words -- say, a pair of words where each word may, depending on state, call the other word. If you define "Word1" first, and "Word2" second, you can:

DEFER Word1

: Word2 ( ... ) ... ;

:NONAME \ Word1 ( ... )
... ; IS Word1

(or if you don't have :NONAME, " : doWord1 ( ... ) ... ; ' doWord1 IS Word1")


Top
 Profile  
Reply with quote  
PostPosted: Wed Feb 07, 2024 11:34 pm 
Offline

Joined: Sat Dec 12, 2015 7:48 pm
Posts: 145
Location: Lake Tahoe
BruceRMcF wrote:
One setting where DEFER can be real handy is where you have mutually recursive words -- say, a pair of words where each word may, depending on state, call the other word. If you define "Word1" first, and "Word2" second, you can:

DEFER Word1

: Word2 ( ... ) ... ;

:NONAME \ Word1 ( ... )
... ; IS Word1

(or if you don't have :NONAME, " : doWord1 ( ... ) ... ; ' doWord1 IS Word1")


Yes, I believe this was the original intent of DEFER, to pre-declare a function so it can be referenced before being defined. But given the implementation, it can be so much more. I'm a fan of function pointers, so I'll be using this quite a bit.


Top
 Profile  
Reply with quote  
PostPosted: Thu Feb 08, 2024 3:04 am 
Offline

Joined: Wed Aug 21, 2019 6:10 pm
Posts: 217
resman wrote:
... I'm a fan of function pointers, so I'll be using this quite a bit.


If you are a fan of function pointers, I hope you have seen mini-oof ("mini-object-oriented-forth): https://bernd-paysan.de/mini-oof.html


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

All times are UTC


Who is online

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