first cut at working VM and forth assembler
This commit is contained in:
commit
f4e8467898
404
qf.bas
Executable file
404
qf.bas
Executable file
|
@ -0,0 +1,404 @@
|
|||
DECLARE SUB vm ()
|
||||
DECLARE SUB interpret (st$)
|
||||
DECLARE SUB execute (op%)
|
||||
DECLARE SUB assemble (name$, asm$)
|
||||
DECLARE FUNCTION lookup! (w$)
|
||||
DECLARE FUNCTION rpeek! ()
|
||||
DECLARE FUNCTION nextword$ ()
|
||||
DECLARE FUNCTION mkstr% (st$)
|
||||
DECLARE SUB opcode (opname$, op%)
|
||||
DECLARE SUB rpush (v%)
|
||||
DECLARE FUNCTION rpop! ()
|
||||
DECLARE SUB push (v%)
|
||||
DECLARE FUNCTION pop! ()
|
||||
DECLARE FUNCTION top! ()
|
||||
TYPE word
|
||||
nm AS STRING * 32
|
||||
df AS INTEGER
|
||||
i AS INTEGER
|
||||
END TYPE
|
||||
DIM SHARED words(256) AS word
|
||||
DIM SHARED strings$(32)
|
||||
DIM SHARED mem(10000) AS INTEGER
|
||||
DIM SHARED stack(64) AS INTEGER
|
||||
DIM SHARED rstack(64) AS INTEGER
|
||||
COMMON SHARED sp AS INTEGER, rsp AS INTEGER, w AS INTEGER, ip AS INTEGER
|
||||
COMMON SHARED in$
|
||||
COMMON SHARED interpretcp AS INTEGER
|
||||
COMMON SHARED trace%
|
||||
|
||||
interpretcp = -1
|
||||
trace% = 0
|
||||
|
||||
OPEN "out.com" FOR OUTPUT AS #1
|
||||
CLOSE #1
|
||||
OPEN "out.com" FOR BINARY AS #1
|
||||
|
||||
IF trace% > 0 THEN
|
||||
OPEN "TRACE.TXT" FOR OUTPUT AS #trace%
|
||||
END IF
|
||||
|
||||
CONST O.RET = -1
|
||||
CONST O.DUP = -2
|
||||
CONST O.SWAP = -3
|
||||
CONST O.DROP = -4
|
||||
CONST O.GET = -5
|
||||
CONST O.SET = -6
|
||||
CONST O.CREATE = -7
|
||||
CONST O.IN = -8
|
||||
CONST O.OUT = -9
|
||||
CONST O.EXEC = -10
|
||||
CONST O.LOOKUP = -11
|
||||
CONST O.WORD = -12
|
||||
CONST O.NUMBER = -13
|
||||
CONST O.LIT = -14
|
||||
CONST O.ADD = -15
|
||||
CONST O.SUB = -16
|
||||
CONST O.MUL = -17
|
||||
CONST O.DIV = -18
|
||||
CONST O.JZ = -19
|
||||
CONST O.RPUSH = -20
|
||||
CONST O.RPOP = -21
|
||||
CONST O.COMMA = -22
|
||||
CONST O.ROT = -23
|
||||
CONST O.LT = -24
|
||||
CONST O.EQ = -25
|
||||
CONST O.JMP = -26
|
||||
CONST O.YELP = -27
|
||||
CONST O.IMMEDIATE = -28
|
||||
|
||||
CONST P.HERE = 0
|
||||
CONST P.LATEST = 1
|
||||
CONST P.NEXTSTR = 2
|
||||
mem(P.LATEST) = -1
|
||||
mem(P.HERE) = 3
|
||||
|
||||
CALL opcode("ret", O.RET)
|
||||
CALL opcode("dup", O.DUP)
|
||||
CALL opcode("swap", O.SWAP)
|
||||
CALL opcode(">rot", O.ROT)
|
||||
CALL opcode("@", O.GET)
|
||||
CALL opcode("!", O.SET)
|
||||
CALL opcode("create", O.CREATE)
|
||||
CALL opcode("in@", O.IN)
|
||||
CALL opcode("out!", O.OUT)
|
||||
CALL opcode("immediate", O.IMMEDIATE)
|
||||
CALL opcode("drop", O.DROP)
|
||||
CALL opcode("execute", O.EXEC)
|
||||
CALL opcode("lookup", O.LOOKUP)
|
||||
CALL opcode("word@", O.WORD)
|
||||
CALL opcode("number", O.NUMBER)
|
||||
CALL opcode("lit_", O.LIT)
|
||||
CALL opcode("+", O.ADD)
|
||||
CALL opcode("-", O.SUB)
|
||||
CALL opcode("*", O.MUL)
|
||||
CALL opcode("/", O.DIV)
|
||||
CALL opcode("<", O.LT)
|
||||
CALL opcode("=", O.EQ)
|
||||
CALL opcode("jz_", O.JZ)
|
||||
CALL opcode("jmp_", O.JMP)
|
||||
CALL opcode(">r", O.RPUSH)
|
||||
CALL opcode("r>", O.RPOP)
|
||||
CALL opcode(",", O.COMMA)
|
||||
CALL opcode("yelp", O.YELP)
|
||||
|
||||
CALL assemble("<rot", ">rot >rot ret")
|
||||
CALL assemble("over", "swap dup >rot ret")
|
||||
CALL assemble("2dup", "over over ret")
|
||||
CALL assemble("$var", "r> ret")
|
||||
CALL assemble("$const", "r> @ ret")
|
||||
CALL assemble("not", "jz_ 3 lit_ 0 ret lit_ 1 ret")
|
||||
CALL assemble("and", "jz_ 4 jz_ 3 jmp_ 4 drop lit_ 0 ret lit_ 1 ret")
|
||||
CALL assemble("or", "not swap not and not ret")
|
||||
CALL assemble(">=", "< not ret")
|
||||
CALL assemble("<=", "2dup < >rot = or ret")
|
||||
CALL assemble(">", "<= not ret")
|
||||
CALL assemble("state", "$var 0")
|
||||
|
||||
CALL assemble("doword", "state @ not or jz_ 2 execute ret , ret")
|
||||
CALL assemble("donum", "state @ jz_ 4 lit_ lit_ , , ret")
|
||||
CALL assemble("donext", "lookup jz_ 2 doword ret number jz_ 2 donum ret yelp ret")
|
||||
CALL assemble("interpret", "word@ dup lit_ 0 >= jz_ 3 donext jmp_ -10 drop ret")
|
||||
CALL assemble("[", "lit_ 0 state ! ret"): CALL execute(O.IMMEDIATE)
|
||||
CALL assemble("]", "lit_ 1 state ! ret")
|
||||
CALL assemble(":", "word@ create ] ret")
|
||||
CALL assemble(";", "lit_ ret , [ ret"): CALL execute(O.IMMEDIATE)
|
||||
|
||||
CALL interpret("65 out! 66 out! 0 out! 67 out! 7 out! 254 out! 10 out! 13 out!")
|
||||
|
||||
SUB assemble (name$, asm$)
|
||||
in$ = asm$
|
||||
done = 0
|
||||
CALL push(mkstr(name$))
|
||||
CALL execute(O.CREATE)
|
||||
IF trace% > 0 THEN
|
||||
PRINT #trace%, name$; " -> "; mem(P.LATEST)
|
||||
END IF
|
||||
WHILE done = 0
|
||||
CALL execute(O.WORD)
|
||||
IF top < 0 THEN
|
||||
done = 1
|
||||
x = pop
|
||||
ELSE
|
||||
word$ = strings$(top)
|
||||
CALL execute(O.LOOKUP)
|
||||
IF pop <> 0 THEN
|
||||
x = pop ' immediate flag, ignore
|
||||
IF trace% > 0 THEN PRINT #trace%, word$; " => "; STR$(top); " => "; mem(P.HERE)
|
||||
CALL execute(O.COMMA)
|
||||
ELSE
|
||||
CALL execute(O.NUMBER)
|
||||
IF pop <> 0 THEN
|
||||
IF trace% > 0 THEN PRINT #trace%, word$; " => "; STR$(top); " => "; mem(P.HERE)
|
||||
CALL execute(O.COMMA)
|
||||
ELSE
|
||||
CALL execute(O.YELP)
|
||||
END IF
|
||||
END IF
|
||||
END IF
|
||||
WEND
|
||||
END SUB
|
||||
|
||||
SUB execute (op%)
|
||||
IF trace% > 0 THEN
|
||||
tp$ = "NA"
|
||||
IF sp > 0 THEN tp$ = STR$(top)
|
||||
PRINT #trace%, "ip:"; ip; " op:"; op%; " sp:"; sp; " tp:"; tp$; " rsp:"; rsp
|
||||
END IF
|
||||
SELECT CASE op%
|
||||
CASE IS >= 0
|
||||
CALL rpush(ip)
|
||||
ip = op%
|
||||
CASE O.RET
|
||||
ip = rpop
|
||||
CASE O.DUP
|
||||
CALL push(top)
|
||||
CASE O.SWAP
|
||||
x = stack(sp - 1)
|
||||
y = stack(sp - 2)
|
||||
stack(sp - 1) = y
|
||||
stack(sp - 2) = x
|
||||
CASE O.ROT
|
||||
s3% = pop
|
||||
s2% = pop
|
||||
s1% = pop
|
||||
' >rot ( 1 2 3 -- 3 1 2 )
|
||||
CALL push(s3%)
|
||||
CALL push(s1%)
|
||||
CALL push(s2%)
|
||||
CASE O.DROP
|
||||
x = pop
|
||||
CASE O.GET
|
||||
ptr = pop
|
||||
CALL push(mem(ptr))
|
||||
CASE O.SET
|
||||
ptr = pop
|
||||
value = pop
|
||||
mem(ptr) = value
|
||||
CASE O.CREATE
|
||||
word = mem(P.LATEST) + 1
|
||||
mem(P.LATEST) = word
|
||||
sname = pop
|
||||
words(word).nm = strings$(sname)
|
||||
words(word).df = mem(P.HERE)
|
||||
CASE O.IN
|
||||
c$ = MID$(in$, 1, 1)
|
||||
in$ = MID$(in$, 2)
|
||||
IF c$ = "" THEN
|
||||
CALL push(-1)
|
||||
ELSE
|
||||
CALL push(ASC(c$))
|
||||
END IF
|
||||
CASE O.OUT
|
||||
byte$ = CHR$(pop)
|
||||
PUT #1, , byte$
|
||||
CASE O.LOOKUP
|
||||
istr% = pop
|
||||
iword = lookup(strings$(istr%))
|
||||
IF iword < 0 THEN
|
||||
CALL push(istr%)
|
||||
CALL push(0)
|
||||
ELSE
|
||||
CALL push(words(iword).df)
|
||||
CALL push(words(iword).i)
|
||||
CALL push(1)
|
||||
END IF
|
||||
CASE O.WORD
|
||||
word$ = nextword$
|
||||
IF word$ <> "" THEN
|
||||
CALL push(mkstr(word$))
|
||||
ELSE
|
||||
CALL push(-1)
|
||||
END IF
|
||||
CASE O.NUMBER
|
||||
istr% = pop
|
||||
n$ = strings$(istr%)
|
||||
IF VAL(n$ + "1") = 0 THEN
|
||||
CALL push(istr%)
|
||||
CALL push(0)
|
||||
ELSE
|
||||
CALL push(VAL(n$))
|
||||
CALL push(1)
|
||||
END IF
|
||||
|
||||
CASE O.IMMEDIATE
|
||||
word = mem(P.LATEST)
|
||||
words(word).i = 1
|
||||
CASE O.EXEC
|
||||
CALL execute(pop)
|
||||
|
||||
CASE O.LIT
|
||||
CALL push(mem(ip))
|
||||
ip = ip + 1
|
||||
CASE O.ADD
|
||||
r = pop
|
||||
l = pop
|
||||
CALL push(l + r)
|
||||
CASE O.SUB
|
||||
r = pop
|
||||
l = pop
|
||||
CALL push(l - r)
|
||||
CASE O.MUL
|
||||
r = pop
|
||||
l = pop
|
||||
CALL push(l * r)
|
||||
CASE O.DIV
|
||||
r = pop
|
||||
l = pop
|
||||
CALL push(l / r)
|
||||
|
||||
CASE O.LT
|
||||
r = pop
|
||||
l = pop
|
||||
CALL push(l < r)
|
||||
CASE O.EQ
|
||||
r = pop
|
||||
l = pop
|
||||
IF l = r THEN
|
||||
CALL push(1)
|
||||
ELSE
|
||||
CALL push(0)
|
||||
END IF
|
||||
CASE O.JZ
|
||||
IF pop = 0 THEN
|
||||
ip = ip + mem(ip) + 1
|
||||
ELSE
|
||||
ip = ip + 1
|
||||
END IF
|
||||
CASE O.JMP
|
||||
ip = ip + mem(ip) + 1
|
||||
CASE O.RPUSH
|
||||
CALL rpush(pop)
|
||||
CASE O.RPOP
|
||||
CALL push(rpop)
|
||||
|
||||
CASE O.COMMA
|
||||
here = mem(P.HERE)
|
||||
mem(here) = pop
|
||||
mem(P.HERE) = here + 1
|
||||
CASE O.YELP
|
||||
PRINT strings$(pop); "?"
|
||||
CASE ELSE
|
||||
PRINT "Unknown op "; op%
|
||||
ip = -1
|
||||
END SELECT
|
||||
END SUB
|
||||
|
||||
SUB interpret (st$)
|
||||
IF interpretcp < 0 THEN
|
||||
CALL push(mkstr("interpret"))
|
||||
CALL execute(O.LOOKUP)
|
||||
z = pop
|
||||
z = pop
|
||||
interpretcp = pop
|
||||
END IF
|
||||
in$ = st$
|
||||
ip = -1
|
||||
CALL execute(interpretcp)
|
||||
CALL vm
|
||||
END SUB
|
||||
|
||||
FUNCTION lookup (w$)
|
||||
lookup = -1
|
||||
FOR i = 0 TO mem(P.LATEST)
|
||||
IF RTRIM$(words(i).nm) = w$ THEN
|
||||
lookup = i
|
||||
GOTO found
|
||||
END IF
|
||||
NEXT
|
||||
found:
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION mkstr% (st$)
|
||||
istr = mem(P.NEXTSTR)
|
||||
mem(P.NEXTSTR) = (istr + 1) MOD 32
|
||||
strings$(istr) = st$
|
||||
mkstr% = istr
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION nextword$
|
||||
wordstart = 0
|
||||
length = 0
|
||||
FOR i = 1 TO LEN(in$)
|
||||
c$ = MID$(in$, i, 1)
|
||||
IF c$ = " " THEN
|
||||
IF wordstart <> 0 THEN
|
||||
GOTO foundword
|
||||
END IF
|
||||
ELSE
|
||||
IF wordstart = 0 THEN
|
||||
wordstart = i
|
||||
END IF
|
||||
length = length + 1
|
||||
END IF
|
||||
NEXT
|
||||
foundword:
|
||||
IF wordstart <> 0 THEN
|
||||
nextword$ = MID$(in$, wordstart, length)
|
||||
in$ = MID$(in$, wordstart + length)
|
||||
END IF
|
||||
END FUNCTION
|
||||
|
||||
SUB opcode (opname$, op%)
|
||||
word = mem(P.LATEST) + 1
|
||||
mem(P.LATEST) = word
|
||||
words(word).nm = opname$
|
||||
words(word).df = op%
|
||||
END SUB
|
||||
|
||||
FUNCTION pop
|
||||
sp = sp - 1
|
||||
pop = stack(sp)
|
||||
END FUNCTION
|
||||
|
||||
SUB push (v%)
|
||||
stack(sp) = v%
|
||||
sp = sp + 1
|
||||
END SUB
|
||||
|
||||
FUNCTION rpeek
|
||||
rpeek = rstack(rsp - 1)
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION rpop
|
||||
rsp = rsp - 1
|
||||
rpop = rstack(rsp)
|
||||
END FUNCTION
|
||||
|
||||
SUB rpush (v%)
|
||||
rstack(rsp) = v%
|
||||
rsp = rsp + 1
|
||||
END SUB
|
||||
|
||||
FUNCTION top
|
||||
top = stack(sp - 1)
|
||||
END FUNCTION
|
||||
|
||||
SUB vm
|
||||
WHILE ip >= 0
|
||||
op% = mem(ip)
|
||||
ip = ip + 1
|
||||
CALL execute(op%)
|
||||
WEND
|
||||
END SUB
|
||||
|
Loading…
Reference in a new issue