6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Sat Apr 27, 2024 5:14 pm

All times are UTC




Post new topic Reply to topic  [ 335 posts ]  Go to page Previous  1 ... 19, 20, 21, 22, 23
Author Message
PostPosted: Sun Jan 07, 2024 10:16 pm 
Offline

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

In my post describing how Fleet Forth handles the text stream I did not show the source for Fleet Forth's WORD . I thought it might be a distraction. Here is the source for Fleet Forth's WORD .
Code:
2VARIABLE HISTORY
: WORD  ( C -- HERE )
   'STREAM                  \ Return address and count of remaining text stream.
   BLK 2@ HISTORY 2!        \ Save the contents of BLK and >IN to HISTORY.
   DUP >IN +!               \ Use count from 'STREAM to push >IN past text stream.
   2PICK SKIP               ( delimiter address count )
   ROT 2PICK -ROT SCAN      ( address address2 count2 )
   1- 0 MAX NEGATE >IN +!   \ Use count from SCAN to pull >IN to correct offset.
   OVER - >HERE ;

'STREAM returns the address and length of the remaining text stream.
After the history is saved for use by WHERE and THRU , >IN is set to point just past the text stream.
SKIP returns the address in the text stream of the sought after text and the length of the remaining text stream.
A copy of this address is saved under the address length and delimiter needed by SCAN .
SCAN returns the address of the delimiter after the sought after text and the length of the remaining text stream. One is subtracted from the length. The maximum of this value and zero is subtracted from >IN to put the offset past the delimiter but no further than the end of the text stream.
All that remains are the addresses of the start of the sought text and the address of the delimiter after the sought text. These are converted to the address and count.
Before >HERE is executed, the address of the string and count are on the stack. The address in the text stream, just like Ansi Forth's PARSE-WORD .
>HERE stores this string at HERE as a counted string with a trailing blank. >HERE is a code word for speed.
I could have defined WORD like this:
Code:
2VARIABLE HISTORY
: PARSE-WORD  ( C -- ADR CNT )
   'STREAM                  \ Return address and count of remaining text stream.
   BLK 2@ HISTORY 2!        \ Save the contents of BLK and >IN to HISTORY.
   DUP >IN +!               \ Use count from 'STREAM to push >IN past text stream.
   2PICK SKIP               ( delimiter address count )
   ROT 2PICK -ROT SCAN      ( address address2 count2 )
   1- 0 MAX NEGATE >IN +!   \ Use count from SCAN to pull >IN to correct offset.
   OVER - ;
: WORD  ( C -- HERE )
   PARSE-WORD >HERE ;

However, I saw no need for PARSE-WORD . Fleet Forth's find primitives require the address of a counted string rather than the address of a string and a count and CREATE needs a counted string at HERE .
Fleet Forth's 'TEXT returns the address of the string in the text stream and the length. It is like Ansi Forth's PARSE with one difference. If the delimiter is not found in the text stream 'TEXT aborts with an error message.
Code:
: 'TEXT  ( C -- ADR CNT )
   DUP>R LIT [ HERE >A 0 , ] C!  \ Store delimiter in ABORT" string.
   'STREAM 2DUP R>               ( ADR LEN ADR LEN C )
   SCAN 0=                       ( ADR LEN ADR2 FLAG )
   [ HERE 3 + A> ! ]             \ If text stream exhausted after SCAN
   ABORT"   MISSING"             \ then the delimiter was not found
   NIP OVER -                    ( ADR CNT )
   DUP 1+ >IN +! ;

'STREAM returns the address and length of the remaining text stream. A copy of the delimiter is stored in the inline string used by ABORT" . After SCAN executes, the address and length 'STREAM placed on the data stack is still there as well as the address of the first occurrence of the delimiter and the length of the text stream after this point. If this length is zero, the delimiter was not found so abort with an error message.
The length returned by 'STREAM is no longer needed so it is NIPped off the data stack and the two addresses are used to calculate the length of the string returned by 'TEXT . A copy of this length plus one is added to >IN .

[Edit: Corrected an error.]


Top
 Profile  
Reply with quote  
PostPosted: Mon Apr 01, 2024 1:48 pm 
Offline

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

