After a discussion on another thread I was wondering what percent of Fleet Forth's words are colon definitions. Less than fifty percent of the words in the system are high level.
557 words
272 : words 48.8330
157 code words 28.1867
59 no body words 10.5925
19 variables 3.4111
11 constants 1.9749
11 deferred words 1.9749
10 values 1.7953
7 user variables 1.2567
4 among 0.7181
3 vocabularies 0.5386
3 xttable 0.5386
1 string array 0.1795
Code words and code words without bodies together make up a almost thirty nine percent.
This data does not include the multitasker lexicon, nor does it include the metacompiler lexicon since neither one was loaded. It also does not include the sixteen headerless words in the kernel. Nor does it include the thirteen high level words used to obtain this data.
Another caveat, this list doesn't include the words defined with :NONAME and it is only for the FORTH vocabulary. If the ASSEMBLER and EDITOR vocabularies were included, the percentage of high level words would be higher.
I originally released Fleet Forth under the General Public Licence. Now that the latest version gets closer to completion I'm not sure if that was the best choice. scotws mentioned the Forth tradition of putting the code in the public domain and I wonder if that is what I should have done, what I should do with the latest version of Fleet Forth?
I originally released Fleet Forth under the General Public Licence. Now that the latest version gets closer to completion I'm not sure if that was the best choice. scotws mentioned the Forth tradition of putting the code in the public domain and I wonder if that is what I should have done, what I should do with the latest version of Fleet Forth?
(It does seem like a good idea, if wanting to discuss ideas which are not Fleet Forth, to use a new thread instead of this Fleet Forth thread. Don't worry about the possibility of not reaching your audience - this place is not so busy. Your new thread will be seen, and if you ask good questions, you will get good answers.)
It uses a minimal ITC, no keep IP and W, all references at return stack.
feel free to comment
Have you considered starting a new thread to discuss your Forth and your design goals?
No need, just point an alternative of ITC. I'm learn a lot in this topic, and could also use some codes ?
Although I am currently experimenting with a subroutine threaded version of Fleet Forth, the version I discuss here is ITC Fleet Forth. I designed and wrote the initial version of Fleet Forth as an indirect threaded Forth because I like the simplicity and elegance of the ITC model. All words have a two byte code field. >BODY and BODY> work for all words. I am not going to change the way NEXT works for the ITC version of Fleet Forth.
As for the idea of keeping IP on the return stack, I keep IP in a dedicated pair of zero page locations. This keeps it out of the way and, I believe, simplifies skipping over inline strings.
As I recall, eForth had a minimal number of primitives so it could be easily ported to different systems. I understand it was dismally slow as a result. Fleet Forth has a large number of primitives. Making it easy to port Fleet Forth was not one of my design goals. I wanted to make Fleet Forth the best ITC Forth for the Commodore 64 that I could.
These were the three main ideas I gleaned from my perusal of IMMU. If you have any other ideas you believe could benefit Fleet Forth, it would be a good idea to present them one at a time here on this forum rather than post a link to a project somewhere else. This gives all interested parties a chance to discuss the pros and cons of any given idea and whether it is a good fit for Fleet Forth without the need to sift though external material just to find it.
I noticed the conversion algorithms here and here and thought routines using the decimal mode might be useful. Then I realized that could be a problem on the Commodore 64. It has the 6510 processor which is basically an NMOS 6502 with a slight modification. What if a nonmaskable interrupt hits while the CPU is in decimal mode?
I tested this scenario. The C64's RESTORE key generates a non-maskable interrupt. If the RUN/STOP key is pressed at that time, the NMI ISR will indirectly jump to Fleet Forth's warm start routine through address $300.
This short code word will run in a loop forever.
the RUN/STOPRESTORE key combination still breaks out of the loop, but after the "WARM START" message the cursor is blinking rapidly and it is virtually impossible to type any characters. Clearly the C64's NMI service routine is adversely affected by the decimal mode.
This is the solution I came up with. The C64's NMI service routine starts out like this:
The address at $318 is normally $FE47.
Replace that address with the address for a small piece of code to compensate for the C64 engineers not putting a CLD instruction in place of the useless SEI instruction:
I was concerned about a non-maskable interrupt hitting while that address is being altered so I conducted a little experiment. I fetched the address from the reset vector at $FFFC and stored it at $318. Any NMI would cause a system reset back to BASIC. Everything worked fine. About a half hour later I wrapped up this experiment by hitting the RUN/STOPRESTORE key combination to reset the C64 back to BASIC.
The only other considerations are that the NMI service routine resets the vector at $318 and the possibility this vector could hold a different address on different models of the C64.
Here are the modifications I made to Fleet Forth's kernel.
Add the short code fragment to clear the decimal mode and resume the NMI service routine.
This will be a four byte code fragment without a header and without a code field. The jump address will be patched by the header-less routine (WARM) called by both the warm start routine and the cold start routine.
Here is the previous version of (WARM)
HSUBR (WARM)
SEI CLD
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
HSUBR (WARM)
SEI
$2F # LDA 0 STA
$36 # LDA 1 STA 1 # LDY
BEGIN
USER.DATA ,Y LDA UP ,Y STA
$318 ,Y LDA NMI2 2+ ,Y STA
DEY
0< UNTIL
$6C # LDA W 1- STA
'THERE USER.DATA - 1- # LDY
BEGIN
USER.DATA ,Y LDA UP )Y STA
DEY
0< UNTIL
CLI
NMI2 SPLIT SWAP
# LDA # LDY $318 STA $319 STY
>FORTH
SP! AP! [COMPILE] [
IORESET SINGLE DECIMAL
PAGE BOOTCOLORS
>ASSEM
RTS END-CODE
When this routine is initially called by the cold start routine and when it is called by WARM by way of the NMI, address $318 will hold the default address. The jump address of NMI2 is patched to this address in the first BEGIN loop.
This latest version of Fleet Forth works fine. The RUN/STOPRESTORE key combination breaks out of the loop in the code word FOREVER and warm starts without a problem.
I also wrote a code word to test the normal interrupt service routine. It sets the decimal mode and runs in a loop until a key-press is detected, saving and restoring the x-register as needed. Once out of the loop, it pushes the processor status and pulls it to the accumulator before clearing the decimal mode. The accumulator is then pushed onto the data stack to confirm that decimal mode was not cleared by the C64 kernal routine to check the keyboard buffer for a key.
The C64 ISR occurs about sixty times a second and I let this word run for about ten seconds before pressing a key. There were no ill effects that I am aware of. It seems as though the normal ISR is immune to decimal mode.
The new Fleet Forth kernel is twenty bytes bigger and still smaller than Blazin' Forth's kernel. Not too bad.
I should mention that I'm not running Fleet Forth on a real Commodore 64, I'm running it on VICE.
Based on my experiences with a real Commodore 64, VICE has been a very accurate simulation so I'm inclined to believe the decimal mode problem with the C64's NMI service routine occurs on the actual hardware as well.
I noticed the conversion algorithms here and here and thought routines using the decimal mode might be useful. Then I realized that could be a problem on the Commodore 64. It has the 6510 processor which is basically an NMOS 6502 with a slight modification. What if a nonmaskable interrupt hits while the CPU is in decimal mode?
...
I've had a few weeks to think about this and decided to go back to the original warm start routine. If another source of non-maskable interrupts is enabled then this 'fix' will not work anyway and there will be a chance of a non-maskable interrupt occurring while the new vector for address $318 is being changed. It's just not worth the hassle, restrictions on sources of non-maskable interrupts or the increased size to safely support decimal mode in Fleet Forth.
Without my reviewing 19 pages of this topic... Is it really a problem? The D flag is part of the status register that gets pushed in the interrupt sequence and pulled in the RTI sequence, and the first instruction in the ISR can be CLD (which is standard procedure on the NMOS 6502, but not necessary on the CMOS 65c02 since CLD is an automatic, implied part of its interrupt sequence). The background program and the ISR code can have their own D status without affecting the other.
It's not a problem. I'll just avoid using decimal mode. As for the ISR code having its own D status, it's a Commodore 64 and the ISRs are a part of the kernel in the C64's ROM and the 6510 NMOS cpu does not clear decimal mode when servicing an interrupt.
I wasn't seeking help in this post. It was more along the lines of 'I'm not doing that now, and here is why.'
Basically, it's a heads up for anyone following along. I may have, on some future date, occasion to mention the source for the warm start routine or mention something else which would conflict with what I said about that change. On that day I may forget to mention that I changed the warm start routine back.
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.
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.
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.
: 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:
: 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 .
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.
: 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.
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.