src/jit/dis_arm.lua - luajit-2.0-src

Functions defined

Source code

  1. ----------------------------------------------------------------------------
  2. -- LuaJIT ARM disassembler module.
  3. --
  4. -- Copyright (C) 2005-2015 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. local type = type
  13. local sub, byte, format = string.sub, string.byte, string.format
  14. local match, gmatch, gsub = string.match, string.gmatch, string.gsub
  15. local concat = table.concat
  16. local bit = require("bit")
  17. local band, bor, ror, tohex = bit.band, bit.bor, bit.ror, bit.tohex
  18. local lshift, rshift, arshift = bit.lshift, bit.rshift, bit.arshift

  19. ------------------------------------------------------------------------------
  20. -- Opcode maps
  21. ------------------------------------------------------------------------------

  22. local map_loadc = {
  23.   shift = 8, mask = 15,
  24.   [10] = {
  25.     shift = 20, mask = 1,
  26.     [0] = {
  27.       shift = 23, mask = 3,
  28.       [0] = "vmovFmDN", "vstmFNdr",
  29.       _ = {
  30.         shift = 21, mask = 1,
  31.         [0] = "vstrFdl",
  32.         { shift = 16, mask = 15, [13] = "vpushFdr", _ = "vstmdbFNdr", }
  33.       },
  34.     },
  35.     {
  36.       shift = 23, mask = 3,
  37.       [0] = "vmovFDNm",
  38.       { shift = 16, mask = 15, [13] = "vpopFdr", _ = "vldmFNdr", },
  39.       _ = {
  40.         shift = 21, mask = 1,
  41.         [0] = "vldrFdl", "vldmdbFNdr",
  42.       },
  43.     },
  44.   },
  45.   [11] = {
  46.     shift = 20, mask = 1,
  47.     [0] = {
  48.       shift = 23, mask = 3,
  49.       [0] = "vmovGmDN", "vstmGNdr",
  50.       _ = {
  51.         shift = 21, mask = 1,
  52.         [0] = "vstrGdl",
  53.         { shift = 16, mask = 15, [13] = "vpushGdr", _ = "vstmdbGNdr", }
  54.       },
  55.     },
  56.     {
  57.       shift = 23, mask = 3,
  58.       [0] = "vmovGDNm",
  59.       { shift = 16, mask = 15, [13] = "vpopGdr", _ = "vldmGNdr", },
  60.       _ = {
  61.         shift = 21, mask = 1,
  62.         [0] = "vldrGdl", "vldmdbGNdr",
  63.       },
  64.     },
  65.   },
  66.   _ = {
  67.     shift = 0, mask = 0 -- NYI ldc, mcrr, mrrc.
  68.   },
  69. }

  70. local map_vfps = {
  71.   shift = 6, mask = 0x2c001,
  72.   [0] = "vmlaF.dnm", "vmlsF.dnm",
  73.   [0x04000] = "vnmlsF.dnm", [0x04001] = "vnmlaF.dnm",
  74.   [0x08000] = "vmulF.dnm", [0x08001] = "vnmulF.dnm",
  75.   [0x0c000] = "vaddF.dnm", [0x0c001] = "vsubF.dnm",
  76.   [0x20000] = "vdivF.dnm",
  77.   [0x24000] = "vfnmsF.dnm", [0x24001] = "vfnmaF.dnm",
  78.   [0x28000] = "vfmaF.dnm", [0x28001] = "vfmsF.dnm",
  79.   [0x2c000] = "vmovF.dY",
  80.   [0x2c001] = {
  81.     shift = 7, mask = 0x1e01,
  82.     [0] = "vmovF.dm", "vabsF.dm",
  83.     [0x0200] = "vnegF.dm", [0x0201] = "vsqrtF.dm",
  84.     [0x0800] = "vcmpF.dm", [0x0801] = "vcmpeF.dm",
  85.     [0x0a00] = "vcmpzF.d", [0x0a01] = "vcmpzeF.d",
  86.     [0x0e01] = "vcvtG.dF.m",
  87.     [0x1000] = "vcvt.f32.u32Fdm", [0x1001] = "vcvt.f32.s32Fdm",
  88.     [0x1800] = "vcvtr.u32F.dm", [0x1801] = "vcvt.u32F.dm",
  89.     [0x1a00] = "vcvtr.s32F.dm", [0x1a01] = "vcvt.s32F.dm",
  90.   },
  91. }

  92. local map_vfpd = {
  93.   shift = 6, mask = 0x2c001,
  94.   [0] = "vmlaG.dnm", "vmlsG.dnm",
  95.   [0x04000] = "vnmlsG.dnm", [0x04001] = "vnmlaG.dnm",
  96.   [0x08000] = "vmulG.dnm", [0x08001] = "vnmulG.dnm",
  97.   [0x0c000] = "vaddG.dnm", [0x0c001] = "vsubG.dnm",
  98.   [0x20000] = "vdivG.dnm",
  99.   [0x24000] = "vfnmsG.dnm", [0x24001] = "vfnmaG.dnm",
  100.   [0x28000] = "vfmaG.dnm", [0x28001] = "vfmsG.dnm",
  101.   [0x2c000] = "vmovG.dY",
  102.   [0x2c001] = {
  103.     shift = 7, mask = 0x1e01,
  104.     [0] = "vmovG.dm", "vabsG.dm",
  105.     [0x0200] = "vnegG.dm", [0x0201] = "vsqrtG.dm",
  106.     [0x0800] = "vcmpG.dm", [0x0801] = "vcmpeG.dm",
  107.     [0x0a00] = "vcmpzG.d", [0x0a01] = "vcmpzeG.d",
  108.     [0x0e01] = "vcvtF.dG.m",
  109.     [0x1000] = "vcvt.f64.u32GdFm", [0x1001] = "vcvt.f64.s32GdFm",
  110.     [0x1800] = "vcvtr.u32FdG.m", [0x1801] = "vcvt.u32FdG.m",
  111.     [0x1a00] = "vcvtr.s32FdG.m", [0x1a01] = "vcvt.s32FdG.m",
  112.   },
  113. }

  114. local map_datac = {
  115.   shift = 24, mask = 1,
  116.   [0] = {
  117.     shift = 4, mask = 1,
  118.     [0] = {
  119.       shift = 8, mask = 15,
  120.       [10] = map_vfps,
  121.       [11] = map_vfpd,
  122.       -- NYI cdp, mcr, mrc.
  123.     },
  124.     {
  125.       shift = 8, mask = 15,
  126.       [10] = {
  127.         shift = 20, mask = 15,
  128.         [0] = "vmovFnD", "vmovFDn",
  129.         [14] = "vmsrD",
  130.         [15] = { shift = 12, mask = 15, [15] = "vmrs", _ = "vmrsD", },
  131.       },
  132.     },
  133.   },
  134.   "svcT",
  135. }

  136. local map_loadcu = {
  137.   shift = 0, mask = 0, -- NYI unconditional CP load/store.
  138. }

  139. local map_datacu = {
  140.   shift = 0, mask = 0, -- NYI unconditional CP data.
  141. }

  142. local map_simddata = {
  143.   shift = 0, mask = 0, -- NYI SIMD data.
  144. }

  145. local map_simdload = {
  146.   shift = 0, mask = 0, -- NYI SIMD load/store, preload.
  147. }

  148. local map_preload = {
  149.   shift = 0, mask = 0, -- NYI preload.
  150. }

  151. local map_media = {
  152.   shift = 20, mask = 31,
  153.   [0] = false,
  154.   { --01
  155.     shift = 5, mask = 7,
  156.     [0] = "sadd16DNM", "sasxDNM", "ssaxDNM", "ssub16DNM",
  157.     "sadd8DNM", false, false, "ssub8DNM",
  158.   },
  159.   { --02
  160.     shift = 5, mask = 7,
  161.     [0] = "qadd16DNM", "qasxDNM", "qsaxDNM", "qsub16DNM",
  162.     "qadd8DNM", false, false, "qsub8DNM",
  163.   },
  164.   { --03
  165.     shift = 5, mask = 7,
  166.     [0] = "shadd16DNM", "shasxDNM", "shsaxDNM", "shsub16DNM",
  167.     "shadd8DNM", false, false, "shsub8DNM",
  168.   },
  169.   false,
  170.   { --05
  171.     shift = 5, mask = 7,
  172.     [0] = "uadd16DNM", "uasxDNM", "usaxDNM", "usub16DNM",
  173.     "uadd8DNM", false, false, "usub8DNM",
  174.   },
  175.   { --06
  176.     shift = 5, mask = 7,
  177.     [0] = "uqadd16DNM", "uqasxDNM", "uqsaxDNM", "uqsub16DNM",
  178.     "uqadd8DNM", false, false, "uqsub8DNM",
  179.   },
  180.   { --07
  181.     shift = 5, mask = 7,
  182.     [0] = "uhadd16DNM", "uhasxDNM", "uhsaxDNM", "uhsub16DNM",
  183.     "uhadd8DNM", false, false, "uhsub8DNM",
  184.   },
  185.   { --08
  186.     shift = 5, mask = 7,
  187.     [0] = "pkhbtDNMU", false, "pkhtbDNMU",
  188.     { shift = 16, mask = 15, [15] = "sxtb16DMU", _ = "sxtab16DNMU", },
  189.     "pkhbtDNMU", "selDNM", "pkhtbDNMU",
  190.   },
  191.   false,
  192.   { --0a
  193.     shift = 5, mask = 7,
  194.     [0] = "ssatDxMu", "ssat16DxM", "ssatDxMu",
  195.     { shift = 16, mask = 15, [15] = "sxtbDMU", _ = "sxtabDNMU", },
  196.     "ssatDxMu", false, "ssatDxMu",
  197.   },
  198.   { --0b
  199.     shift = 5, mask = 7,
  200.     [0] = "ssatDxMu", "revDM", "ssatDxMu",
  201.     { shift = 16, mask = 15, [15] = "sxthDMU", _ = "sxtahDNMU", },
  202.     "ssatDxMu", "rev16DM", "ssatDxMu",
  203.   },
  204.   { --0c
  205.     shift = 5, mask = 7,
  206.     [3] = { shift = 16, mask = 15, [15] = "uxtb16DMU", _ = "uxtab16DNMU", },
  207.   },
  208.   false,
  209.   { --0e
  210.     shift = 5, mask = 7,
  211.     [0] = "usatDwMu", "usat16DwM", "usatDwMu",
  212.     { shift = 16, mask = 15, [15] = "uxtbDMU", _ = "uxtabDNMU", },
  213.     "usatDwMu", false, "usatDwMu",
  214.   },
  215.   { --0f
  216.     shift = 5, mask = 7,
  217.     [0] = "usatDwMu", "rbitDM", "usatDwMu",
  218.     { shift = 16, mask = 15, [15] = "uxthDMU", _ = "uxtahDNMU", },
  219.     "usatDwMu", "revshDM", "usatDwMu",
  220.   },
  221.   { --10
  222.     shift = 12, mask = 15,
  223.     [15] = {
  224.       shift = 5, mask = 7,
  225.       "smuadNMS", "smuadxNMS", "smusdNMS", "smusdxNMS",
  226.     },
  227.     _ = {
  228.       shift = 5, mask = 7,
  229.       [0] = "smladNMSD", "smladxNMSD", "smlsdNMSD", "smlsdxNMSD",
  230.     },
  231.   },
  232.   false, false, false,
  233.   { --14
  234.     shift = 5, mask = 7,
  235.     [0] = "smlaldDNMS", "smlaldxDNMS", "smlsldDNMS", "smlsldxDNMS",
  236.   },
  237.   { --15
  238.     shift = 5, mask = 7,
  239.     [0] = { shift = 12, mask = 15, [15] = "smmulNMS", _ = "smmlaNMSD", },
  240.     { shift = 12, mask = 15, [15] = "smmulrNMS", _ = "smmlarNMSD", },
  241.     false, false, false, false,
  242.     "smmlsNMSD", "smmlsrNMSD",
  243.   },
  244.   false, false,
  245.   { --18
  246.     shift = 5, mask = 7,
  247.     [0] = { shift = 12, mask = 15, [15] = "usad8NMS", _ = "usada8NMSD", },
  248.   },
  249.   false,
  250.   { --1a
  251.     shift = 5, mask = 3, [2] = "sbfxDMvw",
  252.   },
  253.   { --1b
  254.     shift = 5, mask = 3, [2] = "sbfxDMvw",
  255.   },
  256.   { --1c
  257.     shift = 5, mask = 3,
  258.     [0] = { shift = 0, mask = 15, [15] = "bfcDvX", _ = "bfiDMvX", },
  259.   },
  260.   { --1d
  261.     shift = 5, mask = 3,
  262.     [0] = { shift = 0, mask = 15, [15] = "bfcDvX", _ = "bfiDMvX", },
  263.   },
  264.   { --1e
  265.     shift = 5, mask = 3, [2] = "ubfxDMvw",
  266.   },
  267.   { --1f
  268.     shift = 5, mask = 3, [2] = "ubfxDMvw",
  269.   },
  270. }

  271. local map_load = {
  272.   shift = 21, mask = 9,
  273.   {
  274.     shift = 20, mask = 5,
  275.     [0] = "strtDL", "ldrtDL", [4] = "strbtDL", [5] = "ldrbtDL",
  276.   },
  277.   _ = {
  278.     shift = 20, mask = 5,
  279.     [0] = "strDL", "ldrDL", [4] = "strbDL", [5] = "ldrbDL",
  280.   }
  281. }

  282. local map_load1 = {
  283.   shift = 4, mask = 1,
  284.   [0] = map_load, map_media,
  285. }

  286. local map_loadm = {
  287.   shift = 20, mask = 1,
  288.   [0] = {
  289.     shift = 23, mask = 3,
  290.     [0] = "stmdaNR", "stmNR",
  291.     { shift = 16, mask = 63, [45] = "pushR", _ = "stmdbNR", }, "stmibNR",
  292.   },
  293.   {
  294.     shift = 23, mask = 3,
  295.     [0] = "ldmdaNR", { shift = 16, mask = 63, [61] = "popR", _ = "ldmNR", },
  296.     "ldmdbNR", "ldmibNR",
  297.   },
  298. }

  299. local map_data = {
  300.   shift = 21, mask = 15,
  301.   [0] = "andDNPs", "eorDNPs", "subDNPs", "rsbDNPs",
  302.   "addDNPs", "adcDNPs", "sbcDNPs", "rscDNPs",
  303.   "tstNP", "teqNP", "cmpNP", "cmnNP",
  304.   "orrDNPs", "movDPs", "bicDNPs", "mvnDPs",
  305. }

  306. local map_mul = {
  307.   shift = 21, mask = 7,
  308.   [0] = "mulNMSs", "mlaNMSDs", "umaalDNMS", "mlsDNMS",
  309.   "umullDNMSs", "umlalDNMSs", "smullDNMSs", "smlalDNMSs",
  310. }

  311. local map_sync = {
  312.   shift = 20, mask = 15, -- NYI: brackets around N. R(D+1) for ldrexd/strexd.
  313.   [0] = "swpDMN", false, false, false,
  314.   "swpbDMN", false, false, false,
  315.   "strexDMN", "ldrexDN", "strexdDN", "ldrexdDN",
  316.   "strexbDMN", "ldrexbDN", "strexhDN", "ldrexhDN",
  317. }

  318. local map_mulh = {
  319.   shift = 21, mask = 3,
  320.   [0] = { shift = 5, mask = 3,
  321.     [0] = "smlabbNMSD", "smlatbNMSD", "smlabtNMSD", "smlattNMSD", },
  322.   { shift = 5, mask = 3,
  323.     [0] = "smlawbNMSD", "smulwbNMS", "smlawtNMSD", "smulwtNMS", },
  324.   { shift = 5, mask = 3,
  325.     [0] = "smlalbbDNMS", "smlaltbDNMS", "smlalbtDNMS", "smlalttDNMS", },
  326.   { shift = 5, mask = 3,
  327.     [0] = "smulbbNMS", "smultbNMS", "smulbtNMS", "smulttNMS", },
  328. }

  329. local map_misc = {
  330.   shift = 4, mask = 7,
  331.   -- NYI: decode PSR bits of msr.
  332.   [0] = { shift = 21, mask = 1, [0] = "mrsD", "msrM", },
  333.   { shift = 21, mask = 3, "bxM", false, "clzDM", },
  334.   { shift = 21, mask = 3, "bxjM", },
  335.   { shift = 21, mask = 3, "blxM", },
  336.   false,
  337.   { shift = 21, mask = 3, [0] = "qaddDMN", "qsubDMN", "qdaddDMN", "qdsubDMN", },
  338.   false,
  339.   { shift = 21, mask = 3, "bkptK", },
  340. }

  341. local map_datar = {
  342.   shift = 4, mask = 9,
  343.   [9] = {
  344.     shift = 5, mask = 3,
  345.     [0] = { shift = 24, mask = 1, [0] = map_mul, map_sync, },
  346.     { shift = 20, mask = 1, [0] = "strhDL", "ldrhDL", },
  347.     { shift = 20, mask = 1, [0] = "ldrdDL", "ldrsbDL", },
  348.     { shift = 20, mask = 1, [0] = "strdDL", "ldrshDL", },
  349.   },
  350.   _ = {
  351.     shift = 20, mask = 25,
  352.     [16] = { shift = 7, mask = 1, [0] = map_misc, map_mulh, },
  353.     _ = {
  354.       shift = 0, mask = 0xffffffff,
  355.       [bor(0xe1a00000)] = "nop",
  356.       _ = map_data,
  357.     }
  358.   },
  359. }

  360. local map_datai = {
  361.   shift = 20, mask = 31, -- NYI: decode PSR bits of msr. Decode imm12.
  362.   [16] = "movwDW", [20] = "movtDW",
  363.   [18] = { shift = 0, mask = 0xf00ff, [0] = "nopv6", _ = "msrNW", },
  364.   [22] = "msrNW",
  365.   _ = map_data,
  366. }

  367. local map_branch = {
  368.   shift = 24, mask = 1,
  369.   [0] = "bB", "blB"
  370. }

  371. local map_condins = {
  372.   [0] = map_datar, map_datai, map_load, map_load1,
  373.   map_loadm, map_branch, map_loadc, map_datac
  374. }

  375. -- NYI: setend.
  376. local map_uncondins = {
  377.   [0] = false, map_simddata, map_simdload, map_preload,
  378.   false, "blxB", map_loadcu, map_datacu,
  379. }

  380. ------------------------------------------------------------------------------

  381. local map_gpr = {
  382.   [0] = "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
  383.   "r8", "r9", "r10", "r11", "r12", "sp", "lr", "pc",
  384. }

  385. local map_cond = {
  386.   [0] = "eq", "ne", "hs", "lo", "mi", "pl", "vs", "vc",
  387.   "hi", "ls", "ge", "lt", "gt", "le", "al",
  388. }

  389. local map_shift = { [0] = "lsl", "lsr", "asr", "ror", }

  390. ------------------------------------------------------------------------------

  391. -- Output a nicely formatted line with an opcode and operands.
  392. local function putop(ctx, text, operands)
  393.   local pos = ctx.pos
  394.   local extra = ""
  395.   if ctx.rel then
  396.     local sym = ctx.symtab[ctx.rel]
  397.     if sym then
  398.       extra = "\t->"..sym
  399.     elseif band(ctx.op, 0x0e000000) ~= 0x0a000000 then
  400.       extra = "\t; 0x"..tohex(ctx.rel)
  401.     end
  402.   end
  403.   if ctx.hexdump > 0 then
  404.     ctx.out(format("%08x  %s  %-5s %s%s\n",
  405.             ctx.addr+pos, tohex(ctx.op), text, concat(operands, ", "), extra))
  406.   else
  407.     ctx.out(format("%08x  %-5s %s%s\n",
  408.             ctx.addr+pos, text, concat(operands, ", "), extra))
  409.   end
  410.   ctx.pos = pos + 4
  411. end

  412. -- Fallback for unknown opcodes.
  413. local function unknown(ctx)
  414.   return putop(ctx, ".long", { "0x"..tohex(ctx.op) })
  415. end

  416. -- Format operand 2 of load/store opcodes.
  417. local function fmtload(ctx, op, pos)
  418.   local base = map_gpr[band(rshift(op, 16), 15)]
  419.   local x, ofs
  420.   local ext = (band(op, 0x04000000) == 0)
  421.   if not ext and band(op, 0x02000000) == 0 then
  422.     ofs = band(op, 4095)
  423.     if band(op, 0x00800000) == 0 then ofs = -ofs end
  424.     if base == "pc" then ctx.rel = ctx.addr + pos + 8 + ofs end
  425.     ofs = "#"..ofs
  426.   elseif ext and band(op, 0x00400000) ~= 0 then
  427.     ofs = band(op, 15) + band(rshift(op, 4), 0xf0)
  428.     if band(op, 0x00800000) == 0 then ofs = -ofs end
  429.     if base == "pc" then ctx.rel = ctx.addr + pos + 8 + ofs end
  430.     ofs = "#"..ofs
  431.   else
  432.     ofs = map_gpr[band(op, 15)]
  433.     if ext or band(op, 0xfe0) == 0 then
  434.     elseif band(op, 0xfe0) == 0x60 then
  435.       ofs = format("%s, rrx", ofs)
  436.     else
  437.       local sh = band(rshift(op, 7), 31)
  438.       if sh == 0 then sh = 32 end
  439.       ofs = format("%s, %s #%d", ofs, map_shift[band(rshift(op, 5), 3)], sh)
  440.     end
  441.     if band(op, 0x00800000) == 0 then ofs = "-"..ofs end
  442.   end
  443.   if ofs == "#0" then
  444.     x = format("[%s]", base)
  445.   elseif band(op, 0x01000000) == 0 then
  446.     x = format("[%s], %s", base, ofs)
  447.   else
  448.     x = format("[%s, %s]", base, ofs)
  449.   end
  450.   if band(op, 0x01200000) == 0x01200000 then x = x.."!" end
  451.   return x
  452. end

  453. -- Format operand 2 of vector load/store opcodes.
  454. local function fmtvload(ctx, op, pos)
  455.   local base = map_gpr[band(rshift(op, 16), 15)]
  456.   local ofs = band(op, 255)*4
  457.   if band(op, 0x00800000) == 0 then ofs = -ofs end
  458.   if base == "pc" then ctx.rel = ctx.addr + pos + 8 + ofs end
  459.   if ofs == 0 then
  460.     return format("[%s]", base)
  461.   else
  462.     return format("[%s, #%d]", base, ofs)
  463.   end
  464. end

  465. local function fmtvr(op, vr, sh0, sh1)
  466.   if vr == "s" then
  467.     return format("s%d", 2*band(rshift(op, sh0), 15)+band(rshift(op, sh1), 1))
  468.   else
  469.     return format("d%d", band(rshift(op, sh0), 15)+band(rshift(op, sh1-4), 16))
  470.   end
  471. end

  472. -- Disassemble a single instruction.
  473. local function disass_ins(ctx)
  474.   local pos = ctx.pos
  475.   local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4)
  476.   local op = bor(lshift(b3, 24), lshift(b2, 16), lshift(b1, 8), b0)
  477.   local operands = {}
  478.   local suffix = ""
  479.   local last, name, pat
  480.   local vr
  481.   ctx.op = op
  482.   ctx.rel = nil

  483.   local cond = rshift(op, 28)
  484.   local opat
  485.   if cond == 15 then
  486.     opat = map_uncondins[band(rshift(op, 25), 7)]
  487.   else
  488.     if cond ~= 14 then suffix = map_cond[cond] end
  489.     opat = map_condins[band(rshift(op, 25), 7)]
  490.   end
  491.   while type(opat) ~= "string" do
  492.     if not opat then return unknown(ctx) end
  493.     opat = opat[band(rshift(op, opat.shift), opat.mask)] or opat._
  494.   end
  495.   name, pat = match(opat, "^([a-z0-9]*)(.*)")
  496.   if sub(pat, 1, 1) == "." then
  497.     local s2, p2 = match(pat, "^([a-z0-9.]*)(.*)")
  498.     suffix = suffix..s2
  499.     pat = p2
  500.   end

  501.   for p in gmatch(pat, ".") do
  502.     local x = nil
  503.     if p == "D" then
  504.       x = map_gpr[band(rshift(op, 12), 15)]
  505.     elseif p == "N" then
  506.       x = map_gpr[band(rshift(op, 16), 15)]
  507.     elseif p == "S" then
  508.       x = map_gpr[band(rshift(op, 8), 15)]
  509.     elseif p == "M" then
  510.       x = map_gpr[band(op, 15)]
  511.     elseif p == "d" then
  512.       x = fmtvr(op, vr, 12, 22)
  513.     elseif p == "n" then
  514.       x = fmtvr(op, vr, 16, 7)
  515.     elseif p == "m" then
  516.       x = fmtvr(op, vr, 0, 5)
  517.     elseif p == "P" then
  518.       if band(op, 0x02000000) ~= 0 then
  519.         x = ror(band(op, 255), 2*band(rshift(op, 8), 15))
  520.       else
  521.         x = map_gpr[band(op, 15)]
  522.         if band(op, 0xff0) ~= 0 then
  523.           operands[#operands+1] = x
  524.           local s = map_shift[band(rshift(op, 5), 3)]
  525.           local r = nil
  526.           if band(op, 0xf90) == 0 then
  527.             if s == "ror" then s = "rrx" else r = "#32" end
  528.           elseif band(op, 0x10) == 0 then
  529.             r = "#"..band(rshift(op, 7), 31)
  530.           else
  531.             r = map_gpr[band(rshift(op, 8), 15)]
  532.           end
  533.           if name == "mov" then name = s; x = r
  534.           elseif r then x = format("%s %s", s, r)
  535.           else x = s end
  536.         end
  537.       end
  538.     elseif p == "L" then
  539.       x = fmtload(ctx, op, pos)
  540.     elseif p == "l" then
  541.       x = fmtvload(ctx, op, pos)
  542.     elseif p == "B" then
  543.       local addr = ctx.addr + pos + 8 + arshift(lshift(op, 8), 6)
  544.       if cond == 15 then addr = addr + band(rshift(op, 23), 2) end
  545.       ctx.rel = addr
  546.       x = "0x"..tohex(addr)
  547.     elseif p == "F" then
  548.       vr = "s"
  549.     elseif p == "G" then
  550.       vr = "d"
  551.     elseif p == "." then
  552.       suffix = suffix..(vr == "s" and ".f32" or ".f64")
  553.     elseif p == "R" then
  554.       if band(op, 0x00200000) ~= 0 and #operands == 1 then
  555.         operands[1] = operands[1].."!"
  556.       end
  557.       local t = {}
  558.       for i=0,15 do
  559.         if band(rshift(op, i), 1) == 1 then t[#t+1] = map_gpr[i] end
  560.       end
  561.       x = "{"..concat(t, ", ").."}"
  562.     elseif p == "r" then
  563.       if band(op, 0x00200000) ~= 0 and #operands == 2 then
  564.         operands[1] = operands[1].."!"
  565.       end
  566.       local s = tonumber(sub(last, 2))
  567.       local n = band(op, 255)
  568.       if vr == "d" then n = rshift(n, 1) end
  569.       operands[#operands] = format("{%s-%s%d}", last, vr, s+n-1)
  570.     elseif p == "W" then
  571.       x = band(op, 0x0fff) + band(rshift(op, 4), 0xf000)
  572.     elseif p == "T" then
  573.       x = "#0x"..tohex(band(op, 0x00ffffff), 6)
  574.     elseif p == "U" then
  575.       x = band(rshift(op, 7), 31)
  576.       if x == 0 then x = nil end
  577.     elseif p == "u" then
  578.       x = band(rshift(op, 7), 31)
  579.       if band(op, 0x40) == 0 then
  580.         if x == 0 then x = nil else x = "lsl #"..x end
  581.       else
  582.         if x == 0 then x = "asr #32" else x = "asr #"..x end
  583.       end
  584.     elseif p == "v" then
  585.       x = band(rshift(op, 7), 31)
  586.     elseif p == "w" then
  587.       x = band(rshift(op, 16), 31)
  588.     elseif p == "x" then
  589.       x = band(rshift(op, 16), 31) + 1
  590.     elseif p == "X" then
  591.       x = band(rshift(op, 16), 31) - last + 1
  592.     elseif p == "Y" then
  593.       x = band(rshift(op, 12), 0xf0) + band(op, 0x0f)
  594.     elseif p == "K" then
  595.       x = "#0x"..tohex(band(rshift(op, 4), 0x0000fff0) + band(op, 15), 4)
  596.     elseif p == "s" then
  597.       if band(op, 0x00100000) ~= 0 then suffix = "s"..suffix end
  598.     else
  599.       assert(false)
  600.     end
  601.     if x then
  602.       last = x
  603.       if type(x) == "number" then x = "#"..x end
  604.       operands[#operands+1] = x
  605.     end
  606.   end

  607.   return putop(ctx, name..suffix, operands)
  608. end

  609. ------------------------------------------------------------------------------

  610. -- Disassemble a block of code.
  611. local function disass_block(ctx, ofs, len)
  612.   if not ofs then ofs = 0 end
  613.   local stop = len and ofs+len or #ctx.code
  614.   ctx.pos = ofs
  615.   ctx.rel = nil
  616.   while ctx.pos < stop do disass_ins(ctx) end
  617. end

  618. -- Extended API: create a disassembler context. Then call ctx:disass(ofs, len).
  619. local function create(code, addr, out)
  620.   local ctx = {}
  621.   ctx.code = code
  622.   ctx.addr = addr or 0
  623.   ctx.out = out or io.write
  624.   ctx.symtab = {}
  625.   ctx.disass = disass_block
  626.   ctx.hexdump = 8
  627.   return ctx
  628. end

  629. -- Simple API: disassemble code (a string) at address and output via out.
  630. local function disass(code, addr, out)
  631.   create(code, addr, out):disass()
  632. end

  633. -- Return register name for RID.
  634. local function regname(r)
  635.   if r < 16 then return map_gpr[r] end
  636.   return "d"..(r-16)
  637. end

  638. -- Public module functions.
  639. return {
  640.   create = create,
  641.   disass = disass,
  642.   regname = regname
  643. }