local debugInfo = {} local function getDebugInfo() return " ("..debugInfo.mnem.." sub "..debugInfo.sub..")" end local function hex(n) return string.format("%02X", n) end local function cycleAddSignals(cycle, cyc2, cyc2t, ops, sigs) for _, name in ipairs(cycle) do if ops[name] then cycleAddSignals(ops[name], cyc2, cyc2t, ops, sigs) elseif sigs[name] then if not cyc2t[name] then cyc2t[name] = true table.insert(cyc2, name) else error("overspecified signal name: "..name..getDebugInfo()) end else error("invalid signal name: "..name.." does not name a signal or operation") end end end local function cycleSimplify(cycle, ops, sigs) local cyc2, cyc2t = {}, {} cycleAddSignals(cycle, cyc2, cyc2t, ops, sigs) return cyc2 end local function cycleToUcycle(cycle, ops, sigs) local ucycle = {} local cyc2 = cycleSimplify(cycle, ops, sigs) local hasbase = false local nonempty = false for _, name in ipairs(cyc2) do nonempty = true if name=="always1" then hasbase = true end local sig = sigs[name] assert(sig, "no signal named "..name) for i, sbit in ipairs(sig) do ucycle[sbit.rom] = ucycle[sbit.rom] or {} assert(not ucycle[sbit.rom][sbit.bit], "multiply defined bit") ucycle[sbit.rom][sbit.bit] = true end end if nonempty and (not hasbase) then error("cycle has no base"..getDebugInfo()) end return ucycle end local function encodeInstruction(opcode, instr, ucode, ops, sigs) for sub, cycle in ipairs(instr) do debugInfo.sub = sub-1 local ucycle = cycleToUcycle(cycle, ops, sigs) local uaddr = opcode*4 + (sub-1) assert(not ucode[uaddr], "overused ucode addr "..uaddr) ucode[uaddr] = ucycle end end local function sigsFromRoms(roms) local sigs = {} for romidx, rom in ipairs(roms) do for sigidx, sig in ipairs(rom.signals) do sigs[sig] = sigs[sig] or {} table.insert(sigs[sig], { rom = romidx, bit = sigidx-1, }) end end return sigs end local relJmpStr = [[ %s {addr} => { reladdr = addr - $ - 2 assert(reladdr <= 127, "%s: Relative jump target is too far away") assert(reladdr >= -128, "%s: Relative jump target is too far away") %s @ reladdr`8 }]] local wordRelStr = [[ %s+{value: i%i} => { assert(value <= %i, "Relative address is too far away") assert(value >= %i, "Relative address is too far away") %s @ value`%i } %s-{value: i%i} => { mvalue = -value assert(mvalue <= %i, "Relative address is too far away") assert(mvalue >= %i, "Relative address is too far away") %s @ mvalue`%i }]] local function getAsmCode(mnem, instr) local reljmp = instr.jmp and instr.rel local opcodeS = string.format("$%02X", instr.opcode) if reljmp then assert(mnem:find("imm8"), "relative jump without imm8") local mnemPart = mnem:gsub(" imm8", "") return string.format(relJmpStr, mnemPart, mnemPart, mnemPart, opcodeS ) elseif mnem:find("%+imm") then local mnemPart, bitsS = mnem:match("^([^%+]+)%+imm([0-9]+)$") local bits = tonumber(bitsS) local maxVal = math.pow(2, bits-1)-1 local minVal = -math.pow(2, bits-1) return string.format(wordRelStr, mnemPart, bits, maxVal, minVal, opcodeS, bits, mnemPart, bits, maxVal, minVal, opcodeS, bits ) elseif mnem:find("imm8") then mnem = mnem:gsub("imm8", "{value: i8}") return mnem.." => "..opcodeS.." @ value" elseif mnem:find("imm16") then mnem = mnem:gsub("imm16", "{value: i16}") return mnem.." => "..opcodeS.." @ value" else return mnem.." => "..opcodeS end end local function getAsmsFromInstr(instr, aliases) local mnem = instr.mnem local t = {} if mnem then table.insert(t, getAsmCode(mnem, instr)) if aliases[mnem] then for _, mnem2 in ipairs(aliases[mnem]) do table.insert(t, getAsmCode(mnem2, instr)) end end end return t end local function archToUcode(arch) local sigs = sigsFromRoms(arch.roms) local ops = arch.operations local ucode = {} local opcodesUsed = {} local numOpcodesUsed = 0 local infolines = {} local asmlines = {} local catlet = "X" for _, instr in ipairs(arch.instructions) do if instr.category then catlet = instr.catlet table.insert(infolines, "\n") table.insert(infolines, instr.category.." ("..catlet.."):\n") else local mnem = instr.mnem or error("instr has no mnem") local opcode = instr.opcode or error("instr has no opcode "..mnem) local ncycles = instr.ncycles or #instr assert(#instr <= 8, "operation too long "..opcode) local len = math.ceil(#instr / 4) if len>1 then assert(opcode%2==0, "len>1 on odd opcode") end for i = 1, len do local opcode2 = opcode+i-1 assert(not opcodesUsed[opcode2], "overused opcode "..hex(opcode)) opcodesUsed[opcode2] = catlet numOpcodesUsed = numOpcodesUsed+1 end debugInfo.mnem = mnem encodeInstruction(opcode, instr, ucode, ops, sigs) if instr.desc then table.insert(infolines, mnem..(" "):rep(13-#mnem)..hex(opcode).." "..ncycles.." "..instr.desc.."\n") end local asms = getAsmsFromInstr(instr, arch.aliases) for _, a in ipairs(asms) do table.insert(asmlines, a) end end end local lt = {"Opcodes used: "..numOpcodesUsed.."/255\n", " "} for i = 0, 15 do table.insert(lt, hex(i):sub(2, 2)) end table.insert(lt, "\n") for line = 0, 255, 16 do table.insert(lt, hex(line).." | ") for opcode = line, line+15 do if opcodesUsed[opcode] then table.insert(lt, opcodesUsed[opcode]) else table.insert(lt, "-") end end table.insert(lt, "\n") end -- Output instruction list local info = table.concat(infolines).."\n"..table.concat(lt) --print(info) local fo = io.open("instructionList.txt", "w") if fo then fo:write(info) fo:close() end -- Output customASM definitions local fi = io.open("arch-8608-template.asm") local asmDataStr = fi:read("*a") fi:close() local asmTable = string.format(asmDataStr, table.concat(asmlines, "\n\t")) local fo = io.open("arch-8608.asm", "w") if fo then fo:write(asmTable) fo:close() end return ucode end local function ucodeToBricks(ucode, roms) local bricks = {} for uaddr, ucycle in pairs(ucode) do for romidx, uline in ipairs(ucycle) do for bitidx, t in pairs(uline) do local rom = roms[romidx] assert(bitidx >=0 and bitidx <= rom.size[3]-1) local x = rom.pos[1] + (uaddr % rom.size[1]) local y = rom.pos[2] - math.floor(uaddr / rom.size[1]) local z = rom.pos[3] + bitidx bricks[x] = bricks[x] or {} bricks[x][y] = bricks[x][y] or {} assert(not bricks[x][y][z], "overlapping brick at "..x.." "..y.." "..z) bricks[x][y][z] = true end end end return bricks end ts = ts or { eval = function() end, call = function() end, } ts.eval [[ function commandShiftBrick(%x, %y, %z) { commandToServer('shiftBrick', %x, %y, %z); } function commandPlantBrick() { commandToServer('plantBrick'); } ]] local function plantBrickAt(pos, brickPos) local dx, dy, dz = pos[1]-brickPos[1], pos[2]-brickPos[2], pos[3]-brickPos[3] ts.call("commandShiftBrick", dy, -dx, dz) brickPos[1] = pos[1]; brickPos[2] = pos[2]; brickPos[3] = pos[3]; ts.call("commandPlantBrick") end local function buildBricks(bricks) local brickPos = {0, 0, 0} for x, xb in pairs(bricks) do for y, xyb in pairs(xb) do for z, t in pairs(xyb) do plantBrickAt({x, y, z}, brickPos) end end end end local function buildArch(arch) local ucode = archToUcode(arch) local bricks = ucodeToBricks(ucode, arch.roms) buildBricks(bricks) end local arch = require("rom-8608-defs") buildArch(arch)