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_arm.lua (19363B)


      1 ----------------------------------------------------------------------------
      2 -- LuaJIT ARM disassembler module.
      3 --
      4 -- Copyright (C) 2005-2016 Mike Pall. All rights reserved.
      5 -- Released under the MIT 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 most user-mode ARMv7 instructions
     10 -- NYI: Advanced SIMD and VFP instructions.
     11 ------------------------------------------------------------------------------
     12 
     13 local type = type
     14 local sub, byte, format = string.sub, string.byte, string.format
     15 local match, gmatch, gsub = string.match, string.gmatch, string.gsub
     16 local concat = table.concat
     17 local bit = require("bit")
     18 local band, bor, ror, tohex = bit.band, bit.bor, bit.ror, bit.tohex
     19 local lshift, rshift, arshift = bit.lshift, bit.rshift, bit.arshift
     20 
     21 ------------------------------------------------------------------------------
     22 -- Opcode maps
     23 ------------------------------------------------------------------------------
     24 
     25 local map_loadc = {
     26   shift = 8, mask = 15,
     27   [10] = {
     28     shift = 20, mask = 1,
     29     [0] = {
     30       shift = 23, mask = 3,
     31       [0] = "vmovFmDN", "vstmFNdr",
     32       _ = {
     33 	shift = 21, mask = 1,
     34 	[0] = "vstrFdl",
     35 	{ shift = 16, mask = 15, [13] = "vpushFdr", _ = "vstmdbFNdr", }
     36       },
     37     },
     38     {
     39       shift = 23, mask = 3,
     40       [0] = "vmovFDNm",
     41       { shift = 16, mask = 15, [13] = "vpopFdr", _ = "vldmFNdr", },
     42       _ = {
     43 	shift = 21, mask = 1,
     44 	[0] = "vldrFdl", "vldmdbFNdr",
     45       },
     46     },
     47   },
     48   [11] = {
     49     shift = 20, mask = 1,
     50     [0] = {
     51       shift = 23, mask = 3,
     52       [0] = "vmovGmDN", "vstmGNdr",
     53       _ = {
     54 	shift = 21, mask = 1,
     55 	[0] = "vstrGdl",
     56 	{ shift = 16, mask = 15, [13] = "vpushGdr", _ = "vstmdbGNdr", }
     57       },
     58     },
     59     {
     60       shift = 23, mask = 3,
     61       [0] = "vmovGDNm",
     62       { shift = 16, mask = 15, [13] = "vpopGdr", _ = "vldmGNdr", },
     63       _ = {
     64 	shift = 21, mask = 1,
     65 	[0] = "vldrGdl", "vldmdbGNdr",
     66       },
     67     },
     68   },
     69   _ = {
     70     shift = 0, mask = 0 -- NYI ldc, mcrr, mrrc.
     71   },
     72 }
     73 
     74 local map_vfps = {
     75   shift = 6, mask = 0x2c001,
     76   [0] = "vmlaF.dnm", "vmlsF.dnm",
     77   [0x04000] = "vnmlsF.dnm", [0x04001] = "vnmlaF.dnm",
     78   [0x08000] = "vmulF.dnm", [0x08001] = "vnmulF.dnm",
     79   [0x0c000] = "vaddF.dnm", [0x0c001] = "vsubF.dnm",
     80   [0x20000] = "vdivF.dnm",
     81   [0x24000] = "vfnmsF.dnm", [0x24001] = "vfnmaF.dnm",
     82   [0x28000] = "vfmaF.dnm", [0x28001] = "vfmsF.dnm",
     83   [0x2c000] = "vmovF.dY",
     84   [0x2c001] = {
     85     shift = 7, mask = 0x1e01,
     86     [0] = "vmovF.dm", "vabsF.dm",
     87     [0x0200] = "vnegF.dm", [0x0201] = "vsqrtF.dm",
     88     [0x0800] = "vcmpF.dm", [0x0801] = "vcmpeF.dm",
     89     [0x0a00] = "vcmpzF.d", [0x0a01] = "vcmpzeF.d",
     90     [0x0e01] = "vcvtG.dF.m",
     91     [0x1000] = "vcvt.f32.u32Fdm", [0x1001] = "vcvt.f32.s32Fdm",
     92     [0x1800] = "vcvtr.u32F.dm", [0x1801] = "vcvt.u32F.dm",
     93     [0x1a00] = "vcvtr.s32F.dm", [0x1a01] = "vcvt.s32F.dm",
     94   },
     95 }
     96 
     97 local map_vfpd = {
     98   shift = 6, mask = 0x2c001,
     99   [0] = "vmlaG.dnm", "vmlsG.dnm",
    100   [0x04000] = "vnmlsG.dnm", [0x04001] = "vnmlaG.dnm",
    101   [0x08000] = "vmulG.dnm", [0x08001] = "vnmulG.dnm",
    102   [0x0c000] = "vaddG.dnm", [0x0c001] = "vsubG.dnm",
    103   [0x20000] = "vdivG.dnm",
    104   [0x24000] = "vfnmsG.dnm", [0x24001] = "vfnmaG.dnm",
    105   [0x28000] = "vfmaG.dnm", [0x28001] = "vfmsG.dnm",
    106   [0x2c000] = "vmovG.dY",
    107   [0x2c001] = {
    108     shift = 7, mask = 0x1e01,
    109     [0] = "vmovG.dm", "vabsG.dm",
    110     [0x0200] = "vnegG.dm", [0x0201] = "vsqrtG.dm",
    111     [0x0800] = "vcmpG.dm", [0x0801] = "vcmpeG.dm",
    112     [0x0a00] = "vcmpzG.d", [0x0a01] = "vcmpzeG.d",
    113     [0x0e01] = "vcvtF.dG.m",
    114     [0x1000] = "vcvt.f64.u32GdFm", [0x1001] = "vcvt.f64.s32GdFm",
    115     [0x1800] = "vcvtr.u32FdG.m", [0x1801] = "vcvt.u32FdG.m",
    116     [0x1a00] = "vcvtr.s32FdG.m", [0x1a01] = "vcvt.s32FdG.m",
    117   },
    118 }
    119 
    120 local map_datac = {
    121   shift = 24, mask = 1,
    122   [0] = {
    123     shift = 4, mask = 1,
    124     [0] = {
    125       shift = 8, mask = 15,
    126       [10] = map_vfps,
    127       [11] = map_vfpd,
    128       -- NYI cdp, mcr, mrc.
    129     },
    130     {
    131       shift = 8, mask = 15,
    132       [10] = {
    133 	shift = 20, mask = 15,
    134 	[0] = "vmovFnD", "vmovFDn",
    135 	[14] = "vmsrD",
    136 	[15] = { shift = 12, mask = 15, [15] = "vmrs", _ = "vmrsD", },
    137       },
    138     },
    139   },
    140   "svcT",
    141 }
    142 
    143 local map_loadcu = {
    144   shift = 0, mask = 0, -- NYI unconditional CP load/store.
    145 }
    146 
    147 local map_datacu = {
    148   shift = 0, mask = 0, -- NYI unconditional CP data.
    149 }
    150 
    151 local map_simddata = {
    152   shift = 0, mask = 0, -- NYI SIMD data.
    153 }
    154 
    155 local map_simdload = {
    156   shift = 0, mask = 0, -- NYI SIMD load/store, preload.
    157 }
    158 
    159 local map_preload = {
    160   shift = 0, mask = 0, -- NYI preload.
    161 }
    162 
    163 local map_media = {
    164   shift = 20, mask = 31,
    165   [0] = false,
    166   { --01
    167     shift = 5, mask = 7,
    168     [0] = "sadd16DNM", "sasxDNM", "ssaxDNM", "ssub16DNM",
    169     "sadd8DNM", false, false, "ssub8DNM",
    170   },
    171   { --02
    172     shift = 5, mask = 7,
    173     [0] = "qadd16DNM", "qasxDNM", "qsaxDNM", "qsub16DNM",
    174     "qadd8DNM", false, false, "qsub8DNM",
    175   },
    176   { --03
    177     shift = 5, mask = 7,
    178     [0] = "shadd16DNM", "shasxDNM", "shsaxDNM", "shsub16DNM",
    179     "shadd8DNM", false, false, "shsub8DNM",
    180   },
    181   false,
    182   { --05
    183     shift = 5, mask = 7,
    184     [0] = "uadd16DNM", "uasxDNM", "usaxDNM", "usub16DNM",
    185     "uadd8DNM", false, false, "usub8DNM",
    186   },
    187   { --06
    188     shift = 5, mask = 7,
    189     [0] = "uqadd16DNM", "uqasxDNM", "uqsaxDNM", "uqsub16DNM",
    190     "uqadd8DNM", false, false, "uqsub8DNM",
    191   },
    192   { --07
    193     shift = 5, mask = 7,
    194     [0] = "uhadd16DNM", "uhasxDNM", "uhsaxDNM", "uhsub16DNM",
    195     "uhadd8DNM", false, false, "uhsub8DNM",
    196   },
    197   { --08
    198     shift = 5, mask = 7,
    199     [0] = "pkhbtDNMU", false, "pkhtbDNMU",
    200     { shift = 16, mask = 15, [15] = "sxtb16DMU", _ = "sxtab16DNMU", },
    201     "pkhbtDNMU", "selDNM", "pkhtbDNMU",
    202   },
    203   false,
    204   { --0a
    205     shift = 5, mask = 7,
    206     [0] = "ssatDxMu", "ssat16DxM", "ssatDxMu",
    207     { shift = 16, mask = 15, [15] = "sxtbDMU", _ = "sxtabDNMU", },
    208     "ssatDxMu", false, "ssatDxMu",
    209   },
    210   { --0b
    211     shift = 5, mask = 7,
    212     [0] = "ssatDxMu", "revDM", "ssatDxMu",
    213     { shift = 16, mask = 15, [15] = "sxthDMU", _ = "sxtahDNMU", },
    214     "ssatDxMu", "rev16DM", "ssatDxMu",
    215   },
    216   { --0c
    217     shift = 5, mask = 7,
    218     [3] = { shift = 16, mask = 15, [15] = "uxtb16DMU", _ = "uxtab16DNMU", },
    219   },
    220   false,
    221   { --0e
    222     shift = 5, mask = 7,
    223     [0] = "usatDwMu", "usat16DwM", "usatDwMu",
    224     { shift = 16, mask = 15, [15] = "uxtbDMU", _ = "uxtabDNMU", },
    225     "usatDwMu", false, "usatDwMu",
    226   },
    227   { --0f
    228     shift = 5, mask = 7,
    229     [0] = "usatDwMu", "rbitDM", "usatDwMu",
    230     { shift = 16, mask = 15, [15] = "uxthDMU", _ = "uxtahDNMU", },
    231     "usatDwMu", "revshDM", "usatDwMu",
    232   },
    233   { --10
    234     shift = 12, mask = 15,
    235     [15] = {
    236       shift = 5, mask = 7,
    237       "smuadNMS", "smuadxNMS", "smusdNMS", "smusdxNMS",
    238     },
    239     _ = {
    240       shift = 5, mask = 7,
    241       [0] = "smladNMSD", "smladxNMSD", "smlsdNMSD", "smlsdxNMSD",
    242     },
    243   },
    244   false, false, false,
    245   { --14
    246     shift = 5, mask = 7,
    247     [0] = "smlaldDNMS", "smlaldxDNMS", "smlsldDNMS", "smlsldxDNMS",
    248   },
    249   { --15
    250     shift = 5, mask = 7,
    251     [0] = { shift = 12, mask = 15, [15] = "smmulNMS", _ = "smmlaNMSD", },
    252     { shift = 12, mask = 15, [15] = "smmulrNMS", _ = "smmlarNMSD", },
    253     false, false, false, false,
    254     "smmlsNMSD", "smmlsrNMSD",
    255   },
    256   false, false,
    257   { --18
    258     shift = 5, mask = 7,
    259     [0] = { shift = 12, mask = 15, [15] = "usad8NMS", _ = "usada8NMSD", },
    260   },
    261   false,
    262   { --1a
    263     shift = 5, mask = 3, [2] = "sbfxDMvw",
    264   },
    265   { --1b
    266     shift = 5, mask = 3, [2] = "sbfxDMvw",
    267   },
    268   { --1c
    269     shift = 5, mask = 3,
    270     [0] = { shift = 0, mask = 15, [15] = "bfcDvX", _ = "bfiDMvX", },
    271   },
    272   { --1d
    273     shift = 5, mask = 3,
    274     [0] = { shift = 0, mask = 15, [15] = "bfcDvX", _ = "bfiDMvX", },
    275   },
    276   { --1e
    277     shift = 5, mask = 3, [2] = "ubfxDMvw",
    278   },
    279   { --1f
    280     shift = 5, mask = 3, [2] = "ubfxDMvw",
    281   },
    282 }
    283 
    284 local map_load = {
    285   shift = 21, mask = 9,
    286   {
    287     shift = 20, mask = 5,
    288     [0] = "strtDL", "ldrtDL", [4] = "strbtDL", [5] = "ldrbtDL",
    289   },
    290   _ = {
    291     shift = 20, mask = 5,
    292     [0] = "strDL", "ldrDL", [4] = "strbDL", [5] = "ldrbDL",
    293   }
    294 }
    295 
    296 local map_load1 = {
    297   shift = 4, mask = 1,
    298   [0] = map_load, map_media,
    299 }
    300 
    301 local map_loadm = {
    302   shift = 20, mask = 1,
    303   [0] = {
    304     shift = 23, mask = 3,
    305     [0] = "stmdaNR", "stmNR",
    306     { shift = 16, mask = 63, [45] = "pushR", _ = "stmdbNR", }, "stmibNR",
    307   },
    308   {
    309     shift = 23, mask = 3,
    310     [0] = "ldmdaNR", { shift = 16, mask = 63, [61] = "popR", _ = "ldmNR", },
    311     "ldmdbNR", "ldmibNR",
    312   },
    313 }
    314 
    315 local map_data = {
    316   shift = 21, mask = 15,
    317   [0] = "andDNPs", "eorDNPs", "subDNPs", "rsbDNPs",
    318   "addDNPs", "adcDNPs", "sbcDNPs", "rscDNPs",
    319   "tstNP", "teqNP", "cmpNP", "cmnNP",
    320   "orrDNPs", "movDPs", "bicDNPs", "mvnDPs",
    321 }
    322 
    323 local map_mul = {
    324   shift = 21, mask = 7,
    325   [0] = "mulNMSs", "mlaNMSDs", "umaalDNMS", "mlsDNMS",
    326   "umullDNMSs", "umlalDNMSs", "smullDNMSs", "smlalDNMSs",
    327 }
    328 
    329 local map_sync = {
    330   shift = 20, mask = 15, -- NYI: brackets around N. R(D+1) for ldrexd/strexd.
    331   [0] = "swpDMN", false, false, false,
    332   "swpbDMN", false, false, false,
    333   "strexDMN", "ldrexDN", "strexdDN", "ldrexdDN",
    334   "strexbDMN", "ldrexbDN", "strexhDN", "ldrexhDN",
    335 }
    336 
    337 local map_mulh = {
    338   shift = 21, mask = 3,
    339   [0] = { shift = 5, mask = 3,
    340     [0] = "smlabbNMSD", "smlatbNMSD", "smlabtNMSD", "smlattNMSD", },
    341   { shift = 5, mask = 3,
    342     [0] = "smlawbNMSD", "smulwbNMS", "smlawtNMSD", "smulwtNMS", },
    343   { shift = 5, mask = 3,
    344     [0] = "smlalbbDNMS", "smlaltbDNMS", "smlalbtDNMS", "smlalttDNMS", },
    345   { shift = 5, mask = 3,
    346     [0] = "smulbbNMS", "smultbNMS", "smulbtNMS", "smulttNMS", },
    347 }
    348 
    349 local map_misc = {
    350   shift = 4, mask = 7,
    351   -- NYI: decode PSR bits of msr.
    352   [0] = { shift = 21, mask = 1, [0] = "mrsD", "msrM", },
    353   { shift = 21, mask = 3, "bxM", false, "clzDM", },
    354   { shift = 21, mask = 3, "bxjM", },
    355   { shift = 21, mask = 3, "blxM", },
    356   false,
    357   { shift = 21, mask = 3, [0] = "qaddDMN", "qsubDMN", "qdaddDMN", "qdsubDMN", },
    358   false,
    359   { shift = 21, mask = 3, "bkptK", },
    360 }
    361 
    362 local map_datar = {
    363   shift = 4, mask = 9,
    364   [9] = {
    365     shift = 5, mask = 3,
    366     [0] = { shift = 24, mask = 1, [0] = map_mul, map_sync, },
    367     { shift = 20, mask = 1, [0] = "strhDL", "ldrhDL", },
    368     { shift = 20, mask = 1, [0] = "ldrdDL", "ldrsbDL", },
    369     { shift = 20, mask = 1, [0] = "strdDL", "ldrshDL", },
    370   },
    371   _ = {
    372     shift = 20, mask = 25,
    373     [16] = { shift = 7, mask = 1, [0] = map_misc, map_mulh, },
    374     _ = {
    375       shift = 0, mask = 0xffffffff,
    376       [bor(0xe1a00000)] = "nop",
    377       _ = map_data,
    378     }
    379   },
    380 }
    381 
    382 local map_datai = {
    383   shift = 20, mask = 31, -- NYI: decode PSR bits of msr. Decode imm12.
    384   [16] = "movwDW", [20] = "movtDW",
    385   [18] = { shift = 0, mask = 0xf00ff, [0] = "nopv6", _ = "msrNW", },
    386   [22] = "msrNW",
    387   _ = map_data,
    388 }
    389 
    390 local map_branch = {
    391   shift = 24, mask = 1,
    392   [0] = "bB", "blB"
    393 }
    394 
    395 local map_condins = {
    396   [0] = map_datar, map_datai, map_load, map_load1,
    397   map_loadm, map_branch, map_loadc, map_datac
    398 }
    399 
    400 -- NYI: setend.
    401 local map_uncondins = {
    402   [0] = false, map_simddata, map_simdload, map_preload,
    403   false, "blxB", map_loadcu, map_datacu,
    404 }
    405 
    406 ------------------------------------------------------------------------------
    407 
    408 local map_gpr = {
    409   [0] = "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
    410   "r8", "r9", "r10", "r11", "r12", "sp", "lr", "pc",
    411 }
    412 
    413 local map_cond = {
    414   [0] = "eq", "ne", "hs", "lo", "mi", "pl", "vs", "vc",
    415   "hi", "ls", "ge", "lt", "gt", "le", "al",
    416 }
    417 
    418 local map_shift = { [0] = "lsl", "lsr", "asr", "ror", }
    419 
    420 ------------------------------------------------------------------------------
    421 
    422 -- Output a nicely formatted line with an opcode and operands.
    423 local function putop(ctx, text, operands)
    424   local pos = ctx.pos
    425   local extra = ""
    426   if ctx.rel then
    427     local sym = ctx.symtab[ctx.rel]
    428     if sym then
    429       extra = "\t->"..sym
    430     elseif band(ctx.op, 0x0e000000) ~= 0x0a000000 then
    431       extra = "\t; 0x"..tohex(ctx.rel)
    432     end
    433   end
    434   if ctx.hexdump > 0 then
    435     ctx.out(format("%08x  %s  %-5s %s%s\n",
    436 	    ctx.addr+pos, tohex(ctx.op), text, concat(operands, ", "), extra))
    437   else
    438     ctx.out(format("%08x  %-5s %s%s\n",
    439 	    ctx.addr+pos, text, concat(operands, ", "), extra))
    440   end
    441   ctx.pos = pos + 4
    442 end
    443 
    444 -- Fallback for unknown opcodes.
    445 local function unknown(ctx)
    446   return putop(ctx, ".long", { "0x"..tohex(ctx.op) })
    447 end
    448 
    449 -- Format operand 2 of load/store opcodes.
    450 local function fmtload(ctx, op, pos)
    451   local base = map_gpr[band(rshift(op, 16), 15)]
    452   local x, ofs
    453   local ext = (band(op, 0x04000000) == 0)
    454   if not ext and band(op, 0x02000000) == 0 then
    455     ofs = band(op, 4095)
    456     if band(op, 0x00800000) == 0 then ofs = -ofs end
    457     if base == "pc" then ctx.rel = ctx.addr + pos + 8 + ofs end
    458     ofs = "#"..ofs
    459   elseif ext and band(op, 0x00400000) ~= 0 then
    460     ofs = band(op, 15) + band(rshift(op, 4), 0xf0)
    461     if band(op, 0x00800000) == 0 then ofs = -ofs end
    462     if base == "pc" then ctx.rel = ctx.addr + pos + 8 + ofs end
    463     ofs = "#"..ofs
    464   else
    465     ofs = map_gpr[band(op, 15)]
    466     if ext or band(op, 0xfe0) == 0 then
    467     elseif band(op, 0xfe0) == 0x60 then
    468       ofs = format("%s, rrx", ofs)
    469     else
    470       local sh = band(rshift(op, 7), 31)
    471       if sh == 0 then sh = 32 end
    472       ofs = format("%s, %s #%d", ofs, map_shift[band(rshift(op, 5), 3)], sh)
    473     end
    474     if band(op, 0x00800000) == 0 then ofs = "-"..ofs end
    475   end
    476   if ofs == "#0" then
    477     x = format("[%s]", base)
    478   elseif band(op, 0x01000000) == 0 then
    479     x = format("[%s], %s", base, ofs)
    480   else
    481     x = format("[%s, %s]", base, ofs)
    482   end
    483   if band(op, 0x01200000) == 0x01200000 then x = x.."!" end
    484   return x
    485 end
    486 
    487 -- Format operand 2 of vector load/store opcodes.
    488 local function fmtvload(ctx, op, pos)
    489   local base = map_gpr[band(rshift(op, 16), 15)]
    490   local ofs = band(op, 255)*4
    491   if band(op, 0x00800000) == 0 then ofs = -ofs end
    492   if base == "pc" then ctx.rel = ctx.addr + pos + 8 + ofs end
    493   if ofs == 0 then
    494     return format("[%s]", base)
    495   else
    496     return format("[%s, #%d]", base, ofs)
    497   end
    498 end
    499 
    500 local function fmtvr(op, vr, sh0, sh1)
    501   if vr == "s" then
    502     return format("s%d", 2*band(rshift(op, sh0), 15)+band(rshift(op, sh1), 1))
    503   else
    504     return format("d%d", band(rshift(op, sh0), 15)+band(rshift(op, sh1-4), 16))
    505   end
    506 end
    507 
    508 -- Disassemble a single instruction.
    509 local function disass_ins(ctx)
    510   local pos = ctx.pos
    511   local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4)
    512   local op = bor(lshift(b3, 24), lshift(b2, 16), lshift(b1, 8), b0)
    513   local operands = {}
    514   local suffix = ""
    515   local last, name, pat
    516   local vr
    517   ctx.op = op
    518   ctx.rel = nil
    519 
    520   local cond = rshift(op, 28)
    521   local opat
    522   if cond == 15 then
    523     opat = map_uncondins[band(rshift(op, 25), 7)]
    524   else
    525     if cond ~= 14 then suffix = map_cond[cond] end
    526     opat = map_condins[band(rshift(op, 25), 7)]
    527   end
    528   while type(opat) ~= "string" do
    529     if not opat then return unknown(ctx) end
    530     opat = opat[band(rshift(op, opat.shift), opat.mask)] or opat._
    531   end
    532   name, pat = match(opat, "^([a-z0-9]*)(.*)")
    533   if sub(pat, 1, 1) == "." then
    534     local s2, p2 = match(pat, "^([a-z0-9.]*)(.*)")
    535     suffix = suffix..s2
    536     pat = p2
    537   end
    538 
    539   for p in gmatch(pat, ".") do
    540     local x = nil
    541     if p == "D" then
    542       x = map_gpr[band(rshift(op, 12), 15)]
    543     elseif p == "N" then
    544       x = map_gpr[band(rshift(op, 16), 15)]
    545     elseif p == "S" then
    546       x = map_gpr[band(rshift(op, 8), 15)]
    547     elseif p == "M" then
    548       x = map_gpr[band(op, 15)]
    549     elseif p == "d" then
    550       x = fmtvr(op, vr, 12, 22)
    551     elseif p == "n" then
    552       x = fmtvr(op, vr, 16, 7)
    553     elseif p == "m" then
    554       x = fmtvr(op, vr, 0, 5)
    555     elseif p == "P" then
    556       if band(op, 0x02000000) ~= 0 then
    557 	x = ror(band(op, 255), 2*band(rshift(op, 8), 15))
    558       else
    559 	x = map_gpr[band(op, 15)]
    560 	if band(op, 0xff0) ~= 0 then
    561 	  operands[#operands+1] = x
    562 	  local s = map_shift[band(rshift(op, 5), 3)]
    563 	  local r = nil
    564 	  if band(op, 0xf90) == 0 then
    565 	    if s == "ror" then s = "rrx" else r = "#32" end
    566 	  elseif band(op, 0x10) == 0 then
    567 	    r = "#"..band(rshift(op, 7), 31)
    568 	  else
    569 	    r = map_gpr[band(rshift(op, 8), 15)]
    570 	  end
    571 	  if name == "mov" then name = s; x = r
    572 	  elseif r then x = format("%s %s", s, r)
    573 	  else x = s end
    574 	end
    575       end
    576     elseif p == "L" then
    577       x = fmtload(ctx, op, pos)
    578     elseif p == "l" then
    579       x = fmtvload(ctx, op, pos)
    580     elseif p == "B" then
    581       local addr = ctx.addr + pos + 8 + arshift(lshift(op, 8), 6)
    582       if cond == 15 then addr = addr + band(rshift(op, 23), 2) end
    583       ctx.rel = addr
    584       x = "0x"..tohex(addr)
    585     elseif p == "F" then
    586       vr = "s"
    587     elseif p == "G" then
    588       vr = "d"
    589     elseif p == "." then
    590       suffix = suffix..(vr == "s" and ".f32" or ".f64")
    591     elseif p == "R" then
    592       if band(op, 0x00200000) ~= 0 and #operands == 1 then
    593 	operands[1] = operands[1].."!"
    594       end
    595       local t = {}
    596       for i=0,15 do
    597 	if band(rshift(op, i), 1) == 1 then t[#t+1] = map_gpr[i] end
    598       end
    599       x = "{"..concat(t, ", ").."}"
    600     elseif p == "r" then
    601       if band(op, 0x00200000) ~= 0 and #operands == 2 then
    602 	operands[1] = operands[1].."!"
    603       end
    604       local s = tonumber(sub(last, 2))
    605       local n = band(op, 255)
    606       if vr == "d" then n = rshift(n, 1) end
    607       operands[#operands] = format("{%s-%s%d}", last, vr, s+n-1)
    608     elseif p == "W" then
    609       x = band(op, 0x0fff) + band(rshift(op, 4), 0xf000)
    610     elseif p == "T" then
    611       x = "#0x"..tohex(band(op, 0x00ffffff), 6)
    612     elseif p == "U" then
    613       x = band(rshift(op, 7), 31)
    614       if x == 0 then x = nil end
    615     elseif p == "u" then
    616       x = band(rshift(op, 7), 31)
    617       if band(op, 0x40) == 0 then
    618 	if x == 0 then x = nil else x = "lsl #"..x end
    619       else
    620 	if x == 0 then x = "asr #32" else x = "asr #"..x end
    621       end
    622     elseif p == "v" then
    623       x = band(rshift(op, 7), 31)
    624     elseif p == "w" then
    625       x = band(rshift(op, 16), 31)
    626     elseif p == "x" then
    627       x = band(rshift(op, 16), 31) + 1
    628     elseif p == "X" then
    629       x = band(rshift(op, 16), 31) - last + 1
    630     elseif p == "Y" then
    631       x = band(rshift(op, 12), 0xf0) + band(op, 0x0f)
    632     elseif p == "K" then
    633       x = "#0x"..tohex(band(rshift(op, 4), 0x0000fff0) + band(op, 15), 4)
    634     elseif p == "s" then
    635       if band(op, 0x00100000) ~= 0 then suffix = "s"..suffix end
    636     else
    637       assert(false)
    638     end
    639     if x then
    640       last = x
    641       if type(x) == "number" then x = "#"..x end
    642       operands[#operands+1] = x
    643     end
    644   end
    645 
    646   return putop(ctx, name..suffix, operands)
    647 end
    648 
    649 ------------------------------------------------------------------------------
    650 
    651 -- Disassemble a block of code.
    652 local function disass_block(ctx, ofs, len)
    653   if not ofs then ofs = 0 end
    654   local stop = len and ofs+len or #ctx.code
    655   ctx.pos = ofs
    656   ctx.rel = nil
    657   while ctx.pos < stop do disass_ins(ctx) end
    658 end
    659 
    660 -- Extended API: create a disassembler context. Then call ctx:disass(ofs, len).
    661 local function create(code, addr, out)
    662   local ctx = {}
    663   ctx.code = code
    664   ctx.addr = addr or 0
    665   ctx.out = out or io.write
    666   ctx.symtab = {}
    667   ctx.disass = disass_block
    668   ctx.hexdump = 8
    669   return ctx
    670 end
    671 
    672 -- Simple API: disassemble code (a string) at address and output via out.
    673 local function disass(code, addr, out)
    674   create(code, addr, out):disass()
    675 end
    676 
    677 -- Return register name for RID.
    678 local function regname(r)
    679   if r < 16 then return map_gpr[r] end
    680   return "d"..(r-16)
    681 end
    682 
    683 -- Public module functions.
    684 return {
    685   create = create,
    686   disass = disass,
    687   regname = regname
    688 }
    689