1----------------------------------------------------------------------------
2-- LuaJIT ARM64 disassembler module.
3--
4-- Copyright (C) 2005-2017 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
16local type = type
17local sub, byte, format = string.sub, string.byte, string.format
18local match, gmatch, gsub = string.match, string.gmatch, string.gsub
19local concat = table.concat
20local bit = require("bit")
21local band, bor, bxor, tohex = bit.band, bit.bor, bit.bxor, bit.tohex
22local lshift, rshift, arshift = bit.lshift, bit.rshift, bit.arshift
23local ror = bit.ror
24
25------------------------------------------------------------------------------
26-- Opcode maps
27------------------------------------------------------------------------------
28
29local map_adr = { -- PC-relative addressing.
30  shift = 31, mask = 1,
31  [0] = "adrDBx", "adrpDBx"
32}
33
34local map_addsubi = { -- Add/subtract immediate.
35  shift = 29, mask = 3,
36  [0] = "add|movDNIg", "adds|cmnD0NIg", "subDNIg", "subs|cmpD0NIg",
37}
38
39local 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
55local 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
70local 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
92local 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
103local 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
157local 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
207local map_addsubsh = { -- Add/subtract, shifted register.
208  shift = 22, mask = 3,
209  [0] = map_assh, map_assh, map_assh
210}
211
212local 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
220local 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
228local 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
243local 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
258local 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
273local 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
282local 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
306local 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
328local 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
340local 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
380local 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
445local 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
479local 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
525local 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
672local 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
712local 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
720local map_regs = { x = {}, w = {}, d = {}, s = {} }
721
722for 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
727end
728map_regs.x[31] = "sp"
729map_regs.w[31] = "wsp"
730map_regs.d[31] = "d31"
731map_regs.s[31] = "s31"
732
733local map_cond = {
734  [0] = "eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc",
735  "hi", "ls", "ge", "lt", "gt", "le", "al",
736}
737
738local map_shift = { [0] = "lsl", "lsr", "asr", }
739
740local 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.
747local 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
764end
765
766-- Fallback for unknown opcodes.
767local function unknown(ctx)
768  return putop(ctx, ".long", { "0x"..tohex(ctx.op) })
769end
770
771local function match_reg(p, pat, regnum)
772  return map_regs[match(pat, p.."%w-([xwds])")][regnum]
773end
774
775local function fmt_hex32(x)
776  if x < 0 then
777    return tohex(x)
778  else
779    return format("%x", x)
780  end
781end
782
783local imm13_rep = { 0x55555555, 0x11111111, 0x01010101, 0x00010001, 0x00000001 }
784
785local 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
820end
821
822local 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
834end
835
836local 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
841end
842
843local 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
856end
857
858-- Disassemble a single instruction.
859local 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)
1174end
1175
1176------------------------------------------------------------------------------
1177
1178-- Disassemble a block of code.
1179local 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
1185end
1186
1187-- Extended API: create a disassembler context. Then call ctx:disass(ofs, len).
1188local 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
1197end
1198
1199-- Simple API: disassemble code (a string) at address and output via out.
1200local function disass(code, addr, out)
1201  create(code, addr, out):disass()
1202end
1203
1204-- Return register name for RID.
1205local function regname(r)
1206  if r < 32 then return map_regs.x[r] end
1207  return map_regs.d[r-32]
1208end
1209
1210-- Public module functions.
1211return {
1212  create = create,
1213  disass = disass,
1214  regname = regname
1215}
1216
1217