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