mirror of
https://github.com/mgcaret/of816.git
synced 2024-12-27 04:29:32 +00:00
7100 lines
188 KiB
ArmAsm
7100 lines
188 KiB
ArmAsm
|
|
; Forth Built-in Dictionary
|
|
|
|
; Note that no primitive words should start with a JSL as the body-modifying words
|
|
; such as TO, DEFER!, etc. will assume that they can write to the cell immediately
|
|
; following the JSL. This would be bad if they are not supposed to do so.
|
|
; of course, this caution doesn't apply to words in ROM that can't be altered
|
|
|
|
; comments starting with H: define help text to be used for documentation generation
|
|
; including if I ever ship a help command
|
|
|
|
dstart "forth"
|
|
|
|
.if max_search_order > 0
|
|
; ( u -- wid ) search order word list entry by number
|
|
hword WLNUM,"WL#"
|
|
ENTER
|
|
.dword dORDER
|
|
.dword SWAP
|
|
.dword DUP
|
|
ONLIT max_search_order
|
|
.dword ULT
|
|
.dword _IF
|
|
.dword bad
|
|
.dword INCR
|
|
.dword NAPLUS
|
|
EXIT
|
|
bad: ONLIT -49
|
|
.dword THROW
|
|
eword
|
|
|
|
; H: ( widn ... wid1 n -- ) Set dictionary search order.
|
|
dword SET_ORDER,"SET-ORDER"
|
|
ENTER
|
|
.dword DUP
|
|
.dword _IF
|
|
.dword empty
|
|
.dword DUP ; ( ... widn ... wid1 n n' )
|
|
ONLIT 0 ; ( ... widn ... wid1 n n' 0 )
|
|
.dword SLT ; ( ... widn ... wid1 n f )
|
|
.dword _IF ; ( ... widn ... wid1 n )
|
|
.dword dolist
|
|
.dword DROP ; ( n -- )
|
|
.dword FORTH_WORDLIST ; ( -- wid )
|
|
ONLIT 1 ; ( ... wid 1 )
|
|
dolist: .dword DUP ; ( ... widn ... wid1 u u' )
|
|
ONLIT max_search_order ; ( ... widn ... wid1 u u' u2 )
|
|
.dword ULTE ; ( ... widn ... wid1 u f )
|
|
.dword _IF ; ( ... widn ... wid1 u )
|
|
.dword bad
|
|
.dword DUP ; ( ... widn ... wid1 u u' )
|
|
.dword dORDER ; ( ... widn ... wid1 u u' c-addr )
|
|
.dword STORE ; ( ... widn ... wid1 u )
|
|
.dword DECR
|
|
ONLIT 0 ; ( ... widn ... wid1 u' 0 )
|
|
.dword SWAP ; ( ... widn ... wid1 0 u' )
|
|
.dword _DO ; ( ... widn ... wid1 )
|
|
JUMP lpdone
|
|
lp: .dword IX ; ( ... widn ... wid1 u' )
|
|
.dword WLNUM ; ( ... widn ... wid1 c-addr )
|
|
.dword STORE
|
|
ONLIT -1
|
|
.dword _PLOOP
|
|
.dword lp
|
|
lpdone: .dword UNLOOP
|
|
EXIT
|
|
bad: ONLIT -49
|
|
.dword THROW
|
|
empty: .dword dORDER
|
|
.dword STORE
|
|
EXIT
|
|
eword
|
|
|
|
.endif
|
|
|
|
.if max_search_order > 0
|
|
; H: ( -- wid ) Return the WID of the wordlist containing system words.
|
|
dword FORTH_WORDLIST,"FORTH-WORDLIST"
|
|
.else
|
|
hword FORTH_WORDLIST,"FORTH-WORDLIST"
|
|
.endif
|
|
SYSVAR SV_FORTH_WL
|
|
eword
|
|
|
|
; H: ( -- ) Set the first wordlist in the search order to the system words
|
|
dword FORTH,"FORTH"
|
|
.if max_search_order > 0
|
|
ENTER
|
|
.dword FORTH_WORDLIST
|
|
.dword TOP_OF_ORDER
|
|
EXIT
|
|
.else
|
|
; no-op if no search-ordering allowed
|
|
NEXT
|
|
.endif
|
|
eword
|
|
|
|
.if max_search_order > 0
|
|
; H: ( -- wid ) Return the WID of the wordlist for environmental queries.
|
|
dword dENVQ_WL,"$ENV?-WL"
|
|
.else
|
|
hword dENVQ_WL,"$ENV?-WL"
|
|
.endif
|
|
SYSVAR SV_ENV_WL
|
|
eword
|
|
|
|
; The prior was the absolute minimum search order that is possible, but we will
|
|
; not use it directly, "FORTH" will be the minimum. However this will be the root
|
|
; of all additional wordlists so that the system can be brought into a usable state
|
|
; via FORTH.
|
|
|
|
; ( -- a-addr ) variable containing location of search order
|
|
hword ddORDER,"$$ORDER"
|
|
SYSVAR SV_dORDER
|
|
eword
|
|
|
|
; ( -- a-addr ) location of search order stack
|
|
hword dORDER,"$ORDER"
|
|
ENTER
|
|
.dword ddORDER
|
|
.dword FETCH
|
|
EXIT
|
|
eword
|
|
|
|
; ( -- a-addr ) Variable containing current compiler wordlist.
|
|
hword dCURRENT,"$CURRENT"
|
|
SYSVAR SV_CURRENT
|
|
eword
|
|
|
|
.if max_search_order > 0
|
|
; H: ( -- addr ) Return address of cell with first wid in the search order.
|
|
; H: if search order is empty, sets the search order to contain the CURRENT word list.
|
|
dword CONTEXT,"CONTEXT"
|
|
.else
|
|
hword CONTEXT,"CONTEXT"
|
|
.endif
|
|
.if max_search_order > 0
|
|
ENTER
|
|
.dword dORDER ; ( - addr )
|
|
.dword DUP ; ( .. addr addr )
|
|
.dword FETCH ; ( .. addr u )
|
|
.dword SCELLMULT ; ( .. addr u' )
|
|
.dword QDUP ; ( .. addr u' u' | addr u' )
|
|
.dword _IF ; ( .. addr u' | addr )
|
|
.dword empty ; false branch ( .. addr )
|
|
.dword PLUS ; ( addr u' - addr' )
|
|
EXIT
|
|
empty: .dword CELLPLUS ; ( addr - addr' )
|
|
.dword GET_CURRENT ; ( .. addr' wid )
|
|
.dword TOP_OF_ORDER ; ( .. addr' )
|
|
EXIT
|
|
.else
|
|
.dword dCURRENT
|
|
EXIT
|
|
.endif
|
|
eword
|
|
|
|
.if max_search_order > 0
|
|
; H: ( -- wid ) Get WID current compiler wordlist.
|
|
dword GET_CURRENT,"GET-CURRENT"
|
|
.else
|
|
hword GET_CURRENT,"GET-CURRENT"
|
|
.endif
|
|
ENTER
|
|
.dword dCURRENT
|
|
.dword FETCH
|
|
EXIT
|
|
eword
|
|
|
|
.if max_search_order > 0
|
|
; H: ( -- widn ... wid1 u ) Get dictionary search order.
|
|
dword GET_ORDER,"GET-ORDER"
|
|
ENTER
|
|
.dword dORDER
|
|
.dword FETCH
|
|
ONLIT 0
|
|
.dword _QDO
|
|
JUMP lpdone
|
|
lp: .dword IX
|
|
.dword WLNUM
|
|
.dword FETCH
|
|
ONLIT 1
|
|
.dword _PLOOP
|
|
.dword lp
|
|
lpdone: .dword UNLOOP
|
|
.dword dORDER
|
|
.dword FETCH
|
|
EXIT
|
|
eword
|
|
|
|
; ( wid -- ) Set the first wordlist in the search order.
|
|
hword TOP_OF_ORDER,"TOP-OF-ORDER"
|
|
ENTER
|
|
.dword PtoR
|
|
.dword GET_ORDER
|
|
.dword QDUP
|
|
.dword _IF
|
|
.dword default
|
|
.dword NIP
|
|
.dword RtoP
|
|
.dword SWAP
|
|
.dword SET_ORDER
|
|
EXIT
|
|
default: .dword RtoP
|
|
ONLIT 1
|
|
.dword SET_ORDER
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( -- ) Duplicate the first wordlist in the search order.
|
|
dword ALSO,"ALSO"
|
|
ENTER
|
|
.dword GET_ORDER
|
|
.dword QDUP
|
|
.dword _IFFALSE
|
|
.dword :+
|
|
.dword GET_CURRENT
|
|
.dword ONE
|
|
: .dword INCR
|
|
.dword OVER
|
|
.dword SWAP
|
|
.dword SET_ORDER
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( -- ) Remove the first wordlist in the search order.
|
|
dword PREVIOUS,"PREVIOUS"
|
|
ENTER
|
|
.dword GET_ORDER
|
|
.dword QDUP
|
|
.dword _IF
|
|
.dword noorder
|
|
.dword NIP
|
|
.dword DECR
|
|
.dword SET_ORDER
|
|
EXIT
|
|
noorder: ONLIT -50
|
|
.dword THROW
|
|
eword
|
|
|
|
; H: ( wid -- ) Set the compiler wordlist.
|
|
dword SET_CURRENT,"SET-CURRENT"
|
|
ENTER
|
|
.dword dCURRENT
|
|
.dword STORE
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( -- ) Set the search order to contain only the system wordlist.
|
|
dword ONLY,"ONLY"
|
|
ENTER
|
|
.dword FORTH_WORDLIST
|
|
ONLIT 1
|
|
.dword SET_ORDER
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( -- ) Set the search order to contain only the current top of the order.
|
|
dword SEAL,"SEAL"
|
|
ENTER
|
|
.dword CONTEXT
|
|
.dword FETCH
|
|
.dword ONE
|
|
.dword SET_ORDER
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( wid -- addr u ) Return the name of a wordlist, or ^address if no name.
|
|
hword WL_NAME,"WL-NAME"
|
|
ENTER
|
|
.dword DUP
|
|
.dword CELLPLUS
|
|
.dword FETCH
|
|
.dword QDUP
|
|
.dword _IF
|
|
.dword noname
|
|
.dword NIP
|
|
.dword rNAME
|
|
EXIT
|
|
noname: JUMP rNAME_noname1
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( -- ) Display the current search order and compiler wordlist.
|
|
dword ORDER,"ORDER"
|
|
ENTER
|
|
SLIT "Compiling to: "
|
|
.dword TYPE
|
|
.dword GET_CURRENT
|
|
.dword WL_NAME
|
|
.dword TYPE
|
|
.dword CR
|
|
SLIT "Search order:"
|
|
.dword TYPE
|
|
.dword CR
|
|
.dword GET_ORDER
|
|
ONLIT 0
|
|
.dword _QDO
|
|
JUMP lpdone
|
|
lp: .dword WL_NAME
|
|
.dword TYPE
|
|
.dword CR
|
|
ONLIT 1
|
|
.dword _PLOOP
|
|
.dword lp
|
|
lpdone: .dword UNLOOP
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( -- ) Set the compiler wordlist to the first wordlist in the search order.
|
|
dword DEFINITIONS,"DEFINITIONS"
|
|
ENTER
|
|
.dword CONTEXT
|
|
.dword FETCH
|
|
.dword SET_CURRENT
|
|
EXIT
|
|
eword
|
|
.endif
|
|
|
|
; ( -- cell ) read literal cell from instruction stream, place it on the stack
|
|
hword _LIT,"_LIT"
|
|
jsr _fetch_ip_cell
|
|
PUSHNEXT
|
|
eword
|
|
|
|
; ( -- word ) read literal word from instruction stream, place it on the stack
|
|
hword _WLIT,"_WLIT"
|
|
jsr _fetch_ip_word
|
|
tay
|
|
lda #$0000
|
|
PUSHNEXT
|
|
eword
|
|
|
|
; ( -- char ) read literal char from instruction stream, place it on the stack
|
|
hword _CLIT,"_CLIT"
|
|
jsr _fetch_ip_byte
|
|
tay
|
|
lda #$0000
|
|
PUSHNEXT
|
|
eword
|
|
|
|
; ( -- c-addr u ) skip string in instruction stream, place address and len on stack
|
|
; read cell-sized <length> from instruction stream, place it on the stack
|
|
; place the address of the next cell on the stack
|
|
; skip <length> bytes in the instruction stream
|
|
hword _SLIT,"_SLIT"
|
|
jsr _fetch_ip_cell
|
|
sty WR
|
|
sta WR+2
|
|
jsr _pushay
|
|
ldy IP
|
|
lda IP+2
|
|
iny
|
|
bne :+
|
|
inc a
|
|
: jsr _pushay
|
|
jsr _swap
|
|
lda IP
|
|
clc
|
|
adc WR
|
|
sta IP
|
|
lda IP+2
|
|
adc WR+2
|
|
sta IP+2
|
|
NEXT
|
|
eword
|
|
|
|
|
|
; ( -- ) Directly compile a cell literal from IP to [HERE]
|
|
; read next cell from instruction stream, compile it into the dictionary
|
|
hword _COMP_LIT,"_COMP_LIT"
|
|
jsr _fetch_ip_cell
|
|
jsr _ccellay
|
|
NEXT
|
|
eword
|
|
|
|
; ( -- ) Directly compile a character literal from IP to [HERE]
|
|
; read char from instruction stream, compile it into the dictionary
|
|
hword _COMP_CLIT,"_COMP_LIT"
|
|
jsr _fetch_ip_byte
|
|
jsr _cbytea
|
|
NEXT
|
|
eword
|
|
|
|
; ( -- ) System initialization
|
|
hword ddSYSINIT,"$$SYSINIT"
|
|
ENTER
|
|
.dword FORTH_WORDLIST ; make sure current wordlist is the Forth wordlist
|
|
.dword dCURRENT
|
|
.dword STORE
|
|
.dword HERE ; set up minimal search order stuff
|
|
.dword ddORDER
|
|
.dword STORE
|
|
ONLIT 0 ; for # of items in order
|
|
.dword COMMA
|
|
.if max_search_order > 0
|
|
ONLIT max_search_order ; now allocate the storage for the search order
|
|
.dword CELLS
|
|
.dword ALLOT
|
|
.endif ; max_search_order
|
|
.dword dMEMTOP ; set $HIMEM to $MEMTOP for dynamic allocation
|
|
.dword FETCH
|
|
.dword dHIMEM
|
|
.dword STORE
|
|
ONLIT tib_size
|
|
.dword ALLOC ; TODO: catch exception
|
|
.dword dTIB
|
|
.dword STORE
|
|
.if include_fcode
|
|
ONLIT SI_GET_FCODE ; See if system wants us to evaluate FCode
|
|
.dword dSYSIF
|
|
.dword QDUP
|
|
.dword _IF
|
|
.dword no_fcode ; apparently not
|
|
lp: .dword PtoR
|
|
.dword RCOPY
|
|
.dword FETCH
|
|
.dword QDUP
|
|
.dword _IF
|
|
.dword dn_fcode
|
|
.dword ONE
|
|
.dword BYTE_LOAD
|
|
.dword RtoP
|
|
.dword CELLPLUS
|
|
JUMP lp
|
|
dn_fcode: .dword RDROP
|
|
no_fcode:
|
|
.endif
|
|
NLIT NOOP ; set up STATUS defer.
|
|
SLIT "STATUS"
|
|
.dword dDEFER
|
|
.dword PROTECTED ; make sure it can't be FORGETted
|
|
.dword CR ; and say hello
|
|
SLIT "OF816 by M.G."
|
|
.dword TYPE
|
|
.dword CR
|
|
EXIT
|
|
eword
|
|
|
|
; ( xt base -- ) execute xt with temporary number base
|
|
hword TMPBASE,"TMPBASE"
|
|
ENTER
|
|
.dword BASE
|
|
.dword DUP
|
|
.dword FETCH
|
|
.dword PtoR
|
|
.dword STORE
|
|
.dword CATCH
|
|
.dword RtoP
|
|
.dword BASE
|
|
.dword STORE
|
|
.dword THROW
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( -- ) Display version information.
|
|
dword DOTVERSION,".VERSION"
|
|
ENTER
|
|
SLIT .concat("OF816 v",VERSION,"/")
|
|
.dword TYPE
|
|
ONLIT .time
|
|
ONLIT UDOT
|
|
ONLIT 16
|
|
.dword TMPBASE
|
|
.if .strlen(PLATFORM) > 0
|
|
SLIT .concat("for ", PLATFORM, ", CA65 ", .sprintf("%d.%d",.hibyte(.version),(.version & $F0)/$10))
|
|
.else
|
|
SLIT ", CA65"
|
|
.endif
|
|
.dword TYPE
|
|
.dword CR
|
|
.if include_fcode
|
|
SLIT "FCode enabled"
|
|
.dword TYPE
|
|
.dword CR
|
|
.endif
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( -- ) Reset the system.
|
|
dword RESET_ALL,"RESET-ALL"
|
|
lda #SI_RESET_ALL
|
|
jsl _call_sysif
|
|
bcc :+
|
|
jmp _throway
|
|
: NEXT
|
|
eword
|
|
|
|
; H: ( -- ) Restore system stack pointer and exit Forth.
|
|
dword BYE,"BYE"
|
|
lda SYS_RSTK
|
|
tcs
|
|
rtl
|
|
eword
|
|
|
|
; ( n -- ) display exception message
|
|
; Display a message associated with exception # n. It first looks to see if there
|
|
; is a MESSAGE ( n -- n|0 ) word in the current search order. If there is, it calls it and
|
|
; if n2 is nonzero, assumes no message was displayed and proceeds, otherwise we are done.
|
|
hword _MESSAGE,"_MESSAGE"
|
|
ENTER
|
|
SLIT "MESSAGE"
|
|
.dword dFIND
|
|
.dword _IF
|
|
.dword notfound
|
|
.dword CATCH
|
|
.dword _IFFALSE
|
|
.dword exc
|
|
.dword QDUP
|
|
.dword _IFFALSE
|
|
.dword nomsg
|
|
EXIT
|
|
notfound: .dword TWODROP
|
|
nomsg: ONLIT -4
|
|
.dword _IFEQUAL
|
|
.dword :+
|
|
SLIT "Stack u/f"
|
|
JUMP dotype
|
|
: ONLIT -13
|
|
.dword _IFEQUAL
|
|
.dword :+
|
|
SLIT "Def not found"
|
|
JUMP dotype
|
|
: ONLIT -10
|
|
.dword _IFEQUAL
|
|
.dword :+
|
|
SLIT "Div by 0"
|
|
JUMP dotype
|
|
: SLIT "Exception #"
|
|
.dword TYPE
|
|
.dword DOTD
|
|
EXIT
|
|
exc: SLIT "Exc. in MESSAGE!"
|
|
dotype: .dword TYPE
|
|
.dword DROP
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( xt -- xi ... xj n|0 ) Call xt, trap exception, and return it in n.
|
|
; catch return stack frame is:
|
|
; IP (4), old RSTK_SAVE (2), data SP (2, first out)
|
|
dword CATCH,"CATCH"
|
|
jsr _popwr ; remove xt for now, throw exception if none given
|
|
inc CATCHFLAG
|
|
lda IP+2 ; put catch frame on stack
|
|
pha ; starting with IP
|
|
lda IP
|
|
pha
|
|
lda RSTK_SAVE ; old saved return stack pointer
|
|
pha
|
|
phx ; data stack pointer
|
|
tsc
|
|
sta RSTK_SAVE ; save return stack for later restoration
|
|
ldy WR
|
|
lda WR+2
|
|
jsr _pushay ; push xt back on stack
|
|
ENTER
|
|
.dword EXECUTE ; execute framed xt
|
|
CODE
|
|
; no exception if we got here
|
|
lda #$0000
|
|
sta WR ; exit code will be zero
|
|
sta WR+2
|
|
pla ; drop old data SP
|
|
fixup: pla ; get old RSTK_SAVE
|
|
sta RSTK_SAVE
|
|
pla
|
|
sta IP ; restore previous IP (after catch)
|
|
pla
|
|
sta IP+2
|
|
dec CATCHFLAG
|
|
ldy WR
|
|
lda WR+2
|
|
PUSHNEXT
|
|
eword
|
|
|
|
; H: ( n -- ) Throw exception n if n is nonzero.
|
|
dword THROW,"THROW"
|
|
jsr _popay ; get exception # from stack
|
|
throway: .if trace
|
|
wdm $90
|
|
wdm $8f
|
|
.endif
|
|
cmp #$0000 ; is it zero?
|
|
bne :+
|
|
cpy #$0000
|
|
bne :+
|
|
NEXT ; if zero, do nothing
|
|
: sty WR ; if not zero, save it
|
|
sta WR+2
|
|
lda CATCHFLAG ; CATCH active?
|
|
beq uncaught ; nope, go handle it
|
|
lda RSTK_SAVE ; restore stack pointer to catch frame
|
|
tcs
|
|
plx ; restore data stack pointer
|
|
bra CATCH::fixup ; "return" from CATCH
|
|
uncaught: lda #$FFFF ; is negative?
|
|
cmp WR+2
|
|
bne :+ ; nope, don't check for specifics
|
|
lda WR
|
|
cmp #.loword(-1) ; ABORT
|
|
beq abort
|
|
cmp #.loword(-2) ; ABORT"
|
|
beq abort
|
|
: jsr _stackroom ; make room on data stack if needed
|
|
ldy WR
|
|
lda WR+2
|
|
jsr _pushay ; push exception # back on stack
|
|
ENTER ; short routine to display error message
|
|
.dword SPACE
|
|
.dword _MESSAGE
|
|
CODE
|
|
jmp __doquit ; and restart with QUIT
|
|
abort: ldx STK_TOP ; empty data stack per standard for ABORT
|
|
jmp __doquit ; and restart with QUIT
|
|
eword
|
|
_throway = THROW::throway
|
|
|
|
; ( -- f ) return true if a CATCH is active
|
|
hword CATCHQ,"CATCH?"
|
|
ldy CATCHFLAG
|
|
lda #$00
|
|
PUSHNEXT
|
|
eword
|
|
|
|
; ( f c-addr u -- ) word compiled or executed by ABORT"
|
|
; if f is true display c-addr u and execute -2 THROW, otherwise continue execution
|
|
hword _ABORTQ,"_ABORT'"
|
|
ENTER
|
|
.dword ROT
|
|
.dword _IF
|
|
.dword noabort
|
|
.dword CATCHQ
|
|
.dword _IF
|
|
.dword dotype
|
|
.dword TWODROP
|
|
.dword _SKIP
|
|
dotype: .dword TYPE
|
|
;.dword CLEAR
|
|
ONLIT -2
|
|
.dword THROW
|
|
noabort: .dword TWODROP
|
|
EXIT
|
|
eword
|
|
|
|
; H: Compilation/Interpretation: ( [text<">] -- )
|
|
; H: Execution: ( f -- )
|
|
; H: If f is true, display text and execute -2 THROW.
|
|
dwordq ABORTQ,"ABORT'",F_IMMED
|
|
ENTER
|
|
.dword SQ
|
|
.dword _SMART
|
|
.dword interp
|
|
.dword _COMP_LIT
|
|
interp: .dword _ABORTQ
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( -- ) Execute -1 THROW.
|
|
dword ABORT,"ABORT"
|
|
ENTER
|
|
ONLIT -1
|
|
.dword THROW
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( -- addr ) addr = address of the CPU direct page
|
|
dword dDIRECT,"$DIRECT"
|
|
tdc
|
|
tay
|
|
lda #$00
|
|
PUSHNEXT
|
|
eword
|
|
|
|
; H: ( -- addr ) addr = top of usable data space
|
|
dword dMEMTOP,"$MEMTOP"
|
|
ENTER
|
|
.dword dDIRECT
|
|
ONLIT MEM_TOP
|
|
.dword PLUS
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( -- u ) u = unused data space accounting for PAD and dynamic allocations
|
|
dword UNUSED,"UNUSED"
|
|
ENTER
|
|
.dword dHIMEM
|
|
.dword FETCH
|
|
.dword HERE
|
|
.dword MINUS
|
|
ONLIT 16
|
|
.dword MINUS
|
|
ONLIT word_buf_size
|
|
.dword MINUS
|
|
.if pad_size > 0
|
|
ONLIT pad_size
|
|
.dword MINUS
|
|
.endif
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( -- ) Do nothing.
|
|
dword NOOP,"NOOP"
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( -- u ) u = size of char in bytes.
|
|
dword SCHAR,"/C"
|
|
FCONSTANT 1
|
|
eword
|
|
|
|
; H: ( -- u ) u = size of word in bytes.
|
|
dword SWORD,"/W"
|
|
FCONSTANT 2
|
|
eword
|
|
|
|
; H: ( -- u ) u = size of long in bytes.
|
|
dword SLONG,"/L"
|
|
FCONSTANT 4
|
|
eword
|
|
|
|
; H: ( -- u ) u = size of cell in bytes.
|
|
dword SCELL,"/N"
|
|
FCONSTANT 4
|
|
eword
|
|
|
|
; H: ( u1 n -- u2 ) u2 = u1 + n * size of char in bytes.
|
|
dword CAPLUS,"CA+"
|
|
ENTER
|
|
.dword SCHAR
|
|
domath: .dword UMULT
|
|
.dword PLUS
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( u1 n -- u2 ) u2 = u1 + n * size of word in bytes.
|
|
dword WAPLUS,"WA+"
|
|
ENTER
|
|
.dword SWORD
|
|
JUMP CAPLUS::domath
|
|
eword
|
|
|
|
; H: ( u1 n -- u2 ) u2 = u1 + n * size of long in bytes.
|
|
dword LAPLUS,"LA+"
|
|
ENTER
|
|
.dword SLONG
|
|
JUMP CAPLUS::domath
|
|
eword
|
|
|
|
; H: ( u1 n -- u2 ) u2 = u1 + n * size of cell in bytes.
|
|
dword NAPLUS,"NA+"
|
|
ENTER
|
|
.dword SCELL
|
|
JUMP CAPLUS::domath
|
|
eword
|
|
|
|
; H: ( u1 -- u2 ) u2 = u1 + size of char in bytes.
|
|
dword CHARPLUS,"CHAR+"
|
|
ENTER
|
|
.dword SCHAR
|
|
.dword PLUS
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( u1 -- u2 ) u2 = u1 + size of cell in bytes.
|
|
dword CELLPLUS,"CELL+"
|
|
ENTER
|
|
.dword SCELL
|
|
.dword PLUS
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( n1 -- n2 ) n2 = n1 * size of char.
|
|
dword CHARS,"CHARS"
|
|
ENTER
|
|
.dword SCHAR
|
|
.dword UMULT
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( n1 -- n2 ) n2 = n1 * size of cell.
|
|
dword CELLS,"CELLS"
|
|
ENTER
|
|
.dword SCELL
|
|
.dword UMULT
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( u1 -- u2 ) u2 = next aligned address after u1.
|
|
dword ALIGNED,"ALIGNED"
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( n1 -- n2 ) n2 = n1 + size of char.
|
|
dword CAINCR,"CA1+"
|
|
jmp CHARPLUS::code
|
|
eword
|
|
|
|
; H: ( n1 -- n2 ) n2 = n1 + size of word.
|
|
dword WAINCR,"WA1+"
|
|
ENTER
|
|
.dword SWORD
|
|
.dword PLUS
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( n1 -- n2 ) n2 = n1 + size of long.
|
|
dword LAINCR,"LA1+"
|
|
ENTER
|
|
.dword SLONG
|
|
.dword PLUS
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( n1 -- n2 ) n2 = n1 + size of cell.
|
|
dword NAINCR,"NA1+"
|
|
jmp CELLPLUS::code
|
|
eword
|
|
|
|
; H: ( n1 -- n2 ) n2 = n1 * size of char.
|
|
dword SCHARMULT,"/C*"
|
|
jmp CHARS::code
|
|
eword
|
|
|
|
; H: ( n1 -- n2 ) n2 = n1 * size of word.
|
|
dword SWORDMULT,"/W*"
|
|
ENTER
|
|
.dword SWORD
|
|
.dword UMULT
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( n1 -- n2 ) n2 = n1 * size of long.
|
|
dword SLONGMULT,"/L*"
|
|
ENTER
|
|
.dword SLONG
|
|
.dword UMULT
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( n1 -- n2 ) n2 = n1 * size of cell.
|
|
dword SCELLMULT,"/N*"
|
|
jmp CELLS::code
|
|
eword
|
|
|
|
; H: ( u -- u1 ... u4 ) u1 ... u4 = bytes of u.
|
|
dword LBSPLIT,"LBSPLIT"
|
|
jsr _1parm
|
|
lda STACKBASE+0,x
|
|
ldy STACKBASE+2,x
|
|
pha
|
|
and #$FF
|
|
sta STACKBASE+0,x
|
|
stz STACKBASE+2,x
|
|
pla
|
|
xba
|
|
and #$FF
|
|
jsr _pusha
|
|
tya
|
|
and #$FF
|
|
jsr _pusha
|
|
tya
|
|
xba
|
|
and #$FF
|
|
tay
|
|
lda #$0000
|
|
PUSHNEXT
|
|
eword
|
|
|
|
; H: ( u -- u1 ... u2 ) u1 ... u2 = words of u.
|
|
dword LWSPLIT,"LWSPLIT"
|
|
jsr _1parm
|
|
ldy STACKBASE+2,x
|
|
stz STACKBASE+2,x
|
|
lda #$0000
|
|
PUSHNEXT
|
|
eword
|
|
|
|
; H: ( u -- u1 .. u2 ) u1 .. u2 = bytes of word u.
|
|
dword WBSPLIT,"WBSPLIT"
|
|
jsr _1parm
|
|
stz STACKBASE+2,x
|
|
lda STACKBASE+0,x
|
|
pha
|
|
and #$FF
|
|
sta STACKBASE+0,x
|
|
pla
|
|
xba
|
|
and #$FF
|
|
tay
|
|
lda #$00
|
|
PUSHNEXT
|
|
eword
|
|
|
|
; H: ( b.l b2 b3 b.h -- q ) Join bytes into quad.
|
|
dword BLJOIN,"BLJOIN"
|
|
jsr _4parm
|
|
lda STACKBASE+12,x
|
|
and #$FF
|
|
sta STACKBASE+12,x
|
|
lda STACKBASE+8,x
|
|
and #$FF
|
|
xba
|
|
ora STACKBASE+12,x
|
|
sta STACKBASE+12,x
|
|
lda STACKBASE+4,x
|
|
and #$FF
|
|
sta STACKBASE+14,x
|
|
lda STACKBASE+0,x
|
|
and #$FF
|
|
xba
|
|
ora STACKBASE+14,x
|
|
sta STACKBASE+14,x
|
|
_3drop: inx
|
|
inx
|
|
inx
|
|
inx
|
|
_2drop: inx
|
|
inx
|
|
inx
|
|
inx
|
|
_1drop: inx
|
|
inx
|
|
inx
|
|
inx
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( b.l b.h -- w ) Join bytes into word.
|
|
dword BWJOIN,"BWJOIN"
|
|
jsr _2parm
|
|
stz STACKBASE+6,x
|
|
lda STACKBASE+4,x
|
|
and #$FF
|
|
sta STACKBASE+4,x
|
|
lda STACKBASE+0,x
|
|
and #$FF
|
|
xba
|
|
ora STACKBASE+4,x
|
|
sta STACKBASE+4,x
|
|
bra BLJOIN::_1drop
|
|
eword
|
|
|
|
; H: ( w.l w.h -- q ) Join words into quad.
|
|
dword WLJOIN,"WLJOIN"
|
|
jsr _2parm
|
|
lda STACKBASE+0,x
|
|
sta STACKBASE+6,x
|
|
bra BLJOIN::_1drop
|
|
eword
|
|
|
|
; H: ( w -- w' ) Flip the byte order of w.
|
|
dword WBFLIP,"WBFLIP"
|
|
jsr _1parm
|
|
lda STACKBASE+0,x
|
|
xba
|
|
sta STACKBASE+0,x
|
|
lda STACKBASE+2,x
|
|
xba
|
|
sta STACKBASE+2,x
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( q -- q' ) Flip the byte order of quad.
|
|
dword LBFLIP,"LBFLIP"
|
|
jsr _1parm
|
|
ldy STACKBASE+0,x
|
|
lda STACKBASE+2,x
|
|
xba
|
|
sta STACKBASE+0,x
|
|
tya
|
|
xba
|
|
sta STACKBASE+2,x
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( q -- q ) Flip the word order of quad.
|
|
dword LWFLIP,"LWFLIP"
|
|
jsr _1parm
|
|
ldy STACKBASE+0,x
|
|
lda STACKBASE+2,x
|
|
sta STACKBASE+0,x
|
|
sty STACKBASE+2,x
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( word -- sign-extended )
|
|
dword WSX,"WSX"
|
|
jsr _1parm
|
|
ldy #$0000
|
|
lda STACKBASE+0,x
|
|
and #$8000
|
|
beq :+
|
|
dey
|
|
: sty STACKBASE+2,x
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( byte -- sign-extended )
|
|
dword BSX,"BSX"
|
|
jsr _1parm
|
|
ldy #$0000
|
|
lda STACKBASE+0,x
|
|
and #$80
|
|
beq :+
|
|
dey
|
|
: sty STACKBASE+2,x
|
|
tya
|
|
and #$FF00
|
|
ora STACKBASE+0,x
|
|
sta STACKBASE+0,x
|
|
NEXT
|
|
eword
|
|
|
|
; ( -- addr ) variable containing address of top of data space
|
|
hword dHIMEM,"$HIMEM"
|
|
SYSVAR SV_HIMEM
|
|
eword
|
|
|
|
; H: ( u -- c-addr ) Allocate memory from heap.
|
|
dword ALLOC,"ALLOC-MEM"
|
|
jsr _popxr ; size into XR
|
|
jsr _alloc
|
|
bcs :+
|
|
ldy #.loword(-59)
|
|
lda #.hiword(-59)
|
|
jmp _throway
|
|
: PUSHNEXT
|
|
eword
|
|
|
|
; H: ( c-addr u -- ) Release memory to heap, u is currently ignored.
|
|
dword FREE,"FREE-MEM"
|
|
jsr _stackincr ; we should really check this (len)
|
|
jsr _popwr
|
|
jsr _free
|
|
bcs :+
|
|
ldy #.loword(-60)
|
|
lda #.hiword(-60)
|
|
jmp _throway
|
|
: NEXT
|
|
eword
|
|
|
|
; H: ( -- ) Display heap and temporary string information.
|
|
dword DBGMEM,"DEBUG-MEM"
|
|
ENTER
|
|
.dword CR
|
|
SLIT "$CSBUF:"
|
|
.dword TYPE
|
|
.dword dCSBUF
|
|
.dword FETCH
|
|
.dword UDOT
|
|
SLIT "$SBUF0:"
|
|
.dword TYPE
|
|
.dword dSBUF0
|
|
.dword FETCH
|
|
.dword UDOT
|
|
SLIT "$SBUF1:"
|
|
.dword TYPE
|
|
.dword dSBUF1
|
|
.dword FETCH
|
|
.dword UDOT
|
|
.dword dHIMEM ; ( -- $himem )
|
|
loop: .dword CR
|
|
.dword FETCH ; ( $himem -- u )
|
|
.dword DUP ; ( u -- u1 u2 )
|
|
.dword dMEMTOP ; ( u1 u2 -- u1 u2 $memtop )
|
|
.dword FETCH ; ( u1 u2 $memtop -- u1 u2 u3 )
|
|
.dword EQUAL ; ( u1 u2 u3 -- u1 f )
|
|
.dword _IFFALSE ; ( u1 f -- u1 )
|
|
.dword eom
|
|
.dword DUP
|
|
ONLIT HDR_SIZE
|
|
.dword PLUS
|
|
.dword UDOT ; output address
|
|
.dword DUP ; ( u1 -- u1 u2 )
|
|
.dword DUP ; ( ... -- u1 u2 u3 )
|
|
.dword FETCH ; ( u1 u2 u3 -- u1 u2 u3' )
|
|
.dword SWAP ; ( u1 u2 u3' -- u1 u3' u2 )
|
|
.dword MINUS ; ( u1 u2 u3' -- u1 u4 )
|
|
ONLIT HDR_SIZE ; ( u1 u4 -- u1 u4 u5 )
|
|
.dword MINUS ; ( u1 u4 u5 -- u1 u6 )
|
|
.dword UDOT ; ( u1 u6 -- u1 ) output size
|
|
.dword DUP
|
|
ONLIT 4
|
|
.dword PLUS
|
|
.dword WFETCH
|
|
ONLIT $8000
|
|
.dword LAND
|
|
.dword _IF
|
|
.dword free
|
|
SLIT "used "
|
|
JUMP :+
|
|
free: SLIT "free "
|
|
: .dword TYPE
|
|
ONLIT '@'
|
|
.dword EMIT
|
|
.dword DUP
|
|
.dword UDOT ; write header address
|
|
ONLIT '>'
|
|
.dword EMIT
|
|
.dword DUP
|
|
.dword FETCH
|
|
.dword UDOT
|
|
JUMP loop
|
|
eom: .dword UDOT
|
|
SLIT "$MEMTOP"
|
|
.dword TYPE
|
|
.dword CR
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( -- addr ) Variable, zero if interpreting, nonzero if compiling.
|
|
dword STATE,"STATE"
|
|
SYSVAR SV_STATE
|
|
eword
|
|
|
|
; ( -- u ) Variable containing depth of control-flow stack.
|
|
hword dCSDEPTH,"$CSDEPTH"
|
|
SYSVAR SV_dCSDEPTH ; Control-flow stack depth for temporary definitions
|
|
eword
|
|
|
|
; ( -- addr ) Variable to store HERE during temporary definition creation.
|
|
hword dSAVEHERE,"$SAVEHERE"
|
|
SYSVAR SV_dSAVEHERE ; saved HERE for temporary definitions
|
|
eword
|
|
|
|
; ( -- addr ) Variable pointing to memory allocated for temporary definition.
|
|
hword dTMPDEF,"$>TMPDEF"
|
|
SYSVAR SV_pTMPDEF ; pointer to memory allocated for temp def
|
|
eword
|
|
|
|
; H: ( -- ) Enter interpretation state.
|
|
dword STATEI,"[",F_IMMED|F_CONLY
|
|
ENTER
|
|
.dword STATE
|
|
.dword OFF
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( -- ) Enter compilation state.
|
|
; immediacy called out in IEEE 1275-1994
|
|
dword STATEC,"]",F_IMMED
|
|
ENTER
|
|
.dword STATE
|
|
.dword ON
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( -- a-addr ) Variable containing current numeric base.
|
|
dword BASE,"BASE"
|
|
SYSVAR SV_BASE
|
|
eword
|
|
|
|
; H: ( ... u -- ... ) Call system interface function u.
|
|
dword dSYSIF,"$SYSIF"
|
|
jsr _popay
|
|
tya
|
|
jsl _call_sysif
|
|
bcc :+
|
|
jmp _throway
|
|
: NEXT
|
|
eword
|
|
|
|
; Raw function needed by line editor
|
|
.proc _emit
|
|
do_emit: lda #SI_EMIT
|
|
jsl _call_sysif
|
|
bcc :+
|
|
jmp _throway
|
|
: rts
|
|
.endproc
|
|
|
|
; H: ( char -- ) Output char.
|
|
dword EMIT,"EMIT"
|
|
jsr _peekay
|
|
tya
|
|
and #$FF
|
|
cmp #' '
|
|
bcc do_emit ; don't count control chars
|
|
ldy #SV_NOUT
|
|
lda [SYSVARS],y ; increment #OUT
|
|
inc a
|
|
sta [SYSVARS],y
|
|
bne do_emit
|
|
iny
|
|
iny
|
|
lda [SYSVARS],y
|
|
inc a
|
|
sta [SYSVARS],y
|
|
do_emit: jsr _emit
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( addr u -- ) Output string.
|
|
dword TYPE,"TYPE"
|
|
jsr _popxr
|
|
jsr _popwr
|
|
ldy #.loword(do_emit-1)
|
|
lda #.hiword(do_emit-1)
|
|
jsr _str_op_ay
|
|
NEXT
|
|
do_emit: jsr _pusha
|
|
ENTER
|
|
.dword EMIT
|
|
CODE
|
|
clc
|
|
rtl
|
|
eword
|
|
|
|
; H: ( -- f ) If #LINE >= 20, prompt user to continue and return false if they want to.
|
|
dword EXITQ,"EXIT?"
|
|
ENTER
|
|
.dword NLINE
|
|
.dword FETCH
|
|
ONLIT 20 ; TODO: replace with variable
|
|
.dword UGTE
|
|
.dword _IF
|
|
.dword nopage
|
|
ONLIT 0
|
|
.dword NLINE
|
|
.dword STORE
|
|
SLIT "more? (Y/n)"
|
|
.dword TYPE
|
|
.dword KEY
|
|
.dword CR
|
|
.dword LCC
|
|
ONLIT 'n'
|
|
.dword EQUAL
|
|
EXIT
|
|
nopage: .dword FALSE
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( -- addr ) Variable containing the number of lines output.
|
|
dword NLINE,"#LINE"
|
|
SYSVAR SV_NLINE
|
|
eword
|
|
|
|
; H: ( -- addr ) Variable containing the number of chars output on the current line.
|
|
dword NOUT,"#OUT"
|
|
SYSVAR SV_NOUT
|
|
eword
|
|
|
|
; H: ( -- addr ) Variable containing offset to the current parsing area of input buffer.
|
|
dword PIN,">IN"
|
|
SYSVAR SV_PIN
|
|
eword
|
|
|
|
; H: ( -- addr ) Variable containing number of chars in the current input buffer.
|
|
dword NIN,"#IN"
|
|
SYSVAR SV_NIN
|
|
eword
|
|
|
|
; H: ( xt -- ) execute xt, regardless of its flags
|
|
dword EXECUTE,"EXECUTE"
|
|
jsr _popay
|
|
RUN
|
|
eword
|
|
|
|
; ( -- ) Read a cell from the instruction stream, set the next IP to it.
|
|
hword _JUMP,"_JUMP"
|
|
jsr _fetch_ip_cell
|
|
jsr _decay
|
|
go: sty IP
|
|
sta IP+2
|
|
NEXT
|
|
eword
|
|
|
|
; ( -- ) Read and discard a cell from the instruction stream.
|
|
hword _SKIP,"_SKIP"
|
|
jsr _fetch_ip_cell
|
|
NEXT
|
|
eword
|
|
|
|
; ( -- ) Read a cell from the instruction stream; if interpretation state set IP to it.
|
|
hword _SMART,"_SMART"
|
|
.if 1 ; native
|
|
ldy #SV_STATE
|
|
lda [SYSVARS],y
|
|
bne _SKIP::code
|
|
iny
|
|
iny
|
|
lda [SYSVARS],y
|
|
bne _SKIP::code
|
|
beq _JUMP::code
|
|
.else ; mixed
|
|
ENTER
|
|
.dword STATE
|
|
.dword FETCH
|
|
CODE
|
|
jsr _popay
|
|
sty WR
|
|
ora WR
|
|
beq _JUMP::code
|
|
bne _SKIP::code
|
|
.endif
|
|
eword
|
|
|
|
; ( -- ) Read and discard two cells from the instruction stream.
|
|
hword _SKIP2,"_SKIP2"
|
|
jsr _fetch_ip_cell
|
|
bra _SKIP::code
|
|
eword
|
|
|
|
; H: ( n -- ) Compile cell n into the dictionary.
|
|
dword COMMA,","
|
|
jsr _popay
|
|
jsr _ccellay
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( xt -- ) Compile xt into the dictionary.
|
|
; immediacy called out in IEEE 1275-1994, but that conflicts with standards and usage
|
|
dword COMPILECOMMA,"COMPILE,"
|
|
bra COMMA::code
|
|
eword
|
|
|
|
; H: ( char -- ) Compile char into dictionary.
|
|
dword CCOMMA,"C,"
|
|
jsr _popay
|
|
tya
|
|
jsr _cbytea
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( word -- ) Compile word into dictionary.
|
|
dword WCOMMA,"W,"
|
|
jsr _popay
|
|
tya
|
|
jsr _cworda
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( q -- ) Compile cell q into dictionary.
|
|
dword LCOMMA,"L,"
|
|
bra COMMA::code
|
|
eword
|
|
|
|
; H: Compilation: ( n -- )
|
|
; H: Execution: ( -- n )
|
|
dword LITERAL,"LITERAL",F_IMMED
|
|
jsr _1parm
|
|
.if no_fast_lits
|
|
ldy #.loword(_LIT)
|
|
lda #.hiword(_LIT)
|
|
jsr _ccellay ; compile _LIT
|
|
bra COMMA::code ; compile actual number
|
|
.else
|
|
lda STACKBASE+2,x
|
|
beq COMMA::code ; compile fast literal
|
|
ldy #.loword(_LIT)
|
|
lda #.hiword(_LIT)
|
|
jsr _ccellay ; compile _LIT
|
|
bra COMMA::code ; compile actual number
|
|
.endif
|
|
eword
|
|
|
|
dword TWOLITERAL,"2LITERAL",F_IMMED
|
|
ENTER
|
|
do2lit: .dword SWAP
|
|
.dword LITERAL
|
|
.dword LITERAL
|
|
EXIT
|
|
eword
|
|
|
|
; do LITERAL or 2LITERAL
|
|
hword XLITERAL,"XLITERAL"
|
|
ENTER
|
|
.dword TWO
|
|
.dword EQUAL
|
|
.dword _IFFALSE
|
|
.dword TWOLITERAL::do2lit ; true branch
|
|
.dword LITERAL
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( u -- u ) Align u (no-op in this implementation).
|
|
dword ALIGN,"ALIGN"
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( n -- ) Allocate n bytes in the dictionary.
|
|
dword ALLOT,"ALLOT"
|
|
jsr _popay
|
|
pha
|
|
tya
|
|
clc
|
|
adc DHERE
|
|
sta DHERE
|
|
pla
|
|
adc DHERE+2
|
|
sta DHERE+2
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( addr -- n ) Fetch n from addr.
|
|
dword FETCH,"@"
|
|
jsr _popwr
|
|
fetch2: jsr _wrfetchind
|
|
PUSHNEXT
|
|
eword
|
|
|
|
; H: ( addr -- n ) Fetch n from addr.
|
|
dword LFETCH,"L@"
|
|
bra FETCH::code
|
|
eword
|
|
|
|
.if unaligned_words
|
|
; H: ( addr -- n ) Fetch n from addr.
|
|
dword LFECTCHU,"UNALIGNED-L@"
|
|
bra LFETCH::code
|
|
eword
|
|
.endif
|
|
|
|
; H: ( addr -- n1 n2 ) Fetch two consecutive cells from addr.
|
|
dword TWOFETCH,"2@"
|
|
jsr _popwr
|
|
jsr _wrplus4
|
|
jsr _wrfetchind
|
|
jsr _pushay
|
|
jsr _wrminus4
|
|
bra FETCH::fetch2
|
|
eword
|
|
|
|
; H: ( addr -- char ) Fetch char from addr.
|
|
dword CFETCH,"C@"
|
|
jsr _popwr
|
|
sep #SHORT_A
|
|
lda [WR]
|
|
rep #SHORT_A
|
|
and #$00FF
|
|
jsr _pusha
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( addr -- word ) Fetch word from addr.
|
|
dword WFETCH,"W@"
|
|
jsr _popwr
|
|
lda [WR]
|
|
jsr _pusha
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( addr -- n ) Fetch sign-extended word from addr.
|
|
dword WFETCHS,"<W@"
|
|
ENTER
|
|
.dword WFETCH
|
|
.dword WSX
|
|
EXIT
|
|
eword
|
|
|
|
.if unaligned_words
|
|
; H: ( addr -- n ) Fetch word from addr.
|
|
dword WFECTCHU,"UNALIGNED-W@"
|
|
bra WFETCH::code
|
|
eword
|
|
.endif
|
|
|
|
; H: ( n addr -- ) Store n at addr.
|
|
dword STORE,"!"
|
|
jsr _popwr
|
|
store2: jsr _popay
|
|
jsr _wrstoreind
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( n addr -- ) Store n at addr.
|
|
dword LSTORE,"L!"
|
|
bra STORE::code
|
|
eword
|
|
|
|
.if unaligned_words
|
|
; H: ( n addr -- ) Store n at addr.
|
|
dword LSTOREU,"UNALIGNED-L!"
|
|
bra LSTORE::code
|
|
eword
|
|
.endif
|
|
|
|
; H: ( n1 n2 addr -- ) Store two consecutive cells at addr.
|
|
dword TWOSTORE,"2!"
|
|
jsr _popwr
|
|
jsr _popay
|
|
jsr _wrstoreind
|
|
jsr _wrplus4
|
|
bra STORE::store2
|
|
eword
|
|
|
|
; H: ( char addr -- ) Store char at addr.
|
|
dword CSTORE,"C!"
|
|
jsr _popwr
|
|
jsr _popay
|
|
tya
|
|
sep #SHORT_A
|
|
sta [WR]
|
|
rep #SHORT_A
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( word addr -- ) Store word at addr.
|
|
dword WSTORE,"W!"
|
|
jsr _popwr
|
|
jsr _popay
|
|
tya
|
|
sta [WR]
|
|
NEXT
|
|
eword
|
|
|
|
.if unaligned_words
|
|
; H: ( word addr -- ) Store word at addr.
|
|
dword WSTOREU,"UNALIGNED-W!"
|
|
bra WSTORE::code
|
|
eword
|
|
.endif
|
|
|
|
; ( n1 addr -- n2 ) Swap n1 with n2 at addr.
|
|
hword CSWAP,"CSWAP"
|
|
ENTER
|
|
.dword DUP
|
|
.dword FETCH
|
|
.dword NROT
|
|
.dword STORE
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( n1 -- n1 n2 ) n2 = n1.
|
|
dword DUP,"DUP"
|
|
jsr _peekay
|
|
PUSHNEXT
|
|
eword
|
|
|
|
; H: ( 0 -- 0 ) | ( n1 -- n1 n2 ) n2 = n1.
|
|
dword QDUP,"?DUP"
|
|
jsr _peekay
|
|
cmp #$00
|
|
bne :+
|
|
cpy #$00
|
|
bne :+
|
|
NEXT
|
|
: PUSHNEXT
|
|
eword
|
|
|
|
; H: ( n -- ) (R: -- n )
|
|
; must be primitive
|
|
dword PtoR,">R"
|
|
jsr _popay
|
|
pha
|
|
phy
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( n1 n2 -- ) (R: -- n1 n2 )
|
|
; must be primitive
|
|
dword TWOPtoR,"2>R"
|
|
jsr _swap
|
|
jsr _popay
|
|
pha
|
|
phy
|
|
bra PtoR::code
|
|
eword
|
|
|
|
; Common code to copy YR items from parameter stack to
|
|
; return stack.
|
|
.proc _xNPtoR
|
|
lda YR
|
|
beq done
|
|
: jsr _popay
|
|
pha
|
|
phy
|
|
dec YR
|
|
bne :-
|
|
done: lda YR+2
|
|
bpl :+
|
|
and #$7FFF
|
|
jsr _pusha
|
|
: NEXT
|
|
.endproc
|
|
|
|
; ( xu ... x1 u -- u ) ( R: -- x1 ... xu ) remove u items from parameter stack
|
|
; and place on return stack, used by SAVE-INPUT.
|
|
hword XNPtoR,"XN>R"
|
|
jsr _popay
|
|
sty YR
|
|
sty YR+2
|
|
lda #$8000
|
|
tsb YR+2
|
|
bra _xNPtoR
|
|
eword
|
|
|
|
; H: ( xu ... x0 u -- ) ( R: -- x0 ... xu ) remove u+1 items from parameter stack
|
|
; H: and place on return stack.
|
|
dword NPtoR,"N>R"
|
|
jsr _popay
|
|
iny
|
|
sty YR
|
|
stz YR+2
|
|
bra _xNPtoR
|
|
eword
|
|
|
|
; H: ( R: x -- ) ( -- x )
|
|
; must be primitive
|
|
dword RtoP,"R>"
|
|
ply
|
|
pla
|
|
PUSHNEXT
|
|
eword
|
|
|
|
; H: ( R: x1 x2 -- ) ( -- x1 x2 )
|
|
; must be primitive
|
|
dword TWORtoP,"2R>"
|
|
ply
|
|
pla
|
|
jsr _pushay
|
|
ply
|
|
pla
|
|
jsr _pushay
|
|
jsr _swap
|
|
NEXT
|
|
eword
|
|
|
|
; Common code to copy YR items from return stack to
|
|
; parameter stack.
|
|
.proc _xNRtoP
|
|
lda YR
|
|
beq done
|
|
: ply
|
|
pla
|
|
jsr _pushay
|
|
dec YR
|
|
bne :-
|
|
done: lda YR+2
|
|
bpl :+
|
|
and #$7FFF
|
|
jsr _pusha
|
|
: NEXT
|
|
.endproc
|
|
|
|
; ( R: x1 ... xu -- ) ( u -- xu ... x1 u ) remove u items from return stack
|
|
; and place on parameter stack, used by RESTORE-INPUT.
|
|
hword XNRtoP,"XNR>"
|
|
jsr _popay
|
|
sty YR
|
|
sty YR+2
|
|
lda #$8000
|
|
tsb YR+2
|
|
bra _xNRtoP
|
|
eword
|
|
|
|
; H: ( R: x0 ... xu -- ) ( u -- xu ... x0 ) remove u+1 items from return stack
|
|
; H: and place on parameter stack.
|
|
dword NRtoP,"NR>"
|
|
jsr _popay
|
|
iny
|
|
sty YR
|
|
stz YR+2
|
|
bra _xNRtoP
|
|
eword
|
|
|
|
; H: ( R: n -- n ) ( -- n )
|
|
dword RCOPY,"R@"
|
|
lda 1,S
|
|
tay
|
|
lda 3,S
|
|
PUSHNEXT
|
|
eword
|
|
|
|
; H: ( n -- n ) ( R: -- n )
|
|
dword COPYR,">R@"
|
|
jsr _peekay
|
|
pha
|
|
phy
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( R: n1 n2 -- n1 n2 ) ( -- n1 n2 )
|
|
dword TWORCOPY,"2R@"
|
|
lda 5,S
|
|
tay
|
|
lda 7,S
|
|
jsr _pushay
|
|
bra RCOPY::code
|
|
eword
|
|
|
|
; H: ( R: n -- )
|
|
dword RDROP,"RDROP"
|
|
pla
|
|
pla
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( R: n1 -- n2 ) n2 = n1 + 1
|
|
; increment item on return stack
|
|
dword RINCR,"R+1"
|
|
lda 1,s
|
|
inc a
|
|
sta 1,s
|
|
bne :+
|
|
lda 3,s
|
|
inc a
|
|
sta 3,s
|
|
: NEXT
|
|
eword
|
|
|
|
.if 0 ; currently unused
|
|
; H: ( u -- xu ) (R: xu ... x0 -- xu ... x0 )
|
|
hword RPICK,"RPICK"
|
|
jsr _popwr
|
|
tya
|
|
asl
|
|
asl
|
|
sta WR
|
|
tsc
|
|
sec ; +1
|
|
adc WR
|
|
sta WR
|
|
stz WR+2
|
|
ldy #$02
|
|
lda [WR],y
|
|
pha
|
|
lda [WR]
|
|
tay
|
|
pla
|
|
NEXT
|
|
eword
|
|
.endif
|
|
|
|
; ( -- n ) n = cell-extended 24-bit address
|
|
; pluck the machine return address underneath the Forth return address
|
|
; on the return stack and place it on the data stack. Used by DOES>
|
|
hword RPLUCKADDR,"RPLUCKADDR"
|
|
ply ; save top of stack address
|
|
sty WR
|
|
pla
|
|
sta WR+2
|
|
sep #SHORT_A
|
|
.a8
|
|
ply ; pull desired address
|
|
pla
|
|
rep #SHORT_A
|
|
.a16
|
|
and #$00FF
|
|
jsr _pushay
|
|
lda WR+2 ; put back top of stack
|
|
pha
|
|
ldy WR
|
|
phy
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( x1 x2 -- x2 x1 )
|
|
dword SWAP,"SWAP"
|
|
jsr _swap
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( x -- )
|
|
dword DROP,"DROP"
|
|
jsr _stackincr
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( x1 x2 x3 -- )
|
|
dword THREEDROP,"3DROP"
|
|
jsr _stackincr
|
|
twodrop: jsr _stackincr
|
|
jsr _stackincr
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( x1 x2 -- )
|
|
dword TWODROP,"2DROP"
|
|
bra THREEDROP::twodrop
|
|
eword
|
|
|
|
; H: ( ... -- ) Empty stack.
|
|
dword CLEAR,"CLEAR"
|
|
ldx STK_TOP
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( xu ... x1 -- xu ... x1 u )
|
|
dword DEPTH,"DEPTH"
|
|
stx WR
|
|
lda STK_TOP
|
|
sec
|
|
sbc WR
|
|
lsr
|
|
lsr
|
|
tay
|
|
lda #$0000
|
|
PUSHNEXT
|
|
eword
|
|
|
|
; H: ( x1 x2 -- x1 x2 x2 )
|
|
dword OVER,"OVER"
|
|
jsr _over
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( xu ... x1 x0 u -- xu ... x1 xu )
|
|
dword PICK,"PICK"
|
|
jsr _2parm
|
|
lda STACKBASE+0,x
|
|
asl
|
|
asl
|
|
sta WR
|
|
txa
|
|
clc
|
|
adc WR
|
|
phx
|
|
tax
|
|
ldy STACKBASE+4,x
|
|
lda STACKBASE+6,x
|
|
plx
|
|
sty STACKBASE+0,x
|
|
sta STACKBASE+2,x
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( -- ) Display stack contents.
|
|
dword DOTS,".S"
|
|
ENTER
|
|
ONLIT '{'
|
|
.dword EMIT
|
|
.dword SPACE
|
|
.dword DEPTH
|
|
.dword DUP
|
|
.dword DOT
|
|
ONLIT ':'
|
|
.dword EMIT
|
|
.dword SPACE
|
|
.dword DUP
|
|
.dword _IF
|
|
.dword done
|
|
lp: .dword DECR
|
|
.dword DUP
|
|
.dword PtoR
|
|
.dword PICK
|
|
.dword DOT
|
|
.dword RtoP
|
|
.dword DUP
|
|
.dword _IFFALSE
|
|
.dword lp
|
|
done: .dword DROP
|
|
ONLIT '}'
|
|
.dword EMIT
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( x1 x2 -- x2 )
|
|
dword NIP,"NIP"
|
|
jsr _2parm
|
|
lda STACKBASE+0,x
|
|
sta STACKBASE+4,x
|
|
lda STACKBASE+2,x
|
|
sta STACKBASE+6,x
|
|
inx
|
|
inx
|
|
inx
|
|
inx
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( x1 x2 -- x2 x1 x2 )
|
|
dword TUCK,"TUCK"
|
|
ENTER
|
|
.dword SWAP
|
|
.dword OVER
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( x1 x2 x3 -- x3 )
|
|
hword NIPTWO,"NIP2"
|
|
ENTER
|
|
.dword PtoR
|
|
.dword TWODROP
|
|
.dword RtoP
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( x1 x2 -- x1 x2 x1 x2 )
|
|
dword TWODUP,"2DUP"
|
|
jsr _over
|
|
jsr _over
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( x1 x2 x3 -- x1 x2 x3 x1 x2 x3 )
|
|
dword THREEDUP,"3DUP"
|
|
ENTER
|
|
ONLIT 2
|
|
.dword PICK
|
|
ONLIT 2
|
|
.dword PICK
|
|
ONLIT 2
|
|
.dword PICK
|
|
EXIT
|
|
eword
|
|
|
|
.proc _rot
|
|
ldy STACKBASE+10,x
|
|
lda STACKBASE+6,x
|
|
sta STACKBASE+10,x
|
|
lda STACKBASE+2,x
|
|
sta STACKBASE+6,x
|
|
sty STACKBASE+2,x
|
|
ldy STACKBASE+8,x
|
|
lda STACKBASE+4,x
|
|
sta STACKBASE+8,x
|
|
lda STACKBASE+0,x
|
|
sta STACKBASE+4,x
|
|
sty STACKBASE+0,x
|
|
rts
|
|
.endproc
|
|
|
|
; H: ( x1 x2 x3 -- x2 x3 x1 )
|
|
dword ROT,"ROT"
|
|
.if 1 ; native
|
|
jsr _3parm
|
|
jsr _rot
|
|
NEXT
|
|
.else ; secondary
|
|
ENTER
|
|
.dword PtoR
|
|
.dword SWAP
|
|
.dword RtoP
|
|
.dword SWAP
|
|
EXIT
|
|
.endif
|
|
eword
|
|
|
|
; H: ( x1 x2 x3 -- x3 x1 x2 )
|
|
dword NROT,"-ROT"
|
|
.if 1 ; native
|
|
jsr _3parm
|
|
ldy STACKBASE+2,x
|
|
lda STACKBASE+6,x
|
|
sta STACKBASE+2,x
|
|
lda STACKBASE+10,x
|
|
sta STACKBASE+6,x
|
|
sty STACKBASE+10,x
|
|
ldy STACKBASE+0,x
|
|
lda STACKBASE+4,x
|
|
sta STACKBASE+0,x
|
|
lda STACKBASE+8,x
|
|
sta STACKBASE+4,x
|
|
sty STACKBASE+8,x
|
|
NEXT
|
|
.else ; secondary
|
|
ENTER
|
|
.dword ROT
|
|
.dword ROT
|
|
EXIT
|
|
.endif
|
|
eword
|
|
|
|
; H: ( xu ... x0 u -- xu-1 .. x0 xu )
|
|
dword ROLL,"ROLL"
|
|
jsr _popxr ; put roll depth into XR
|
|
lda XR ; number of items - 1 that we are moving
|
|
beq done ; if none, GTFO
|
|
asl ; to see if enough room on stack
|
|
asl
|
|
sta XR+2 ; number of cells we are moving
|
|
txa
|
|
adc XR+2
|
|
cmp STK_TOP
|
|
bcc :+
|
|
jmp _stku_err
|
|
: stx WR ; save SP
|
|
tax ; change SP to xu
|
|
lda STACKBASE+2,x ; save xu
|
|
pha
|
|
lda STACKBASE+0,x
|
|
pha
|
|
lp: dex ; move to next-toward-top entry
|
|
dex
|
|
dex
|
|
dex
|
|
lda STACKBASE+2,x ; copy this entry to the one below
|
|
sta STACKBASE+6,x
|
|
lda STACKBASE+0,x
|
|
sta STACKBASE+4,x
|
|
cpx WR ; are we done?
|
|
beq :+
|
|
bcs lp
|
|
: pla ; finally put xu on top
|
|
sta STACKBASE+0,x
|
|
pla
|
|
sta STACKBASE+2,x
|
|
done: NEXT
|
|
eword
|
|
|
|
; H: ( x1 x2 x3 x4 -- x3 x4 x1 x2 )
|
|
dword TWOSWAP,"2SWAP"
|
|
ENTER
|
|
.dword PtoR
|
|
.dword NROT
|
|
.dword RtoP
|
|
.dword NROT
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( x1 x2 x3 x4 -- x1 x2 x3 x4 x1 x2 )
|
|
dword TWOOVER,"2OVER"
|
|
ENTER
|
|
.dword TWOPtoR
|
|
.dword TWODUP
|
|
.dword TWORtoP
|
|
.dword TWOSWAP
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( x1 x2 x3 x4 x5 x6 -- x3 x4 x5 x6 x1 x2 )
|
|
dword TWOROT,"2ROT"
|
|
ENTER
|
|
.dword TWOPtoR
|
|
.dword TWOSWAP
|
|
.dword TWORtoP
|
|
.dword TWOSWAP
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( addr -- ) Store all zero bits in cell at addr.
|
|
dword OFF,"OFF"
|
|
jsr _popwr
|
|
lda #$0000
|
|
onoff: tay
|
|
jsr _wrstoreind
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( addr -- ) Store all one bits to cell at addr.
|
|
dword ON,"ON"
|
|
jsr _popwr
|
|
lda #$FFFF
|
|
bra OFF::onoff
|
|
eword
|
|
|
|
; H: ( -- false ) false = all zero bits
|
|
dword FALSE,"FALSE"
|
|
lda #$0000
|
|
tay
|
|
PUSHNEXT
|
|
eword
|
|
|
|
; H: ( -- true ) true = all one bits
|
|
dword TRUE,"TRUE"
|
|
lda #$FFFF
|
|
tay
|
|
PUSHNEXT
|
|
eword
|
|
|
|
; small assembly routine common to zero comparisons
|
|
.proc _zcmpcom
|
|
jsr _1parm
|
|
ldy #$0000
|
|
lda STACKBASE+2,x
|
|
rts
|
|
.endproc
|
|
|
|
; H: ( x -- f ) f = true if x is zero, false if not.
|
|
dword ZEROQ,"0="
|
|
jsr _zcmpcom
|
|
ora STACKBASE+0,x
|
|
bne :+
|
|
dey
|
|
st: jmp _cmpstore
|
|
eword
|
|
_cmpstore2 = ZEROQ::st
|
|
|
|
; H: ( x -- f ) f = false if x is zero, true if not.
|
|
dword ZERONEQ,"0<>"
|
|
jsr _zcmpcom
|
|
ora STACKBASE+0,x
|
|
beq _cmpstore2
|
|
dey
|
|
: bra _cmpstore2
|
|
eword
|
|
|
|
; H: ( n -- f ) f = true if n > 0, false if not.
|
|
dword ZEROGT,"0>"
|
|
jsr _zcmpcom
|
|
bmi _cmpstore2
|
|
ora STACKBASE+0,x
|
|
beq _cmpstore2
|
|
dey
|
|
bra _cmpstore2
|
|
eword
|
|
|
|
; H: ( n -- f ) f = true if n >= 0, false if not.
|
|
dword ZEROGTE,"0>="
|
|
jsr _zcmpcom
|
|
bmi _cmpstore2
|
|
dey
|
|
bra _cmpstore2
|
|
eword
|
|
|
|
; H: ( n -- f ) f = true if n < 0, false if not.
|
|
dword ZEROLT,"0<"
|
|
jsr _zcmpcom
|
|
bpl _cmpstore
|
|
dey
|
|
bra _cmpstore
|
|
eword
|
|
|
|
; H: ( n -- f ) f = true if n <= 0, false if not.
|
|
dword ZEROLTE,"0<="
|
|
jsr _zcmpcom
|
|
bmi :+
|
|
ora STACKBASE+0,x
|
|
bne _cmpstore
|
|
: dey
|
|
bra _cmpstore
|
|
eword
|
|
|
|
; H: ( x1 x2 -- f ) f = true if x1 = x2, false if not.
|
|
dword EQUAL,"="
|
|
jsr _ucmpcom
|
|
bne _2cmpstore
|
|
dey
|
|
bra _2cmpstore
|
|
eword
|
|
|
|
; H: ( x1 x2 -- f ) f = true if x1 <> x2, false if not.
|
|
dword NOTEQUAL,"<>"
|
|
jsr _ucmpcom
|
|
beq _2cmpstore
|
|
dey
|
|
bra _2cmpstore
|
|
eword
|
|
|
|
; H: ( u1 u2 -- f ) f = true if u1 < u2, false if not.
|
|
dword ULT,"U<"
|
|
jsr _ucmpcom
|
|
bcs _2cmpstore
|
|
dey
|
|
bra _2cmpstore
|
|
eword
|
|
|
|
; H: ( u1 u2 -- f ) f = true if u1 <= u2, false if not.
|
|
dword ULTE,"U<="
|
|
jsr _ucmpcom
|
|
beq :+
|
|
bcs _2cmpstore
|
|
: dey
|
|
bra _2cmpstore
|
|
eword
|
|
|
|
; more comparison helper routines
|
|
.proc _2cmpstore
|
|
inx
|
|
inx
|
|
inx
|
|
inx
|
|
; fall-through
|
|
.endproc
|
|
|
|
.proc _cmpstore
|
|
sty STACKBASE+0,x
|
|
sty STACKBASE+2,x
|
|
NEXT
|
|
.endproc
|
|
|
|
; H: ( u1 u2 -- f ) f = true if u1 > u2, false if not.
|
|
dword UGT,"U>"
|
|
jsr _ucmpcom
|
|
beq _2cmpstore
|
|
bcc _2cmpstore
|
|
dey
|
|
bra _2cmpstore
|
|
eword
|
|
|
|
; H: ( u1 u2 -- f ) f = true if u1 >= u2, false if not.
|
|
dword UGTE,"U>="
|
|
jsr _ucmpcom
|
|
bcc _2cmpstore
|
|
dey
|
|
bra _2cmpstore
|
|
eword
|
|
|
|
; H: ( n1 n2 -- f ) f = true if n1 < n2, false if not.
|
|
dword SLT,"<"
|
|
jsr _scmpcom
|
|
bcs _2cmpstore
|
|
dey
|
|
bra _2cmpstore
|
|
eword
|
|
|
|
; H: ( n1 n2 -- f ) f = true if n1 <= n2, false if not.
|
|
dword SLTE,"<="
|
|
jsr _scmpcom
|
|
beq :+
|
|
bcs _2cmpstore
|
|
: dey
|
|
bra _2cmpstore
|
|
eword
|
|
|
|
; H: ( n1 n2 -- f ) f = true if n1 > n2, false if not.
|
|
dword SGT,">"
|
|
jsr _scmpcom
|
|
beq _2cmpstore
|
|
bcc _2cmpstore
|
|
dey
|
|
bra _2cmpstore
|
|
eword
|
|
|
|
; H: ( n1 n2 -- f ) f = true if n1 >= n2, false if not.
|
|
dword SGTE,">="
|
|
jsr _scmpcom
|
|
beq :+
|
|
bcc _2cmpstore
|
|
: dey
|
|
bra _2cmpstore
|
|
eword
|
|
|
|
; H: ( n1 n2 -- n1|n2 ) Return the greater of n1 or n2.
|
|
dword MAX,"MAX"
|
|
jsr _scmpcom
|
|
bcs drop
|
|
swap: jsr _swap
|
|
drop: inx
|
|
inx
|
|
inx
|
|
inx
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( n1 n2 -- n1|n2 ) Return the smaller of n1 or n2.
|
|
dword MIN,"MIN"
|
|
jsr _scmpcom
|
|
bcc MAX::drop
|
|
bra MAX::swap
|
|
eword
|
|
|
|
; common routine for unsigned comparisons
|
|
.proc _ucmpcom
|
|
jsr _2parm
|
|
ldy #$0000
|
|
lda STACKBASE+6,x
|
|
cmp STACKBASE+2,x
|
|
bne :+
|
|
lda STACKBASE+4,x
|
|
cmp STACKBASE+0,x
|
|
: rts
|
|
.endproc
|
|
|
|
; common routine for signed comparisons
|
|
.proc _scmpcom
|
|
jsr _2parm
|
|
ldy #$0000
|
|
jmp _stest32
|
|
.endproc
|
|
|
|
; ( addr -- ) Set dictionary pointer to addr.
|
|
hword toHERE,"->HERE"
|
|
jsr _popay
|
|
sty DHERE
|
|
sta DHERE+2
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( -- addr ) Return dictionary pointer.
|
|
dword HERE,"HERE"
|
|
ldy DHERE
|
|
lda DHERE+2
|
|
PUSHNEXT
|
|
eword
|
|
|
|
; H: ( -- addr ) Return address of last definition in current vocabulary.
|
|
; non-standard
|
|
dword LAST,"LAST"
|
|
ENTER
|
|
.dword GET_CURRENT
|
|
.dword FETCH
|
|
EXIT
|
|
eword
|
|
|
|
hword dCURDEF,"$CURDEF"
|
|
SYSVAR SV_dCURDEF
|
|
eword
|
|
|
|
; ( -- c-addr ) return address of $OLDHERE system variable
|
|
hword dOLDHERE,"$OLDHERE"
|
|
SYSVAR SV_OLDHERE
|
|
eword
|
|
|
|
; ( -- c-addr ) return HERE address prior to starting current definition
|
|
; used by PATCH to forget partial definiton when uncaught exception occurs
|
|
hword OLDHERE,"OLDHERE"
|
|
ENTER
|
|
.dword dOLDHERE
|
|
.dword FETCH
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( -- ) Exit this word, to the caller.
|
|
dword DEXIT,"EXIT",F_CONLY
|
|
jmp _exit_next
|
|
eword
|
|
|
|
; ( n -- ) read cell from instruction stream, discard if n is true, set IP if false
|
|
; word compiled by IF
|
|
hword _IF,"_IF"
|
|
jsr _popay
|
|
ora #$0000
|
|
bne :+
|
|
tya
|
|
bne :+
|
|
jmp _JUMP::code
|
|
: jmp _SKIP::code
|
|
eword
|
|
|
|
; ( n -- ) read cell from instruction stream, discard if n is false, set IP if true
|
|
hword _IFFALSE,"_IFFALSE"
|
|
jsr _popay
|
|
ora #$0000
|
|
bne :+
|
|
tya
|
|
bne :+
|
|
jmp _SKIP::code
|
|
: jmp _JUMP::code
|
|
eword
|
|
|
|
; ( x1 x2 -- x1 ) read cell from instruction stream, discard if x1 = x2, set IP if false
|
|
; saves some space in hand-coded routines that need CASE-like construction such as
|
|
; _MESSAGE
|
|
hword _IFEQUAL,"_IFEQUAL"
|
|
jsr _popay
|
|
cmp STACKBASE+2,x
|
|
bne :+
|
|
tya
|
|
cmp STACKBASE+0,x
|
|
bne :+
|
|
jmp _SKIP::code
|
|
: jmp _JUMP::code
|
|
eword
|
|
|
|
; ( -- ) throw exception -22, control structure mismatch
|
|
; used for unresolved forward references
|
|
hword _CONTROL_MM,"_CONTROL_MM"
|
|
ldy #.loword(-22)
|
|
lda #.hiword(-22)
|
|
jmp _throway
|
|
eword
|
|
|
|
; H: Compilation: ( -- orig )
|
|
; H: Execution: ( -- ) Jump ahead as to the resolution of orig.
|
|
dword AHEAD,"AHEAD",F_IMMED|F_CONLY|F_TEMPD
|
|
ENTER
|
|
.dword _COMP_LIT
|
|
.dword _JUMP
|
|
.dword HERE
|
|
.dword _COMP_LIT
|
|
.dword _CONTROL_MM
|
|
EXIT
|
|
eword
|
|
|
|
; H: Compilation: ( -- if-sys )
|
|
; H: Execution: ( n -- ) Begin IF ... ELSE ... ENDIF.
|
|
dword IF,"IF",F_IMMED|F_CONLY|F_TEMPD
|
|
ENTER
|
|
.dword _COMP_LIT
|
|
.dword _IF ; compile _IF
|
|
.dword HERE ; save to resolve later
|
|
.dword _COMP_LIT
|
|
.dword _CONTROL_MM ; compile unresolved
|
|
EXIT
|
|
eword
|
|
|
|
; H: Compilation: ( if-sys -- else-sys )
|
|
; H: Execution: ( -- ) ELSE clause of IF ... ELSE ... THEN.
|
|
dword ELSE,"ELSE",F_IMMED|F_CONLY
|
|
ENTER
|
|
.dword _COMP_LIT
|
|
.dword _JUMP
|
|
.dword HERE ; to be resolved later
|
|
.dword _COMP_LIT
|
|
.dword _CONTROL_MM
|
|
.dword SWAP ; put IF's unresolved address in place
|
|
.dword HERE ; IF's false branch goes here
|
|
.dword SWAP
|
|
.dword STORE ; resolve IF
|
|
EXIT
|
|
eword
|
|
|
|
; H: Compilation: ( if-sys|else-sys -- )
|
|
; H: Execution: ( -- ) Conclustion of IF ... ELSE ... THEN.
|
|
dword THEN,"THEN",F_IMMED|F_CONLY
|
|
ENTER
|
|
.dword HERE ; IF or ELSE branch goes here
|
|
.dword SWAP
|
|
.dword STORE ; resolve IF or ELSE
|
|
.dword dTEMPSEMIQ ; see if we need to end a temporary def
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( n1 n2 -- n1+n2 n1 )
|
|
dword BOUNDS,"BOUNDS"
|
|
jsr _swap
|
|
lda STACKBASE+0,x
|
|
clc
|
|
adc STACKBASE+4,x
|
|
sta STACKBASE+4,x
|
|
lda STACKBASE+2,x
|
|
adc STACKBASE+6,x
|
|
sta STACKBASE+6,x
|
|
NEXT
|
|
eword
|
|
|
|
; H: Compilation: ( -- dest )
|
|
; H: Execution: ( -- ) start a BEGIN loop
|
|
; BEGIN is basically an immediate HERE
|
|
dword BEGIN,"BEGIN",F_IMMED|F_CONLY|F_TEMPD
|
|
jmp HERE::code ; dest on stack
|
|
eword
|
|
|
|
; H: Compilation: ( dest -- orig dest )
|
|
; H: Execution: ( x -- ) WHILE clause of BEGIN...WHILE...REPEAT loop
|
|
dword WHILE,"WHILE",F_IMMED|F_CONLY
|
|
ENTER
|
|
.dword _COMP_LIT
|
|
.dword _IF
|
|
.dword HERE ; ( dest -- dest orig )
|
|
.dword SWAP ; ( dest orig -- orig dest )
|
|
.dword _COMP_LIT
|
|
.dword _CONTROL_MM
|
|
EXIT
|
|
eword
|
|
|
|
; H: Compilation: ( dest -- )
|
|
; H: Execution: ( x -- ) UNTIL clause of BEGIN...UNTIL loop
|
|
dword UNTIL,"UNTIL",F_IMMED|F_CONLY
|
|
ENTER
|
|
.dword _COMP_LIT
|
|
.dword _IF
|
|
.dword COMMA
|
|
.dword dTEMPSEMIQ ; see if we need to end a temporary def
|
|
EXIT
|
|
eword
|
|
|
|
; H: Compilation: ( orig dest -- ) Resolve orig and dest.
|
|
; H: Execution: ( -- ) Repeat BEGIN loop.
|
|
dword REPEAT,"REPEAT",F_IMMED|F_CONLY
|
|
ENTER
|
|
.dword _COMP_LIT
|
|
.dword _JUMP
|
|
.dword COMMA
|
|
.dword HERE
|
|
.dword SWAP
|
|
.dword STORE
|
|
.dword dTEMPSEMIQ ; see if we need to end a temporary def
|
|
EXIT
|
|
eword
|
|
|
|
; H: Compilation: ( dest -- ) Resolve dest.
|
|
; H: Execution: ( -- ) Jump to BEGIN.
|
|
dword AGAIN,"AGAIN",F_IMMED|F_CONLY
|
|
ENTER
|
|
.dword _COMP_LIT
|
|
.dword _JUMP
|
|
.dword COMMA
|
|
.dword dTEMPSEMIQ ; see if we need to end a temporary def
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( n1|u1 n2|u2 n3|u3 -- f ) f = true if n2|u2 <= n1|u1 < n3|u3, false otherwise
|
|
dword WITHIN,"WITHIN"
|
|
ENTER
|
|
.dword OVER ; ( n1 n2 n3 -- n1 n2 n3 n2' )
|
|
.dword MINUS ; ( n1 n2 n3 n2' -- n1 n2 n4 )
|
|
.dword PtoR ; ( n1 n2 n4 -- n1 n2 ) ( R: -- n4 )
|
|
.dword MINUS ; ( n1 n2 -- n5 )
|
|
.dword RtoP ; ( n5 -- n5 n4 )
|
|
.dword ULT ; ( n5 n4 -- f )
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( n1 n2 n3 -- f ) f = true if n2<=n1<=n3, false otherwise
|
|
; this implementation fails when N3 is max-int and should be
|
|
; replaced with something better at some point
|
|
dword BETWEEN,"BETWEEN"
|
|
ENTER
|
|
.dword INCR
|
|
.dword WITHIN
|
|
EXIT
|
|
eword
|
|
|
|
; ( limit start -- ) ( R: -- loop-sys )
|
|
; Run-time semantics for DO
|
|
; loop-sys = ( -- leave-IP index limit )
|
|
hword _DO,"_DO"
|
|
jsr _2parm
|
|
lda IP+2 ; put IP on stack for LEAVE target
|
|
pha
|
|
lda IP
|
|
pha
|
|
jsr _popay ; index
|
|
pha
|
|
phy
|
|
jsr _popay ; limit
|
|
pha
|
|
phy
|
|
jmp _SKIP2::code ; skip LEAVE target (usually a _JUMP)
|
|
eword
|
|
|
|
; ( limit start -- ) ( R: -- loop-sys )
|
|
; Run-time semantics for ?DO
|
|
hword _QDO,"_QDO"
|
|
jsr _2parm
|
|
lda IP+2 ; put IP on stack for LEAVE target
|
|
pha
|
|
lda IP
|
|
pha
|
|
jsr _popay ; index
|
|
pha
|
|
phy
|
|
jsr _popay ; limit
|
|
pha
|
|
phy
|
|
lda 1,s
|
|
cmp 5,s
|
|
bne doloop
|
|
lda 3,s
|
|
cmp 7,s
|
|
bne doloop
|
|
NEXT ; leave immediately
|
|
doloop: jmp _SKIP2::code ; enter loop
|
|
eword
|
|
|
|
; H: Compilation: ( -- do-sys )
|
|
; H: Execution: ( limit start -- ) Start DO loop.
|
|
dword DO,"DO",F_IMMED|F_CONLY|F_TEMPD
|
|
ENTER
|
|
.dword _COMP_LIT
|
|
.dword _DO ; compile execution semantics
|
|
qdo: .dword HERE ; do-sys
|
|
.dword _COMP_LIT
|
|
.dword _JUMP ; LEAVE resumes execution here
|
|
.dword _COMP_LIT
|
|
.dword _CONTROL_MM ; LOOP/+LOOP will jump to do-sys+4, after this cell
|
|
EXIT
|
|
eword
|
|
|
|
; H: Compilation: ( -- do-sys )
|
|
; H: Execution: ( limit start -- ) Start DO loop, skip if limit=start.
|
|
dword QDO,"?DO",F_IMMED|F_CONLY|F_TEMPD
|
|
ENTER
|
|
.dword _COMP_LIT
|
|
.dword _QDO
|
|
JUMP DO::qdo
|
|
eword
|
|
|
|
; H: ( -- ) ( R: loop-sys -- ) Remove loop parameters from return stack.
|
|
dword UNLOOP,"UNLOOP",F_CONLY
|
|
pla ; drop limit
|
|
pla
|
|
pla ; drop index
|
|
pla
|
|
pla ; drop leave-IP
|
|
pla
|
|
NEXT
|
|
eword
|
|
|
|
; run-time semantics for +LOOP
|
|
; With ( i -- ) and ( R: index(5,7) limit(1,3) -- index' limit )
|
|
; if new index in termination range, exit va _SKIP, otherwise via _JUMP
|
|
; stack-relative addressing is very helpful here
|
|
; WR will contain the limit, XR will contain the limit plus the loop increment
|
|
; We then see if the loop index is between them and if so we terminate the loop
|
|
hword _PLOOP,"_+LOOP"
|
|
jsr _1parm
|
|
lda 5,s ; Compute new index low byte
|
|
clc
|
|
adc STACKBASE+0,x ; increment low byte
|
|
sta 5,s ; write it back
|
|
lda 7,s ; new index high byte
|
|
adc STACKBASE+2,x ; increment high byte
|
|
sta 7,s ; write it back
|
|
jsr _stackdecr ; make some room on stack
|
|
jsr _stackdecr
|
|
lda 1,s ; compute termination bounds
|
|
sta STACKBASE+4,x
|
|
clc
|
|
adc STACKBASE+8,x
|
|
sta STACKBASE+0,x
|
|
lda 3,s
|
|
sta STACKBASE+6,x
|
|
adc STACKBASE+10,x
|
|
sta STACKBASE+2,x
|
|
lda 5,s ; finally, write new index into third stack entry
|
|
sta STACKBASE+8,x
|
|
lda 7,s
|
|
sta STACKBASE+10,x
|
|
ENTER
|
|
.dword TWODUP
|
|
.dword MAX
|
|
.dword PtoR
|
|
.dword MIN
|
|
.dword RtoP
|
|
.dword WITHIN
|
|
CODE
|
|
lda STACKBASE+0,x
|
|
ora STACKBASE+2,x
|
|
php
|
|
inx
|
|
inx
|
|
inx
|
|
inx
|
|
plp
|
|
beq :+
|
|
jmp _SKIP::code
|
|
: jmp _JUMP::code
|
|
eword
|
|
|
|
; H: Compilation: ( do-sys -- )
|
|
; H: Execution: ( u|n -- ) Add u|n to loop index and continue loop if within bounds.
|
|
dword PLOOP,"+LOOP",F_IMMED|F_CONLY
|
|
ENTER
|
|
.dword _COMP_LIT ; compile execution semantics
|
|
.dword _PLOOP
|
|
.dword DUP ; ( loop-sys -- loop-sys loop-sys' )
|
|
ONLIT 8 ; two cells
|
|
.dword PLUS ; ( loop-sys loop-sys' -- loop-sys loop-sys'' ) get target of loop jump
|
|
.dword COMMA ; ( loop-sys loop-sys'' -- loop-sys ) and compile as target of _PLOOP
|
|
.dword HERE ; ( loop-sys -- loop-sys t )
|
|
.dword SWAP ; ( loop-sys t -- t loop-sys )
|
|
.dword _COMP_LIT ; compile in an UNLOOP
|
|
.dword UNLOOP
|
|
ONLIT 4 ; one cell
|
|
.dword PLUS ; get address to resolve
|
|
.dword STORE ; and resolve all the leaves
|
|
.dword dTEMPSEMIQ ; see if we need to end a temporary def
|
|
EXIT
|
|
eword
|
|
|
|
; H: Compilation: ( do-sys -- )
|
|
; H: Execution: ( -- ) Add 1 to loop index and continue loop if within bounds.
|
|
dword LOOP,"LOOP",F_IMMED|F_CONLY
|
|
ENTER
|
|
.dword _COMP_LIT
|
|
.dword ONE
|
|
.dword PLOOP
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( -- ) Exit DO loop.
|
|
dword LEAVE,"LEAVE",F_CONLY
|
|
lda 9,s
|
|
tay
|
|
lda 11,s
|
|
jmp _JUMP::go
|
|
eword
|
|
|
|
; H: ( f -- ) Exit do loop if f is nonzero.
|
|
dword QLEAVE,"?LEAVE",F_CONLY
|
|
jsr _popay
|
|
ora #$0000
|
|
bne LEAVE::code
|
|
tya
|
|
bne LEAVE::code
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( -- n ) Copy inner loop index to stack.
|
|
dword IX,"I",F_CONLY
|
|
lda 5,s
|
|
tay
|
|
lda 7,s
|
|
PUSHNEXT
|
|
eword
|
|
|
|
; H: ( -- n ) Copy second-inner loop index to stack.
|
|
dword JX,"J",F_CONLY
|
|
lda 17,s
|
|
tay
|
|
lda 19,s
|
|
PUSHNEXT
|
|
eword
|
|
|
|
.if 0
|
|
; H: ( -- n ) Copy third-inner loop index to stack.
|
|
dword KX,"K",F_CONLY ; noindex
|
|
lda 29,s
|
|
tay
|
|
lda 31,s
|
|
PUSHNEXT
|
|
eword
|
|
.endif
|
|
|
|
; H: Compilation: ( -- case-sys ) start a CASE...ENDCASE structure
|
|
; H: Execution: ( -- )
|
|
dword CASE,"CASE",F_IMMED|F_CONLY|F_TEMPD
|
|
ENTER
|
|
.dword _COMP_LIT
|
|
.dword _SKIP2 ; compile execution semantics
|
|
.dword HERE ; case-sys
|
|
.dword _COMP_LIT
|
|
.dword _JUMP ; ENDOF resumes execution here
|
|
.dword _COMP_LIT ; compile unresolved
|
|
.dword _CONTROL_MM
|
|
EXIT
|
|
eword
|
|
|
|
; ( n1 n2 -- n1 ) run-time semantics of OF
|
|
; test against CASE value, SKIP if match
|
|
; otherwise JUMP (to cell after ENDOF)
|
|
hword _OF,"_OF"
|
|
jsr _2parm
|
|
lda STACKBASE+4,x
|
|
cmp STACKBASE+0,x
|
|
bne nomatch
|
|
lda STACKBASE+6,x
|
|
cmp STACKBASE+2,x
|
|
bne nomatch
|
|
jsr _stackincr ; drop test value
|
|
jsr _stackincr ; and value being tested
|
|
jmp _SKIP::code ; and skip jump target
|
|
nomatch: jsr _stackincr ; drop test value
|
|
jmp _JUMP::code ; go to jump target
|
|
eword
|
|
|
|
; H: Compilation: ( case-sys -- case-sys of-sys ) Begin an OF...ENDOF structure.
|
|
; H: Execution: ( x1 x2 -- | x1 ) Execute OF clause if x1 = x2, leave x1 on stack if not.
|
|
dword OF,"OF",F_IMMED|F_CONLY
|
|
ENTER
|
|
.dword _COMP_LIT
|
|
.dword _OF
|
|
.dword HERE ; of-sys
|
|
.dword _COMP_LIT ; compile unresolved
|
|
.dword _CONTROL_MM
|
|
EXIT
|
|
eword
|
|
|
|
; H: Compilation; ( case-sys of-sys -- case-sys ) Conclude an OF...ENDOF structure.
|
|
; H: Execution: Continue execution at ENDCASE of case-sys.
|
|
dword ENDOF,"ENDOF",F_IMMED|F_CONLY
|
|
ENTER
|
|
.dword _COMP_LIT ; compile a jump
|
|
.dword _JUMP
|
|
.dword OVER ; copy case-sys
|
|
.dword COMPILECOMMA ; which is the jump target
|
|
.dword HERE ; unmatched OF jumps here
|
|
.dword SWAP
|
|
.dword STORE ; resolve of-sys
|
|
EXIT
|
|
eword
|
|
|
|
; H: Compilation: ( case-sys -- ) Conclude a CASE...ENDCASE structure.
|
|
; H: Execution: ( | n -- ) Continue execution, dropping n if no OF matched.
|
|
dword ENDCASE,"ENDCASE",F_IMMED|F_CONLY
|
|
ENTER
|
|
.dword _COMP_LIT ; compile drop value under test
|
|
.dword DROP
|
|
.dword HERE ; case-sys jump goes here
|
|
.dword SWAP
|
|
.dword CELLPLUS
|
|
.dword STORE ; resolve case-sys
|
|
.dword dTEMPSEMIQ ; see if we need to end a temporary def
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( -- ) Store 16 to BASE.
|
|
dword HEX,"HEX"
|
|
ENTER
|
|
ONLIT 16
|
|
.dword BASE
|
|
.dword STORE
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( -- ) Store 10 to BASE.
|
|
dword DECIMAL,"DECIMAL"
|
|
ENTER
|
|
ONLIT 10
|
|
.dword BASE
|
|
.dword STORE
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( -- ) Store 2 to BASE.
|
|
dword BINARY,"BINARY"
|
|
ENTER
|
|
ONLIT 2
|
|
.dword BASE
|
|
.dword STORE
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( -- ) Store 8 to BASE.
|
|
dword OCTAL,"OCTAL"
|
|
ENTER
|
|
ONLIT 8
|
|
.dword BASE
|
|
.dword STORE
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( x1 -- x2 ) x2 = x1 + 1
|
|
dword INCR,"1+"
|
|
jsr _1parm
|
|
doinc: inc STACKBASE+0,x
|
|
bne :+
|
|
inc STACKBASE+2,x
|
|
: NEXT
|
|
eword
|
|
|
|
; H: ( x1 -- x2 ) x2 = x1 - 1
|
|
dword DECR,"1-"
|
|
jsr _1parm
|
|
lda STACKBASE+0,x
|
|
bne :+
|
|
dec STACKBASE+2,x
|
|
: dec STACKBASE+0,x
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( x1 -- x2 ) x2 = x1 + 2
|
|
dword TWOINCR,"2+"
|
|
jsr _1parm
|
|
lda STACKBASE+0,x
|
|
clc
|
|
adc #$02
|
|
sta STACKBASE+0,x
|
|
bcc :+
|
|
inc STACKBASE+2,x
|
|
: NEXT
|
|
eword
|
|
|
|
; H: ( x1 -- x2 ) x2 = x1 - 2
|
|
dword TWODECR,"2-"
|
|
jsr _1parm
|
|
lda STACKBASE+0,x
|
|
sec
|
|
sbc #$02
|
|
sta STACKBASE+0,x
|
|
bcs :+
|
|
dec STACKBASE+2,x
|
|
: NEXT
|
|
eword
|
|
|
|
; H: ( x1 -- x2 ) Invert the bits in x1.
|
|
dword INVERT,"INVERT"
|
|
jsr _1parm
|
|
jsr _invert
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( x1 -- x2 ) Invert the bits in x1.
|
|
dword NOT,"NOT"
|
|
bra INVERT::code
|
|
eword
|
|
|
|
; H: ( n1 -- n2 ) Negate n1.
|
|
dword NEGATE,"NEGATE"
|
|
jsr _1parm
|
|
jsr _negate
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( n1 f -- n1|n2 ) If f < 0, then negate n.
|
|
; non-standard
|
|
hword QNEGATE,"?NEGATE"
|
|
jsr _popay
|
|
and #$8000
|
|
bne NEGATE::code
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( n1 -- n2 ) Take the absolute value of n1.
|
|
; we don't check parms on stack here because
|
|
; NEGATE will error if empty
|
|
dword ABS,"ABS"
|
|
lda STACKBASE+2,x
|
|
bpl :+
|
|
jsr _negate
|
|
: NEXT
|
|
eword
|
|
|
|
; H: ( d1 -- d2 ) Negate d1.
|
|
dword DNEGATE,"DNEGATE"
|
|
jsr _2parm
|
|
jsr _dnegate
|
|
NEXT ; push high cell
|
|
eword
|
|
|
|
; H: ( d1 -- d1|d2 ) Take the absolute value of d1.
|
|
dword DABS,"DABS"
|
|
lda STACKBASE+2,x
|
|
bpl :+
|
|
jsr _dnegate
|
|
: NEXT
|
|
eword
|
|
|
|
; H: ( x1 x2 -- x3 ) x3 = x1 + x2
|
|
dword PLUS,"+"
|
|
jsr _2parm
|
|
lda STACKBASE+4,x
|
|
clc
|
|
adc STACKBASE+0,x
|
|
sta STACKBASE+4,x
|
|
lda STACKBASE+6,x
|
|
adc STACKBASE+2,x
|
|
sta STACKBASE+6,x
|
|
stkinc: inx
|
|
inx
|
|
inx
|
|
inx
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( x1 x2 -- x3 ) x3 = x1 - x2
|
|
dword MINUS,"-"
|
|
jsr _2parm
|
|
lda STACKBASE+4,x
|
|
sec
|
|
sbc STACKBASE+0,x
|
|
sta STACKBASE+4,x
|
|
lda STACKBASE+6,x
|
|
sbc STACKBASE+2,x
|
|
sta STACKBASE+6,x
|
|
bra PLUS::stkinc
|
|
eword
|
|
|
|
; H: ( u1 u2 -- u3 ) u3 = u1 & u2
|
|
dword LAND,"AND"
|
|
jsr _2parm
|
|
lda STACKBASE+4,x
|
|
and STACKBASE+0,x
|
|
sta STACKBASE+4,x
|
|
lda STACKBASE+6,x
|
|
and STACKBASE+2,x
|
|
sta STACKBASE+6,x
|
|
bra PLUS::stkinc
|
|
eword
|
|
|
|
; H: ( u1 u2 -- u3 ) u3 = u1 | u2
|
|
dword LOR,"OR"
|
|
jsr _2parm
|
|
lda STACKBASE+4,x
|
|
ora STACKBASE+0,x
|
|
sta STACKBASE+4,x
|
|
lda STACKBASE+6,x
|
|
ora STACKBASE+2,x
|
|
sta STACKBASE+6,x
|
|
bra PLUS::stkinc
|
|
eword
|
|
|
|
; H: ( u1 u2 -- u3 ) u3 = u1 ^ u2
|
|
dword LXOR,"XOR"
|
|
jsr _2parm
|
|
lda STACKBASE+4,x
|
|
eor STACKBASE+0,x
|
|
sta STACKBASE+4,x
|
|
lda STACKBASE+6,x
|
|
eor STACKBASE+2,x
|
|
sta STACKBASE+6,x
|
|
bra PLUS::stkinc
|
|
eword
|
|
|
|
; H: ( u1 u2 -- u3 ) u3 = u1 << u2
|
|
dword LSHIFT,"LSHIFT"
|
|
jsr _2parm
|
|
jsr _popxr
|
|
ldy #.loword(shift-1)
|
|
lda #.hiword(shift-1)
|
|
jsr _iter_ay
|
|
NEXT
|
|
shift: asl STACKBASE+0,x
|
|
rol STACKBASE+2,x
|
|
clc
|
|
rtl
|
|
eword
|
|
|
|
; H: ( u1 u2 -- u3 ) u3 = u1 >> u2
|
|
dword RSHIFT,"RSHIFT"
|
|
jsr _2parm
|
|
jsr _popxr
|
|
ldy #.loword(shift-1)
|
|
lda #.hiword(shift-1)
|
|
jsr _iter_ay
|
|
NEXT
|
|
shift: lsr STACKBASE+2,x
|
|
ror STACKBASE+0,x
|
|
clc
|
|
rtl
|
|
eword
|
|
|
|
; H: ( u1 u2 -- u3 ) u3 = u1 << u2
|
|
dword LSHIFTX,"<<"
|
|
bra LSHIFT::code
|
|
eword
|
|
|
|
; H: ( u1 u2 -- u3 ) u3 = u1 >> u2
|
|
dword RSHIFTX,">>"
|
|
bra RSHIFT::code
|
|
eword
|
|
|
|
; H: ( x1 x2 -- x3 ) x3 = x1 >> x2, extending sign bit.
|
|
dword ARSHIFT,">>A"
|
|
jsr _2parm
|
|
jsr _popxr
|
|
ldy #.loword(shift-1)
|
|
lda #.hiword(shift-1)
|
|
jsr _iter_ay
|
|
NEXT
|
|
shift: lda STACKBASE+2,x
|
|
cmp #$8000
|
|
ror STACKBASE+2,x
|
|
ror STACKBASE+0,x
|
|
clc
|
|
rtl
|
|
eword
|
|
|
|
; H: ( u1 -- u2 ) Shift n1 one bit left.
|
|
dword TWOMULT,"2*"
|
|
jsr _1parm
|
|
jsl LSHIFT::shift
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( u1 -- u2 ) Shift n1 one bit right.
|
|
dword UTWODIV,"U2/"
|
|
jsr _1parm
|
|
jsl RSHIFT::shift
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( x1 -- x2 ) Shift x1 one bit right, extending sign bit.
|
|
dword TWODIV,"2/"
|
|
jsr _1parm
|
|
jsl ARSHIFT::shift
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( n addr -- ) Add n to value at addr.
|
|
dword PSTORE,"+!"
|
|
ENTER
|
|
.dword DUP
|
|
.dword FETCH
|
|
.dword ROT
|
|
.dword PLUS
|
|
.dword SWAP
|
|
.dword STORE
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( d -- n ) Convert double-number to number.
|
|
dword DtoS,"D>S"
|
|
jmp DROP::code
|
|
eword
|
|
|
|
; H: ( n -- d ) Convert number to double-number.
|
|
dword StoD,"S>D"
|
|
jsr _1parm
|
|
lda STACKBASE+2,x
|
|
and #$8000
|
|
bpl :+
|
|
lda #$FFFF
|
|
: tay
|
|
PUSHNEXT
|
|
eword
|
|
|
|
; H: ( n1 n2 -- d1 d2 ) Convert two numbers to double-numbers.
|
|
dword TWOStoD,"2S>D"
|
|
ENTER
|
|
.dword PtoR
|
|
.dword StoD
|
|
.dword RtoP
|
|
.dword StoD
|
|
EXIT
|
|
eword
|
|
|
|
; Factored for number conversion
|
|
.proc _dplus
|
|
lda STACKBASE+12,x
|
|
clc
|
|
adc STACKBASE+4,x
|
|
sta STACKBASE+12,x
|
|
lda STACKBASE+14,x
|
|
adc STACKBASE+6,x
|
|
sta STACKBASE+14,x
|
|
lda STACKBASE+8,x
|
|
adc STACKBASE+0,x
|
|
sta STACKBASE+8,x
|
|
lda STACKBASE+10,x
|
|
adc STACKBASE+2,x
|
|
sta STACKBASE+10,x
|
|
stkinc: txa
|
|
clc
|
|
adc #$08
|
|
tax
|
|
rts
|
|
.endproc
|
|
|
|
; H: ( d1 d2 -- d3 ) d3 = d1 + d2
|
|
dword DPLUS,"D+"
|
|
jsr _4parm
|
|
jsr _dplus
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( d1 d2 -- d3 ) d3 = d1 - d2
|
|
dword DMINUS,"D-"
|
|
jsr _4parm
|
|
lda STACKBASE+12,x
|
|
sec
|
|
sbc STACKBASE+4,x
|
|
sta STACKBASE+12,x
|
|
lda STACKBASE+14,x
|
|
sbc STACKBASE+6,x
|
|
sta STACKBASE+14,x
|
|
lda STACKBASE+8,x
|
|
sbc STACKBASE+0,x
|
|
sta STACKBASE+8,x
|
|
lda STACKBASE+10,x
|
|
sbc STACKBASE+2,x
|
|
sta STACKBASE+10,x
|
|
jsr _dplus::stkinc
|
|
NEXT
|
|
eword
|
|
|
|
; System variables for temporary string buffers
|
|
hword dSBUF0,"$SBUF0"
|
|
SYSVAR SV_SBUF0
|
|
eword
|
|
|
|
hword dSBUF1,"$SBUF1"
|
|
SYSVAR SV_SBUF1
|
|
eword
|
|
|
|
hword dCSBUF,"$CSBUF"
|
|
SYSVAR SV_CSBUF
|
|
eword
|
|
|
|
; H: ( addr1 u1 -- addr2 u1 )
|
|
; H: Allocate a temporary string buffer for interpretation semantics of strings
|
|
; H: and return the address and length of the buffer. If taking the slot used
|
|
; H: by an existing buffer, free it.
|
|
dword dTMPSTR,"$TMPSTR"
|
|
jsr _2parm
|
|
lda STACKBASE+0,x ; get u1
|
|
sta XR
|
|
lda STACKBASE+2,x
|
|
bne nomem ; only going to support ~64K strings for this
|
|
sta XR+2
|
|
jsr _alloc ; allocate memory for it
|
|
bcc nomem
|
|
pha ; save pointer
|
|
phy
|
|
ldy #SV_CSBUF ; get current string buffer
|
|
lda [SYSVARS],y
|
|
inc a
|
|
and #$01 ; only need low bit
|
|
sta [SYSVARS],y
|
|
pha ; save it
|
|
bne getbuf1
|
|
ldy #SV_SBUF0+2 ; select buf 0
|
|
bra getbuf
|
|
getbuf1: ldy #SV_SBUF1+2 ; select buf 1
|
|
getbuf: lda [SYSVARS],y ; get buffer pointer
|
|
sta WR+2 ; into WR
|
|
dey
|
|
dey
|
|
lda [SYSVARS],y
|
|
sta WR
|
|
ora WR+2
|
|
beq :+ ; no prior allocation if zero
|
|
jsr _free ; otherwise, free current memory
|
|
: lda STACKBASE+0,x ; length to XR
|
|
sta XR
|
|
lda STACKBASE+2,x
|
|
sta XR+2
|
|
lda STACKBASE+4,x ; original address to WR
|
|
sta WR
|
|
lda STACKBASE+6,x
|
|
sta WR+2
|
|
pla
|
|
bne setbuf1
|
|
ldy #SV_SBUF0 ; select buf 0
|
|
bra setbuf
|
|
setbuf1: ldy #SV_SBUF1 ; select buf 1
|
|
setbuf: pla ; update pointers
|
|
sta YR ; in YR
|
|
sta [SYSVARS],y ; in the appropriate system var
|
|
sta STACKBASE+4,x ; in the parameter stack
|
|
iny
|
|
iny
|
|
pla
|
|
sta YR+2
|
|
sta [SYSVARS],y
|
|
sta STACKBASE+6,x
|
|
sec ; move down is faster
|
|
jsr _memmove
|
|
NEXT
|
|
nomem: ldy #.loword(-18)
|
|
lda #.hiword(-18)
|
|
jmp _throway
|
|
eword
|
|
|
|
; H: ( -- <space> )
|
|
dword BL,"BL"
|
|
lda #' '
|
|
jsr _pusha
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( -- ) emit a space
|
|
dword SPACE,"SPACE"
|
|
ENTER
|
|
.dword BL
|
|
.dword EMIT
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( u -- ) emit u spaces
|
|
dword SPACES,"SPACES"
|
|
jsr _popxr
|
|
ldy #.loword(do_emit-1)
|
|
lda #.hiword(do_emit-1)
|
|
jsr _iter_ay
|
|
NEXT
|
|
do_emit: ENTER
|
|
.dword BL
|
|
.dword EMIT
|
|
CODE
|
|
clc
|
|
rtl
|
|
eword
|
|
|
|
; H: ( -- <cr> )
|
|
dword CARRET,"CARRET"
|
|
lda #c_cr
|
|
jsr _pusha
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( -- <lf> )
|
|
dword LINEFEED,"LINEFEED"
|
|
lda #c_lf
|
|
jsr _pusha
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( -- ) Emit a CR with no linefeed, set #OUT to 0.
|
|
dword pCR,"(CR"
|
|
ENTER
|
|
.dword CARRET
|
|
.dword EMIT
|
|
.dword NOUT
|
|
.dword OFF
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( -- ) Emit a LF, increment #LINE.
|
|
hword LF,"LF"
|
|
ENTER
|
|
ONLIT 1
|
|
.dword NLINE
|
|
.dword PSTORE
|
|
.dword LINEFEED
|
|
.dword EMIT
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( -- ) Emit a CR/LF combination, increment #LINE, set #OUT to 0.
|
|
dword CR,"CR"
|
|
ENTER
|
|
.dword pCR
|
|
.dword LF
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( -- <bel> )
|
|
dword BELL,"BELL"
|
|
lda #c_bell
|
|
jsr _pusha
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( -- <bs> )
|
|
dword BS,"BS"
|
|
lda #c_bs
|
|
jsr _pusha
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( -- ) Clear screen & home cursor (uses ANSI escape sequence).
|
|
dword PAGE,"PAGE"
|
|
ENTER
|
|
.dword _SLIT
|
|
.dword 7
|
|
.byte $1B,"[2J",$1B,"[H"
|
|
.dword TYPE
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( u1 u2 -- ) Place cursor at col u1 row u2 (uses ANSI escape sequence).
|
|
dword AT_XY,"AT-XY"
|
|
ENTER
|
|
ONLIT $1B
|
|
.dword EMIT
|
|
ONLIT '['
|
|
.dword EMIT
|
|
.dword INCR
|
|
ONLIT UDOTZ
|
|
ONLIT 10
|
|
.dword TMPBASE
|
|
ONLIT ';'
|
|
.dword EMIT
|
|
.dword INCR
|
|
ONLIT UDOTZ
|
|
ONLIT 10
|
|
.dword TMPBASE
|
|
ONLIT 'H'
|
|
.dword EMIT
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( ud u1 -- u2 u3 ) Divide ud by u1, giving quotient u3 and remainder u2.
|
|
dword UMDIVMOD,"UM/MOD"
|
|
jsr _3parm
|
|
lda STACKBASE+0,x
|
|
ora STACKBASE+2,x
|
|
beq _divzero
|
|
jsr _umdivmod
|
|
bcs _overflow
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( d n1 -- n2 n3 ) Symmetric divide d by n1, giving quotient n3 and remainder n2.
|
|
dword SMDIVREM,"SM/REM"
|
|
.if 1 ; native version
|
|
jsr _3parm
|
|
lda STACKBASE+0,x
|
|
ora STACKBASE+2,x
|
|
beq _divzero
|
|
jsr _smdivrem
|
|
bcs _overflow
|
|
NEXT
|
|
.else ; secondary version
|
|
ENTER
|
|
.dword TWODUP
|
|
.dword LXOR ; compute result sign
|
|
.dword PtoR ; and save
|
|
.dword OVER ; copy dividend sign
|
|
.dword PtoR ; and save
|
|
.dword ABS ; take absolute value of args
|
|
.dword PtoR
|
|
.dword DABS
|
|
.dword RtoP
|
|
.dword UMDIVMOD ; perform unsigned division
|
|
.dword SWAP ; move quotient out of the way
|
|
.dword RtoP ; get dividend sign
|
|
.dword QNEGATE ; and negate the remainder if it should be negative
|
|
.dword SWAP ; put the quotient back
|
|
.dword RtoP ; get result sign
|
|
.dword QNEGATE ; and make negative if it should be negative
|
|
EXIT
|
|
.endif
|
|
eword
|
|
|
|
; helpers to throw division errors
|
|
.proc _divzero
|
|
ldy #.loword(-10)
|
|
lda #.hiword(-10)
|
|
jmp _throway
|
|
.endproc
|
|
|
|
.proc _overflow
|
|
ldy #.loword(-11)
|
|
lda #.hiword(-11)
|
|
jmp _throway
|
|
.endproc
|
|
|
|
; H: ( n -- s ) s = -1 if n is negative, 0 if 0, 1 if positive.
|
|
dword SIGNUM,"SIGNUM"
|
|
jsr _1parm
|
|
jsr _signum
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( d n1 -- n2 n3 ) Floored divide d by n1, giving quotient n3 and remainder n2.
|
|
dword FMDIVMOD,"FM/MOD"
|
|
.if 1 ; primitive, using math lib FM/MOD code
|
|
jsr _3parm
|
|
lda STACKBASE+0,x
|
|
ora STACKBASE+2,x
|
|
beq _divzero
|
|
jsr _fmdivmod
|
|
bcs _overflow
|
|
NEXT
|
|
.elseif 1 ; secondary, using UM/MOD
|
|
ENTER
|
|
.dword DUP
|
|
.dword PtoR
|
|
.dword DUP
|
|
.dword ZEROLT
|
|
.dword _IF
|
|
.dword :+
|
|
.dword NEGATE
|
|
.dword PtoR
|
|
.dword DNEGATE
|
|
.dword RtoP
|
|
: .dword PtoR
|
|
.dword DUP
|
|
.dword ZEROLT
|
|
.dword RCOPY
|
|
.dword LAND
|
|
.dword PLUS
|
|
.dword RtoP
|
|
.dword UMDIVMOD
|
|
.dword RtoP
|
|
.dword ZEROLT
|
|
.dword _IF
|
|
.dword :+
|
|
.dword SWAP
|
|
.dword NEGATE
|
|
.dword SWAP
|
|
: EXIT
|
|
.else ; secondary, using SM/REM
|
|
ENTER
|
|
.dword DUP
|
|
.dword PtoR
|
|
.dword SMDIVREM
|
|
.dword OVER
|
|
.dword SIGNUM
|
|
.dword RCOPY
|
|
.dword SIGNUM
|
|
.dword NEGATE
|
|
.dword EQUAL
|
|
.dword _IF
|
|
.dword else
|
|
.dword DECR
|
|
.dword SWAP
|
|
.dword RtoP
|
|
.dword PLUS
|
|
.dword SWAP
|
|
EXIT
|
|
else: .dword RDROP
|
|
EXIT
|
|
.endif
|
|
eword
|
|
|
|
; H: ( u1 u2 -- u3 u4 ) Divide u1 by u2, giving quotient u4 and remainder u3.
|
|
dword UDIVMOD,"U/MOD"
|
|
ENTER
|
|
.dword PtoR
|
|
.dword StoD
|
|
.dword RtoP
|
|
.dword UMDIVMOD
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( n1 n2 -- n3 n4 ) Divide n1 by n2, giving quotient n4 and remainder n3.
|
|
dword DIVMOD,"/MOD"
|
|
ENTER
|
|
.dword PtoR
|
|
.dword StoD
|
|
.dword RtoP
|
|
.dword FMDIVMOD
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( n1 n2 -- n3 ) Divide n1 by n2, giving remainder n3.
|
|
dword MOD,"MOD"
|
|
ENTER
|
|
.dword DIVMOD
|
|
.dword DROP
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( n1 n2 -- n3 ) Divide n1 by n2, giving quotient n3.
|
|
dword DIV,"/"
|
|
ENTER
|
|
.dword DIVMOD
|
|
.dword NIP
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( n1 n2 n3 -- n4 n5 ) n4, n5 = rem, quot of n1*n2/n3.
|
|
dword MULTDIVMOD,"*/MOD"
|
|
ENTER
|
|
.dword PtoR
|
|
.dword MMULT
|
|
.dword RtoP
|
|
.dword FMDIVMOD
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( n1 n2 n3 -- n4 ) n4 = quot of n1*n2/n3.
|
|
dword MULTDIV,"*/"
|
|
ENTER
|
|
.dword MULTDIVMOD
|
|
.dword NIP
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( d1 n1 -- d2 n2 ) d2, n2 = remainder and quotient of d1/n1
|
|
; unsigned 64-bit by 32-bit divide, leaving 64-bit quotient and 32-bit remainder
|
|
; used by double-number pictured numeric output routines only
|
|
dword UDDIVMOD,"UD/MOD"
|
|
ENTER
|
|
.dword PtoR
|
|
.dword ZERO
|
|
.dword RCOPY
|
|
.dword UMDIVMOD
|
|
.dword RtoP
|
|
.dword SWAP
|
|
.dword PtoR
|
|
.dword UMDIVMOD
|
|
.dword RtoP
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( u1 u2 -- ud ) ud = u1*u2
|
|
dword UMMULT,"UM*"
|
|
jsr _2parm
|
|
jsr _umult
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( u1 u2 -- u3 ) u3 = u1*u2
|
|
dword UMULT,"U*"
|
|
ENTER
|
|
.dword UMMULT
|
|
.dword DtoS
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( n1 n2 -- d ) d = n1*n2
|
|
dword MMULT,"M*"
|
|
jsr _2parm
|
|
lda STACKBASE+2,x ; calculate sign flag
|
|
eor STACKBASE+6,x
|
|
pha ; save it for later
|
|
jsr _2abs
|
|
jsr _umult
|
|
pla
|
|
bpl :+
|
|
jsr _dnegate
|
|
: NEXT
|
|
eword
|
|
|
|
; H: ( n1 n2 -- n3 ) n3 = n1*n2
|
|
dword MULT,"*"
|
|
ENTER
|
|
.dword MMULT
|
|
.dword DtoS
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( u1 -- u2 u3 ) u2 = closest square root <= to the true root, u3 = remainder.
|
|
dword SQRTREM,"SQRTREM"
|
|
jsr _sqroot
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( n1 -- n1|n2 ) n2 = n1+1 if n1 is odd.
|
|
dword EVEN,"EVEN"
|
|
jsr _1parm
|
|
lda STACKBASE+0,x
|
|
and #1
|
|
beq :+
|
|
jmp INCR::code
|
|
: NEXT
|
|
eword
|
|
|
|
; ( -- a-addr ) return address of WORD buffer
|
|
hword WORDBUF,"WORDBUF"
|
|
ENTER
|
|
.dword HERE
|
|
ONLIT 16
|
|
.dword PLUS
|
|
EXIT
|
|
eword
|
|
|
|
.if pad_size > 0
|
|
; H: ( -- a-addr ) return address of PAD
|
|
dword PAD,"PAD"
|
|
ENTER
|
|
.dword WORDBUF
|
|
ONLIT word_buf_size
|
|
.dword PLUS
|
|
EXIT
|
|
eword
|
|
.endif
|
|
|
|
; ( -- a-addr ) variable containing pictured numeric output pointer
|
|
hword dPPTR,"$PPTR"
|
|
SYSVAR SV_dPPTR
|
|
eword
|
|
|
|
; H: ( -- ) Begin pictured numeric output.
|
|
dword PBEGIN,"<#"
|
|
ENTER
|
|
.dword WORDBUF
|
|
ONLIT word_buf_size
|
|
.dword PLUS
|
|
.dword dPPTR
|
|
.dword STORE
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( c -- ) Place c in pictured numeric output.
|
|
dword PHOLD,"HOLD"
|
|
ENTER
|
|
.dword dPPTR
|
|
.dword FETCH
|
|
.dword DECR
|
|
.dword DUP
|
|
.dword dPPTR
|
|
.dword STORE
|
|
.dword CSTORE
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( n -- ) Place - in pictured numeric output if n is negative.
|
|
dword PSIGN,"SIGN"
|
|
jsr _popay
|
|
and #$8000
|
|
beq :+
|
|
lda #'-'
|
|
jsr _pusha
|
|
jmp PHOLD::code
|
|
: NEXT
|
|
eword
|
|
|
|
; H: ( ud1 -- ud2 ) Divide ud1 by BASE, convert remainder to char and HOLD it, ud2 = quotient.
|
|
dword PNUM,"#"
|
|
ENTER
|
|
.dword BASE
|
|
.dword FETCH
|
|
.dword UDDIVMOD
|
|
.dword ROT
|
|
CODE
|
|
hold: jsr _popay
|
|
tya
|
|
jsr _d_to_c
|
|
jsr _pusha
|
|
jmp PHOLD::code
|
|
eword
|
|
|
|
; H: ( u1 -- u2 ) Divide u1 by BASE, convert remainder to char and HOLD it, u2 = quotient.
|
|
dword PUNUM,"U#"
|
|
ENTER
|
|
.dword ZERO
|
|
.dword BASE
|
|
.dword FETCH
|
|
.dword UMDIVMOD
|
|
.dword SWAP
|
|
CODE
|
|
bra PNUM::hold
|
|
eword
|
|
|
|
; H: ( ud -- 0 ) Perform # until quotient is zero.
|
|
dword PNUMS,"#S"
|
|
ENTER
|
|
another: .dword PNUM
|
|
.dword TWODUP
|
|
.dword LOR
|
|
.dword _IFFALSE
|
|
.dword another
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( u -- 0 ) Perform U# until quotient is zero.
|
|
dword PUNUMS,"U#S"
|
|
ENTER
|
|
another: .dword PUNUM
|
|
.dword DUP
|
|
.dword _IFFALSE
|
|
.dword another
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( ud -- ) Conclude pictured numeric output.
|
|
dword PDONE,"#>"
|
|
ENTER
|
|
.dword TWODROP
|
|
getstr: .dword dPPTR
|
|
.dword FETCH
|
|
.dword WORDBUF
|
|
ONLIT word_buf_size
|
|
.dword PLUS
|
|
.dword dPPTR
|
|
.dword FETCH
|
|
.dword MINUS
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( u -- ) Conclude pictured numeric output.
|
|
dword PUDONE,"U#>"
|
|
ENTER
|
|
.dword DROP
|
|
JUMP PDONE::getstr
|
|
eword
|
|
|
|
; ( d f -- c-addr u), f = true if signed number
|
|
hword dUDFMT,"$UDFMT"
|
|
ENTER
|
|
.dword _IF
|
|
.dword ns
|
|
.dword DUP
|
|
.dword PtoR
|
|
.dword DABS
|
|
JUMP doit
|
|
ns: .dword ZERO
|
|
.dword PtoR
|
|
doit: .dword PBEGIN
|
|
.dword PNUMS
|
|
.dword RtoP
|
|
.dword PSIGN
|
|
.dword PDONE
|
|
EXIT
|
|
eword
|
|
|
|
; ( n f -- c-addr u), f = true if signed number
|
|
hword dUFMT,"$UFMT"
|
|
.if 1 ; slightly smaller & slower
|
|
ENTER
|
|
.dword _IF
|
|
.dword ns
|
|
.dword DUP
|
|
.dword PtoR
|
|
.dword ABS
|
|
JUMP :+
|
|
ns: .dword ZERO
|
|
.dword PtoR
|
|
: .dword ZERO ; we already saved the sign, no need to sign-extend
|
|
JUMP dUDFMT::doit
|
|
.else ; bigger & faster
|
|
ENTER
|
|
.dword _IF
|
|
.dword ns
|
|
.dword DUP
|
|
.dword PtoR
|
|
.dword ABS
|
|
JUMP doit
|
|
ns: .dword ZERO
|
|
.dword PtoR
|
|
doit: .dword PBEGIN
|
|
.dword PUNUMS
|
|
.dword RtoP
|
|
.dword PSIGN
|
|
.dword PUDONE
|
|
EXIT
|
|
.endif
|
|
eword
|
|
|
|
; H: ( n -- addr u ) Convert n to text via pictured numeric output.
|
|
dword NTOTXT,"(.)"
|
|
ENTER
|
|
.dword TRUE
|
|
.dword dUFMT
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( u -- addr u ) Convert u to text via pictured numeric output.
|
|
dword UTOTXT,"(U.)"
|
|
ENTER
|
|
.dword FALSE
|
|
.dword dUFMT
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( addr u1 u2 ) output addr u1 in a field of size u2
|
|
hword DFIELD,"$FIELD"
|
|
ENTER
|
|
.dword OVER ; ( c-addr u1 u2 -- c-addr u1 u2 u1' )
|
|
.dword MINUS ; ( c-addr u1 u2 u1' -- c-addr u1 u3 ) u3=remaining field
|
|
.dword DUP ; ( c-addr u1 u3 -- c-addr u1 u3 u3'
|
|
.dword ZEROLT ; ( c-addr u1 u3 u3' -- c-addr u1 u3 f )
|
|
.dword _IF ; ( c-addr u1 u3 f -- c-addr u1 u3 )
|
|
.dword :+ ; 0 or more in field, go print some spaces
|
|
.dword DROP
|
|
.dword TYPE
|
|
EXIT
|
|
: .dword SPACES
|
|
.dword TYPE
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( d u -- ) Output d in a field of u chars.
|
|
dword DDOTR,"D.R"
|
|
ENTER
|
|
.dword PtoR
|
|
.dword TRUE
|
|
.dword dUDFMT
|
|
.dword RtoP
|
|
.dword DFIELD
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( d -- ) Output d.
|
|
dword DDOT,"D."
|
|
ENTER
|
|
.dword TRUE
|
|
.dword dUDFMT
|
|
.dword TYPE
|
|
.dword SPACE
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( u1 u2 -- ) Output u1 in a field of u2 chars.
|
|
dword UDOTR,"U.R"
|
|
ENTER
|
|
.dword PtoR
|
|
.dword FALSE
|
|
.dword dUFMT
|
|
.dword RtoP
|
|
.dword DFIELD
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( u1 -- ) Output u1 with no trailing space.
|
|
dword UDOTZ,"U.0"
|
|
ENTER
|
|
.dword ZERO
|
|
.dword UDOTR
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( n u -- ) Output n in a field of u chars.
|
|
dword DOTR,".R"
|
|
ENTER
|
|
.dword PtoR
|
|
.dword TRUE
|
|
.dword dUFMT
|
|
.dword RtoP
|
|
.dword DFIELD
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( u -- ) Output u.
|
|
dword UDOT,"U."
|
|
ENTER
|
|
.dword FALSE
|
|
.dword dUFMT
|
|
.dword TYPE
|
|
.dword SPACE
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( n -- ) Output n.
|
|
dword DOT,"."
|
|
ENTER
|
|
.dword TRUE
|
|
.dword dUFMT
|
|
.dword TYPE
|
|
.dword SPACE
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( n -- ) Output n.
|
|
dword SDOT,"S."
|
|
bra DOT::code
|
|
eword
|
|
|
|
; H: ( addr -- ) Output signed contents of cell at addr.
|
|
dword SHOW,"?"
|
|
ENTER
|
|
.dword FETCH
|
|
.dword DOT
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( n -- ) Output n in decimal base.
|
|
dword DOTD,".D"
|
|
ENTER
|
|
ONLIT 10
|
|
tmpbase: ONLIT DOT
|
|
.dword SWAP
|
|
.dword TMPBASE
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( n -- ) Output n in hexadecimal base.
|
|
dword DOTH,".H"
|
|
ENTER
|
|
ONLIT 16
|
|
JUMP DOTD::tmpbase
|
|
eword
|
|
|
|
.proc _popxryrwr
|
|
jsr _popxr
|
|
jsr _popyr
|
|
jmp _popwr
|
|
.endproc
|
|
|
|
; H: ( addr1 addr2 len -- ) Move memory.
|
|
dword MOVE,"MOVE"
|
|
jsr _popxryrwr
|
|
jsr _memmove
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( addr1 addr2 len -- ) Move memory, startomg from the bottom.
|
|
dword CMOVE,"CMOVE"
|
|
jsr _popxryrwr
|
|
clc
|
|
jsr _memmove_c
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( addr1 addr2 len -- ) Move memory, starting from the top.
|
|
dword CMOVEUP,"CMOVE>"
|
|
jsr _popxryrwr
|
|
sec
|
|
jsr _memmove_c
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( addr1 addr2 u1 -- n1 ) Compare two strings of length u1.
|
|
; IEEE 1275
|
|
dword COMP,"COMP"
|
|
stz ZR ; case sensitive
|
|
docomp: jsr _popxryrwr
|
|
sep #SHORT_A
|
|
.a8
|
|
ldy #$0000
|
|
lp: cpy XR
|
|
bcs equal
|
|
bit ZR
|
|
bmi insens
|
|
lda [WR],y ; case sensitive compare
|
|
cmp [YR],y
|
|
postcmp: bne neq
|
|
iny
|
|
bra lp
|
|
insens: lda [WR],y ; case insensitive compare
|
|
jsr _cupper8
|
|
sta ZR+2 ; use ZR+2 to hold converted byte
|
|
lda [YR],y
|
|
jsr _cupper8
|
|
cmp ZR+2
|
|
bra postcmp
|
|
neq: rep #SHORT_A
|
|
.a16
|
|
bcc less
|
|
lda #$0000
|
|
tay
|
|
iny
|
|
PUSHNEXT
|
|
less: lda #$FFFF
|
|
tay
|
|
PUSHNEXT
|
|
equal: rep #SHORT_A
|
|
.a16
|
|
lda #$0000
|
|
tay
|
|
PUSHNEXT
|
|
eword
|
|
|
|
; H: ( addr1 addr2 u1 -- n1 ) Case-insensitive compare two strings of length u1.
|
|
; non-standard
|
|
dword CICOMP,"CICOMP"
|
|
stz ZR
|
|
dec ZR
|
|
bra COMP::docomp
|
|
eword
|
|
|
|
; H: ( addr1 u1 addr2 u2 -- n1 ) Compare two strings.
|
|
; ANS Forth
|
|
dword COMPARE,"COMPARE"
|
|
ENTER
|
|
.dword ROT ; ( addr1 u1 addr2 u2 -- addr1 addr2 u2 u1 )
|
|
.dword TWODUP
|
|
.dword TWOPtoR ; ( R: -- u2' u1' )
|
|
.dword MIN
|
|
.dword COMP
|
|
.dword DUP
|
|
.dword _IF
|
|
.dword equal
|
|
.dword RDROP
|
|
.dword RDROP
|
|
EXIT
|
|
equal: .dword DROP
|
|
.dword TWORtoP
|
|
.dword SWAP
|
|
.dword MINUS
|
|
.dword SIGNUM
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( c-addr1 u1 n -- c-addr2 u2 ) Adjust string.
|
|
dword sSTRING,"/STRING"
|
|
.if 1 ; secondary - shorter, slower
|
|
ENTER
|
|
.dword TUCK
|
|
.dword MINUS
|
|
.dword PtoR
|
|
.dword PLUS
|
|
.dword RtoP
|
|
EXIT
|
|
.else ; primitive - longer, faster
|
|
jsr _3parm
|
|
lda STACKBASE+8,x
|
|
clc
|
|
adc STACKBASE+0,x
|
|
sta STACKBASE+8,x
|
|
lda STACKBASE+10,x
|
|
adc STACKBASE+2,x
|
|
sta STACKBASE+10,x
|
|
lda STACKBASE+4,x
|
|
sec
|
|
sbc STACKBASE+0,x
|
|
sta STACKBASE+4,x
|
|
lda STACKBASE+6,x
|
|
sbc STACKBASE+2,x
|
|
sta STACKBASE+6,x
|
|
jsr _stackincr
|
|
NEXT
|
|
.endif
|
|
eword
|
|
|
|
; H: ( c-addr1 u1 c-addr2 u2 -- c-addr3 u3 flag ) Search for substring.
|
|
; WR XR YR ZR
|
|
; in practice ZR can only be 16-bit like most other string stuff
|
|
dword SEARCH,"SEARCH"
|
|
jsr _4parm
|
|
jsr _popay
|
|
sty ZR
|
|
sta ZR+2
|
|
jsr _popyr
|
|
lda STACKBASE+0,x ; now we are down to ( c-addr1 u1 ) on stack
|
|
sta XR ; get them and put them into WR and XR
|
|
lda STACKBASE+2,x
|
|
sta XR+2
|
|
lda STACKBASE+4,x
|
|
sta WR
|
|
lda STACKBASE+6,x
|
|
sta WR+2
|
|
bra chklen
|
|
next: rep #SHORT_A
|
|
.a16
|
|
jsr _incwr
|
|
jsr _decxr
|
|
chklen: lda XR+2
|
|
cmp ZR+2
|
|
bne :+
|
|
lda XR
|
|
cmp ZR
|
|
: bcc nomatch ; XR < ZR, no match found!
|
|
ldy ZR ; let's see if there's a match
|
|
beq nomatch ; nope out of u2 is zero
|
|
sep #SHORT_A
|
|
.a8
|
|
lp: dey ; it needs to be one less than
|
|
lda [WR],y
|
|
cmp [YR],y
|
|
bne next
|
|
cpy #$0000
|
|
bne lp ; keep matching
|
|
rep #SHORT_A
|
|
.a16
|
|
lda WR+2 ; match found, return results!
|
|
sta STACKBASE+6,x
|
|
lda WR
|
|
sta STACKBASE+4,x
|
|
lda XR+2
|
|
sta STACKBASE+2,x
|
|
lda XR
|
|
sta STACKBASE+0,x
|
|
lda #$FFFF
|
|
bra :+
|
|
nomatch: lda #$0000
|
|
: tay
|
|
PUSHNEXT
|
|
eword
|
|
|
|
; H: ( addr len char -- ) Fill memory with char.
|
|
dword FILL,"FILL"
|
|
ENTER
|
|
.dword NROT
|
|
CODE
|
|
ldy #.loword(dofill-1)
|
|
lda #.hiword(dofill-1)
|
|
jsr _str_op_ays
|
|
jsr _stackincr
|
|
NEXT
|
|
dofill: sep #SHORT_A
|
|
.a8
|
|
lda STACKBASE+0,x
|
|
sta [WR]
|
|
rep #SHORT_A
|
|
.a16
|
|
clc
|
|
rtl
|
|
eword
|
|
|
|
; H: ( addr len -- ) Fill memory with spaces.
|
|
dword BLANK,"BLANK"
|
|
ENTER
|
|
ONLIT ' '
|
|
.dword FILL
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( addr len -- ) Zero fill memory.
|
|
dword ERASE,"ERASE"
|
|
ENTER
|
|
.dword ZERO
|
|
.dword FILL
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( addr len -- ) Perform WBFLIP on the words in memory.
|
|
dword WBFLIPS,"WBFLIPS"
|
|
ldy #.loword(doflip-1)
|
|
lda #.hiword(doflip-1)
|
|
jsr _str_op_ays
|
|
NEXT
|
|
doflip: lda [WR]
|
|
xba
|
|
sta [WR]
|
|
jsr _incwr
|
|
clc
|
|
rtl
|
|
eword
|
|
|
|
; H: ( addr len -- ) Perform LBFLIP on the cells in memory.
|
|
dword LBFLIPS,"LBFLIPS"
|
|
ldy #.loword(doflip-1)
|
|
lda #.hiword(doflip-1)
|
|
jsr _str_op_ays
|
|
NEXT
|
|
doflip: ldy #$02
|
|
lda [WR]
|
|
xba
|
|
pha
|
|
lda [WR],y
|
|
xba
|
|
cont: sta [WR]
|
|
pla
|
|
sta [WR],y
|
|
lda WR
|
|
clc
|
|
adc #.loword(3)
|
|
sta WR
|
|
lda WR+2
|
|
adc #.hiword(3)
|
|
sta WR+2
|
|
clc
|
|
rtl
|
|
eword
|
|
|
|
; H: ( addr len -- ) Perform LWFLIP on the cells in memory.
|
|
dword LWFLIPS,"LWFLIPS"
|
|
ldy #.loword(doflip-1)
|
|
lda #.hiword(doflip-1)
|
|
jsr _str_op_ays
|
|
NEXT
|
|
doflip: ldy #$02
|
|
lda [WR]
|
|
pha
|
|
lda [WR],y
|
|
bra LBFLIPS::cont
|
|
eword
|
|
|
|
.if include_fcode
|
|
; FCode support words
|
|
|
|
; H: ( addr -- char true ) Access memory at addr, returning char.
|
|
dword CPEEK,"CPEEK"
|
|
ENTER
|
|
.dword CFETCH
|
|
.dword TRUE
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( addr -- word true ) Access memory at addr, returning word.
|
|
dword WPEEK,"WPEEK"
|
|
ENTER
|
|
.dword WFETCH
|
|
.dword TRUE
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( addr -- cell true ) Access memory at addr, returning cell.
|
|
dword LPEEK,"LPEEK"
|
|
ENTER
|
|
.dword LFETCH
|
|
.dword TRUE
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( char addr -- true ) Store char at addr.
|
|
dword CPOKE,"CPOKE"
|
|
ENTER
|
|
.dword CSTORE
|
|
.dword TRUE
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( word addr -- true ) Store word at addr.
|
|
dword WPOKE,"WPOKE"
|
|
ENTER
|
|
.dword WSTORE
|
|
.dword TRUE
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( cell addr -- true ) Store cell at addr.
|
|
dword LPOKE,"LPOKE"
|
|
ENTER
|
|
.dword LSTORE
|
|
.dword TRUE
|
|
EXIT
|
|
eword
|
|
|
|
; FCode evaluator variables:
|
|
|
|
; Variable containing FCode instruction pointer
|
|
hword dFCODE_IP,"$FCODE-IP"
|
|
SYSVAR SV_FCODE_IP
|
|
eword
|
|
|
|
; If set nonzero, FCode interpretation will end and the value thrown
|
|
hword dFCODE_END,"$FCODE-END"
|
|
SYSVAR SV_FCODE_END
|
|
eword
|
|
|
|
; Bytes to increment $FCODE-IP for an FCode fetch. Nearly always 1.
|
|
hword dFCODE_SPREAD,"$FCODE-SPREAD"
|
|
SYSVAR SV_FCODE_SPREAD
|
|
eword
|
|
|
|
; If zero, the FCode offset size is 8 bits, otherwise 16.
|
|
hword dFCODE_OFFSET,"$FCODE-OFFSET"
|
|
SYSVAR SV_FCODE_OFFSET
|
|
eword
|
|
|
|
; Contains the XT of the FCode fetch instruction, usually RB@
|
|
hword dFCODE_FETCH,"$FCODE-FETCH"
|
|
SYSVAR SV_FCODE_FETCH
|
|
eword
|
|
|
|
; Contains the address of the FCode Master Table
|
|
hword dFCODE_TABLES,"$FCODE-TABLES"
|
|
SYSVAR SV_FCODE_TABLES
|
|
eword
|
|
|
|
; Contains the address of the last defined FCode function
|
|
hword dFCODE_LAST,"$FCODE-LAST"
|
|
SYSVAR SV_FCODE_LAST
|
|
eword
|
|
|
|
; If one, place headers on header-optional Fcode functions
|
|
; set by $BYTE-EXEC to the result of FCODE-DEBUG? if it exists
|
|
hword dFCODE_DEBUG,"$FCODE-DEBUG"
|
|
SYSVAR SV_FCODE_DEBUG
|
|
eword
|
|
|
|
; H: ( -- u ) Return FCode revision
|
|
dword xFCODE_REVISION,"FCODE-REVISION"
|
|
ENTER
|
|
ONLIT $87
|
|
.dword DO_TOKEN
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( -- ) Display FCode IP and byte, throw exception -256.
|
|
dword FERROR,"FERROR"
|
|
ENTER
|
|
.dword dFCODE_IP
|
|
.dword FETCH
|
|
.dword DUP
|
|
.dword UDOT
|
|
.dword CFETCH
|
|
.dword UDOT
|
|
ONLIT -256
|
|
.dword THROW
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( xt fcode# f -- ) Set fcode# to execute xt, immediacy f.
|
|
dword SET_TOKEN,"SET-TOKEN"
|
|
jml xSET_TOKEN_code
|
|
eword
|
|
|
|
; H: ( fcode# -- xt f ) Get fcode#'s xt and immediacy.
|
|
dword GET_TOKEN,"GET-TOKEN"
|
|
jsr _1parm
|
|
jsl lGET_TOKEN
|
|
NEXT
|
|
eword
|
|
|
|
; FCode atomic memory accessors, IEEE 1275-1994 says these may be overwritten by FCode
|
|
; to do device-specific accesses.
|
|
|
|
; ( addr -- char ) fetch char at addr, atomically
|
|
hword dRBFETCH,"$RB@"
|
|
jmp CFETCH::code
|
|
eword
|
|
|
|
; ( addr -- word ) fetch word at addr
|
|
; Note that IEEE 1275-1994 requires the fetch to occur in a single access, but the '816
|
|
; has an 8-bit bus so this is technically impossible.
|
|
hword dRWFETCH,"$RW@"
|
|
jmp WFETCH::code
|
|
eword
|
|
|
|
; ( addr -- cell ) fetch cell at addr
|
|
; Note that IEEE 1275-1994 requires the fetch to occur in a single access, but the '816
|
|
; has an 8-bit bus so this is technically impossible.
|
|
hword dRLFETCH,"$RL@"
|
|
jmp LFETCH::code
|
|
eword
|
|
|
|
; ( byte addr -- ) store byte at addr, atomically
|
|
hword dRBSTORE,"$RB!"
|
|
jmp CSTORE::code
|
|
eword
|
|
|
|
; ( word addr -- ) store word at addr
|
|
; Note that IEEE 1275-1994 requires the store to occur in a single access, but the '816
|
|
; has an 8-bit bus so this is technically impossible.
|
|
hword dRWSTORE,"$RW!"
|
|
jmp WSTORE::code
|
|
eword
|
|
|
|
; ( cell addr -- ) store cell at addr
|
|
; Note that IEEE 1275-1994 requires the store to occur in a single access, but the '816
|
|
; has an 8-bit bus so this is technically impossible.
|
|
hword dRLSTORE,"$RL!"
|
|
jmp LSTORE::code
|
|
eword
|
|
|
|
; H: ( addr -- byte ) Perform FCode-equivalent RB@: fetch byte.
|
|
dword RBFETCH,"RB@",F_IMMED
|
|
ENTER
|
|
ONLIT $230
|
|
.dword DO_TOKEN
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( addr -- word ) Perform FCode-equivalent RW@: fetch word.
|
|
dword RWFETCH,"RW@",F_IMMED
|
|
ENTER
|
|
ONLIT $232
|
|
.dword DO_TOKEN
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( addr -- cell ) Perform FCode-equivalent RL@: fetch cell.
|
|
dword RLFETCH,"RL@",F_IMMED
|
|
ENTER
|
|
ONLIT $234
|
|
.dword DO_TOKEN
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( byte addr -- ) Perform FCode-equivalent RB!: store byte.
|
|
dword RBSTORE,"RB!",F_IMMED
|
|
ENTER
|
|
ONLIT $231
|
|
.dword DO_TOKEN
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( word addr -- ) Perform FCode-equivalent RW!: store word.
|
|
dword RWSTORE,"RW!",F_IMMED
|
|
ENTER
|
|
ONLIT $233
|
|
.dword DO_TOKEN
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( cell addr -- ) Perform FCode-equivalent RL!, store cell.
|
|
dword RLSTORE,"RL!",F_IMMED
|
|
ENTER
|
|
ONLIT $235
|
|
.dword DO_TOKEN
|
|
EXIT
|
|
eword
|
|
|
|
.if 0 ; stuff for testing
|
|
dword xSET_MUTABLE_FTABLES,"SET-MUTABLE-FTABLES" ; noindex
|
|
ENTER
|
|
.dword SET_MUTABLE_FTABLES
|
|
EXIT
|
|
eword
|
|
|
|
dword xSET_RAM_FTABLE,"SET-RAM-FTABLE" ; noindex
|
|
ENTER
|
|
.dword SET_RAM_FTABLE
|
|
EXIT
|
|
eword
|
|
|
|
dword xSET_ROM_FTABLE,"SET-ROM-FTABLE" ; noindex
|
|
ENTER
|
|
.dword SET_ROM_FTABLE
|
|
EXIT
|
|
eword
|
|
|
|
dword xGET_FTABLES,"GET-FTABLES" ; noindex
|
|
ENTER
|
|
.dword GET_FTABLES
|
|
EXIT
|
|
eword
|
|
|
|
dword xSAVE_FCODE_STATE,"SAVE-FCODE-STATE" ; noindex
|
|
ENTER
|
|
.dword SAVE_FCODE_STATE
|
|
EXIT
|
|
eword
|
|
|
|
dword xRESTORE_FCODE_STATE,"RESTORE-FCODE-STATE" ; noindex
|
|
ENTER
|
|
.dword RESTORE_FCODE_STATE
|
|
EXIT
|
|
eword
|
|
.endif
|
|
|
|
; FCode evaluation
|
|
; this does *not* save and restore the FCode evaluator state, that's what byte-load is
|
|
; for. This just gets things going, and unless SET-TOKEN is called, sticks with the ROM
|
|
; FCode tables.
|
|
; H: ( addr xt -- ) evaluate FCode at addr with fetch function xt, do not save FCode
|
|
; H: evaluator state
|
|
dword dBYTE_EXEC,"$BYTE-EXEC"
|
|
jsr _2parm
|
|
ENTER
|
|
SLIT "FCODE-DEBUG?" ; see if user wants optional headers
|
|
.dword dFIND
|
|
.dword _IF
|
|
.dword nope
|
|
.dword EXECUTE
|
|
.dword dFCODE_DEBUG
|
|
.dword STORE
|
|
.dword _SKIP
|
|
nope: .dword TWODROP
|
|
.dword DUP
|
|
.dword ONE
|
|
.dword ULTE
|
|
.dword _IF
|
|
.dword usext
|
|
.dword DROP ; Drop supplied xt
|
|
ONLIT $230 ; RB@
|
|
.dword GET_TOKEN ; get XT
|
|
.dword DROP ; drop the flag
|
|
usext: .dword dFCODE_FETCH ; and put it in $FCODE-FETCH
|
|
.dword STORE
|
|
.dword DECR ; need to start with address -1
|
|
.dword dFCODE_IP
|
|
.dword STORE
|
|
.dword ONE
|
|
.dword dFCODE_SPREAD
|
|
.dword STORE
|
|
.dword dFCODE_END
|
|
.dword OFF
|
|
.dword dFCODE_OFFSET
|
|
.dword OFF
|
|
.dword xFCODE_EVALUATE
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( addr xt -- ) Evaluate FCode at addr with fetch function xt, saving and
|
|
; restoring FCode evaluator state.
|
|
dword BYTE_LOAD,"BYTE-LOAD"
|
|
ENTER
|
|
.dword SAVE_FCODE_STATE
|
|
.dword PtoR
|
|
ONLIT dBYTE_EXEC
|
|
.dword CATCH
|
|
;.dword DOTS
|
|
.dword RtoP
|
|
.dword RESTORE_FCODE_STATE
|
|
.dword THROW
|
|
EXIT
|
|
eword
|
|
.endif ; end of FCode stuff
|
|
|
|
; H: ( addr len -- ) Dump memory.
|
|
dword DUMP,"DUMP"
|
|
ENTER
|
|
.dword BOUNDS
|
|
JUMP addr
|
|
lp: .dword DUP
|
|
ONLIT $F
|
|
.dword LAND
|
|
.dword _IFFALSE
|
|
.dword noaddr
|
|
addr: .dword CR
|
|
.dword DUP
|
|
ONLIT 8
|
|
.dword UDOTR
|
|
ONLIT ':'
|
|
.dword EMIT
|
|
.dword SPACE
|
|
noaddr: .dword DUP
|
|
.dword CFETCH
|
|
ONLIT 2
|
|
.dword UDOTR
|
|
.dword SPACE
|
|
.dword INCR
|
|
.dword TWODUP
|
|
.dword ULTE
|
|
.dword _IF
|
|
.dword lp
|
|
.dword TWODROP
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( xt -- addr|0 ) Get link field of word at xt or 0 if none.
|
|
dword rLINK,">LINK"
|
|
jsr _popyr
|
|
jsr _xttohead
|
|
bcc nolink
|
|
ldy YR
|
|
lda YR+2
|
|
PUSHNEXT
|
|
nolink: lda #$0000
|
|
tay
|
|
PUSHNEXT
|
|
eword
|
|
|
|
; H: ( xt -- c-addr u ) Get string name of word at xt, or ^xt if anonymous/noname.
|
|
; H: Uses pictured numeric output.
|
|
dword rNAME,">NAME"
|
|
ENTER
|
|
.dword ZERO ; ( xt -- xt 0 )
|
|
.dword PtoR ; ( xt 0 -- xt ) ( R: -- 0 )
|
|
lp: .dword RCOPY ; ( xt u )
|
|
ONLIT NAMEMSK ; ( xt u -- xt u u1 )
|
|
.dword UGT ; ( xt u u1 -- xt f ) is name too long?
|
|
.dword _IFFALSE ; ( xt f -- xt )
|
|
.dword noname ; True branch, stack is ( xt 0 ) (R: u )
|
|
.dword DUP ; ( xt -- xt xt' )
|
|
.dword RCOPY ; ( xt xt' - xt xt' u )
|
|
.dword INCR ; ( xt xt' u -- xt xt' u' )
|
|
.dword MINUS ; ( xt xt' u' -- xt xt'' )
|
|
.dword CFETCH ; ( xt xt'' -- xt c )
|
|
.dword DUP ; ( xt c -- xt c c' )
|
|
ONLIT $80 ; ( xt c c' -- xt c c' $80 )
|
|
.dword LAND ; ( xt c c' -- xt c f )
|
|
.dword _IFFALSE ; ( xt c f -- xt c )
|
|
.dword done ; true branch
|
|
.dword DROP ; ( xt c -- xt )
|
|
.dword RINCR ; ( xt ) ( R: u -- u' )
|
|
JUMP lp
|
|
done: ONLIT NAMEMSK ; ( xt c -- xt c m )
|
|
.dword LAND ; ( xt c m -- xt l ) l = length
|
|
.dword RCOPY ; ( xt l -- xt l u ) ( R: u )
|
|
.dword EQUAL ; ( xt l u -- xt f )
|
|
.dword _IF ; ( xt f -- xt )
|
|
.dword noname ; false branch, stack is ( xt ) ( R: u )
|
|
.dword RCOPY ; ( xt -- xt u ) ( R: u )
|
|
.dword QDUP ; ( xt u -- xt u | xt u u )
|
|
.dword _IF ; ( xt u | xt u u -- xt | xt u )
|
|
.dword noname ; false branch, stack is ( xt ) ( R: u )
|
|
.dword MINUS ; ( xt u -- c-addr )
|
|
.dword RtoP ; ( c-addr -- c-addr u )
|
|
EXIT
|
|
noname: .dword RDROP ; ( xt ) ( R: u -- )
|
|
noname1: .dword PBEGIN
|
|
.dword PUNUMS ; ( xt -- )
|
|
ONLIT '^'
|
|
.dword PHOLD
|
|
.dword PUDONE ; ( -- c-addr u )
|
|
EXIT
|
|
eword
|
|
rNAME_noname1 = rNAME::noname1
|
|
|
|
; H: ( addr -- addr+1 u ) Count packed string at addr.
|
|
dword COUNT,"COUNT"
|
|
ENTER
|
|
.dword DUP
|
|
.dword INCR
|
|
.dword SWAP
|
|
.dword CFETCH
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( str len addr -- addr ) Pack string into addr, similar to PLACE in some Forths.
|
|
dword PACK,"PACK"
|
|
jsr _3parm
|
|
jsr _popyr
|
|
jsr _popxr
|
|
jsr _popwr
|
|
lda XR+2
|
|
bne bad
|
|
lda XR
|
|
cmp #$100
|
|
bcs bad
|
|
sta [YR]
|
|
ldy YR
|
|
lda YR+2
|
|
jsr _pushay
|
|
inc YR
|
|
bne :+
|
|
inc YR+2
|
|
: sec ; move down is faster
|
|
jsr _memmove_c
|
|
NEXT
|
|
bad: ldy #.loword(-18)
|
|
lda #.hiword(-18)
|
|
jmp _throway
|
|
eword
|
|
|
|
; H: ( addr u1 -- addr u2 ) u2 = length of string with trailing spaces omitted.
|
|
dword MTRAILING,"-TRAILING"
|
|
lda STACKBASE+4,x
|
|
sta WR
|
|
lda STACKBASE+6,x
|
|
sta WR+2
|
|
jsr _decwr
|
|
ldy STACKBASE+0,x
|
|
lp: lda [WR],y
|
|
and #$FF
|
|
cmp #' '
|
|
bne done
|
|
dey
|
|
bne lp
|
|
done: sty STACKBASE+0,x
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( ud1 addr1 u1 -- ud2 addr2 u2 ) Convert text to number.
|
|
; note: only converts positive numbers!
|
|
; Direct page use:
|
|
; YR = current BASE
|
|
; XR = length left to go (initially u1), only 64K string supported
|
|
; XR + 2 = number of chars processed so far
|
|
; WR = pointer to current char
|
|
dword GNUMBER,">NUMBER"
|
|
jsr _4parm
|
|
ldy #SV_BASE+2
|
|
lda [SYSVARS],y
|
|
sta YR+2
|
|
dey
|
|
dey
|
|
lda [SYSVARS],y
|
|
sta YR
|
|
jsr _popxr ; u1 (length)
|
|
jsr _popwr ; c-addr1 ( stack is now just d )
|
|
stz XR+2
|
|
digit: lda XR ; see if no more chars left
|
|
beq done
|
|
lda [WR]
|
|
and #$FF ; enforce char from 16-bit load
|
|
cmp #'.' ; IEEE 1275-1994 requires these to be ignored
|
|
beq ignore ; when embedded in the number
|
|
cmp #','
|
|
beq ignore
|
|
jsr _c_to_d ; convert to digit
|
|
bcc done ; if out of range, can't use it
|
|
cmp YR ; check against base
|
|
bcs done ; if >=, can't use it
|
|
jsr _pusha ; ( -- ud1l ud1h n )
|
|
jsr _swap ; ( -- ud1l n ud1h )
|
|
ldy YR
|
|
lda #$0000
|
|
jsr _pushay ; ( -- ud1l n ud1h base )
|
|
jsr _umult ; ( -- ud1l n ud1h*basel 0 )
|
|
inx
|
|
inx
|
|
inx
|
|
inx ; ( -- ud1l n ud1h*basel )
|
|
jsr _rot ; ( -- n ud1h*basel ud1l )
|
|
ldy YR
|
|
lda #$0000
|
|
jsr _pushay ; ( -- n ud1h*basel ud1l base )
|
|
jsr _umult ; ( -- n ud1h*basel ud1l*basel ud1l*baseh )
|
|
jsr _dplus ; ( -- ud2 )
|
|
next: jsr _incwr
|
|
dec XR
|
|
inc XR+2
|
|
bra digit
|
|
done: ldy WR
|
|
lda WR+2
|
|
jsr _pushay
|
|
ldy XR
|
|
lda #$0000
|
|
PUSHNEXT
|
|
ignore: lda XR+2
|
|
beq done ; can't be the first
|
|
lda XR
|
|
dec a
|
|
beq done ; nor the last
|
|
bra next
|
|
eword
|
|
|
|
; H: ( str len char -- r-str r-len l-str l-len ) Parse string for char, returning
|
|
; H: the left and right sides.
|
|
dword LEFT_PARSE_STRING,"LEFT-PARSE-STRING"
|
|
jsr _popyr ; char
|
|
jsr _popxr ; len
|
|
jsr _popwr ; str
|
|
ldy #$0000
|
|
lda XR
|
|
ora XR+2
|
|
beq done
|
|
lp: lda [WR],y
|
|
and #$00FF
|
|
iny
|
|
beq done
|
|
cmp YR
|
|
beq done
|
|
cpy XR
|
|
bcc lp
|
|
ldy #$0000
|
|
done: tya
|
|
beq nomatch
|
|
sta XR+2
|
|
lda WR ; addr of str 2 = WR+(XR+2)
|
|
clc
|
|
adc XR+2
|
|
tay
|
|
lda WR+2
|
|
adc #$0000
|
|
jsr _pushay
|
|
lda XR ; len of str 2 = XR-(XR+2)
|
|
sec
|
|
sbc XR+2
|
|
jsr _pusha
|
|
ldy WR
|
|
lda WR+2
|
|
jsr _pushay
|
|
ldy XR+2
|
|
dey
|
|
: lda #$0000
|
|
PUSHNEXT
|
|
nomatch: jsr _pushay
|
|
jsr _pushay
|
|
ldy WR
|
|
lda WR+2
|
|
jsr _pushay
|
|
ldy XR
|
|
bra :-
|
|
eword
|
|
|
|
; H: ( str len -- val.lo val.hi ) Parse two integers from string in the form "n2,n2".
|
|
dword PARSE_2INT,"PARSE-2INT"
|
|
ENTER
|
|
ONLIT ','
|
|
.dword LEFT_PARSE_STRING
|
|
.dword TWOPtoR
|
|
.dword ZERO
|
|
.dword StoD
|
|
.dword TWOSWAP
|
|
.dword GNUMBER
|
|
.dword THREEDROP
|
|
.dword ZERO
|
|
.dword StoD
|
|
.dword TWORtoP
|
|
.dword GNUMBER
|
|
.dword THREEDROP
|
|
EXIT
|
|
eword
|
|
|
|
; ( c-addr u wid -- xt ) Search wordlist wid for word.
|
|
hword dWLSEARCH,"$WLSEARCH"
|
|
jsr _popwr ; wid -> WR
|
|
ldy #$02
|
|
lda [WR],y ; LAST of wordlist at wid, high word
|
|
sta YR+2 ; to YR
|
|
lda [WR] ; now low word
|
|
sta YR
|
|
jsr _popxr ; u -> XR
|
|
jsr _popwr ; c-addr -> WR
|
|
jsr _search_unsmudged
|
|
PUSHNEXT
|
|
eword
|
|
|
|
.if max_search_order > 0
|
|
; H: ( c-addr u wid -- 0 | xt +-1 ) Search wordlist for word.
|
|
dword SEARCH_WORDLIST,"SEARCH-WORDLIST"
|
|
.else
|
|
hword SEARCH_WORDLIST,"SEARCH-WORDLIST"
|
|
.endif
|
|
ENTER
|
|
.dword dWLSEARCH ; ( c-addr u wid -- 0 | xt )
|
|
.dword DUP ; ( 0 | xt -- 0 0 | xt xt )
|
|
.dword _IF ; ( 0 0 | xt xt - 0 | xt )
|
|
.dword notfound ; ( 0 ) if taken
|
|
.dword IMMEDQ ; ( xt -- xt f )
|
|
.dword ONE ; ( xt f -- xt f 1 )
|
|
.dword LOR ; ( ... xt 1/-1 )
|
|
.dword NEGATE ; ( ... xt -1/1 )
|
|
notfound: EXIT
|
|
eword
|
|
|
|
; H: ( c-addr u -- 0 | xt +-1 ) Search for word in current search order.
|
|
dword SEARCH_ALL,"$SEARCH"
|
|
ENTER
|
|
.if max_search_order > 0
|
|
.dword dORDER
|
|
.dword FETCH
|
|
.dword QDUP
|
|
.dword _IF
|
|
.dword noorder
|
|
lp: .dword PtoR ; ( c-addr u1 u2 -- c-addr u1 )
|
|
.dword TWODUP ; ( c-addr u1 -- c-addr u1 c-addr' u1' )
|
|
.dword RtoP ; ( ... c-addr u1 c-addr' u1' u2 )
|
|
.dword DECR ; ( ... c-addr u1 c-addr' u1' u2' )
|
|
.dword DUP ; ( ... c-addr u1 c-addr' u1' u2' u2'' )
|
|
.dword PtoR ; ( ... c-addr u1 c-addr' u1' u2' )
|
|
.dword WLNUM ; ( ... c-addr u1 c-addr' u1' wid-addr )
|
|
.dword FETCH ; ( ... c-addr u1 c-addr' u1' wid )
|
|
.dword SEARCH_WORDLIST ; ( ... c-addr u1 0 | c-addr u1 xt +-1 )
|
|
.dword QDUP ; ( ... c-addr u1 0 | c-addr u1 xt +-1 +-1 )
|
|
.dword _IFFALSE ; ( ... c-addr u1 | c-addr u1 xt +-1 )
|
|
.dword found
|
|
.dword RtoP ; ( ... c-addr u1 u2 )
|
|
.dword DUP ; ( ... c-addr u1 u2 u2' )
|
|
.dword _IFFALSE ; ( ... c-addr u1 u2 )
|
|
.dword lp
|
|
.dword NIPTWO ; ( ... u2 )
|
|
EXIT
|
|
found: .dword RDROP
|
|
.dword TWOPtoR ; ( c-addr u1 xt +-1 -- c-addr u1 )
|
|
.dword TWODROP ; ( c-addr u1 -- )
|
|
.dword TWORtoP ; ( -- xt +-1 )
|
|
EXIT
|
|
.endif
|
|
noorder: .dword GET_CURRENT ; If no search order, search current
|
|
.dword SEARCH_WORDLIST ; compiler wordlist.
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( c-addr u -- xn...x1 t | f ) Environmental query.
|
|
dword ENVIRONMENTQ,"ENVIRONMENT?"
|
|
ENTER
|
|
.dword dENVQ_WL
|
|
.dword SEARCH_WORDLIST
|
|
.dword DUP
|
|
.dword _IF
|
|
.dword nope
|
|
.dword DROP
|
|
.dword EXECUTE
|
|
.dword TRUE
|
|
nope: EXIT
|
|
eword
|
|
|
|
; H: ( c-addr u -- xt true | c-addr u false ) Find word in search order.
|
|
dword dFIND,"$FIND"
|
|
ENTER
|
|
.dword TWODUP
|
|
.dword SEARCH_ALL
|
|
.dword DUP
|
|
.dword _IF
|
|
.dword notfnd
|
|
.dword DROP
|
|
.dword NIPTWO
|
|
.dword TRUE ; IEEE 1275 requires true, not -1 or 1
|
|
notfnd: EXIT
|
|
eword
|
|
|
|
; H: ( c-addr -- xt|0 ) Find packed string word in search order, 0 if not found.
|
|
dword FIND,"FIND"
|
|
ENTER
|
|
.dword DUP
|
|
.dword PtoR
|
|
.dword COUNT
|
|
.dword SEARCH_ALL
|
|
.dword DUP
|
|
.dword _IF
|
|
.dword notfd
|
|
.dword RDROP
|
|
EXIT
|
|
notfd: .dword RtoP
|
|
.dword SWAP
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( [old-name< >] -- xt ) Parse old-name in input stream, return xt of word.
|
|
dword PARSEFIND,"'"
|
|
ENTER
|
|
.dword PARSE_WORD
|
|
.dword SEARCH_ALL
|
|
.dword QDUP
|
|
.dword _IF
|
|
.dword exc
|
|
.dword DROP
|
|
EXIT
|
|
exc: ONLIT -13
|
|
.dword THROW
|
|
eword
|
|
|
|
; H: ( [old-name< >] -- xt ) Immediately parse old-name in input stream, return xt of word.
|
|
dword CPARSEFIND,"[']",F_IMMED
|
|
ENTER
|
|
.dword PARSEFIND
|
|
.dword LITERAL
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( xt -- a-addr) return body of word at xt, if unable then throw exception -31
|
|
dword rBODY,">BODY"
|
|
jsr _popwr ; xt -> wr
|
|
ldy #$01
|
|
lda [WR],y
|
|
and #$FF
|
|
cmp #opJSL
|
|
beq :+
|
|
ldy #.loword(-31)
|
|
lda #.hiword(-31)
|
|
jmp _throway
|
|
: lda WR
|
|
clc
|
|
adc #$05
|
|
tay
|
|
lda WR+2
|
|
adc #$00
|
|
PUSHNEXT
|
|
eword
|
|
|
|
; H: ( a-addr -- xt ) return xt of word with body at a-addr, if unable throw exc. -31
|
|
dword BODYr,"BODY>"
|
|
ENTER
|
|
ONLIT 1
|
|
.dword CELLS
|
|
.dword MINUS
|
|
.dword DUP
|
|
.dword CFETCH
|
|
ONLIT opJSL
|
|
.dword EQUAL
|
|
.dword _IF
|
|
.dword bad
|
|
.dword DECR
|
|
EXIT
|
|
bad: ONLIT -31
|
|
.dword THROW
|
|
eword
|
|
|
|
; ( a-addr -- xt ) from link field address, return xt of word
|
|
hword drXT,"$>XT"
|
|
ENTER
|
|
.dword CELLPLUS
|
|
.dword DUP
|
|
.dword CFETCH
|
|
ONLIT NAMEMSK
|
|
.dword LAND
|
|
.dword PLUS
|
|
.dword CHARPLUS
|
|
EXIT
|
|
eword
|
|
|
|
; ( xt -- xt f ) return immediacy of word at xt
|
|
hword IMMEDQ,"IMMED?"
|
|
jsr _peekwr
|
|
lda [WR]
|
|
and #F_IMMED
|
|
tf: beq :+
|
|
lda #$FFFF
|
|
: tay
|
|
PUSHNEXT
|
|
eword
|
|
|
|
; ( xt -- xt f ) return compile-only flag of word at xt
|
|
hword CONLYQ,"CONLY?"
|
|
jsr _peekwr
|
|
lda [WR]
|
|
and #F_CONLY
|
|
bra IMMEDQ::tf
|
|
eword
|
|
|
|
; ( xt -- xt f ) return temp def flag of word at xt
|
|
; words with temp def flag will trigger a temporary definition to be created in order
|
|
; to run control-flow words in interpretation state
|
|
hword TEMPDQ,"TEMPD?"
|
|
jsr _peekwr
|
|
lda [WR]
|
|
and #F_TEMPD
|
|
bra IMMEDQ::tf
|
|
eword
|
|
|
|
; needed by line editor
|
|
.proc _key
|
|
lda #SI_KEY
|
|
jsl _call_sysif
|
|
bcc :+
|
|
jmp _throway
|
|
: rts
|
|
.endproc
|
|
|
|
; H: ( -- char ) wait for input char, return it
|
|
dword KEY,"KEY"
|
|
jsr _key
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( -- f ) f = true if input char is ready, false otherwise
|
|
dword KEYQ,"KEY?"
|
|
lda #SI_KEYQ
|
|
jsl _call_sysif
|
|
bcc :+
|
|
jmp _throway
|
|
: NEXT
|
|
eword
|
|
|
|
; ( -- a-addr ) variable with address of terminal input buffer
|
|
hword dTIB,"$TIB"
|
|
SYSVAR SV_dTIB
|
|
eword
|
|
|
|
; ( -- c-addr ) return address of terminal input buffer
|
|
hword TIB,"TIB"
|
|
ENTER
|
|
.dword dTIB
|
|
.dword FETCH
|
|
EXIT
|
|
eword
|
|
|
|
; ( -- a-addr ) variable with address of current input buffer
|
|
hword dCIB,"$CIB"
|
|
SYSVAR SV_dCIB
|
|
eword
|
|
|
|
; ( -- u ) variable with number of characters accepted by EXPECT
|
|
dword SPAN,"SPAN"
|
|
SYSVAR SV_SPAN
|
|
eword
|
|
|
|
; TODO: add Open Firmware editing
|
|
; H: ( addr len -- u ) get input line of up to len chars, stor at addr, u = # chars accepted
|
|
dword ACCEPT,"ACCEPT"
|
|
clc
|
|
expect1: ror YR ; if YR high bit set, do auto-termination mode
|
|
jsr _popxr
|
|
jsr _popwr
|
|
inline: ldy #$00 ; entered length
|
|
getchar: phy
|
|
jsr _key
|
|
jsr _popay
|
|
tya
|
|
ply
|
|
cmp #c_bs ; basic editing functions
|
|
beq backspc
|
|
cmp #c_del
|
|
beq backspc
|
|
cmp #c_cr
|
|
beq done
|
|
cmp #' '
|
|
bcc getchar ; ignore nonprintables
|
|
cpy XR ; if we are at max size already
|
|
bcs getchar ; then don't accept this char
|
|
sta [WR],y
|
|
phy
|
|
tay
|
|
jsr do_emit
|
|
ply
|
|
iny
|
|
cpy XR
|
|
bcc getchar
|
|
checkexp: bit YR ; in EXPECT mode?
|
|
bmi done ; yep, auto-terminate
|
|
bra getchar
|
|
backspc: cpy #$00 ; is line empty?
|
|
beq inline ; just start over if so
|
|
dey
|
|
phy ; otherwise do backspace & erase
|
|
ldy #c_bs
|
|
jsr do_emit
|
|
ldy #' '
|
|
jsr do_emit
|
|
ldy #c_bs
|
|
jsr do_emit
|
|
ply
|
|
bra getchar
|
|
done: lda #$00
|
|
jsr _pushay
|
|
bit YR
|
|
bmi expect2
|
|
ENTER
|
|
JUMP docr
|
|
expect2: ENTER
|
|
.dword SPAN
|
|
.dword STORE
|
|
docr: .dword CR
|
|
EXIT
|
|
do_emit: jsr _pushay
|
|
jsr _emit
|
|
rts
|
|
eword
|
|
|
|
; H: ( addr len -- ) get input line of up to len chars, stor at addr, actual len in SPAN
|
|
dword EXPECT,"EXPECT"
|
|
sec
|
|
jmp ACCEPT::expect1
|
|
eword
|
|
|
|
; ( -- ) set current input source to the keyboard/console
|
|
hword SETKBD,"SETKBD"
|
|
ENTER
|
|
.dword TIB
|
|
.dword dCIB
|
|
.dword STORE
|
|
dokbd: ONLIT 0
|
|
doany: .dword dSOURCEID
|
|
.dword STORE
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( -- a-addr ) variable containing current input source ID
|
|
dword dSOURCEID,"$SOURCE-ID"
|
|
SYSVAR SV_SOURCEID
|
|
eword
|
|
|
|
; H: ( -- n ) return current input source id (0 = console, -1 = string, >0 = file)
|
|
dword SOURCEID,"SOURCE-ID"
|
|
ldy #SV_SOURCEID
|
|
lda [SYSVARS],y
|
|
pha
|
|
iny
|
|
iny
|
|
lda [SYSVARS],y
|
|
ply
|
|
PUSHNEXT
|
|
eword
|
|
|
|
; H: ( -- c-addr u ) return address and length of input source buffer
|
|
dword SOURCE,"SOURCE"
|
|
ENTER
|
|
.dword dCIB
|
|
.dword FETCH
|
|
.dword NIN
|
|
.dword FETCH
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( -- f ) refill input buffer, f = true if that worked, false if not
|
|
dword REFILL,"REFILL"
|
|
ENTER
|
|
.dword SOURCEID
|
|
.dword DUP
|
|
.dword _IFFALSE
|
|
.dword notkbd ; return false if input source isn't console
|
|
.dword PIN ; >IN, note zero is on the stack here
|
|
.dword STORE
|
|
.dword TIB
|
|
ONLIT tib_size
|
|
.dword ACCEPT
|
|
.dword NIN ; #IN
|
|
.dword STORE
|
|
.dword TRUE
|
|
EXIT
|
|
notkbd: .dword ZEROLT
|
|
.dword _IFFALSE ; is less than zero?
|
|
.dword noinput ; yes, go throw a false on the stack
|
|
SLIT "$REFILL" ; ( -- addr len true | false )
|
|
.dword dFIND ; see if someone else handles it
|
|
.dword _IF ; $REFILL exists?
|
|
.dword noinput ; nope, nobody handles it
|
|
.dword EXECUTE ; otherwise, execute it and see what happens
|
|
.dword _IF ; that work out OK?
|
|
.dword noinput ; nope, just return false
|
|
.dword ZERO ; otherwise zero input pointer
|
|
.dword PIN
|
|
.dword STORE
|
|
.dword NIN ; set #IN to returned length
|
|
.dword STORE
|
|
.dword dCIB ; make it the input buffer
|
|
.dword STORE
|
|
EXIT
|
|
noinput: .dword FALSE
|
|
EXIT
|
|
eword
|
|
|
|
; ( -- f ) f = true if there is remaining input in the input stream, false otherwise
|
|
hword INQ,"IN?"
|
|
ENTER
|
|
.dword PIN
|
|
.dword FETCH
|
|
.dword NIN
|
|
.dword FETCH
|
|
.dword ULT
|
|
EXIT
|
|
eword
|
|
|
|
; ( -- c-addr ) return address of next character in input stream
|
|
hword INPTR,"INPTR"
|
|
ENTER
|
|
.dword PIN
|
|
.dword FETCH
|
|
.dword dCIB
|
|
.dword FETCH
|
|
.dword PLUS
|
|
EXIT
|
|
eword
|
|
|
|
; ( -- ) increment >IN
|
|
hword INC_INPTR,"INPTR+"
|
|
ENTER
|
|
.dword ONE
|
|
.dword PIN
|
|
.dword PSTORE
|
|
EXIT
|
|
eword
|
|
|
|
; ( -- char ) fetch char from input stream
|
|
hword GETCH,"GETCH"
|
|
ENTER
|
|
.dword INPTR
|
|
.dword CFETCH
|
|
.dword INC_INPTR
|
|
EXIT
|
|
eword
|
|
|
|
hword tSTATUS,">STATUS"
|
|
ENTER
|
|
SLIT "STATUS"
|
|
.dword dFIND
|
|
EXIT
|
|
eword
|
|
|
|
; ( -- ) call STATUS if defined, display OK (interpreting) or [OK] (compiling).
|
|
hword dSTATUS,"$STATUS"
|
|
ENTER
|
|
.dword SOURCEID
|
|
.dword ZEROQ
|
|
.dword _IF
|
|
.dword done ; do nothing if console is not source
|
|
.dword tSTATUS
|
|
.dword _IF
|
|
.dword nostatus
|
|
.dword EXECUTE
|
|
JUMP :+
|
|
nostatus: .dword TWODROP
|
|
: .dword SPACE
|
|
.dword _SMART
|
|
.dword interp
|
|
SLIT "[OK]"
|
|
JUMP dprompt
|
|
interp: SLIT "OK"
|
|
dprompt: .dword TYPE
|
|
.dword CR
|
|
done: EXIT
|
|
eword
|
|
|
|
; H: ( -- ) assuming STATUS is a defer, set it to .S
|
|
dword SHOWSTACK,"SHOWSTACK"
|
|
ENTER
|
|
ONLIT DOTS
|
|
set: .dword tSTATUS
|
|
.dword _IF
|
|
.dword nostatus
|
|
.dword rBODY
|
|
.dword STORE
|
|
EXIT
|
|
nostatus: .dword THREEDROP
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( -- ) assuming STATUS is a defer, set it to NOOP
|
|
dword NOSHOWSTACK,"NOSHOWSTACK"
|
|
ENTER
|
|
ONLIT NOOP
|
|
JUMP SHOWSTACK::set
|
|
eword
|
|
|
|
; ( char -- ) see if char is a space (or unprintable)
|
|
hword ISSPC,"ISSPACE?"
|
|
ENTER
|
|
.dword BL
|
|
.dword INCR
|
|
.dword ULT
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( [word< >] -- addr u ) Parse word from input stream, return address and length.
|
|
dword PARSE_WORD,"PARSE-WORD"
|
|
ENTER
|
|
l1: .dword INQ ; is there input?
|
|
.dword _IF
|
|
.dword none ; nope, return empty
|
|
.dword GETCH ; get char
|
|
.dword ISSPC ; is space?
|
|
.dword _IFFALSE ; if not...
|
|
.dword l1 ; do loop if it is
|
|
.dword INPTR ; get address
|
|
.dword DECR ; fixup because INPTR is 1 ahead now
|
|
.dword ONE ; we have 1 char
|
|
l2: .dword INQ ; more input?
|
|
.dword _IF
|
|
.dword e1 ; if not, exit
|
|
.dword GETCH
|
|
.dword ISSPC
|
|
.dword _IFFALSE
|
|
.dword e1 ; yes, stop
|
|
.dword INCR ; count non-spaces
|
|
JUMP l2
|
|
e1: EXIT
|
|
none: .dword INPTR
|
|
.dword ZERO
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( [word< >] -- addr u ) Alias of PARSE-WORD.
|
|
dword PARSE_NAME,"PARSE-NAME"
|
|
bra PARSE_WORD::code
|
|
eword
|
|
|
|
; H: ( char [text<char>] -- addr u ) Parse text from input stream, delimited by char.
|
|
dword PARSE,"PARSE"
|
|
ENTER
|
|
.dword PtoR
|
|
.dword INPTR
|
|
.dword ZERO
|
|
l1: .dword INQ
|
|
.dword _IF
|
|
.dword e1
|
|
.dword GETCH
|
|
.dword RCOPY
|
|
.dword EQUAL
|
|
.dword _IF
|
|
.dword i1
|
|
e1: .dword RDROP
|
|
EXIT
|
|
i1: .dword INCR
|
|
JUMP l1
|
|
eword
|
|
|
|
; H: ( char [text<char>] -- addr ) Parse text from input stream delimited by char, return
|
|
; H: address of WORD buffer containing packed string.
|
|
dword WORD,"WORD"
|
|
ENTER
|
|
.dword PARSE
|
|
.dword DUP
|
|
ONLIT word_buf_size
|
|
.dword ULT
|
|
.dword _IF
|
|
.dword bad
|
|
.dword WORDBUF
|
|
.dword PACK
|
|
EXIT
|
|
bad: ONLIT -18
|
|
.dword THROW
|
|
eword
|
|
|
|
; H: ( [word< >] -- char ) Parse word from input stream, return value of first char.
|
|
dword CHAR,"CHAR"
|
|
ENTER
|
|
do: .dword PARSE_WORD
|
|
.dword DROP
|
|
.dword CFETCH
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( [word< >] -- char ) Immediately perform CHAR and compile literal.
|
|
dword CCHAR,"[CHAR]",F_IMMED|F_CONLY
|
|
ENTER
|
|
do: .dword CHAR
|
|
.dword LITERAL
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( [word< >] -- char ) Perform either CHAR or [CHAR] per the current compile state.
|
|
dword ASCII,"ASCII",F_IMMED
|
|
ENTER
|
|
.dword _SMART
|
|
.dword CHAR::do
|
|
JUMP CCHAR::do
|
|
eword
|
|
|
|
; H: ( [text<)>] -- ) Parse and discard text until a right paren or end of input.
|
|
dword LPAREN,"(",F_IMMED
|
|
ENTER
|
|
ONLIT ')'
|
|
.dword PARSE
|
|
.dword TWODROP
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( [text<)>] -- ) Parse text until a right paren or end of input, output text.
|
|
dword DOTPAREN,".(",F_IMMED
|
|
ENTER
|
|
ONLIT ')'
|
|
.dword PARSE
|
|
.dword TYPE
|
|
EXIT
|
|
eword
|
|
|
|
; Helper to compile a string
|
|
; ( addr u -- )
|
|
hword CSTRING,"CSTRING"
|
|
jsr _2parm
|
|
ldy #.loword(docs-1)
|
|
lda #.hiword(docs-1)
|
|
jsr _str_op_ays
|
|
NEXT
|
|
docs: jsr _cbytea
|
|
clc
|
|
rtl
|
|
eword
|
|
|
|
; H: Compiling: ( addr1 u -- ) compile string literal into current def
|
|
; H: Execution: ( -- addr2 u ) return compiled string
|
|
dword SLITERAL,"SLITERAL",F_IMMED|F_CONLY
|
|
ENTER
|
|
.dword _COMP_LIT
|
|
.dword _SLIT
|
|
.dword DUP
|
|
.dword COMPILECOMMA
|
|
.dword CSTRING
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( [text<">] -- addr u )
|
|
dwordq SQ,"S'",F_IMMED
|
|
ENTER
|
|
ONLIT '"'
|
|
.dword PARSE
|
|
.dword _SMART
|
|
.dword interp
|
|
.dword SLITERAL
|
|
EXIT
|
|
interp: .dword dTMPSTR
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( [text<">] -- ) Parse text and output.
|
|
dwordq DOTQ,".'",F_IMMED
|
|
ENTER
|
|
.dword SQ
|
|
.dword _SMART
|
|
.dword interp
|
|
.dword _COMP_LIT
|
|
interp: .dword TYPE
|
|
EXIT
|
|
eword
|
|
|
|
; parse paired hex digits until right paren
|
|
; return string in buffer created by alloc-mem
|
|
; H: ( [text<)>] -- addr u ) Parse hex digits, return in allocated string.
|
|
dword dHEXP,"$HEX(",F_IMMED
|
|
ENTER
|
|
ONLIT 256
|
|
.dword ALLOC
|
|
ONLIT ')'
|
|
.dword PARSE
|
|
CODE
|
|
jsr _popxr ; length of parsed string
|
|
jsr _popwr ; address of parsed string
|
|
jsr _popyr ; address of allocated buffer
|
|
stz XR+2 ; will count how many digits we have stuffed
|
|
ldy #$00 ; will count the source chars processed
|
|
lp: cpy XR
|
|
beq done
|
|
sep #SHORT_A
|
|
lda [WR],y
|
|
rep #SHORT_A
|
|
and #$FF
|
|
jsr _c_to_d
|
|
bcc next ; invalid digit
|
|
cmp #$10
|
|
bcs next ; bigger than a hex digit
|
|
phy ; save index
|
|
pha ; save digit
|
|
lda XR+2
|
|
inc XR+2
|
|
lsr
|
|
tay
|
|
pla
|
|
bcc store ; even digits (from 0) just need to store
|
|
odd: sep #SHORT_A ; odd digits shift into the low nibble
|
|
asl ; C 000d => 00d0
|
|
asl
|
|
asl
|
|
asl
|
|
xba ; C 00d0 => d000
|
|
lda [YR],y ; C d000 => d00e
|
|
xba ; C d00e => 0ed0
|
|
rep #SHORT_A
|
|
lsr
|
|
lsr
|
|
lsr
|
|
lsr
|
|
store: sep #SHORT_A
|
|
sta [YR],y
|
|
rep #SHORT_A
|
|
ply ; get counter back
|
|
next: iny
|
|
bra lp
|
|
done: ldy YR
|
|
lda YR+2
|
|
jsr _pushay
|
|
lda XR+2 ; # of digits
|
|
lsr ; convert to # chars
|
|
adc #$00 ; if odd, round up
|
|
tay
|
|
lda #$00
|
|
PUSHNEXT
|
|
eword
|
|
|
|
; ( addr1 u1 addr2 u2 -- addr1 u1+u2 ) Concatenate strings.
|
|
; addr1 is assumed to have enough room for the string
|
|
hword SCONCAT,"SCONCAT"
|
|
jsr _4parm
|
|
lda STACKBASE+12,x ; get c-addr1+u1 to YR
|
|
clc
|
|
adc STACKBASE+8,x
|
|
sta YR
|
|
lda STACKBASE+14,x
|
|
adc STACKBASE+10,x
|
|
sta YR+2
|
|
jsr _popxr ; u2 to xr
|
|
jsr _popwr ; c-addr2 to WR
|
|
lda XR
|
|
clc
|
|
adc STACKBASE+0,x ; make u1+u2
|
|
sta STACKBASE+0,x
|
|
lda XR+2
|
|
adc STACKBASE+2,x
|
|
sta STACKBASE+2,x
|
|
sec ; move down is faster
|
|
jsr _memmove_c ; move the string
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( [text<">] -- c-addr u ) Parse text in input buffer, copy to allocated string.
|
|
dwordq ASTR,"A'"
|
|
ENTER
|
|
ONLIT '"'
|
|
.dword PARSE
|
|
.dword DUP
|
|
.dword ALLOC
|
|
.dword ZERO
|
|
.dword TWOSWAP
|
|
.dword SCONCAT
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( addr1 u1 addr2 u2 -- addr3 u1+u2 ) Concatenate allocated strings,
|
|
; H: freeing the originals.
|
|
; Concatenate two strings that are in memory returned by ALLOC-MEM
|
|
; returning a string allocated via ALLOC-MEM and the original strings
|
|
; freed via FREE-MEM
|
|
dword ACONCAT,"ACONCAT"
|
|
ENTER
|
|
.dword TWOPtoR ; ( c-addr1 u1 c-addr2 u2 -- c-addr u1 ) save second string
|
|
.dword DUP ; ( ... c-addr1 u1 u1' ) copy u1
|
|
.dword RCOPY ; ( ... c-addr1 u1 u1' u2' ) get a copy of u2
|
|
.dword PLUS ; ( ... c-addr1 u1 u3 )sum them to get u1+u2
|
|
.dword ALLOC ; ( ... c-addr1 u1 c-addr3 ) allocate that many
|
|
ONLIT 0 ; ( ... c-addr1 u1 c-addr3 0 ) say it's zero length
|
|
.dword TWOSWAP ; ( ... c-addr3 0 c-addr1 u1 ) put it at the front
|
|
.dword OVER ; ( ... c-addr3 0 c-addr1 u1 c-addr1' )copy c-addr1
|
|
.dword PtoR ; ( ... c-addr3 0 c-addr1 u1 ) save for FREE-MEM
|
|
.dword SCONCAT ; ( ... c-addr3 u1 ) copy first string
|
|
.dword RtoP ; ( ... c-addr3 u1 c-addr1 )
|
|
ONLIT 0
|
|
.dword FREE ; ( ... c-addr3 u1 ) free it
|
|
.dword TWORtoP ; ( ... c-addr3 u1 c-addr2 u2 )
|
|
.dword OVER ; ( ... c-addr3 u1 c-addr2 u2 c-addr2' )
|
|
.dword PtoR ; ( ... c-addr3 u1 c-addr2 u2 )
|
|
.dword SCONCAT ; ( ... c-addr3 u1+u2 )
|
|
.dword RtoP ; ( ... c-addr3 u1+u2 c-addr2 )
|
|
ONLIT 0
|
|
.dword FREE ; ( ... c-addr3 u1+u2 )
|
|
EXIT
|
|
eword
|
|
|
|
; H: Compiling: ( [text<">] -- ) Parse string, including IEEE 1275-1994 hex interpolation.
|
|
; H: Execution: ( -- addr u ) Return parsed string.
|
|
dwordq QUOTE,"'",F_IMMED
|
|
ENTER
|
|
.dword ZERO ; ( -- 0 )
|
|
.dword ALLOC ; ( 0 -- c-addr1 ) empty allocation
|
|
.dword ZERO ; ( c-addr1 -- c-addr1 0 )
|
|
moretext: .dword ASTR ; ( c-addr1 u1 -- c-addr1 u1 c-addr2 u2 )
|
|
.dword ACONCAT ; ( ... c-addr3 u3 )
|
|
.dword INQ ; ( ... c-addr3 u3 f )
|
|
.dword _IF ; ( ... c-addr3 u3 )
|
|
.dword finish ; no more text to parse, finish up
|
|
.dword GETCH ; ( ... c-addr3 u3 c )
|
|
.dword DUP ; ( ... c-addr3 u3 c c' )
|
|
.dword ISSPC ; ( ... c-addr3 u3 c f )
|
|
.dword _IFFALSE ; ( ... c-addr3 u3 c )
|
|
.dword space ; is a space, drop space and return string
|
|
ONLIT '(' ; ( ... c-addr3 u3 c '(' )
|
|
.dword EQUAL ; ( ... c-addr3 u3 f )
|
|
.dword _IF ; ( ... c-addr3 u3 )
|
|
.dword finish ; finish, but we will probably error later in parsing
|
|
.dword dHEXP ; ( ... c-addr3 u3 c-addr4 u4 )
|
|
.dword ACONCAT ; ( ... c-addr5 u5 )
|
|
JUMP moretext ; and switch back to parsing quoted string
|
|
space: .dword DROP
|
|
finish: .dword OVER ; ( c-addr3 u3 -- c-addr3 u3 c-addr3' )
|
|
.dword PtoR ; ( ... c-addr3 u3 ) ( R: -- c-addr3' )
|
|
.dword _SMART
|
|
.dword interp
|
|
.dword SLITERAL ; ( c-addr3 u3 -- )
|
|
JUMP done
|
|
interp: .dword dTMPSTR
|
|
done: .dword RtoP ; ( -- c-addr3' ) ( R: c-addr3' -- )
|
|
ONLIT 0
|
|
.dword FREE ; ( c-addr3' -- )
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( -- ) Compile code to compile the immediately following word which must resolve to an xt.
|
|
; H: Better to use POSTPONE in most cases.
|
|
dword COMPILE,"COMPILE",F_IMMED|F_CONLY
|
|
ENTER
|
|
.dword _COMP_LIT ; Compile a _COMP_LIT
|
|
.dword _COMP_LIT
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( [name< >] -- ) Compile name now. Better to use POSTPONE.
|
|
dword ICOMPILE,"[COMPILE]",F_IMMED
|
|
ENTER
|
|
.dword PARSEFIND
|
|
.dword COMPILECOMMA
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( [name< >] -- ) Compile the compilation semantics of name.
|
|
; Basically, if the word is immediate, compile its xt
|
|
; If not, compile code that compiles its xt
|
|
dword POSTPONE,"POSTPONE",F_IMMED
|
|
ENTER
|
|
.dword PARSE_WORD
|
|
.dword SEARCH_ALL
|
|
.dword QDUP
|
|
.dword _IF
|
|
.dword exc
|
|
.dword ZEROLT
|
|
.dword _IF
|
|
.dword immed ; if >0, it is an IMMEDIATE word, go compile xt
|
|
.dword LITERAL ; compile its xt as a literal
|
|
.dword _COMP_LIT ; and compile COMPILE,
|
|
immed: .dword COMPILECOMMA
|
|
EXIT
|
|
exc: ONLIT -13
|
|
.dword THROW
|
|
eword
|
|
|
|
; H: ( -- ) Output the words in the CONTEXT wordlist.
|
|
dword WORDS,"WORDS"
|
|
ENTER
|
|
.dword CONTEXT
|
|
.dword FETCH
|
|
.dword FETCH
|
|
lp: .dword DUP ; ( h -- h h )
|
|
.dword _IF ; ( h h -- h )
|
|
.dword done
|
|
.dword DUP ; ( h -- h h )
|
|
.dword drXT ; ( h -- h xt )
|
|
.dword DUP ; ( h xt -- h xt xt )
|
|
.dword UDOT ; ( h xt xt -- h xt )
|
|
.dword rNAME ; ( h xt -- h c-addr u )
|
|
.dword TYPE ; ( h c-addr u -- h )
|
|
.dword CR
|
|
.dword EXITQ ; ( h -- h f )
|
|
.dword _IFFALSE
|
|
.dword done
|
|
.dword FETCH ; ( h -- h' )
|
|
JUMP lp
|
|
done: .dword DROP ; ( h -- )
|
|
EXIT
|
|
eword
|
|
|
|
.if include_see
|
|
; H: ( xt -- ) Attempt to decompile the word at xt.
|
|
dword dSEE,"(SEE)"
|
|
ENTER
|
|
.dword QDUP
|
|
.dword _IF
|
|
.dword notxt
|
|
SLIT "Flags: " ; ( xt -- xt str len )
|
|
.dword TYPE ; ( str len -- )
|
|
.dword DUP ; ( xt -- xt xt' )
|
|
.dword CFETCH ; ( xt xt' -- xt u )
|
|
.dword UDOT ; ( xt u -- xt )
|
|
.dword CR
|
|
.dword DUP ; ( xt -- xt xt' )
|
|
.dword rNAME ; ( xt xt' -- xt str len )
|
|
.dword ROT ; ( xt str len -- str len xt )
|
|
.dword INCR ; ( str len xt -- str len a-addr )
|
|
.dword DUP ; ( ... str len a-addr a-addr' )
|
|
.dword FETCH ; ( ... str len a-addr u )
|
|
ONLIT (_enter << 8)+opJSL ; ( ... str len a-addr u x )
|
|
.dword EQUAL ; ( ... str len a-addr f )
|
|
.dword _IF ; ( ... str len a-addr )
|
|
.dword cant
|
|
ONLIT ':' ; ( ... str len a-addr ':' )
|
|
.dword EMIT ; ( ... str len a-addr )
|
|
.dword SPACE
|
|
.dword NROT ; ( ... a-addr str len )
|
|
.dword TYPE ; ( ... a-addr )
|
|
.dword CR
|
|
lp: .dword CELLPLUS ; ( a-addr(old) -- a-addr )
|
|
.dword DUP ; ( ... a-addr a-addr' )
|
|
.dword FETCH ; ( ... a-addr u )
|
|
ONLIT _exit_next-1
|
|
.dword _IFEQUAL
|
|
.dword :+
|
|
.dword DROP
|
|
ONLIT ';'
|
|
.dword EMIT
|
|
quit: .dword DROP
|
|
notxt: EXIT
|
|
: .dword OVER ; ( ... a-addr u a-addr' )
|
|
.dword UDOT ; ( ... a-addr u )
|
|
ONLIT _LIT
|
|
.dword _IFEQUAL
|
|
.dword :+
|
|
.dword DROP
|
|
.dword CELLPLUS
|
|
.dword DUP
|
|
.dword FETCH
|
|
.dword DOT
|
|
JUMP crlp
|
|
: ONLIT _WLIT
|
|
.dword _IFEQUAL
|
|
.dword :+
|
|
.dword DROP
|
|
.dword CELLPLUS
|
|
.dword DUP
|
|
.dword WFETCH
|
|
.dword DOT
|
|
.dword TWODECR
|
|
JUMP crlp
|
|
: ONLIT _CLIT
|
|
.dword _IFEQUAL
|
|
.dword :+
|
|
.dword DROP
|
|
.dword CELLPLUS
|
|
.dword DUP
|
|
.dword CFETCH
|
|
.dword DOT
|
|
.dword THREE
|
|
.dword MINUS
|
|
JUMP crlp
|
|
: ONLIT _SLIT
|
|
.dword _IFEQUAL
|
|
.dword :+
|
|
.dword DROP ; ( ... a-addr )
|
|
.dword CELLPLUS ; skip _SLIT
|
|
.dword DUP
|
|
.dword FETCH ; ( ... a-addr len ) get length of string
|
|
.dword SWAP ; ( ... len a-addr )
|
|
;.dword CELLPLUS ; ( ... len a-addr )
|
|
.dword TWODUP ; ( ... len a-addr len a-addr )
|
|
.dword CELLPLUS
|
|
.dword SWAP ; ( ... len a-addr a-addr len )
|
|
ONLIT '"'
|
|
.dword EMIT
|
|
.dword TYPE ; ( ... len a-addr )
|
|
ONLIT '"'
|
|
.dword EMIT
|
|
.dword PLUS
|
|
JUMP crlp
|
|
: .dword rNAME ; ( ... a-addr str len )
|
|
.dword TYPE ; ( ... a-addr )
|
|
crlp: .dword CR
|
|
.dword EXITQ ; ( ... a-addr f )
|
|
.dword _IFFALSE ; ( ... a-addr )
|
|
.dword quit
|
|
JUMP lp
|
|
cant: .dword DROP ; drop pointer
|
|
SLIT "Can't see "
|
|
.dword TYPE
|
|
.dword TYPE
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( [text< >] -- ) Attempt to decompile name.
|
|
dword SEE,"SEE"
|
|
ENTER
|
|
.dword PARSEFIND
|
|
.dword dSEE
|
|
EXIT
|
|
eword
|
|
.endif
|
|
|
|
; H: ( addr u -- ) Like CREATE but use addr u for name.
|
|
dword dCREATE,"$CREATE"
|
|
jsr _mkentry
|
|
docreate: ldy #.loword(_pushda)
|
|
lda #.hiword(_pushda)
|
|
jsr _cjsl
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( [name< >] -- ) Create a definition, when executed pushes the body address.
|
|
dword CREATE,"CREATE"
|
|
ENTER
|
|
.dword PARSE_WORD
|
|
.dword dCREATE
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( [name< >] -- ) Execute CREATE name and allocate one cell, initially a zero.
|
|
dword VARIABLE,"VARIABLE"
|
|
ENTER
|
|
.dword CREATE
|
|
.dword ZERO
|
|
.dword COMMA
|
|
EXIT
|
|
eword
|
|
|
|
; action of DOES
|
|
; modify the most recent definition (CREATED) to jsl to the address immediately
|
|
; following whoever JSLed to this and return to caller
|
|
.proc _does
|
|
ENTER
|
|
.dword LAST
|
|
.dword drXT
|
|
.dword INCR
|
|
CODE
|
|
jsr _popyr
|
|
pla
|
|
sta WR
|
|
sep #SHORT_A
|
|
pla
|
|
rep #SHORT_A
|
|
and #$00FF
|
|
sta WR+2
|
|
jsr _incwr
|
|
ldy #$00
|
|
lda [YR],y
|
|
and #$00FF
|
|
cmp #opJSL
|
|
bne csmm
|
|
lda WR
|
|
iny
|
|
sta [YR],y
|
|
lda WR+2
|
|
iny
|
|
iny
|
|
sep #SHORT_A
|
|
sta [YR],y
|
|
rep #SHORT_A
|
|
NEXT
|
|
csmm: jmp _CONTROL_MM::code
|
|
.endproc
|
|
|
|
; H: ( -- ) alter execution semantics of most recently-CREATEd definition to
|
|
; H: perform the execution semantics of the code following DOES>.
|
|
dword DOES,"DOES>",F_IMMED|F_CONLY
|
|
ENTER
|
|
.dword SEMIS
|
|
.dword _COMP_LIT
|
|
jsl f:_does ; better be 4 bytes! (hint: it is)
|
|
.dword _COMP_LIT
|
|
ENTER ; not really, now
|
|
.dword _COMP_LIT
|
|
.dword RPLUCKADDR
|
|
.dword _COMP_LIT
|
|
.dword INCR
|
|
.dword STATEC ; ensure still in compiling state
|
|
EXIT
|
|
eword
|
|
|
|
; ( -- ) throw exception -13
|
|
hword dUNDEFERRED,"$UNDEFERRED"
|
|
ldy #.loword(-13)
|
|
lda #.hiword(-13)
|
|
jmp _throway
|
|
eword
|
|
|
|
; ( xt addr u -- ) Create a deferred word with xt as its initial behavior.
|
|
hword dDEFER,"$DEFER"
|
|
jsr _3parm
|
|
jsr _mkentry
|
|
dodefer: ldy #.loword(_deferred)
|
|
lda #.hiword(_deferred)
|
|
jsr _cjsl
|
|
jsr _popay
|
|
jsr _ccellay
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( [name< >] -- ) Create definition that executes the first word of the body as an xt.
|
|
dword DEFER,"DEFER"
|
|
ENTER
|
|
NLIT dUNDEFERRED
|
|
.dword PARSE_WORD
|
|
.dword dDEFER
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( xt -- ) Return the first cell of the body of word at xt, normally a DEFER word
|
|
; H: but will do the same on some other types of words (CREATE, VARIABLE, VALUE, etc).
|
|
dword BEHAVIOR,"BEHAVIOR"
|
|
ENTER
|
|
.dword rBODY
|
|
.dword FETCH
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( addr u xt -- ) Create a DEFER definition for string with xt as its initial behavior.
|
|
dword IS_USER_WORD,"(IS-USER-WORD)"
|
|
ENTER
|
|
.dword NROT ; reorder for $DEFER
|
|
.dword dDEFER
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( n addr u -- ) Create a definition that pushes the first cell of the body, initially n.
|
|
dword dVALUE,"$VALUE"
|
|
jsr _3parm ; avoid dictionary corruption from stack underflow
|
|
jsr _mkentry
|
|
dovalue: ldy #.loword(_pushvalue)
|
|
lda #.hiword(_pushvalue)
|
|
jsr _cjsl
|
|
jsr _popay
|
|
jsr _ccellay
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( n1 n2 addr u -- ) Create a definition that pushes the first two cells of the body.
|
|
; H: initially n1 and n2
|
|
dword dTWOVALUE,"$2VALUE"
|
|
jsr _4parm ; avoid dictionary corruption from stack underflow
|
|
jsr _mkentry
|
|
ldy #.loword(_push2value)
|
|
lda #.hiword(_push2value)
|
|
jsr _cjsl
|
|
jsr _popay
|
|
jsr _ccellay
|
|
jsr _popay
|
|
jsr _ccellay
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( n [name< >] -- ) Create a definition that pushes n on the stack,
|
|
; H: n can be changed with TO.
|
|
dword VALUE,"VALUE"
|
|
ENTER
|
|
.dword PARSE_WORD
|
|
.dword dVALUE
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( n1 n2 [name< >] -- ) Create a definition that pushes n1 and n2 on the stack,
|
|
; H: n1 and n2 can be changed with TO.
|
|
dword TWOVALUE,"2VALUE"
|
|
ENTER
|
|
.dword PARSE_WORD
|
|
.dword dTWOVALUE
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( n [name< >] -- ) Allocate n bytes of memory, create definition that
|
|
; H: returns the address of the allocated memory.
|
|
dword BUFFERC,"BUFFER:"
|
|
ENTER
|
|
.dword ALLOC
|
|
.dword VALUE
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( n [name< >] -- ) alias of VALUE, OF816 doesn't have true constants
|
|
; we don't have real constants, they can be modified with TO
|
|
dword CONSTANT,"CONSTANT"
|
|
bra VALUE::code
|
|
eword
|
|
|
|
; FCode support, these are needed to support the INSTANCE feature when it is installed
|
|
; and so are included in the main dictionary. By default the FCodes for b(value),
|
|
; b(buffer), b(variable), and b(defer) point to these. When the INSTANCE feature
|
|
; is installed, it will call set-token to replace these, but will still need to call them
|
|
; in the case that INSTANCE was not used.
|
|
.if include_fcode
|
|
; ( -- ) compile the machine execution semantics of CREATE (jsl _pushda)
|
|
hword pCREATE,"%CREATE" ; noindex
|
|
jmp dCREATE::docreate
|
|
eword
|
|
|
|
; H: ( n -- ) Compile the machine execution semantics of VALUE (jsl _pushvalue)
|
|
; H: and the value.
|
|
dword pVALUE,"%VALUE" ; noindex
|
|
jsr _1parm
|
|
jmp dVALUE::dovalue
|
|
eword
|
|
|
|
; H: ( addr -- ) Compile the machine execution semantics of BUFFER (jsl _pushvalue)
|
|
; H: and the buffer address.
|
|
dword pBUFFER,"%BUFFER" ; noindex
|
|
ENTER
|
|
.dword ALLOC
|
|
.dword pVALUE
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( -- ) Compile the machine execution semantics of CREATE (jsl _pushda)
|
|
; H: and compile a zero.
|
|
dword pVARIABLE,"%VARIABLE" ; noindex
|
|
ENTER
|
|
.dword pCREATE
|
|
.dword ZERO
|
|
.dword COMMA
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( -- ) Compile the machine execution semantics of DEFER (jsl _deferred).
|
|
dword pDEFER,"%DEFER" ; noindex
|
|
ldy #.loword(dUNDEFERRED)
|
|
lda #.hiword(dUNDEFERRED)
|
|
jsr _pushay
|
|
jmp dDEFER::dodefer
|
|
eword
|
|
.endif
|
|
|
|
; H: ( n1 n2 [name< >] -- ) Create name, name does ( -- n1 n2 ) when executed.
|
|
dword TWOCONSTANT,"2CONSTANT"
|
|
ENTER
|
|
.dword PARSE_WORD
|
|
.dword dTWOVALUE
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( [name1< >] [name2< >] -- ) create name1, name1 is a synonym for name2
|
|
dword ALIAS,"ALIAS"
|
|
ENTER
|
|
.dword PARSE_WORD
|
|
.dword PARSEFIND
|
|
.dword INCR
|
|
.dword NROT
|
|
CODE
|
|
jsr _mkentry
|
|
jsr _popay
|
|
jsr _cjml
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( n xt | n1 n2 xt -- ) change the first cell or two of the body of xt
|
|
; H: if xt is a 2VALUE, change the first two cells of the body
|
|
; H: if xt is any other created word, change the first cell of the body
|
|
dword _TO,"(TO)"
|
|
ENTER
|
|
.dword DUP
|
|
.dword INCR
|
|
.dword FETCH
|
|
ONLIT (_push2value << 8) | opJSL
|
|
.dword EQUAL
|
|
.dword _IF
|
|
.dword just1
|
|
.dword rBODY
|
|
.dword TUCK
|
|
.dword STORE
|
|
.dword CELLPLUS
|
|
.dword _SKIP
|
|
just1: .dword rBODY
|
|
.dword STORE
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( n [name< >] -- ) Change the first cell of the body of xt to n. Can be used on
|
|
; H: most words created with CREATE, DEFER, VALUE, etc. (even VARIABLE).
|
|
dword TO,"TO",F_IMMED
|
|
ENTER
|
|
.dword PARSEFIND
|
|
doto: .dword _SMART
|
|
.dword setval
|
|
.dword LITERAL
|
|
.dword _COMP_LIT
|
|
setval: .dword _TO
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( -- 0 )
|
|
dword STRUCT,"STRUCT"
|
|
lda #$0000
|
|
tay
|
|
PUSHNEXT
|
|
eword
|
|
|
|
; ( offset size addr u -- offset+size ) create word specified by addr u with
|
|
; execution semantics: ( addr -- addr+offset)
|
|
hword dFIELD,"$FIELD"
|
|
jsr _4parm
|
|
jsr _mkentry
|
|
dofield: ldy #.loword(_field)
|
|
lda #.hiword(_field)
|
|
jsr _cjsl
|
|
ldy STACKBASE+4,x
|
|
lda STACKBASE+6,x
|
|
jsr _ccellay
|
|
lda STACKBASE+0,x
|
|
clc
|
|
adc STACKBASE+4,x
|
|
sta STACKBASE+4,x
|
|
lda STACKBASE+2,x
|
|
adc STACKBASE+6,x
|
|
sta STACKBASE+6,x
|
|
jsr _stackincr
|
|
NEXT
|
|
eword
|
|
|
|
; H: Compilation: ( offset size [name< >] -- offset+size ) create name
|
|
; H: Execution of name: ( addr -- addr+offset)
|
|
dword FIELD,"FIELD"
|
|
ENTER
|
|
.dword PARSE_WORD
|
|
.dword dFIELD
|
|
EXIT
|
|
eword
|
|
|
|
; ( str len -- xt ) define word with empty execution semantics
|
|
hword dDEFWORD,"$DEFWORD"
|
|
ldy #SV_OLDHERE
|
|
lda DHERE
|
|
sta [SYSVARS],y
|
|
iny
|
|
iny
|
|
lda DHERE+2
|
|
sta [SYSVARS],y
|
|
jsr _mkentry
|
|
jsr _pushay ; flags/XT
|
|
NEXT
|
|
eword
|
|
|
|
; ( -- ) compile colon definition execution semantics (JSL _enter)
|
|
hword dCOLON,"$COLON"
|
|
ldy #.loword(_enter)
|
|
lda #.hiword(_enter)
|
|
jsr _cjsl
|
|
NEXT
|
|
eword
|
|
|
|
; ( xt -- ) hide visibility of definition at xt
|
|
hword SMUDGE,"SMUDGE"
|
|
ENTER
|
|
.dword DUP ; dup XT (flags addr)
|
|
.dword CFETCH ; so we can smudge it
|
|
ONLIT F_SMUDG
|
|
.dword LOR
|
|
.dword SWAP
|
|
.dword CSTORE
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( [name< >] -- colon-sys ) Parse name, start colon definition and enter compiling state.
|
|
dword COLON,":"
|
|
ENTER
|
|
.dword PARSE_WORD
|
|
.dword dDEFWORD
|
|
.dword dCOLON
|
|
.dword DUP ; one for setting flags, one for colon-sys
|
|
.dword SMUDGE
|
|
.dword DUP ; and one for RECURSE
|
|
.dword dCURDEF
|
|
.dword STORE
|
|
.dword STATEC
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( -- colon-sys ) Create an anonymous colon definition and enter compiling state.
|
|
; H: The xt of the anonymous definition is left on the stack after ;.
|
|
dword NONAME,":NONAME"
|
|
ENTER
|
|
ONLIT $80 ; name length is 0 for noname
|
|
.dword CCOMMA
|
|
.dword HERE ; XT/flags
|
|
.dword DUP ; one for user, one for colon-sys
|
|
.dword DUP ; and one for RECURSE
|
|
.dword dCURDEF
|
|
.dword STORE
|
|
ONLIT $00 ; noname flags
|
|
.dword CCOMMA
|
|
.dword STATEC
|
|
.dword dCOLON
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( -- colon-sys ) Create a temporary anonymous colon definition and enter
|
|
; H: compiling state. The temporary definition is executed immediately after ;.
|
|
; word supporting temporary colon definitions to implement IEEE 1275
|
|
; words that are extended to run in interpretation state
|
|
dword dTEMPCOLON,":TEMP"
|
|
ENTER
|
|
;SLIT "Starting temp def... "
|
|
;.dword TYPE
|
|
ONLIT max_tempdef ; allocate 128 cells worth of tempdef
|
|
.dword ALLOC
|
|
.dword DUP
|
|
.dword dTMPDEF ; and save its allocation
|
|
.dword STORE
|
|
.dword HERE ; save HERE
|
|
.dword dSAVEHERE
|
|
.dword STORE
|
|
.dword toHERE ; and then set it to the temp def allocation
|
|
.dword NONAME ; start anonymous definition
|
|
.dword DEPTH ; save stack depth (data stack is control stack)
|
|
.dword dCSDEPTH
|
|
.dword STORE
|
|
done: EXIT
|
|
eword
|
|
|
|
; word to end temporary colon definition and run it
|
|
; called whenever control-flow-ending words are executed
|
|
; and a temporary definition is open
|
|
; ( xt xt' -- )
|
|
hword dTEMPSEMIQ,"$;TEMP?",F_IMMED|F_CONLY
|
|
ENTER
|
|
.dword dTMPDEF ; ( -- a-addr ) first see if we are in a temp def
|
|
.dword FETCH ; ( a-addr -- x ) 0 if not in temp def
|
|
.dword _IF ; ( x -- )
|
|
.dword notmp ; if not in temp def
|
|
dosemi: .dword DEPTH ; ( -- u1 ) next see if the stack depth matches
|
|
.dword dCSDEPTH ; ( u1 -- u1 c-addr1 ) verify stack depth is what it should be
|
|
.dword FETCH ; ( u1 c-addr1 -- u1 u2 )
|
|
.dword ULTE ; ( u1 u2 -- f ) is less than or equal to?
|
|
.dword _IFFALSE ; ( f -- )
|
|
.dword tmpdone ; true branch, finish up temp def
|
|
notmp: EXIT
|
|
tmpdone: ;SLIT "Ending temp def... "
|
|
;.dword TYPE
|
|
.dword DEPTH ; ( -- u1 )
|
|
.dword dCSDEPTH ; ( u1 -- u1 c-addr1 ) verify stack depth is what it should be
|
|
.dword FETCH ; ( u1 c-addr1 -- u1 u2 )
|
|
;.dword DOTS
|
|
.dword EQUAL ; ( u1 u2 -- f )
|
|
.dword _IF ; ( f -- )
|
|
.dword csmm ; if not, we have a problem
|
|
.dword _COMP_LIT ; compile EXIT into temporary def
|
|
EXIT ; NOTE: not really EXITing here
|
|
.dword STATEI ; ( -- )
|
|
.dword dSAVEHERE ; ( -- a-addr ) restore HERE
|
|
.dword FETCH ; ( a-addr -- c-addr )
|
|
.dword toHERE ; ( c-addr -- )
|
|
.dword dTMPDEF ; ( -- a-addr ) get location of temporary definition
|
|
.dword DUP ; ( -- a-addr a-addr' ) one for FREE, one to write zero into it
|
|
.dword FETCH ; ( a-addr a-addr' -- a-addr c-addr )
|
|
.dword PtoR ; ( a-addr c-addr -- a-addr ) ( R: -- c-addr ) safe for FREE
|
|
.dword OFF ; ( a-addr -- ) zero $TEMPDEF
|
|
.dword DROP ; ( xt xt -- xt ) now we worry about ( xt xt ) consume colon-sys
|
|
.dword CATCH ; ( xt -- * r ) execute the temporary definition within catch
|
|
.dword RtoP ; ( r -- r c-addr ) ( R: c-addr -- )
|
|
dofree: ONLIT max_tempdef ; ( r c-addr -- r c-addr u )
|
|
.dword FREE ; ( r c-addr u -- r )
|
|
.dword THROW ; ( r -- ) re-throw any error in temp def
|
|
EXIT ; this really is an exit
|
|
csmm: .dword STATEI ; ( -- )
|
|
.dword dSAVEHERE ; ( -- a-addr ) restore HERE
|
|
.dword FETCH ; ( a-addr -- c-addr )
|
|
.dword toHERE ; ( c-addr -- )
|
|
ONLIT -22 ; ( -- -22 ) will be thrown
|
|
.dword dTMPDEF ; ( -22 -- -22 c-addr )
|
|
JUMP dofree ; note that thrown error will clean up dTMPDEF
|
|
eword
|
|
|
|
; ( xt -- ) make definition at xt visible
|
|
hword UNSMUDGE,"UNSMUDGE"
|
|
ENTER
|
|
.dword DUP ; dup XT (flags addr)
|
|
.dword CFETCH ; so we can unsmudge it
|
|
ONLIT F_SMUDG
|
|
.dword INVERT
|
|
.dword LAND
|
|
.dword SWAP
|
|
.dword CSTORE
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( colon-sys -- ) Consume colon-sys and enter interpretation state, ending the current
|
|
; H: definition. If the definition was temporary, execute it.
|
|
dword SEMI,";",F_IMMED|F_CONLY
|
|
ENTER
|
|
.dword dTMPDEF ; see if it's a temporary definition
|
|
.dword FETCH
|
|
.dword _IF
|
|
.dword :+
|
|
.dword dTEMPSEMIQ ; if it is, do that instead
|
|
EXIT
|
|
: .dword _COMP_LIT ; compile EXIT into current def
|
|
EXIT ; NOTE: not really EXITing here
|
|
dosemi: .dword UNSMUDGE ; consume colon-sys
|
|
.dword STATEI ; exit compilation state
|
|
ONLIT 0
|
|
.dword dOLDHERE
|
|
.dword STORE
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( -- ) Make the current definition findable during compilation.
|
|
dword RECURSIVE,"RECURSIVE",F_IMMED|F_CONLY
|
|
ENTER
|
|
.dword dCURDEF
|
|
.dword FETCH
|
|
.dword UNSMUDGE
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( -- ) Compile the execution semantics of the most current definition.
|
|
dword RECURSE,"RECURSE",F_IMMED|F_CONLY
|
|
ENTER
|
|
.dword dCURDEF
|
|
.dword FETCH
|
|
.dword COMPILECOMMA
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( [name< >] -- code-sys ) Create a new CODE definiion.
|
|
; TODO: activate ASSEMBLER words if available
|
|
dword CODEDEF,"CODE"
|
|
ENTER
|
|
.dword PARSE_WORD
|
|
.dword dDEFWORD
|
|
docode: .dword DUP ; one for setting flags, one for colon-sys
|
|
.dword SMUDGE
|
|
; .dword STATEC
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( [name< >] -- code-sys ) Create a new LABEL definition.
|
|
dword LABEL,"LABEL"
|
|
ENTER
|
|
.dword PARSE_WORD
|
|
.dword dCREATE
|
|
.dword LAST
|
|
.dword drXT
|
|
JUMP CODEDEF::docode
|
|
eword
|
|
|
|
; H: ( code-sys -- ) Consume code-sys, end CODE or LABEL definition.
|
|
dword CSEMI,"C;"
|
|
jsr _1parm
|
|
ldy #.loword(_next)
|
|
lda #.hiword(_next)
|
|
jsr _cjml
|
|
ENTER
|
|
JUMP SEMI::dosemi
|
|
eword
|
|
|
|
; H: ( code-sys -- ) Synonym for C;.
|
|
dword ENDCODE,"END-CODE",F_IMMED|F_CONLY
|
|
bra CSEMI::code
|
|
eword
|
|
|
|
; ( xt -- ) Mark XT as immediate.
|
|
hword dIMMEDIATE,"$IMMEDIATE"
|
|
ENTER
|
|
.dword DUP ; dup XT (flags addr)
|
|
.dword CFETCH
|
|
ONLIT F_IMMED
|
|
.dword LOR
|
|
.dword SWAP
|
|
.dword CSTORE
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( -- ) Mark last compiled word as an immediate word.
|
|
dword IMMEDIATE,"IMMEDIATE"
|
|
ENTER
|
|
.dword LAST
|
|
.dword drXT
|
|
.dword dIMMEDIATE
|
|
EXIT
|
|
eword
|
|
|
|
; ( xt -- ) Mark word at xt as protected (from FORGET, not MARKER).
|
|
hword dPROTECTED,"$PROTECTED"
|
|
ENTER
|
|
.dword DUP ; dup XT (flags addr)
|
|
.dword CFETCH
|
|
ONLIT F_PROT
|
|
.dword LOR
|
|
.dword SWAP
|
|
.dword CSTORE
|
|
EXIT
|
|
eword
|
|
|
|
; ( -- ) Mark last created word as protected (from FORGET, not MARKER).
|
|
hword PROTECTED,"PROTECTED"
|
|
ENTER
|
|
.dword LAST
|
|
.dword drXT
|
|
.dword dPROTECTED
|
|
EXIT
|
|
eword
|
|
|
|
; ( -- ) for DOES> and ;CODE
|
|
hword SEMIS,"SEMIS"
|
|
ENTER
|
|
.dword _COMP_LIT
|
|
CODE ; not really, see NOTE above
|
|
.dword RECURSIVE ; allow word to be found
|
|
EXIT
|
|
eword
|
|
|
|
; TODO attempt to activate assembler package
|
|
; H: ( -- ) End compiler mode, begin machine code section of definition.
|
|
dword SCODE,";CODE",F_IMMED|F_CONLY
|
|
bra SEMIS::code
|
|
eword
|
|
|
|
.if 0
|
|
; ANS Forth locals - half-baked and not usable yet
|
|
|
|
; ( u -- ) ( R: -- old_locals_ptr u*0 u2 )
|
|
; u2 = old SP after
|
|
hword dCREATE_LOCALS,"$CREATE-LOCALS"
|
|
lda locals_ptr ; current locals pointer (in stack)
|
|
pha ; save it
|
|
tsc ; current stack pointer (for fast cleanup)
|
|
sta WR ; save for now
|
|
jsr _popay ; get number of locals
|
|
lda #$0000 ; gonna zero them all out
|
|
lp: dey
|
|
bmi done
|
|
pha ; for each local, throw a cell on the stack
|
|
pha
|
|
bra lp
|
|
done: tsc ; now set up locals pointer to new block of locals
|
|
inc a ; 'cause '02 stack ptr is at the free byte
|
|
sta locals_ptr
|
|
lda WR
|
|
pha
|
|
NEXT
|
|
eword
|
|
|
|
; ( u -- ) ( R: u*n -- )
|
|
hword dDESTROY_LOCALS,"$DESTROY-LOCALS"
|
|
pla ; this is the old SP after saved locals poubter
|
|
tcs ; restore return stack
|
|
pla ; get old locals pointer
|
|
sta locals_ptr ; and make it current
|
|
eword
|
|
|
|
|
|
; ( u -- ) common routine to set up WR and Y register to access a local by number
|
|
.proc _localcom
|
|
lda locals_ptr ; get current locals pointer
|
|
sta WR ; set up WR to point to it
|
|
stz WR+2
|
|
jsr _popay ; get local number
|
|
tya ; and compute offset into locals
|
|
asl
|
|
tay
|
|
rts
|
|
.endproc
|
|
|
|
; ( u -- n ) fetch from local
|
|
hword dLOCALFETCH,"$LOCAL@"
|
|
jsr _localcom ; set up WR and Y reg
|
|
lda [WR],y ; low byte
|
|
pha ; save for now
|
|
iny ; move to high byte
|
|
iny
|
|
lda [WR],y ; get it
|
|
ply ; get low byte back
|
|
PUSHNEXT ; and toss on stack
|
|
eword
|
|
|
|
; ( n u -- )
|
|
hword dLOCALSTORE,"$LOCAL!"
|
|
jsr _swap ; get value to top
|
|
jsr _popay ; and put on return stack for now
|
|
pha
|
|
phy
|
|
jsr _localcom ; set up WR and Y reg
|
|
pla ; get low byte of value back
|
|
sta [WR],y ; store it
|
|
iny ; move to high byte
|
|
iny
|
|
pla ; get it back
|
|
sta [WR],y ; and store
|
|
NEXT
|
|
eword
|
|
|
|
.endif
|
|
|
|
.if enable_quotations
|
|
; Quotations enable syntax as follows:
|
|
; during compilation: [: ( -- quot-sys ) ... ;] ( quot-sys -- ) define a quotation
|
|
; (anonymous def within a definition)
|
|
; run time: ( -- xt ) leave xt of the quotation on the stack
|
|
; note that SEE cannot decode words with quotations.
|
|
; This implementation skips the quotation with AHEAD and afterwards leaves the
|
|
; the xt on the stack.
|
|
; quot-sys is ( -- old-$CURDEF forward-ref xt )
|
|
; H: Compilation: ( -- quot-sys ) Start a quotation.
|
|
; H: Execution: ( -- ) Skip over quotation code.
|
|
dword SQUOT,"[:",F_IMMED|F_CONLY
|
|
ENTER
|
|
.dword dCURDEF ; fix current def to quotation
|
|
.dword FETCH ; save current def for RECURSE
|
|
.dword AHEAD ; leaves address to resolve later
|
|
.dword NONAME ; start an anonymous definition
|
|
.dword DROP ; leave only one copy
|
|
EXIT
|
|
eword
|
|
|
|
; H: Compilation: ( quot-sys -- ) End a quotation.
|
|
; H: Execution: ( -- xt ) Leave xt of the quotation on the stack.
|
|
dword EQUOT,";]",F_IMMED|F_CONLY
|
|
ENTER
|
|
.dword _COMP_LIT ; compile EXIT into current def
|
|
EXIT ; NOTE: not really EXITing here
|
|
.dword SWAP ; put ahead target on top
|
|
.dword THEN ; resolve AHEAD
|
|
.dword LITERAL ; consume XT of word, place on stack at run-time
|
|
.dword dCURDEF ; restore current def to parent
|
|
.dword STORE ; and consume that
|
|
EXIT
|
|
eword
|
|
.endif
|
|
|
|
|
|
.if max_search_order > 0
|
|
|
|
; ( -- wid )
|
|
; ( root -- wid ) create a wordlist rooted at root
|
|
hword dCREATE_WL,"$CREATE-WL"
|
|
ENTER
|
|
.dword HERE ; WID
|
|
.dword SWAP
|
|
.dword COMMA ; compile pointer to root
|
|
.dword _COMP_LIT
|
|
.dword 0 ; pointer to xt of vocabulary def, none in this case
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( -- wid ) Create a new wordlist.
|
|
; wordlists are allocated from the dictionary space, containing two cells
|
|
; the first being the last word defined in the wordlist, and the second containing
|
|
; an xt to an associated vocabulary definition if one has been defined
|
|
; the wid is the pointer to the first cell
|
|
dword WORDLIST,"WORDLIST"
|
|
ENTER
|
|
ONLIT H_FORTH ; root of all dictionaries
|
|
.dword dCREATE_WL
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( -- wid ) Create a new empty wordlist (danger!).
|
|
; non-standard method to create a completely empty wordlist. If this is the only
|
|
; list in the search order, it may be impossible to get out of the situation
|
|
dword dEMPTY_WL,"$EMPTY-WL"
|
|
ENTER
|
|
.dword ZERO ; null root
|
|
.dword dCREATE_WL
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( "name"<> -- ) Create a new named wordlist definition. When name is executed,
|
|
; H: put the WID of the wordlist at the top of the search order.
|
|
; H: The WID is the address of the body of the named wordlist definition.
|
|
dword VOCABULARY,"VOCABULARY"
|
|
ENTER
|
|
.dword CREATE
|
|
dovocab: .dword _COMP_LIT
|
|
.dword H_FORTH ; root of all dictionaries
|
|
.dword LAST
|
|
.dword drXT ; XT of the just-created word
|
|
.dword COMMA
|
|
CODE
|
|
jsl f:_does
|
|
ENTER ; action of the vocabulary definition
|
|
.dword RPLUCKADDR
|
|
.dword INCR
|
|
.dword TOP_OF_ORDER
|
|
EXIT
|
|
eword
|
|
|
|
; ( c-addr u -- ) Create a new named wordlist definition as per VOCABULARY.
|
|
; Meant for adding more builtin dictionaries (e.g. platform specific dictionaries)
|
|
; which are expected to adjust the root to the new wordlist
|
|
hword dVOCAB,"$VOCAB"
|
|
ENTER
|
|
.dword dCREATE
|
|
JUMP VOCABULARY::dovocab
|
|
eword
|
|
|
|
.endif
|
|
|
|
.if 0 ; half-baked
|
|
; ( -- )
|
|
; "Restore all dictionary allocation and search order pointers to the state they had just
|
|
; prior to the definition of name. Remove the definition of name and all subsequent
|
|
; definitions. Restoration of any structures still existing that could refer to deleted
|
|
; definitions or deallocated data space is not necessarily provided. No other contextual
|
|
; information such as numeric base is affected."
|
|
; May need to change the wordlist structures to be a linked list so that we are aware of
|
|
; all of them, because at least one of them will have their head change and may not be
|
|
; in the search order.
|
|
; So in total when the marker is created we need to:
|
|
; * save HERE in order to deallocate the space later
|
|
; * save CURRENT to restore compiler word list
|
|
; * save the search order
|
|
; * save the heads of all wordlists
|
|
; * save the head of the wordlists list
|
|
; When the marker is executed, restore all of the above:
|
|
; * restoring head of the wordlists ensures removal of all wordlists
|
|
; that are removed by the marker
|
|
; * restoring the heads of the (remaining) wordlists removes all definitions created
|
|
; after the marker
|
|
; * restoring the search order and CURRENT ensures no removed wordlists are in use
|
|
; * Restoring HERE deallocates all dictionary space from the marker and beyond.
|
|
dword MARKER,"MARKER" ; noindex
|
|
ENTER
|
|
|
|
CODE
|
|
jsl f:_does
|
|
ENTER ; action of the marker
|
|
|
|
EXIT
|
|
eword
|
|
.endif
|
|
|
|
; H: ( [text<end>] -- ) Discard the rest of the input buffer (or line during EVALUATE)
|
|
dword BACKSLASH,"\",F_IMMED
|
|
ENTER
|
|
.dword SOURCEID
|
|
.dword _IF
|
|
.dword term ; faster
|
|
.dword ZERO
|
|
lp: .dword DROP
|
|
.dword INQ
|
|
.dword _IF
|
|
.dword done ; whole enchilada has been eaten
|
|
.dword GETCH
|
|
.dword DUP
|
|
ONLIT c_cr
|
|
.dword EQUAL
|
|
.dword _IFFALSE
|
|
.dword ddone ; taken if = CR
|
|
.dword DUP
|
|
ONLIT c_lf
|
|
.dword EQUAL
|
|
.dword _IF
|
|
.dword lp ; taken if <> LF
|
|
ddone: .dword DROP
|
|
done: EXIT
|
|
term: .dword NIN
|
|
.dword FETCH
|
|
.dword PIN
|
|
.dword STORE
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( char -- char' ) Upper case convert char.
|
|
dword UPC,"UPC"
|
|
jsr _1parm
|
|
lda STACKBASE+0,x
|
|
jsr _cupper
|
|
sta STACKBASE+0,x
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( char -- char' ) Lower case convert char.
|
|
dword LCC,"LCC"
|
|
jsr _1parm
|
|
lda STACKBASE+0,x
|
|
cmp #'A'
|
|
bcc done
|
|
cmp #'Z'+1
|
|
bcs done
|
|
ora #$20
|
|
sta STACKBASE+0,X
|
|
done: NEXT
|
|
eword
|
|
|
|
; H: ( [name< >] ) Parse name, place low 5 bits of first char on stack.
|
|
; H: If compiling state, compile it as a literal.
|
|
dword CONTROL,"CONTROL",F_IMMED
|
|
ENTER
|
|
.dword CHAR
|
|
ONLIT $1F
|
|
.dword LAND
|
|
.dword _SMART
|
|
.dword interp
|
|
.dword LITERAL
|
|
interp: EXIT
|
|
eword
|
|
|
|
; H: ( char base -- digit true | char false ) Attempt to convert char to digit.
|
|
dword DIGIT,"DIGIT"
|
|
jsr _2parm
|
|
lda STACKBASE+4,x
|
|
jsr _c_to_d
|
|
ldy #$0000
|
|
bcc bad
|
|
cmp STACKBASE+0,x
|
|
bcs bad
|
|
sta STACKBASE+4,x
|
|
dey
|
|
bad: sty STACKBASE+0,x
|
|
sty STACKBASE+2,X
|
|
NEXT
|
|
eword
|
|
|
|
; H: ( addr len -- 0 | n 1 | d 2 ) Attmept to convert string to number.
|
|
hword dgNUM,"$>NUM"
|
|
ENTER
|
|
.dword OVER
|
|
.dword CFETCH
|
|
ONLIT '-'
|
|
.dword EQUAL
|
|
.dword PtoR
|
|
.dword RCOPY
|
|
.dword _IF
|
|
.dword :+
|
|
.dword DECR
|
|
.dword SWAP
|
|
.dword INCR
|
|
.dword SWAP
|
|
: .dword TWOPtoR ; ( c-addr u -- )
|
|
.dword ZERO ; ( -- 0 )
|
|
.dword StoD ; ( 0 -- ud )
|
|
.dword TWORtoP ; ( ud -- ud c-addr u )
|
|
.dword GNUMBER ; ( ud c-addr u -- ud' c-addr' u' ) u' = 0 if no unconverted
|
|
.dword QDUP ; ( ud' c-addr' u' -- ud' c-addr' u' | ud' c-addr' u' u' )
|
|
.dword _IF
|
|
.dword okay ; branch taken: ( ... ud c-addr' )
|
|
.dword ONE ; ( ud' c-addr' u' - ud' c-addr' u' 1 )
|
|
.dword EQUAL ; ( ud' c-addr' u' 1 -- ud' c-addr' f )
|
|
.dword _IF ; ( ud' c-addr' f -- ud' c-addr' )
|
|
.dword notok
|
|
.dword CFETCH ; ( ud' c-addr' -- ud' c )
|
|
ONLIT '.' ; ( ud' c -- ud' c '.' )
|
|
.dword EQUAL ; ( ud' c '.' -- ud' f )
|
|
.dword _IFFALSE ; ( ud' f -- ud' )
|
|
.dword dokay ; if true
|
|
.dword ZERO ; ( ud' -- ud' 0 ) p/h for THREEDROP
|
|
notok: .dword THREEDROP ; ( ud' c-addr' -- )
|
|
.dword RDROP ; lose negative
|
|
.dword ZERO ; ( -- 0 )
|
|
EXIT
|
|
okay: .dword DROP ; ( ud' c-addr' -- ud' )
|
|
.dword DtoS ; ( ud' -- n )
|
|
.dword RtoP
|
|
.dword QNEGATE
|
|
.dword ONE ; ( n -- n 1 )
|
|
EXIT
|
|
dokay: .dword RtoP ; ( ud' -- ud' f )
|
|
.dword _IF ; ( ud' f -- ud' )
|
|
.dword :+
|
|
.dword DNEGATE ; ( ud' -- d )
|
|
: .dword TWO ; ( d -- d 2 )
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( addr len -- true | n false ) Attmept to convert string to number.
|
|
dword dNUMBER,"$NUMBER"
|
|
ENTER
|
|
.dword dgNUM
|
|
.dword ZEROQ
|
|
EXIT
|
|
eword
|
|
|
|
; ( xx...xx1 -- yx...yx1 )
|
|
; Interpret text from current input source
|
|
hword INTERPRET,"INTERPRET"
|
|
ENTER
|
|
loop: .dword INQ ; ( -- f )
|
|
.dword _IF ; ( f -- )
|
|
.dword done
|
|
.dword PARSE_WORD ; ( -- c-addr u )
|
|
.dword QDUP ; ( c-addr u -- c-addr u | c-addr u u )
|
|
.dword _IF ; ( c-addr u | c-addr u u | c-addr | c-addr u )
|
|
.dword null
|
|
.dword TWODUP ; ( c-addr u -- c-addr u c-addr u )
|
|
.dword SEARCH_ALL ; ( c-addr u c-addr u - c-addr u xt|0 )
|
|
.dword QDUP ; ( c-addr u xt|0 -- c-addr u 0 | c-addr u xt xt )
|
|
.dword _IF ; ( c-addr u 0 | c-addr u xt xt -- c-addr u | c-addr u xt )
|
|
.dword trynum ; if xt = 0
|
|
.dword DROP ; drop flag
|
|
.dword NIPTWO ; ( c-addr u xt -- xt )
|
|
.dword CONLYQ ; compile-only? (leaves xt on stack
|
|
.dword _IFFALSE
|
|
.dword conly
|
|
.dword _SMART ; no, see if we should compile or execute
|
|
.dword exec ; if interpreting
|
|
chkimm: .dword IMMEDQ ; compiling, immediate? (leaves xt on stack)
|
|
.dword _IFFALSE
|
|
.dword exec ; yes, go do it
|
|
NLIT COMPILECOMMA
|
|
exec: .dword EXECUTE
|
|
JUMP loop
|
|
trynum: .dword TWODUP ; ( c-addr u -- c-addr u c-addr u )
|
|
.dword dgNUM ; ( c-addr u c-addr u -- c-addr u 0 | c-addr u n 1 | c-addr u d 2 )
|
|
.dword QDUP
|
|
.dword _IFFALSE
|
|
.dword isnum ; nonzero = is number
|
|
.dword SPACE
|
|
.dword TYPE
|
|
ONLIT '?'
|
|
.dword EMIT
|
|
ONLIT -13
|
|
.dword THROW
|
|
isnum: .dword XNPtoR
|
|
.dword NIPTWO
|
|
.dword XNRtoP
|
|
.dword _SMART
|
|
.dword inum
|
|
.dword XLITERAL
|
|
.dword _SKIP
|
|
inum: .dword DROP
|
|
JUMP loop
|
|
conly: .dword _SMART
|
|
.dword trytemp ; if interpreting, try temporary def
|
|
JUMP chkimm ; otherwise check immediacy
|
|
trytemp: .dword TEMPDQ ; has flag for starting temp def
|
|
.dword _IFFALSE
|
|
.dword dotemp ; true, so start temporary def
|
|
.dword DROP ; otherwise bad state, drop XT
|
|
ONLIT -14 ; and throw exception
|
|
.dword THROW
|
|
null: .dword DROP
|
|
done: EXIT
|
|
; now we gotta do some juggling stack is ( xt )
|
|
dotemp: .dword PtoR ; ( xt -- ) ( R: -- xt )
|
|
.dword dTEMPCOLON ; start temporary colon definition
|
|
.dword RtoP ; ( -- xt ) ( R: xt -- )
|
|
JUMP chkimm ; most or all of these should also be immediate...
|
|
eword
|
|
|
|
; ( -- xn...x1 n ) save current source input state
|
|
dword SAVEINPUT,"SAVE-INPUT"
|
|
ENTER
|
|
.dword SOURCE ; address and length of current input
|
|
.dword PIN
|
|
.dword FETCH ; position in buffer
|
|
.dword SOURCEID
|
|
ONLIT 4 ; that was 4 things
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( xn...x1 n f1 -- f2 ) restore current source input state,
|
|
; H: including source ID if f1 is true.
|
|
dword dRESTOREINPUT,"$RESTORE-INPUT"
|
|
ENTER
|
|
.dword SWAP ; ( ... addr len ptr srcid f 4 )
|
|
ONLIT 4 ; ( ... addr len ptr srcid f 4 4 ) sanity check
|
|
.dword EQUAL ; ( ... addr len ptr srcid f1 f2 )
|
|
.dword _IF ; ( ... addr len ptr srcid f )
|
|
.dword bad
|
|
.dword _IF ; ( ... addr len ptr srcid )
|
|
.dword nosrcid
|
|
.dword dSOURCEID ; ( ... addr len ptr srcid var )
|
|
.dword STORE ; ( ... addr len ptr )
|
|
JUMP :+
|
|
nosrcid: .dword SOURCEID ; ( ... addr len ptr srcid srcid' )
|
|
.dword EQUAL ; ( ... addr len ptr f )
|
|
.dword _IF ; ( ... addr len ptr )
|
|
.dword bad ; can't change sources
|
|
: .dword PIN ; otherwise restore all the things
|
|
.dword STORE
|
|
.dword NIN
|
|
.dword STORE
|
|
.dword dCIB
|
|
.dword STORE
|
|
.dword TRUE
|
|
EXIT
|
|
bad: ONLIT -12
|
|
.dword THROW
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( xn...x1 n -- f ) Restore current source input state, source ID must match current.
|
|
dword RESTOREINPUT,"RESTORE-INPUT"
|
|
ENTER
|
|
.dword FALSE
|
|
.dword dRESTOREINPUT
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( xxn...xx1 addr u -- yxn...yx1 ) Interpret text in addr u.
|
|
dword EVALUATE,"EVALUATE"
|
|
ENTER
|
|
.dword SAVEINPUT
|
|
.dword XNPtoR ; throw it all on the return stack
|
|
.dword PtoR ; along with the count
|
|
ONLIT -1
|
|
.dword dSOURCEID ; standard requires source-id to be -1 during EVALUATE
|
|
.dword STORE
|
|
ONLIT 0 ; input to first character
|
|
.dword PIN
|
|
.dword STORE
|
|
.dword NIN ; string length to #IN
|
|
.dword STORE
|
|
.dword dCIB ; current input buffer to string address
|
|
.dword STORE
|
|
ONLIT INTERPRET
|
|
.dword CATCH ; we do this so that we can restore input if exception
|
|
.dword RtoP ; now put the input back to where we were
|
|
.dword XNRtoP
|
|
.dword TRUE
|
|
.dword dRESTOREINPUT ; restore the input spec, including source ID
|
|
.dword DROP
|
|
.dword THROW ; finally, re-throw any exception
|
|
EXIT
|
|
eword
|
|
|
|
; H: synonym for EVALUATE
|
|
dword EVAL,"EVAL"
|
|
bra EVALUATE::code
|
|
eword
|
|
|
|
; ( [number< >] n ) Parse number in input stream, compile as literal if compiling.
|
|
hword nNUM,"#NUM"
|
|
ENTER
|
|
.dword PARSE_WORD
|
|
.dword DUP
|
|
.dword _IF
|
|
.dword empty
|
|
.dword dgNUM
|
|
.dword DUP
|
|
.dword _IF
|
|
.dword bad
|
|
.dword _SMART
|
|
.dword interp
|
|
.dword XLITERAL
|
|
.dword _SKIP
|
|
interp: .dword DROP
|
|
EXIT
|
|
empty: .dword TWODROP
|
|
bad: ONLIT -24
|
|
.dword THROW
|
|
eword
|
|
|
|
|
|
; H: ( [number< >] n ) Parse number as decimal, compile as literal if compiling.
|
|
dword DNUM,"D#",F_IMMED
|
|
ENTER
|
|
ONLIT 10
|
|
tmpbase: ONLIT nNUM
|
|
.dword SWAP
|
|
.dword TMPBASE
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( [number< >] n ) Parse number as hexadecimal, compile as literal if compiling.
|
|
dword HNUM,"H#",F_IMMED
|
|
ENTER
|
|
ONLIT 16
|
|
JUMP DNUM::tmpbase
|
|
eword
|
|
|
|
; H: ( [number< >] n ) Parse number as octal, compile as literal if compiling.
|
|
dword ONUM,"O#",F_IMMED
|
|
ENTER
|
|
ONLIT 8
|
|
JUMP DNUM::tmpbase
|
|
eword
|
|
|
|
; Forget is a stupidly dangerous word when you have multiple wordlists, noname words,
|
|
; and such. Not recommended to use except for the most recently-defined few words in
|
|
; the current wordlist.
|
|
; first we will scan the dictionary to see if the word to be forgotten is below
|
|
; the protection bit, and if it is found before we match the XT, we don't allow the
|
|
; forget
|
|
; H: ( xt -- ) Forget word referenced by xt and subsequent words.
|
|
dword dFORGET,"$FORGET"
|
|
ENTER
|
|
.dword DUP ; ( xt -- xt xt' )
|
|
.dword QDUP
|
|
.dword _IF
|
|
.dword cant
|
|
.dword rLINK ; ( xt xt' -- xt link )
|
|
.dword _IF ; ( xt link -- xt )
|
|
.dword cant
|
|
.dword LAST ; ( xt -- xt a-addr )
|
|
lp: .dword DUP ; ( xt a-addr -- xt a-addr a-addr' )
|
|
.dword drXT ; ( xt a-addr a-addr' -- xt a-addr xt2 )
|
|
.dword DUP ; ( xt a-addr xt2 -- xt a-addr xt2 xt2' )
|
|
.dword FETCH ; ( xt a-addr xt2 xt2' -- xt a-addr xt2 flags )
|
|
ONLIT F_PROT ; ( xt a-addr xt2 flags -- xt a-addr xt2 flags F_PROT )
|
|
.dword LAND ; ( xt a-addr xt2 flags F_PROT -- xt a-addr xt2 f )
|
|
.dword _IFFALSE ; ( xt a-addr xt2 f -- xt a-addr xt2 )
|
|
.dword prot
|
|
.dword SWAP ; ( ... xt xt2 a-addr )
|
|
.dword PtoR ; ( ... xt xt2 ) ( R: -- a-addr )
|
|
.dword OVER ; ( ... xt xt2 xt' )
|
|
.dword EQUAL ; ( ... xt f )
|
|
.dword _IFFALSE ; ( ... xt )
|
|
.dword amnesia
|
|
.dword RtoP ; ( xt -- xt a-addr ) ( R: a-addr -- )
|
|
.dword FETCH ; ( xt a-addr -- xt a-addr2 )
|
|
.dword QDUP
|
|
.dword _IF
|
|
.dword cant
|
|
JUMP lp
|
|
amnesia: .dword RDROP ; ( R: a-addr -- )
|
|
.dword rLINK
|
|
.dword DUP
|
|
.dword toHERE
|
|
.dword FETCH
|
|
.dword GET_CURRENT
|
|
.dword STORE
|
|
EXIT
|
|
prot: .dword TWODROP ; ( xt a-addr xt2 -- xt )
|
|
cant: SLIT "Can't forget " ; ( xt -- xt str len )
|
|
.dword TYPE ; ( xt str len -- xt )
|
|
.dword rNAME ; ( xt -- str len )
|
|
.dword TYPE ; ( str len -- )
|
|
EXIT
|
|
eword
|
|
|
|
; H: ( [name< >] -- ) Attempt to forget name and subsequent definitions in compiler
|
|
; H: word list. This may have unintended consequences if things like wordlists and
|
|
; H: such were defined after name.
|
|
dword FORGET,"FORGET"
|
|
ENTER
|
|
.dword PARSEFIND
|
|
.dword dFORGET
|
|
EXIT
|
|
eword
|
|
|
|
; remove any incomplete or temporary definitions
|
|
; executed by QUIT to clean up after an exception results in a return to the outer
|
|
; interpreter.
|
|
hword dPATCH,"$PATCH"
|
|
ENTER
|
|
.dword STATEI ; ensure interpretation state
|
|
.dword dTMPDEF
|
|
.dword FETCH
|
|
.dword _IF ; in the middle of a temporary definition?
|
|
.dword :+ ; no, see if we were doing a normal def
|
|
.dword dSAVEHERE ; ( -- a-addr ) restore HERE
|
|
.dword FETCH ; ( a-addr -- c-addr )
|
|
.dword toHERE ; ( c-addr -- )
|
|
ONLIT 0 ; ( -- 0 )
|
|
.dword dTMPDEF ; ( 0 -- 0 a-addr )
|
|
.dword DUP ; ( 0 a-addr -- 0 a-addr a-addr' )
|
|
.dword FETCH ; ( 0 a-addr a-addr' -- 0 a-addr c-addr )
|
|
ONLIT max_tempdef ; ( ... 0 a-addr c-addr u )
|
|
.dword FREE ; ( ... 0 a-addr )
|
|
.dword STORE ; ( 0 a-addr -- )
|
|
: .dword OLDHERE ; is OLDHERE not 0?
|
|
.dword _IF
|
|
.dword nopatch ; is zero, no need to patch
|
|
.dword LAST ; it is! check smudge bit of last definition
|
|
.dword drXT
|
|
.dword CFETCH
|
|
ONLIT F_SMUDG
|
|
.dword LAND
|
|
.dword _IF ; is smudge bit set?
|
|
.dword nopatch ; nope, no need to patch
|
|
.dword LAST ; yes, start fixup by setting LAST to the value at [LAST]
|
|
.dword FETCH ; LAST @
|
|
.dword GET_CURRENT ; CURRENT
|
|
.dword STORE ; !
|
|
.dword OLDHERE ; fix HERE
|
|
.dword toHERE ; ->HERE
|
|
ONLIT 0 ; clear OLDHERE
|
|
.dword dOLDHERE ; $OLDHERE
|
|
.dword STORE ; !
|
|
nopatch: EXIT
|
|
eword
|
|
|
|
; H: ( -- ) ( R: ... -- ) Enter outer interpreter loop, aborting any execution.
|
|
dword QUIT,"QUIT"
|
|
lda RSTK_TOP ; reset return stack pointer
|
|
tcs
|
|
ENTER
|
|
.dword dPATCH ; fix top of dictionary/remove temp defs
|
|
.dword CR
|
|
source0: .dword SETKBD ; set keyboard as input source
|
|
lp: ONLIT 0 ; clear #LINE since we are at input prompt
|
|
.dword NLINE
|
|
.dword STORE
|
|
.dword REFILL ; fill input buffer
|
|
.dword _IF ; get anything?
|
|
.dword source0 ; no, reset to keyboard and get more
|
|
.dword INTERPRET ; otherwise, interpret
|
|
.dword dSTATUS ; display status
|
|
JUMP lp
|
|
eword
|
|
__doquit = QUIT::code
|
|
|
|
PLATFORM_INCLUDE "platform-words.s" ; Platform additional dictionary words
|
|
|
|
; Leave these toward the top
|
|
|
|
; H: ( -- -1 )
|
|
dword MINUSONE,"-1"
|
|
lda #$FFFF
|
|
tay
|
|
PUSHNEXT
|
|
eword
|
|
|
|
; H: ( -- 3 )
|
|
dword THREE,"3"
|
|
FCONSTANT 3
|
|
eword
|
|
|
|
; H: ( -- 2 )
|
|
dword TWO,"2"
|
|
FCONSTANT 2
|
|
eword
|
|
|
|
; H: ( -- 1 )
|
|
dword ONE,"1"
|
|
lda #$0000
|
|
tay
|
|
iny
|
|
PUSHNEXT
|
|
eword
|
|
|
|
; H: ( -- 0 )
|
|
dword ZERO,"0"
|
|
lda #$0000
|
|
tay
|
|
PUSHNEXT
|
|
eword
|
|
|
|
|
|
dend
|
|
|
|
|