8608/assembler-8608.lua

322 lines
9.3 KiB
Lua

local function trim(s) return s:gsub("^ +", ""):gsub(" +$", "").."" end
local function getCodeDir()
local f0 = arg[0]:gsub("/", "\\")
local d0 = f0:match("(.+)[\\][^\\]+$") or "."
return d0
end
local function popenResult(cmd)
local fp = io.popen(cmd)
local out = fp:read("*a")
fp:close()
return out
end
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
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 = {}
local lastLabel = {}
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
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
end
return mem, code, symbols
end
local function fullPath(fn)
return popenResult(getCodeDir().."\\fullPath.bat \""..fn.."\"")
end
local function assembleFile(fn, arch)
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)
local mnems = {}
for _, instr in ipairs(arch.instructions) do
if instr.mnem then
local len = 1
for l in instr.mnem:gmatch("imm([0-9]+)") do len = len + tonumber(l)/8 end
mnems[instr.opcode] = { mnem = instr.mnem, rel = instr.rel, jmp = instr.jmp, len = len, }
end
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(arch, mem, code, symbols)
local mnems = mnemsFromArch(arch)
local addr = 0
local function nextByte(d) local b = mem[addr]; addr = addr+1; return b or d; end
local lastaddr = 0
local jmpaddrs = {}
while addr<=0xFFFF do
local startaddr = addr
local opcode = nextByte()
if opcode and ((not code) or code[startaddr]) then
local mnem = mnems[opcode]
if mnem then
if mnem.jmp then
local jmpdest
if mnem.rel then jmpdest = toSigned8(nextByte(0)) + addr
else jmpdest = nextByte(0)*256 + nextByte(0)
end
if jmpdest then
if not jmpaddrs[jmpdest] then
jmpaddrs[jmpdest] = { rel = mnem.rel, from = {}, }
end
table.insert(jmpaddrs[jmpdest].from, startaddr)
jmpaddrs[jmpdest].rel = jmpaddrs[jmpdest].rel and mnem.rel
end
else
addr = addr + mnem.len - 1
end
end
end
end
local labelnum, subnum = 0, 0
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
local startaddr = addr
local opcode = nextByte()
if opcode and ((not code) or code[startaddr]) then
local line = {}
local mnem = mnems[opcode].mnem or "???"
table.insert(line, trim(mnem:gsub("imm[0-9]+", "")))
local tlen = 1
for lens in mnem:gmatch("imm([0-9]+)") do local len = tonumber(lens)/8
if len==1 then
local data = nextByte(0)
local jmp
if mnems[opcode].rel then
local jmpdest = (addr + toSigned8(data))%65536
jmp = jmpaddrs[jmpdest]
if jmp then
table.insert(line, jmp.name)
--table.insert(line, ";")
--table.insert(line, "$"..string.format("%04X", jmpdest)..",")
end
end
if not jmp then table.insert(line, "$"..string.format("%02X", data)) end
elseif len==2 then
local data = nextByte(0)*256 + nextByte(0)
local jmp
if mnems[opcode].jmp then
local jmpdest = data
jmp = jmpaddrs[jmpdest]
if jmp then
table.insert(line, jmp.name)
--table.insert(line, ";")
end
end
if not jmp then table.insert(line, "$"..string.format("%04X", data)) end
else error("invalid imm len") end
tlen = tlen + len
end
local lineb = {}
for i = addr-tlen, addr-1 do
table.insert(lineb, string.format("%02X", mem[i] or 0))
end
local label = ""
local jmp = jmpaddrs[startaddr]
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(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")
end
local function memToHex(hex)
local mem = {}
local addr = 0
for d in hex:gmatch("[0-9a-fA-F][0-9a-fA-F]") do
mem[addr] = tonumber(d, 16)
addr = addr+1
end
return mem
end
local function disassembleHex(hex, arch)
return disassembleMemory(arch, memToHex(hex), nil, nil)
end
local printableCharsS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789`-=[]\\;\',./~!@#$%^&*()_+{}|:\"<> "
local printableChars = {}; for i = 1, #printableCharsS do printableChars[printableCharsS:sub(i, i)] = true end;
local function toPrintableChar(n)
local c = string.char(n)
return printableChars[c] and c or "?"
end
local function printMemory(mem)
local anynonempty = false
local lastbase = -16
local lastline = ""
local numreps = 0
local lines = {}
local function closereps(base)
if numreps~=0 then
table.insert(lines, "(repeated "..numreps.." more times, up to "..string.format("%04X", base+15)..")")
numreps = 0
end
end
for base = 0, 0xFFF0, 16 do
local line = {}
local strt = {}
local nonempty = false
for addr = base, base+15 do
if addr%4==0 then table.insert(line, " ") end
if mem[addr]==false then
nonempty = true
table.insert(line, "XX ")
table.insert(strt, "X")
elseif mem[addr] then
nonempty = true
table.insert(line, string.format("%02X", mem[addr]).." ")
table.insert(strt, toPrintableChar(mem[addr]))
else
table.insert(line, "-- ")
table.insert(strt, "-")
end
end
if nonempty then
local l = table.concat(line)
if l~=lastline or base~=lastbase+16 then
closereps(base-16)
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
lastline = l
lastbase = base
anynonempty = true
end
end
closereps(lastbase)
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,
}
ts.eval [[
function commandShiftBrick(%x, %y, %z) { commandToServer('shiftBrick', %x, %y, %z); }
function commandPlantBrick() { commandToServer('plantBrick'); }
]]
local function plantBrickAt(brickpos, pos)
local dx, dy, dz = pos[1]-brickpos[1], pos[2]-brickpos[2], pos[3]-brickpos[3]
ts.call("commandShiftBrick", dy, -dx, dz)
ts.call("commandPlantBrick")
brickpos[1], brickpos[2], brickpos[3] = pos[1], pos[2], pos[3]
end
local function buildMemory(mem, romsize, offset, len)
offset = offset or 0
local rombytes = romsize[1]*romsize[2]*romsize[3]/8
if len and len>rombytes then error("rom not big enough to hold "..len.." bytes (holds "..rombytes..")") end
if not len then
for i = 0, 0xFFFF do
if mem[i] and (i<offset or i>=offset+rombytes) then error("memory does not fit in rom at addr "..string.format("%04X", i)) end
end
end
local brickpos = {0, 0, 0}
for x = 0, romsize[1]-1 do
for y = 0, romsize[2]-1 do
for z = 0, romsize[3]-1 do
local addr = offset + ((romsize[3]/8)*(x + y*romsize[1]) + math.floor(z/8))
local pow = math.pow(2, z%8)
local data = (addr>=offset and ((not len) or addr<offset+len) and mem[addr]) or 0
local bit = math.floor(data/pow)%2
if bit==1 then plantBrickAt(brickpos, {x, -y, z}) end
end
end
end
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