remake assembler with customasm, misc, need to clean up
This commit is contained in:
@@ -1,483 +1,89 @@
|
||||
|
||||
local function loadutf8table(fn)
|
||||
local tt = {}
|
||||
for l in io.lines(fn) do if l~="" then
|
||||
local c, d = l:match("^([^ ]+) (.+)$")
|
||||
local t = {}; for v in d:gmatch("[^ ]+") do table.insert(t, tonumber(v, 16)) end;
|
||||
tt[c] = t
|
||||
end end
|
||||
return tt
|
||||
end
|
||||
local utf8table = loadutf8table((RelPath or "./").."utf8table.txt")
|
||||
|
||||
local function trim(s) return s:gsub("^ +", ""):gsub(" +$", "").."" end
|
||||
local function getutf8len(c)
|
||||
local d = c:byte()
|
||||
if bit.band(d, 0xE0)==0xC0 then return 2
|
||||
elseif bit.band(d, 0xF0)==0xE0 then return 3
|
||||
elseif bit.band(d, 0xF8)==0xF0 then return 4
|
||||
else error("invalid utf8 first byte: "..string.format("%02X", d)) end
|
||||
end
|
||||
local function validWordsFromInstrs(instrs)
|
||||
local words = {}
|
||||
for mnem, _ in pairs(instrs) do
|
||||
for word in mnem:gmatch("[^ ]+") do
|
||||
words[word] = true
|
||||
end
|
||||
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" 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)
|
||||
local imms = {}
|
||||
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
|
||||
table.insert(imms, { val = val, len = len } )
|
||||
return " imm"..(len*8).." "
|
||||
end
|
||||
local function addLabel(n)
|
||||
n = trim(n)
|
||||
local len = 2
|
||||
local linei8 = line:gsub(n, "imm8", 1, true):lower()
|
||||
if instrs[linei8] then len = 1 end
|
||||
table.insert(imms, { label = n, len = len } )
|
||||
return " imm"..(len*8).." "
|
||||
end
|
||||
|
||||
local mnem = " "..line:gsub(" ", " ").." "
|
||||
mnem = mnem:gsub("%- *", " %+%-")
|
||||
mnem = mnem:gsub("([%*%+])", " %1 ")
|
||||
mnem = mnem:gsub(" %-?%$[0-9a-fA-F]+ " , function(n) return addNum (n) end)
|
||||
mnem = mnem:gsub(" %-?0x[0-9a-fA-F]+ " , function(n) return addNum (n) end)
|
||||
mnem = mnem:gsub(" %-?0b[01]+ " , function(n) return addNum (n) end)
|
||||
mnem = mnem:gsub(" %-?[0-9a-fA-F]+h " , function(n) if not validWords[trim(n)] then return addNum (n) end end)
|
||||
mnem = mnem:gsub(" %-?[01]+b " , function(n) if not validWords[trim(n)] then return addNum (n) end end)
|
||||
mnem = mnem:gsub(" %-?[0-9]+ " , function(n) if not validWords[trim(n)] then return addNum (n) end end)
|
||||
mnem = mnem:gsub(" [a-zA-Z_][a-zA-Z0-9_%.]* ", function(n) if not validWords[trim(n)] then return addLabel(n) end end)
|
||||
mnem = trim(mnem):gsub(" +", " "):lower()
|
||||
|
||||
if not instrs[mnem] then mnem = mnem:gsub("%+ imm", "imm") end
|
||||
|
||||
return mnem, imms
|
||||
end
|
||||
local function addByte(state, val, code)
|
||||
assert(val>=-128 and val<=255, "invalid byte "..val)
|
||||
assert(state.memory[state.curAddr]==nil, "overwriting memory at $"..string.format("%04X", state.curAddr))
|
||||
state.memory[state.curAddr] = val%256
|
||||
if code then state.codeMap[state.curAddr] = true end
|
||||
state.curAddr = state.curAddr + 1
|
||||
end
|
||||
local function addWord(state, val, code)
|
||||
assert(val>=0 and val<=65535, "invalid word "..val)
|
||||
addByte(state, math.floor(val/256), code)
|
||||
addByte(state, val%256, code)
|
||||
end
|
||||
local function addSpace(state, len)
|
||||
for i = 1, len do
|
||||
assert(state.memory[state.curAddr]==nil, "overwriting memory at $"..string.format("%04X", state.curAddr))
|
||||
state.memory[state.curAddr] = false
|
||||
state.curAddr = state.curAddr + 1
|
||||
end
|
||||
end
|
||||
local function assembleInstruction(line, state, instrs, validWords)
|
||||
local mnem, imms = mnemFromLine(line, instrs, validWords)
|
||||
local opcode = instrs[mnem] or error("invalid instruction \""..line.."\" (mnem \""..mnem.."\")")
|
||||
local writeimms = true
|
||||
local padlen = 0
|
||||
local isInstr
|
||||
if type(opcode)=="function" then
|
||||
padlen, writeimms = opcode(imms)
|
||||
addSpace(state, padlen)
|
||||
elseif opcode>=0 then
|
||||
isInstr = true
|
||||
addByte(state, opcode, isInstr)
|
||||
end
|
||||
if writeimms then
|
||||
for _, imm in ipairs(imms) do
|
||||
if imm.val then
|
||||
if imm.len==1 then addByte(state, imm.val, isInstr)
|
||||
elseif imm.len==2 then addWord(state, imm.val, isInstr)
|
||||
else error("invalid imm len") end
|
||||
elseif imm.label then
|
||||
table.insert(state.labelReplacements, {
|
||||
name = imm.label,
|
||||
addr = state.curAddr,
|
||||
len = imm.len,
|
||||
rel = imm.len==1,
|
||||
isCode = isInstr,
|
||||
})
|
||||
state.curAddr = state.curAddr + imm.len
|
||||
else error("invalid imm") end
|
||||
end
|
||||
end
|
||||
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) 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 postEvaluateExpression(expr, labels)
|
||||
|
||||
end
|
||||
local function assembleCode(code, instrs, uexprs)
|
||||
local validWords = validWordsFromInstrs(instrs)
|
||||
|
||||
local state = {
|
||||
lineNum = 0,
|
||||
fileName = "",
|
||||
curAddr = 0,
|
||||
memory = {},
|
||||
codeMap = {},
|
||||
labelReplacements = {},
|
||||
labelAddrs = {},
|
||||
}
|
||||
|
||||
for line in code:gmatch("[^\n]+") do
|
||||
line = trim(line)
|
||||
if line:sub(1, 1)=="." then -- directive
|
||||
local dir, rest = line:match("^%.([^ ]+) *(.*)$")
|
||||
assert(dir and rest, "no directive on line "..line)
|
||||
local dirf = directiveFunctions[dir] or error("invalid directive "..dir)
|
||||
dirf(state, rest)
|
||||
elseif line:sub(#line, #line)==":" then -- label
|
||||
local name = line:sub(1, #line-1)
|
||||
assert(not state.labelAddrs[name], "redefinition of label "..name)
|
||||
state.labelAddrs[name] = state.curAddr
|
||||
elseif line:find("[^ ]") then
|
||||
assembleInstruction(line, state, instrs, validWords)
|
||||
end
|
||||
end
|
||||
|
||||
for _, rep in ipairs(state.labelReplacements) do
|
||||
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
|
||||
|
||||
local function getCodeDir()
|
||||
local f0 = arg[0]:gsub("/", "\\")
|
||||
local d0 = f0:match("(.+)[\\][^\\]+$") or "."
|
||||
return d0
|
||||
end
|
||||
|
||||
local function readFile(fn)
|
||||
local fi, err = io.open(fn, "r")
|
||||
if not fi then error("could not open file "..fn..": "..err) end
|
||||
local text = fi:read("*a")
|
||||
fi:close()
|
||||
return text
|
||||
local function popenResult(cmd)
|
||||
local fp = io.popen(cmd)
|
||||
local out = fp:read("*a")
|
||||
fp:close()
|
||||
return out
|
||||
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, uexprs)
|
||||
expr = expr:gsub("[^%+%-%*%/]+", function(word)
|
||||
local val = decodeNumber(word) or error("invalid number in expression: "..word)
|
||||
return val
|
||||
end)
|
||||
assert(not expr:find("[^a-zA-Z0-9_%(%)%+%-%*%/ \t\r\n]"), "invalid char in expression: "..expr)
|
||||
local exprf = loadstring("return "..expr)
|
||||
local eval = exprf() or error("invalid expr: "..expr)
|
||||
return eval
|
||||
end
|
||||
local function preprocessCode(code)
|
||||
code = "\n"..code.."\n"
|
||||
|
||||
-- apply brace labels and scoped labels
|
||||
local curscope = ""
|
||||
local codet = {}
|
||||
local wordt = {}
|
||||
local lastword = ""
|
||||
local function addword(word)
|
||||
lastword = word
|
||||
if word:sub(1, 1)=="." and not directiveFunctions[word:sub(2, #word)] then word = curscope..word end
|
||||
table.insert(codet, word)
|
||||
end
|
||||
for i = 1, #code do
|
||||
local c = code:sub(i, i)
|
||||
if c:find("[%.a-zA-Z0-9_]") then table.insert(wordt, c)
|
||||
else
|
||||
if #wordt>0 then
|
||||
addword(table.concat(wordt))
|
||||
wordt = {}
|
||||
end
|
||||
if c==":" and lastword:sub(1, 1)~="." and not lastword:find("_BRACE_") then
|
||||
curscope = lastword
|
||||
end
|
||||
table.insert(codet, c)
|
||||
local function memFromCustomasmHexstr(out)
|
||||
local mem = {}
|
||||
local addr = 0
|
||||
for hexS in out:gmatch("[0-9a-fA-F][0-9a-fA-F]") do
|
||||
local val = tonumber(hexS, 16)
|
||||
if val~=0 then
|
||||
mem[addr] = val
|
||||
end
|
||||
addr = addr + 1
|
||||
end
|
||||
|
||||
code = "\n"..table.concat(codet).."\n"
|
||||
|
||||
-- apply function macros
|
||||
local funcmacros = {}
|
||||
code = code:gsub(".define ([%.a-zA-Z0-9_]+)%(([^%)]+)%) ([^\n]+)", function(name, args, repl)
|
||||
local argt = separateCommas(args)
|
||||
for argidx, arg in ipairs(argt) do assert(not arg:find("[^a-zA-Z0-9_]"), "invalid character in macro arg name: "..name.." "..arg) end
|
||||
repl = " "..repl.." "
|
||||
local invoc = 0
|
||||
funcmacros[name] = function(b, callargs)
|
||||
invoc = invoc + 1
|
||||
local callargt = separateCommas(callargs)
|
||||
local callrepl = repl
|
||||
for argidx, arg in ipairs(argt) do
|
||||
local callarg = callargt[argidx]
|
||||
callrepl = callrepl:gsub("([^a-zA-Z0-9_])"..arg.."([^a-zA-Z0-9_])", "%1"..callarg.."%2")
|
||||
end
|
||||
callrepl = callrepl:gsub("(_BRACE_[0-9]+_)", "%1"..invoc.."_")
|
||||
return b..callrepl
|
||||
end
|
||||
return ""
|
||||
end)
|
||||
for name, replf in pairs(funcmacros) do code = code:gsub("([^a-zA-Z0-9_])"..name.." *%(([^%)]+)%)", replf) end
|
||||
|
||||
-- apply simple macros
|
||||
local simplemacros = {}
|
||||
code = code:gsub("%.define +([%.a-zA-Z0-9_]+) +([^\n]+)", function(name, repl)
|
||||
assert(not simplemacros[name], "Redefinition of macro "..name)
|
||||
simplemacros[name] = repl
|
||||
return ""
|
||||
end)
|
||||
--for name, repl in pairs(simplemacros) do code = code:gsub(name, repl, 1, true) end
|
||||
for name, repl in pairs(simplemacros) do
|
||||
local invoc = 0
|
||||
code = code:gsub("([^a-zA-Z0-9_])"..name.."([^a-zA-Z0-9_])", function(b, a)
|
||||
invoc = invoc+1
|
||||
return b..(repl:gsub("(_BRACE_[0-9]+_)", "%1"..invoc.."_"))..a
|
||||
end)
|
||||
print(name, code)
|
||||
local code = nil
|
||||
local symbols = nil
|
||||
return mem, code, symbols
|
||||
end
|
||||
|
||||
local function toData(s)
|
||||
local d = {}
|
||||
for p in s:gmatch("[0-9a-zA-Z][0-9a-zA-Z]") do
|
||||
table.insert(d, tonumber(p, 16))
|
||||
end
|
||||
return d
|
||||
end
|
||||
local function memFromCustomasmAnnotated(out)
|
||||
local mem = {}
|
||||
local code = {}
|
||||
local symbols = {}
|
||||
|
||||
code = code:gsub("\\\\", "\n")
|
||||
local lastLabel = {}
|
||||
|
||||
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), uexprs))
|
||||
exprt = {}
|
||||
for line in out:gmatch("[^\r\n]+") do
|
||||
if not line:find("^ outp | addr | data %(base 16%)$") then
|
||||
local addrS, dataS, comment = line:match("^[^|]+ | ([^|]+) | ([^;]+) ; (.+)$")
|
||||
local addr = tonumber(addrS, 16)
|
||||
local data = toData(dataS)
|
||||
if comment:sub(#comment, #comment)==":" then
|
||||
assert(#data==0)
|
||||
local label = comment:sub(1, #comment-1)
|
||||
local noDots = label:gsub("^%.+", "")
|
||||
local numDots = #label - #noDots
|
||||
local fullLabel = numDots>0 and lastLabel[numDots-1].."."..noDots or label
|
||||
lastLabel[numDots] = fullLabel
|
||||
symbols[addr] = fullLabel
|
||||
else
|
||||
table.insert(exprt, c)
|
||||
end
|
||||
else
|
||||
if parenLevel==0 then table.insert(codet, c)
|
||||
else table.insert(exprt, c) end
|
||||
end
|
||||
end
|
||||
code = table.concat(codet)
|
||||
|
||||
return code, uexprs
|
||||
end
|
||||
local function fixCode(code)
|
||||
code = code:gsub(",", " ")
|
||||
code = code:gsub(":([^\\/])", ":\n%1")
|
||||
code = code:gsub("[ \t]+:", ":")
|
||||
code = code:gsub("%]", " %] ")
|
||||
code = code:gsub("%[", " %[ ")
|
||||
code = code:gsub("%*", " %* ")
|
||||
code = code:gsub("\n[ \t\r\n]*", "\n")
|
||||
code = code:gsub(" +", " ")
|
||||
|
||||
return code
|
||||
end
|
||||
local stringEscapes = { ["\\"] = "\\", ["n"] = "\n", ["r"] = "\r", ["t"] = "\t", ["0"] = "\0", ["\""] = "\"", ["\'"] = "\'", }
|
||||
local prefixIdx = 0
|
||||
local function prefixCode(code, fn) -- fix strings, add line numbers
|
||||
prefixIdx = prefixIdx + 1
|
||||
|
||||
local outt = {}
|
||||
local outnextnl = {}
|
||||
local linenum = 1
|
||||
local skipnl = false
|
||||
local function last() return outt[#outt] end
|
||||
local function out(c) assert(type(c)=="string"); table.insert(outt, c); end
|
||||
local function outn(n) out("$"..string.format("%02X", n)) out("\\") end
|
||||
local function outnext(c) assert(type(c)=="string"); table.insert(outnextnl, c); end
|
||||
local state = "code" -- code, comment, string, stringesc, commentml
|
||||
|
||||
local lastbracelabel = 0
|
||||
local function bracelabel() lastbracelabel = lastbracelabel+1; return "_BRACE_"..string.format("%02d", prefixIdx)..lastbracelabel.."_"; end
|
||||
local bracestack = {}
|
||||
local bracehasmid = {}
|
||||
local lastnl = false
|
||||
|
||||
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");
|
||||
local i = 1
|
||||
while i <= #code do
|
||||
local c = code:sub(i, i)
|
||||
local cn = code:sub(i+1, i+1)
|
||||
local cp = code:sub(i-1, i-1)
|
||||
|
||||
if state=="code" then
|
||||
if c=="\r" then
|
||||
elseif c=="\n" then -- (c=="/" and cn~="/" and cn~="*")
|
||||
linenum = linenum+1
|
||||
if not skipnl then out("\n") out(".ln "..linenum); out("\n"); end
|
||||
newline()
|
||||
skipnl = false
|
||||
elseif c=="#" or c==";" or (c=="/" and cn=="/") then state = "comment"
|
||||
elseif c=="/" and cn=="*" then state = "commentml"
|
||||
elseif c=="\t" or c==" " then if (not lastnl) then out(" ") end
|
||||
elseif c=="\"" then state = "string" lastnl = false
|
||||
elseif c=="\\" then skipnl = true; out("\\");
|
||||
elseif c==":" then out(c); if skipnl then out("\\") else out("\n") end; lastnl = true;
|
||||
elseif c:find("^[a-zA-Z0-9_%.%$%(%)%*,%[%]%+%-%*%/]$") then out(c); lastnl = false
|
||||
elseif c=="{" then
|
||||
table.insert(bracestack, bracelabel())
|
||||
if not lastnl then out(bracestack[#bracestack].."MID") end
|
||||
outnext(bracestack[#bracestack].."START:"); outnext("\n");
|
||||
elseif c=="}" then
|
||||
if not lastnl then out(bracestack[#bracestack].."START") end
|
||||
if not bracehasmid[#bracestack] then outnext(bracestack[#bracestack].."MID:"); outnext("\n"); end
|
||||
outnext(bracestack[#bracestack].."END:"); outnext("\n");
|
||||
bracehasmid[#bracestack] = nil
|
||||
bracestack[#bracestack] = nil
|
||||
elseif c=="|" then
|
||||
if not lastnl then out(bracestack[#bracestack].."END") end
|
||||
outnext(bracestack[#bracestack].."MID:"); outnext("\n");
|
||||
bracehasmid[#bracestack] = true
|
||||
else error("invalid char "..c) end
|
||||
elseif state=="comment" then
|
||||
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
|
||||
if c=="\\" then state = "stringesc"
|
||||
elseif c=="\"" then state = "code"
|
||||
elseif c:byte()>=128 then
|
||||
utf8str = c
|
||||
utf8len = getutf8len(c)
|
||||
state = "stringutf8"
|
||||
else outn(c:byte()) end
|
||||
elseif state=="stringesc" then
|
||||
outn(string.byte(stringEscapes[c] or error("invalid escape "..c))); state = "string";
|
||||
elseif state=="stringutf8" then
|
||||
utf8str = utf8str..c
|
||||
if #utf8str == utf8len then
|
||||
local valt = utf8table[utf8str]
|
||||
if not valt then local datastr = ""; for i = 1, #utf8str do datastr = datastr .. string.format("%02X ", utf8str:sub(i, i):byte()) end;
|
||||
error("Unrecognized UTF-8 character: "..datastr); end
|
||||
for i, v in ipairs(valt) do outn(v) end
|
||||
state = "string"
|
||||
if comment:match("^[a-z]+") then
|
||||
for i, v in ipairs(data) do
|
||||
mem [addr+i-1] = v
|
||||
code[addr+i-1] = true
|
||||
end
|
||||
else
|
||||
for i, v in ipairs(data) do
|
||||
mem [addr+i-1] = v
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
i = i+1
|
||||
end
|
||||
assert(#bracestack==0, "unclosed brace")
|
||||
local code2 = table.concat(outt)
|
||||
|
||||
return code2
|
||||
return mem, code, symbols
|
||||
end
|
||||
local function fixFilename(fn)
|
||||
fn = fn:gsub("[^a-zA-Z0-9_]", "_")
|
||||
return fn
|
||||
end
|
||||
local function includeFile(fn)
|
||||
local code = readFile(fn)
|
||||
code = prefixCode(code, 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
|
||||
end
|
||||
local function instrsFromArch(arch)
|
||||
local function arraySize(imms) local s = 1; for i = 1, #imms do s = s*(imms[i].val or error("invalid array size")) end; return s; end
|
||||
local instrs = {
|
||||
imm8 = function() return 0, true end,
|
||||
imm16 = function() return 0, true end,
|
||||
byte = function() return 1, false end,
|
||||
word = function() return 2, false end,
|
||||
["byte imm8"] = function() return 0, true end,
|
||||
["word imm16"] = function() return 0, true end,
|
||||
["byte [ imm8 ]" ] = function(imms) return arraySize(imms) , false end,
|
||||
["byte [ imm16 ]"] = function(imms) return arraySize(imms) , false end,
|
||||
["word [ imm8 ]" ] = function(imms) return arraySize(imms)*2, false end,
|
||||
["word [ imm16 ]"] = function(imms) return arraySize(imms)*2, false end,
|
||||
}
|
||||
local function addMnem(mnem, opcode)
|
||||
instrs[mnem] = opcode
|
||||
if mnem:find("%*") then instrs[mnem:gsub("%*", "%[").." ]"] = opcode end
|
||||
end
|
||||
for _, instr in ipairs(arch.instructions) do
|
||||
if instr.mnem then
|
||||
local mnem = instr.mnem
|
||||
mnem = mnem:gsub("([%*%+%-])", " %1 ")
|
||||
mnem = trim(mnem):gsub(" +", " ")
|
||||
addMnem(mnem, instr.opcode)
|
||||
local alias = arch.aliases[trim(mnem)]
|
||||
if alias then for _, v in ipairs(alias) do addMnem(v, instr.opcode) end end
|
||||
end
|
||||
end
|
||||
return instrs
|
||||
|
||||
local function fullPath(fn)
|
||||
return popenResult(getCodeDir().."\\fullPath.bat \""..fn.."\"")
|
||||
end
|
||||
|
||||
local function assembleFile(fn, arch)
|
||||
local code = includeFile(fn)
|
||||
code, uexprs = preprocessCode(code)
|
||||
code = fixCode(code)
|
||||
local instrs = instrsFromArch(arch)
|
||||
local mem, code = assembleCode(code, instrs, uexprs)
|
||||
return mem, code
|
||||
local fnf = fullPath(fn)
|
||||
--local out = popenResult("customasm -p -q --format hexstr \""..fnf.."\"")
|
||||
--return memFromCustomasmHexstr(out)
|
||||
local out = popenResult("customasm -p -q --format annotated,base:16,group:2 \""..fnf.."\"")
|
||||
return memFromCustomasmAnnotated(out)
|
||||
end
|
||||
|
||||
local function mnemsFromArch(arch)
|
||||
@@ -491,8 +97,11 @@ local function mnemsFromArch(arch)
|
||||
end
|
||||
return mnems
|
||||
end
|
||||
|
||||
local function symbolCrunchDots(s) return s:gsub("[^%.]+%.", "%.").."" end
|
||||
|
||||
local function toSigned8(x) return x>=128 and x-256 or x end
|
||||
local function disassembleMemory(mem, code, arch)
|
||||
local function disassembleMemory(arch, mem, code, symbols)
|
||||
local mnems = mnemsFromArch(arch)
|
||||
|
||||
local addr = 0
|
||||
@@ -524,11 +133,13 @@ local function disassembleMemory(mem, code, arch)
|
||||
end
|
||||
end
|
||||
local labelnum, subnum = 0, 0
|
||||
for _, jmp in pairs(jmpaddrs) do
|
||||
if jmp.rel then jmp.name = "label_" ..labelnum; labelnum = labelnum+1;
|
||||
else jmp.name = "subroutine_"..subnum ; subnum = subnum +1; end
|
||||
for dest, jmp in pairs(jmpaddrs) do
|
||||
if symbols[dest] then jmp.name = symbolCrunchDots(symbols[dest])
|
||||
elseif jmp.rel then jmp.name = "label_" ..labelnum; labelnum = labelnum+1;
|
||||
else jmp.name = "subroutine_"..subnum ; subnum = subnum +1; end
|
||||
end
|
||||
|
||||
local maxLabelLen = 20
|
||||
local lines = {}
|
||||
addr = 0
|
||||
while addr<=0xFFFF do
|
||||
@@ -574,11 +185,14 @@ local function disassembleMemory(mem, code, arch)
|
||||
end
|
||||
local label = ""
|
||||
local jmp = jmpaddrs[startaddr]
|
||||
if jmp then label = jmp.name..":" end
|
||||
if symbols[startaddr] then label = symbolCrunchDots(symbols[startaddr])..":"
|
||||
elseif jmp then label = jmp.name..":" end
|
||||
local lb = table.concat(lineb, " ")
|
||||
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, " "))
|
||||
table.insert(lines, string.format("%04X", addr-tlen).." | "..(" "):rep(8-#lb)..lb.." | "..(" "):rep(maxLabelLen-#label)..label.." "..table.concat(line, " "))
|
||||
lastaddr = addr
|
||||
elseif opcode and opcode~=0 then
|
||||
table.insert(lines, "data: "..string.format("%02X", opcode))
|
||||
end
|
||||
end
|
||||
return table.concat(lines, "\n")
|
||||
@@ -593,7 +207,7 @@ local function memToHex(hex)
|
||||
return mem
|
||||
end
|
||||
local function disassembleHex(hex, arch)
|
||||
return disassembleMemory(memToHex(hex), arch)
|
||||
return disassembleMemory(arch, memToHex(hex), nil, nil)
|
||||
end
|
||||
|
||||
local printableCharsS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789`-=[]\\;\',./~!@#$%^&*()_+{}|:\"<> "
|
||||
@@ -693,31 +307,15 @@ local function buildMemory(mem, romsize, offset, len)
|
||||
end
|
||||
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
|
||||
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); }
|
||||
]]
|
||||
if not HasTs then AssembleBuildFile(arg[1] or "../8608programs/test.asm", "16 16 8", "0", "256") end
|
||||
if arg[1] then
|
||||
local fn = arg[1]
|
||||
local mem, code, symbols = assembleFile(fn)
|
||||
local arch = dofile(getCodeDir().."\\rom-8608-defs.lua")
|
||||
print(disassembleMemory(arch, mem, code, symbols))
|
||||
else
|
||||
return {
|
||||
assembleFile = assembleFile,
|
||||
disassembleMemory = disassembleMemory,
|
||||
printMemory = printMemory,
|
||||
}
|
||||
end
|
||||
|
||||
return {
|
||||
assembleFile = assembleFile,
|
||||
disassembleMemory = disassembleMemory,
|
||||
printMemory = printMemory,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user