Page 15 of 25
Re: Fleet Forth design considerations
Posted: Thu Dec 16, 2021 5:35 am
by GARTHWILSON
My :NONAME is defined as:
Code: Select all
: :NONAME ( -- addr ) \ ANS_CORE_EXT
?EXEC
ALIGN HERE
DEPTH CSP !
['] nest ,
] ;
but I never thought of making it work with RECURSE. Anyway, all it compiles is the CFA of nest.
Re: Fleet Forth design considerations
Posted: Fri Dec 17, 2021 10:11 pm
by JimBoyd
Here is the version of :NONAME I'm considering adding to Fleet Forth's system loader.
Code: Select all
: ALIGN ( ADR1 -- ADR2 )
HERE 1+ SPLIT DROP 0= ABS ALLOT ;
// DUMMY TARGET TO UNSMUDGE
HERE >A 0 C,
: :NONAME ( -- ADR )
ALIGN HERE DUP (IS) LAST
CURRENT @ CONTEXT !
[ A> ] LITERAL TRUE
[ ' : @ ] LITERAL , ] ;
This version of align doesn't align to an even address. It will allot one byte if the address is of the form $xxFF. The C64 uses the 6510 processor which has the infamous indirect jump bug.
Fleet Forth doesn't save the stack depth for colon or code words. The address to unsmudge and the value of true are pushed on the stack for compiler security. The one byte allotted prior to the definition of :NONAME provides a safe address for semicolon to unsmudge. Here are Fleet Forth's : and ; .
Code: Select all
: : ( -- ADR TRUE )
] CREATE LATEST
TRUE OVER TSB
CURRENT @ CONTEXT !
;CODE
IP 1+ LDA PHA
IP LDA PHA
CLC
2 # LDA W ADC IP STA
TYA W 1+ ADC IP 1+ STA
NEXT JMP END-CODE
: ?PAIRS ( N1 N2 -- )
<>
ABORT" STRUCTURE MISMATCH" ;
: ; ( ADR TRUE -- )
COMPILE EXIT
[COMPILE] [
TRUE ?PAIRS TSB ; IMMEDIATE
TSB takes an address and toggles the smudge bit at that address.
Code: Select all
// TOGGLE SMUDGE BIT
: TSB ( ADR -- )
SBIT TOGGLE ;
As for the ability to support recursion in a headerless word, Fleet Forth has the word VOCS to display the vocabulary tree. VOCS uses the recursive word .VOCS . .VOCS could be made headerless.
Code: Select all
// .VOCS WITHOUT A NAME
:NONAME ( PFA CNT -- )
?STACK
DUP>R CR SPACES
DUP BODY> >NAME ID. VOC-LINK @
BEGIN
2DUP 2- @ =
IF
DUP 2- 2- R@ 2+ RECURSE
THEN
@ ?DUP 0=
UNTIL
R> 2DROP ;
and used like this.
Code: Select all
>A
: VOCS
[ ' FORTH >BODY ] LITERAL 0
[ A> , ] CR ;
Or if there is no aux stack.
Code: Select all
: VOCS
[ ' FORTH >BODY ] LITERAL 0
[ ROT , ] CR ;
Here is a definition for CNONAME , a word which is used to create headerless code words.
Code: Select all
: CNONAME ( -- ADR ) // CODE DEF
:NONAME [COMPILE] [
ASSEMBLER [ ASSEMBLER ] MEM
LAST 2+ LAST ! ; // OVERWRITE CFA
Although I doubt I would need CNONAME .
LAST is handy if there is an error while compiling. The following will not compile successfully. There is no matching THEN .
Code: Select all
:NONAME ( N -- )
2 MOD
IF
CR ." ODD"
ELSE
CR ." EVEN"
;
When the system aborts, the stacks will be cleared.
Code: Select all
0 FH LOAD
SCR# 8489 LINE# 7
;
^
STRUCTURE MISMATCH
.S EMPTY OK
If the definition was compiled from the command line, there will not even be source to examine; however, there is a LAST resort to (SEE) what went wrong.
Code: Select all
LAST (SEE)
23025 2262 2
23027 5716 MOD
23029 2749 ?BRANCH 23045
23033 7052 CR
23035 6741 (.") ODD
23041 3043 BRANCH 0
23045 7052 CR
23047 6741 (.") EVEN
23054 2949 EXIT
31
OK
That BRANCH 0 shows that the ELSE was never resolved by a matching THEN .
Re: Fleet Forth design considerations
Posted: Fri Dec 17, 2021 10:23 pm
by JimBoyd
In stead of using :NONAME, can one not just use HERE, then write some code, leave HERE on the stack so that when some future word definition needs it, the address to return to is already there. You wouldn't even need to create a colon definition or use :NONAME.
:NONAME is used to create a headerless colon definition and some of the words in Fleet Forth's system loader are candidates. These colon definitions are complex enough that I would not want to rewrite them as code words, code words which would be much larger.
It would just be:
The
0 , would be needed since
CODE does a -2 allot, which stores the
CFA which points to the
PFA (or body) of a regular word definition. The address for
HERE gets left on the stack and is used, when needed, in the assembler calculation. Which I believe needs to be
HERE+2 to skip past the
CFA pointer.
Using CODE to create a headerless code word doesn't work because CODE parses the text stream for a name and creates a header. 64Forth's CODE behaves the same way.
Code: Select all
: CODE ?EXEC CREATE [COMPILE] ASSEMBLER MEM !CSP ;S IMMEDIATE
This is Fleet Forth's CODE
Code: Select all
: SUBR ( -- ADR TRUE )
CREATE LATEST TRUE
OVER TSB
[ ASSEMBLER ] ASSEMBLER MEM ;
: CODE ( -- ADR TRUE )
SUBR HERE DUP 2- ! ;
Neither version has -2 ALLOT .
The word SUBR is for creating subroutines. Here is an example from Fleet Forth's kernel.
Code: Select all
SUBR (>FORTH) ( -- )
CLC
PLA 1 # ADC N STA
PLA 0 # ADC TAY
IP 1+ LDA PHA
IP LDA PHA
N LDA
IP STA
IP 1+ STY
NEXT JMP END-CODE
And its use in the assembler.
Code: Select all
ASSEMBLER DEFINITIONS
: >FORTH ( -- )
?EXEC
(>FORTH) JSR
CURRENT @ CONTEXT !
] ; IMMEDIATE
FORTH DEFINITIONS
Although (>FORTH) , and all SUBR words, return the address of the code, the code in (>FORTH) does not alter the data stack.
I've also looked at the fig-FORTH Installation Manual and it appears that version of CODE also creates a header.
Here is a test screen.
Code: Select all
SCR# 8001
0: // TEST -- THIS IS GOING TO FAIL!
1: HERE 0 , CODE ( N -- N+10 )
2: CLC
3: 0 ,X LDA 10 # ADC 0 ,X STA
4: TYA 1 ,X ADC 1 ,X STA
5: NEXT JMP
6: END-CODE
7:
8:
9:
A:
B:
C:
D:
E:
F:
And the result of trying to load it.
Code: Select all
0 FH LOAD
( EXISTS
SCR# 32769 LINE# 1
HERE 0 , CODE ( N -- N+10 )
^^
WHAT?
So CODE tries to redefine open parenthesis and the system aborts when double dash can't be found in the specified search order. Not surprising since double dash isn't defined in Fleet Forth.
One way to create a headerless code word in Fleet Forth would be the following.
Code: Select all
ASSEMBLER MEM ALIGN // NAMELESS CODE DEF -- INCONVENIENT
HERE DUP 2+ ,
$D020 INC
NEXT JMP
FORTH
I should mention that ALIGN does not align to an even address. If the address is of the form $xxFF, ALIGN allots one byte to push the address past the page boundary. This is done to avoid the indirect jump bug found in NMOS 6502s and the C64's 6510 processor.
Here is a way to create headerless subroutines in Fleet Forth. There is no CFA, just an address to to be used by JSR.
Code: Select all
HERE ASSEMBLER MEM
$D020 INC
RTS
FORTH
Used like so.
Code: Select all
>A \ push address to aux stack
CODE TEST
A> JSR NEXT JMP \ pull it and use with JSR
END-CODE
Re: Fleet Forth design considerations
Posted: Sat Dec 25, 2021 2:14 am
by JimBoyd
I've been entertaining some ideas on changing the working of INTERPRET which breaks the way INTERPRET would exit the interpret loop.
I already mentioned that Fleet Forth's WORD parses the text stream and returns the address of a counted string padded with a trailing blank as per the Forth-83 Standard. The address it returns is HERE . If the text stream is exhausted as WORD is called, then a count of zero followed by a trailing blank will result.
The find primitive, when checking a header for a match, checks the count and each character, using exclusive or, until it finds a non-match. The high bit of the last comparison is masked off ( $7F and). If this results in a match, the word was found. If not, follow the link to the next header, unless the link field has a value of zero. This is the shortest and fastest way I could write the search with a traditional dictionary with headers next to their respective code fields. Because of this behavior, if there is a name in the dictionary with just a count of zero and a blank space (both having the high bit set), it will be found when WORD is called on an empty text stream and the returned address is passed to FIND .
This was the means used to exit the interpreter in Fleet Forth. The mystery word was an immediate alias for EXIT which had no parameter field, just a code field which pointed to the body of EXIT .
My latest changes to Fleet Forth did away with that. The mystery word is now gone and INTERPRET checks the size of the string returned by WORD . If the size is zero, INTERPRET exits.
Here is the new INTERPRET .
Code: Select all
// INTERPRET
: INTERPRET ( -- )
BEGIN
PAUSE NAME C@ 0EXIT
HERE FIND ?DUP
IF
STATE @ =
IF
,
ELSE
EXECUTE ?STACK
THEN
ELSE
NUMBER? ?HUH ?STACK
DPL @ 0<
IF
DROP STATE @
IF
[COMPILE] LITERAL
THEN
ELSE
STATE @
IF
SWAP
[COMPILE] LITERAL
[COMPILE] LITERAL
THEN
THEN
THEN
AGAIN ; -2 ALLOT
This version of INTERPRET is six bytes larger; however, removing the mystery word saves six bytes. So far, everything works fine.
This is what I'm going to try next:
Code: Select all
DEFER I/C
: INTERPRET
BEGIN
PAUSE NAME C@ 0EXIT
HERE I/C
AGAIN ; -2 ALLOT
which is just a streamlined version of this:
Code: Select all
DEFER I/C
: INTERPRET
BEGIN
PAUSE NAME DUP C@
WHILE
I/C
REPEAT
DROP ;
with the default vector for I/C :
Code: Select all
: (I/C) ( ADR -- ? )
FIND ?DUP
IF
STATE @ =
IF , EXIT THEN
EXECUTE ?STACK EXIT
THEN
NUMBER? ?HUH ?STACK DPL @ 0<
IF
DROP STATE @ 0EXIT
[COMPILE] LITERAL EXIT
THEN
STATE @ 0EXIT SWAP
[COMPILE] LITERAL
[COMPILE] LITERAL ;
This latest modification will allow me to remove some of the 'tricky code' from the metacompiler. I wrestled with this for a while because the metacompiler is not a part of Fleet Forth for normal day to day use. It's more like an application written in Fleet Forth to build new versions of the kernel. The metacompiler is already large and reducing a little complexity in it at the expense of even a little bit larger Fleet Forth didn't seem worthwhile. I'm investigating this newest version of Fleet Forth's interpreter not just for the benefit of the metacompiler, but because it will open up other possibilities as well.
[Edit: Minor correction. It's the metacompiler's complexity which could be reduced, not necessarily its size.]
Re: Fleet Forth design considerations
Posted: Sat Dec 25, 2021 2:26 am
by JimBoyd
Here is the version of :NONAME I'm considering adding to Fleet Forth's system loader.
Code: Select all
: ALIGN ( ADR1 -- ADR2 )
HERE 1+ SPLIT DROP 0= ABS ALLOT ;
// DUMMY TARGET TO UNSMUDGE
HERE >A 0 C,
: :NONAME ( -- ADR )
ALIGN HERE DUP (IS) LAST
CURRENT @ CONTEXT !
[ A> ] LITERAL TRUE
[ ' : @ ] LITERAL , ] ;
There is a slight error in the stack comment for :NONAME . The actual stack comment should be
( -- ADR ADR2 TRUE )
ADR2 and TRUE are consumed by semicolon . ADR2 is the address of an unused byte, a safe target semicolon can 'unsmudge'. After semicolon executes, ADR, the address of the nameless word's code field, is the only one of the three parameters left on the stack
For comparison, here is the stack effect for colon.
( -- LATEST TRUE )
[Edit: corrected an error. EXIT does not consume anything from the stack. It pulls an address off the return stack and places it in IP .
Code: Select all
: ?PAIRS ( N1 N2 -- )
<>
ABORT" STRUCTURE MISMATCH" ;
: ; ( ADR TRUE -- )
COMPILE EXIT
[COMPILE] [
TRUE ?PAIRS TSB ; IMMEDIATE
Re: Fleet Forth design considerations
Posted: Thu Dec 30, 2021 1:52 am
by JimBoyd
This is what I'm going to try next:
Code: Select all
DEFER I/C
: INTERPRET
BEGIN
PAUSE NAME C@ 0EXIT
HERE I/C
AGAIN ; -2 ALLOT
which is just a streamlined version of this:
Code: Select all
DEFER I/C
: INTERPRET
BEGIN
PAUSE NAME DUP C@
WHILE
I/C
REPEAT
DROP ;
with the default vector for I/C :
Code: Select all
: (I/C) ( ADR -- ? )
FIND ?DUP
IF
STATE @ =
IF , EXIT THEN
EXECUTE ?STACK EXIT
THEN
NUMBER? ?HUH ?STACK DPL @ 0<
IF
DROP STATE @ 0EXIT
[COMPILE] LITERAL EXIT
THEN
STATE @ 0EXIT SWAP
[COMPILE] LITERAL
[COMPILE] LITERAL ;
I've made this change to Fleet Forth and it works great. Since FIND is in (I/C) rather than INTERPRET , (I/C) and any other vector used with the deferred word I/C takes the string address returned by BL WORD .
I mentioned multi line comments and the word COMMENT: in another post. The drawbacks to this version of COMMENT: are that it is case sensitive in a case insensitive Forth and it only works within the current text stream. This is more than sufficient if loading source from blocks; however, Fleet Forth also has the ability to include source from Commodore 64 sequential files (C64 version of text files).
The given file is included one line at a time. The original version of COMMENT: will not work with multi line comments in ordinary text files because the file is interpreted/compiled one line at a time, just like text typed in from the keyboard.
Fleet Forth's new INTERPRET allows an elegant solution.
Code: Select all
// MULTI LINE COMMENTS
: MLC ( ADR -- )
9 " ;COMMENT" TEXT= 0EXIT
['] (I/C) IS I/C ;
: COMMENT: ( -- )
['] MLC IS I/C ; IMMEDIATE
If the word COMMENT: is encountered, it sets I/C to MLC (Multi Line Comment). MLC checks if the string returned by BL WORD is ';COMMENT' . If it is, MLC sets I/C back to (I/C) .
This version of multi line comments will span multiple lines in a text file. It will even span multiple blocks loaded with THRU . I have also tested it by typing COMMENT: at the Forth's command line. Nothing else typed has any effect until I type ;COMMENT . It even displays the 'OK' prompt if in interpreting state.
Re: Fleet Forth design considerations
Posted: Sun Jan 09, 2022 7:53 am
by JimBoyd
I just noticed in the Ans Forth Standard, the word INCLUDE parses the text stream for a file name and the word INCLUDED takes the address and count of a string for the file name.
Fleet Forth's INCLUDE was written to take the address of a counted string.
I've redefined Fleet Forth's INCLUDE so it too parses the text stream.
I still have not made Fleet Forth's INCLUDE nestable and I'm not sure if I will.
Re: Fleet Forth design considerations
Posted: Mon Jan 10, 2022 12:00 am
by JimBoyd
Here are words for conditional compilation using Fleet Forth's redesigned interpreter. The code was inspired by the conditional compilation words in the Ans Forth Standard.
Code: Select all
// CONDITIONAL COMPILATION
: <IF> ( NL1 ADR -- NL2 )
( NL1 ADR -- )
DUP 5 " [IF]" TEXT= ABS UNDER+
DUP 7 " [THEN]" TEXT= UNDER+
7 " [ELSE]" TEXT= OVER 1 =
AND + ?DUP ?EXIT
['] (I/C) IS I/C ;
: [THEN] ; IMMEDIATE
: [ELSE] ( -- )
1 ['] <IF> IS I/C ; IMMEDIATE
: [IF] ( FLAG -- )
?EXIT
[COMPILE] [ELSE] ; IMMEDIATE
The string left on the stack for I/C is a counted string and so is a string compiled by " .
TEXT= is used to compare strings. It takes the address of one string, the length to compare, and the address of a second string. It is normally used in MATCH where a sub string is sought in a larger string.
For this application an exact match is sought so that, for example, the string '[THEN]AGAIN' isn't mistaken for the string '[THEN]'. This is why counted strings are used and the size of the comparison for each string is one byte larger than the string. The count byte is treated as part of the string for the comparison. TEXT= doesn't check for valid PETSCII characters. It just compares a certain number of bytes at two addresses for equality.
The conditional compilation words can span multiple blocks or, more importantly, multiple lines in a sequential file (C64 text file).
Re: Fleet Forth design considerations
Posted: Mon Jan 10, 2022 12:41 am
by JimBoyd
I'd like to mention something for those new to Forth. With a Forth-83 Standard system, and maybe a few others, it should be possible to experiment with new interpreter ideas without the need to rebuild the system. Just define the new INTERPRET then load the source for QUIT . Here is a fairly generic QUIT .
Code: Select all
: QUIT ( -- )
[COMPILE] [
BEGIN
RP! CR QUERY INTERPRET
STATE @ 0=
IF ." OK" THEN
AGAIN ;
Some of you may have noticed that QUIT doesn't set BLK to zero. I've made the assumption that QUERY sets BLK , as well as >IN , to zero like it does in Fleet Forth.
Just type QUIT and the new QUIT loop is running. If there is an ABORT the old quit loop will be running.
Don't forget to type ABORT before forgetting the new words.
Experimenting with a new interpreter can be risky, so caution is advised. This will make Fleet Forth unresponsive.
It will still display the 'OK' prompt. It just won't do anything. This is remedied with the run/stop restore key combination. As a precaution, I added I/C to the table of deferred words which will be reset when a warm start happens. A warm start can be caused by pressing the run/stop and restore keys at the same time. The restore key generates a non-maskable interrupt, so this key combination is also useful to get out of an endless loop.
Re: Fleet Forth design considerations
Posted: Mon Jan 17, 2022 11:25 pm
by JimBoyd
I have removed the following user variables from Fleet Forth:
SPLIM The full mark for the data stack.
APLIM The full mark for the auxiliary stack.
When I originally included these variables, the idea was that each task would be able to perform a stack check. This is fine for a system with multiple users; however, Fleet Forth only supports background tasks in addition to the main task. While working with my multitasker, I found that I do not need them. Since background tasks do not run a quit loop, no stack bounds checking is performed. Obviously, code run by a background task needs tested in the main task before it is set to run in the background.
For stack checking in the main task, ?STACK was rewritten so that SPLIM is no longer needed.
Code: Select all
: ?STACK ( -- )
DEPTH 0<
ABORT" STACK EMPTY!"
>ASSEM
SP.LIMIT # CPX
NEXT.JMP CS BRAN
>FORTH TRUE
ABORT" STACK FULL!" ;
-2 ALLOT
SP.LIMIT is a metacompiler macro.
A metacompiler macro is a word with a string which is evaluated when the macro is executed. The macros for the Fleet Forth kernel are all defined on screens 2 and 3 of the kernel source.
As for APLIM , the primitives which move data to and from the auxiliary stack perform stack bounds checking. This is necessary since the return stack resides just below the auxiliary stack and above the auxiliary stack are the tables the C64 Kernal uses to keep track of open files.
Re: Fleet Forth design considerations
Posted: Mon Jan 17, 2022 11:37 pm
by JimBoyd
As written, COMMENT: is not nestable; however, multiple occurrences of COMMENT: can all terminate with a single ;COMMENT as in the following screen.
Code: Select all
0: // 4# .BIT .BITS <[ ]>
1: : 4# ( -- ) 4 0 DO # LOOP ;
2: : .BIT ( N -- )
3: RB 2 BASE !
4: 0 <# 4# BL HOLD 4# BL HOLD
5: 4# BL HOLD 4# #> TYPE ;
6: : .BITS ( N1 ... NM M -- )
7: 0 ?DO ?STACK CR .BIT LOOP ;
8: : <[ SP@ >A ;
9: : ]> SP@ A> SWAP - DUP 0<
A: ABORT" STACK?" 2/ ;
B: COMMENT: TEST1
C: RB DECIMAL TRUE <[ 21 73 37 12 ]> .BITS CR .S SP!
D: COMMENT: TEST2
E: 1 2 3 <[ CR .S DROP CR .S ]>
F: ;COMMENT
Neither test case will run when this screen is loaded; however, they can be run individually with the appropriate LINELOAD as shown in this log file.
Code: Select all
OK
$C 0 FH LINELOAD
0000 0000 0000 1100
0000 0000 0010 0101
0000 0000 0100 1001
0000 0000 0001 0101
65535 OK
$E 0 FH LINELOAD
1 2 3
1 2
SCR# 4166 LINE# 14
1 2 3 <[ CR .S DROP CR .S ]>
^^
STACK?
CONSOLE
Re: Fleet Forth design considerations
Posted: Tue Feb 01, 2022 1:48 am
by JimBoyd
Here is the screen defining the constants used in Fleet Forth's assembler:
Code: Select all
// SYSTEM CONSTANTS
HEX
85 CONSTANT N 8D CONSTANT XSAVE
8E CONSTANT UP FB CONSTANT IP
FE CONSTANT W
A4F CONSTANT PUSH
A56 CONSTANT NEXT
A81 CONSTANT SETUP
' 2DROP @ CONSTANT POPTWO
PUSH 2+ CONSTANT PUT
POPTWO 2+ CONSTANT POP
' 1 @ 2+ CONSTANT APUSH
APUSH 2+ CONSTANT AYPUSH
AYPUSH 2+ CONSTANT AYPUT
The first five constants are set by design. The value of the others can change depending on how I modify the source for the kernel. Of the constants that are not set by the system design, three where not defined in terms of other constants. As a result, the last step of metacompiling was to insert the system loader disk and load this screen, then issue the metacompiler command ASSEM-CONSTANTS and edit the screen as necessary.
I was able to do something about that and I don't know why it didn't occur to me sooner.
NOOP is a no op defined in Fleet Forth. It is to give some of the deferred words, such as PAUSE , nothing to do. NOOP is a code word with no body. It's code field points directly to NEXT. PUSH is exactly seven bytes before NEXT. CLIT is a code word with a six byte body and the subroutine SETUP follows CLIT in memory.
Here is the modified screen.
Code: Select all
// SYSTEM CONSTANTS
HEX
85 CONSTANT N 8D CONSTANT XSAVE
8E CONSTANT UP FB CONSTANT IP
FE CONSTANT W
' NOOP @ CONSTANT NEXT
NEXT 7 - CONSTANT PUSH
' CLIT 8 + CONSTANT SETUP
' 2DROP @ CONSTANT POPTWO
PUSH 2+ CONSTANT PUT
POPTWO 2+ CONSTANT POP
' 1 @ 2+ CONSTANT APUSH
APUSH 2+ CONSTANT AYPUSH
AYPUSH 2+ CONSTANT AYPUT
Re: Fleet Forth design considerations
Posted: Tue Mar 15, 2022 2:43 am
by JimBoyd
In another thread,
Jim, you have quite a few words above that are neither part of any standard I know of, nor defined above. One I'll ask about however is REDEFINE:. It appears to edit the old word to redirect execution to the new one, for secondaries that are already compiled using it, so those secondaries don't need to be recompiled.. Is that what's happening? I've had a way to do that but I like yours more.
I haven't written a word to implement this, but I implemented the idea in my metacompiler. I make use of >FORTH since it is already in Fleet Forth.
And here is a quick implementation of this idea. It does not redirect execution to another name, nor does it create a new name. It just redirects the execution of the old word to a new body. Fleet Forth's END-CODE and ; both need an address to unsmudge and TRUE on the stack. A dummy address is provided since no new name is created.
Code: Select all
HERE 0 C, >A
: REDEFINE ( -- ADR TRUE )
HERE ' !
ASSEMBLER [ ASSEMBLER ] MEM
[ A> ] LITERAL TRUE ;
This will redirect execution to a new code word without a name or code field of its own. With >FORTH , the code word can become a high level word. Here is an example.
Code: Select all
: TEST ( -- )
CR ." TEST FINISHED." ;
: RUN.TEST ( -- )
TEST ;
REDEFINE TEST
>FORTH
CR ." START TEST." ;
The old behavior can be appended to the new word with a BRANCH.
Code: Select all
: TEST ( -- )
CR ." TEST FINISHED." ;
: RUN.TEST ( -- )
TEST ;
REDEFINE TEST
>FORTH
CR ." START TEST."
BRANCH [ ' TEST >BODY , ] ;
Re: Fleet Forth design considerations
Posted: Sun Mar 20, 2022 8:30 pm
by JimBoyd
Notice that in the source for REDEFINE , the context VOCABULARY was set to ASSEMBLER , to include words from the ASSEMBLER vocabulary, and not set back to FORTH ; however, the definition for REDEFINE and the test words were on the same screen and colon sets the CONTEXT vocabulary equal to the CURRENT vocabulary, at least in Fleet Forth and Blazin' Forth.
I recently realized I don't even need to waste a byte of memory as an address for semicolon to 'unsmudge'. Once a code word like TOGGLE (or any of the memory access words) is executing, the W register is not needed until NEXT uses it. Here is a version of REDEFINE which uses the W register. The double slash ( // ) is for end of line comments just like backslash ( \ ).
Code: Select all
: REDEFINE ( -- ADR TRUE )
// <NAME>
HERE ' !
ASSEMBLER [ ASSEMBLER ] MEM
W TRUE ;
FORTH
It is used just like the other version.
Here is a version which is not dependent on knowledge of the workings of colon and semicolon, so it is more portable. It does create a new name in the dictionary, but is still used the same.
Code: Select all
: REDEFINE ( -- ADR TRUE )
// <NAME>
>IN @ >R CODE R> >IN !
HERE ' ! ;
Here is a version which takes a new name and the old name to redefine.
Code: Select all
: REDEFINE ( -- ADR TRUE )
// <NEW NAME> <OLD NAME>
CODE HERE ' ! ;
: TEST ( -- )
CR ." TEST FINISHED." ;
: RUN.TEST ( -- )
TEST ;
So it is used a little differently.
Code: Select all
REDEFINE TEST2 TEST
>FORTH
CR ." START TEST."
BRANCH [ ' TEST >BODY , ] ;
The BRANCH is optional if the original behavior of the word is not to be appended to the new behavior.
Code: Select all
REDEFINE TEST2 TEST
>FORTH
CR ." START TEST." ;
With this version of REDEFINE , TEST has the behavior of TEST2 and both names are visible in the dictionary. They are aliases of each other.
Re: Fleet Forth design considerations
Posted: Tue May 10, 2022 2:37 am
by JimBoyd
I'm considering making a slight change to Fleet Forth's interpreter. The word (I/C) would change from this:
Code: Select all
: (I/C) ( ADR -- )
FIND ?DUP
IF
STATE @ =
IF , EXIT THEN
EXECUTE ?STACK EXIT
THEN
NUMBER? ?HUH ?STACK DPL @ 0<
IF
DROP STATE @ 0EXIT
[COMPILE] LITERAL EXIT
THEN
STATE @ 0EXIT SWAP
[COMPILE] LITERAL
[COMPILE] LITERAL ;
to this:
Code: Select all
: (I/C) ( ADR -- )
FIND ?DUP
IF
STATE @ 0<> =
IF , EXIT THEN
EXECUTE ?STACK EXIT
THEN
NUMBER? ?HUH ?STACK DPL @ 0<
IF
DROP STATE @ 0EXIT
[COMPILE] LITERAL EXIT
THEN
STATE @ 0EXIT SWAP
[COMPILE] LITERAL
[COMPILE] LITERAL ;
The inclusion of 0<> after STATE @ would allow INTERPRET to work even if STATE has a true value other than -1. Although it adds two bytes and slightly more time ( one extra pass through NEXT ) , it would be more robust. Normally only ] and [ set the value of STATE , so this might be needlessly redundant. Then again, Murphy's law.