All pastes #238953 Raw Edit

Miscellany

public text v1 · immutable
·published 2006-11-05 04:38 UTC
rendered paste body
256 constant input-buffer-size
assemble-start

\ word structure: (xt is address of type flag)
\   pointer to previous word or 0 for first word
\   counted-string name
\   type flag
\   primitive-byte or program

\ type:
\   1: immediate
\   2: not immediate
\   3: not immediate, xt is a primitive

:program-name
    .s' Virtual Forth 0.1'

:input-buffer
    .dw 0
\    .org [ data-index input-buffer-size + ]

:>in
    .db 0

:input-count
    .db 0

:in-index
    .dw 0

:immediate-flag
    .dw -1

:state
    .dw :count
    .s' state'
    .db 2
:state-code
    lit :immediate-flag ret

:+
    .dw 0
    .s' +'
    .db 3 +

:count ( c-addr1 -- c-addr2 u )
    .dw :+
    .s' count'
    .db 2
:count-code
    dup c@ swap 1+ swap ret

:min ( n1 n2 -- n3 )
    .dw :count
    .s' min'
    .db 2
:min-code
    over over
    > jz :min-less
    swap drop
    ret
:min-less
    drop ret

:compare-count
    .db 0
:compare-addr1
    .dw 0
:compare-count1
    .db 0
:compare-addr2
    .dw 0
:compare-count2
    .db 0
:compare ( c-addr1 u1 c-addr2 u2 -- n )
    .dw :min
    .s' compare'
    .db 2
:compare-code
    lit :compare-count2 c! lit :compare-addr2 !
    lit :compare-count1 c! lit :compare-addr1 !
    lit :compare-count1 c@ lit :compare-count2 c@ call :min-code
    lit :compare-count c!
:compare-next
    lit :compare-count c@ jnz :compare-char
:compare-length
    lit :compare-count1 c@ lit :compare-count2 c@ < jnz :compare-less
    lit :compare-count1 c@ lit :compare-count2 c@ > jnz :compare-greater
    jump :compare-equal
:compare-char
    lit :compare-addr1 @ c@ lit :compare-addr2 @ c@ < jnz :compare-less
    lit :compare-addr1 @ c@ lit :compare-addr2 @ c@ > jnz :compare-less
    lit :compare-addr1 dup @ 1+ swap !
    lit :compare-addr2 dup @ 1+ swap !
    lit :compare-count c@ 1- lit :compare-count c!
    jump :compare-next
:compare-less
    lit -1 ret
:compare-greater
    lit 1 ret
:compare-equal
    lit 0 ret

:find-word-address
    .dw :compare
:find-word-count
    .db 0
:find-word ( c-addr u -- false  |  xt true )
    .dw :compare
    .s' find-word'
    .db 2
:find-word-code
    lit :find-word-count c!
    lit :find-word-address !
    lit :last-word @
:find-word-next
    dup lit 2 + call :count-code
    lit :find-word-address @ lit :find-word-count c@ call :compare-code
    jnz :find-word-step
    lit 2 + call :count-code + lit -1 ret
:find-word-step
    @ dup jnz :find-word-next
    ret

:type ( c-addr u -- )
    .dw :find-word
    .s' type'
    .db 2
:type-code
    dup jz :type-end
    swap dup c@ emit 1+ swap 1-
    jump :type-code
:type-end
    drop
    drop
    ret

:move-count
    .dw 0
:move-addr1
    .dw 0
:move-addr2
    .dw 0
:move ( addr1 addr2 count -- )
    .dw :type
    .s' move'
    .db 2
:move-code
    lit :move-count !
    lit :move-addr2 !
    lit :move-addr1 !
:move-start
    lit :move-count @ jz :move-end
    lit :move-addr1 @ c@ lit :move-addr2 @ c!
    lit :move-addr1 dup @ 1+ swap !
    lit :move-addr2 dup @ 1+ swap !
    lit :move-count dup @ 1+ swap !
    jump :move-start
:move-end
    ret

:execute ( xt -- )
    .dw :move
    .s' execute'
    .db 2
:execute-code
    dup c@ lit 3 = jnz :execute-primitive
    1+ pop-call ret
:execute-primitive
    1+ c@ execute ret

:is-whitespace
    lit 32 =
    ret

:skip-while-whitespace
    lit :>in c@ lit :input-count c@ = jnz :skip-while-whitespace-end
    lit :>in c@ lit :input-buffer @ + c@
    call :is-whitespace jz :skip-while-whitespace-end
    lit :>in dup c@ 1+ swap c!
    jump :skip-while-whitespace
:skip-while-whitespace-end
    ret

