Page 19 of 25
Re: Fleet Forth design considerations
Posted: Tue Mar 14, 2023 3:00 am
by JimBoyd
I mentioned another way to define THRU . Here are the block loading words in Fleet Forth.
These are in the kernel.
Code: Select all
2VARIABLE HISTORY
: LINELOAD ( LINE# BLK# -- )
DUP 0=
ABORT" CAN'T LOAD 0"
RB DECIMAL
BLK 2@ 2>R
BLK ! C/L * >IN !
INTERPRET 2R>
BRANCH [ BLK.2! , ] -;
: LOAD ( U -- )
0 SWAP BRANCH
[ ' LINELOAD >BODY , ] -;
: RAM ( BLK#1 -- BLK#2 )
RAM.OFFSET + ;
: FH ( N1 -- N2 )
BLK @ DUP UNDER+ ?EXIT
SCR @ + ;
: DR+ ( BLK#1 #DR -- BLK#2 )
7 AND DR.OFS.PWR LSHIFT + ;
HISTORY is set by WORD . HISTORY was originally added to Fleet Forth so WHERE could accurately display where an error originated.
LINELOAD takes a line number and a block number. It will load a screen starting at the specified line number.
LOAD loads the entire block. It branches to the beginning of LINELOAD .
Fleet Forth sets aside a range of block numbers for each disk as well as a range for the Ram Expansion Unit.
In the current version of Fleet Forth, RAM adds an offset of 16384 ($4000) to the number on the stack. Fleet Forth's BLOCK and BUFFER will access blocks higher than 16383 ($3FFF) from the Ram Expansion Unit
DR+ masks off the higher bits of a number and treats drive numbers 0 and 8 as the same device, 1 and 9 as the same device on up to treating 7 and 15 as the same device. DR+ adds the offset for the desired drive.
Code: Select all
0 DR+ \
8 DR+ \ adds 0 to the number on the stack
1 DR+ \
9 DR+ \ adds 2048 to the number on the stack
2 DR+ \
10 DR+ \ adds 4096 to the number on the stack
...
7 DR+ \
15 DR+ \ adds 14336 to the number on the stack
RAM \ adds 16384 to the number on the stack
Each drive, and the Ram Expansion Unit, sees its block range starting at 0.
Assume there is a disk with Forth blocks in drive 8 and drive 9 and both drives are open for block access.
Code: Select all
1 LOAD \ loads block 1 from drive 8
1 8 DR+ LOAD \ also loads block 1 from drive 8
1 9 DR+ LOAD \ loads block 1 from drive 9
1 RAM LOAD \ loads block 1 from the Ram Expansion Unit
FH , from here, is a nice utility word from Leo Brodie's "Thinking Forth". When loading a screen, it adds the current block number to the number on the stack. This allows loading blocks (with LOAD or THRU ) relative from the currently loading block. When not loading a block, FH adds the current screen number to the number on the stack to assist editing.
THRU is defined in the system loader. This is its source in Fleet Forth.
Code: Select all
: THRU ( U1 U2 -- )
>R
BEGIN
5 ?CR
DUP U. LOAD
R@ HISTORY @ 1+ DUP>R U<
DONE? OR
?LEAVE
R>
AGAIN -;
Yes, that is ?LEAVE used without a DO LOOP . Fleet Forth's ?LEAVE will, if the flag on the data stack is TRUE , drop two items from the return stack and pull a third item from the return stack and store it in IP .?LEAVE is used in Fleet Forth's definition of THRU to make it 2 bytes smaller than this:
Code: Select all
: THRU ( U1 U2 -- )
>R
BEGIN
5 ?CR
DUP U. LOAD
R@ HISTORY @ 1+ TUCK U<
DONE? OR
UNTIL
R> 2DROP ;
Fleet Forth's source for THRU is not portable to other Forth systems with ?LEAVE in there and it doesn't matter. THRU is in the Controlled Reference Words of the Forth-83 Standard and it is in the ANSI Forth Standard. Any Forth system which uses blocks will likely have THRU .
I have not yet added --> except as a temporary definition. Should I choose to define it in the system, it will be defined in the system loader along with THRU .
Re: Fleet Forth design considerations
Posted: Sat Mar 18, 2023 12:45 am
by JimBoyd
Leo Brodie introduced the word \S in his book Thinking Forth. \S stops the rest of a screen from loading. Here is his source for \S
In Fleet Forth it can be defined as this
However, since the value of >IN is used in an unsigned comparison and WORD does not increment >IN beyond the size of the text stream, \S can be shortened further.
This will also work with Fleet Forth.
It can be made even smaller. I once mentioned that Fleet Forth's LEAVE is equivalent to using this phrase.
anywhere that phrase is used LEAVE can be used and vice versa.
For example
Code: Select all
: TESTWORD
10 0
DO
<DO SOME STUFF>
<TEST> IF LEAVE THEN
LOOP ;
can be rewritten as this
Code: Select all
: TESTWORD
10 0
DO
<DO SOME STUFF>
<TEST> IF 2R> 2DROP EXIT THEN
LOOP ;
That phrase does the same thing as Fleet Forth's LEAVE , pull the top two loop parameters from the return stack and discard then exit to the address of the third parameter. The new TESTWORD is larger and slower, but it is functionally equivalent.
This
compiles the same thing as this
so Fleet Forth's source for \S can be changed to this
Re: Fleet Forth design considerations
Posted: Sat Mar 18, 2023 1:16 am
by GARTHWILSON
I use that enough that I've made it part of my kernel, as ?LEAVE. It pays for itself in memory, and also runs a lot faster because the ?leave (the internal compiled by the immediate compile-only word ?LEAVE) is a primitive.
Re: Fleet Forth design considerations
Posted: Sat Mar 18, 2023 1:29 am
by JimBoyd
I use that enough that I've made it part of my kernel, as ?LEAVE. It pays for itself in memory, and also runs a lot faster because the ?leave (the internal compiled by the immediate compile-only word ?LEAVE) is a primitive.
So have I. Since the redefinition of \S uses LEAVE , that is the one I used in the example.
Code: Select all
?LEAVE
2130 INX
2131 INX
2132 254 ,X LDA W
2134 255 ,X ORA W 1+
2136 2164 BNE ' (LOOP) >BODY 13 +
2138 2174 BEQ NEXT
10
The first branch branches to the body of LEAVE , which LEAVE shares with (LOOP)
Code: Select all
2164 PLA \ CFA OF LEAVE POINTS HERE
2165 PLA
2166 PLA
2167 PLA
2168 PLA \ CFA OF EXIT POINTS HERE
2169 251 STA IP
2171 PLA
2172 252 STA IP 1+
which falls into NEXT .
Fleet Forth's LEAVE and ?LEAVE are not compiled by secondaries.
Re: Fleet Forth design considerations
Posted: Tue Apr 11, 2023 2:30 am
by JimBoyd
Although it looks as though Fleet Forth's (LOOP) falls into NEXT , it really doesn't. The following is a disassembly of Fleet Forth's (LOOP) .
Code: Select all
SEE (LOOP)
(LOOP)
867 PLA
868 TAY
869 INY
86A 8EB BNE ' ?BRANCH >BODY 16 +
86C SEC
86D PLA
86E 0 # ADC
870 8EA BVC ' ?BRANCH >BODY 15 +
872 876 BVS
874 PLA
875 PLA
876 PLA
877 PLA
878 PLA
879 FB STA IP
87B PLA
87C FC STA IP 1+
87E 1 # LDY
880 FB )Y LDA IP
882 FF STA W 1+
884 DEY
885 FB )Y LDA IP
887 FE STA W
889 CLC
88A FB LDA IP
88C 2 # ADC
88E FB STA IP
890 895 BCS
892 FD JMP W 1-
895 FC INC IP 1+
897 FD JMP W 1-
33
OK
The two complementary branches, BVC and BVS, mark the end of (LOOP) . The string of PLA instructions which follow are the start of the code for LEAVE . It's header and code field are farther ahead and it's code field points to address $874.
Fleet Forth's kernel was rewritten to make this obvious and to give the new metacompiler one more test. The disassembly is as follows.
Code: Select all
SEE LEAVE
LEAVE
866 PLA
867 PLA
868 PLA
869 PLA
86A PLA
86B FB STA IP
86D PLA
86E FC STA IP 1+
870 1 # LDY
872 FB )Y LDA IP
874 FF STA W 1+
876 DEY
877 FB )Y LDA IP
879 FE STA W
87B CLC
87C FB LDA IP
87E 2 # ADC
880 FB STA IP
882 887 BCS
884 FD JMP W 1-
887 FC INC IP 1+
889 FD JMP W 1-
26
OK
SEE (LOOP)
(LOOP)
897 PLA
898 TAY
899 INY
89A 900 BNE ' ?BRANCH >BODY 17 +
89C SEC
89D PLA
89E 0 # ADC
8A0 8FF BVC ' ?BRANCH >BODY 16 +
8A2 868 ^^ BVS ' LEAVE >BODY 2 +
D
OK
SEE (+LOOP)
(+LOOP)
8B0 INX
8B1 INX
8B2 CLC
8B3 PLA
8B4 FE ,X ADC W
8B6 TAY
8B7 PLA
8B8 FF ,X ADC W 1+
8BA 8FF BVC ' ?BRANCH >BODY 16 +
8BC 868 ^^ BVS ' LEAVE >BODY 2 +
E
OK
It is now clear that (LOOP) branches into the body of ?BRANCH or into the body of LEAVE .
Another change to the kernel is having store, ! , fall into the body of 2DROP .
Code: Select all
CODE ! ( N ADR -- )
2 ,X LDA 0 X) STA 0 ,X INC
0= IF 1 ,X INC THEN
3 ,X LDA 0 X) STA
LABEL POPTWO ( N1 N2 -- )
INX INX
LABEL POP
INX INX
NEXT 0< NOT BRAN
LABEL RUNG1
// WILL BE PATCHED
0 JSR // >FORTH
LABEL DO.?STACK
0 , // ?STACK
END-CODE
...
CODE 2DROP ( N1 N2 -- )
POPTWO LATEST NAME> !
END-CODE
CODE DROP ( N1 -- )
POP LATEST NAME> !
END-CODE
I've shown the disassembly for the loop words because the source can be difficult to follow for those not used to an assembler which uses RPN and the use of the auxiliary stack to temporarily store control flow data. The assembler and meta assembler both use the same size control flow data as Fleet Forth's high level control flow words.
Here is the source code for ?EXIT 0EXIT ?LEAVE LEAVE (LOOP) (+LOOP) ! and ?BRANCH .
Code: Select all
CODE ?EXIT ( F -- )
INX INX
$FE ,X LDA $FF ,X ORA
0= IF CS>A // EXIT
0= NOT IF CS>A END-CODE // NEXT
CODE 0EXIT ( F -- )
INX INX
$FE ,X LDA $FF ,X ORA
0= NOT IF CS>A // EXIT
0= IF CS>A END-CODE // NEXT
CODE ?LEAVE ( F -- )
INX INX
$FE ,X LDA $FF ,X ORA
0= IF CS>A // LEAVE
0= NOT IF CS>A END-CODE // NEXT
CODE LEAVE
A>CS A>CS THEN
PLA PLA
PLA PLA
A>CS A>CS THEN
A>CS A>CS THEN
LABEL EXIT.BODY
PLA IP STA PLA IP 1+ STA
THEN
THEN
THEN
LABEL NEXT
1 # LDY
IP )Y LDA W 1+ STA DEY
IP )Y LDA W STA CLC
IP LDA 2 # ADC IP STA
CS NOT IF
W 1- JMP
THEN
IP 1+ INC
W 1- JMP END-CODE
CODE (LOOP)
PLA TAY INY
0= IF // TO ?BRANCH
SEC PLA 0 # ADC
VS IF // TO ?BRANCH
' LEAVE @ 2+ VS BRAN
CS-SWAP CS>A CS>A
END-CODE
CODE (+LOOP) ( N -- )
INX INX CLC
PLA $FE ,X ADC TAY
PLA $FF ,X ADC
VS IF CS>A // TO ?BRANCH
' LEAVE @ 2+ VS BRAN
END-CODE
CODE ! ( N ADR -- )
2 ,X LDA 0 X) STA 0 ,X INC
0= IF 1 ,X INC THEN
3 ,X LDA 0 X) STA
LABEL POPTWO ( N1 N2 -- )
INX INX
LABEL POP
INX INX
NEXT 0< NOT BRAN
LABEL RUNG1
// WILL BE PATCHED
0 JSR // >FORTH
LABEL DO.?STACK
0 , // ?STACK
END-CODE
CODE ?BRANCH ( F -- )
INX INX
$FE ,X LDA $FF ,X ORA
0= NOT IF
LABEL 2.IP.+!
CLC
IP LDA 2 # ADC IP STA
CS IF IP 1+ INC THEN
NEXT JMP
A>CS THEN A>CS THEN
PHA
A>CS THEN
TYA PHA 0 # LDY
THEN
LABEL BRANCH.BODY
IP )Y LDA PHA INY
IP )Y LDA IP 1+ STA
PLA IP STA
NEXT 2+ JMP END-CODE
Re: Fleet Forth design considerations
Posted: Sat Apr 15, 2023 2:20 am
by JimBoyd
I've mentioned Fleet Forth's WORD before. Reading through "standard" Forth and the following quotes from scotws motivated me to clarify something about Fleet Forth's WORD .
... but what this does is use PARSE-NAME (that's BL WORD COUNT in paleolithic Forths)...
... PARSE-NAME replaces WORD and just does ( "name" -- addr u ) while WORD ends up being BL WORD COUNT anyway...
Two of the drawbacks to WORD mentioned are the time used copying the parsed string to a transient area and the possibility of overwriting something beyond the transient area.
This is the source for Fleet Forth's WORD .
Code: Select all
: WORD ( C -- HERE )
'STREAM
BLK 2@ HISTORY 2!
DUP >IN +!
2PICK SKIP
ROT 2PICK -ROT SCAN
1- 0 MAX NEGATE >IN +!
OVER - >HERE ;
The word >HERE just before the semicolon takes an address and count. It copies the string at that address to HERE as a counted string with a trailing blank. It will NOT copy more than 255 bytes! With the count byte and trailing blank, that is a maximum of 257 bytes at HERE which could get overwritten. Sure, the first 172 byes of PAD could possibly be overwritten. I wrote >HERE as a primitive so it would be fast.
I mentioned that >HERE takes an address and a count. If I wanted to support PARSE-NAME , I would have factored (WORD) out of WORD .
Code: Select all
: (WORD) ( C -- ADR CNT )
'STREAM
BLK 2@ HISTORY 2!
DUP >IN +!
2PICK SKIP
ROT 2PICK -ROT SCAN
1- 0 MAX NEGATE >IN +!
OVER -
;
: WORD ( C -- HERE )
(WORD) >HERE ;
: PARSE-NAME ( -- ADR CNT )
BL (WORD) ;
Why haven't I done this? COUNT does not appear after WORD in any of the source files for Fleet Forth. The counted string from WORD is used as is. WORD is normally just used to parse names and numbers from the text stream.
I have another word to parse strings. It is used by the following words.
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.
WORD does not end up being BL WORD COUNT anyway. The two standards did things differently. It's not like PARSE-NAME just becomes PARSE-NAME >HERE anyway.
Re: Fleet Forth design considerations
Posted: Sun Apr 16, 2023 7:31 pm
by JimBoyd
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.
And now I have. I failed to consider the case where a screen might not contain source to compile but commands to run like a script. Just a reminder, a screen is a block which contains source to compile OR commands to execute. Consider a set of blocks which have screens of tests. Nothing gets compiled, the tests are run by loading the screen. Depending on the tests, it would be convenient to be able to set the BASE prior to loading.
Likewise, If the a screen changes BASE to something other than decimal, it will not be set to decimal for any screens chain loaded from that one with --> .
It is for these reasons that Fleet Forth's LOAD will no longer have a default base, nor will it restore BASE to whatever it was prior to loading. I believe this will result in more flexibility in how screens are used.
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.
This may not be necessary because Fleet Forth's NUMBER? accepts a leading conversion base specifier ($,# or %) in a numeric string.
The source for Fleet Forth's new LINELOAD and LOAD .
Code: Select all
: 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 , ] -;
Re: Fleet Forth design considerations
Posted: Mon Apr 17, 2023 1:05 pm
by SamCoVT
I failed to consider the case where a screen might not contain source to compile but commands to run like a script.
I use blocks like this all the time, and often have blocks that load other blocks with
LOAD and
THRU. I usually set the base, if it's important, right at the top and set it to decimal right at the end. I would like to make some words that save and restore the old base so that I can put it back the way it was, but it hasn't been a big enough issue to focus on and I just use the stack to hold the old base when I really need to do that.
You showed your co-routine method earlier in this thread and I was able to get your co-routine method to work in Tali (even though it's STC and your forth was ITC), so I will probably implement your
RB (Restore Base) word that way when/if I get around to it.
This may not be necessary because Fleet Forth's NUMBER? accepts a leading conversion base specifier ($,# or %) in a numeric string.
Tali2 supports this as well, and I've recently started to make use of it. It's actually a much better solution all around, in my opinion, because the person reading the code (which will probably be me at a later date) can easily tell $100 from #100 from %100 without having to figure out what the base is at that moment. It also allows for multiple bases to be used easily in the same screen, such as looping a decimal number of times while accessing a hex address with a binary mask.
Re: Fleet Forth design considerations
Posted: Sat Apr 22, 2023 4:28 pm
by JimBoyd
I've added --> to Fleet Forth. I use it to link together all screens used by a given word. I do not use --> to link all screens in place of THRU . Since Fleet Forth's THRU is compatible with --> , I can use THRU to load a range of screens for some useful utilities while still chaining together the two screens spanned by a word such as (RR/W) .
Code: Select all
SCR# 52
// (RR/W)
HEX
CODE (RR/W) ( ADR BLK# R/WF CNT -- )
DF09 STY DF0A STY
DF09 LDA 1F # CMP
0= IF
DF0A LDA 3F # CMP
0= IF
DF04 STY 4 ,X LDA
.A ASL 5 ,X ROL
.A ASL 5 ,X ROL
DF05 STA
5 ,X LDA DF06 STA
0 ,X LDA DF07 STA
1 ,X LDA DF08 STA
-->
SCR# 53
// (RR/W)
6 ,X LDA DF02 STA
7 ,X LDA DF03 STA
2 ,X LDA 90 # ORA DF01 STA
BEGIN
DF00 BIT
VS UNTIL
INX INX INX INX
POPTWO JMP
THEN
THEN
>FORTH
TRUE ABORT" NO REU" -;
' (RR/W) IS RR/W
(RR/W) spans the screens 52 and 53 so they are linked together with --> at the bottom of screen 52; however, they can still be loaded with the screens in the range 46 - 78. If I have an idea to improve (RR/W) , I can load all of it just by loading screen 52. This loads (RR/W) , and only (RR/W) , for testing. This use of --> to chain together all the screens spanned by a word, and using it with a compatible version of THRU , rather than using --> to replace the function of THRU , negates one of the arguments against --> ; specifically, the argument that the use of --> precludes the ability to load individual screens. The way I use --> , the only screens which can not be loaded individually are the ones which should not be loaded individually.
Re: Fleet Forth design considerations
Posted: Mon Apr 24, 2023 6:50 pm
by SamCoVT
This use of --> to chain together all the screens spanned by a word, and using it with a compatible version of THRU , rather than using --> to replace the function of THRU , negates one of the arguments against --> ; specifically, the argument that the use of --> precludes the ability to load individual screens. The way I use --> , the only screens which can not be loaded individually are the ones which should not be loaded individually.
That is very good work. The Forth-83 standard lists both
--> and
THRU as "controlled reference words" (eg. you don't have to provide them to meet the standard, but if you do provide them they should have defined behavior) and I always thought they were two different ways to solve the same issue (code that spans multiple screens). It looks like
--> was dropped in the 94 standard and
THRU was officially adopted into the "Block Extension" word set. The advantage of your forth having both words is that you are able to load older FIG Forth code as well as newer code.
I can see the advantage to having
--> around (when it's like your version that is compatible with
THRU) to ensure that a multiscreen definition of a single word always has the full definition loaded.
Re: Fleet Forth design considerations
Posted: Sun Apr 30, 2023 7:56 pm
by JimBoyd
--> is a very simple word.
Code: Select all
: --> ( -- )
1 BLK +! >IN OFF ; IMMEDIATE
Here are generic versions of LOAD and THRU which are compatible with --> .
Re: Fleet Forth design considerations
Posted: Sun Apr 30, 2023 9:03 pm
by JimBoyd
I've made some changes to Fleet Forth's multitasker.
UNLINK-TASK has been removed in an effort to keep the multitasker small.
UNLINK-ALL has been replaced with UNLINK .UNLINK is integrated into the kernel. The source for UNLINK-ALL
Code: Select all
: UNLINK-ALL ( -- )
[ ASSEMBLER UP @ ] LITERAL
DUP UP ! ENTRY ! ; FORTH
and the source for a subroutine used by the WARM and COLD start routines.
Code: Select all
HSUBR (WARM)
SEI
$2F # LDA 0 STA
$36 # LDA 1 STA
USER.DATA LDA UP STA
USER.DATA 1+ LDA UP 1+ STA
$6C # LDA W 1- STA
'THERE USER.DATA - 1- # LDY
BEGIN
USER.DATA ,Y LDA UP )Y STA
DEY
0< UNTIL
CLI
>FORTH
SP! AP! [COMPILE] [
IORESET SINGLE DECIMAL
PAGE BOOTCOLORS
>ASSEM
RTS
END-CODE
Some of the functionality of (WARM) has been factored out into the new code word UNLINK .
Code: Select all
CODE UNLINK ( -- )
USER.DATA LDA UP STA
USER.DATA 1+ LDA UP 1+ STA
'THERE USER.DATA - 1- # LDY
BEGIN
USER.DATA ,Y LDA UP )Y STA
DEY
0< UNTIL
NEXT JMP END-CODE
Here is the new subroutine (WARM) .
Code: Select all
HSUBR (WARM)
SEI
$2F # LDA 0 STA
$36 # LDA 1 STA
$6C # LDA W 1- STA
CLI
>FORTH
UNLINK
SP! AP! [COMPILE] [
IORESET SINGLE DECIMAL
PAGE BOOTCOLORS
>ASSEM
RTS
END-CODE
TASK and ACTIVATE have also been modified. They now support partitioning the auxiliary stack.
Code: Select all
: ACTIVATE ( TCF-ADR TASK-ADR -- )
DUP WAKE
TUCK RP0 LOCAL @ 1- DUP>R !
DUP SP0 LOCAL @ R@ 1- C!
DUP TOS LOCAL R> 2- SWAP!
AP0 LOCAL @ DUP ! ;
: TASK ( U AP0 SP0 RP0 -- )
CREATE
( -- TADR )
HERE RP0 LOCAL !
HERE SP0 LOCAL !
HERE AP0 LOCAL !
// OPTIONAL
#10 HERE BASE LOCAL !
HERE #USER + HERE DP LOCAL !
#12 UMAX ALLOT ;
The way ACTIVATE works is different. It now takes the address of a thread of high level Forth, such as the PFA , and the address of a task.
Here is the creation of a sample task:
Note: since this is on a Commodore 64, just think of each // as \.
Code: Select all
DECIMAL
#USER 80 + // 80 BYTES FOR 'HERE'
AP0 @ 42 - // HALF OF AUX STACK
SP0 @ 62 - // HALF OF DATA STACK
RP0 @ 128 - // HALF OF RETURN STACK
TASK BGTASK
Here is linking and activating.
Code: Select all
: FLASH ( -- )
BEGIN
$D020 C@ 1+ BORDER // INCREMENT THE BORDER COLOR
60 JIFFIES // WAIT 1 SECOND
AGAIN -;
BGTASK LINK-TASK
' FLASH >BODY BGTASK ACTIVATE
MULTI // ENABLE MULTITASKING
PAUSE is not in the source for FLASH because JIFFIES calls DJIFFIES which has PAUSE .
A word of caution! FORGET switches off multitasking but does not unlink the tasks. After the use of FORGET it is the programmers responsibility to be certain none of the linked tasks or the words they run have been forgotten.
I could update FORGET to call UNLINK . It would be the programmers responsibility to relink and wake the tasks after the use of FORGET .
Re: Fleet Forth design considerations
Posted: Fri May 05, 2023 7:05 pm
by JimBoyd
I can see the advantage to having --> around (when it's like your version that is compatible with THRU) to ensure that a multiscreen definition of a single word always has the full definition loaded.
Yes, having all of a multi screen definition loaded when the first screen of said definition is loaded is nice. Fleet Forth has the word FH from Leo Brodie's Thinking Forth. After editing a screen, I can load it with the phrase 0 FH . It would be convenient after editing a word spanning multiple screens to be able to type 0 FH and load the new word regardless of which screen of a multi screen definition I was just editing. I would have liked to have such a feature back when I replaced PUSH and PUT with AYPUSH and AYPUT in Fleet Forth.
I've found a way to do just that. --> is redefined so it skips loading the comment line of the next block.
Code: Select all
: --> ( -- )
1 BLK +! C/L >IN ! ; IMMEDIATE
And a new word is added.
Code: Select all
: <-- ( -- )
TRUE BLK +! >IN OFF ; IMMEDIATE
Here is Fleet Forth's BLOCK
Code: Select all
SCR# 112
0: // BLOCK
1: CODE BLOCK ( BLK -- ADR )
2: DEY
3: BLK/BUF STY
4: ' MRU >BODY LDA W STA
5: ' MRU >BODY 1+ LDA W 1+ STA
6: 6 # LDY
7: W )Y LDA 0 ,X CMP
8: 0= IF
9: INY W )Y LDA 1 ,X CMP
A: 0= IF
B: INY (W)PUT JMP
C: THEN
D: THEN
E: -->
F:
SCR# 113
0: // BLOCK
1: >FORTH
2: #BUF 1+ 2
3: ?DO
4: DUP I >BT @ =
5: IF
6: DROP I UNLOOP
7: AHEAD CS>A
8: THEN
9: LOOP
A: LRU 2+ 2+ @
B: IF
C: LRU 2@ 0 B/BUF R/W
D: LRU 2+ 2+ OFF
E: THEN
F: -->
SCR# 114
0: // BLOCK BUFFER
1: BLK/BUF C@
2: IF
3: LRU ON
4: LRU 2+ @ DUP B/BUF TRUE FILL
5: OVER 1 B/BUF R/W
6: THEN
7: LRU ! #BUF
8: A>CS THEN
9: DUP >BT MRU 6 CMOVE
A: MRU 1 >BT ROT 6 * CMOVE>
B: MRU 2+ @ ;
C:
D: CODE BUFFER ( BLK# -- ADR )
E: ' BLOCK @ 1+ LATEST NAME> !
F: END-CODE
Here is the modified source.
Code: Select all
SCR# 112
0: // BLOCK
1: CODE BLOCK ( BLK -- ADR )
2: DEY
3: BLK/BUF STY
4: ' MRU >BODY LDA W STA
5: ' MRU >BODY 1+ LDA W 1+ STA
6: 6 # LDY
7: W )Y LDA 0 ,X CMP
8: 0= IF
9: INY W )Y LDA 1 ,X CMP
A: 0= IF
B: INY (W)PUT JMP
C: THEN
D: THEN
E: -->
F:
SCR# 113
0: <-- BLOCK
1: >FORTH
2: #BUF 1+ 2
3: ?DO
4: DUP I >BT @ =
5: IF
6: DROP I UNLOOP
7: AHEAD CS>A
8: THEN
9: LOOP
A: LRU 2+ 2+ @
B: IF
C: LRU 2@ 0 B/BUF R/W
D: LRU 2+ 2+ OFF
E: THEN
F: -->
SCR# 114
0: <-- BLOCK BUFFER
1: BLK/BUF C@
2: IF
3: LRU ON
4: LRU 2+ @ DUP B/BUF TRUE FILL
5: OVER 1 B/BUF R/W
6: THEN
7: LRU ! #BUF
8: A>CS THEN
9: DUP >BT MRU 6 CMOVE
A: MRU 1 >BT ROT 6 * CMOVE>
B: MRU 2+ @ ;
C:
D: CODE BUFFER ( BLK# -- ADR )
E: ' BLOCK @ 1+ LATEST NAME> !
F: END-CODE
Here are the same three screens with most of the lines removed.
Code: Select all
SCR# 112
0: // BLOCK
1: CODE BLOCK ( BLK -- ADR )
E: -->
SCR# 113
0: <-- BLOCK
F: -->
SCR# 114
0: <-- BLOCK BUFFER
D: CODE BUFFER ( BLK# -- ADR )
E: ' BLOCK @ 1+ LATEST NAME> !
F: END-CODE
By replacing the end of line comment word with <-- in the second and third screens used for BLOCK , I can load any one of the screens for BLOCK and all three will be loaded in the correct order.
When a screen starting with <-- is loaded by LOAD or THRU , the screen does not finish loading. loading resumes with the previous screen. This continues until loading resumes with a screen not having <-- . That screen loads normally. Assuming that screen has --> , loading resumes with the next screen but the comment line is skipped.
Re: Fleet Forth design considerations
Posted: Sun May 07, 2023 11:49 pm
by JimBoyd
There are more changes for Fleet Forth's multitasker.
The data used to initialize the first six user variables has been changed so the main task is always awake by default.
The old system user area boot up values.
Code: Select all
// SYSTEM USER AREA BOOT UP VALUES
USER.AREA , // ENTRY
0 , // READY
0 , // TOS
'RP0 , // RP0
'SP0 , // SP0
'AP0 , // AP0
The new values.
Code: Select all
// SYSTEM USER AREA BOOT UP VALUES
USER.AREA , // ENTRY
TRUE , // READY
0 , // TOS
'RP0 , // RP0
'SP0 , // SP0
'AP0 , // AP0
UNLINK has been added to FORGET just before SINGLE so FORGET and EMPTY will unlink all tasks.
TASK gives the created task the default of running the loop in STOP .
Code: Select all
: STOP ( -- )
BEGIN
READY OFF PAUSE
AGAIN -;
With these changes it is no longer necessary for LINK-TASK to put a task to sleep before linking it.
The multitasker has been moved from the utilities disk to the system loader disk. It is now loaded whenever the system is added to a new Fleet Forth kernel.
Re: Fleet Forth design considerations
Posted: Mon May 08, 2023 12:37 am
by JimBoyd
I once mentioned that my Forth would not have a permanent stack display and it will not; however, there is a way to add a stack display to the top of the screen on an as needed temporary basis. This could be handy when hand tracing yet to be compiled (as in, not finished) forth words.
I was trying to think of a simple background task to demonstrate the multitasker when I thought of this.
Fleet Forth's interpret has the word PAUSE as the first word in the interpreter loop.
Code: Select all
: INTERPRET
BEGIN
PAUSE NAME C@ 0EXIT
HERE I/C
AGAIN -;
Fleet Forth's IS only works with DEFERred words and its TO only works with VALUEs. When compiling, they both compile (IS) . (IS) will alter the first cell of the parameter field of any word.
It is used to define words to 'patch' the interpreter.
Code: Select all
: TOP.S
$400 #160 BLANK
XY 2>R CHARS @ >R
0 1 AT-XY CHARS OFF .S
R> CHARS ! 2R> AT-XY ;
: TOP.S.ON ( -- )
['] TOP.S (IS) INTERPRET ;
: TOP.S.OFF ( -- )
['] PAUSE (IS) INTERPRET ;
: PAGE
PAGE CR CR CR CR ;
TOP.S saves the current screen coordinates as well as the number of characters emitted since the last page or carriage return. It sets the screen coordinates to the beginning of the second line and displays the stack contents before restoring the screen coordinates and the number of characters emitted.
TOP.S.ON and TOP.S.OFF switch on and off this capability.
The new PAGE is to leave room for the stack display.
Because task switching can occur in .S , multitasking still works while interpreting/compiling.