X-Git-Url: https://thelambdalab.xyz/gitweb/index.cgi?a=blobdiff_plain;f=src%2Fforth.jl;h=7cf0ca7d7d96d0c450c130758a318aab73f88618;hb=cccf599127228c9e6029deff1a6698a010089f50;hp=c6b8cba2f3bc0118c3275ee06c9410e9c74ceff9;hpb=411fedeac60fe61f32b8bcd8eba55d125baf7751;p=forth.jl.git diff --git a/src/forth.jl b/src/forth.jl index c6b8cba..7cf0ca7 100644 --- a/src/forth.jl +++ b/src/forth.jl @@ -1,165 +1,218 @@ module forth -RS = Array{Int64, 1}(1024) -RSP = 0 +# VM mem size +size_mem = 640*1024 + +# Buffer sizes +size_BIVar = 16 # +size_RS = 1024 # Return stack size +size_PS = 1024 # Parameter stack size +size_TIB = 4096 # Terminal input buffer size + +# The mem array constitutes the memory of the VM. It has the following geography: +# +# mem = +-----------------------+ +# | Built-in Variables | +# +-----------------------+ +# | Return Stack | +# +-----------------------+ +# | Parameter Stack | +# +-----------------------+ +# | Terminal Input Buffer | +# +-----------------------+ +# | Dictionary | +# +-----------------------+ +# +# Note that all words (user-defined, primitive, variables, etc) are included in +# the dictionary. +# +# Simple linear addressing is used with one exception: references to primitive code +# blocks, which are represented as anonymous functions, appear the negative index +# into the primitives array which contains only these functions. + +mem = Array{Int64,1}(size_mem) +primitives = Array{Function,1}() -PS = Array{Int64, 1}(1024) -PSP =0 - -IP = 0 -W = 0 -X = 0 - -jmp = 0 - -primitives = Array{Expr,1}() -memory = Array{Int64,1}(64*1024) -LATEST = 0 -HERE = 1 - -# Intperpreter state - -STATE = 0 - -# Current radix +# Built-in variables -BASE = 10 +nextVarAddr = 1 +RSP0 = nextVarAddr; nextVarAddr += 1 +PSP0 = nextVarAddr; nextVarAddr += 1 +HERE = nextVarAddr; nextVarAddr += 1 +LATEST = nextVarAddr; nextVarAddr += 1 + +mem[RSP0] = size_BIVar # bottom of RS +mem[PSP0] = mem[RSP0] + size_RS # bottom of PS +TIB = mem[PSP0] + size_PS # address of terminal input buffer +mem[HERE] = TIB + size_TIB # location of bottom of dictionary +mem[LATEST] = 0 # no previous definition + +# VM registers +type Reg + RSP::Int64 # Return stack pointer + PSP::Int64 # Parameter/data stack pointer + IP::Int64 # Instruction pointer + W::Int64 # Working register + X::Int64 # Extra register +end +reg = Reg(mem[RSP0], mem[PSP0], 0, 0, 0) -# Stack manipulation macros +# Stack manipulation functions function pushRS(val::Int64) - global RSP - RS[RSP += 1] = val + mem[reg.RSP+=1] = val end function popRS() - global RSP - val = RS[RSP] - RSP -= 1 + val = mem[reg.RSP] + reg.RSP -= 1 return val end function pushPS(val::Int64) - global PSP - PS[PSP += 1] = val + mem[reg.PSP += 1] = val end function popPS() - global PSP - val = PS[PSP] - PSP -= 1 + val = mem[reg.PSP] + reg.PSP -= 1 return val end -# Primitive creation functions +# Primitive creation and calling functions -function defPrim(name::AbstractString, expr::Expr) - global HERE, LATEST +function createHeader(name::AbstractString, flags::Int64) + mem[mem[HERE]] = mem[LATEST] + mem[LATEST] = mem[HERE] + mem[HERE] += 1 - memory[HERE] = LATEST - LATEST = HERE - HERE += 1 + mem[mem[HERE]] = length(name) + flags; mem[HERE] += 1 + mem[mem[HERE]:(mem[HERE]+length(name)-1)] = [Int(c) for c in name]; mem[HERE] += length(name) +end - memory[HERE] = length(name); HERE += 1 - memory[HERE:(HERE+length(name)-1)] = [Int(c) for c in name]; HERE += length(name) +function defPrim(name::AbstractString, f::Function; flags::Int64=0) + createHeader(name, flags) - push!(primitives, expr) - memory[HERE] = -length(primitives) - HERE += 1 + push!(primitives, f) + mem[mem[HERE]] = -length(primitives) + mem[HERE] += 1 return -length(primitives) end -defVar(name::AbstractString, var::Symbol) = defPrim(name, quote - pushPS($var) - jmp = NEXT -end) +callPrim(addr::Int64) = primitives[-addr]() -defConst(name::AbstractString, val::Int64) = defPrim(name, quote - pushPS($val) - jmp = Next -end) +function defExistingVar(name::AbstractString, varAddr::Int64; flags::Int64=0) + defPrim(name, eval(:(() -> begin + pushPS($(varAddr)) + return NEXT + end))) +end + +function defConst(name::AbstractString, val::Int64; flags::Int64=0) + defPrim(name, eval(:(() -> begin + pushPS($(val)) + return NEXT + end))) +end + +function defNewVar(name::AbstractString, initial::Int64; flags::Int64=0) + createHeader(name, flags) + + varAddr = mem[HERE] + 1 + push!(primitives, eval(:(() -> begin + pushPS($(varAddr)) + return NEXT + end))) + mem[mem[HERE]] = -length(primitives); mem[HERE] += 1 + + mem[mem[HERE]] = initial; mem[HERE] += 1 + + return varAddr +end # Threading Primitives -NEXT = defPrim("NEXT", quote - W = memory[IP] - IP += 1 - X = memory[W] - jmp = X +NEXT = defPrim("NEXT", () -> begin + reg.W = mem[reg.IP] + reg.IP += 1 + X = mem[reg.W] + return X end) -DOCOL = defPrim("DOCOL", quote - pushRS(IP) - IP = W + 1 - jmp = NEXT +DOCOL = defPrim("DOCOL", () -> begin + pushRS(reg.IP) + reg.IP = reg.W + 1 + return NEXT end) -EXIT = defPrim("EXIT", quote - IP = popRS() - jmp = NEXT +EXIT = defPrim("EXIT", () -> begin + reg.IP = popRS() + return NEXT end) # Basic forth primitives -DROP = defPrim("DROP", quote +DROP = defPrim("DROP", () -> begin popPS() - jmp = NEXT + return NEXT end) -SWAP = defPrim("SWAP", quote - PS[PSP], PS[PSP-1] = PS[PSP-1], PS[PS] - jmp = NEXT +SWAP = defPrim("SWAP", () -> begin + mem[reg.PSP], mem[reg.PSP-1] = mem[reg.PSP-1], mem[reg.PSP] + return NEXT end) -DUP = defPrim("DUP", quote - pushPS(PS[PSP]) - jmp = NEXT +DUP = defPrim("DUP", () -> begin + pushPS(mem[reg.PSP]) + return NEXT end) -LIT = defPrim("LIT", quote - pushPS(memory[IP]) - IP += 1 - jmp = NEXT +LIT = defPrim("LIT", () -> begin + pushPS(mem[reg.IP]) + reg.IP += 1 + return NEXT end) # Memory primitives -STORE = defPrim("!", quote +STORE = defPrim("!", () -> begin addr = popPS() dat = popPS() - memory[addr] = dat - jmp = NEXT + mem[addr] = dat + return NEXT end) -FETCH = defPrim("@", quote +FETCH = defPrim("@", () -> begin addr = popPS() - pushPS(memory[addr]) - jmp = NEXT + pushPS(mem[addr]) + return NEXT end) -ADDSTORE = defPrim("+!", quote +ADDSTORE = defPrim("+!", () -> begin addr = popPS() toAdd = popPS() - memory[addr] += toAdd - jmp = NEXT + mem[addr] += toAdd + return NEXT end) -SUBSTORE = defPrim("-!", quote +SUBSTORE = defPrim("-!", () -> begin addr = popPS() toSub = popPS() - memory[addr] -= toSub - jmp = NEXT + mem[addr] -= toSub + return NEXT end) # Built-in variables -defVar("STATE", :STATE) -defVar("HERE", :HERE) -defVar("LATEST", :LATEST) -defVAR("BASE", :BASE) +defExistingVar("HERE", HERE) +defExistingVar("LATEST", LATEST) +defExistingVar("PSP0", PSP0) +defExistingVar("RSP0", RSP0) +defNewVar("STATE", 0) +defNewVar("BASE", 10) # Constants @@ -168,53 +221,114 @@ defConst("DOCOL", DOCOL) # Return Stack -TOR = defPrim(">R", quote +TOR = defPrim(">R", () -> begin pushRS(popPS()) - jmp = NEXT + return NEXT end) -FROMR = defPrim("R>", quote +FROMR = defPrim("R>", () -> begin pushPS(popRS()) + return NEXT end) -RSPFETCH = defPrim("RSP@", quote +RSPFETCH = defPrim("RSP@", () -> begin pushPS(RSP) - jmp = NEXT + return NEXT end) -RSPSTORE = defPrim("RSP!", quote +RSPSTORE = defPrim("RSP!", () -> begin RSP = popPS() - jmp = NEXT + return NEXT end) -RDROP = defPrim("RDROP", quote +RDROP = defPrim("RDROP", () -> begin popRS() - jmp = NEXT + return NEXT end) # Parameter Stack -PSPFETCH = defPrim("PSP@", quote +PSPFETCH = defPrim("PSP@", () -> begin pushPS(PSP) - jmp = NEXT + return NEXT end) -PSPSTORE = defPrim("PSP!", quote +PSPSTORE = defPrim("PSP!", () -> begin PSP = popPS() - jmp = NEXT + return NEXT end) # I/O -KEY = defPrim("KEY", quote - jmp = NEXT -end) +defConst("TIB", TIB) +NUMTIB = defNewVar("#TIB", 0) +TOIN = defNewVar(">IN", TIB) + +#KEY = defPrim("KEY", (reg) -> begin +# if toin >= numtib +# +# end +# +# return NEXT +#end) +# +#EMIT = defPrim("EMIT", (reg) -> begin +# +# return NEXT +#end) +# +#WORD = defPrim("WORD", (reg) -> begin +# +# return NEXT +#end) +# +#NUMBER = defPrim("NUMBER", (reg) -> begin +# +# return NEXT +#end) +# +#### VM loop #### +#function runVM(reg::Reg) +# jmp = NEXT +# while (jmp = callPrim(reg, jmp)) != 0 end +#end + +# Debugging tools + +function coredump(startAddr::Int64; count::Int64 = 16, cellsPerLine::Int64 = 8) + chars = Array{Char,1}(cellsPerLine) + + for i in 0:(count-1) + addr = startAddr + i + if i%cellsPerLine == 0 + print("$addr:") + end + + print("\t$(mem[addr]) ") + + if (mem[addr]>=32 && mem[addr]<176) + chars[i%cellsPerLine + 1] = Char(mem[addr]) + else + chars[i%cellsPerLine + 1] = '.' + end + + if i%cellsPerLine == cellsPerLine-1 + println(string("\t", ASCIIString(chars))) + end + end +end -# VM loop -jmp = NEXT -function runVM() - while true - eval(primitives[-jmp]) +function dumpPS() + count = reg.PSP - mem[PSP0] + + if count > 0 + print("<$count>") + for i in (mem[PSP0]+1):reg.PSP + print(" $(mem[i])") + end + println() + else + println("Parameter stack empty") end end