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

All times are UTC




Post new topic Reply to topic  [ 354 posts ]  Go to page Previous  1 ... 19, 20, 21, 22, 23, 24  Next
Author Message
PostPosted: Tue Nov 07, 2023 11:42 pm 
Offline

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

One feature of VICE I have used in moderation is the ability to paste text into the simulator. Fleet Forth behaves as though the text is typed in. This does introduce a problem. When loading source from screens on blocks or even from a Commodore sequential file, an ABORT stops the load and returns control to Forth's QUIT loop. When text is pasted into the simulator the QUIT loop still has control. an ABORT stops interpreting text which has already been EXPECTed, or ACCEPTed; however, the simulator continues to fill the keyboard buffer until all pasted text has been inserted into the keyboard buffer. The behavior is exactly as if a programmer typing in source ignores errors and continues typing.
Fleet Forth's ABORT , which is called by ABORT" , is extensible.
Code:
: ABORT  ( -- )
   SINGLE ERR SP! AP! QUIT -;

SINGLE switches off multitasking.
SP! and AP! clear the data and auxiliary stacks.
ERR is a deferred word which normally executes NOOP a Forth no-op.
I set ERR to the word PURGE when I wish to paste Forth source from a Linux text file.
Code:
: PURGE           // EMPTY KEYBOARD
   BEGIN          // BUFFER
      BEGIN
         KEY?     // ANY KEYSTROKES?
      WHILE
         KEY DROP
      REPEAT
      1 JIFFIES   // WAIT 1/60 SEC
      KEY? 0=     // BUFFER EMPTY?
   UNTIL ;

KEY? is an AnsiForth word which returns TRUE if a character is available.
The 1 jiffy delay is to give VICE time to refill the keyboard buffer. The inner loop empties the keyboard buffer fast enough that without the delay, PURGE returns before VICE is finished adding pasted text to the buffer.


Top
 Profile  
Reply with quote  
PostPosted: Sun Nov 12, 2023 8:01 pm 
Offline

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

I should have mentioned that keyboard input in Fleet Forth causes the cursor to blink while waiting for input. When PURGE is used to purge the pasted text after an error occurs, I know it is done when the cursor resumes blinking at a steady rate.


Top
 Profile  
Reply with quote  
PostPosted: Thu Nov 23, 2023 4:55 am 
Offline

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

In the Fleet Forth kernel there are eighteen headerless words. Words like the one formerly named TRAVERSE or some of the words used by the sector read/write word, SR/W . These headerless words have no use outside the kernel, which is why they are headerless. There is another group of words which are not directly used outside the kernel, the run-time words of other words.

Some of the run-time words now have the same name as the associated compiling word.
Code:
DO     DO
?DO    ?DO
LOOP   LOOP
+LOOP  +LOOP
"      "
."     ."
ABORT" ABORT"

(IS) is still the run-time for IS and TO .
The run-time words are defined before the word FORTH-83 and the compiling words are defined after. This made updating the source for SEE easier.
To find the run-time words, the FORTH vocabulary is set as both the CONTEXT and CURRENT vocabularies. Create a false vocabulary on the auxiliary stack with FORTH-83 as the latest word with the following:
Code:
0 ' FORTH-83 >LINK 2>A

and make it the CONTEXT vocabulary.
Code:
AP0 @ @ CONTEXT !

The false vocabulary, the portion of the FORTH vocabulary from FORTH-83 to the beginning is searched. If the sought word is not found in this false CONTEXT vocabulary, the CURRENT vocabulary, which is all of the FORTH vocabulary, is searched.
Code:
 OK
SEE DO
DO IMMEDIATE
 2DA2 20A6 COMPILE
 2DA4  912 DO
 2DA6 2CAE >MARK
 2DA8 12E6 2+
 2DAA 2CC2 <MARK
 2DAC 12E6 2+
 2DAE  9A8 EXIT
E
 OK