An error in one of the machine states in a demo for A Finite State Machine Engine in Forth revealed a shortcoming in Fleet Forth's error handling.
My initial assumption was that words run as background tasks would be fully debugged before set as background tasks. That assumption was in error. When a background task is a word to run through a list of finite state machines, every machine state for every machine installed must be error free. Ideally, a finite state machine should be fully tested before it is installed as one of the machines running in the background, but mistakes do happen.
I've improved Fleet Forth's error handling. Since error reporting uses WHERE to report the location of the error, WHERE was modified to include SINGLE and UNLINK .
Code:
: WHERE
   ['] EXIT (IS) RECURSE
   SINGLE UNLINK            \ <- This line was added to switch
                            \ off multitasking and reset
                            \ the user pointer and user area's
                            \ first six user variables.
   HISTORY 2@ TUCK  BLK 2!
    ...

SINGLE switches off multitasking and UNLINK points UP (the user pointer) back at the main user area and resets the first six user variables to their boot-up values from the boot area. This causes the main user area's ENTRY to point back to itself, make sure the main task is awake and reset the default stack bases for the return, data, and auxiliary stacks.

With these changes, an ABORT" in one of the machine states for a finite state machine running in the background no longer causes problems in Fleet Forth.


Top
 Profile  
Reply with quote  
PostPosted: Wed Apr 03, 2024 4:44 pm 
Offline

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

One version of the Finite State Machine Engine runs the chain of state machines in the background as a background task. It had the word FSM to initialize the background machine chain and link it into the loop of round robin tasks.
Code:
: FSM  ( -- )
   [ ' <FSM> >BODY ] LITERAL
   BACKGROUND-MACHINE-CHAIN ACTIVATE
   BACKGROUND-MACHINE-CHAIN LINK-TASK
   MULTI ;

This word could not be executed more than once or the task for the background machine chain would be linked more than once, breaking the loop. I saw this as a design flaw in Fleet Forth's multitasker so I fixed it.
I added the word LINKED? to test if a task is already linked. Rather than burden the user with testing if a task is linked, I added this word to LINK-TASK , the word which links tasks. If a task is already linked, LINK-TASK does not try to link it again. It is idempotent.
Code:
: LINKED?  ( TADR -- FLAG )
   ENTRY
   BEGIN
      @ DUP 2PICK <>
   WHILE
      DUP ENTRY =
   UNTIL
      0=
   THEN
   0<>  NIP ;

: LINK-TASK  ( TADR -- )
   DUP LINKED?
   IF  DROP EXIT  THEN
   ENTRY 2DUP @ SWAP! ! ;



Top
 Profile  
Reply with quote  
PostPosted: Thu Apr 18, 2024 11:49 pm 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 851
JimBoyd wrote:

Here is DOWN-COUNTER for Fleet Forth.
Code:
: DOWN-COUNTER
   2VARIABLE  ( -- )
   DOES>  ( -- ADR )
      JIFFY@ DROP
      OVER 2+ @
      OVER -  0 MIN
      2PICK +!
      OVER 2+ ! ;

That '0 MIN' is to handle the case when the jiffy timer resets to zero when it reaches twenty four hours.
A down-counter, a child word of DOWN-COUNTER , has two cells of storage. The first cell holds a value. The second cell holds the low cell of the Commodore 64 jiffy clock from the last time the down-counter was executed. Each time a down-counter is executed the amount of time, in jiffies, which passed from the last time is subtracted from the value in the first cell.
Code:
DOWN-COUNTER DELAY1 OK
300 DELAY1 ! OK
DELAY1 ? 209  OK
DELAY1 ? 118  OK
DELAY1 ? 5  OK
DELAY1 ? -445  OK

Of course, the values returned each time I type "DELAY1 ?" depends on how long I wait.

Here is a slightly improved version of DOWN-COUNTER .
Code:
: DOWN-COUNTER
   2VARIABLE  ( ++ )
   DOES>  ( -- ADR )
      JIFFY@ DROP           \ Only need low cell of jiffy clock
      OVER 2@ SWAP 2PICK -  \ new.time value -delta.time
      0 MIN                 \ Compensate for reset at midnight.
      +  2PICK 2! ;         \ add negative time difference to
                            \ old value and store new time
                            \ and new value to down-counter
                            \ variable and leave address.

JIFFY@ leaves the value of the Commodore 64 jiffy clock on the stack as a double number. Yes the jiffy clock resets to zero after twenty four hours.
2PICK is syntactically equivalent to 2 PICK .
Code:
CODE 2PICK  ( N1 N2 N3 -- N1 N2 N3 N1 )
   4 ,X LDA  5 ,X LDY
   AYPUSH JMP  END-CODE

A DOWN-COUNTER is used when some code is run periodically, but there is a portion which must be run less often.
Code:
DOWN-COUNTER DELAY

     .
     .
     .
   DOWN-COUNTER @ 0<          \
   IF                         \ If this code fragment is run often enough
      #3600 DOWN-COUNTER !    \ <DO.SOMETHING> only runs
      <DO.SOMETHING>          \ once a minute.
   THEN                       \
     .
     .
     .



Top
 Profile  
Reply with quote  
PostPosted: Tue Apr 23, 2024 10:37 pm 
Offline

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

Fleet Forth is not a multi user Forth system. It was designed for one person to use at a time on a Commodore 64. Fleet Forth supports multitasking with background tasks. With this in mind the Fleet Forth system has only ten user variables. More user variables can be added.
The user area for the foreground task is not part of the loadable image, it is in a section of unused memory on the Commodore 64; therefore, the first eight user variables need initialized at bootup. Fleet Forth's boot area holds the bootup values for the first seven user variables.
Code:
ENTRY  -- Points to next task's user area or itself if no other tasks.
READY  -- Flag. TRUE - task is awake. FALSE - task is asleep.
TOS    -- Holds the return stack pointer when a task is switched out.
RP0    -- Base of return stack.
SP0    -- Base of data stack.
AP0    -- Base of auxiliary stack.
DP     -- Dictionary Pointer.

The first six user variables are initialized by the word UNLINK , which also sets the user pointer UP to the address of the foreground task's user area. This unlinks all background tasks and restores control to the main task. UNLINK is included in a routine included in the coldstart and warmstart routines.
The word EMPTY initializes DP from this bootup area and branches into the body of FORGET to handle any necessary pruning. EMPTY is included in the coldstart routine.
These are the next three user variables:
Code:
BASE   -- Base for numeric conversion to or from text.
DPL    -- Decimal place.
HLD    -- Holds the address for the next character during conversion
          from text to a double number.

DECIMAL , which sets BASE to decimal, is included in the routine which is included in the coldstart and warmstart routines.

DPL and HLD are not initialized during coldstart or warmstart. It is not required. NUMBER? initializes DPL and <# initializes HLD .
The Fleet Forth system has only ten user variables. The other variables in the system are not user variables as they should not be needed in background tasks.

As I mentioned, Fleet Forth allows the defining of new user variables. The word for this is USER . It takes a number and is followed by a name.
Code:
<USER.AREA.OFFSET> USER <NAME.OF.USER.VARIABLE>

For example:
Code:
#20 USER STOOGES

To make defining new user variables easier (some may be defined in different sources), Fleet Forth has a 'system value' or 'soft constant' #USER .
The source for Fleet Forth's USER .
Code:
: USER  ( N -- )
   DUP 2+ #USER UMAX (IS) #USER
   CREATE C,
   ;CODE  ( -- N )
      2 # LDY  CLC
      W )Y LDA  UP ADC
      UP 1+ LDY  CS IF  INY  THEN
      AYPUSH JMP  END-CODE

If #USER has the value #20, the following:
Code:
#USER USER LARRY
#USER USER CURLY
#USER USER MOE

will create the user variable LARRY with the user area offset #20, the user variable CURLY with the user area offset #22, and the user variable MOE with the user area offset #24.
To set aside more of the user area for a particular user variable, the following can be done.
Code:
#USER USER LAUREL
#USER 8 + USER HARDY

This will 'allot' eight more bytes in the user area for the user variable LAUREL .


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 335 posts ]  Go to page Previous  1 ... 19, 20, 21, 22, 23

All times are UTC


Who is online

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