ljx

FORK: LuaJIT with native 5.2 and 5.3 support
git clone https://git.neptards.moe/neptards/ljx.git
Log | Files | Refs | README

dis_ppc.lua (20318B)


      1 ----------------------------------------------------------------------------
      2 -- LuaJIT PPC disassembler module.
      3 --
      4 -- Copyright (C) 2005-2016 Mike Pall. All rights reserved.
      5 -- Released under the MIT/X license. See Copyright Notice in luajit.h
      6 ----------------------------------------------------------------------------
      7 -- This is a helper module used by the LuaJIT machine code dumper module.
      8 --
      9 -- It disassembles all common, non-privileged 32/64 bit PowerPC instructions
     10 -- plus the e500 SPE instructions and some Cell/Xenon extensions.
     11 --
     12 -- NYI: VMX, VMX128
     13 ------------------------------------------------------------------------------
     14 
     15 local type = type
     16 local sub, byte, format = string.sub, string.byte, string.format
     17 local match, gmatch, gsub = string.match, string.gmatch, string.gsub
     18 local concat = table.concat
     19 local bit = require("bit")
     20 local band, bor, tohex = bit.band, bit.bor, bit.tohex
     21 local lshift, rshift, arshift = bit.lshift, bit.rshift, bit.arshift
     22 
     23 ------------------------------------------------------------------------------
     24 -- Primary and extended opcode maps
     25 ------------------------------------------------------------------------------
     26 
     27 local map_crops = {
     28   shift = 1, mask = 1023,
     29   [0] = "mcrfXX",
     30   [33] = "crnor|crnotCCC=", [129] = "crandcCCC",
     31   [193] = "crxor|crclrCCC%", [225] = "crnandCCC",
     32   [257] = "crandCCC", [289] = "creqv|crsetCCC%",
     33   [417] = "crorcCCC", [449] = "cror|crmoveCCC=",
     34   [16] = "b_lrKB", [528] = "b_ctrKB",
     35   [150] = "isync",
     36 }
     37 
     38 local map_rlwinm = setmetatable({
     39   shift = 0, mask = -1,
     40 },
     41 { __index = function(t, x)
     42     local rot = band(rshift(x, 11), 31)
     43     local mb = band(rshift(x, 6), 31)
     44     local me = band(rshift(x, 1), 31)
     45     if mb == 0 and me == 31-rot then
     46       return "slwiRR~A."
     47     elseif me == 31 and mb == 32-rot then
     48       return "srwiRR~-A."
     49     else
     50       return "rlwinmRR~AAA."
     51     end
     52   end
     53 })
     54 
     55 local map_rld = {
     56   shift = 2, mask = 7,
     57   [0] = "rldiclRR~HM.", "rldicrRR~HM.", "rldicRR~HM.", "rldimiRR~HM.",
     58   {
     59     shift = 1, mask = 1,
     60     [0] = "rldclRR~RM.", "rldcrRR~RM.",
     61   },
     62 }
     63 
     64 local map_ext = setmetatable({
     65   shift = 1, mask = 1023,
     66 
     67   [0] = "cmp_YLRR", [32] = "cmpl_YLRR",
     68   [4] = "twARR", [68] = "tdARR",
     69 
     70   [8] = "subfcRRR.", [40] = "subfRRR.",
     71   [104] = "negRR.", [136] = "subfeRRR.",
     72   [200] = "subfzeRR.", [232] = "subfmeRR.",
     73   [520] = "subfcoRRR.", [552] = "subfoRRR.",
     74   [616] = "negoRR.", [648] = "subfeoRRR.",
     75   [712] = "subfzeoRR.", [744] = "subfmeoRR.",
     76 
     77   [9] = "mulhduRRR.", [73] = "mulhdRRR.", [233] = "mulldRRR.",
     78   [457] = "divduRRR.", [489] = "divdRRR.",
     79   [745] = "mulldoRRR.",
     80   [969] = "divduoRRR.", [1001] = "divdoRRR.",
     81 
     82   [10] = "addcRRR.", [138] = "addeRRR.",
     83   [202] = "addzeRR.", [234] = "addmeRR.", [266] = "addRRR.",
     84   [522] = "addcoRRR.", [650] = "addeoRRR.",
     85   [714] = "addzeoRR.", [746] = "addmeoRR.", [778] = "addoRRR.",
     86 
     87   [11] = "mulhwuRRR.", [75] = "mulhwRRR.", [235] = "mullwRRR.",
     88   [459] = "divwuRRR.", [491] = "divwRRR.",
     89   [747] = "mullwoRRR.",
     90   [971] = "divwouRRR.", [1003] = "divwoRRR.",
     91 
     92   [15] = "iselltRRR", [47] = "iselgtRRR", [79] = "iseleqRRR",
     93 
     94   [144] = { shift = 20, mask = 1, [0] = "mtcrfRZ~", "mtocrfRZ~", },
     95   [19] = { shift = 20, mask = 1, [0] = "mfcrR", "mfocrfRZ", },
     96   [371] = { shift = 11, mask = 1023, [392] = "mftbR", [424] = "mftbuR", },
     97   [339] = {
     98     shift = 11, mask = 1023,
     99     [32] = "mferR", [256] = "mflrR", [288] = "mfctrR", [16] = "mfspefscrR",
    100   },
    101   [467] = {
    102     shift = 11, mask = 1023,
    103     [32] = "mtxerR", [256] = "mtlrR", [288] = "mtctrR", [16] = "mtspefscrR",
    104   },
    105 
    106   [20] = "lwarxRR0R", [84] = "ldarxRR0R",
    107 
    108   [21] = "ldxRR0R", [53] = "lduxRRR",
    109   [149] = "stdxRR0R", [181] = "stduxRRR",
    110   [341] = "lwaxRR0R", [373] = "lwauxRRR",
    111 
    112   [23] = "lwzxRR0R", [55] = "lwzuxRRR",
    113   [87] = "lbzxRR0R", [119] = "lbzuxRRR",
    114   [151] = "stwxRR0R", [183] = "stwuxRRR",
    115   [215] = "stbxRR0R", [247] = "stbuxRRR",
    116   [279] = "lhzxRR0R", [311] = "lhzuxRRR",
    117   [343] = "lhaxRR0R", [375] = "lhauxRRR",
    118   [407] = "sthxRR0R", [439] = "sthuxRRR",
    119 
    120   [54] = "dcbst-R0R", [86] = "dcbf-R0R",
    121   [150] = "stwcxRR0R.", [214] = "stdcxRR0R.",
    122   [246] = "dcbtst-R0R", [278] = "dcbt-R0R",
    123   [310] = "eciwxRR0R", [438] = "ecowxRR0R",
    124   [470] = "dcbi-RR",
    125 
    126   [598] = {
    127     shift = 21, mask = 3,
    128     [0] = "sync", "lwsync", "ptesync",
    129   },
    130   [758] = "dcba-RR",
    131   [854] = "eieio", [982] = "icbi-R0R", [1014] = "dcbz-R0R",
    132 
    133   [26] = "cntlzwRR~", [58] = "cntlzdRR~",
    134   [122] = "popcntbRR~",
    135   [154] = "prtywRR~", [186] = "prtydRR~",
    136 
    137   [28] = "andRR~R.", [60] = "andcRR~R.", [124] = "nor|notRR~R=.",
    138   [284] = "eqvRR~R.", [316] = "xorRR~R.",
    139   [412] = "orcRR~R.", [444] = "or|mrRR~R=.", [476] = "nandRR~R.",
    140   [508] = "cmpbRR~R",
    141 
    142   [512] = "mcrxrX",
    143 
    144   [532] = "ldbrxRR0R", [660] = "stdbrxRR0R",
    145 
    146   [533] = "lswxRR0R", [597] = "lswiRR0A",
    147   [661] = "stswxRR0R", [725] = "stswiRR0A",
    148 
    149   [534] = "lwbrxRR0R", [662] = "stwbrxRR0R",
    150   [790] = "lhbrxRR0R", [918] = "sthbrxRR0R",
    151 
    152   [535] = "lfsxFR0R", [567] = "lfsuxFRR",
    153   [599] = "lfdxFR0R", [631] = "lfduxFRR",
    154   [663] = "stfsxFR0R", [695] = "stfsuxFRR",
    155   [727] = "stfdxFR0R", [759] = "stfduxFR0R",
    156   [855] = "lfiwaxFR0R",
    157   [983] = "stfiwxFR0R",
    158 
    159   [24] = "slwRR~R.",
    160 
    161   [27] = "sldRR~R.", [536] = "srwRR~R.",
    162   [792] = "srawRR~R.", [824] = "srawiRR~A.",
    163 
    164   [794] = "sradRR~R.", [826] = "sradiRR~H.", [827] = "sradiRR~H.",
    165   [922] = "extshRR~.", [954] = "extsbRR~.", [986] = "extswRR~.",
    166 
    167   [539] = "srdRR~R.",
    168 },
    169 { __index = function(t, x)
    170     if band(x, 31) == 15 then return "iselRRRC" end
    171   end
    172 })
    173 
    174 local map_ld = {
    175   shift = 0, mask = 3,
    176   [0] = "ldRRE", "lduRRE", "lwaRRE",
    177 }
    178 
    179 local map_std = {
    180   shift = 0, mask = 3,
    181   [0] = "stdRRE", "stduRRE",
    182 }
    183 
    184 local map_fps = {
    185   shift = 5, mask = 1,
    186   {
    187     shift = 1, mask = 15,
    188     [0] = false, false, "fdivsFFF.", false,
    189     "fsubsFFF.", "faddsFFF.", "fsqrtsF-F.", false,
    190     "fresF-F.", "fmulsFF-F.", "frsqrtesF-F.", false,
    191     "fmsubsFFFF~.", "fmaddsFFFF~.", "fnmsubsFFFF~.", "fnmaddsFFFF~.",
    192   }
    193 }
    194 
    195 local map_fpd = {
    196   shift = 5, mask = 1,
    197   [0] = {
    198     shift = 1, mask = 1023,
    199     [0] = "fcmpuXFF", [32] = "fcmpoXFF", [64] = "mcrfsXX",
    200     [38] = "mtfsb1A.", [70] = "mtfsb0A.", [134] = "mtfsfiA>>-A>",
    201     [8] = "fcpsgnFFF.", [40] = "fnegF-F.", [72] = "fmrF-F.",
    202     [136] = "fnabsF-F.", [264] = "fabsF-F.",
    203     [12] = "frspF-F.",
    204     [14] = "fctiwF-F.", [15] = "fctiwzF-F.",
    205     [583] = "mffsF.", [711] = "mtfsfZF.",
    206     [392] = "frinF-F.", [424] = "frizF-F.",
    207     [456] = "fripF-F.", [488] = "frimF-F.",
    208     [814] = "fctidF-F.", [815] = "fctidzF-F.", [846] = "fcfidF-F.",
    209   },
    210   {
    211     shift = 1, mask = 15,
    212     [0] = false, false, "fdivFFF.", false,
    213     "fsubFFF.", "faddFFF.", "fsqrtF-F.", "fselFFFF~.",
    214     "freF-F.", "fmulFF-F.", "frsqrteF-F.", false,
    215     "fmsubFFFF~.", "fmaddFFFF~.", "fnmsubFFFF~.", "fnmaddFFFF~.",
    216   }
    217 }
    218 
    219 local map_spe = {
    220   shift = 0, mask = 2047,
    221 
    222   [512] = "evaddwRRR", [514] = "evaddiwRAR~",
    223   [516] = "evsubwRRR~", [518] = "evsubiwRAR~",
    224   [520] = "evabsRR", [521] = "evnegRR",
    225   [522] = "evextsbRR", [523] = "evextshRR", [524] = "evrndwRR",
    226   [525] = "evcntlzwRR", [526] = "evcntlswRR",
    227 
    228   [527] = "brincRRR",
    229 
    230   [529] = "evandRRR", [530] = "evandcRRR", [534] = "evxorRRR",
    231   [535] = "evor|evmrRRR=", [536] = "evnor|evnotRRR=",
    232   [537] = "eveqvRRR", [539] = "evorcRRR", [542] = "evnandRRR",
    233 
    234   [544] = "evsrwuRRR", [545] = "evsrwsRRR",
    235   [546] = "evsrwiuRRA", [547] = "evsrwisRRA",
    236   [548] = "evslwRRR", [550] = "evslwiRRA",
    237   [552] = "evrlwRRR", [553] = "evsplatiRS",
    238   [554] = "evrlwiRRA", [555] = "evsplatfiRS",
    239   [556] = "evmergehiRRR", [557] = "evmergeloRRR",
    240   [558] = "evmergehiloRRR", [559] = "evmergelohiRRR",
    241 
    242   [560] = "evcmpgtuYRR", [561] = "evcmpgtsYRR",
    243   [562] = "evcmpltuYRR", [563] = "evcmpltsYRR",
    244   [564] = "evcmpeqYRR",
    245 
    246   [632] = "evselRRR", [633] = "evselRRRW",
    247   [634] = "evselRRRW", [635] = "evselRRRW",
    248   [636] = "evselRRRW", [637] = "evselRRRW",
    249   [638] = "evselRRRW", [639] = "evselRRRW",
    250 
    251   [640] = "evfsaddRRR", [641] = "evfssubRRR",
    252   [644] = "evfsabsRR", [645] = "evfsnabsRR", [646] = "evfsnegRR",
    253   [648] = "evfsmulRRR", [649] = "evfsdivRRR",
    254   [652] = "evfscmpgtYRR", [653] = "evfscmpltYRR", [654] = "evfscmpeqYRR",
    255   [656] = "evfscfuiR-R", [657] = "evfscfsiR-R",
    256   [658] = "evfscfufR-R", [659] = "evfscfsfR-R",
    257   [660] = "evfsctuiR-R", [661] = "evfsctsiR-R",
    258   [662] = "evfsctufR-R", [663] = "evfsctsfR-R",
    259   [664] = "evfsctuizR-R", [666] = "evfsctsizR-R",
    260   [668] = "evfststgtYRR", [669] = "evfststltYRR", [670] = "evfststeqYRR",
    261 
    262   [704] = "efsaddRRR", [705] = "efssubRRR",
    263   [708] = "efsabsRR", [709] = "efsnabsRR", [710] = "efsnegRR",
    264   [712] = "efsmulRRR", [713] = "efsdivRRR",
    265   [716] = "efscmpgtYRR", [717] = "efscmpltYRR", [718] = "efscmpeqYRR",
    266   [719] = "efscfdR-R",
    267   [720] = "efscfuiR-R", [721] = "efscfsiR-R",
    268   [722] = "efscfufR-R", [723] = "efscfsfR-R",
    269   [724] = "efsctuiR-R", [725] = "efsctsiR-R",
    270   [726] = "efsctufR-R", [727] = "efsctsfR-R",
    271   [728] = "efsctuizR-R", [730] = "efsctsizR-R",
    272   [732] = "efststgtYRR", [733] = "efststltYRR", [734] = "efststeqYRR",
    273 
    274   [736] = "efdaddRRR", [737] = "efdsubRRR",
    275   [738] = "efdcfuidR-R", [739] = "efdcfsidR-R",
    276   [740] = "efdabsRR", [741] = "efdnabsRR", [742] = "efdnegRR",
    277   [744] = "efdmulRRR", [745] = "efddivRRR",
    278   [746] = "efdctuidzR-R", [747] = "efdctsidzR-R",
    279   [748] = "efdcmpgtYRR", [749] = "efdcmpltYRR", [750] = "efdcmpeqYRR",
    280   [751] = "efdcfsR-R",
    281   [752] = "efdcfuiR-R", [753] = "efdcfsiR-R",
    282   [754] = "efdcfufR-R", [755] = "efdcfsfR-R",
    283   [756] = "efdctuiR-R", [757] = "efdctsiR-R",
    284   [758] = "efdctufR-R", [759] = "efdctsfR-R",
    285   [760] = "efdctuizR-R", [762] = "efdctsizR-R",
    286   [764] = "efdtstgtYRR", [765] = "efdtstltYRR", [766] = "efdtsteqYRR",
    287 
    288   [768] = "evlddxRR0R", [769] = "evlddRR8",
    289   [770] = "evldwxRR0R", [771] = "evldwRR8",
    290   [772] = "evldhxRR0R", [773] = "evldhRR8",
    291   [776] = "evlhhesplatxRR0R", [777] = "evlhhesplatRR2",
    292   [780] = "evlhhousplatxRR0R", [781] = "evlhhousplatRR2",
    293   [782] = "evlhhossplatxRR0R", [783] = "evlhhossplatRR2",
    294   [784] = "evlwhexRR0R", [785] = "evlwheRR4",
    295   [788] = "evlwhouxRR0R", [789] = "evlwhouRR4",
    296   [790] = "evlwhosxRR0R", [791] = "evlwhosRR4",
    297   [792] = "evlwwsplatxRR0R", [793] = "evlwwsplatRR4",
    298   [796] = "evlwhsplatxRR0R", [797] = "evlwhsplatRR4",
    299 
    300   [800] = "evstddxRR0R", [801] = "evstddRR8",
    301   [802] = "evstdwxRR0R", [803] = "evstdwRR8",
    302   [804] = "evstdhxRR0R", [805] = "evstdhRR8",
    303   [816] = "evstwhexRR0R", [817] = "evstwheRR4",
    304   [820] = "evstwhoxRR0R", [821] = "evstwhoRR4",
    305   [824] = "evstwwexRR0R", [825] = "evstwweRR4",
    306   [828] = "evstwwoxRR0R", [829] = "evstwwoRR4",
    307 
    308   [1027] = "evmhessfRRR", [1031] = "evmhossfRRR", [1032] = "evmheumiRRR",
    309   [1033] = "evmhesmiRRR", [1035] = "evmhesmfRRR", [1036] = "evmhoumiRRR",
    310   [1037] = "evmhosmiRRR", [1039] = "evmhosmfRRR", [1059] = "evmhessfaRRR",
    311   [1063] = "evmhossfaRRR", [1064] = "evmheumiaRRR", [1065] = "evmhesmiaRRR",
    312   [1067] = "evmhesmfaRRR", [1068] = "evmhoumiaRRR", [1069] = "evmhosmiaRRR",
    313   [1071] = "evmhosmfaRRR", [1095] = "evmwhssfRRR", [1096] = "evmwlumiRRR",
    314   [1100] = "evmwhumiRRR", [1101] = "evmwhsmiRRR", [1103] = "evmwhsmfRRR",
    315   [1107] = "evmwssfRRR", [1112] = "evmwumiRRR", [1113] = "evmwsmiRRR",
    316   [1115] = "evmwsmfRRR", [1127] = "evmwhssfaRRR", [1128] = "evmwlumiaRRR",
    317   [1132] = "evmwhumiaRRR", [1133] = "evmwhsmiaRRR", [1135] = "evmwhsmfaRRR",
    318   [1139] = "evmwssfaRRR", [1144] = "evmwumiaRRR", [1145] = "evmwsmiaRRR",
    319   [1147] = "evmwsmfaRRR",
    320 
    321   [1216] = "evaddusiaawRR", [1217] = "evaddssiaawRR",
    322   [1218] = "evsubfusiaawRR", [1219] = "evsubfssiaawRR",
    323   [1220] = "evmraRR",
    324   [1222] = "evdivwsRRR", [1223] = "evdivwuRRR",
    325   [1224] = "evaddumiaawRR", [1225] = "evaddsmiaawRR",
    326   [1226] = "evsubfumiaawRR", [1227] = "evsubfsmiaawRR",
    327 
    328   [1280] = "evmheusiaawRRR", [1281] = "evmhessiaawRRR",
    329   [1283] = "evmhessfaawRRR", [1284] = "evmhousiaawRRR",
    330   [1285] = "evmhossiaawRRR", [1287] = "evmhossfaawRRR",
    331   [1288] = "evmheumiaawRRR", [1289] = "evmhesmiaawRRR",
    332   [1291] = "evmhesmfaawRRR", [1292] = "evmhoumiaawRRR",
    333   [1293] = "evmhosmiaawRRR", [1295] = "evmhosmfaawRRR",
    334   [1320] = "evmhegumiaaRRR", [1321] = "evmhegsmiaaRRR",
    335   [1323] = "evmhegsmfaaRRR", [1324] = "evmhogumiaaRRR",
    336   [1325] = "evmhogsmiaaRRR", [1327] = "evmhogsmfaaRRR",
    337   [1344] = "evmwlusiaawRRR", [1345] = "evmwlssiaawRRR",
    338   [1352] = "evmwlumiaawRRR", [1353] = "evmwlsmiaawRRR",
    339   [1363] = "evmwssfaaRRR", [1368] = "evmwumiaaRRR",
    340   [1369] = "evmwsmiaaRRR", [1371] = "evmwsmfaaRRR",
    341   [1408] = "evmheusianwRRR", [1409] = "evmhessianwRRR",
    342   [1411] = "evmhessfanwRRR", [1412] = "evmhousianwRRR",
    343   [1413] = "evmhossianwRRR", [1415] = "evmhossfanwRRR",
    344   [1416] = "evmheumianwRRR", [1417] = "evmhesmianwRRR",
    345   [1419] = "evmhesmfanwRRR", [1420] = "evmhoumianwRRR",
    346   [1421] = "evmhosmianwRRR", [1423] = "evmhosmfanwRRR",
    347   [1448] = "evmhegumianRRR", [1449] = "evmhegsmianRRR",
    348   [1451] = "evmhegsmfanRRR", [1452] = "evmhogumianRRR",
    349   [1453] = "evmhogsmianRRR", [1455] = "evmhogsmfanRRR",
    350   [1472] = "evmwlusianwRRR", [1473] = "evmwlssianwRRR",
    351   [1480] = "evmwlumianwRRR", [1481] = "evmwlsmianwRRR",
    352   [1491] = "evmwssfanRRR", [1496] = "evmwumianRRR",
    353   [1497] = "evmwsmianRRR", [1499] = "evmwsmfanRRR",
    354 }
    355 
    356 local map_pri = {
    357   [0] = false,	false,		"tdiARI",	"twiARI",
    358   map_spe,	false,		false,		"mulliRRI",
    359   "subficRRI",	false,		"cmpl_iYLRU",	"cmp_iYLRI",
    360   "addicRRI",	"addic.RRI",	"addi|liRR0I",	"addis|lisRR0I",
    361   "b_KBJ",	"sc",		 "bKJ",		map_crops,
    362   "rlwimiRR~AAA.", map_rlwinm,	false,		"rlwnmRR~RAA.",
    363   "oriNRR~U",	"orisRR~U",	"xoriRR~U",	"xorisRR~U",
    364   "andi.RR~U",	"andis.RR~U",	map_rld,	map_ext,
    365   "lwzRRD",	"lwzuRRD",	"lbzRRD",	"lbzuRRD",
    366   "stwRRD",	"stwuRRD",	"stbRRD",	"stbuRRD",
    367   "lhzRRD",	"lhzuRRD",	"lhaRRD",	"lhauRRD",
    368   "sthRRD",	"sthuRRD",	"lmwRRD",	"stmwRRD",
    369   "lfsFRD",	"lfsuFRD",	"lfdFRD",	"lfduFRD",
    370   "stfsFRD",	"stfsuFRD",	"stfdFRD",	"stfduFRD",
    371   false,	false,		map_ld,		map_fps,
    372   false,	false,		map_std,	map_fpd,
    373 }
    374 
    375 ------------------------------------------------------------------------------
    376 
    377 local map_gpr = {
    378   [0] = "r0", "sp", "r2", "r3", "r4", "r5", "r6", "r7",
    379   "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
    380   "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
    381   "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31",
    382 }
    383 
    384 local map_cond = { [0] = "lt", "gt", "eq", "so", "ge", "le", "ne", "ns", }
    385 
    386 -- Format a condition bit.
    387 local function condfmt(cond)
    388   if cond <= 3 then
    389     return map_cond[band(cond, 3)]
    390   else
    391     return format("4*cr%d+%s", rshift(cond, 2), map_cond[band(cond, 3)])
    392   end
    393 end
    394 
    395 ------------------------------------------------------------------------------
    396 
    397 -- Output a nicely formatted line with an opcode and operands.
    398 local function putop(ctx, text, operands)
    399   local pos = ctx.pos
    400   local extra = ""
    401   if ctx.rel then
    402     local sym = ctx.symtab[ctx.rel]
    403     if sym then extra = "\t->"..sym end
    404   end
    405   if ctx.hexdump > 0 then
    406     ctx.out(format("%08x  %s  %-7s %s%s\n",
    407 	    ctx.addr+pos, tohex(ctx.op), text, concat(operands, ", "), extra))
    408   else
    409     ctx.out(format("%08x  %-7s %s%s\n",
    410 	    ctx.addr+pos, text, concat(operands, ", "), extra))
    411   end
    412   ctx.pos = pos + 4
    413 end
    414 
    415 -- Fallback for unknown opcodes.
    416 local function unknown(ctx)
    417   return putop(ctx, ".long", { "0x"..tohex(ctx.op) })
    418 end
    419 
    420 -- Disassemble a single instruction.
    421 local function disass_ins(ctx)
    422   local pos = ctx.pos
    423   local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4)
    424   local op = bor(lshift(b0, 24), lshift(b1, 16), lshift(b2, 8), b3)
    425   local operands = {}
    426   local last = nil
    427   local rs = 21
    428   ctx.op = op
    429   ctx.rel = nil
    430 
    431   local opat = map_pri[rshift(b0, 2)]
    432   while type(opat) ~= "string" do
    433     if not opat then return unknown(ctx) end
    434     opat = opat[band(rshift(op, opat.shift), opat.mask)]
    435   end
    436   local name, pat = match(opat, "^([a-z0-9_.]*)(.*)")
    437   local altname, pat2 = match(pat, "|([a-z0-9_.]*)(.*)")
    438   if altname then pat = pat2 end
    439 
    440   for p in gmatch(pat, ".") do
    441     local x = nil
    442     if p == "R" then
    443       x = map_gpr[band(rshift(op, rs), 31)]
    444       rs = rs - 5
    445     elseif p == "F" then
    446       x = "f"..band(rshift(op, rs), 31)
    447       rs = rs - 5
    448     elseif p == "A" then
    449       x = band(rshift(op, rs), 31)
    450       rs = rs - 5
    451     elseif p == "S" then
    452       x = arshift(lshift(op, 27-rs), 27)
    453       rs = rs - 5
    454     elseif p == "I" then
    455       x = arshift(lshift(op, 16), 16)
    456     elseif p == "U" then
    457       x = band(op, 0xffff)
    458     elseif p == "D" or p == "E" then
    459       local disp = arshift(lshift(op, 16), 16)
    460       if p == "E" then disp = band(disp, -4) end
    461       if last == "r0" then last = "0" end
    462       operands[#operands] = format("%d(%s)", disp, last)
    463     elseif p >= "2" and p <= "8" then
    464       local disp = band(rshift(op, rs), 31) * p
    465       if last == "r0" then last = "0" end
    466       operands[#operands] = format("%d(%s)", disp, last)
    467     elseif p == "H" then
    468       x = band(rshift(op, rs), 31) + lshift(band(op, 2), 4)
    469       rs = rs - 5
    470     elseif p == "M" then
    471       x = band(rshift(op, rs), 31) + band(op, 0x20)
    472     elseif p == "C" then
    473       x = condfmt(band(rshift(op, rs), 31))
    474       rs = rs - 5
    475     elseif p == "B" then
    476       local bo = rshift(op, 21)
    477       local cond = band(rshift(op, 16), 31)
    478       local cn = ""
    479       rs = rs - 10
    480       if band(bo, 4) == 0 then
    481 	cn = band(bo, 2) == 0 and "dnz" or "dz"
    482 	if band(bo, 0x10) == 0 then
    483 	  cn = cn..(band(bo, 8) == 0 and "f" or "t")
    484 	end
    485 	if band(bo, 0x10) == 0 then x = condfmt(cond) end
    486 	name = name..(band(bo, 1) == band(rshift(op, 15), 1) and "-" or "+")
    487       elseif band(bo, 0x10) == 0 then
    488 	cn = map_cond[band(cond, 3) + (band(bo, 8) == 0 and 4 or 0)]
    489 	if cond > 3 then x = "cr"..rshift(cond, 2) end
    490 	name = name..(band(bo, 1) == band(rshift(op, 15), 1) and "-" or "+")
    491       end
    492       name = gsub(name, "_", cn)
    493     elseif p == "J" then
    494       x = arshift(lshift(op, 27-rs), 29-rs)*4
    495       if band(op, 2) == 0 then x = ctx.addr + pos + x end
    496       ctx.rel = x
    497       x = "0x"..tohex(x)
    498     elseif p == "K" then
    499       if band(op, 1) ~= 0 then name = name.."l" end
    500       if band(op, 2) ~= 0 then name = name.."a" end
    501     elseif p == "X" or p == "Y" then
    502       x = band(rshift(op, rs+2), 7)
    503       if x == 0 and p == "Y" then x = nil else x = "cr"..x end
    504       rs = rs - 5
    505     elseif p == "W" then
    506       x = "cr"..band(op, 7)
    507     elseif p == "Z" then
    508       x = band(rshift(op, rs-4), 255)
    509       rs = rs - 10
    510     elseif p == ">" then
    511       operands[#operands] = rshift(operands[#operands], 1)
    512     elseif p == "0" then
    513       if last == "r0" then
    514 	operands[#operands] = nil
    515 	if altname then name = altname end
    516       end
    517     elseif p == "L" then
    518       name = gsub(name, "_", band(op, 0x00200000) ~= 0 and "d" or "w")
    519     elseif p == "." then
    520       if band(op, 1) == 1 then name = name.."." end
    521     elseif p == "N" then
    522       if op == 0x60000000 then name = "nop"; break end
    523     elseif p == "~" then
    524       local n = #operands
    525       operands[n-1],  operands[n] = operands[n], operands[n-1]
    526     elseif p == "=" then
    527       local n = #operands
    528       if last == operands[n-1] then
    529 	operands[n] = nil
    530 	name = altname
    531       end
    532     elseif p == "%" then
    533       local n = #operands
    534       if last == operands[n-1] and last == operands[n-2] then
    535 	operands[n] = nil
    536 	operands[n-1] = nil
    537 	name = altname
    538       end
    539     elseif p == "-" then
    540       rs = rs - 5
    541     else
    542       assert(false)
    543     end
    544     if x then operands[#operands+1] = x; last = x end
    545   end
    546 
    547   return putop(ctx, name, operands)
    548 end
    549 
    550 ------------------------------------------------------------------------------
    551 
    552 -- Disassemble a block of code.
    553 local function disass_block(ctx, ofs, len)
    554   if not ofs then ofs = 0 end
    555   local stop = len and ofs+len or #ctx.code
    556   stop = stop - stop % 4
    557   ctx.pos = ofs - ofs % 4
    558   ctx.rel = nil
    559   while ctx.pos < stop do disass_ins(ctx) end
    560 end
    561 
    562 -- Extended API: create a disassembler context. Then call ctx:disass(ofs, len).
    563 local function create(code, addr, out)
    564   local ctx = {}
    565   ctx.code = code
    566   ctx.addr = addr or 0
    567   ctx.out = out or io.write
    568   ctx.symtab = {}
    569   ctx.disass = disass_block
    570   ctx.hexdump = 8
    571   return ctx
    572 end
    573 
    574 -- Simple API: disassemble code (a string) at address and output via out.
    575 local function disass(code, addr, out)
    576   create(code, addr, out):disass()
    577 end
    578 
    579 -- Return register name for RID.
    580 local function regname(r)
    581   if r < 32 then return map_gpr[r] end
    582   return "f"..(r-32)
    583 end
    584 
    585 -- Public module functions.
    586 return {
    587   create = create,
    588   disass = disass,
    589   regname = regname
    590 }
    591