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+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