1 ----------------------------------------------------------------------------
2 -- LuaJIT ARM64 disassembler module.
4 -- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
5 -- Released under the MIT license. See Copyright Notice in luajit.h
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.
12 -- It disassembles most user-mode AArch64 instructions.
13 -- NYI: Advanced SIMD and VFP instructions.
14 ------------------------------------------------------------------------------
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
25 ------------------------------------------------------------------------------
27 ------------------------------------------------------------------------------
29 local map_adr
= { -- PC-relative addressing.
31 [0] = "adrDBx", "adrpDBx"
34 local map_addsubi
= { -- Add/subtract immediate.
36 [0] = "add|movDNIg", "adds|cmnD0NIg", "subDNIg", "subs|cmpD0NIg",
39 local map_logi
= { -- Logical immediate.
45 [0] = "andDNig", "orr|movDN0ig", "eorDNig", "ands|tstD0Nig"
51 [0] = "andDNig", "orr|movDN0ig", "eorDNig", "ands|tstD0Nig"
55 local map_movwi
= { -- Move wide immediate.
61 [0] = "movnDWRg", false, "movz|movDYRg", "movkDWRg"
62 }, false -- unallocated
66 [0] = "movnDWRg", false, "movz|movDYRg", "movkDWRg"
70 local map_bitf
= { -- Bitfield.
76 [0] = "sbfm|sbfiz|sbfx|asr|sxtw|sxth|sxtbDN12w",
78 "ubfm|ubfiz|ubfx|lsr|lsl|uxth|uxtbDN12w"
85 [0] = "sbfm|sbfiz|sbfx|asr|sxtw|sxth|sxtbDN12x",
87 "ubfm|ubfiz|ubfx|lsr|lsl|uxth|uxtbDN12x"
92 local map_datai
= { -- Data processing - immediate.
94 [0] = map_adr
, map_adr
, map_addsubi
, false,
95 map_logi
, map_movwi
, map_bitf
,
97 shift
= 15, mask
= 0x1c0c1,
98 [0] = "extr|rorDNM4w", [0x10080] = "extr|rorDNM4x",
99 [0x10081] = "extr|rorDNM4x"
103 local map_logsr
= { -- Logical, shifted register.
104 shift
= 31, mask
= 1,
106 shift
= 15, mask
= 1,
108 shift
= 29, mask
= 3,
110 shift
= 21, mask
= 1,
111 [0] = "andDNMSg", "bicDNMSg"
114 shift
= 21, mask
= 1,
115 [0] = "orr|movDN0MSg", "orn|mvnDN0MSg"
118 shift
= 21, mask
= 1,
119 [0] = "eorDNMSg", "eonDNMSg"
122 shift
= 21, mask
= 1,
123 [0] = "ands|tstD0NMSg", "bicsDNMSg"
129 shift
= 29, mask
= 3,
131 shift
= 21, mask
= 1,
132 [0] = "andDNMSg", "bicDNMSg"
135 shift
= 21, mask
= 1,
136 [0] = "orr|movDN0MSg", "orn|mvnDN0MSg"
139 shift
= 21, mask
= 1,
140 [0] = "eorDNMSg", "eonDNMSg"
143 shift
= 21, mask
= 1,
144 [0] = "ands|tstD0NMSg", "bicsDNMSg"
150 shift
= 31, mask
= 1,
152 shift
= 15, mask
= 1,
154 shift
= 29, mask
= 3,
156 shift
= 22, mask
= 3,
157 [0] = "addDNMSg", "addDNMSg", "addDNMSg", "addDNMg"
160 shift
= 22, mask
= 3,
161 [0] = "adds|cmnD0NMSg", "adds|cmnD0NMSg",
162 "adds|cmnD0NMSg", "adds|cmnD0NMg"
165 shift
= 22, mask
= 3,
166 [0] = "sub|negDN0MSg", "sub|negDN0MSg", "sub|negDN0MSg", "sub|negDN0Mg"
169 shift
= 22, mask
= 3,
170 [0] = "subs|cmp|negsD0N0MzSg", "subs|cmp|negsD0N0MzSg",
171 "subs|cmp|negsD0N0MzSg", "subs|cmp|negsD0N0Mzg"
177 shift
= 29, mask
= 3,
179 shift
= 22, mask
= 3,
180 [0] = "addDNMSg", "addDNMSg", "addDNMSg", "addDNMg"
183 shift
= 22, mask
= 3,
184 [0] = "adds|cmnD0NMSg", "adds|cmnD0NMSg", "adds|cmnD0NMSg",
188 shift
= 22, mask
= 3,
189 [0] = "sub|negDN0MSg", "sub|negDN0MSg", "sub|negDN0MSg", "sub|negDN0Mg"
192 shift
= 22, mask
= 3,
193 [0] = "subs|cmp|negsD0N0MzSg", "subs|cmp|negsD0N0MzSg",
194 "subs|cmp|negsD0N0MzSg", "subs|cmp|negsD0N0Mzg"
199 local map_addsubsh
= { -- Add/subtract, shifted register.
200 shift
= 22, mask
= 3,
201 [0] = map_assh
, map_assh
, map_assh
204 local map_addsubex
= { -- Add/subtract, extended register.
205 shift
= 22, mask
= 3,
207 shift
= 29, mask
= 3,
208 [0] = "addDNMXg", "adds|cmnD0NMXg", "subDNMXg", "subs|cmpD0NMzXg",
212 local map_addsubc
= { -- Add/subtract, with carry.
213 shift
= 10, mask
= 63,
215 shift
= 29, mask
= 3,
216 [0] = "adcDNMg", "adcsDNMg", "sbc|ngcDN0Mg", "sbcs|ngcsDN0Mg",
223 shift
= 10, mask
= 3,
224 [0] = { -- Conditional compare register.
225 shift
= 29, mask
= 3,
226 "ccmnNMVCg", false, "ccmpNMVCg",
228 [2] = { -- Conditional compare immediate.
229 shift
= 29, mask
= 3,
230 "ccmnN5VCg", false, "ccmpN5VCg",
235 local map_csel
= { -- Conditional select.
236 shift
= 11, mask
= 1,
238 shift
= 10, mask
= 1,
240 shift
= 29, mask
= 3,
241 [0] = "cselDNMzCg", false, "csinv|cinv|csetmDNMcg", false,
244 shift
= 29, mask
= 3,
245 [0] = "csinc|cinc|csetDNMcg", false, "csneg|cnegDNMcg", false,
250 local map_data1s
= { -- Data processing, 1 source.
251 shift
= 29, mask
= 1,
253 shift
= 31, mask
= 1,
255 shift
= 10, mask
= 0x7ff,
256 [0] = "rbitDNg", "rev16DNg", "revDNw", false, "clzDNg", "clsDNg"
259 shift
= 10, mask
= 0x7ff,
260 [0] = "rbitDNg", "rev16DNg", "rev32DNx", "revDNx", "clzDNg", "clsDNg"
265 local map_data2s
= { -- Data processing, 2 sources.
266 shift
= 29, mask
= 1,
268 shift
= 10, mask
= 63,
269 false, "udivDNMg", "sdivDNMg", false, false, false, false, "lslDNMg",
270 "lsrDNMg", "asrDNMg", "rorDNMg"
274 local map_data3s
= { -- Data processing, 3 sources.
275 shift
= 29, mask
= 7,
277 shift
= 21, mask
= 7,
279 shift
= 15, mask
= 1,
280 [0] = "madd|mulDNMA0g", "msub|mnegDNMA0g"
282 }, false, false, false,
284 shift
= 15, mask
= 1,
286 shift
= 21, mask
= 7,
287 [0] = "madd|mulDNMA0g", "smaddl|smullDxNMwA0x", "smulhDNMx", false,
288 false, "umaddl|umullDxNMwA0x", "umulhDNMx"
291 shift
= 21, mask
= 7,
292 [0] = "msub|mnegDNMA0g", "smsubl|smneglDxNMwA0x", false, false,
293 false, "umsubl|umneglDxNMwA0x"
298 local map_datar
= { -- Data processing, register.
299 shift
= 28, mask
= 1,
301 shift
= 24, mask
= 1,
304 shift
= 21, mask
= 1,
305 [0] = map_addsubsh
, map_addsubex
309 shift
= 21, mask
= 15,
310 [0] = map_addsubc
, false, map_ccomp
, false, map_csel
, false,
312 shift
= 30, mask
= 1,
313 [0] = map_data2s
, map_data1s
315 false, map_data3s
, map_data3s
, map_data3s
, map_data3s
, map_data3s
,
316 map_data3s
, map_data3s
, map_data3s
320 local map_lrl
= { -- Load register, literal.
321 shift
= 26, mask
= 1,
323 shift
= 30, mask
= 3,
324 [0] = "ldrDwB", "ldrDxB", "ldrswDxB"
327 shift
= 30, mask
= 3,
328 [0] = "ldrDsB", "ldrDdB"
332 local map_lsriind
= { -- Load/store register, immediate pre/post-indexed.
333 shift
= 30, mask
= 3,
335 shift
= 26, mask
= 1,
337 shift
= 22, mask
= 3,
338 [0] = "strbDwzL", "ldrbDwzL", "ldrsbDxzL", "ldrsbDwzL"
342 shift
= 26, mask
= 1,
344 shift
= 22, mask
= 3,
345 [0] = "strhDwzL", "ldrhDwzL", "ldrshDxzL", "ldrshDwzL"
349 shift
= 26, mask
= 1,
351 shift
= 22, mask
= 3,
352 [0] = "strDwzL", "ldrDwzL", "ldrswDxzL"
355 shift
= 22, mask
= 3,
356 [0] = "strDszL", "ldrDszL"
360 shift
= 26, mask
= 1,
362 shift
= 22, mask
= 3,
363 [0] = "strDxzL", "ldrDxzL"
366 shift
= 22, mask
= 3,
367 [0] = "strDdzL", "ldrDdzL"
373 shift
= 21, mask
= 1,
374 [0] = { -- Load/store register immediate.
375 shift
= 10, mask
= 3,
376 [0] = { -- Unscaled immediate.
377 shift
= 26, mask
= 1,
379 shift
= 30, mask
= 3,
381 shift
= 22, mask
= 3,
382 [0] = "sturbDwK", "ldurbDwK"
385 shift
= 22, mask
= 3,
386 [0] = "sturhDwK", "ldurhDwK"
389 shift
= 22, mask
= 3,
390 [0] = "sturDwK", "ldurDwK"
393 shift
= 22, mask
= 3,
394 [0] = "sturDxK", "ldurDxK"
397 }, map_lsriind
, false, map_lsriind
399 { -- Load/store register, register offset.
400 shift
= 10, mask
= 3,
402 shift
= 26, mask
= 1,
404 shift
= 30, mask
= 3,
406 shift
= 22, mask
= 3,
407 [0] = "strbDwO", "ldrbDwO", "ldrsbDxO", "ldrsbDwO"
410 shift
= 22, mask
= 3,
411 [0] = "strhDwO", "ldrhDwO", "ldrshDxO", "ldrshDwO"
414 shift
= 22, mask
= 3,
415 [0] = "strDwO", "ldrDwO", "ldrswDxO"
418 shift
= 22, mask
= 3,
419 [0] = "strDxO", "ldrDxO"
423 shift
= 30, mask
= 3,
425 shift
= 22, mask
= 3,
426 [0] = "strDsO", "ldrDsO"
429 shift
= 22, mask
= 3,
430 [0] = "strDdO", "ldrDdO"
437 local map_lsp
= { -- Load/store register pair, offset.
438 shift
= 22, mask
= 1,
440 shift
= 30, mask
= 3,
442 shift
= 26, mask
= 1,
443 [0] = "stpDzAzwP", "stpDzAzsP",
446 shift
= 26, mask
= 1,
450 shift
= 26, mask
= 1,
455 shift
= 30, mask
= 3,
457 shift
= 26, mask
= 1,
458 [0] = "ldpDzAzwP", "ldpDzAzsP",
461 shift
= 26, mask
= 1,
462 [0] = "ldpswDAxP", "ldpDzAzdP"
465 shift
= 26, mask
= 1,
471 local map_ls
= { -- Loads and stores.
472 shift
= 24, mask
= 0x31,
473 [0x10] = map_lrl
, [0x30] = map_lsriro
,
475 shift
= 23, mask
= 3,
476 map_lsp
, map_lsp
, map_lsp
479 shift
= 23, mask
= 3,
480 map_lsp
, map_lsp
, map_lsp
483 shift
= 26, mask
= 1,
485 shift
= 30, mask
= 3,
487 shift
= 22, mask
= 3,
488 [0] = "strbDwzU", "ldrbDwzU"
491 shift
= 22, mask
= 3,
492 [0] = "strhDwzU", "ldrhDwzU"
495 shift
= 22, mask
= 3,
496 [0] = "strDwzU", "ldrDwzU"
499 shift
= 22, mask
= 3,
500 [0] = "strDxzU", "ldrDxzU"
504 shift
= 30, mask
= 3,
506 shift
= 22, mask
= 3,
507 [0] = "strDszU", "ldrDszU"
510 shift
= 22, mask
= 3,
511 [0] = "strDdzU", "ldrDdzU"
517 local map_datafp
= { -- Data processing, SIMD and FP.
518 shift
= 28, mask
= 7,
520 shift
= 24, mask
= 1,
522 shift
= 21, mask
= 1,
524 shift
= 10, mask
= 3,
526 shift
= 12, mask
= 1,
528 shift
= 13, mask
= 1,
530 shift
= 14, mask
= 1,
532 shift
= 15, mask
= 1,
533 [0] = { -- FP/int conversion.
534 shift
= 31, mask
= 1,
536 shift
= 16, mask
= 0xff,
537 [0x20] = "fcvtnsDwNs", [0x21] = "fcvtnuDwNs",
538 [0x22] = "scvtfDsNw", [0x23] = "ucvtfDsNw",
539 [0x24] = "fcvtasDwNs", [0x25] = "fcvtauDwNs",
540 [0x26] = "fmovDwNs", [0x27] = "fmovDsNw",
541 [0x28] = "fcvtpsDwNs", [0x29] = "fcvtpuDwNs",
542 [0x30] = "fcvtmsDwNs", [0x31] = "fcvtmuDwNs",
543 [0x38] = "fcvtzsDwNs", [0x39] = "fcvtzuDwNs",
544 [0x60] = "fcvtnsDwNd", [0x61] = "fcvtnuDwNd",
545 [0x62] = "scvtfDdNw", [0x63] = "ucvtfDdNw",
546 [0x64] = "fcvtasDwNd", [0x65] = "fcvtauDwNd",
547 [0x68] = "fcvtpsDwNd", [0x69] = "fcvtpuDwNd",
548 [0x70] = "fcvtmsDwNd", [0x71] = "fcvtmuDwNd",
549 [0x78] = "fcvtzsDwNd", [0x79] = "fcvtzuDwNd"
552 shift
= 16, mask
= 0xff,
553 [0x20] = "fcvtnsDxNs", [0x21] = "fcvtnuDxNs",
554 [0x22] = "scvtfDsNx", [0x23] = "ucvtfDsNx",
555 [0x24] = "fcvtasDxNs", [0x25] = "fcvtauDxNs",
556 [0x28] = "fcvtpsDxNs", [0x29] = "fcvtpuDxNs",
557 [0x30] = "fcvtmsDxNs", [0x31] = "fcvtmuDxNs",
558 [0x38] = "fcvtzsDxNs", [0x39] = "fcvtzuDxNs",
559 [0x60] = "fcvtnsDxNd", [0x61] = "fcvtnuDxNd",
560 [0x62] = "scvtfDdNx", [0x63] = "ucvtfDdNx",
561 [0x64] = "fcvtasDxNd", [0x65] = "fcvtauDxNd",
562 [0x66] = "fmovDxNd", [0x67] = "fmovDdNx",
563 [0x68] = "fcvtpsDxNd", [0x69] = "fcvtpuDxNd",
564 [0x70] = "fcvtmsDxNd", [0x71] = "fcvtmuDxNd",
565 [0x78] = "fcvtzsDxNd", [0x79] = "fcvtzuDxNd"
569 { -- FP data-processing, 1 source.
570 shift
= 31, mask
= 1,
572 shift
= 22, mask
= 3,
574 shift
= 15, mask
= 63,
575 [0] = "fmovDNf", "fabsDNf", "fnegDNf",
576 "fsqrtDNf", false, "fcvtDdNs", false, false,
577 "frintnDNf", "frintpDNf", "frintmDNf", "frintzDNf",
578 "frintaDNf", false, "frintxDNf", "frintiDNf",
581 shift
= 15, mask
= 63,
582 [0] = "fmovDNf", "fabsDNf", "fnegDNf",
583 "fsqrtDNf", "fcvtDsNd", false, false, false,
584 "frintnDNf", "frintpDNf", "frintmDNf", "frintzDNf",
585 "frintaDNf", false, "frintxDNf", "frintiDNf",
591 shift
= 31, mask
= 1,
593 shift
= 14, mask
= 3,
595 shift
= 23, mask
= 1,
597 shift
= 0, mask
= 31,
598 [0] = "fcmpNMf", [8] = "fcmpNZf",
599 [16] = "fcmpeNMf", [24] = "fcmpeNZf",
606 shift
= 31, mask
= 1,
608 shift
= 5, mask
= 31,
610 shift
= 23, mask
= 1,
616 { -- FP conditional compare.
617 shift
= 31, mask
= 1,
619 shift
= 23, mask
= 1,
622 [0] = "fccmpNMVCf", "fccmpeNMVCf"
626 { -- FP data-processing, 2 sources.
627 shift
= 31, mask
= 1,
629 shift
= 23, mask
= 1,
631 shift
= 12, mask
= 15,
632 [0] = "fmulDNMf", "fdivDNMf", "faddDNMf", "fsubDNMf",
633 "fmaxDNMf", "fminDNMf", "fmaxnmDNMf", "fminnmDNMf",
638 { -- FP conditional select.
639 shift
= 31, mask
= 1,
641 shift
= 23, mask
= 1,
647 { -- FP data-processing, 3 sources.
648 shift
= 31, mask
= 1,
650 shift
= 15, mask
= 1,
652 shift
= 21, mask
= 5,
653 [0] = "fmaddDNMAf", "fnmaddDNMAf"
656 shift
= 21, mask
= 5,
657 [0] = "fmsubDNMAf", "fnmsubDNMAf"
664 local map_br
= { -- Branches, exception generating and system instructions.
665 shift
= 29, mask
= 7,
667 { -- Compare & branch, immediate.
668 shift
= 24, mask
= 3,
669 [0] = "cbzDBg", "cbnzDBg", "tbzDTBw", "tbnzDTBw"
671 { -- Conditional branch, immediate.
672 shift
= 24, mask
= 3,
676 shift
= 0, mask
= 15,
677 [0] = "beqB", "bneB", "bhsB", "bloB", "bmiB", "bplB", "bvsB", "bvcB",
678 "bhiB", "blsB", "bgeB", "bltB", "bgtB", "bleB", "balB"
682 { -- Compare & branch, immediate.
683 shift
= 24, mask
= 3,
684 [0] = "cbzDBg", "cbnzDBg", "tbzDTBx", "tbnzDTBx"
687 shift
= 24, mask
= 3,
688 [0] = { -- Exception generation.
689 shift
= 0, mask
= 0xe0001f,
692 { -- System instructions.
693 shift
= 0, mask
= 0x3fffff,
696 { -- Unconditional branch, register.
697 shift
= 0, mask
= 0xfffc1f,
698 [0x1f0000] = "brNx", [0x3f0000] = "blrNx",
705 shift
= 25, mask
= 15,
706 [0] = false, false, false, false, map_ls
, map_datar
, map_ls
, map_datafp
,
707 map_datai
, map_datai
, map_br
, map_br
, map_ls
, map_datar
, map_ls
, map_datafp
710 ------------------------------------------------------------------------------
712 local map_regs
= { x
= {}, w
= {}, d
= {}, s
= {} }
715 map_regs
.x
[i
] = "x"..i
716 map_regs
.w
[i
] = "w"..i
717 map_regs
.d
[i
] = "d"..i
718 map_regs
.s
[i
] = "s"..i
720 map_regs
.x
[31] = "sp"
721 map_regs
.w
[31] = "wsp"
722 map_regs
.d
[31] = "d31"
723 map_regs
.s
[31] = "s31"
726 [0] = "eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc",
727 "hi", "ls", "ge", "lt", "gt", "le", "al",
730 local map_shift
= { [0] = "lsl", "lsr", "asr", "ror"}
733 [0] = "uxtb", "uxth", "uxtw", "uxtx", "sxtb", "sxth", "sxtw", "sxtx",
736 ------------------------------------------------------------------------------
738 -- Output a nicely formatted line with an opcode and operands.
739 local function putop(ctx
, text
, operands
)
743 local sym
= ctx
.symtab
[ctx
.rel
]
748 if ctx
.hexdump
> 0 then
749 ctx
.out(format("%08x %s %-5s %s%s\n",
750 ctx
.addr
+pos
, tohex(ctx
.op
), text
, concat(operands
, ", "), extra
))
752 ctx
.out(format("%08x %-5s %s%s\n",
753 ctx
.addr
+pos
, text
, concat(operands
, ", "), extra
))
758 -- Fallback for unknown opcodes.
759 local function unknown(ctx
)
760 return putop(ctx
, ".long", { "0x"..tohex(ctx
.op
) })
763 local function match_reg(p
, pat
, regnum
)
764 return map_regs
[match(pat
, p
.."%w-([xwds])")][regnum
]
767 local function fmt_hex32(x
)
771 return format("%x", x
)
775 local imm13_rep
= { 0x55555555, 0x11111111, 0x01010101, 0x00010001, 0x00000001 }
777 local function decode_imm13(op
)
778 local imms
= band(rshift(op
, 10), 63)
779 local immr
= band(rshift(op
, 16), 63)
780 if band(op
, 0x00400000) == 0 then
783 if imms
>= 60 then len
= 1 else len
= 2 end
784 elseif imms
>= 48 then len
= 3 elseif imms
>= 32 then len
= 4 end
785 local l
= lshift(1, len
)-1
786 local s
= band(imms
, l
)
787 local r
= band(immr
, l
)
788 local imm
= ror(rshift(-1, 31-s
), r
)
789 if len
~= 5 then imm
= band(imm
, lshift(1, l
)-1) + rshift(imm
, 31-l
) end
790 imm
= imm
* imm13_rep
[len
]
791 local ix
= fmt_hex32(imm
)
792 if rshift(op
, 31) ~= 0 then
793 return ix
..tohex(imm
)
799 if imms
< 32 then lo
= rshift(-1, 31-imms
) else hi
= rshift(-1, 63-imms
) end
801 lo
, hi
= ror(lo
, immr
), ror(hi
, immr
)
802 local x
= immr
== 32 and 0 or band(bxor(lo
, hi
), lshift(-1, 32-immr
))
803 lo
, hi
= bxor(lo
, x
), bxor(hi
, x
)
804 if immr
>= 32 then lo
, hi
= hi
, lo
end
807 return fmt_hex32(hi
)..tohex(lo
)
814 local function parse_immpc(op
, name
)
815 if name
== "b" or name
== "bl" then
816 return arshift(lshift(op
, 6), 4)
817 elseif name
== "adr" or name
== "adrp" then
818 local immlo
= band(rshift(op
, 29), 3)
819 local immhi
= lshift(arshift(lshift(op
, 8), 13), 2)
820 return bor(immhi
, immlo
)
821 elseif name
== "tbz" or name
== "tbnz" then
822 return lshift(arshift(lshift(op
, 13), 18), 2)
824 return lshift(arshift(lshift(op
, 8), 13), 2)
828 local function parse_fpimm8(op
)
829 local sign
= band(op
, 0x100000) == 0 and 1 or -1
830 local exp = bxor(rshift(arshift(lshift(op
, 12), 5), 24), 0x80) - 131
831 local frac
= 16+band(rshift(op
, 13), 15)
832 return sign
* frac
* 2^
exp
835 local function prefer_bfx(sf
, uns
, imms
, immr
)
836 if imms
< immr
or imms
== 31 or imms
== 63 then
840 if sf
== 0 and (imms
== 7 or imms
== 15) then
843 if sf
~= 0 and uns
== 0 and (imms
== 7 or imms
== 15 or imms
== 31) then
850 -- Disassemble a single instruction.
851 local function disass_ins(ctx
)
853 local b0
, b1
, b2
, b3
= byte(ctx
.code
, pos
+1, pos
+4)
854 local op
= bor(lshift(b3
, 24), lshift(b2
, 16), lshift(b1
, 8), b0
)
857 local last
, name
, pat
863 opat
= map_init
[band(rshift(op
, 25), 15)]
864 while type(opat
) ~= "string" do
865 if not opat
then return unknown(ctx
) end
866 opat
= opat
[band(rshift(op
, opat
.shift
), opat
.mask
)] or opat
._
868 name
, pat
= match(opat
, "^([a-z0-9]*)(.*)")
869 local altname
, pat2
= match(pat
, "|([a-z0-9_.|]*)(.*)")
870 if altname
then pat
= pat2
end
871 if sub(pat
, 1, 1) == "." then
872 local s2
, p2
= match(pat
, "^([a-z0-9.]*)(.*)")
877 local rt
= match(pat
, "[gf]")
880 map_reg
= band(op
, 0x80000000) ~= 0 and map_regs
.x
or map_regs
.w
882 map_reg
= band(op
, 0x400000) ~= 0 and map_regs
.d
or map_regs
.s
888 for p
in gmatch(pat
, ".") do
891 local regnum
= band(op
, 31)
892 x
= rt
and map_reg
[regnum
] or match_reg(p
, pat
, regnum
)
894 local regnum
= band(rshift(op
, 5), 31)
895 x
= rt
and map_reg
[regnum
] or match_reg(p
, pat
, regnum
)
897 local regnum
= band(rshift(op
, 16), 31)
898 x
= rt
and map_reg
[regnum
] or match_reg(p
, pat
, regnum
)
900 local regnum
= band(rshift(op
, 10), 31)
901 x
= rt
and map_reg
[regnum
] or match_reg(p
, pat
, regnum
)
903 local addr
= ctx
.addr
+ pos
+ parse_immpc(op
, name
)
905 x
= "0x"..tohex(addr
)
907 x
= bor(band(rshift(op
, 26), 32), band(rshift(op
, 19), 31))
911 x
= map_cond
[band(rshift(op
, 12), 15)]
913 local rn
= band(rshift(op
, 5), 31)
914 local rm
= band(rshift(op
, 16), 31)
915 local cond
= band(rshift(op
, 12), 15)
916 local invc
= bxor(cond
, 1)
918 if altname
and cond
~= 14 and cond
~= 15 then
919 local a1
, a2
= match(altname
, "([^|]*)|(.*)")
925 if a1
then name
= a1
else name
= altname
end
933 x
= band(rshift(op
, 5), 0xffff)
935 x
= band(rshift(op
, 5), 0xffff)
936 local hw
= band(rshift(op
, 21), 3)
937 if altname
and (hw
== 0 or x
~= 0) then
941 local rn
= map_regs
.x
[band(rshift(op
, 5), 31)]
942 local imm9
= arshift(lshift(op
, 11), 23)
943 if band(op
, 0x800) ~= 0 then
944 x
= "["..rn
..", #"..imm9
.."]!"
946 x
= "["..rn
.."], #"..imm9
949 local rn
= map_regs
.x
[band(rshift(op
, 5), 31)]
950 local sz
= band(rshift(op
, 30), 3)
951 local imm12
= lshift(rshift(lshift(op
, 10), 20), sz
)
953 x
= "["..rn
..", #"..imm12
.."]"
958 local rn
= map_regs
.x
[band(rshift(op
, 5), 31)]
959 local imm9
= arshift(lshift(op
, 11), 23)
961 x
= "["..rn
..", #"..imm9
.."]"
966 local rn
, rm
= map_regs
.x
[band(rshift(op
, 5), 31)]
967 local m
= band(rshift(op
, 13), 1)
969 rm
= map_regs
.w
[band(rshift(op
, 16), 31)]
971 rm
= map_regs
.x
[band(rshift(op
, 16), 31)]
973 x
= "["..rn
..", "..rm
974 local opt
= band(rshift(op
, 13), 7)
975 local s
= band(rshift(op
, 12), 1)
976 local sz
= band(rshift(op
, 30), 3)
977 -- extension to be applied
979 if s
== 0 then x
= x
.."]"
980 else x
= x
..", lsl #"..sz
.."]" end
981 elseif opt
== 2 or opt
== 6 or opt
== 7 then
982 if s
== 0 then x
= x
..", "..map_extend
[opt
].."]"
983 else x
= x
..", "..map_extend
[opt
].." #"..sz
.."]" end
988 local sh
= 2 + rshift(op
, 31 - band(rshift(op
, 26), 1))
989 local imm7
= lshift(arshift(lshift(op
, 10), 25), sh
)
990 local rn
= map_regs
.x
[band(rshift(op
, 5), 31)]
991 local ind
= band(rshift(op
, 23), 3)
993 x
= "["..rn
.."], #"..imm7
998 x
= "["..rn
..", #"..imm7
.."]"
1000 elseif ind
== 3 then
1001 x
= "["..rn
..", #"..imm7
.."]!"
1003 elseif p
== "I" then
1004 local shf
= band(rshift(op
, 22), 3)
1005 local imm12
= band(rshift(op
, 10), 0x0fff)
1006 local rn
, rd
= band(rshift(op
, 5), 31), band(op
, 31)
1007 if altname
== "mov" and shf
== 0 and imm12
== 0 and (rn
== 31 or rd
== 31) then
1010 elseif shf
== 0 then
1012 elseif shf
== 1 then
1013 x
= imm12
..", lsl #12"
1015 elseif p
== "i" then
1016 x
= "#0x"..decode_imm13(op
)
1017 elseif p
== "1" then
1018 immr
= band(rshift(op
, 16), 63)
1020 elseif p
== "2" then
1021 x
= band(rshift(op
, 10), 63)
1023 local a1
, a2
, a3
, a4
, a5
, a6
=
1024 match(altname
, "([^|]*)|([^|]*)|([^|]*)|([^|]*)|([^|]*)|(.*)")
1025 local sf
= band(rshift(op
, 26), 32)
1026 local uns
= band(rshift(op
, 30), 1)
1027 if prefer_bfx(sf
, uns
, x
, immr
) then
1030 elseif immr
== 0 and x
== 7 then
1034 operands
[n
-1] = gsub(operands
[n
-1], "x", "w")
1036 last
= operands
[n
-1]
1039 elseif immr
== 0 and x
== 15 then
1043 operands
[n
-1] = gsub(operands
[n
-1], "x", "w")
1045 last
= operands
[n
-1]
1048 elseif x
== 31 or x
== 63 then
1049 if x
== 31 and immr
== 0 and name
== "sbfm" then
1054 operands
[n
-1] = gsub(operands
[n
-1], "x", "w")
1056 last
= operands
[n
-1]
1061 elseif band(x
, 31) ~= 31 and immr
== x
+1 and name
== "ubfm" then
1063 last
= "#"..(sf
+32 - immr
)
1064 operands
[#operands
] = last
1066 elseif x
< immr
then
1068 last
= "#"..(sf
+32 - immr
)
1069 operands
[#operands
] = last
1073 elseif p
== "3" then
1074 x
= band(rshift(op
, 10), 63)
1076 local a1
, a2
= match(altname
, "([^|]*)|(.*)")
1079 local sf
= band(rshift(op
, 26), 32)
1080 last
= "#"..(sf
+32 - immr
)
1081 operands
[#operands
] = last
1088 elseif p
== "4" then
1089 x
= band(rshift(op
, 10), 63)
1090 local rn
= band(rshift(op
, 5), 31)
1091 local rm
= band(rshift(op
, 16), 31)
1092 if altname
and rn
== rm
then
1095 last
= operands
[n
-1]
1098 elseif p
== "5" then
1099 x
= band(rshift(op
, 16), 31)
1100 elseif p
== "S" then
1101 x
= band(rshift(op
, 10), 63)
1102 if x
== 0 then x
= nil
1103 else x
= map_shift
[band(rshift(op
, 22), 3)].." #"..x
end
1104 elseif p
== "X" then
1105 local opt
= band(rshift(op
, 13), 7)
1106 -- Width specifier <R>.
1107 if opt
~= 3 and opt
~= 7 then
1108 last
= map_regs
.w
[band(rshift(op
, 16), 31)]
1109 operands
[#operands
] = last
1111 x
= band(rshift(op
, 10), 7)
1113 if opt
== 2 + band(rshift(op
, 31), 1) and
1114 band(rshift(op
, second0
and 5 or 0), 31) == 31 then
1115 if x
== 0 then x
= nil
1116 else x
= "lsl #"..x
end
1118 if x
== 0 then x
= map_extend
[band(rshift(op
, 13), 7)]
1119 else x
= map_extend
[band(rshift(op
, 13), 7)].." #"..x
end
1121 elseif p
== "R" then
1122 x
= band(rshift(op
,21), 3)
1123 if x
== 0 then x
= nil
1124 else x
= "lsl #"..x
*16 end
1125 elseif p
== "z" then
1127 if operands
[n
] == "sp" then operands
[n
] = "xzr"
1128 elseif operands
[n
] == "wsp" then operands
[n
] = "wzr"
1130 elseif p
== "Z" then
1132 elseif p
== "F" then
1133 x
= parse_fpimm8(op
)
1134 elseif p
== "g" or p
== "f" or p
== "x" or p
== "w" or
1135 p
== "d" or p
== "s" then
1136 -- These are handled in D/N/M/A.
1137 elseif p
== "0" then
1138 if last
== "sp" or last
== "wsp" then
1141 last
= operands
[n
-1]
1143 local a1
, a2
= match(altname
, "([^|]*)|(.*)")
1147 name
, altname
= a2
, a1
1149 name
, altname
= a1
, a2
1159 if type(x
) == "number" then x
= "#"..x
end
1160 operands
[#operands
+1] = x
1164 return putop(ctx
, name
..suffix
, operands
)
1167 ------------------------------------------------------------------------------
1169 -- Disassemble a block of code.
1170 local function disass_block(ctx
, ofs
, len
)
1171 if not ofs
then ofs
= 0 end
1172 local stop
= len
and ofs
+len
or #ctx
.code
1175 while ctx
.pos
< stop
do disass_ins(ctx
) end
1178 -- Extended API: create a disassembler context. Then call ctx:disass(ofs, len).
1179 local function create(code
, addr
, out
)
1182 ctx
.addr
= addr
or 0
1183 ctx
.out
= out
or io
.write
1185 ctx
.disass
= disass_block
1190 -- Simple API: disassemble code (a string) at address and output via out.
1191 local function disass(code
, addr
, out
)
1192 create(code
, addr
, out
):disass()
1195 -- Return register name for RID.
1196 local function regname(r
)
1197 if r
< 32 then return map_regs
.x
[r
] end
1198 return map_regs
.d
[r
-32]
1201 -- Public module functions.