diff options
Diffstat (limited to 'src/jit/dis_arm64.lua')
-rw-r--r-- | src/jit/dis_arm64.lua | 1216 |
1 files changed, 1216 insertions, 0 deletions
diff --git a/src/jit/dis_arm64.lua b/src/jit/dis_arm64.lua new file mode 100644 index 00000000..d1596ebc --- /dev/null +++ b/src/jit/dis_arm64.lua | |||
@@ -0,0 +1,1216 @@ | |||
1 | ---------------------------------------------------------------------------- | ||
2 | -- LuaJIT ARM64 disassembler module. | ||
3 | -- | ||
4 | -- Copyright (C) 2005-2020 Mike Pall. All rights reserved. | ||
5 | -- Released under the MIT license. See Copyright Notice in luajit.h | ||
6 | -- | ||
7 | -- Contributed by Djordje Kovacevic and Stefan Pejic from RT-RK.com. | ||
8 | -- Sponsored by Cisco Systems, Inc. | ||
9 | ---------------------------------------------------------------------------- | ||
10 | -- This is a helper module used by the LuaJIT machine code dumper module. | ||
11 | -- | ||
12 | -- It disassembles most user-mode AArch64 instructions. | ||
13 | -- NYI: Advanced SIMD and VFP instructions. | ||
14 | ------------------------------------------------------------------------------ | ||
15 | |||
16 | local type = type | ||
17 | local sub, byte, format = string.sub, string.byte, string.format | ||
18 | local match, gmatch, gsub = string.match, string.gmatch, string.gsub | ||
19 | local concat = table.concat | ||
20 | local bit = require("bit") | ||
21 | local band, bor, bxor, tohex = bit.band, bit.bor, bit.bxor, bit.tohex | ||
22 | local lshift, rshift, arshift = bit.lshift, bit.rshift, bit.arshift | ||
23 | local ror = bit.ror | ||
24 | |||
25 | ------------------------------------------------------------------------------ | ||
26 | -- Opcode maps | ||
27 | ------------------------------------------------------------------------------ | ||
28 | |||
29 | local map_adr = { -- PC-relative addressing. | ||
30 | shift = 31, mask = 1, | ||
31 | [0] = "adrDBx", "adrpDBx" | ||
32 | } | ||
33 | |||
34 | local map_addsubi = { -- Add/subtract immediate. | ||
35 | shift = 29, mask = 3, | ||
36 | [0] = "add|movDNIg", "adds|cmnD0NIg", "subDNIg", "subs|cmpD0NIg", | ||
37 | } | ||
38 | |||
39 | local map_logi = { -- Logical immediate. | ||
40 | shift = 31, mask = 1, | ||
41 | [0] = { | ||
42 | shift = 22, mask = 1, | ||
43 | [0] = { | ||
44 | shift = 29, mask = 3, | ||
45 | [0] = "andDNig", "orr|movDN0ig", "eorDNig", "ands|tstD0Nig" | ||
46 | }, | ||
47 | false -- unallocated | ||
48 | }, | ||
49 | { | ||
50 | shift = 29, mask = 3, | ||
51 | [0] = "andDNig", "orr|movDN0ig", "eorDNig", "ands|tstD0Nig" | ||
52 | } | ||
53 | } | ||
54 | |||
55 | local map_movwi = { -- Move wide immediate. | ||
56 | shift = 31, mask = 1, | ||
57 | [0] = { | ||
58 | shift = 22, mask = 1, | ||
59 | [0] = { | ||
60 | shift = 29, mask = 3, | ||
61 | [0] = "movnDWRg", false, "movz|movDYRg", "movkDWRg" | ||
62 | }, false -- unallocated | ||
63 | }, | ||
64 | { | ||
65 | shift = 29, mask = 3, | ||
66 | [0] = "movnDWRg", false, "movz|movDYRg", "movkDWRg" | ||
67 | }, | ||
68 | } | ||
69 | |||
70 | local map_bitf = { -- Bitfield. | ||
71 | shift = 31, mask = 1, | ||
72 | [0] = { | ||
73 | shift = 22, mask = 1, | ||
74 | [0] = { | ||
75 | shift = 29, mask = 3, | ||
76 | [0] = "sbfm|sbfiz|sbfx|asr|sxtw|sxth|sxtbDN12w", | ||
77 | "bfm|bfi|bfxilDN13w", | ||
78 | "ubfm|ubfiz|ubfx|lsr|lsl|uxth|uxtbDN12w" | ||
79 | } | ||
80 | }, | ||
81 | { | ||
82 | shift = 22, mask = 1, | ||
83 | { | ||
84 | shift = 29, mask = 3, | ||
85 | [0] = "sbfm|sbfiz|sbfx|asr|sxtw|sxth|sxtbDN12x", | ||
86 | "bfm|bfi|bfxilDN13x", | ||
87 | "ubfm|ubfiz|ubfx|lsr|lsl|uxth|uxtbDN12x" | ||
88 | } | ||
89 | } | ||
90 | } | ||
91 | |||
92 | local map_datai = { -- Data processing - immediate. | ||
93 | shift = 23, mask = 7, | ||
94 | [0] = map_adr, map_adr, map_addsubi, false, | ||
95 | map_logi, map_movwi, map_bitf, | ||
96 | { | ||
97 | shift = 15, mask = 0x1c0c1, | ||
98 | [0] = "extr|rorDNM4w", [0x10080] = "extr|rorDNM4x", | ||
99 | [0x10081] = "extr|rorDNM4x" | ||
100 | } | ||
101 | } | ||
102 | |||
103 | local map_logsr = { -- Logical, shifted register. | ||
104 | shift = 31, mask = 1, | ||
105 | [0] = { | ||
106 | shift = 15, mask = 1, | ||
107 | [0] = { | ||
108 | shift = 29, mask = 3, | ||
109 | [0] = { | ||
110 | shift = 21, mask = 7, | ||
111 | [0] = "andDNMSg", "bicDNMSg", "andDNMSg", "bicDNMSg", | ||
112 | "andDNMSg", "bicDNMSg", "andDNMg", "bicDNMg" | ||
113 | }, | ||
114 | { | ||
115 | shift = 21, mask = 7, | ||
116 | [0] ="orr|movDN0MSg", "orn|mvnDN0MSg", "orr|movDN0MSg", "orn|mvnDN0MSg", | ||
117 | "orr|movDN0MSg", "orn|mvnDN0MSg", "orr|movDN0Mg", "orn|mvnDN0Mg" | ||
118 | }, | ||
119 | { | ||
120 | shift = 21, mask = 7, | ||
121 | [0] = "eorDNMSg", "eonDNMSg", "eorDNMSg", "eonDNMSg", | ||
122 | "eorDNMSg", "eonDNMSg", "eorDNMg", "eonDNMg" | ||
123 | }, | ||
124 | { | ||
125 | shift = 21, mask = 7, | ||
126 | [0] = "ands|tstD0NMSg", "bicsDNMSg", "ands|tstD0NMSg", "bicsDNMSg", | ||
127 | "ands|tstD0NMSg", "bicsDNMSg", "ands|tstD0NMg", "bicsDNMg" | ||
128 | } | ||
129 | }, | ||
130 | false -- unallocated | ||
131 | }, | ||
132 | { | ||
133 | shift = 29, mask = 3, | ||
134 | [0] = { | ||
135 | shift = 21, mask = 7, | ||
136 | [0] = "andDNMSg", "bicDNMSg", "andDNMSg", "bicDNMSg", | ||
137 | "andDNMSg", "bicDNMSg", "andDNMg", "bicDNMg" | ||
138 | }, | ||
139 | { | ||
140 | shift = 21, mask = 7, | ||
141 | [0] = "orr|movDN0MSg", "orn|mvnDN0MSg", "orr|movDN0MSg", "orn|mvnDN0MSg", | ||
142 | "orr|movDN0MSg", "orn|mvnDN0MSg", "orr|movDN0Mg", "orn|mvnDN0Mg" | ||
143 | }, | ||
144 | { | ||
145 | shift = 21, mask = 7, | ||
146 | [0] = "eorDNMSg", "eonDNMSg", "eorDNMSg", "eonDNMSg", | ||
147 | "eorDNMSg", "eonDNMSg", "eorDNMg", "eonDNMg" | ||
148 | }, | ||
149 | { | ||
150 | shift = 21, mask = 7, | ||
151 | [0] = "ands|tstD0NMSg", "bicsDNMSg", "ands|tstD0NMSg", "bicsDNMSg", | ||
152 | "ands|tstD0NMSg", "bicsDNMSg", "ands|tstD0NMg", "bicsDNMg" | ||
153 | } | ||
154 | } | ||
155 | } | ||
156 | |||
157 | local map_assh = { | ||
158 | shift = 31, mask = 1, | ||
159 | [0] = { | ||
160 | shift = 15, mask = 1, | ||
161 | [0] = { | ||
162 | shift = 29, mask = 3, | ||
163 | [0] = { | ||
164 | shift = 22, mask = 3, | ||
165 | [0] = "addDNMSg", "addDNMSg", "addDNMSg", "addDNMg" | ||
166 | }, | ||
167 | { | ||
168 | shift = 22, mask = 3, | ||
169 | [0] = "adds|cmnD0NMSg", "adds|cmnD0NMSg", | ||
170 | "adds|cmnD0NMSg", "adds|cmnD0NMg" | ||
171 | }, | ||
172 | { | ||
173 | shift = 22, mask = 3, | ||
174 | [0] = "sub|negDN0MSg", "sub|negDN0MSg", "sub|negDN0MSg", "sub|negDN0Mg" | ||
175 | }, | ||
176 | { | ||
177 | shift = 22, mask = 3, | ||
178 | [0] = "subs|cmp|negsD0N0MzSg", "subs|cmp|negsD0N0MzSg", | ||
179 | "subs|cmp|negsD0N0MzSg", "subs|cmp|negsD0N0Mzg" | ||
180 | }, | ||
181 | }, | ||
182 | false -- unallocated | ||
183 | }, | ||
184 | { | ||
185 | shift = 29, mask = 3, | ||
186 | [0] = { | ||
187 | shift = 22, mask = 3, | ||
188 | [0] = "addDNMSg", "addDNMSg", "addDNMSg", "addDNMg" | ||
189 | }, | ||
190 | { | ||
191 | shift = 22, mask = 3, | ||
192 | [0] = "adds|cmnD0NMSg", "adds|cmnD0NMSg", "adds|cmnD0NMSg", | ||
193 | "adds|cmnD0NMg" | ||
194 | }, | ||
195 | { | ||
196 | shift = 22, mask = 3, | ||
197 | [0] = "sub|negDN0MSg", "sub|negDN0MSg", "sub|negDN0MSg", "sub|negDN0Mg" | ||
198 | }, | ||
199 | { | ||
200 | shift = 22, mask = 3, | ||
201 | [0] = "subs|cmp|negsD0N0MzSg", "subs|cmp|negsD0N0MzSg", | ||
202 | "subs|cmp|negsD0N0MzSg", "subs|cmp|negsD0N0Mzg" | ||
203 | } | ||
204 | } | ||
205 | } | ||
206 | |||
207 | local map_addsubsh = { -- Add/subtract, shifted register. | ||
208 | shift = 22, mask = 3, | ||
209 | [0] = map_assh, map_assh, map_assh | ||
210 | } | ||
211 | |||
212 | local map_addsubex = { -- Add/subtract, extended register. | ||
213 | shift = 22, mask = 3, | ||
214 | [0] = { | ||
215 | shift = 29, mask = 3, | ||
216 | [0] = "addDNMXg", "adds|cmnD0NMXg", "subDNMXg", "subs|cmpD0NMzXg", | ||
217 | } | ||
218 | } | ||
219 | |||
220 | local map_addsubc = { -- Add/subtract, with carry. | ||
221 | shift = 10, mask = 63, | ||
222 | [0] = { | ||
223 | shift = 29, mask = 3, | ||
224 | [0] = "adcDNMg", "adcsDNMg", "sbc|ngcDN0Mg", "sbcs|ngcsDN0Mg", | ||
225 | } | ||
226 | } | ||
227 | |||
228 | local map_ccomp = { | ||
229 | shift = 4, mask = 1, | ||
230 | [0] = { | ||
231 | shift = 10, mask = 3, | ||
232 | [0] = { -- Conditional compare register. | ||
233 | shift = 29, mask = 3, | ||
234 | "ccmnNMVCg", false, "ccmpNMVCg", | ||
235 | }, | ||
236 | [2] = { -- Conditional compare immediate. | ||
237 | shift = 29, mask = 3, | ||
238 | "ccmnN5VCg", false, "ccmpN5VCg", | ||
239 | } | ||
240 | } | ||
241 | } | ||
242 | |||
243 | local map_csel = { -- Conditional select. | ||
244 | shift = 11, mask = 1, | ||
245 | [0] = { | ||
246 | shift = 10, mask = 1, | ||
247 | [0] = { | ||
248 | shift = 29, mask = 3, | ||
249 | [0] = "cselDNMzCg", false, "csinv|cinv|csetmDNMcg", false, | ||
250 | }, | ||
251 | { | ||
252 | shift = 29, mask = 3, | ||
253 | [0] = "csinc|cinc|csetDNMcg", false, "csneg|cnegDNMcg", false, | ||
254 | } | ||
255 | } | ||
256 | } | ||
257 | |||
258 | local map_data1s = { -- Data processing, 1 source. | ||
259 | shift = 29, mask = 1, | ||
260 | [0] = { | ||
261 | shift = 31, mask = 1, | ||
262 | [0] = { | ||
263 | shift = 10, mask = 0x7ff, | ||
264 | [0] = "rbitDNg", "rev16DNg", "revDNw", false, "clzDNg", "clsDNg" | ||
265 | }, | ||
266 | { | ||
267 | shift = 10, mask = 0x7ff, | ||
268 | [0] = "rbitDNg", "rev16DNg", "rev32DNx", "revDNx", "clzDNg", "clsDNg" | ||
269 | } | ||
270 | } | ||
271 | } | ||
272 | |||
273 | local map_data2s = { -- Data processing, 2 sources. | ||
274 | shift = 29, mask = 1, | ||
275 | [0] = { | ||
276 | shift = 10, mask = 63, | ||
277 | false, "udivDNMg", "sdivDNMg", false, false, false, false, "lslDNMg", | ||
278 | "lsrDNMg", "asrDNMg", "rorDNMg" | ||
279 | } | ||
280 | } | ||
281 | |||
282 | local map_data3s = { -- Data processing, 3 sources. | ||
283 | shift = 29, mask = 7, | ||
284 | [0] = { | ||
285 | shift = 21, mask = 7, | ||
286 | [0] = { | ||
287 | shift = 15, mask = 1, | ||
288 | [0] = "madd|mulDNMA0g", "msub|mnegDNMA0g" | ||
289 | } | ||
290 | }, false, false, false, | ||
291 | { | ||
292 | shift = 15, mask = 1, | ||
293 | [0] = { | ||
294 | shift = 21, mask = 7, | ||
295 | [0] = "madd|mulDNMA0g", "smaddl|smullDxNMwA0x", "smulhDNMx", false, | ||
296 | false, "umaddl|umullDxNMwA0x", "umulhDNMx" | ||
297 | }, | ||
298 | { | ||
299 | shift = 21, mask = 7, | ||
300 | [0] = "msub|mnegDNMA0g", "smsubl|smneglDxNMwA0x", false, false, | ||
301 | false, "umsubl|umneglDxNMwA0x" | ||
302 | } | ||
303 | } | ||
304 | } | ||
305 | |||
306 | local map_datar = { -- Data processing, register. | ||
307 | shift = 28, mask = 1, | ||
308 | [0] = { | ||
309 | shift = 24, mask = 1, | ||
310 | [0] = map_logsr, | ||
311 | { | ||
312 | shift = 21, mask = 1, | ||
313 | [0] = map_addsubsh, map_addsubex | ||
314 | } | ||
315 | }, | ||
316 | { | ||
317 | shift = 21, mask = 15, | ||
318 | [0] = map_addsubc, false, map_ccomp, false, map_csel, false, | ||
319 | { | ||
320 | shift = 30, mask = 1, | ||
321 | [0] = map_data2s, map_data1s | ||
322 | }, | ||
323 | false, map_data3s, map_data3s, map_data3s, map_data3s, map_data3s, | ||
324 | map_data3s, map_data3s, map_data3s | ||
325 | } | ||
326 | } | ||
327 | |||
328 | local map_lrl = { -- Load register, literal. | ||
329 | shift = 26, mask = 1, | ||
330 | [0] = { | ||
331 | shift = 30, mask = 3, | ||
332 | [0] = "ldrDwB", "ldrDxB", "ldrswDxB" | ||
333 | }, | ||
334 | { | ||
335 | shift = 30, mask = 3, | ||
336 | [0] = "ldrDsB", "ldrDdB" | ||
337 | } | ||
338 | } | ||
339 | |||
340 | local map_lsriind = { -- Load/store register, immediate pre/post-indexed. | ||
341 | shift = 30, mask = 3, | ||
342 | [0] = { | ||
343 | shift = 26, mask = 1, | ||
344 | [0] = { | ||
345 | shift = 22, mask = 3, | ||
346 | [0] = "strbDwzL", "ldrbDwzL", "ldrsbDxzL", "ldrsbDwzL" | ||
347 | } | ||
348 | }, | ||
349 | { | ||
350 | shift = 26, mask = 1, | ||
351 | [0] = { | ||
352 | shift = 22, mask = 3, | ||
353 | [0] = "strhDwzL", "ldrhDwzL", "ldrshDxzL", "ldrshDwzL" | ||
354 | } | ||
355 | }, | ||
356 | { | ||
357 | shift = 26, mask = 1, | ||
358 | [0] = { | ||
359 | shift = 22, mask = 3, | ||
360 | [0] = "strDwzL", "ldrDwzL", "ldrswDxzL" | ||
361 | }, | ||
362 | { | ||
363 | shift = 22, mask = 3, | ||
364 | [0] = "strDszL", "ldrDszL" | ||
365 | } | ||
366 | }, | ||
367 | { | ||
368 | shift = 26, mask = 1, | ||
369 | [0] = { | ||
370 | shift = 22, mask = 3, | ||
371 | [0] = "strDxzL", "ldrDxzL" | ||
372 | }, | ||
373 | { | ||
374 | shift = 22, mask = 3, | ||
375 | [0] = "strDdzL", "ldrDdzL" | ||
376 | } | ||
377 | } | ||
378 | } | ||
379 | |||
380 | local map_lsriro = { | ||
381 | shift = 21, mask = 1, | ||
382 | [0] = { -- Load/store register immediate. | ||
383 | shift = 10, mask = 3, | ||
384 | [0] = { -- Unscaled immediate. | ||
385 | shift = 26, mask = 1, | ||
386 | [0] = { | ||
387 | shift = 30, mask = 3, | ||
388 | [0] = { | ||
389 | shift = 22, mask = 3, | ||
390 | [0] = "sturbDwK", "ldurbDwK" | ||
391 | }, | ||
392 | { | ||
393 | shift = 22, mask = 3, | ||
394 | [0] = "sturhDwK", "ldurhDwK" | ||
395 | }, | ||
396 | { | ||
397 | shift = 22, mask = 3, | ||
398 | [0] = "sturDwK", "ldurDwK" | ||
399 | }, | ||
400 | { | ||
401 | shift = 22, mask = 3, | ||
402 | [0] = "sturDxK", "ldurDxK" | ||
403 | } | ||
404 | } | ||
405 | }, map_lsriind, false, map_lsriind | ||
406 | }, | ||
407 | { -- Load/store register, register offset. | ||
408 | shift = 10, mask = 3, | ||
409 | [2] = { | ||
410 | shift = 26, mask = 1, | ||
411 | [0] = { | ||
412 | shift = 30, mask = 3, | ||
413 | [0] = { | ||
414 | shift = 22, mask = 3, | ||
415 | [0] = "strbDwO", "ldrbDwO", "ldrsbDxO", "ldrsbDwO" | ||
416 | }, | ||
417 | { | ||
418 | shift = 22, mask = 3, | ||
419 | [0] = "strhDwO", "ldrhDwO", "ldrshDxO", "ldrshDwO" | ||
420 | }, | ||
421 | { | ||
422 | shift = 22, mask = 3, | ||
423 | [0] = "strDwO", "ldrDwO", "ldrswDxO" | ||
424 | }, | ||
425 | { | ||
426 | shift = 22, mask = 3, | ||
427 | [0] = "strDxO", "ldrDxO" | ||
428 | } | ||
429 | }, | ||
430 | { | ||
431 | shift = 30, mask = 3, | ||
432 | [2] = { | ||
433 | shift = 22, mask = 3, | ||
434 | [0] = "strDsO", "ldrDsO" | ||
435 | }, | ||
436 | [3] = { | ||
437 | shift = 22, mask = 3, | ||
438 | [0] = "strDdO", "ldrDdO" | ||
439 | } | ||
440 | } | ||
441 | } | ||
442 | } | ||
443 | } | ||
444 | |||
445 | local map_lsp = { -- Load/store register pair, offset. | ||
446 | shift = 22, mask = 1, | ||
447 | [0] = { | ||
448 | shift = 30, mask = 3, | ||
449 | [0] = { | ||
450 | shift = 26, mask = 1, | ||
451 | [0] = "stpDzAzwP", "stpDzAzsP", | ||
452 | }, | ||
453 | { | ||
454 | shift = 26, mask = 1, | ||
455 | "stpDzAzdP" | ||
456 | }, | ||
457 | { | ||
458 | shift = 26, mask = 1, | ||
459 | [0] = "stpDzAzxP" | ||
460 | } | ||
461 | }, | ||
462 | { | ||
463 | shift = 30, mask = 3, | ||
464 | [0] = { | ||
465 | shift = 26, mask = 1, | ||
466 | [0] = "ldpDzAzwP", "ldpDzAzsP", | ||
467 | }, | ||
468 | { | ||
469 | shift = 26, mask = 1, | ||
470 | [0] = "ldpswDAxP", "ldpDzAzdP" | ||
471 | }, | ||
472 | { | ||
473 | shift = 26, mask = 1, | ||
474 | [0] = "ldpDzAzxP" | ||
475 | } | ||
476 | } | ||
477 | } | ||
478 | |||
479 | local map_ls = { -- Loads and stores. | ||
480 | shift = 24, mask = 0x31, | ||
481 | [0x10] = map_lrl, [0x30] = map_lsriro, | ||
482 | [0x20] = { | ||
483 | shift = 23, mask = 3, | ||
484 | map_lsp, map_lsp, map_lsp | ||
485 | }, | ||
486 | [0x21] = { | ||
487 | shift = 23, mask = 3, | ||
488 | map_lsp, map_lsp, map_lsp | ||
489 | }, | ||
490 | [0x31] = { | ||
491 | shift = 26, mask = 1, | ||
492 | [0] = { | ||
493 | shift = 30, mask = 3, | ||
494 | [0] = { | ||
495 | shift = 22, mask = 3, | ||
496 | [0] = "strbDwzU", "ldrbDwzU" | ||
497 | }, | ||
498 | { | ||
499 | shift = 22, mask = 3, | ||
500 | [0] = "strhDwzU", "ldrhDwzU" | ||
501 | }, | ||
502 | { | ||
503 | shift = 22, mask = 3, | ||
504 | [0] = "strDwzU", "ldrDwzU" | ||
505 | }, | ||
506 | { | ||
507 | shift = 22, mask = 3, | ||
508 | [0] = "strDxzU", "ldrDxzU" | ||
509 | } | ||
510 | }, | ||
511 | { | ||
512 | shift = 30, mask = 3, | ||
513 | [2] = { | ||
514 | shift = 22, mask = 3, | ||
515 | [0] = "strDszU", "ldrDszU" | ||
516 | }, | ||
517 | [3] = { | ||
518 | shift = 22, mask = 3, | ||
519 | [0] = "strDdzU", "ldrDdzU" | ||
520 | } | ||
521 | } | ||
522 | }, | ||
523 | } | ||
524 | |||
525 | local map_datafp = { -- Data processing, SIMD and FP. | ||
526 | shift = 28, mask = 7, | ||
527 | { -- 001 | ||
528 | shift = 24, mask = 1, | ||
529 | [0] = { | ||
530 | shift = 21, mask = 1, | ||
531 | { | ||
532 | shift = 10, mask = 3, | ||
533 | [0] = { | ||
534 | shift = 12, mask = 1, | ||
535 | [0] = { | ||
536 | shift = 13, mask = 1, | ||
537 | [0] = { | ||
538 | shift = 14, mask = 1, | ||
539 | [0] = { | ||
540 | shift = 15, mask = 1, | ||
541 | [0] = { -- FP/int conversion. | ||
542 | shift = 31, mask = 1, | ||
543 | [0] = { | ||
544 | shift = 16, mask = 0xff, | ||
545 | [0x20] = "fcvtnsDwNs", [0x21] = "fcvtnuDwNs", | ||
546 | [0x22] = "scvtfDsNw", [0x23] = "ucvtfDsNw", | ||
547 | [0x24] = "fcvtasDwNs", [0x25] = "fcvtauDwNs", | ||
548 | [0x26] = "fmovDwNs", [0x27] = "fmovDsNw", | ||
549 | [0x28] = "fcvtpsDwNs", [0x29] = "fcvtpuDwNs", | ||
550 | [0x30] = "fcvtmsDwNs", [0x31] = "fcvtmuDwNs", | ||
551 | [0x38] = "fcvtzsDwNs", [0x39] = "fcvtzuDwNs", | ||
552 | [0x60] = "fcvtnsDwNd", [0x61] = "fcvtnuDwNd", | ||
553 | [0x62] = "scvtfDdNw", [0x63] = "ucvtfDdNw", | ||
554 | [0x64] = "fcvtasDwNd", [0x65] = "fcvtauDwNd", | ||
555 | [0x68] = "fcvtpsDwNd", [0x69] = "fcvtpuDwNd", | ||
556 | [0x70] = "fcvtmsDwNd", [0x71] = "fcvtmuDwNd", | ||
557 | [0x78] = "fcvtzsDwNd", [0x79] = "fcvtzuDwNd" | ||
558 | }, | ||
559 | { | ||
560 | shift = 16, mask = 0xff, | ||
561 | [0x20] = "fcvtnsDxNs", [0x21] = "fcvtnuDxNs", | ||
562 | [0x22] = "scvtfDsNx", [0x23] = "ucvtfDsNx", | ||
563 | [0x24] = "fcvtasDxNs", [0x25] = "fcvtauDxNs", | ||
564 | [0x28] = "fcvtpsDxNs", [0x29] = "fcvtpuDxNs", | ||
565 | [0x30] = "fcvtmsDxNs", [0x31] = "fcvtmuDxNs", | ||
566 | [0x38] = "fcvtzsDxNs", [0x39] = "fcvtzuDxNs", | ||
567 | [0x60] = "fcvtnsDxNd", [0x61] = "fcvtnuDxNd", | ||
568 | [0x62] = "scvtfDdNx", [0x63] = "ucvtfDdNx", | ||
569 | [0x64] = "fcvtasDxNd", [0x65] = "fcvtauDxNd", | ||
570 | [0x66] = "fmovDxNd", [0x67] = "fmovDdNx", | ||
571 | [0x68] = "fcvtpsDxNd", [0x69] = "fcvtpuDxNd", | ||
572 | [0x70] = "fcvtmsDxNd", [0x71] = "fcvtmuDxNd", | ||
573 | [0x78] = "fcvtzsDxNd", [0x79] = "fcvtzuDxNd" | ||
574 | } | ||
575 | } | ||
576 | }, | ||
577 | { -- FP data-processing, 1 source. | ||
578 | shift = 31, mask = 1, | ||
579 | [0] = { | ||
580 | shift = 22, mask = 3, | ||
581 | [0] = { | ||
582 | shift = 15, mask = 63, | ||
583 | [0] = "fmovDNf", "fabsDNf", "fnegDNf", | ||
584 | "fsqrtDNf", false, "fcvtDdNs", false, false, | ||
585 | "frintnDNf", "frintpDNf", "frintmDNf", "frintzDNf", | ||
586 | "frintaDNf", false, "frintxDNf", "frintiDNf", | ||
587 | }, | ||
588 | { | ||
589 | shift = 15, mask = 63, | ||
590 | [0] = "fmovDNf", "fabsDNf", "fnegDNf", | ||
591 | "fsqrtDNf", "fcvtDsNd", false, false, false, | ||
592 | "frintnDNf", "frintpDNf", "frintmDNf", "frintzDNf", | ||
593 | "frintaDNf", false, "frintxDNf", "frintiDNf", | ||
594 | } | ||
595 | } | ||
596 | } | ||
597 | }, | ||
598 | { -- FP compare. | ||
599 | shift = 31, mask = 1, | ||
600 | [0] = { | ||
601 | shift = 14, mask = 3, | ||
602 | [0] = { | ||
603 | shift = 23, mask = 1, | ||
604 | [0] = { | ||
605 | shift = 0, mask = 31, | ||
606 | [0] = "fcmpNMf", [8] = "fcmpNZf", | ||
607 | [16] = "fcmpeNMf", [24] = "fcmpeNZf", | ||
608 | } | ||
609 | } | ||
610 | } | ||
611 | } | ||
612 | }, | ||
613 | { -- FP immediate. | ||
614 | shift = 31, mask = 1, | ||
615 | [0] = { | ||
616 | shift = 5, mask = 31, | ||
617 | [0] = { | ||
618 | shift = 23, mask = 1, | ||
619 | [0] = "fmovDFf" | ||
620 | } | ||
621 | } | ||
622 | } | ||
623 | }, | ||
624 | { -- FP conditional compare. | ||
625 | shift = 31, mask = 1, | ||
626 | [0] = { | ||
627 | shift = 23, mask = 1, | ||
628 | [0] = { | ||
629 | shift = 4, mask = 1, | ||
630 | [0] = "fccmpNMVCf", "fccmpeNMVCf" | ||
631 | } | ||
632 | } | ||
633 | }, | ||
634 | { -- FP data-processing, 2 sources. | ||
635 | shift = 31, mask = 1, | ||
636 | [0] = { | ||
637 | shift = 23, mask = 1, | ||
638 | [0] = { | ||
639 | shift = 12, mask = 15, | ||
640 | [0] = "fmulDNMf", "fdivDNMf", "faddDNMf", "fsubDNMf", | ||
641 | "fmaxDNMf", "fminDNMf", "fmaxnmDNMf", "fminnmDNMf", | ||
642 | "fnmulDNMf" | ||
643 | } | ||
644 | } | ||
645 | }, | ||
646 | { -- FP conditional select. | ||
647 | shift = 31, mask = 1, | ||
648 | [0] = { | ||
649 | shift = 23, mask = 1, | ||
650 | [0] = "fcselDNMCf" | ||
651 | } | ||
652 | } | ||
653 | } | ||
654 | }, | ||
655 | { -- FP data-processing, 3 sources. | ||
656 | shift = 31, mask = 1, | ||
657 | [0] = { | ||
658 | shift = 15, mask = 1, | ||
659 | [0] = { | ||
660 | shift = 21, mask = 5, | ||
661 | [0] = "fmaddDNMAf", "fnmaddDNMAf" | ||
662 | }, | ||
663 | { | ||
664 | shift = 21, mask = 5, | ||
665 | [0] = "fmsubDNMAf", "fnmsubDNMAf" | ||
666 | } | ||
667 | } | ||
668 | } | ||
669 | } | ||
670 | } | ||
671 | |||
672 | local map_br = { -- Branches, exception generating and system instructions. | ||
673 | shift = 29, mask = 7, | ||
674 | [0] = "bB", | ||
675 | { -- Compare & branch, immediate. | ||
676 | shift = 24, mask = 3, | ||
677 | [0] = "cbzDBg", "cbnzDBg", "tbzDTBw", "tbnzDTBw" | ||
678 | }, | ||
679 | { -- Conditional branch, immediate. | ||
680 | shift = 24, mask = 3, | ||
681 | [0] = { | ||
682 | shift = 4, mask = 1, | ||
683 | [0] = { | ||
684 | shift = 0, mask = 15, | ||
685 | [0] = "beqB", "bneB", "bhsB", "bloB", "bmiB", "bplB", "bvsB", "bvcB", | ||
686 | "bhiB", "blsB", "bgeB", "bltB", "bgtB", "bleB", "balB" | ||
687 | } | ||
688 | } | ||
689 | }, false, "blB", | ||
690 | { -- Compare & branch, immediate. | ||
691 | shift = 24, mask = 3, | ||
692 | [0] = "cbzDBg", "cbnzDBg", "tbzDTBx", "tbnzDTBx" | ||
693 | }, | ||
694 | { | ||
695 | shift = 24, mask = 3, | ||
696 | [0] = { -- Exception generation. | ||
697 | shift = 0, mask = 0xe0001f, | ||
698 | [0x200000] = "brkW" | ||
699 | }, | ||
700 | { -- System instructions. | ||
701 | shift = 0, mask = 0x3fffff, | ||
702 | [0x03201f] = "nop" | ||
703 | }, | ||
704 | { -- Unconditional branch, register. | ||
705 | shift = 0, mask = 0xfffc1f, | ||
706 | [0x1f0000] = "brNx", [0x3f0000] = "blrNx", | ||
707 | [0x5f0000] = "retNx" | ||
708 | }, | ||
709 | } | ||
710 | } | ||
711 | |||
712 | local map_init = { | ||
713 | shift = 25, mask = 15, | ||
714 | [0] = false, false, false, false, map_ls, map_datar, map_ls, map_datafp, | ||
715 | map_datai, map_datai, map_br, map_br, map_ls, map_datar, map_ls, map_datafp | ||
716 | } | ||
717 | |||
718 | ------------------------------------------------------------------------------ | ||
719 | |||
720 | local map_regs = { x = {}, w = {}, d = {}, s = {} } | ||
721 | |||
722 | for i=0,30 do | ||
723 | map_regs.x[i] = "x"..i | ||
724 | map_regs.w[i] = "w"..i | ||
725 | map_regs.d[i] = "d"..i | ||
726 | map_regs.s[i] = "s"..i | ||
727 | end | ||
728 | map_regs.x[31] = "sp" | ||
729 | map_regs.w[31] = "wsp" | ||
730 | map_regs.d[31] = "d31" | ||
731 | map_regs.s[31] = "s31" | ||
732 | |||
733 | local map_cond = { | ||
734 | [0] = "eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc", | ||
735 | "hi", "ls", "ge", "lt", "gt", "le", "al", | ||
736 | } | ||
737 | |||
738 | local map_shift = { [0] = "lsl", "lsr", "asr", } | ||
739 | |||
740 | local map_extend = { | ||
741 | [0] = "uxtb", "uxth", "uxtw", "uxtx", "sxtb", "sxth", "sxtw", "sxtx", | ||
742 | } | ||
743 | |||
744 | ------------------------------------------------------------------------------ | ||
745 | |||
746 | -- Output a nicely formatted line with an opcode and operands. | ||
747 | local function putop(ctx, text, operands) | ||
748 | local pos = ctx.pos | ||
749 | local extra = "" | ||
750 | if ctx.rel then | ||
751 | local sym = ctx.symtab[ctx.rel] | ||
752 | if sym then | ||
753 | extra = "\t->"..sym | ||
754 | end | ||
755 | end | ||
756 | if ctx.hexdump > 0 then | ||
757 | ctx.out(format("%08x %s %-5s %s%s\n", | ||
758 | ctx.addr+pos, tohex(ctx.op), text, concat(operands, ", "), extra)) | ||
759 | else | ||
760 | ctx.out(format("%08x %-5s %s%s\n", | ||
761 | ctx.addr+pos, text, concat(operands, ", "), extra)) | ||
762 | end | ||
763 | ctx.pos = pos + 4 | ||
764 | end | ||
765 | |||
766 | -- Fallback for unknown opcodes. | ||
767 | local function unknown(ctx) | ||
768 | return putop(ctx, ".long", { "0x"..tohex(ctx.op) }) | ||
769 | end | ||
770 | |||
771 | local function match_reg(p, pat, regnum) | ||
772 | return map_regs[match(pat, p.."%w-([xwds])")][regnum] | ||
773 | end | ||
774 | |||
775 | local function fmt_hex32(x) | ||
776 | if x < 0 then | ||
777 | return tohex(x) | ||
778 | else | ||
779 | return format("%x", x) | ||
780 | end | ||
781 | end | ||
782 | |||
783 | local imm13_rep = { 0x55555555, 0x11111111, 0x01010101, 0x00010001, 0x00000001 } | ||
784 | |||
785 | local function decode_imm13(op) | ||
786 | local imms = band(rshift(op, 10), 63) | ||
787 | local immr = band(rshift(op, 16), 63) | ||
788 | if band(op, 0x00400000) == 0 then | ||
789 | local len = 5 | ||
790 | if imms >= 56 then | ||
791 | if imms >= 60 then len = 1 else len = 2 end | ||
792 | elseif imms >= 48 then len = 3 elseif imms >= 32 then len = 4 end | ||
793 | local l = lshift(1, len)-1 | ||
794 | local s = band(imms, l) | ||
795 | local r = band(immr, l) | ||
796 | local imm = ror(rshift(-1, 31-s), r) | ||
797 | if len ~= 5 then imm = band(imm, lshift(1, l)-1) + rshift(imm, 31-l) end | ||
798 | imm = imm * imm13_rep[len] | ||
799 | local ix = fmt_hex32(imm) | ||
800 | if rshift(op, 31) ~= 0 then | ||
801 | return ix..tohex(imm) | ||
802 | else | ||
803 | return ix | ||
804 | end | ||
805 | else | ||
806 | local lo, hi = -1, 0 | ||
807 | if imms < 32 then lo = rshift(-1, 31-imms) else hi = rshift(-1, 63-imms) end | ||
808 | if immr ~= 0 then | ||
809 | lo, hi = ror(lo, immr), ror(hi, immr) | ||
810 | local x = immr == 32 and 0 or band(bxor(lo, hi), lshift(-1, 32-immr)) | ||
811 | lo, hi = bxor(lo, x), bxor(hi, x) | ||
812 | if immr >= 32 then lo, hi = hi, lo end | ||
813 | end | ||
814 | if hi ~= 0 then | ||
815 | return fmt_hex32(hi)..tohex(lo) | ||
816 | else | ||
817 | return fmt_hex32(lo) | ||
818 | end | ||
819 | end | ||
820 | end | ||
821 | |||
822 | local function parse_immpc(op, name) | ||
823 | if name == "b" or name == "bl" then | ||
824 | return arshift(lshift(op, 6), 4) | ||
825 | elseif name == "adr" or name == "adrp" then | ||
826 | local immlo = band(rshift(op, 29), 3) | ||
827 | local immhi = lshift(arshift(lshift(op, 8), 13), 2) | ||
828 | return bor(immhi, immlo) | ||
829 | elseif name == "tbz" or name == "tbnz" then | ||
830 | return lshift(arshift(lshift(op, 13), 18), 2) | ||
831 | else | ||
832 | return lshift(arshift(lshift(op, 8), 13), 2) | ||
833 | end | ||
834 | end | ||
835 | |||
836 | local function parse_fpimm8(op) | ||
837 | local sign = band(op, 0x100000) == 0 and 1 or -1 | ||
838 | local exp = bxor(rshift(arshift(lshift(op, 12), 5), 24), 0x80) - 131 | ||
839 | local frac = 16+band(rshift(op, 13), 15) | ||
840 | return sign * frac * 2^exp | ||
841 | end | ||
842 | |||
843 | local function prefer_bfx(sf, uns, imms, immr) | ||
844 | if imms < immr or imms == 31 or imms == 63 then | ||
845 | return false | ||
846 | end | ||
847 | if immr == 0 then | ||
848 | if sf == 0 and (imms == 7 or imms == 15) then | ||
849 | return false | ||
850 | end | ||
851 | if sf ~= 0 and uns == 0 and (imms == 7 or imms == 15 or imms == 31) then | ||
852 | return false | ||
853 | end | ||
854 | end | ||
855 | return true | ||
856 | end | ||
857 | |||
858 | -- Disassemble a single instruction. | ||
859 | local function disass_ins(ctx) | ||
860 | local pos = ctx.pos | ||
861 | local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4) | ||
862 | local op = bor(lshift(b3, 24), lshift(b2, 16), lshift(b1, 8), b0) | ||
863 | local operands = {} | ||
864 | local suffix = "" | ||
865 | local last, name, pat | ||
866 | local map_reg | ||
867 | ctx.op = op | ||
868 | ctx.rel = nil | ||
869 | last = nil | ||
870 | local opat | ||
871 | opat = map_init[band(rshift(op, 25), 15)] | ||
872 | while type(opat) ~= "string" do | ||
873 | if not opat then return unknown(ctx) end | ||
874 | opat = opat[band(rshift(op, opat.shift), opat.mask)] or opat._ | ||
875 | end | ||
876 | name, pat = match(opat, "^([a-z0-9]*)(.*)") | ||
877 | local altname, pat2 = match(pat, "|([a-z0-9_.|]*)(.*)") | ||
878 | if altname then pat = pat2 end | ||
879 | if sub(pat, 1, 1) == "." then | ||
880 | local s2, p2 = match(pat, "^([a-z0-9.]*)(.*)") | ||
881 | suffix = suffix..s2 | ||
882 | pat = p2 | ||
883 | end | ||
884 | |||
885 | local rt = match(pat, "[gf]") | ||
886 | if rt then | ||
887 | if rt == "g" then | ||
888 | map_reg = band(op, 0x80000000) ~= 0 and map_regs.x or map_regs.w | ||
889 | else | ||
890 | map_reg = band(op, 0x400000) ~= 0 and map_regs.d or map_regs.s | ||
891 | end | ||
892 | end | ||
893 | |||
894 | local second0, immr | ||
895 | |||
896 | for p in gmatch(pat, ".") do | ||
897 | local x = nil | ||
898 | if p == "D" then | ||
899 | local regnum = band(op, 31) | ||
900 | x = rt and map_reg[regnum] or match_reg(p, pat, regnum) | ||
901 | elseif p == "N" then | ||
902 | local regnum = band(rshift(op, 5), 31) | ||
903 | x = rt and map_reg[regnum] or match_reg(p, pat, regnum) | ||
904 | elseif p == "M" then | ||
905 | local regnum = band(rshift(op, 16), 31) | ||
906 | x = rt and map_reg[regnum] or match_reg(p, pat, regnum) | ||
907 | elseif p == "A" then | ||
908 | local regnum = band(rshift(op, 10), 31) | ||
909 | x = rt and map_reg[regnum] or match_reg(p, pat, regnum) | ||
910 | elseif p == "B" then | ||
911 | local addr = ctx.addr + pos + parse_immpc(op, name) | ||
912 | ctx.rel = addr | ||
913 | x = "0x"..tohex(addr) | ||
914 | elseif p == "T" then | ||
915 | x = bor(band(rshift(op, 26), 32), band(rshift(op, 19), 31)) | ||
916 | elseif p == "V" then | ||
917 | x = band(op, 15) | ||
918 | elseif p == "C" then | ||
919 | x = map_cond[band(rshift(op, 12), 15)] | ||
920 | elseif p == "c" then | ||
921 | local rn = band(rshift(op, 5), 31) | ||
922 | local rm = band(rshift(op, 16), 31) | ||
923 | local cond = band(rshift(op, 12), 15) | ||
924 | local invc = bxor(cond, 1) | ||
925 | x = map_cond[cond] | ||
926 | if altname and cond ~= 14 and cond ~= 15 then | ||
927 | local a1, a2 = match(altname, "([^|]*)|(.*)") | ||
928 | if rn == rm then | ||
929 | local n = #operands | ||
930 | operands[n] = nil | ||
931 | x = map_cond[invc] | ||
932 | if rn ~= 31 then | ||
933 | if a1 then name = a1 else name = altname end | ||
934 | else | ||
935 | operands[n-1] = nil | ||
936 | name = a2 | ||
937 | end | ||
938 | end | ||
939 | end | ||
940 | elseif p == "W" then | ||
941 | x = band(rshift(op, 5), 0xffff) | ||
942 | elseif p == "Y" then | ||
943 | x = band(rshift(op, 5), 0xffff) | ||
944 | local hw = band(rshift(op, 21), 3) | ||
945 | if altname and (hw == 0 or x ~= 0) then | ||
946 | name = altname | ||
947 | end | ||
948 | elseif p == "L" then | ||
949 | local rn = map_regs.x[band(rshift(op, 5), 31)] | ||
950 | local imm9 = arshift(lshift(op, 11), 23) | ||
951 | if band(op, 0x800) ~= 0 then | ||
952 | x = "["..rn..", #"..imm9.."]!" | ||
953 | else | ||
954 | x = "["..rn.."], #"..imm9 | ||
955 | end | ||
956 | elseif p == "U" then | ||
957 | local rn = map_regs.x[band(rshift(op, 5), 31)] | ||
958 | local sz = band(rshift(op, 30), 3) | ||
959 | local imm12 = lshift(arshift(lshift(op, 10), 20), sz) | ||
960 | if imm12 ~= 0 then | ||
961 | x = "["..rn..", #"..imm12.."]" | ||
962 | else | ||
963 | x = "["..rn.."]" | ||
964 | end | ||
965 | elseif p == "K" then | ||
966 | local rn = map_regs.x[band(rshift(op, 5), 31)] | ||
967 | local imm9 = arshift(lshift(op, 11), 23) | ||
968 | if imm9 ~= 0 then | ||
969 | x = "["..rn..", #"..imm9.."]" | ||
970 | else | ||
971 | x = "["..rn.."]" | ||
972 | end | ||
973 | elseif p == "O" then | ||
974 | local rn, rm = map_regs.x[band(rshift(op, 5), 31)] | ||
975 | local m = band(rshift(op, 13), 1) | ||
976 | if m == 0 then | ||
977 | rm = map_regs.w[band(rshift(op, 16), 31)] | ||
978 | else | ||
979 | rm = map_regs.x[band(rshift(op, 16), 31)] | ||
980 | end | ||
981 | x = "["..rn..", "..rm | ||
982 | local opt = band(rshift(op, 13), 7) | ||
983 | local s = band(rshift(op, 12), 1) | ||
984 | local sz = band(rshift(op, 30), 3) | ||
985 | -- extension to be applied | ||
986 | if opt == 3 then | ||
987 | if s == 0 then x = x.."]" | ||
988 | else x = x..", lsl #"..sz.."]" end | ||
989 | elseif opt == 2 or opt == 6 or opt == 7 then | ||
990 | if s == 0 then x = x..", "..map_extend[opt].."]" | ||
991 | else x = x..", "..map_extend[opt].." #"..sz.."]" end | ||
992 | else | ||
993 | x = x.."]" | ||
994 | end | ||
995 | elseif p == "P" then | ||
996 | local opcv, sh = rshift(op, 26), 2 | ||
997 | if opcv >= 0x2a then sh = 4 elseif opcv >= 0x1b then sh = 3 end | ||
998 | local imm7 = lshift(arshift(lshift(op, 10), 25), sh) | ||
999 | local rn = map_regs.x[band(rshift(op, 5), 31)] | ||
1000 | local ind = band(rshift(op, 23), 3) | ||
1001 | if ind == 1 then | ||
1002 | x = "["..rn.."], #"..imm7 | ||
1003 | elseif ind == 2 then | ||
1004 | if imm7 == 0 then | ||
1005 | x = "["..rn.."]" | ||
1006 | else | ||
1007 | x = "["..rn..", #"..imm7.."]" | ||
1008 | end | ||
1009 | elseif ind == 3 then | ||
1010 | x = "["..rn..", #"..imm7.."]!" | ||
1011 | end | ||
1012 | elseif p == "I" then | ||
1013 | local shf = band(rshift(op, 22), 3) | ||
1014 | local imm12 = band(rshift(op, 10), 0x0fff) | ||
1015 | local rn, rd = band(rshift(op, 5), 31), band(op, 31) | ||
1016 | if altname == "mov" and shf == 0 and imm12 == 0 and (rn == 31 or rd == 31) then | ||
1017 | name = altname | ||
1018 | x = nil | ||
1019 | elseif shf == 0 then | ||
1020 | x = imm12 | ||
1021 | elseif shf == 1 then | ||
1022 | x = imm12..", lsl #12" | ||
1023 | end | ||
1024 | elseif p == "i" then | ||
1025 | x = "#0x"..decode_imm13(op) | ||
1026 | elseif p == "1" then | ||
1027 | immr = band(rshift(op, 16), 63) | ||
1028 | x = immr | ||
1029 | elseif p == "2" then | ||
1030 | x = band(rshift(op, 10), 63) | ||
1031 | if altname then | ||
1032 | local a1, a2, a3, a4, a5, a6 = | ||
1033 | match(altname, "([^|]*)|([^|]*)|([^|]*)|([^|]*)|([^|]*)|(.*)") | ||
1034 | local sf = band(rshift(op, 26), 32) | ||
1035 | local uns = band(rshift(op, 30), 1) | ||
1036 | if prefer_bfx(sf, uns, x, immr) then | ||
1037 | name = a2 | ||
1038 | x = x - immr + 1 | ||
1039 | elseif immr == 0 and x == 7 then | ||
1040 | local n = #operands | ||
1041 | operands[n] = nil | ||
1042 | if sf ~= 0 then | ||
1043 | operands[n-1] = gsub(operands[n-1], "x", "w") | ||
1044 | end | ||
1045 | last = operands[n-1] | ||
1046 | name = a6 | ||
1047 | x = nil | ||
1048 | elseif immr == 0 and x == 15 then | ||
1049 | local n = #operands | ||
1050 | operands[n] = nil | ||
1051 | if sf ~= 0 then | ||
1052 | operands[n-1] = gsub(operands[n-1], "x", "w") | ||
1053 | end | ||
1054 | last = operands[n-1] | ||
1055 | name = a5 | ||
1056 | x = nil | ||
1057 | elseif x == 31 or x == 63 then | ||
1058 | if x == 31 and immr == 0 and name == "sbfm" then | ||
1059 | name = a4 | ||
1060 | local n = #operands | ||
1061 | operands[n] = nil | ||
1062 | if sf ~= 0 then | ||
1063 | operands[n-1] = gsub(operands[n-1], "x", "w") | ||
1064 | end | ||
1065 | last = operands[n-1] | ||
1066 | else | ||
1067 | name = a3 | ||
1068 | end | ||
1069 | x = nil | ||
1070 | elseif band(x, 31) ~= 31 and immr == x+1 and name == "ubfm" then | ||
1071 | name = a4 | ||
1072 | last = "#"..(sf+32 - immr) | ||
1073 | operands[#operands] = last | ||
1074 | x = nil | ||
1075 | elseif x < immr then | ||
1076 | name = a1 | ||
1077 | last = "#"..(sf+32 - immr) | ||
1078 | operands[#operands] = last | ||
1079 | x = x + 1 | ||
1080 | end | ||
1081 | end | ||
1082 | elseif p == "3" then | ||
1083 | x = band(rshift(op, 10), 63) | ||
1084 | if altname then | ||
1085 | local a1, a2 = match(altname, "([^|]*)|(.*)") | ||
1086 | if x < immr then | ||
1087 | name = a1 | ||
1088 | local sf = band(rshift(op, 26), 32) | ||
1089 | last = "#"..(sf+32 - immr) | ||
1090 | operands[#operands] = last | ||
1091 | x = x + 1 | ||
1092 | elseif x >= immr then | ||
1093 | name = a2 | ||
1094 | x = x - immr + 1 | ||
1095 | end | ||
1096 | end | ||
1097 | elseif p == "4" then | ||
1098 | x = band(rshift(op, 10), 63) | ||
1099 | local rn = band(rshift(op, 5), 31) | ||
1100 | local rm = band(rshift(op, 16), 31) | ||
1101 | if altname and rn == rm then | ||
1102 | local n = #operands | ||
1103 | operands[n] = nil | ||
1104 | last = operands[n-1] | ||
1105 | name = altname | ||
1106 | end | ||
1107 | elseif p == "5" then | ||
1108 | x = band(rshift(op, 16), 31) | ||
1109 | elseif p == "S" then | ||
1110 | x = band(rshift(op, 10), 63) | ||
1111 | if x == 0 then x = nil | ||
1112 | else x = map_shift[band(rshift(op, 22), 3)].." #"..x end | ||
1113 | elseif p == "X" then | ||
1114 | local opt = band(rshift(op, 13), 7) | ||
1115 | -- Width specifier <R>. | ||
1116 | if opt ~= 3 and opt ~= 7 then | ||
1117 | last = map_regs.w[band(rshift(op, 16), 31)] | ||
1118 | operands[#operands] = last | ||
1119 | end | ||
1120 | x = band(rshift(op, 10), 7) | ||
1121 | -- Extension. | ||
1122 | if opt == 2 + band(rshift(op, 31), 1) and | ||
1123 | band(rshift(op, second0 and 5 or 0), 31) == 31 then | ||
1124 | if x == 0 then x = nil | ||
1125 | else x = "lsl #"..x end | ||
1126 | else | ||
1127 | if x == 0 then x = map_extend[band(rshift(op, 13), 7)] | ||
1128 | else x = map_extend[band(rshift(op, 13), 7)].." #"..x end | ||
1129 | end | ||
1130 | elseif p == "R" then | ||
1131 | x = band(rshift(op,21), 3) | ||
1132 | if x == 0 then x = nil | ||
1133 | else x = "lsl #"..x*16 end | ||
1134 | elseif p == "z" then | ||
1135 | local n = #operands | ||
1136 | if operands[n] == "sp" then operands[n] = "xzr" | ||
1137 | elseif operands[n] == "wsp" then operands[n] = "wzr" | ||
1138 | end | ||
1139 | elseif p == "Z" then | ||
1140 | x = 0 | ||
1141 | elseif p == "F" then | ||
1142 | x = parse_fpimm8(op) | ||
1143 | elseif p == "g" or p == "f" or p == "x" or p == "w" or | ||
1144 | p == "d" or p == "s" then | ||
1145 | -- These are handled in D/N/M/A. | ||
1146 | elseif p == "0" then | ||
1147 | if last == "sp" or last == "wsp" then | ||
1148 | local n = #operands | ||
1149 | operands[n] = nil | ||
1150 | last = operands[n-1] | ||
1151 | if altname then | ||
1152 | local a1, a2 = match(altname, "([^|]*)|(.*)") | ||
1153 | if not a1 then | ||
1154 | name = altname | ||
1155 | elseif second0 then | ||
1156 | name, altname = a2, a1 | ||
1157 | else | ||
1158 | name, altname = a1, a2 | ||
1159 | end | ||
1160 | end | ||
1161 | end | ||
1162 | second0 = true | ||
1163 | else | ||
1164 | assert(false) | ||
1165 | end | ||
1166 | if x then | ||
1167 | last = x | ||
1168 | if type(x) == "number" then x = "#"..x end | ||
1169 | operands[#operands+1] = x | ||
1170 | end | ||
1171 | end | ||
1172 | |||
1173 | return putop(ctx, name..suffix, operands) | ||
1174 | end | ||
1175 | |||
1176 | ------------------------------------------------------------------------------ | ||
1177 | |||
1178 | -- Disassemble a block of code. | ||
1179 | local function disass_block(ctx, ofs, len) | ||
1180 | if not ofs then ofs = 0 end | ||
1181 | local stop = len and ofs+len or #ctx.code | ||
1182 | ctx.pos = ofs | ||
1183 | ctx.rel = nil | ||
1184 | while ctx.pos < stop do disass_ins(ctx) end | ||
1185 | end | ||
1186 | |||
1187 | -- Extended API: create a disassembler context. Then call ctx:disass(ofs, len). | ||
1188 | local function create(code, addr, out) | ||
1189 | local ctx = {} | ||
1190 | ctx.code = code | ||
1191 | ctx.addr = addr or 0 | ||
1192 | ctx.out = out or io.write | ||
1193 | ctx.symtab = {} | ||
1194 | ctx.disass = disass_block | ||
1195 | ctx.hexdump = 8 | ||
1196 | return ctx | ||
1197 | end | ||
1198 | |||
1199 | -- Simple API: disassemble code (a string) at address and output via out. | ||
1200 | local function disass(code, addr, out) | ||
1201 | create(code, addr, out):disass() | ||
1202 | end | ||
1203 | |||
1204 | -- Return register name for RID. | ||
1205 | local function regname(r) | ||
1206 | if r < 32 then return map_regs.x[r] end | ||
1207 | return map_regs.d[r-32] | ||
1208 | end | ||
1209 | |||
1210 | -- Public module functions. | ||
1211 | return { | ||
1212 | create = create, | ||
1213 | disass = disass, | ||
1214 | regname = regname | ||
1215 | } | ||
1216 | |||