add emulator
This commit is contained in:
@@ -1,6 +1,4 @@
|
||||
|
||||
local arch8608 = require("rom-8608-defs")
|
||||
|
||||
local function loadutf8table(fn)
|
||||
local tt = {}
|
||||
for l in io.lines(fn) do if l~="" then
|
||||
@@ -10,7 +8,7 @@ local function loadutf8table(fn)
|
||||
end end
|
||||
return tt
|
||||
end
|
||||
local utf8table = loadutf8table("./utf8table.txt")
|
||||
local utf8table = loadutf8table((RelPath or "./").."utf8table.txt")
|
||||
|
||||
local function trim(s) return s:gsub("^ +", ""):gsub(" +$", "").."" end
|
||||
local function getutf8len(c)
|
||||
@@ -29,19 +27,25 @@ local function validWordsFromInstrs(instrs)
|
||||
end
|
||||
return words
|
||||
end
|
||||
local function lobyte(n) return n%256 end
|
||||
local function hibyte(n) return math.floor(n/256) end
|
||||
local function decodeNumber(n)
|
||||
n = trim(n)
|
||||
local sign = 1; if n:sub(1, 1)=="-" then sign = -1; n = n:sub(2, #n); end;
|
||||
if n:sub(1, 1)=="$" then return sign*(tonumber(n:sub(2, #n ), 16) or error("invalid hex number "..n)), math.ceil((#n-1)/2)
|
||||
elseif n:sub(1, 2)=="0x" then return sign*(tonumber(n:sub(3, #n ), 16) or error("invalid hex number "..n)), math.ceil((#n-2)/2)
|
||||
elseif n:sub(#n, #n)=="h" then return sign*(tonumber(n:sub(1, #n-1), 16) or error("invalid hex number "..n)), math.ceil((#n-1)/2)
|
||||
elseif n:sub(1, 2)=="0b" then return sign*(tonumber(n:sub(3, #n ), 2) or error("invalid binary number "..n)), math.ceil((#n-2)/8)
|
||||
elseif n:sub(#n, #n)=="b" then return sign*(tonumber(n:sub(1, #n-1), 2) or error("invalid binary number "..n)), math.ceil((#n-1)/8)
|
||||
else
|
||||
if n:sub(1, 1)=="$" then return sign*(tonumber(n:sub(2, #n ), 16) or error("invalid hex number "..n)), math.ceil((#n-1)/2)
|
||||
elseif n:sub(1, 2)=="0x" then return sign*(tonumber(n:sub(3, #n ), 16) or error("invalid hex number "..n)), math.ceil((#n-2)/2)
|
||||
elseif n:sub(#n, #n)=="h" and n:find("^[0-9a-fA-F]+h$") then return sign*(tonumber(n:sub(1, #n-1), 16) or error("invalid hex number "..n)), math.ceil((#n-1)/2)
|
||||
elseif n:sub(1, 2)=="0b" then return sign*(tonumber(n:sub(3, #n ), 2) or error("invalid binary number "..n)), math.ceil((#n-2)/8)
|
||||
elseif n:sub(#n, #n)=="b" and n:find("^[01]+b$") then return sign*(tonumber(n:sub(1, #n-1), 2) or error("invalid binary number "..n)), math.ceil((#n-1)/8)
|
||||
elseif n:sub(1, 3)=="lo(" and n:sub(#n, #n)==")" then return lobyte(decodeNumber(n:sub(4, #n-1))), 1
|
||||
elseif n:sub(1, 3)=="hi(" and n:sub(#n, #n)==")" then return hibyte(decodeNumber(n:sub(4, #n-1))), 1
|
||||
elseif n:find("^[0-9]+$") then
|
||||
local v = sign*(tonumber(n) or error("invalid decimal number "..n))
|
||||
if v>=-128 and v<=255 then return v, 1
|
||||
elseif v>=-32768 and v<=65535 then return v, 2
|
||||
else error("out-of-range number "..v) end
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
local function mnemFromLine(line, instrs, validWords)
|
||||
@@ -49,6 +53,7 @@ local function mnemFromLine(line, instrs, validWords)
|
||||
local function addNum(n)
|
||||
n = trim(n)
|
||||
local val, len = decodeNumber(n)
|
||||
assert(val and len, "invalid number "..n)
|
||||
local linei8 = line:gsub(n, "imm8", 1, true):lower()
|
||||
local linei16 = line:gsub(n, "imm16", 1, true):lower()
|
||||
if len==1 and (not instrs[linei8]) and instrs[linei16] then len = 2 end
|
||||
@@ -134,12 +139,15 @@ end
|
||||
local directiveFunctions = {
|
||||
fn = function(state, fn) state.fileName = fn end,
|
||||
ln = function(state, ln) state.lineNum = tonumber(ln) end,
|
||||
org = function(state, addr) state.curAddr = decodeNumber(addr) end,
|
||||
org = function(state, addr) state.curAddr = decodeNumber(addr) or error("Invalid origin \""..addr.."\"") end,
|
||||
align = function(state, alns) local aln = decodeNumber(alns); if state.curAddr % aln ~= 0 then state.curAddr = state.curAddr + (aln - state.curAddr%aln) end end,
|
||||
define = true,
|
||||
space = function(state, amts) local amt = decodeNumber(amts); state.curAddr = state.curAddr + amt; end,
|
||||
}
|
||||
local function assembleCode(code, instrs)
|
||||
local function postEvaluateExpression(expr, labels)
|
||||
|
||||
end
|
||||
local function assembleCode(code, instrs, uexprs)
|
||||
local validWords = validWordsFromInstrs(instrs)
|
||||
|
||||
local state = {
|
||||
@@ -169,11 +177,19 @@ local function assembleCode(code, instrs)
|
||||
end
|
||||
|
||||
for _, rep in ipairs(state.labelReplacements) do
|
||||
local labelAddr = state.labelAddrs[rep.name] or error("no label named "..rep.name)
|
||||
state.curAddr = rep.addr
|
||||
if rep.len==1 then addByte(state, labelAddr-(rep.addr+1), rep.isCode)
|
||||
elseif rep.len==2 then addWord(state, labelAddr , rep.isCode)
|
||||
else error("invalid labelreplace len") end
|
||||
local expr = uexprs[rep.name]
|
||||
if expr then
|
||||
local val = postEvaluateExpression(expr, state.labelAddrs)
|
||||
if rep.len==1 then addByte(state, val, rep.isCode)
|
||||
elseif rep.len==2 then addWord(state, val, rep.isCode)
|
||||
else error("invalid expr replace len "..rep.len) end
|
||||
else
|
||||
local labelAddr = state.labelAddrs[rep.name] or error("no label named "..rep.name)
|
||||
state.curAddr = rep.addr
|
||||
if rep.len==1 then addByte(state, labelAddr-(rep.addr+1), rep.isCode)
|
||||
elseif rep.len==2 then addWord(state, labelAddr , rep.isCode)
|
||||
else error("invalid labelreplace len "..rep.len) end
|
||||
end
|
||||
end
|
||||
|
||||
return state.memory, state.codeMap
|
||||
@@ -190,7 +206,7 @@ end
|
||||
local function separateCommas(l)
|
||||
local c = {}; for a in l:gmatch("[^,]+") do table.insert(c, trim(a)) end; return c;
|
||||
end
|
||||
local function evaluateExpression(expr)
|
||||
local function evaluateExpression(expr, uexprs)
|
||||
expr = expr:gsub("[^%+%-%*%/]+", function(word)
|
||||
local val = decodeNumber(word) or error("invalid number in expression: "..word)
|
||||
return val
|
||||
@@ -267,18 +283,23 @@ local function preprocessCode(code)
|
||||
|
||||
code = code:gsub("\\", "\n")
|
||||
|
||||
local uexprs = {}
|
||||
|
||||
local codet = {}
|
||||
local exprt = {}
|
||||
local parenLevel = 0
|
||||
for i = 1, #code do
|
||||
local c = code:sub(i, i)
|
||||
if c=="(" then
|
||||
if parenLevel>0 then table.insert(exprt, c) end
|
||||
parenLevel = parenLevel+1
|
||||
elseif c==")" then
|
||||
parenLevel = parenLevel-1
|
||||
if parenLevel==0 then
|
||||
table.insert(codet, evaluateExpression(table.concat(exprt)))
|
||||
table.insert(codet, evaluateExpression(table.concat(exprt), uexprs))
|
||||
exprt = {}
|
||||
else
|
||||
table.insert(exprt, c)
|
||||
end
|
||||
else
|
||||
if parenLevel==0 then table.insert(codet, c)
|
||||
@@ -287,7 +308,7 @@ local function preprocessCode(code)
|
||||
end
|
||||
code = table.concat(codet)
|
||||
|
||||
return code
|
||||
return code, uexprs
|
||||
end
|
||||
local function fixCode(code)
|
||||
code = code:gsub(",", " ")
|
||||
@@ -322,6 +343,14 @@ local function prefixCode(code, fn) -- fix strings, add line numbers
|
||||
local utf8str = ""
|
||||
local utf8len = 0
|
||||
|
||||
local function newline()
|
||||
lastnl = true
|
||||
for _, v in ipairs(outnextnl) do
|
||||
if v=="\n" and skipnl then out("\\")
|
||||
else out(v) end
|
||||
end; outnextnl = {};
|
||||
end
|
||||
|
||||
out(".ln 1"); out("\n");
|
||||
for i = 1, #code do
|
||||
local c = code:sub(i, i)
|
||||
@@ -333,11 +362,7 @@ local function prefixCode(code, fn) -- fix strings, add line numbers
|
||||
elseif c=="\n" or (c=="/" and cn~="/" and cn~="*") then
|
||||
linenum = linenum+1
|
||||
if not skipnl then out("\n") out(".ln "..linenum); out("\n"); end
|
||||
lastnl = true
|
||||
for _, v in ipairs(outnextnl) do
|
||||
if v=="\n" and skipnl then out("\\")
|
||||
else out(v) end
|
||||
end; outnextnl = {};
|
||||
newline()
|
||||
skipnl = false
|
||||
elseif c=="#" or c==";" or (c=="/" and cn=="/") then state = "comment"
|
||||
elseif c=="/" and cn=="*" then state = "commentml"
|
||||
@@ -362,7 +387,7 @@ local function prefixCode(code, fn) -- fix strings, add line numbers
|
||||
bracehasmid[#bracestack] = true
|
||||
else error("invalid char "..c) end
|
||||
elseif state=="comment" then
|
||||
if c=="\n" then state = "code" out("\n") lastnl = true end
|
||||
if c=="\n" then state = "code" out("\n") newline() end
|
||||
elseif state=="commentml" then
|
||||
if c=="/" and cp=="*" then state = "code" end
|
||||
elseif state=="string" then
|
||||
@@ -401,6 +426,7 @@ local function includeFile(fn)
|
||||
local fnf = fixFilename(fn)
|
||||
code = ".fn "..fnf.."\n"..code
|
||||
code = code:gsub(".include ([^\r\n]+)", function(fn2)
|
||||
fn2 = fn:gsub("[^\\/]+$", "")..fn2
|
||||
return "\n"..includeFile(fn2).."\n"..".fn "..fnf.."\n"
|
||||
end)
|
||||
return code
|
||||
@@ -437,10 +463,10 @@ local function instrsFromArch(arch)
|
||||
end
|
||||
local function assembleFile(fn, arch)
|
||||
local code = includeFile(fn)
|
||||
code = preprocessCode(code)
|
||||
code, uexprs = preprocessCode(code)
|
||||
code = fixCode(code)
|
||||
local instrs = instrsFromArch(arch)
|
||||
local mem, code = assembleCode(code, instrs)
|
||||
local mem, code = assembleCode(code, instrs, uexprs)
|
||||
return mem, code
|
||||
end
|
||||
|
||||
@@ -457,7 +483,6 @@ local function mnemsFromArch(arch)
|
||||
end
|
||||
local function toSigned8(x) return x>=128 and x-256 or x end
|
||||
local function disassembleMemory(mem, code, arch)
|
||||
print("Disassembly:")
|
||||
local mnems = mnemsFromArch(arch)
|
||||
|
||||
local addr = 0
|
||||
@@ -494,6 +519,7 @@ local function disassembleMemory(mem, code, arch)
|
||||
else jmp.name = "subroutine_"..subnum ; subnum = subnum +1; end
|
||||
end
|
||||
|
||||
local lines = {}
|
||||
addr = 0
|
||||
while addr<=0xFFFF do
|
||||
local startaddr = addr
|
||||
@@ -540,12 +566,12 @@ local function disassembleMemory(mem, code, arch)
|
||||
local jmp = jmpaddrs[startaddr]
|
||||
if jmp then label = jmp.name..":" end
|
||||
local lb = table.concat(lineb, " ")
|
||||
if lastaddr~=addr-tlen then print("...") end
|
||||
print(string.format("%04X", addr-tlen).." | "..(" "):rep(8-#lb)..lb.." | "..(" "):rep(13-#label)..label.." "..table.concat(line, " "))
|
||||
if lastaddr~=addr-tlen then table.insert(lines, "...") end
|
||||
table.insert(lines, string.format("%04X", addr-tlen).." | "..(" "):rep(8-#lb)..lb.." | "..(" "):rep(13-#label)..label.." "..table.concat(line, " "))
|
||||
lastaddr = addr
|
||||
end
|
||||
end
|
||||
print()
|
||||
return table.concat(lines, "\n")
|
||||
end
|
||||
local function memToHex(hex)
|
||||
local mem = {}
|
||||
@@ -557,7 +583,7 @@ local function memToHex(hex)
|
||||
return mem
|
||||
end
|
||||
local function disassembleHex(hex, arch)
|
||||
disassembleMemory(memToHex(hex), arch)
|
||||
return disassembleMemory(memToHex(hex), arch)
|
||||
end
|
||||
|
||||
local printableCharsS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789`-=[]\\;\',./~!@#$%^&*()_+{}|:\"<> "
|
||||
@@ -567,14 +593,14 @@ local function toPrintableChar(n)
|
||||
return printableChars[c] and c or "?"
|
||||
end
|
||||
local function printMemory(mem)
|
||||
print("Memory Dump:")
|
||||
local anynonempty = false
|
||||
local lastbase = -16
|
||||
local lastline = ""
|
||||
local numreps = 0
|
||||
local lines = {}
|
||||
local function closereps(base)
|
||||
if numreps~=0 then
|
||||
print("(repeated "..numreps.." more times, up to "..string.format("%04X", base+15)..")")
|
||||
table.insert(lines, "(repeated "..numreps.." more times, up to "..string.format("%04X", base+15)..")")
|
||||
numreps = 0
|
||||
end
|
||||
end
|
||||
@@ -601,8 +627,8 @@ local function printMemory(mem)
|
||||
local l = table.concat(line)
|
||||
if l~=lastline or base~=lastbase+16 then
|
||||
closereps(base-16)
|
||||
if base ~= lastbase+16 then print("...") end
|
||||
print(string.format("%04X", base).." | "..l.." | "..table.concat(strt))
|
||||
if base ~= lastbase+16 then table.insert(lines, "...") end
|
||||
table.insert(lines, string.format("%04X", base).." | "..l.." | "..table.concat(strt))
|
||||
else
|
||||
numreps = numreps+1
|
||||
end
|
||||
@@ -612,10 +638,12 @@ local function printMemory(mem)
|
||||
end
|
||||
end
|
||||
closereps(lastbase)
|
||||
if not anynonempty then print("Empty") end
|
||||
print()
|
||||
if not anynonempty then table.insert(lines, "Empty") end
|
||||
|
||||
return table.concat(lines, "\n")
|
||||
end
|
||||
|
||||
local HasTs = ts~=nil
|
||||
local ts = ts or {
|
||||
call = function() end,
|
||||
eval = function() end,
|
||||
@@ -656,17 +684,30 @@ local function buildMemory(mem, romsize, offset, len)
|
||||
end
|
||||
|
||||
local function strtovec(str) local v = {}; for word in str:gmatch("[^ \t\r\n]+") do table.insert(v, tonumber(word)) end; return v; end
|
||||
function AssembleFile(fn, romsizes, offsets, lens) local offset = tonumber(offsets); local len = tonumber(lens); local romsize = strtovec(romsizes);
|
||||
local arch = arch8608
|
||||
local mem, code = assembleFile(fn, arch)
|
||||
print(""..fn:match("[^/\\]+$").."\n")
|
||||
printMemory(mem)
|
||||
assert(#romsize==3, "incorrect rom size")
|
||||
buildMemory(mem, romsize, offset, len)
|
||||
disassembleMemory(mem, code, arch)
|
||||
if HasTs or (not AsmIncluded) then
|
||||
function AssembleBuildFile(fn, romsizes, offsets, lens) local offset = tonumber(offsets); local len = tonumber(lens); local romsize = strtovec(romsizes);
|
||||
local arch = require("rom-8608-defs")
|
||||
local mem, code = assembleFile(fn, arch)
|
||||
print(""..fn:match("[^/\\]+$").."\n")
|
||||
|
||||
print("Memory Dump:")
|
||||
print(printMemory(mem))
|
||||
print()
|
||||
print("Disassembly:")
|
||||
print(disassembleMemory(mem, code, arch))
|
||||
print()
|
||||
|
||||
assert(#romsize==3, "incorrect rom size")
|
||||
buildMemory(mem, romsize, offset, len)
|
||||
end
|
||||
ts.eval [[
|
||||
function AssembleBuildFile(%fn, %romsize, %offset, %len) { luacall("AssembleBuildFile", strReplace(%fn, "$", "Add-ons/_misc/rom/8608programs/"), %romsize, %offset, %len); }
|
||||
]]
|
||||
AssembleBuildFile(arg[1] or "../8608programs/test.asm", "16 16 8", "0", "256")
|
||||
end
|
||||
ts.eval [[
|
||||
function AssembleFile(%fn, %romsize, %offset, %len) { luacall("AssembleFile", strReplace(%fn, "$", "Add-ons/_misc/rom/8608programs/"), %romsize, %offset, %len); }
|
||||
]]
|
||||
|
||||
if arg then AssembleFile(arg[1] or "../8608programs/test.asm", "16 16 8", "0", "256") end
|
||||
return {
|
||||
assembleFile = assembleFile,
|
||||
disassembleMemory = disassembleMemory,
|
||||
printMemory = printMemory,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user