whartung wrote:
I take it "; " terminates the compiler whether under >ASSEM or >FORTH ?
No. My Forth is a Forth-83 standard ITC Forth for the Commodore 64. Semicolon
; terminates high level (colon) definitions. Code definitions are terminated by
END-CODE. A code word must have a jump to
NEXT or code that jumps to or falls through ( like
PUSH and
PUT) to
NEXT.
>FORTH is an immediate word that can only be used while interpreting, as in compiling a code definition. If
STATE is compiling, then
>FORTH aborts with the message "EXECUTION ONLY".
>FORTH compiles a
JSR to the body of
(>FORTH) then sets the context vocabulary to the current one and sets the compiling state.
(>FORTH) pulls the
JSR's return address off the return stack, increments it and saves it in
N, the scratch pad area in zero page, and the
Y register. It then pushes
IP to the return stack. It takes the address saved in
N and the
Y register and stores it in
IP and jumps to
NEXT. Since
(>FORTH) performs the function of 'nest', the word transitions from a code word to a high level 'colon definition' without the need to return to the assembly code level. This high level code can now be written to do anything any other high level code can do. It can have multiple
EXIT's (
EXIT is the word compiled by semicolon
;) or branch into another high level word.
This is in the kernel source.
Code:
CODE (>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 this is in the system loader loaded by the kernel.
Code:
: >FORTH ( -- ) ?EXEC
[ ' (>FORTH) >BODY ] LITERAL
[ ASSEMBLER ] JSR, [ FORTH ]
CURRENT @ CONTEXT !
] ; IMMEDIATE
>ASSEM is an immediate word that can only be used while compiling. If
STATE is interpreting, then
>ASSEM aborts with the message "COMPILING ONLY".
>ASSEM compiles
(>ASSEM) into the high level code then sets
CONTEXT to the assembler vocabulary and sets the interpreting state.
(>ASSEM) stores
IP in
N and
N+1, then pulls the value off the return stack and stores it in
IP, performing an unnest. It then performs an indirect jump through
N. Since
(>ASSEM) performs the function 'unnest' (what
EXIT does), the word transitions from a high level 'colon definition' to a code word without the need to return to high level. This code level word can now be written to do anything any other code word can do.
This is in the kernel source.
Code:
CODE (>ASSEM) ( -- )
IP LDA, N STA,
IP 1+ LDA, N 1+ STA,
PLA, IP STA, PLA, IP 1+ STA,
N ) JMP, END-CODE
And this is in the system loader loaded by the kernel.
Code:
: >ASSEM ( -- )
COMPILE (>ASSEM)
[ ASSEMBLER ] MEM ASSEMBLER
[ FORTH ]
[COMPILE] [ ; IMMEDIATE
Here is my cold start routine for an example. Since this is on a Commodore 64, the system has one line of basic:
Code:
10 SYS 10952
The
SYS causes a jump to the cold start routine at address 10952, hexadecimal 2AC8.
Code:
SCR# A1
// COLD
HEX // USER COLD START
: COLD ( -- )
SAVE-BUFFERS EMPTY
ACLOSE // CLOSE ALL FILES
>ASSEM
// POWERUP COLD START
// PATCH THE 'BASIC FUSE'
DECIMAL HERE 0 (UD.)
HEX 80B OVER - SWAP CMOVE>V
SEI,
0FF87 JSR, // RAMTAS
0FF8A JSR, // RESTOR
0FFE7 JSR, // CLALL
0FF84 JSR, // IOINIT
0FF81 JSR, // CINT
SCR# A2
// COLD
' WARM @ 100 /MOD SWAP
# LDA, 300 STA,
# LDA, 301 STA,
0D # LDY, (WARM) JSR,
>FORTH
RP!
EMPTY 0 DRIVE CONFIGURE
0A SPACES
." C64 FORTH"
CR CR
." COPYRIGHT (C) 1995-2018 BY JAMES BOYD"
CR INITIAL ABORT ; -2 ALLOT
At start up, BASIC performs a
SYS to the Set Interrupt Disable instruction (
SEI,) in the cold start routine. Once the system is up and running, the cold start routine can also be called with the word
COLD.
COLD first saves the block buffers, empties the dictionary to the lesser of its initial and current sizes (saving this value as the new empty point), and closes all files before transitioning to the low level cold start routine.
The Commodore 64 lacks a backslash key so the double slash
// is the word to treat the rest of the line as a comment. The funny names like RAMTAS and RESTOR are the names given to Commodore kernel routines in the Commodore 64 Programmer's Reference Guide.
I blanked out the name I'm using for my Forth for now. I'm having trouble thinking of a really good one.
Sorry about the commas at the end of the names of the op codes, I have them there to be compatible with other C64 Forths. Both 64Forth and Blazin' Forth have commas in the op code names. I'd personally like to remove them from my assembler but I hope to make this Forth publicly available one day and I don't know how much grief it would cause someone to rewrite assembly code, by removing all the commas in the opcode names of their source, to try on this Forth.
For another example, here is the full source for
(RR/W), the code to access the
REU, Commodore's Ram Expansion Unit, as Forth blocks.
Code:
SCR# 2A
// (RR/W)
HEX
CODE (RR/W) ( ADR BLK# F CNT -- )
DF09 LDA, 1F # CMP, 0= IF,
DF0A LDA, 3F # CMP,
0= NOT ELIF,
>FORTH TRUE
ABORT" REU NOT PRESENT"
>ASSEM
THEN,
DF04 STY, 4 ,X LDA,
.A ASL, 5 ,X ROL,
.A ASL, 5 ,X ROL,
DF05 STA,
5 ,X LDA, DF06 STA,
SCR# 2B
// (RR/W)
0 ,X LDA, DF07 STA,
1 ,X LDA, DF08 STA,
6 ,X LDA, DF02 STA,
7 ,X LDA, DF03 STA,
2 ,X LDA, 10 # ORA, DF01 STA,
80 # ORA, DF01 STA,
BEGIN,
DF00 LDA, 40 # AND,
0= NOT UNTIL,
INX, INX, INX, INX,
POPTWO JMP, END-CODE
' (RR/W) IS RR/W
There is one caveat when using
>FORTH and
>ASSEM with control structures which should be obvious, but I'll mention it anyway. If a transition from low to high or high to low occurs in a control structure, the opposite transition must occur before a matching control structure word is used. I don't have actual examples other than the
IF, ELIF, THEN, control block in the code above for the ram expander, so here is some pseudo-code:
Code:
: <A-HIGH-LEVEL-WORD>
BEGIN
SOME-HIGH-LEVEL-STUFF
>ASSEM
SOME-LOW-LEVEL-STUFF
>FORTH
WHILE
MORE-STUFF
REPEAT ;
CODE <A-CODE-LEVEL-WORD>
BEGIN,
SOME-LOW-LEVEL-STUFF
WHILE,
MORE-LOW-LEVEL-STUFF
>FORTH
SOME-HIGH-LEVEL-STUFF
>ASSEM
EVEN-MORE-LOW-LEVEL-STUFF
>FORTH
MORE-HIGH-LEVEL-STUFF
>ASSEM
REPEAT,
NEXT JMP, END-CODE
Cheers,
Jim