---------------------------------------------------------------------------- -- LuaJIT MIPS disassembler module. -- -- Copyright (C) 2005-2022 Mike Pall. All rights reserved. -- Released under the MIT/X license. See Copyright Notice in luajit.h ---------------------------------------------------------------------------- -- This is a helper module used by the LuaJIT machine code dumper module. -- -- It disassembles all standard MIPS32R1/R2 instructions. -- Default mode is big-endian, but see: dis_mipsel.lua ------------------------------------------------------------------------------ local type = type local byte, format = string.byte, string.format local match, gmatch = string.match, string.gmatch local concat = table.concat local bit = require("bit") local band, bor, tohex = bit.band, bit.bor, bit.tohex local lshift, rshift, arshift = bit.lshift, bit.rshift, bit.arshift ------------------------------------------------------------------------------ -- Extended opcode maps common to all MIPS releases ------------------------------------------------------------------------------ local map_srl = { shift = 21, mask = 1, [0] = "srlDTA", "rotrDTA", } local map_srlv = { shift = 6, mask = 1, [0] = "srlvDTS", "rotrvDTS", } local map_cop0 = { shift = 25, mask = 1, [0] = { shift = 21, mask = 15, [0] = "mfc0TDW", [4] = "mtc0TDW", [10] = "rdpgprDT", [11] = { shift = 5, mask = 1, [0] = "diT0", "eiT0", }, [14] = "wrpgprDT", }, { shift = 0, mask = 63, [1] = "tlbr", [2] = "tlbwi", [6] = "tlbwr", [8] = "tlbp", [24] = "eret", [31] = "deret", [32] = "wait", }, } ------------------------------------------------------------------------------ -- Primary and extended opcode maps for MIPS R1-R5 ------------------------------------------------------------------------------ local map_movci = { shift = 16, mask = 1, [0] = "movfDSC", "movtDSC", } local map_special = { shift = 0, mask = 63, [0] = { shift = 0, mask = -1, [0] = "nop", _ = "sllDTA" }, map_movci, map_srl, "sraDTA", "sllvDTS", false, map_srlv, "sravDTS", "jrS", "jalrD1S", "movzDST", "movnDST", "syscallY", "breakY", false, "sync", "mfhiD", "mthiS", "mfloD", "mtloS", "dsllvDST", false, "dsrlvDST", "dsravDST", "multST", "multuST", "divST", "divuST", "dmultST", "dmultuST", "ddivST", "ddivuST", "addDST", "addu|moveDST0", "subDST", "subu|neguDS0T", "andDST", "or|moveDST0", "xorDST", "nor|notDST0", false, false, "sltDST", "sltuDST", "daddDST", "dadduDST", "dsubDST", "dsubuDST", "tgeSTZ", "tgeuSTZ", "tltSTZ", "tltuSTZ", "teqSTZ", false, "tneSTZ", false, "dsllDTA", false, "dsrlDTA", "dsraDTA", "dsll32DTA", false, "dsrl32DTA", "dsra32DTA", } local map_special2 = { shift = 0, mask = 63, [0] = "maddST", "madduST", "mulDST", false, "msubST", "msubuST", [32] = "clzDS", [33] = "cloDS", [63] = "sdbbpY", } local map_bshfl = { shift = 6, mask = 31, [2] = "wsbhDT", [16] = "sebDT", [24] = "sehDT", } local map_dbshfl = { shift = 6, mask = 31, [2] = "dsbhDT", [5] = "dshdDT", } local map_special3 = { shift = 0, mask = 63, [0] = "extTSAK", [1] = "dextmTSAP", [3] = "dextTSAK", [4] = "insTSAL", [6] = "dinsuTSEQ", [7] = "dinsTSAL", [32] = map_bshfl, [36] = map_dbshfl, [59] = "rdhwrTD", } local map_regimm = { shift = 16, mask = 31, [0] = "bltzSB", "bgezSB", "bltzlSB", "bgezlSB", false, false, false, false, "tgeiSI", "tgeiuSI", "tltiSI", "tltiuSI", "teqiSI", false, "tneiSI", false, "bltzalSB", "bgezalSB", "bltzallSB", "bgezallSB", false, false, false, false, false, false, false, false, false, false, false, "synciSO", } local map_cop1s = { shift = 0, mask = 63, [0] = "add.sFGH", "sub.sFGH", "mul.sFGH", "div.sFGH", "sqrt.sFG", "abs.sFG", "mov.sFG", "neg.sFG", "round.l.sFG", "trunc.l.sFG", "ceil.l.sFG", "floor.l.sFG", "round.w.sFG", "trunc.w.sFG", "ceil.w.sFG", "floor.w.sFG", false, { shift = 16, mask = 1, [0] = "movf.sFGC", "movt.sFGC" }, "movz.sFGT", "movn.sFGT", false, "recip.sFG", "rsqrt.sFG", false, false, false, false, false, false, false, false, false, false, "cvt.d.sFG", false, false, "cvt.w.sFG", "cvt.l.sFG", "cvt.ps.sFGH", false, false, false, false, false, false, false, false, false, "c.f.sVGH", "c.un.sVGH", "c.eq.sVGH", "c.ueq.sVGH", "c.olt.sVGH", "c.ult.sVGH", "c.ole.sVGH", "c.ule.sVGH", "c.sf.sVGH", "c.ngle.sVGH", "c.seq.sVGH", "c.ngl.sVGH", "c.lt.sVGH", "c.nge.sVGH", "c.le.sVGH", "c.ngt.sVGH", } local map_cop1d = { shift = 0, mask = 63, [0] = "add.dFGH", "sub.dFGH", "mul.dFGH", "div.dFGH", "sqrt.dFG", "abs.dFG", "mov.dFG", "neg.dFG", "round.l.dFG", "trunc.l.dFG", "ceil.l.dFG", "floor.l.dFG", "round.w.dFG", "trunc.w.dFG", "ceil.w.dFG", "floor.w.dFG", false, { shift = 16, mask = 1, [0] = "movf.dFGC", "movt.dFGC" }, "movz.dFGT", "movn.dFGT", false, "recip.dFG", "rsqrt.dFG", false, false, false, false, false, false, false, false, false, "cvt.s.dFG", false, false, false, "cvt.w.dFG", "cvt.l.dFG", false, false, false, false, false, false, false, false, false, false, "c.f.dVGH", "c.un.dVGH", "c.eq.dVGH", "c.ueq.dVGH", "c.olt.dVGH", "c.ult.dVGH", "c.ole.dVGH", "c.ule.dVGH", "c.df.dVGH", "c.ngle.dVGH", "c.deq.dVGH", "c.ngl.dVGH", "c.lt.dVGH", "c.nge.dVGH", "c.le.dVGH", "c.ngt.dVGH", } local map_cop1ps = { shift = 0, mask = 63, [0] = "add.psFGH", "sub.psFGH", "mul.psFGH", false, false, "abs.psFG", "mov.psFG", "neg.psFG", false, false, false, false, false, false, false, false, false, { shift = 16, mask = 1, [0] = "movf.psFGC", "movt.psFGC" }, "movz.psFGT", "movn.psFGT", false, false, false, false, false, false, false, false, false, false, false, false, "cvt.s.puFG", false, false, false, false, false, false, false, "cvt.s.plFG", false, false, false, "pll.psFGH", "plu.psFGH", "pul.psFGH", "puu.psFGH", "c.f.psVGH", "c.un.psVGH", "c.eq.psVGH", "c.ueq.psVGH", "c.olt.psVGH", "c.ult.psVGH", "c.ole.psVGH", "c.ule.psVGH", "c.psf.psVGH", "c.ngle.psVGH", "c.pseq.psVGH", "c.ngl.psVGH", "c.lt.psVGH", "c.nge.psVGH", "c.le.psVGH", "c.ngt.psVGH", } local map_cop1w = { shift = 0, mask = 63, [32] = "cvt.s.wFG", [33] = "cvt.d.wFG", } local map_cop1l = { shift = 0, mask = 63, [32] = "cvt.s.lFG", [33] = "cvt.d.lFG", } local map_cop1bc = { shift = 16, mask = 3, [0] = "bc1fCB", "bc1tCB", "bc1flCB", "bc1tlCB", } local map_cop1 = { shift = 21, mask = 31, [0] = "mfc1TG", "dmfc1TG", "cfc1TG", "mfhc1TG", "mtc1TG", "dmtc1TG", "ctc1TG", "mthc1TG", map_cop1bc, false, false, false, false, false, false, false, map_cop1s, map_cop1d, false, false, map_cop1w, map_cop1l, map_cop1ps, } local map_cop1x = { shift = 0, mask = 63, [0] = "lwxc1FSX", "ldxc1FSX", false, false, false, "luxc1FSX", false, false, "swxc1FSX", "sdxc1FSX", false, false, false, "suxc1FSX", false, "prefxMSX", false, false, false, false, false, false, false, false, false, false, false, false, false, false, "alnv.psFGHS", false, "madd.sFRGH", "madd.dFRGH", false, false, false, false, "madd.psFRGH", false, "msub.sFRGH", "msub.dFRGH", false, false, false, false, "msub.psFRGH", false, "nmadd.sFRGH", "nmadd.dFRGH", false, false, false, false, "nmadd.psFRGH", false, "nmsub.sFRGH", "nmsub.dFRGH", false, false, false, false, "nmsub.psFRGH", false, } local map_pri = { [0] = map_special, map_regimm, "jJ", "jalJ", "beq|beqz|bST00B", "bne|bnezST0B", "blezSB", "bgtzSB", "addiTSI", "addiu|liTS0I", "sltiTSI", "sltiuTSI", "andiTSU", "ori|liTS0U", "xoriTSU", "luiTU", map_cop0, map_cop1, false, map_cop1x, "beql|beqzlST0B", "bnel|bnezlST0B", "blezlSB", "bgtzlSB", "daddiTSI", "daddiuTSI", false, false, map_special2, "jalxJ", false, map_special3, "lbTSO", "lhTSO", "lwlTSO", "lwTSO", "lbuTSO", "lhuTSO", "lwrTSO", false, "sbTSO", "shTSO", "swlTSO", "swTSO", false, false, "swrTSO", "cacheNSO", "llTSO", "lwc1HSO", "lwc2TSO", "prefNSO", false, "ldc1HSO", "ldc2TSO", "ldTSO", "scTSO", "swc1HSO", "swc2TSO", false, false, "sdc1HSO", "sdc2TSO", "sdTSO", } ------------------------------------------------------------------------------ -- Primary and extended opcode maps for MIPS R6 ------------------------------------------------------------------------------ local map_mul_r6 = { shift = 6, mask = 3, [2] = "mulDST", [3] = "muhDST" } local map_mulu_r6 = { shift = 6, mask = 3, [2] = "muluDST", [3] = "muhuDST" } local map_div_r6 = { shift = 6, mask = 3, [2] = "divDST", [3] = "modDST" } local map_divu_r6 = { shift = 6, mask = 3, [2] = "divuDST", [3] = "moduDST" } local map_dmul_r6 = { shift = 6, mask = 3, [2] = "dmulDST", [3] = "dmuhDST" } local map_dmulu_r6 = { shift = 6, mask = 3, [2] = "dmuluDST", [3] = "dmuhuDST" } local map_ddiv_r6 = { shift = 6, mask = 3, [2] = "ddivDST", [3] = "dmodDST" } local map_ddivu_r6 = { shift = 6, mask = 3, [2] = "ddivuDST", [3] = "dmoduDST" } local map_special_r6 = { shift = 0, mask = 63, [0] = { shift = 0, mask = -1, [0] = "nop", _ = "sllDTA" }, false, map_srl, "sraDTA", "sllvDTS", false, map_srlv, "sravDTS", "jrS", "jalrD1S", false, false, "syscallY", "breakY", false, "sync", "clzDS", "cloDS", "dclzDS", "dcloDS", "dsllvDST", "dlsaDSTA", "dsrlvDST", "dsravDST", map_mul_r6, map_mulu_r6, map_div_r6, map_divu_r6, map_dmul_r6, map_dmulu_r6, map_ddiv_r6, map_ddivu_r6, "addDST", "addu|moveDST0", "subDST", "subu|neguDS0T", "andDST", "or|moveDST0", "xorDST", "nor|notDST0", false, false, "sltDST", "sltuDST", "daddDST", "dadduDST", "dsubDST", "dsubuDST", "tgeSTZ", "tgeuSTZ", "tltSTZ", "tltuSTZ", "teqSTZ", "seleqzDST", "tneSTZ", "selnezDST", "dsllDTA", false, "dsrlDTA", "dsraDTA", "dsll32DTA", false, "dsrl32DTA", "dsra32DTA", } local map_bshfl_r6 = { shift = 9, mask = 3, [1] = "alignDSTa", _ = { shift = 6, mask = 31, [0] = "bitswapDT", [2] = "wsbhDT", [16] = "sebDT", [24] = "sehDT", } } local map_dbshfl_r6 = { shift = 9, mask = 3, [1] = "dalignDSTa", _ = { shift = 6, mask = 31, [0] = "dbitswapDT", [2] = "dsbhDT", [5] = "dshdDT", } } local map_special3_r6 = { shift = 0, mask = 63, [0] = "extTSAK", [1] = "dextmTSAP", [3] = "dextTSAK", [4] = "insTSAL", [6] = "dinsuTSEQ", [7] = "dinsTSAL", [32] = map_bshfl_r6, [36] = map_dbshfl_r6, [59] = "rdhwrTD", } local map_regimm_r6 = { shift = 16, mask = 31, [0] = "bltzSB", [1] = "bgezSB", [6] = "dahiSI", [30] = "datiSI", [23] = "sigrieI", [31] = "synciSO", } local map_pcrel_r6 = { shift = 19, mask = 3, [0] = "addiupcS2", "lwpcS2", "lwupcS2", { shift = 18, mask = 1, [0] = "ldpcS3", { shift = 16, mask = 3, [2] = "auipcSI", [3] = "aluipcSI" } } } local map_cop1s_r6 = { shift = 0, mask = 63, [0] = "add.sFGH", "sub.sFGH", "mul.sFGH", "div.sFGH", "sqrt.sFG", "abs.sFG", "mov.sFG", "neg.sFG", "round.l.sFG", "trunc.l.sFG", "ceil.l.sFG", "floor.l.sFG", "round.w.sFG", "trunc.w.sFG", "ceil.w.sFG", "floor.w.sFG", "sel.sFGH", false, false, false, "seleqz.sFGH", "recip.sFG", "rsqrt.sFG", "selnez.sFGH", "maddf.sFGH", "msubf.sFGH", "rint.sFG", "class.sFG", "min.sFGH", "mina.sFGH", "max.sFGH", "maxa.sFGH", false, "cvt.d.sFG", false, false, "cvt.w.sFG", "cvt.l.sFG", } local map_cop1d_r6 = { shift = 0, mask = 63, [0] = "add.dFGH", "sub.dFGH", "mul.dFGH", "div.dFGH", "sqrt.dFG", "abs.dFG", "mov.dFG", "neg.dFG", "round.l.dFG", "trunc.l.dFG", "ceil.l.dFG", "floor.l.dFG", "round.w.dFG", "trunc.w.dFG", "ceil.w.dFG", "floor.w.dFG", "sel.dFGH", false, false, false, "seleqz.dFGH", "recip.dFG", "rsqrt.dFG", "selnez.dFGH", "maddf.dFGH", "msubf.dFGH", "rint.dFG", "class.dFG", "min.dFGH", "mina.dFGH", "max.dFGH", "maxa.dFGH", "cvt.s.dFG", false, false, false, "cvt.w.dFG", "cvt.l.dFG", } local map_cop1w_r6 = { shift = 0, mask = 63, [0] = "cmp.af.sFGH", "cmp.un.sFGH", "cmp.eq.sFGH", "cmp.ueq.sFGH", "cmp.lt.sFGH", "cmp.ult.sFGH", "cmp.le.sFGH", "cmp.ule.sFGH", "cmp.saf.sFGH", "cmp.sun.sFGH", "cmp.seq.sFGH", "cmp.sueq.sFGH", "cmp.slt.sFGH", "cmp.sult.sFGH", "cmp.sle.sFGH", "cmp.sule.sFGH", false, "cmp.or.sFGH", "cmp.une.sFGH", "cmp.ne.sFGH", false, false, false, false, false, "cmp.sor.sFGH", "cmp.sune.sFGH", "cmp.sne.sFGH", false, false, false, false, "cvt.s.wFG", "cvt.d.wFG", } local map_cop1l_r6 = { shift = 0, mask = 63, [0] = "cmp.af.dFGH", "cmp.un.dFGH", "cmp.eq.dFGH", "cmp.ueq.dFGH", "cmp.lt.dFGH", "cmp.ult.dFGH", "cmp.le.dFGH", "cmp.ule.dFGH", "cmp.saf.dFGH", "cmp.sun.dFGH", "cmp.seq.dFGH", "cmp.sueq.dFGH", "cmp.slt.dFGH", "cmp.sult.dFGH", "cmp.sle.dFGH", "cmp.sule.dFGH", false, "cmp.or.dFGH", "cmp.une.dFGH", "cmp.ne.dFGH", false, false, false, false, false, "cmp.sor.dFGH", "cmp.sune.dFGH", "cmp.sne.dFGH", false, false, false, false, "cvt.s.lFG", "cvt.d.lFG", } local map_cop1_r6 = { shift = 21, mask = 31, [0] = "mfc1TG", "dmfc1TG", "cfc1TG", "mfhc1TG", "mtc1TG", "dmtc1TG", "ctc1TG", "mthc1TG", false, "bc1eqzHB", false, false, false, "bc1nezHB", false, false, map_cop1s_r6, map_cop1d_r6, false, false, map_cop1w_r6, map_cop1l_r6, } local function maprs_popTS(rs, rt) if rt == 0 then return 0 elseif rs == 0 then return 1 elseif rs == rt then return 2 else return 3 end end local map_pop06_r6 = { maprs = maprs_popTS, [0] = "blezSB", "blezalcTB", "bgezalcTB", "bgeucSTB" } local map_pop07_r6 = { maprs = maprs_popTS, [0] = "bgtzSB", "bgtzalcTB", "bltzalcTB", "bltucSTB" } local map_pop26_r6 = { maprs = maprs_popTS, "blezcTB", "bgezcTB", "bgecSTB" } local map_pop27_r6 = { maprs = maprs_popTS, "bgtzcTB", "bltzcTB", "bltcSTB" } local function maprs_popS(rs, rt) if rs == 0 then return 0 else return 1 end end local map_pop66_r6 = { maprs = maprs_popS, [0] = "jicTI", "beqzcSb" } local map_pop76_r6 = { maprs = maprs_popS, [0] = "jialcTI", "bnezcSb" } local function maprs_popST(rs, rt) if rs >= rt then return 0 elseif rs == 0 then return 1 else return 2 end end local map_pop10_r6 = { maprs = maprs_popST, [0] = "bovcSTB", "beqzalcTB", "beqcSTB" } local map_pop30_r6 = { maprs = maprs_popST, [0] = "bnvcSTB", "bnezalcTB", "bnecSTB" } local map_pri_r6 = { [0] = map_special_r6, map_regimm_r6, "jJ", "jalJ", "beq|beqz|bST00B", "bne|bnezST0B", map_pop06_r6, map_pop07_r6, map_pop10_r6, "addiu|liTS0I", "sltiTSI", "sltiuTSI", "andiTSU", "ori|liTS0U", "xoriTSU", "aui|luiTS0U", map_cop0, map_cop1_r6, false, false, false, false, map_pop26_r6, map_pop27_r6, map_pop30_r6, "daddiuTSI", false, false, false, "dauiTSI", false, map_special3_r6, "lbTSO", "lhTSO", false, "lwTSO", "lbuTSO", "lhuTSO", false, false, "sbTSO", "shTSO", false, "swTSO", false, false, false, false, false, "lwc1HSO", "bc#", false, false, "ldc1HSO", map_pop66_r6, "ldTSO", false, "swc1HSO", "balc#", map_pcrel_r6, false, "sdc1HSO", map_pop76_r6, "sdTSO", } ------------------------------------------------------------------------------ local map_gpr = { [0] = "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "sp", "r30", "ra", } ------------------------------------------------------------------------------ -- Output a nicely formatted line with an opcode and operands. local function putop(ctx, text, operands) local pos = ctx.pos local extra = "" if ctx.rel then local sym = ctx.symtab[ctx.rel] if sym then extra = "\t->"..sym end end if ctx.hexdump > 0 then ctx.out(format("%08x %s %-7s %s%s\n", ctx.addr+pos, tohex(ctx.op), text, concat(operands, ", "), extra)) else ctx.out(format("%08x %-7s %s%s\n", ctx.addr+pos, text, concat(operands, ", "), extra)) end ctx.pos = pos + 4 end -- Fallback for unknown opcodes. local function unknown(ctx) return putop(ctx, ".long", { "0x"..tohex(ctx.op) }) end local function get_be(ctx) local pos = ctx.pos local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4) return bor(lshift(b0, 24), lshift(b1, 16), lshift(b2, 8), b3) end local function get_le(ctx) local pos = ctx.pos local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4) return bor(lshift(b3, 24), lshift(b2, 16), lshift(b1, 8), b0) end -- Disassemble a single instruction. local function disass_ins(ctx) local op = ctx:get() local operands = {} local last = nil ctx.op = op ctx.rel = nil local opat = ctx.map_pri[rshift(op, 26)] while type(opat) ~= "string" do if not opat then return unknown(ctx) end if opat.maprs then opat = opat[opat.maprs(band(rshift(op,21),31), band(rshift(op,16),31))] else opat = opat[band(rshift(op, opat.shift), opat.mask)] or opat._ end end local name, pat = match(opat, "^([a-z0-9_.]*)(.*)") local altname, pat2 = match(pat, "|([a-z0-9_.|]*)(.*)") if altname then pat = pat2 end for p in gmatch(pat, ".") do local x = nil if p == "S" then x = map_gpr[band(rshift(op, 21), 31)] elseif p == "T" then x = map_gpr[band(rshift(op, 16), 31)] elseif p == "D" then x = map_gpr[band(rshift(op, 11), 31)] elseif p == "F" then x = "f"..band(rshift(op, 6), 31) elseif p == "G" then x = "f"..band(rshift(op, 11), 31) elseif p == "H" then x = "f"..band(rshift(op, 16), 31) elseif p == "R" then x = "f"..band(rshift(op, 21), 31) elseif p == "A" then x = band(rshift(op, 6), 31) elseif p == "a" then x = band(rshift(op, 6), 7) elseif p == "E" then x = band(rshift(op, 6), 31) + 32 elseif p == "M" then x = band(rshift(op, 11), 31) elseif p == "N" then x = band(rshift(op, 16), 31) elseif p == "C" then x = band(rshift(op, 18), 7) if x == 0 then x = nil end elseif p == "K" then x = band(rshift(op, 11), 31) + 1 elseif p == "P" then x = band(rshift(op, 11), 31) + 33 elseif p == "L" then x = band(rshift(op, 11), 31) - last + 1 elseif p == "Q" then x = band(rshift(op, 11), 31) - last + 33 elseif p == "I" then x = arshift(lshift(op, 16), 16) elseif p == "2" then x = arshift(lshift(op, 13), 11) elseif p == "3" then x = arshift(lshift(op, 14), 11) elseif p == "U" then x = band(op, 0xffff) elseif p == "O" then local disp = arshift(lshift(op, 16), 16) operands[#operands] = format("%d(%s)", disp, last) elseif p == "X" then local index = map_gpr[band(rshift(op, 16), 31)] operands[#operands] = format("%s(%s)", index, last) elseif p == "B" then x = ctx.addr + ctx.pos + arshift(lshift(op, 16), 14) + 4 ctx.rel = x x = format("0x%08x", x) elseif p == "b" then x = ctx.addr + ctx.pos + arshift(lshift(op, 11), 9) + 4 ctx.rel = x x = format("0x%08x", x) elseif p == "#" then x = ctx.addr + ctx.pos + arshift(lshift(op, 6), 4) + 4 ctx.rel = x x = format("0x%08x", x) elseif p == "J" then local a = ctx.addr + ctx.pos x = a - band(a, 0x0fffffff) + band(op, 0x03ffffff)*4 ctx.rel = x x = format("0x%08x", x) elseif p == "V" then x = band(rshift(op, 8), 7) if x == 0 then x = nil end elseif p == "W" then x = band(op, 7) if x == 0 then x = nil end elseif p == "Y" then x = band(rshift(op, 6), 0x000fffff) if x == 0 then x = nil end elseif p == "Z" then x = band(rshift(op, 6), 1023) if x == 0 then x = nil end elseif p == "0" then if last == "r0" or last == 0 then local n = #operands operands[n] = nil last = operands[n-1] if altname then local a1, a2 = match(altname, "([^|]*)|(.*)") if a1 then name, altname = a1, a2 else name = altname end end end elseif p == "1" then if last == "ra" then operands[#operands] = nil end else assert(false) end if x then operands[#operands+1] = x; last = x end end return putop(ctx, name, operands) end ------------------------------------------------------------------------------ -- Disassemble a block of code. local function disass_block(ctx, ofs, len) if not ofs then ofs = 0 end local stop = len and ofs+len or #ctx.code stop = stop - stop % 4 ctx.pos = ofs - ofs % 4 ctx.rel = nil while ctx.pos < stop do disass_ins(ctx) end end -- Extended API: create a disassembler context. Then call ctx:disass(ofs, len). local function create(code, addr, out) local ctx = {} ctx.code = code ctx.addr = addr or 0 ctx.out = out or io.write ctx.symtab = {} ctx.disass = disass_block ctx.hexdump = 8 ctx.get = get_be ctx.map_pri = map_pri return ctx end local function create_el(code, addr, out) local ctx = create(code, addr, out) ctx.get = get_le return ctx end local function create_r6(code, addr, out) local ctx = create(code, addr, out) ctx.map_pri = map_pri_r6 return ctx end local function create_r6_el(code, addr, out) local ctx = create(code, addr, out) ctx.get = get_le ctx.map_pri = map_pri_r6 return ctx end -- Simple API: disassemble code (a string) at address and output via out. local function disass(code, addr, out) create(code, addr, out):disass() end local function disass_el(code, addr, out) create_el(code, addr, out):disass() end local function disass_r6(code, addr, out) create_r6(code, addr, out):disass() end local function disass_r6_el(code, addr, out) create_r6_el(code, addr, out):disass() end -- Return register name for RID. local function regname(r) if r < 32 then return map_gpr[r] end return "f"..(r-32) end -- Public module functions. return { create = create, create_el = create_el, create_r6 = create_r6, create_r6_el = create_r6_el, disass = disass, disass_el = disass_el, disass_r6 = disass_r6, disass_r6_el = disass_r6_el, regname = regname }