forth: add "UNWORD" flag

Also, reorder word fields so that the flag field is more easily accessible.
This commit is contained in:
Virgil Dupras 2020-03-12 11:39:27 -04:00
parent a8e573c84a
commit ff281f69a8
5 changed files with 131 additions and 89 deletions

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, BRANCH, 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:
@ -65,6 +70,7 @@ branchWord:
pop de pop de
jp exit jp exit
.db 0b10 ; Flags
BRANCH: BRANCH:
.dw branchWord .dw branchWord
@ -82,6 +88,7 @@ cbranchWord:
ld (ix+1), h ld (ix+1), h
jp exit jp exit
.db 0b10 ; Flags
CBRANCH: CBRANCH:
.dw cbranchWord .dw cbranchWord
@ -102,6 +109,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 +128,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 +154,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 +176,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 +191,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 +203,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 +215,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 +228,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 +243,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 +291,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,21 +315,21 @@ 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 dec hl
dec hl dec hl
inc (hl) set FLAG_IMMED, (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, (CMPDST)
@ -323,8 +342,9 @@ LITERAL:
; ( -- c ) ; ( -- c )
.db "KEY" .db "KEY"
.fill 5 .fill 4
.dw LITERAL .dw LITERAL
.db 0
KEY: KEY:
.dw nativeWord .dw nativeWord
call stdioGetC call stdioGetC
@ -334,8 +354,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 +370,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 +402,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 +415,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 +427,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 +439,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 +452,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 +463,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 +475,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 +492,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 +504,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 +519,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 +533,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 +552,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 +565,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 +579,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 +592,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 +605,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 +619,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
@ -594,8 +634,8 @@ CMP:
.db "IF" .db "IF"
.fill 5 .fill 5
.db 1 ; IMMEDIATE
.dw CMP .dw CMP
.db 1 ; IMMEDIATE
IF: IF:
.dw nativeWord .dw nativeWord
; Spit a conditional branching atom, followed by an empty 1b cell. Then, ; Spit a conditional branching atom, followed by an empty 1b cell. Then,
@ -611,8 +651,8 @@ IF:
.db "ELSE" .db "ELSE"
.fill 3 .fill 3
.db 1 ; IMMEDIATE
.dw IF .dw IF
.db 1 ; IMMEDIATE
ELSE: ELSE:
.dw nativeWord .dw nativeWord
; First, let's set IF's branching cell. ; First, let's set IF's branching cell.
@ -637,8 +677,8 @@ ELSE:
.db "THEN" .db "THEN"
.fill 3 .fill 3
.db 1 ; IMMEDIATE
.dw ELSE .dw ELSE
.db 1 ; IMMEDIATE
THEN: THEN:
.dw nativeWord .dw nativeWord
; See comments in IF and ELSE ; See comments in IF and ELSE
@ -652,8 +692,8 @@ THEN:
jp exit jp exit
.db "RECURSE" .db "RECURSE"
.db 0
.dw THEN .dw THEN
.db 0
RECURSE: RECURSE:
.dw nativeWord .dw nativeWord
call popRS call popRS

View File

@ -8,6 +8,13 @@
; 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

View File

@ -87,13 +87,6 @@ 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.
@ -176,16 +169,9 @@ 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 nz, .notWord
jr z, .notWord
call HLPointsBRANCH
jr z, .notWord
call HLPointsEXIT
jr z, .notWord
call HLPointsQUIT
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
call intoHL call intoHL
@ -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.