1
0
mirror of https://github.com/hsoft/collapseos.git synced 2024-11-26 10:28:05 +11:00

Compare commits

...

5 Commits

Author SHA1 Message Date
Virgil Dupras
d0d92a4559 forth: Forth-ify IF and ELSE
Now, I really need comments...
2020-03-12 21:49:10 -04:00
Virgil Dupras
d5a7d5faf8 forth: add words "(fbr)", "(fbr?)", "'", "[']" 2020-03-12 21:16:20 -04:00
Virgil Dupras
fb54fd51af forth: implement THEN in Forth
Also, add "," and "C,"
2020-03-12 13:52:27 -04:00
Virgil Dupras
3819dbc083 forth: remove CMPDST
We now always do ","-style writes in (HERE), even in IMMEDIATE mode. Simplifies
things and paves the way for compiling words in Forth.
2020-03-12 13:08:11 -04:00
Virgil Dupras
ff281f69a8 forth: add "UNWORD" flag
Also, reorder word fields so that the flag field is more easily accessible.
2020-03-12 11:39:27 -04:00
7 changed files with 267 additions and 203 deletions

View File

@ -1,8 +1,15 @@
: H HERE @ ;
: -^ SWAP - ;
: ? @ . ; : ? @ . ;
: +! SWAP OVER @ + SWAP ! ; : +! SWAP OVER @ + SWAP ! ;
: ALLOT HERE +! ; : ALLOT HERE +! ;
: VARIABLE CREATE 2 ALLOT ; : VARIABLE CREATE 2 ALLOT ;
: CONSTANT CREATE HERE @ ! DOES> @ ; : CONSTANT CREATE H ! DOES> @ ;
: , H ! 2 ALLOT ;
: C, H C! 1 ALLOT ;
: IF ['] (fbr?) , H 0 C, ; IMMEDIATE
: THEN DUP H -^ SWAP C! ; IMMEDIATE
: ELSE ['] (fbr) , 0 C, DUP H -^ SWAP C! H 1 - ; IMMEDIATE
: NOT IF 0 ELSE 1 THEN ; : NOT IF 0 ELSE 1 THEN ;
: = CMP NOT ; : = CMP NOT ;
: < CMP 0 1 - = ; : < CMP 0 1 - = ;

View File

@ -1,7 +1,7 @@
; A dictionary entry has this structure: ; A dictionary entry has this structure:
; - 7b name (zero-padded) ; - 7b name (zero-padded)
; - 1b flags (bit 0: IMMEDIATE)
; - 2b prev pointer ; - 2b prev pointer
; - 1b flags (bit 0: IMMEDIATE. bit 1: UNWORD)
; - 2b code pointer ; - 2b code pointer
; - Parameter field (PF) ; - Parameter field (PF)
; ;
@ -9,6 +9,11 @@
; with IY pointing to the PF. They themselves are expected to end by jumping ; with IY pointing to the PF. They themselves are expected to end by jumping
; to the address at the top of the Return Stack. They will usually do so with ; to the address at the top of the Return Stack. They will usually do so with
; "jp exit". ; "jp exit".
;
; That's for "regular" words (words that are part of the dict chain). There are
; also "special words", for example NUMBER, LIT, FBR, that have a slightly
; different structure. They're also a pointer to an executable, but as for the
; other fields, the only one they have is the "flags" field.
; Execute a word containing native code at its PF address (PFA) ; Execute a word containing native code at its PF address (PFA)
nativeWord: nativeWord:
@ -51,40 +56,6 @@ doesWord:
push hl \ pop iy push hl \ pop iy
jr compiledWord jr compiledWord
; This word is followed by 1b *relative* offset (to the cell's addr) to where to
; branch to. For example, The branching cell of "IF THEN" would contain 3. Add
; this value to RS.
branchWord:
push de
ld l, (ix)
ld h, (ix+1)
ld a, (hl)
call addHL
ld (ix), l
ld (ix+1), h
pop de
jp exit
BRANCH:
.dw branchWord
; Conditional branch, only branch if TOS is zero
cbranchWord:
pop hl
ld a, h
or l
jr z, branchWord
; skip next byte in RS
ld l, (ix)
ld h, (ix+1)
inc hl
ld (ix), l
ld (ix+1), h
jp exit
CBRANCH:
.dw cbranchWord
; This is not a word, but a number literal. This works a bit differently than ; This is not a word, but a number literal. This works a bit differently than
; others: PF means nothing and the actual number is placed next to the ; others: PF means nothing and the actual number is placed next to the
; numberWord reference in the compiled word list. What we need to do to fetch ; numberWord reference in the compiled word list. What we need to do to fetch
@ -102,6 +73,8 @@ numberWord:
ld (ix+1), h ld (ix+1), h
push de push de
jp exit jp exit
.db 0b10 ; Flags
NUMBER: NUMBER:
.dw numberWord .dw numberWord
@ -119,6 +92,8 @@ litWord:
jp abort jp abort
.msg: .msg:
.db "undefined word", 0 .db "undefined word", 0
.db 0b10 ; Flags
LIT: LIT:
.dw litWord .dw litWord
@ -143,16 +118,18 @@ exit:
; ( R:I -- ) ; ( R:I -- )
.db "QUIT" .db "QUIT"
.fill 4 .fill 3
.dw EXIT .dw EXIT
.db 0
QUIT: QUIT:
.dw nativeWord .dw nativeWord
quit: quit:
jp forthRdLine jp forthRdLine
.db "ABORT" .db "ABORT"
.fill 3 .fill 2
.dw QUIT .dw QUIT
.db 0
ABORT: ABORT:
.dw nativeWord .dw nativeWord
abort: abort:
@ -163,8 +140,9 @@ ABORTREF:
.dw ABORT .dw ABORT
.db "BYE" .db "BYE"
.fill 5 .fill 4
.dw ABORT .dw ABORT
.db 0
BYE: BYE:
.dw nativeWord .dw nativeWord
; Goodbye Forth! Before we go, let's restore the stack ; Goodbye Forth! Before we go, let's restore the stack
@ -177,8 +155,9 @@ BYE:
; ( c -- ) ; ( c -- )
.db "EMIT" .db "EMIT"
.fill 4 .fill 3
.dw BYE .dw BYE
.db 0
EMIT: EMIT:
.dw nativeWord .dw nativeWord
pop hl pop hl
@ -188,8 +167,9 @@ EMIT:
; ( c port -- ) ; ( c port -- )
.db "PC!" .db "PC!"
.fill 5 .fill 4
.dw EMIT .dw EMIT
.db 0
PSTORE: PSTORE:
.dw nativeWord .dw nativeWord
pop bc pop bc
@ -199,8 +179,9 @@ PSTORE:
; ( port -- c ) ; ( port -- c )
.db "PC@" .db "PC@"
.fill 5 .fill 4
.dw PSTORE .dw PSTORE
.db 0
PFETCH: PFETCH:
.dw nativeWord .dw nativeWord
pop bc pop bc
@ -211,8 +192,8 @@ PFETCH:
; ( addr -- ) ; ( addr -- )
.db "EXECUTE" .db "EXECUTE"
.db 0
.dw PFETCH .dw PFETCH
.db 0
EXECUTE: EXECUTE:
.dw nativeWord .dw nativeWord
pop iy ; is a wordref pop iy ; is a wordref
@ -226,8 +207,9 @@ executeCodeLink:
jp (hl) ; go! jp (hl) ; go!
.db ":" .db ":"
.fill 7 .fill 6
.dw EXECUTE .dw EXECUTE
.db 0
DEFINE: DEFINE:
.dw nativeWord .dw nativeWord
call entryhead call entryhead
@ -273,8 +255,9 @@ DEFINE:
.db "DOES>" .db "DOES>"
.fill 3 .fill 2
.dw DEFINE .dw DEFINE
.db 0
DOES: DOES:
.dw nativeWord .dw nativeWord
; We run this when we're in an entry creation context. Many things we ; We run this when we're in an entry creation context. Many things we
@ -296,35 +279,77 @@ DOES:
.db "IMMEDIA" .db "IMMEDIA"
.db 0
.dw DOES .dw DOES
.db 0
IMMEDIATE: IMMEDIATE:
.dw nativeWord .dw nativeWord
ld hl, (CURRENT) ld hl, (CURRENT)
dec hl dec hl
dec hl set FLAG_IMMED, (hl)
dec hl
inc (hl)
jp exit jp exit
; ( n -- ) ; ( n -- )
.db "LITERAL" .db "LITERAL"
.db 1 ; IMMEDIATE
.dw IMMEDIATE .dw IMMEDIATE
.db 1 ; IMMEDIATE
LITERAL: LITERAL:
.dw nativeWord .dw nativeWord
ld hl, (CMPDST) ld hl, (HERE)
ld de, NUMBER ld de, NUMBER
call DEinHL call DEinHL
pop de ; number from stack pop de ; number from stack
call DEinHL call DEinHL
ld (CMPDST), hl ld (HERE), hl
jp exit jp exit
.db "'"
.fill 6
.dw LITERAL
.db 0
APOS:
.dw nativeWord
call readLITBOS
call find
jr nz, .notfound
push de
jp exit
.notfound:
ld hl, .msg
call printstr
jp abort
.msg:
.db "word not found", 0
.db "[']"
.fill 4
.dw APOS
.db 0b01 ; IMMEDIATE
APOSI:
.dw nativeWord
call readword
call find
jr nz, .notfound
ld hl, (HERE)
push de ; --> lvl 1
ld de, NUMBER
call DEinHL
pop de ; <-- lvl 1
call DEinHL
ld (HERE), hl
jp exit
.notfound:
ld hl, .msg
call printstr
jp abort
.msg:
.db "word not found", 0
; ( -- c ) ; ( -- c )
.db "KEY" .db "KEY"
.fill 5 .fill 4
.dw LITERAL .dw APOSI
.db 0
KEY: KEY:
.dw nativeWord .dw nativeWord
call stdioGetC call stdioGetC
@ -334,8 +359,9 @@ KEY:
jp exit jp exit
.db "CREATE" .db "CREATE"
.fill 2 .fill 1
.dw KEY .dw KEY
.db 0
CREATE: CREATE:
.dw nativeWord .dw nativeWord
call entryhead call entryhead
@ -349,23 +375,25 @@ CREATE:
jp exit jp exit
.db "HERE" .db "HERE"
.fill 4 .fill 3
.dw CREATE .dw CREATE
.db 0
HERE_: ; Caution: conflicts with actual variable name HERE_: ; Caution: conflicts with actual variable name
.dw sysvarWord .dw sysvarWord
.dw HERE .dw HERE
.db "CURRENT" .db "CURRENT"
.db 0
.dw HERE_ .dw HERE_
.db 0
CURRENT_: CURRENT_:
.dw sysvarWord .dw sysvarWord
.dw CURRENT .dw CURRENT
; ( n -- ) ; ( n -- )
.db "." .db "."
.fill 7 .fill 6
.dw CURRENT_ .dw CURRENT_
.db 0
DOT: DOT:
.dw nativeWord .dw nativeWord
pop de pop de
@ -379,8 +407,9 @@ DOT:
; ( n a -- ) ; ( n a -- )
.db "!" .db "!"
.fill 7 .fill 6
.dw DOT .dw DOT
.db 0
STORE: STORE:
.dw nativeWord .dw nativeWord
pop iy pop iy
@ -391,8 +420,9 @@ STORE:
; ( n a -- ) ; ( n a -- )
.db "C!" .db "C!"
.fill 6 .fill 5
.dw STORE .dw STORE
.db 0
CSTORE: CSTORE:
.dw nativeWord .dw nativeWord
pop hl pop hl
@ -402,8 +432,9 @@ CSTORE:
; ( a -- n ) ; ( a -- n )
.db "@" .db "@"
.fill 7 .fill 6
.dw CSTORE .dw CSTORE
.db 0
FETCH: FETCH:
.dw nativeWord .dw nativeWord
pop hl pop hl
@ -413,8 +444,9 @@ FETCH:
; ( a -- c ) ; ( a -- c )
.db "C@" .db "C@"
.fill 6 .fill 5
.dw FETCH .dw FETCH
.db 0
CFETCH: CFETCH:
.dw nativeWord .dw nativeWord
pop hl pop hl
@ -425,8 +457,9 @@ CFETCH:
; ( -- a ) ; ( -- a )
.db "LIT@" .db "LIT@"
.fill 4 .fill 3
.dw CFETCH .dw CFETCH
.db 0
LITFETCH: LITFETCH:
.dw nativeWord .dw nativeWord
call readLITTOS call readLITTOS
@ -435,8 +468,9 @@ LITFETCH:
; ( a b -- b a ) ; ( a b -- b a )
.db "SWAP" .db "SWAP"
.fill 4 .fill 3
.dw LITFETCH .dw LITFETCH
.db 0
SWAP: SWAP:
.dw nativeWord .dw nativeWord
pop hl pop hl
@ -446,8 +480,9 @@ SWAP:
; ( a b c d -- c d a b ) ; ( a b c d -- c d a b )
.db "2SWAP" .db "2SWAP"
.fill 3 .fill 2
.dw SWAP .dw SWAP
.db 0
SWAP2: SWAP2:
.dw nativeWord .dw nativeWord
pop de ; D pop de ; D
@ -462,8 +497,9 @@ SWAP2:
; ( a -- a a ) ; ( a -- a a )
.db "DUP" .db "DUP"
.fill 5 .fill 4
.dw SWAP2 .dw SWAP2
.db 0
DUP: DUP:
.dw nativeWord .dw nativeWord
pop hl pop hl
@ -473,8 +509,9 @@ DUP:
; ( a b -- a b a b ) ; ( a b -- a b a b )
.db "2DUP" .db "2DUP"
.fill 4 .fill 3
.dw DUP .dw DUP
.db 0
DUP2: DUP2:
.dw nativeWord .dw nativeWord
pop hl ; B pop hl ; B
@ -487,8 +524,9 @@ DUP2:
; ( a b -- a b a ) ; ( a b -- a b a )
.db "OVER" .db "OVER"
.fill 4 .fill 3
.dw DUP2 .dw DUP2
.db 0
OVER: OVER:
.dw nativeWord .dw nativeWord
pop hl ; B pop hl ; B
@ -500,8 +538,9 @@ OVER:
; ( a b c d -- a b c d a b ) ; ( a b c d -- a b c d a b )
.db "2OVER" .db "2OVER"
.fill 3 .fill 2
.dw OVER .dw OVER
.db 0
OVER2: OVER2:
.dw nativeWord .dw nativeWord
pop hl ; D pop hl ; D
@ -518,8 +557,9 @@ OVER2:
; ( a b -- c ) A + B ; ( a b -- c ) A + B
.db "+" .db "+"
.fill 7 .fill 6
.dw OVER2 .dw OVER2
.db 0
PLUS: PLUS:
.dw nativeWord .dw nativeWord
pop hl pop hl
@ -530,8 +570,9 @@ PLUS:
; ( a b -- c ) A - B ; ( a b -- c ) A - B
.db "-" .db "-"
.fill 7 .fill 6
.dw PLUS .dw PLUS
.db 0
MINUS: MINUS:
.dw nativeWord .dw nativeWord
pop de ; B pop de ; B
@ -543,8 +584,9 @@ MINUS:
; ( a b -- c ) A * B ; ( a b -- c ) A * B
.db "*" .db "*"
.fill 7 .fill 6
.dw MINUS .dw MINUS
.db 0
MULT: MULT:
.dw nativeWord .dw nativeWord
pop de pop de
@ -555,8 +597,9 @@ MULT:
; ( a b -- c ) A / B ; ( a b -- c ) A / B
.db "/" .db "/"
.fill 7 .fill 6
.dw MULT .dw MULT
.db 0
DIV: DIV:
.dw nativeWord .dw nativeWord
pop de pop de
@ -567,8 +610,9 @@ DIV:
; ( a1 a2 -- b ) ; ( a1 a2 -- b )
.db "SCMP" .db "SCMP"
.fill 4 .fill 3
.dw DIV .dw DIV
.db 0
SCMP: SCMP:
.dw nativeWord .dw nativeWord
pop de pop de
@ -580,8 +624,9 @@ SCMP:
; ( n1 n2 -- f ) ; ( n1 n2 -- f )
.db "CMP" .db "CMP"
.fill 5 .fill 4
.dw SCMP .dw SCMP
.db 0
CMP: CMP:
.dw nativeWord .dw nativeWord
pop hl pop hl
@ -592,68 +637,48 @@ CMP:
push bc push bc
jp exit jp exit
.db "IF" ; This word's atom is followed by 1b *relative* offset (to the cell's addr) to
.fill 5 ; where to branch to. For example, The branching cell of "IF THEN" would
.db 1 ; IMMEDIATE ; contain 3. Add this value to RS.
.dw CMP .db "(fbr)"
IF: .fill 2
.dw nativeWord .dw CMP
; Spit a conditional branching atom, followed by an empty 1b cell. Then, .db 0
; push the address of that cell on the PS. ELSE or THEN will pick FBR:
; them up and set the offset. .dw nativeWord
ld hl, (CMPDST) push de
ld de, CBRANCH ld l, (ix)
call DEinHL ld h, (ix+1)
push hl ; address of cell to fill ld a, (hl)
inc hl ; empty 1b cell call addHL
ld (CMPDST), hl ld (ix), l
ld (ix+1), h
pop de
jp exit jp exit
.db "ELSE" ; Conditional branch, only branch if TOS is zero
.fill 3 .db "(fbr?)"
.db 1 ; IMMEDIATE .fill 1
.dw IF .dw FBR
ELSE: .db 0
.dw nativeWord FBRC:
; First, let's set IF's branching cell. .dw nativeWord
pop de ; cell's address pop hl
ld hl, (CMPDST) ld a, h
; also skip ELSE word. or l
inc hl \ inc hl \ inc hl jr z, FBR+2
or a ; clear carry ; skip next byte in RS
sbc hl, de ; HL now has relative offset ld l, (ix)
ld a, l ld h, (ix+1)
ld (de), a inc hl
; Set IF's branching cell to current atom address and spit our own ld (ix), l
; uncondition branching cell, which will then be picked up by THEN. ld (ix+1), h
; First, let's spit our 4 bytes
ld hl, (CMPDST)
ld de, BRANCH
call DEinHL
push hl ; address of cell to fill
inc hl ; empty 1b cell
ld (CMPDST), hl
jp exit jp exit
.db "THEN"
.fill 3
.db 1 ; IMMEDIATE
.dw ELSE
THEN:
.dw nativeWord
; See comments in IF and ELSE
pop de ; cell's address
ld hl, (CMPDST)
; There is nothing to skip because THEN leaves nothing.
or a ; clear carry
sbc hl, de ; HL now has relative offset
ld a, l
ld (de), a
jp exit
.db "RECURSE" .db "RECURSE"
.dw FBRC
.db 0 .db 0
.dw THEN
RECURSE: RECURSE:
.dw nativeWord .dw nativeWord
call popRS call popRS

View File

@ -1,6 +1,7 @@
Stack notation: "<stack before> -- <stack after>". Rightmost is top of stack Stack notation: "<stack before> -- <stack after>". Rightmost is top of stack
(TOS). For example, in "a b -- c d", b is TOS before, d is TOS after. "R:" means (TOS). For example, in "a b -- c d", b is TOS before, d is TOS after. "R:" means
that the Return Stack is modified. that the Return Stack is modified. "I:" prefix means "IMMEDIATE", that is, that
this stack transformation is made at compile time.
DOES>: Used inside a colon definition that itself uses CREATE, DOES> transforms DOES>: Used inside a colon definition that itself uses CREATE, DOES> transforms
that newly created word into a "does cell", that is, a regular cell ( when that newly created word into a "does cell", that is, a regular cell ( when
@ -25,27 +26,39 @@ Atom: A word of the type compiledWord contains, in its PF, a list of what we
call "atoms". Those atoms are most of the time word references, but they can call "atoms". Those atoms are most of the time word references, but they can
also be references to NUMBER and LIT. also be references to NUMBER and LIT.
Words between "()" are "support words" that aren't really meant to be used
directly, but as part of another word.
"*I*" in description indicates an IMMEDIATE word.
*** Defining words *** *** Defining words ***
: x ... -- Define a new word : x ... -- Define a new word
; R:I -- Exit a colon definition ; R:I -- Exit a colon definition
, n -- Write n in HERE and advance it.
' x -- a Push addr of word x to a.
['] x -- *I* Like "'", but spits the addr as a number literal.
ALLOT n -- Move HERE by n bytes ALLOT n -- Move HERE by n bytes
C, b -- Write byte b in HERE and advance it.
CREATE x -- Create cell named x. Doesn't allocate a PF. CREATE x -- Create cell named x. Doesn't allocate a PF.
CONSTANT x n -- Creates cell x that when called pushes its value CONSTANT x n -- Creates cell x that when called pushes its value
DOES> -- See description at top of file DOES> -- See description at top of file
IMMEDIATE -- Flag the latest defined word as immediate. IMMEDIATE -- Flag the latest defined word as immediate.
LITERAL n -- Inserts number from TOS as a literal LITERAL n -- *I* Inserts number from TOS as a literal
VARIABLE c -- Creates cell x with 2 bytes allocation. VARIABLE c -- Creates cell x with 2 bytes allocation.
*** Flow *** *** Flow ***
ELSE -- Branch to THEN (fbr?) f -- Conditionally branches forward by the number
specified in its atom's cell.
(fbr) -- Branches forward by the number specified in its
atom's cell.
ELSE I:a -- *I* Compiles a (fbr) and set branching cell at a.
EXECUTE a -- Execute wordref at addr a EXECUTE a -- Execute wordref at addr a
IF n -- Branch to ELSE or THEN if n is zero IF -- I:a *I* Compiles a (fbr?) and pushes its cell's addr
INTERPRET -- Get a line from stdin, compile it in tmp memory, INTERPRET -- Get a line from stdin, compile it in tmp memory,
then execute the compiled contents. then execute the compiled contents.
QUIT R:drop -- Return to interpreter promp immediately QUIT R:drop -- Return to interpreter promp immediately
RECURSE R:I -- R:I-2 Run the current word again. RECURSE R:I -- R:I-2 Run the current word again.
THEN -- Does nothing. Serves as a branching merker for IF THEN I:a -- *I* Set branching cell at a.
and ELSE.
*** Stack *** *** Stack ***
DUP a -- a a DUP a -- a a
@ -64,11 +77,13 @@ C@ a -- c Set c to byte at address a
C! c a -- Store byte c in address a C! c a -- Store byte c in address a
CURRENT -- n Set n to wordref of last added entry. CURRENT -- n Set n to wordref of last added entry.
HERE -- a Push HERE's address HERE -- a Push HERE's address
H -- a HERE @
*** Arithmetic *** *** Arithmetic ***
+ a b -- c a + b -> c + a b -- c a + b -> c
- a b -- c a - b -> c - a b -- c a - b -> c
-^ a b -- c b - a -> c
* a b -- c a * b -> c * a b -- c a * b -> c
/ a b -- c a / b -> c / a b -- c a / b -> c

View File

@ -8,15 +8,20 @@
; Offset of the code link relative to the beginning of the word ; Offset of the code link relative to the beginning of the word
.equ CODELINK_OFFSET NAMELEN+3 .equ CODELINK_OFFSET NAMELEN+3
; Flags for the "flag field" of the word structure
; IMMEDIATE word
.equ FLAG_IMMED 0
; This wordref is not a regular word (it's not preceeded by a name). It's one
; of the NUMBER, LIT, BRANCH etc. entities.
.equ FLAG_UNWORD 1
; *** Variables *** ; *** Variables ***
.equ INITIAL_SP FORTH_RAMSTART .equ INITIAL_SP FORTH_RAMSTART
.equ CURRENT @+2 .equ CURRENT @+2
.equ HERE @+2 .equ HERE @+2
.equ OLDHERE @+2
; Pointer to where we currently are in the interpretation of the current line. ; Pointer to where we currently are in the interpretation of the current line.
.equ INPUTPOS @+2 .equ INPUTPOS @+2
; Pointer to where compiling words should output. During interpret, it's a
; moving target in (COMPBUF). During DEFINE, it's (HERE).
.equ CMPDST @+2
; Buffer where we compile the current input line. Same size as STDIO_BUFSIZE. ; Buffer where we compile the current input line. Same size as STDIO_BUFSIZE.
.equ COMPBUF @+2 .equ COMPBUF @+2
.equ FORTH_RAMEND @+0x40 .equ FORTH_RAMEND @+0x40
@ -31,19 +36,29 @@
; ;
; 1. read single word from line ; 1. read single word from line
; 2. compile word to atom ; 2. compile word to atom
; 3. execute atom ; 3. if immediate, execute atom
; 4. goto 1 ; 4. goto 1 until we exhaust words
; 5. Execute compiled atom list as if it was a regular compiledWord.
; ;
; During step 3, it's possible that atom read from input, so INPUTPOS might ; Because the Parameter Stack uses SP, we can't just go around calling routines:
; have moved between 3 and 4.
;
; Because the Parameter Stack uses PS, we can't just go around calling routines:
; This messes with the PS. This is why we almost always jump (unless our call ; This messes with the PS. This is why we almost always jump (unless our call
; doesn't involve Forth words in any way). ; doesn't involve Forth words in any way).
; ;
; This presents a challenge for our interpret loop because step 4, "goto 1" ; This presents a challenge for our interpret loop because step 4, "goto 1"
; isn't obvious. To be able to do that, we must push a "return routine" to the ; isn't obvious. To be able to do that, we must push a "return routine" to the
; Return Stack before step 3. ; Return Stack before step 3.
;
; HERE and IMMEDIATE: When compiling in step 2, we spit compiled atoms in
; (HERE) to simplify "," semantic in Forth (spitting, in all cases, is done in
; (HERE)). However, suring input line compilation, it isn't like during ":", we
; aren't creating a new entry.
;
; Compiling and executing from (HERE) would be dangerous because an
; entry-creation word, during runtime, could end up overwriting the atom list
; we're executing. This is why we have this list in COMPBUF.
;
; During IMMEDIATE mode, (HERE) is temporarily set to COMPBUF, and when we're
; done, we restore (HERE) for runtime. This way, everyone is happy.
; *** Code *** ; *** Code ***
forthMain: forthMain:
@ -71,8 +86,12 @@ forthRdLine:
call stdioReadLine call stdioReadLine
ld ix, RS_ADDR-2 ; -2 because we inc-before-push ld ix, RS_ADDR-2 ; -2 because we inc-before-push
ld (INPUTPOS), hl ld (INPUTPOS), hl
; We're about to compile the line and possibly execute IMMEDIATE words.
; Let's save current (HERE) and temporarily set it to COMPBUF.
ld hl, (HERE)
ld (OLDHERE), hl
ld hl, COMPBUF ld hl, COMPBUF
ld (CMPDST), hl ld (HERE), hl
forthInterpret: forthInterpret:
call readword call readword
jr nz, .execute jr nz, .execute
@ -104,9 +123,9 @@ forthInterpret:
; called, triggering an abort. ; called, triggering an abort.
ld de, LIT ld de, LIT
call .writeDE call .writeDE
ld de, (CMPDST) ld de, (HERE)
call strcpyM call strcpyM
ld (CMPDST), de ld (HERE), de
jr forthInterpret jr forthInterpret
.immed: .immed:
push hl ; --> lvl 1 push hl ; --> lvl 1
@ -117,16 +136,19 @@ forthInterpret:
.execute: .execute:
ld de, QUIT ld de, QUIT
call .writeDE call .writeDE
; Compilation done, let's restore (HERE) and execute!
ld hl, (OLDHERE)
ld (HERE), hl
ld iy, COMPBUF ld iy, COMPBUF
jp compiledWord jp compiledWord
.writeDE: .writeDE:
push hl push hl
ld hl, (CMPDST) ld hl, (HERE)
ld (hl), e ld (hl), e
inc hl inc hl
ld (hl), d ld (hl), d
inc hl inc hl
ld (CMPDST), hl ld (HERE), hl
pop hl pop hl
ret ret

View File

@ -69,12 +69,12 @@ HLPointsLIT:
pop de pop de
ret ret
HLPointsBRANCH: HLPointsBR:
push de push de
ld de, BRANCH ld de, FBR
call HLPointsDE call HLPointsDE
jr z, .end jr z, .end
ld de, CBRANCH ld de, FBRC
call HLPointsDE call HLPointsDE
.end: .end:
pop de pop de
@ -87,20 +87,13 @@ HLPointsEXIT:
pop de pop de
ret ret
HLPointsQUIT:
push de
ld de, QUIT
call HLPointsDE
pop de
ret
; Skip the compword where HL is currently pointing. If it's a regular word, ; Skip the compword where HL is currently pointing. If it's a regular word,
; it's easy: we inc by 2. If it's a NUMBER, we inc by 4. If it's a LIT, we skip ; it's easy: we inc by 2. If it's a NUMBER, we inc by 4. If it's a LIT, we skip
; to after null-termination. ; to after null-termination.
compSkip: compSkip:
call HLPointsNUMBER call HLPointsNUMBER
jr z, .isNum jr z, .isNum
call HLPointsBRANCH call HLPointsBR
jr z, .isBranch jr z, .isBranch
call HLPointsLIT call HLPointsLIT
jr nz, .isWord jr nz, .isWord
@ -176,15 +169,8 @@ readLIT:
ex de, hl ex de, hl
ret ret
.notLIT: .notLIT:
; Alright, not a literal, but is it a word? If it's not a number, then ; Alright, not a literal, but is it a word?
; it's a word. call HLPointsUNWORD
call HLPointsNUMBER
jr z, .notWord
call HLPointsBRANCH
jr z, .notWord
call HLPointsEXIT
jr z, .notWord
call HLPointsQUIT
jr z, .notWord jr z, .notWord
; Not a number, then it's a word. Copy word to pad and point to it. ; Not a number, then it's a word. Copy word to pad and point to it.
push hl ; --> lvl 1. we need it to set DE later push hl ; --> lvl 1. we need it to set DE later
@ -233,18 +219,6 @@ readLITTOS:
pop de pop de
ret ret
; For DE being a wordref, move DE to the previous wordref.
; Z is set if DE point to 0 (no entry). NZ if not.
prev:
dec de \ dec de ; prev field
call intoDE
; DE points to prev. Is it zero?
xor a
or d
or e
; Z will be set if DE is zero
ret
; Find the entry corresponding to word where (HL) points to and sets DE to ; Find the entry corresponding to word where (HL) points to and sets DE to
; point to that entry. ; point to that entry.
; Z if found, NZ if not. ; Z if found, NZ if not.
@ -264,7 +238,7 @@ find:
call strncmp call strncmp
pop de ; <-- lvl 1, return to wordref pop de ; <-- lvl 1, return to wordref
jr z, .end ; found jr z, .end ; found
call prev call .prev
jr nz, .inner jr nz, .inner
; Z set? end of dict unset Z ; Z set? end of dict unset Z
inc a inc a
@ -273,6 +247,18 @@ find:
pop hl pop hl
ret ret
; For DE being a wordref, move DE to the previous wordref.
; Z is set if DE point to 0 (no entry). NZ if not.
.prev:
dec de \ dec de \ dec de ; prev field
call intoDE
; DE points to prev. Is it zero?
xor a
or d
or e
; Z will be set if DE is zero
ret
; Write compiled data from HL into IY, advancing IY at the same time. ; Write compiled data from HL into IY, advancing IY at the same time.
wrCompHL: wrCompHL:
ld (iy), l ld (iy), l
@ -291,13 +277,11 @@ entryhead:
ld de, (CURRENT) ld de, (CURRENT)
ld a, NAMELEN ld a, NAMELEN
call addHL call addHL
xor a ; IMMED call DEinHL
; Set word flags: not IMMED, not UNWORD, so it's 0
xor a
ld (hl), a ld (hl), a
inc hl inc hl
ld (hl), e
inc hl
ld (hl), d
inc hl
ld (CURRENT), hl ld (CURRENT), hl
ld (HERE), hl ld (HERE), hl
xor a ; set Z xor a ; set Z
@ -306,16 +290,10 @@ entryhead:
; Sets Z if wordref at HL is of the IMMEDIATE type ; Sets Z if wordref at HL is of the IMMEDIATE type
HLisIMMED: HLisIMMED:
dec hl dec hl
dec hl bit FLAG_IMMED, (hl)
dec hl inc hl
; We need an invert flag. We want to Z to be set when flag is non-zero. ; We need an invert flag. We want to Z to be set when flag is non-zero.
ld a, 1 jp toggleZ
and (hl)
dec a ; if A was 1, Z is set. Otherwise, Z is unset
inc hl
inc hl
inc hl
ret
; Sets Z if wordref at (HL) is of the IMMEDIATE type ; Sets Z if wordref at (HL) is of the IMMEDIATE type
HLPointsIMMED: HLPointsIMMED:
@ -325,6 +303,22 @@ HLPointsIMMED:
pop hl pop hl
ret ret
; Sets Z if wordref at HL is of the UNWORD type
HLisUNWORD:
dec hl
bit FLAG_UNWORD, (hl)
inc hl
; We need an invert flag. We want to Z to be set when flag is non-zero.
jp toggleZ
; Sets Z if wordref at (HL) is of the IMMEDIATE type
HLPointsUNWORD:
push hl
call intoHL
call HLisUNWORD
pop hl
ret
; Checks flags Z and C and sets BC to 0 if Z, 1 if C and -1 otherwise ; Checks flags Z and C and sets BC to 0 if Z, 1 if C and -1 otherwise
flagsToBC: flagsToBC:
ld bc, 0 ld bc, 0

View File

@ -105,3 +105,10 @@ strlen:
dec a dec a
pop bc pop bc
ret ret
; make Z the opposite of what it is now
toggleZ:
jp z, unsetZ
cp a
ret

View File

@ -24,12 +24,6 @@ subDEFromHL:
pop af pop af
ret ret
; make Z the opposite of what it is now
toggleZ:
jp z, unsetZ
cp a
ret
; Compares strings pointed to by HL and DE up to A count of characters in a ; Compares strings pointed to by HL and DE up to A count of characters in a
; case-insensitive manner. ; case-insensitive manner.
; If equal, Z is set. If not equal, Z is reset. ; If equal, Z is set. If not equal, Z is reset.