0 ' FORTH-83 >LINK 2>A AP0 @ @ CONTEXT !  OK
SEE DO
DO
  914         INY
  915   FB )Y LDA IP
  917         PHA
  918         DEY
  919   FB )Y LDA IP
  91B         PHA
  91C         CLC
  91D    3 ,X LDA
  91F   80  # ADC
  921         PHA
  922    3 ,X STA
  924    2 ,X LDA
  926         PHA
  927         SEC
  928    0 ,X LDA
  92A    2 ,X SBC
  92C         TAY
  92D    1 ,X LDA
  92F    3 ,X SBC
  931         PHA
  932         TYA
  933         PHA
  934         INX
  935         INX
  936         INX
  937         INX
  938  8ED ^^ BPL ' ?BRANCH >BODY 8 +
  93A  839    JMP ' ! >BODY 14 +
29
 OK
CONSOLE

[Edit: Corrected explanation of the vocabulary search.]


Last edited by JimBoyd on Wed Dec 20, 2023 11:55 pm, edited 1 time in total.

Top
 Profile  
Reply with quote  
PostPosted: Fri Nov 24, 2023 12:30 am 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 895
JimBoyd wrote:
Leo Brodie introduced the word \S in his book Thinking Forth. \S stops the rest of a screen from loading.


The Forth-83 Standard has the uncontrolled reference word ;S which does the same thing.
Code:
          ;S           --                            Interpret only"semi-s"
               Stop interpretation of a block.



Top
 Profile  
