diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/dis_arm.lua | 538 |
1 files changed, 538 insertions, 0 deletions
diff --git a/lib/dis_arm.lua b/lib/dis_arm.lua new file mode 100644 index 00000000..d007f86c --- /dev/null +++ b/lib/dis_arm.lua | |||
| @@ -0,0 +1,538 @@ | |||
| 1 | ---------------------------------------------------------------------------- | ||
| 2 | -- LuaJIT ARM disassembler module. | ||
| 3 | -- | ||
| 4 | -- Copyright (C) 2005-2010 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 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 = 9, mask = 7, | ||
| 27 | [5] = { | ||
| 28 | shift = 0, mask = 0 -- NYI VFP load/store. | ||
| 29 | }, | ||
| 30 | _ = { | ||
| 31 | shift = 0, mask = 0 -- NYI ldc, mcrr, mrrc. | ||
| 32 | }, | ||
| 33 | } | ||
| 34 | |||
| 35 | local map_datac = { | ||
| 36 | shift = 24, mask = 1, | ||
| 37 | [0] = { | ||
| 38 | shift = 9, mask = 7, | ||
| 39 | [5] = { | ||
| 40 | shift = 0, mask = 0 -- NYI VFP data. | ||
| 41 | }, | ||
| 42 | _ = { | ||
| 43 | shift = 0, mask = 0 -- NYI cdp, mcr, mrc. | ||
| 44 | }, | ||
| 45 | }, | ||
| 46 | "svcT", | ||
| 47 | } | ||
| 48 | |||
| 49 | local map_loadcu = { | ||
| 50 | shift = 0, mask = 0, -- NYI unconditional CP load/store. | ||
| 51 | } | ||
| 52 | |||
| 53 | local map_datacu = { | ||
| 54 | shift = 0, mask = 0, -- NYI unconditional CP data. | ||
| 55 | } | ||
| 56 | |||
| 57 | local map_simddata = { | ||
| 58 | shift = 0, mask = 0, -- NYI SIMD data. | ||
| 59 | } | ||
| 60 | |||
| 61 | local map_simdload = { | ||
| 62 | shift = 0, mask = 0, -- NYI SIMD load/store, preload. | ||
| 63 | } | ||
| 64 | |||
| 65 | local map_preload = { | ||
| 66 | shift = 0, mask = 0, -- NYI preload. | ||
| 67 | } | ||
| 68 | |||
| 69 | local map_media = { | ||
| 70 | shift = 20, mask = 31, | ||
| 71 | [0] = false, | ||
| 72 | { --01 | ||
| 73 | shift = 5, mask = 7, | ||
| 74 | [0] = "sadd16DNM", "sasxDNM", "ssaxDNM", "ssub16DNM", | ||
| 75 | "sadd8DNM", false, false, "ssub8DNM", | ||
| 76 | }, | ||
| 77 | { --02 | ||
| 78 | shift = 5, mask = 7, | ||
| 79 | [0] = "qadd16DNM", "qasxDNM", "qsaxDNM", "qsub16DNM", | ||
| 80 | "qadd8DNM", false, false, "qsub8DNM", | ||
| 81 | }, | ||
| 82 | { --03 | ||
| 83 | shift = 5, mask = 7, | ||
| 84 | [0] = "shadd16DNM", "shasxDNM", "shsaxDNM", "shsub16DNM", | ||
| 85 | "shadd8DNM", false, false, "shsub8DNM", | ||
| 86 | }, | ||
| 87 | false, | ||
| 88 | { --05 | ||
| 89 | shift = 5, mask = 7, | ||
| 90 | [0] = "uadd16DNM", "uasxDNM", "usaxDNM", "usub16DNM", | ||
| 91 | "uadd8DNM", false, false, "usub8DNM", | ||
| 92 | }, | ||
| 93 | { --06 | ||
| 94 | shift = 5, mask = 7, | ||
| 95 | [0] = "uqadd16DNM", "uqasxDNM", "uqsaxDNM", "uqsub16DNM", | ||
| 96 | "uqadd8DNM", false, false, "uqsub8DNM", | ||
| 97 | }, | ||
| 98 | { --07 | ||
| 99 | shift = 5, mask = 7, | ||
| 100 | [0] = "uhadd16DNM", "uhasxDNM", "uhsaxDNM", "uhsub16DNM", | ||
| 101 | "uhadd8DNM", false, false, "uhsub8DNM", | ||
| 102 | }, | ||
| 103 | { --08 | ||
| 104 | shift = 5, mask = 7, | ||
| 105 | [0] = "pkhbtDNMU", false, "pkhtbDNMU", | ||
| 106 | { shift = 16, mask = 15, [15] = "sxtb16DMU", _ = "sxtab16DNMU", }, | ||
| 107 | "pkhbtDNMU", "selDNM", "pkhtbDNMU", | ||
| 108 | }, | ||
| 109 | false, | ||
| 110 | { --0a | ||
| 111 | shift = 5, mask = 7, | ||
| 112 | [0] = "ssatDxMu", "ssat16DxM", "ssatDxMu", | ||
| 113 | { shift = 16, mask = 15, [15] = "sxtbDMU", _ = "sxtabDNMU", }, | ||
| 114 | "ssatDxMu", false, "ssatDxMu", | ||
| 115 | }, | ||
| 116 | { --0b | ||
| 117 | shift = 5, mask = 7, | ||
| 118 | [0] = "ssatDxMu", "revDM", "ssatDxMu", | ||
| 119 | { shift = 16, mask = 15, [15] = "sxthDMU", _ = "sxtahDNMU", }, | ||
| 120 | "ssatDxMu", "rev16DM", "ssatDxMu", | ||
| 121 | }, | ||
| 122 | { --0c | ||
| 123 | shift = 5, mask = 7, | ||
| 124 | [3] = { shift = 16, mask = 15, [15] = "uxtb16DMU", _ = "uxtab16DNMU", }, | ||
| 125 | }, | ||
| 126 | false, | ||
| 127 | { --0e | ||
| 128 | shift = 5, mask = 7, | ||
| 129 | [0] = "usatDwMu", "usat16DwM", "usatDwMu", | ||
| 130 | { shift = 16, mask = 15, [15] = "uxtbDMU", _ = "uxtabDNMU", }, | ||
| 131 | "usatDwMu", false, "usatDwMu", | ||
| 132 | }, | ||
| 133 | { --0f | ||
| 134 | shift = 5, mask = 7, | ||
| 135 | [0] = "usatDwMu", "rbitDM", "usatDwMu", | ||
| 136 | { shift = 16, mask = 15, [15] = "uxthDMU", _ = "uxtahDNMU", }, | ||
| 137 | "usatDwMu", "revshDM", "usatDwMu", | ||
| 138 | }, | ||
| 139 | { --10 | ||
| 140 | shift = 12, mask = 15, | ||
| 141 | [15] = { | ||
| 142 | shift = 5, mask = 7, | ||
| 143 | "smuadNMS", "smuadxNMS", "smusdNMS", "smusdxNMS", | ||
| 144 | }, | ||
| 145 | _ = { | ||
| 146 | shift = 5, mask = 7, | ||
| 147 | [0] = "smladNMSD", "smladxNMSD", "smlsdNMSD", "smlsdxNMSD", | ||
| 148 | }, | ||
| 149 | }, | ||
| 150 | false, false, false, | ||
| 151 | { --14 | ||
| 152 | shift = 5, mask = 7, | ||
| 153 | [0] = "smlaldDNMS", "smlaldxDNMS", "smlsldDNMS", "smlsldxDNMS", | ||
| 154 | }, | ||
| 155 | { --15 | ||
| 156 | shift = 5, mask = 7, | ||
| 157 | [0] = { shift = 12, mask = 15, [15] = "smmulNMS", _ = "smmlaNMSD", }, | ||
| 158 | { shift = 12, mask = 15, [15] = "smmulrNMS", _ = "smmlarNMSD", }, | ||
| 159 | false, false, false, false, | ||
| 160 | "smmlsNMSD", "smmlsrNMSD", | ||
| 161 | }, | ||
| 162 | false, false, | ||
| 163 | { --18 | ||
| 164 | shift = 5, mask = 7, | ||
| 165 | [0] = { shift = 12, mask = 15, [15] = "usad8NMS", _ = "usada8NMSD", }, | ||
| 166 | }, | ||
| 167 | false, | ||
| 168 | { --1a | ||
| 169 | shift = 5, mask = 3, [2] = "sbfxDMvw", | ||
| 170 | }, | ||
| 171 | { --1b | ||
| 172 | shift = 5, mask = 3, [2] = "sbfxDMvw", | ||
| 173 | }, | ||
| 174 | { --1c | ||
| 175 | shift = 5, mask = 3, | ||
| 176 | [0] = { shift = 0, mask = 15, [15] = "bfcDvX", _ = "bfiDMvX", }, | ||
| 177 | }, | ||
| 178 | { --1d | ||
| 179 | shift = 5, mask = 3, | ||
| 180 | [0] = { shift = 0, mask = 15, [15] = "bfcDvX", _ = "bfiDMvX", }, | ||
| 181 | }, | ||
| 182 | { --1e | ||
| 183 | shift = 5, mask = 3, [2] = "ubfxDMvw", | ||
| 184 | }, | ||
| 185 | { --1f | ||
| 186 | shift = 5, mask = 3, [2] = "ubfxDMvw", | ||
| 187 | }, | ||
| 188 | } | ||
| 189 | |||
| 190 | local map_load = { | ||
| 191 | shift = 21, mask = 9, | ||
| 192 | { | ||
| 193 | shift = 20, mask = 5, | ||
| 194 | [0] = "strtDL", "ldrtDL", [4] = "strbtDL", [5] = "ldrbtDL", | ||
| 195 | }, | ||
| 196 | _ = { | ||
| 197 | shift = 20, mask = 5, | ||
| 198 | [0] = "strDL", "ldrDL", [4] = "strbDL", [5] = "ldrbDL", | ||
| 199 | } | ||
| 200 | } | ||
| 201 | |||
| 202 | local map_load1 = { | ||
| 203 | shift = 4, mask = 1, | ||
| 204 | [0] = map_load, map_media, | ||
| 205 | } | ||
| 206 | |||
| 207 | local map_loadm = { | ||
| 208 | shift = 20, mask = 1, | ||
| 209 | [0] = { | ||
| 210 | shift = 23, mask = 3, | ||
| 211 | [0] = "stmdaNR", "stmNR", | ||
| 212 | { shift = 16, mask = 63, [45] = "pushR", _ = "stmdbNR", }, "stmibNR", | ||
| 213 | }, | ||
| 214 | { | ||
| 215 | shift = 23, mask = 3, | ||
| 216 | [0] = "ldmdaNR", { shift = 16, mask = 63, [61] = "popR", _ = "ldmNR", }, | ||
| 217 | "ldmdbNR", "ldmibNR", | ||
| 218 | }, | ||
| 219 | } | ||
| 220 | |||
| 221 | local map_data = { | ||
| 222 | shift = 21, mask = 15, | ||
| 223 | [0] = "andDNPs", "eorDNPs", "subDNPs", "rsbDNPs", | ||
| 224 | "addDNPs", "adcDNPs", "sbcDNPs", "rscDNPs", | ||
| 225 | "tstNP", "teqNP", "cmpNP", "cmnNP", | ||
| 226 | "orrDNPs", "movDPs", "bicDNPs", "mvnDPs", | ||
| 227 | } | ||
| 228 | |||
| 229 | local map_mul = { | ||
| 230 | shift = 21, mask = 7, | ||
| 231 | [0] = "mulNMSs", "mlaNMSDs", "umaalDNMS", "mlsDNMS", | ||
| 232 | "umullDNMSs", "umlalDNMSs", "smullDNMSs", "smlalDNMSs", | ||
| 233 | } | ||
| 234 | |||
| 235 | local map_sync = { | ||
| 236 | shift = 20, mask = 15, -- NYI: brackets around N. R(D+1) for ldrexd/strexd. | ||
| 237 | [0] = "swpDMN", false, false, false, | ||
| 238 | "swpbDMN", false, false, false, | ||
| 239 | "strexDMN", "ldrexDN", "strexdDN", "ldrexdDN", | ||
| 240 | "strexbDMN", "ldrexbDN", "strexhDN", "ldrexhDN", | ||
| 241 | } | ||
| 242 | |||
| 243 | local map_mulh = { | ||
| 244 | shift = 21, mask = 3, | ||
| 245 | [0] = { shift = 5, mask = 3, | ||
| 246 | [0] = "smlabbNMSD", "smlatbNMSD", "smlabtNMSD", "smlattNMSD", }, | ||
| 247 | { shift = 5, mask = 3, | ||
| 248 | [0] = "smlawbNMSD", "smulwbNMS", "smlawtNMSD", "smulwtNMS", }, | ||
| 249 | { shift = 5, mask = 3, | ||
| 250 | [0] = "smlalbbDNMS", "smlaltbDNMS", "smlalbtDNMS", "smlalttDNMS", }, | ||
| 251 | { shift = 5, mask = 3, | ||
| 252 | [0] = "smulbbNMS", "smultbNMS", "smulbtNMS", "smulttNMS", }, | ||
| 253 | } | ||
| 254 | |||
| 255 | local map_misc = { | ||
| 256 | shift = 4, mask = 7, | ||
| 257 | -- NYI: decode PSR bits of msr. | ||
| 258 | [0] = { shift = 21, mask = 1, [0] = "mrsD", "msrM", }, | ||
| 259 | { shift = 21, mask = 3, "bxM", false, "clzDM", }, | ||
| 260 | { shift = 21, mask = 3, "bxjM", }, | ||
| 261 | { shift = 21, mask = 3, "blxM", }, | ||
| 262 | false, | ||
| 263 | { shift = 21, mask = 3, [0] = "qaddDMN", "qsubDMN", "qdaddDMN", "qdsubDMN", }, | ||
| 264 | false, | ||
| 265 | { shift = 21, mask = 3, "bkptK", }, | ||
| 266 | } | ||
| 267 | |||
| 268 | local map_datar = { | ||
| 269 | shift = 4, mask = 9, | ||
| 270 | [9] = { | ||
| 271 | shift = 5, mask = 3, | ||
| 272 | [0] = { shift = 24, mask = 1, [0] = map_mul, map_sync, }, | ||
| 273 | { shift = 20, mask = 1, [0] = "strhDL", "ldrhDL", }, | ||
| 274 | { shift = 20, mask = 1, [0] = "ldrdDL", "ldrsbDL", }, | ||
| 275 | { shift = 20, mask = 1, [0] = "strdDL", "ldrshDL", }, | ||
| 276 | }, | ||
| 277 | _ = { | ||
| 278 | shift = 20, mask = 25, | ||
| 279 | [16] = { shift = 7, mask = 1, [0] = map_misc, map_mulh, }, | ||
| 280 | _ = { | ||
| 281 | shift = 0, mask = 0xffffffff, | ||
| 282 | [bor(0xe1a00000)] = "nop", | ||
| 283 | _ = map_data, | ||
| 284 | } | ||
| 285 | }, | ||
| 286 | } | ||
| 287 | |||
| 288 | local map_datai = { | ||
| 289 | shift = 20, mask = 31, -- NYI: decode PSR bits of msr. Decode imm12. | ||
| 290 | [16] = "movwDW", [20] = "movtDW", | ||
| 291 | [18] = { shift = 0, mask = 0xf00ff, [0] = "nopv6", _ = "msrNW", }, | ||
| 292 | [22] = "msrNW", | ||
| 293 | _ = map_data, | ||
| 294 | } | ||
| 295 | |||
| 296 | local map_branch = { | ||
| 297 | shift = 24, mask = 1, | ||
| 298 | [0] = "bB", "blB" | ||
| 299 | } | ||
| 300 | |||
| 301 | local map_condins = { | ||
| 302 | [0] = map_datar, map_datai, map_load, map_load1, | ||
| 303 | map_loadm, map_branch, map_loadc, map_datac | ||
| 304 | } | ||
| 305 | |||
| 306 | -- NYI: setend. | ||
| 307 | local map_uncondins = { | ||
| 308 | [0] = false, map_simddata, map_simdload, map_preload, | ||
| 309 | false, "blxB", map_loadcu, map_datacu, | ||
| 310 | } | ||
| 311 | |||
| 312 | ------------------------------------------------------------------------------ | ||
| 313 | |||
| 314 | local map_gpr = { | ||
| 315 | [0] = "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", | ||
| 316 | "r8", "r9", "r10", "r11", "r12", "sp", "lr", "pc", | ||
| 317 | } | ||
| 318 | |||
| 319 | local map_cond = { | ||
| 320 | [0] = "eq", "ne", "hs", "lo", "mi", "pl", "vs", "vc", | ||
| 321 | "hi", "ls", "ge", "lt", "gt", "le", "al", | ||
| 322 | } | ||
| 323 | |||
| 324 | local map_shift = { [0] = "lsl", "lsr", "asr", "ror", } | ||
| 325 | |||
| 326 | ------------------------------------------------------------------------------ | ||
| 327 | |||
| 328 | -- Output a nicely formatted line with an opcode and operands. | ||
| 329 | local function putop(ctx, text, operands) | ||
| 330 | local pos = ctx.pos | ||
| 331 | local extra = "" | ||
| 332 | if ctx.rel then | ||
| 333 | local sym = ctx.symtab[ctx.rel] | ||
| 334 | if sym then | ||
| 335 | extra = "\t->"..sym | ||
| 336 | elseif band(ctx.op, 0x0e000000) ~= 0x0a000000 then | ||
| 337 | extra = "\t; 0x"..tohex(ctx.rel) | ||
| 338 | end | ||
| 339 | end | ||
| 340 | if ctx.hexdump > 0 then | ||
| 341 | ctx.out(format("%08x %s %-5s %s%s\n", | ||
| 342 | ctx.addr+pos, tohex(ctx.op), text, concat(operands, ", "), extra)) | ||
| 343 | else | ||
| 344 | ctx.out(format("%08x %-5s %s%s\n", | ||
| 345 | ctx.addr+pos, text, concat(operands, ", "), extra)) | ||
| 346 | end | ||
| 347 | ctx.pos = pos + 4 | ||
| 348 | end | ||
| 349 | |||
| 350 | -- Fallback for unknown opcodes. | ||
| 351 | local function unknown(ctx) | ||
| 352 | return putop(ctx, ".long", { "0x"..tohex(ctx.op) }) | ||
| 353 | end | ||
| 354 | |||
| 355 | -- Format operand 2 of load/store opcodes. | ||
| 356 | local function fmtload(ctx, op, pos) | ||
| 357 | local base = map_gpr[band(rshift(op, 16), 15)] | ||
| 358 | local x, ofs | ||
| 359 | local ext = (band(op, 0x04000000) == 0) | ||
| 360 | if not ext and band(op, 0x02000000) == 0 then | ||
| 361 | ofs = band(op, 4095) | ||
| 362 | if band(op, 0x00800000) == 0 then ofs = -ofs end | ||
| 363 | if base == "pc" then ctx.rel = ctx.addr + pos + 8 + ofs end | ||
| 364 | ofs = "#"..ofs | ||
| 365 | elseif ext and band(op, 0x00400000) ~= 0 then | ||
| 366 | ofs = band(op, 15) + band(rshift(op, 4), 0xf0) | ||
| 367 | if band(op, 0x00800000) == 0 then ofs = -ofs end | ||
| 368 | if base == "pc" then ctx.rel = ctx.addr + pos + 8 + ofs end | ||
| 369 | ofs = "#"..ofs | ||
| 370 | else | ||
| 371 | ofs = map_gpr[band(op, 15)] | ||
| 372 | if ext or band(op, 0xfe0) == 0 then | ||
| 373 | elseif band(op, 0xfe0) == 0x60 then | ||
| 374 | ofs = format("%s, rrx", ofs) | ||
| 375 | else | ||
| 376 | local sh = band(rshift(op, 7), 31) | ||
| 377 | if sh == 0 then sh = 32 end | ||
| 378 | ofs = format("%s, %s #%d", ofs, map_shift[band(rshift(op, 5), 3)], sh) | ||
| 379 | end | ||
| 380 | if band(op, 0x00800000) == 0 then ofs = "-"..ofs end | ||
| 381 | end | ||
| 382 | if ofs == "#0" then | ||
| 383 | x = format("[%s]", base) | ||
| 384 | elseif band(op, 0x01000000) == 0 then | ||
| 385 | x = format("[%s], %s", base, ofs) | ||
| 386 | else | ||
| 387 | x = format("[%s, %s]", base, ofs) | ||
| 388 | end | ||
| 389 | if band(op, 0x01200000) == 0x01200000 then x = x.."!" end | ||
| 390 | return x | ||
| 391 | end | ||
| 392 | |||
| 393 | -- Disassemble a single instruction. | ||
| 394 | local function disass_ins(ctx) | ||
| 395 | local pos = ctx.pos | ||
| 396 | local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4) | ||
| 397 | local op = bor(lshift(b3, 24), lshift(b2, 16), lshift(b1, 8), b0) | ||
| 398 | local operands = {} | ||
| 399 | local suffix = "" | ||
| 400 | local last, name, pat | ||
| 401 | ctx.op = op | ||
| 402 | ctx.rel = nil | ||
| 403 | |||
| 404 | local cond = rshift(op, 28) | ||
| 405 | local opat | ||
| 406 | if cond == 15 then | ||
| 407 | opat = map_uncondins[band(rshift(op, 25), 7)] | ||
| 408 | else | ||
| 409 | if cond ~= 14 then suffix = map_cond[cond] end | ||
| 410 | opat = map_condins[band(rshift(op, 25), 7)] | ||
| 411 | end | ||
| 412 | while type(opat) ~= "string" do | ||
| 413 | if not opat then return unknown(ctx) end | ||
| 414 | opat = opat[band(rshift(op, opat.shift), opat.mask)] or opat._ | ||
| 415 | end | ||
| 416 | name, pat = match(opat, "^([a-z0-9]*)(.*)") | ||
| 417 | |||
| 418 | for p in gmatch(pat, ".") do | ||
| 419 | local x = nil | ||
| 420 | if p == "D" then | ||
| 421 | x = map_gpr[band(rshift(op, 12), 15)] | ||
| 422 | elseif p == "N" then | ||
| 423 | x = map_gpr[band(rshift(op, 16), 15)] | ||
| 424 | elseif p == "S" then | ||
| 425 | x = map_gpr[band(rshift(op, 8), 15)] | ||
| 426 | elseif p == "M" then | ||
| 427 | x = map_gpr[band(op, 15)] | ||
| 428 | elseif p == "P" then | ||
| 429 | if band(op, 0x02000000) ~= 0 then | ||
| 430 | x = ror(band(op, 255), 2*band(rshift(op, 8), 15)) | ||
| 431 | else | ||
| 432 | x = map_gpr[band(op, 15)] | ||
| 433 | if band(op, 0xff0) ~= 0 then | ||
| 434 | operands[#operands+1] = x | ||
| 435 | local s = map_shift[band(rshift(op, 5), 3)] | ||
| 436 | local r = nil | ||
| 437 | if band(op, 0xf90) == 0 then | ||
| 438 | if s == "ror" then s = "rrx" else r = "#32" end | ||
| 439 | elseif band(op, 0x10) == 0 then | ||
| 440 | r = "#"..band(rshift(op, 7), 31) | ||
| 441 | else | ||
| 442 | r = map_gpr[band(rshift(op, 8), 15)] | ||
| 443 | end | ||
| 444 | if name == "mov" then name = s; x = r | ||
| 445 | elseif r then x = format("%s %s", s, r) | ||
| 446 | else x = s end | ||
| 447 | end | ||
| 448 | end | ||
| 449 | elseif p == "L" then | ||
| 450 | x = fmtload(ctx, op, pos, false) | ||
| 451 | elseif p == "B" then | ||
| 452 | local addr = ctx.addr + pos + 8 + arshift(lshift(op, 8), 6) | ||
| 453 | if cond == 15 then addr = addr + band(rshift(op, 23), 2) end | ||
| 454 | ctx.rel = addr | ||
| 455 | x = "0x"..tohex(addr) | ||
| 456 | elseif p == "R" then | ||
| 457 | if band(op, 0x00200000) ~= 0 and #operands == 1 then | ||
| 458 | operands[1] = operands[1].."!" | ||
| 459 | end | ||
| 460 | local t = {} | ||
| 461 | for i=0,15 do | ||
| 462 | if band(rshift(op, i), 1) == 1 then t[#t+1] = map_gpr[i] end | ||
| 463 | end | ||
| 464 | x = "{"..concat(t, ", ").."}" | ||
| 465 | elseif p == "W" then | ||
| 466 | x = band(op, 0x0fff) + band(rshift(op, 4), 0xf000) | ||
| 467 | elseif p == "T" then | ||
| 468 | x = "#0x"..tohex(band(op, 0x00ffffff), 6) | ||
| 469 | elseif p == "U" then | ||
| 470 | x = band(rshift(op, 7), 31) | ||
| 471 | if x == 0 then x = nil end | ||
| 472 | elseif p == "u" then | ||
| 473 | x = band(rshift(op, 7), 31) | ||
| 474 | if band(op, 0x40) == 0 then | ||
| 475 | if x == 0 then x = nil else x = "lsl #"..x end | ||
| 476 | else | ||
| 477 | if x == 0 then x = "asr #32" else x = "asr #"..x end | ||
| 478 | end | ||
| 479 | elseif p == "v" then | ||
| 480 | x = band(rshift(op, 7), 31) | ||
| 481 | elseif p == "w" then | ||
| 482 | x = band(rshift(op, 16), 31) | ||
| 483 | elseif p == "x" then | ||
| 484 | x = band(rshift(op, 16), 31) + 1 | ||
| 485 | elseif p == "X" then | ||
| 486 | x = band(rshift(op, 16), 31) - last + 1 | ||
| 487 | elseif p == "K" then | ||
| 488 | x = "#0x"..tohex(band(rshift(op, 4), 0x0000fff0) + band(op, 15), 4) | ||
| 489 | elseif p == "s" then | ||
| 490 | if band(op, 0x00100000) ~= 0 then suffix = "s"..suffix end | ||
| 491 | else | ||
| 492 | assert(false) | ||
| 493 | end | ||
| 494 | if x then | ||
| 495 | last = x | ||
| 496 | if type(x) == "number" then x = "#"..x end | ||
| 497 | operands[#operands+1] = x | ||
| 498 | end | ||
| 499 | end | ||
| 500 | |||
| 501 | return putop(ctx, name..suffix, operands) | ||
| 502 | end | ||
| 503 | |||
| 504 | ------------------------------------------------------------------------------ | ||
| 505 | |||
| 506 | -- Disassemble a block of code. | ||
| 507 | local function disass_block(ctx, ofs, len) | ||
| 508 | if not ofs then ofs = 0 end | ||
| 509 | local stop = len and ofs+len or #ctx.code | ||
| 510 | ctx.pos = ofs | ||
| 511 | ctx.rel = nil | ||
| 512 | while ctx.pos < stop do disass_ins(ctx) end | ||
| 513 | end | ||
| 514 | |||
| 515 | -- Extended API: create a disassembler context. Then call ctx:disass(ofs, len). | ||
| 516 | local function create_(code, addr, out) | ||
| 517 | local ctx = {} | ||
| 518 | ctx.code = code | ||
| 519 | ctx.addr = addr or 0 | ||
| 520 | ctx.out = out or io.write | ||
| 521 | ctx.symtab = {} | ||
| 522 | ctx.disass = disass_block | ||
| 523 | ctx.hexdump = 8 | ||
| 524 | return ctx | ||
| 525 | end | ||
| 526 | |||
| 527 | -- Simple API: disassemble code (a string) at address and output via out. | ||
| 528 | local function disass_(code, addr, out) | ||
| 529 | create_(code, addr, out):disass() | ||
| 530 | end | ||
| 531 | |||
| 532 | |||
| 533 | -- Public module functions. | ||
| 534 | module(...) | ||
| 535 | |||
| 536 | create = create_ | ||
| 537 | disass = disass_ | ||
| 538 | |||
