diff options
author | Mike Pall <mike> | 2011-05-22 18:09:08 +0200 |
---|---|---|
committer | Mike Pall <mike> | 2011-05-22 18:11:08 +0200 |
commit | c156c8a10171f7760fcbffe95ae4c8813e56da12 (patch) | |
tree | 194a4211cfac60157be34000e3abaabacb780e3d /lib | |
parent | afad72af2587e5ca41a2c93225cdcbb8abbbbd05 (diff) | |
download | luajit-c156c8a10171f7760fcbffe95ae4c8813e56da12.tar.gz luajit-c156c8a10171f7760fcbffe95ae4c8813e56da12.tar.bz2 luajit-c156c8a10171f7760fcbffe95ae4c8813e56da12.zip |
ARM: Add ARM disassembler.
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 | |||