Reply with quote  
PostPosted: Sat Nov 25, 2023 8:53 pm 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 895
JimBoyd wrote:
I have another word to parse strings. It is used by the following words.
Code:
(
.(
"
."
ABORT"

I call this word CHAR ; however, the Ansi Forth standard has a word by that name which, along with [CHAR] , does what ASCII does in a Forth-83 system so I may rename Fleet Forth's CHAR . It works a lot like the Ansi Forth word PARSE in that it does not skip initial occurrences of the delimiter and it returns an address and count. It is different in that it will ABORT if the delimiter is not found.


I do not wish to change this word's name to PARSE for two reasons. It does more than ANSI Forth's PARSE by aborting if the delimiter is not found and I do not think PARSE is a good name for extracting a string from a text stream. The word PARSE implies some kind of analysis, such as is performed by the parser of some language compilers.
I think the name PLUCK would be a good one. PLUCK a string from the text stream.


Top
 Profile  
Reply with quote  
PostPosted: Mon Nov 27, 2023 2:44 am 
Offline

Joined: Wed Aug 21, 2019 6:10 pm
Posts: 217
JimBoyd wrote:
... The word PARSE implies some kind of analysis, such as is performed by the parser of some language compilers. ...


The dictionary definition I find of parsing has it as a synonym of syntatic analysis and does indeed refer to analyzing a string of symbols, but if the underlying syntax is as simple as Forth ... tokens and separators with the tokens referring to tags if they are present in a dictionary, if not referring to numbers under the current base, ... then the location and the length of the token is much of the syntactic analysis that is required.

Forth 200x PARSE-NAME then is an action to implement the syntactic analysis required for the default syntax, working well with whitespace by skipping leading delimiters, and Forth94 PARSE is an action to support additional syntax to terminate a substring in the string of symbols by defining an explicit terminator, while allowing for empty substrings by not skipping leading delimiters.

In any event, neither Forth94 PARSE nor Forth200x PARSE-NAME have an abort or error on delimiter not found, but both implicitly take the end of the input buffer as matching any parsing delimiter, so if there is an abort on the delimiter not found, perhaps it's best to not call it PARSE or PARSE-NAME to avoid confusion.

Quote:
... I think the name PLUCK would be a good one. PLUCK a string from the text stream.


Is that pluck as in chicken feathers or pluck as in a harp string?

Given that the Forth style syntax is just slicing up the input buffer into tokens, I like SLICE if you want to name it in terms of the action or TOKEN or STRING if you want to name it by the result it is going to give.


Top
 Profile  
Reply with quote  
PostPosted: Sat Dec 02, 2023 8:46 pm 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 895
BruceRMcF wrote:
Is that pluck as in chicken feathers or pluck as in a harp string?

Harp string.
BruceRMcF wrote:
Given that the Forth style syntax is just slicing up the input buffer into tokens, I like SLICE if you want to name it in terms of the action or TOKEN or STRING if you want to name it by the result it is going to give.

I'm avoiding the name STRING. It seems more suitable for a word which creates string variables. Scott Ballantyne's Blazin' Forth has the word STRING which does exactly that. Fleet Forth's word in question returns an address and count of the text so I am going with the name 'TEXT ( address of text ).


Top
 Profile  
Reply with quote  
PostPosted: Tue Dec 26, 2023 9:32 pm 
Offline

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

Fleet Forth has been revised since I last posted how it handles the text stream. This is how the current version of Fleet Forth handles the text stream.
First, Fleet Forth's QUIT loop.
Fleet Forth has a word WHERE which shows where an error occurred. The first thing WHERE does is store the address of EXIT in its first cell. This prevents recursive error handling if WHERE should cause an error; however, this means QUIT must restore the first cell of WHERE .
Code:
: QUIT  ( -- )
   [COMPILE] [
   BEGIN
         RP!                 // CLEAR RETURN STACK
         ['] LIT (IS) WHERE  // RESTORE WHERE
         CR QUERY INTERPRET
         STATE @ 0=
      CS-DUP UNTIL  // CONTINUE IF COMPILING
      ."  OK"
   AGAIN -;         // LOOP FOREVER


QUERY is the Forth-83 Standard word to read text from the input device (usually the keyboard) and store it in the text input buffer.
QUERY is the only word which places text in the text input buffer. Fleet Forth's QUERY has support for any word which may point TIB to an address other than the text input buffer by pointing TIB to the actual text input buffer before using TIB .
Code:
: QUERY  ( -- )
   'TIB IS TIB  TIB #80 EXPECT
   SPAN C@ #TIB !
   0 0
   LABEL BLK.2!
   BLK 2! ;

'TIB is a metacompiler macro which evaluates to $2A7, the address for the text input buffer.
BLK is actually a double variable. The first cell is normally accessed by words using BLK . >IN is a constant which points to the second cell of BLK . The ability to fetch the values of both BLK and >IN with a single 2@ and store both values with a single 2! results in a smaller Fleet Forth kernel.
QUERY uses EXPECT to read text into the text input buffer ( TIB ) and stores the value of SPAN in #TIB . EXPECT does not store a terminating zero byte.
QUERY then stores a zero in both BLK and >IN .

LOAD and LINELOAD .
Code:
: LINELOAD  ( LINE# BLK# -- )
   DUP 0=
   ABORT" CAN'T LOAD 0"
   BLK 2@ 2>R
   BLK !  C/L * >IN !
   INTERPRET  2R>
   BRANCH [ BLK.2! , ] -;
: LOAD  ( U -- )
   0 SWAP BRANCH
   [ ' LINELOAD >BODY , ] -;

LINELOAD aborts if the block to load is zero. For any other block number, LINELOAD saves the contents of BLK and >IN to the return stack. It then stores the block number on the data stack in BLK then multiplies the line number by C/L and stores that value in >IN . LINELOAD uses INTERPRET to interpret the contents of the block before restoring the previous contents of BLK and >IN .
LOAD places zero on the stack, swaps this number with the block number and branches to LINELOAD .

EVALUATE is a word in ANSI Forth which will interpret the contents of a string. It takes an address and the length of a string. I have found it to be a useful addition to the Fleet Forth system.
Code:
: EVALUATE  ( ADR CNT -- )
   TIB #TIB @ 2>R
   LIT [ >MARK ] ENTER
   0 0 LIT
   [ ' LINELOAD >BODY DUP TRUE
     " LOAD 0" COUNT MATCH ?HUH
     + , ]
   ENTER
   2R>
   [ >RESOLVE ]
   #TIB ! (IS) TIB ;

This source is for a very compact version of EVALUATE ; however, it is relatively straight forward. Save the contents of TIB and #TIB to the return stack. Point TIB at the string to be evaluated and save the string length in #TIB . Place two zeroes on the data stack and use ENTER to perform the equivalent of a GOSUB into the body of LINELOAD just past LINELOAD's inline error string "CAN'T LOAD 0." LINELOAD will save and restore the contents of BLK and >IN . Since EVALUATE supplies a block number of zero, INTERPRET will actually interpret the contents of the string pointed to by TIB and #TIB .
Finally, restore the original contents of TIB and #TIB .

INTERPRET interprets (and/or compiles) the text from the text stream. It exits when the text stream is exhausted. INTERPRET uses NAME which uses WORD to process strings from the text stream one blank delimited string at a time.
WORD uses 'STREAM to get the address and length of the remaining text stream.
Code:
: 'STREAM  ( -- ADR N )
   BLK @ ?DUP
   IF
      BLOCK B/BUF
   ELSE
      TIB #TIB @
   THEN
   >IN @
   OVER UMIN /STRING ;

'STREAM places the address of a block buffer and $400 on the data stack if a non zero block number is stored in BLK or it will place the address of the text input buffer and its length on the data stack.
'STREAM then duplicates the length of the text stream. The unsigned minimum of the length and the value of >IN is used with /STRING to return the address and length of the portion of the text stream which has not yet been processed. If the value of >IN is equal to or greater than the length of the text stream, the text stream has been exhausted and 'STREAM will return an address just past the text stream and a length of zero.
WORD uses 'STREAM so it has access to the address of the unprocessed portion of the text stream as well as the remaining length. WORD stores a string at HERE as a count byte followed by the text of the string and a trailing blank. The trailing blank is needed by NUMBER? .
Fleet Forth has no word called ENCLOSE . Fleet Forth's WORD uses two primitives named SKIP and SCAN . Both words take the address of a string, its length and a character (the delimiter). SKIP returns the address of the first character which is not the delimiter and the remaining length of the string. SCAN returns the address of the first delimiter encountered and the remaining length of the string. If SKIP only encounters delimiter characters or SCAN can not find a delimiter, the address just past the string and a length of zero is returned. Neither word alters the string. Both SKIP and SCAN can handle strings of arbitrary size.

The source for INTERPRET .
Code:
: INTERPRET
   BEGIN
      PAUSE NAME C@ 0EXIT
      HERE I/C
   AGAIN -;

PAUSE is the task switcher, which is set to NOOP when not multitasking. PAUSE is included in INTERPRET's loop to allow multitasking while the text stream is being interpreted.
There is no special word with a blank name to stop interpretation. If the text stream is exhausted, WORD returns a string with a length of zero. INTERPRET checks the length of the string and exits if it is zero.
If the length of the string is not zero, INTERPRET places the address of HERE on the stack for I/C , the word which interprets or compiles one word.
This is why Fleet Forth's text input buffer, block buffers and evaluated strings do not need a trailing zero.


Top
 Profile  
Reply with quote  
PostPosted: Sun Jan 07, 2024 10:16 pm 
Offline

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

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: 895

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: 895

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: 895
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: 895

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  
PostPosted: Sun Apr 28, 2024 9:39 pm 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 895
JimBoyd wrote:
I've just streamlined DJIFFIES , the word that takes a positive double number and waits that many jiffies ( 1/60th of a second on the Commodore 64 ) . It is also used by JIFFIES , the word that takes an unsigned single number and waits that many jiffies.
DJIFFIES works by keeping the number of jiffies to delay on the stack as well as the latest jiffy clock value. Each time through the loop, it subtracts the difference from the amount of time to wait. The idea came from DOWN-COUNTER on page 130 ( 140 of the PDF ) in the book Real Time Forth by Tim Hendtlass.
When I was testing my multitasker, I noticed that the loop in DJIFFIES runs several times per jiffy. With three background tasks, two using DJIFFIES ( actually JIFFIES ) for a delay and one counting how many times it runs, the entire round robin runs several times a jiffy. I realized that when the difference between the current jiffy clock value and the previous one is subtracted from the amount of time to delay, either 0 or -1 is added to the remaining time. I only needed to use the lower cell of the jiffy clock value. Here is the code:
Code:
SCR# 41
// DJIFFIES
HEX
// TAKES POSITIVE DOUBLE NUMBER
// AND DELAYS THAT MANY JIFFIES
: DJIFFIES  ( D+ -- )
   JIFFY@ DROP
   BEGIN
      PAUSE
      JIFFY@ DROP  DUP>R -
// COMPENSATE FOR RESET AT 24 HOURS
      0 MIN
      S>D D+  R> OVER 0<
   UNTIL
   DROP 2DROP ;

SCR# 42
// JIFFIES
HEX
: JIFFIES  ( U -- )
   0 DJIFFIES ;

JIFFIES takes an unsigned number and has a maximum delay of:
18 minutes 12 seconds and 15 jiffies.
DJIFFIES takes a positive double number and has a maximum delay of:
414 days 6 hours 3 minutes 14 seconds and 7 jiffies or
2,147,483,647 jiffies.


Because DJIFFIES waits until the count goes negative, DJIFFIES and JIFFIES wait one jiffy more than what is requested. Not a big problem. There is an easy solution. Just subtract one from the initial value returned by JIFFY@ DROP .
Code:
// DJIFFIES JIFFIES
// TAKES POSITIVE DOUBLE NUMBER
// AND DELAYS THAT MANY JIFFIES
: DJIFFIES  ( D+ -- )
   JIFFY@ DROP 1-
   BEGIN
      PAUSE
      JIFFY@ DROP  DUP>R -
      // COMPENSATE FOR DAILY RESET
      0 MIN
      S>D D+  R> OVER 0<
   UNTIL
   DROP 2DROP ;
: JIFFIES  ( U -- )
   0 DJIFFIES ;

Now DJIFFIES and JIFFIES will wait the requested number of jiffies.
Note: // (double forward slash) is a Commodore 64 Forth alias for \ (backslash) .


Top
 Profile  
Reply with quote  
PostPosted: Sun Apr 28, 2024 10:16 pm 
Offline

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

In a previous post I showed the source for Fleet Forth's word USER , the word to create new user variables. I also mentioned #USER. The only user variables I've ever defined other than the ones defined in the kernel are the three user variables used for multitasking. They have the offsets 0, 2, and 4.
Code:
0 USER ENTRY   2 USER READY
4 USER TOS

Other than these three, I have never defined new user variables; therefore, I simplified the source for USER and removed #USER from Fleet Forth's kernel.
Code:
: USER  ( N ++ )
   CREATE
      C,
   ;CODE  ( -- ADR )
      2 # LDY  CLC
      W )Y LDA  UP ADC
      UP 1+ LDY  CS IF  INY  THEN
      AYPUSH JMP  END-CODE

I also had to change the source for the multitasker's task creation word TASK .
Code:
// TASK CREATION AND ACTIVATION
: TASK  ( U AP0 SP0 RP0 -- )
   CREATE
        ( -- TADR )
      HERE RP0 LOCAL !
      HERE SP0 LOCAL !
      HERE AP0 LOCAL !
      [ ' STOP >BODY ] LITERAL
      HERE ACTIVATE
      // OPTIONAL
      #10 HERE BASE LOCAL !
      [ 0 HLD LOCAL 2+ ] LITERAL
      HERE +  HERE DP LOCAL !
      #12 UMAX ALLOT ;

Note that HLD is the last user variable defined in the kernel. It has an offset of eighteen. The line
Code:
      [ 0 HLD LOCAL 2+ ] LITERAL

compiles a literal decimal twenty. Since there are a total of ten user variables, this points the task's DP just past the area used by the last user variable. Setting a task's DP to point twenty bytes into a task's user area is done so data stored at a background task's HERE will not overwrite any of the task's user variables.


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

All times are UTC


Who is online

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