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.
Code:
CODE FOREVER
BEGIN AGAIN
END-CODE
If the RUN/STOP RESTORE key combination is used, Fleet Forth's warm start routine runs and everything is fine.
If SED is added before the loop:
Code:
CODE FOREVER
SED
BEGIN AGAIN
END-CODE
the RUN/STOP RESTORE 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:
Code:
FFFA @ DIS
FE43 SEI
FE44 318 ) JMP
4
OK
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:
Code:
CLD
$FE47 JMP
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/STOP RESTORE 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.
Code:
HSUBR NMI2
CLD 0 JMP END-CODE
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)
Code:
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
The new version of (WARM)
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.
Code:
$318 ,Y LDA NMI2 2+ ,Y STA
NMI2 is installed as the new vector for address $318 by this section of code.
Code:
NMI2 SPLIT SWAP
# LDA # LDY $318 STA $319 STY
This latest version of Fleet Forth works fine. The RUN/STOP RESTORE key combination breaks out of the loop in the code word FOREVER and warm starts without a problem.
Code:
CODE FOREVER
SED
BEGIN AGAIN
END-CODE
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.