:skip-until-whitespace
    lit :>in c@ lit :input-count c@ = jnz :skip-until-whitespace-end
    lit :>in c@ lit :input-buffer @ + c@
    call :is-whitespace jnz :skip-until-whitespace-end
    lit :>in dup c@ 1+ swap c!
    jump :skip-until-whitespace
:skip-until-whitespace-end
    ret

:parse-number-neg
    .db 0
:parse-number-result
    .dw 0
:parse-number-word
    .dw 0
:parse-number-count
    .db 0
:parse-number ( addr u -- number -1 | 0 )
    \ check for empty string
    dup jnz :parse-number-at-least-one-char
    swap drop ret
:parse-number-at-least-one-char
    \ init variables
    lit :parse-number-count c!
    lit :parse-number-word !
    lit 0 lit :parse-number-result !
    lit 0 lit :parse-number-neg c!
    \ if first character is "-", then increment pointer and set flag
    lit :parse-number-word @ c@ lit 45 = jz :parse-number-positive
    lit -1 lit :parse-number-neg c!
    lit :parse-number-count dup c@ 1- swap c!
    lit :parse-number-word dup @ 1+ swap !
:parse-number-positive
    \ check for empty string after possible "-"
    lit :parse-number-count c@ jnz :parse-number-positive2
    lit 0 ret
:parse-number-positive2
    \ shift current result
    lit :parse-number-result dup @ lit 10 * swap !
    \ check next digit
    lit :parse-number-word @ c@
    lit 48 - dup lit 0 < over lit 9 > or jz :parse-number-valid-digit
    drop lit 0 ret
:parse-number-valid-digit
    \ add digit
    lit :parse-number-result @ + lit :parse-number-result !
    \ increment pointer
    lit :parse-number-count dup c@ 1- swap c!
    lit :parse-number-word dup @ 1+ swap !
    \ test for last character
    lit :parse-number-count c@ jnz :parse-number-positive2
    \ was last character, test negate flag
    lit :parse-number-result @ lit :parse-number-neg c@ jz :parse-number-valid
    negate
:parse-number-valid
    lit -1 ret

:next-word ( -- 0 | c-addr u -1 )
    call :skip-while-whitespace
    lit :>in c@ lit :input-count c@ = jz :next-word-until-whitespace
    lit 0 ret
:next-word-until-whitespace
    lit :>in c@ dup lit :input-buffer @ + swap
    call :skip-until-whitespace
    lit :>in c@ swap - lit -1 ret

:,
    .dw :execute
    .s' ,'
    .db 2
:,-code
    here ! lit 2 allot ret

:c,
    .dw :,
    .s' c,'
    .db 2
:c,-code
    here c! lit 1 allot ret

::
    .dw :c,
    .s' :'
    .db 2
::-code
    \ store pointer to previous word and update last word pointer
    here lit :last-word @ call :,-code lit :last-word !
    \ read word name and copy it
    call :next-word jz ::-error
    dup call :c,-code swap over here swap call :move-code allot
    \ set type flag
    lit 2 call :c,-code
    \ set compile mode
    lit 0 lit :immediate-flag !
    ret
::-error
    \ TODO: handle EOF errors
    ret

:;
    .dw ::
    .s' ;'
    .db 1
:;-code
    \ finish word
    lit ret call :c,-code
    \ set immediate mode
    lit -1 lit :immediate-flag !
    ret

:evaluate-number ( n -- n | )
    lit :immediate-flag @ jz :compile-number
    ret
:compile-number
    lit lit call :c, call :,
    ret

:evaluate-word ( xt -- )
    lit :immediate-flag @ jz :compile-word
    call :execute-code
    ret
:compile-word
    dup c@ lit 1 = jz :compile-word-no-immediate
    1+ c@ pop-call ret
:compile-word-no-immediate
    dup c@ lit 2 = jz :compile-word-primtive
    lit call call :c,
    1+ call :, ret
:compile-word-primtive
    c@ call :c, ret

:evaluate ( c-addr u -- )
    .dw :;
    .s' evaluate'
    .db 2
:evaluate-code
    \ set input buffer
    lit :input-count c! lit :input-buffer ! lit 0 lit :>in c!
:evaluate-next
    \ get next word
    call :next-word jz :evaluate-end
    \ try to evaluate it as a word
    over over call :find-word-code jz :evaluate-try-number
    swap drop swap drop call :evaluate-word jump :evaluate-next
:evaluate-try-number
    \ try to evaluate it as a number
    call :parse-number jz :evaluate-error
    call :evaluate-number
:evaluate-error
    \ evaluate error: ignore
    jump :evaluate-next
:evaluate-end
    ret

:last-word
    .dw :evaluate

:data-start

.end