isa.c (52959B)
1 /* AArch64 instruction descriptor table + operand print/parse dispatch. 2 * 3 * The table mirrors the inline encoders in aa64_isa.h: each row records 4 * (mnemonic, match, mask, format, flags) so the disassembler can identify 5 * a raw 32-bit word with one mask-and-compare and then dispatch on the 6 * format to extract operand fields via the same unpack functions the 7 * encoder uses. Encoder and decoder share the bit knowledge — when an 8 * opcode value or field position changes, both sides update at one site. 9 * 10 * Mask values include the family mask plus the bits that distinguish a 11 * specific instruction from its siblings in the same family. sf (bit 31) 12 * is intentionally a don't-care for formats where both 32- and 64-bit 13 * forms share one row; the unpacker reads sf separately when printing 14 * operands. 15 * 16 * Row ordering: first-match wins. Aliases (rows with AA64_ASMFL_ALIAS) 17 * are tighter masks placed BEFORE the canonical row they alias so the 18 * disassembler renders the alias spelling. The assembler accepts both 19 * spellings — they map to the same encoded word. */ 20 21 #include "arch/aa64/isa.h" 22 23 #include <stddef.h> 24 25 /* Mnemonic Slice literal for a static table row (compile-time length). */ 26 #define MN(s) {{(s)}, sizeof(s) - 1} 27 28 /* Load/store unsigned-imm12 opc field values for the V=0 signed sub-word 29 * loads. opc=00/01 are STR/LDR (AA64_LDST_OPC_STR/LDR in isa.h); the signed 30 * loads reuse the same size/imm12 layout but sign-extend the loaded value: 31 * opc=10 (LDRS_X) sign-extends to the 64-bit X destination, 32 * opc=11 (LDRS_W) sign-extends to the 32-bit W destination. */ 33 #define AA64_LDST_OPC_LDRS_X 2u 34 #define AA64_LDST_OPC_LDRS_W 3u 35 36 static const AA64SysRegName aa64_sysreg_names[] = { 37 {"tpidr_el0", 3, 3, 13, 0, 2}, {"tpidrro_el0", 3, 3, 13, 0, 3}, 38 {"tpidr_el1", 3, 0, 13, 0, 4}, {"midr_el1", 3, 0, 0, 0, 0}, 39 {"mpidr_el1", 3, 0, 0, 0, 5}, {"ctr_el0", 3, 3, 0, 0, 1}, 40 {"dczid_el0", 3, 3, 0, 0, 7}, {"nzcv", 3, 3, 4, 2, 0}, 41 {"daif", 3, 3, 4, 2, 1}, {"fpcr", 3, 3, 4, 4, 0}, 42 {"fpsr", 3, 3, 4, 4, 1}, 43 }; 44 45 static char aa64_lc(char c) { 46 return (c >= 'A' && c <= 'Z') ? (char)(c + ('a' - 'A')) : c; 47 } 48 49 static int aa64_icase_eq(const char* a, size_t an, const char* b) { 50 size_t i = 0; 51 for (; i < an && b[i]; ++i) { 52 if (aa64_lc(a[i]) != aa64_lc(b[i])) return 0; 53 } 54 return i == an && b[i] == '\0'; 55 } 56 57 static int aa64_sysreg_rd_field(const char* s, size_t n, size_t* i, u32* out) { 58 u32 v = 0; 59 if (*i >= n || s[*i] < '0' || s[*i] > '9') return 0; 60 while (*i < n && s[*i] >= '0' && s[*i] <= '9') { 61 v = v * 10u + (u32)(s[*i] - '0'); 62 if (v > 15u) return 0; 63 ++(*i); 64 } 65 *out = v; 66 return 1; 67 } 68 69 static int aa64_parse_generic_sysreg(const char* s, size_t n, u32* op0, 70 u32* op1, u32* crn, u32* crm, u32* op2) { 71 size_t i = 0; 72 if (i >= n || aa64_lc(s[i]) != 's') return 0; 73 ++i; 74 if (!aa64_sysreg_rd_field(s, n, &i, op0)) return 0; 75 if (i >= n || s[i] != '_') return 0; 76 ++i; 77 if (!aa64_sysreg_rd_field(s, n, &i, op1)) return 0; 78 if (i + 1 >= n || s[i] != '_' || aa64_lc(s[i + 1]) != 'c') return 0; 79 i += 2; 80 if (!aa64_sysreg_rd_field(s, n, &i, crn)) return 0; 81 if (i + 1 >= n || s[i] != '_' || aa64_lc(s[i + 1]) != 'c') return 0; 82 i += 2; 83 if (!aa64_sysreg_rd_field(s, n, &i, crm)) return 0; 84 if (i >= n || s[i] != '_') return 0; 85 ++i; 86 if (!aa64_sysreg_rd_field(s, n, &i, op2)) return 0; 87 return i == n && *op0 <= 3u && *op1 <= 7u && *crn <= 15u && *crm <= 15u && 88 *op2 <= 7u; 89 } 90 91 int aa64_sysreg_by_name(const char* s, size_t n, u32* op0, u32* op1, u32* crn, 92 u32* crm, u32* op2) { 93 size_t k; 94 if (!s || !n) return 0; 95 for (k = 0; k < sizeof aa64_sysreg_names / sizeof aa64_sysreg_names[0]; ++k) { 96 if (aa64_icase_eq(s, n, aa64_sysreg_names[k].name)) { 97 *op0 = aa64_sysreg_names[k].op0; 98 *op1 = aa64_sysreg_names[k].op1; 99 *crn = aa64_sysreg_names[k].crn; 100 *crm = aa64_sysreg_names[k].crm; 101 *op2 = aa64_sysreg_names[k].op2; 102 return 1; 103 } 104 } 105 return aa64_parse_generic_sysreg(s, n, op0, op1, crn, crm, op2); 106 } 107 108 const char* aa64_sysreg_name(u32 op0, u32 op1, u32 crn, u32 crm, u32 op2) { 109 size_t k; 110 for (k = 0; k < sizeof aa64_sysreg_names / sizeof aa64_sysreg_names[0]; ++k) { 111 const AA64SysRegName* r = &aa64_sysreg_names[k]; 112 if (r->op0 == op0 && r->op1 == op1 && r->crn == crn && r->crm == crm && 113 r->op2 == op2) 114 return r->name; 115 } 116 return NULL; 117 } 118 119 /* Canonical 4-bit condition-code index -> mnemonic suffix. Single source 120 * of truth for the disassembler (b.<cond>, CSEL aliases) and the 121 * codegen/assembler emit path. Order is the architectural cond encoding: 122 * EQ=0, NE=1, CS=2, CC=3, ... AL=14, NV=15. */ 123 static const char* aa64_cond_names[16] = { 124 "eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc", 125 "hi", "ls", "ge", "lt", "gt", "le", "al", "nv", 126 }; 127 128 const char* aa64_cond_name(u32 cond) { return aa64_cond_names[cond & 0xfu]; } 129 130 int aa64_cond_from_name(const char* s, size_t n, u32* out) { 131 u32 c; 132 if (!s) return 0; 133 /* Aliases: HS (higher-or-same, unsigned) is CS=2; LO (lower, unsigned) 134 * is CC=3. NV=15 is intentionally not accepted as an input spelling. */ 135 if (aa64_icase_eq(s, n, "hs")) { 136 *out = 2u; 137 return 1; 138 } 139 if (aa64_icase_eq(s, n, "lo")) { 140 *out = 3u; 141 return 1; 142 } 143 for (c = 0; c < 15u; ++c) { 144 if (aa64_icase_eq(s, n, aa64_cond_names[c])) { 145 *out = c; 146 return 1; 147 } 148 } 149 return 0; 150 } 151 152 const AA64InsnDesc aa64_insn_table[] = { 153 /* ----- Move-wide immediate (MOVN / MOVZ / MOVK) ----- */ 154 {MN("movn"), 0x12800000u, 0x7F800000u, AA64_FMT_MOVEWIDE, 0, {0, 0}}, 155 {MN("movz"), 0x52800000u, 0x7F800000u, AA64_FMT_MOVEWIDE, 0, {0, 0}}, 156 {MN("movk"), 0x72800000u, 0x7F800000u, AA64_FMT_MOVEWIDE, 0, {0, 0}}, 157 158 /* ----- Logical, shifted register ----- 159 * Alias MOV Rd, Rm is ORR Rd, ZR, Rm with shift=0, imm6=0. The mask 160 * pins Rn (bits 9:5) to 11111 (ZR) and shift/imm6 to 0 so only the 161 * MOV spelling matches; broader ORR rows below catch the rest. */ 162 {MN("mov"), 163 0x2A0003E0u, 164 0x7FE0FFE0u, 165 AA64_FMT_LOG_SR, 166 AA64_ASMFL_ALIAS, 167 {0, 0}}, 168 /* MVN Rd, Rm ≡ ORN Rd, ZR, Rm (logical N=1, Rn=ZR, no shift) */ 169 {MN("mvn"), 170 0x2A2003E0u, 171 0x7FE0FFE0u, 172 AA64_FMT_LOG_SR, 173 AA64_ASMFL_ALIAS, 174 {0, 0}}, 175 {MN("and"), 0x0A000000u, 0x7F200000u, AA64_FMT_LOG_SR, 0, {0, 0}}, 176 {MN("bic"), 0x0A200000u, 0x7F200000u, AA64_FMT_LOG_SR, 0, {0, 0}}, 177 {MN("orr"), 0x2A000000u, 0x7F200000u, AA64_FMT_LOG_SR, 0, {0, 0}}, 178 {MN("orn"), 0x2A200000u, 0x7F200000u, AA64_FMT_LOG_SR, 0, {0, 0}}, 179 {MN("eor"), 0x4A000000u, 0x7F200000u, AA64_FMT_LOG_SR, 0, {0, 0}}, 180 {MN("eon"), 0x4A200000u, 0x7F200000u, AA64_FMT_LOG_SR, 0, {0, 0}}, 181 {MN("ands"), 0x6A000000u, 0x7F200000u, AA64_FMT_LOG_SR, 0, {0, 0}}, 182 {MN("bics"), 0x6A200000u, 0x7F200000u, AA64_FMT_LOG_SR, 0, {0, 0}}, 183 184 /* ----- Add/Sub, shifted register ----- 185 * NEG Rd, Rm ≡ SUB Rd, ZR, Rm (Rn=ZR, shift=0, imm6=0). */ 186 {MN("neg"), 187 0x4B0003E0u, 188 0x7FE0FFE0u, 189 AA64_FMT_ADDSUB_SR, 190 AA64_ASMFL_ALIAS, 191 {0, 0}}, 192 {MN("negs"), 193 0x6B0003E0u, 194 0x7FE0FFE0u, 195 AA64_FMT_ADDSUB_SR, 196 AA64_ASMFL_ALIAS, 197 {0, 0}}, 198 /* CMP Rn, Rm ≡ SUBS ZR, Rn, Rm. */ 199 {MN("cmp"), 200 0x6B00001Fu, 201 0x7F20001Fu, 202 AA64_FMT_ADDSUB_SR, 203 AA64_ASMFL_ALIAS, 204 {0, 0}}, 205 /* CMN Rn, Rm ≡ ADDS ZR, Rn, Rm. */ 206 {MN("cmn"), 207 0x2B00001Fu, 208 0x7F20001Fu, 209 AA64_FMT_ADDSUB_SR, 210 AA64_ASMFL_ALIAS, 211 {0, 0}}, 212 {MN("add"), 0x0B000000u, 0x7F200000u, AA64_FMT_ADDSUB_SR, 0, {0, 0}}, 213 {MN("adds"), 0x2B000000u, 0x7F200000u, AA64_FMT_ADDSUB_SR, 0, {0, 0}}, 214 {MN("sub"), 0x4B000000u, 0x7F200000u, AA64_FMT_ADDSUB_SR, 0, {0, 0}}, 215 {MN("subs"), 0x6B000000u, 0x7F200000u, AA64_FMT_ADDSUB_SR, 0, {0, 0}}, 216 217 /* ----- Data-processing 3-source ----- 218 * MUL Rd, Rn, Rm ≡ MADD Rd, Rn, Rm, ZR (Ra=ZR, op31=0, o0=0). */ 219 {MN("mul"), 220 0x1B007C00u, 221 0x7FE0FC00u, 222 AA64_FMT_DP3, 223 AA64_ASMFL_ALIAS, 224 {0, 0}}, 225 /* MNEG Rd, Rn, Rm ≡ MSUB Rd, Rn, Rm, ZR. */ 226 {MN("mneg"), 227 0x1B00FC00u, 228 0x7FE0FC00u, 229 AA64_FMT_DP3, 230 AA64_ASMFL_ALIAS, 231 {0, 0}}, 232 {MN("madd"), 0x1B000000u, 0x7FE08000u, AA64_FMT_DP3, 0, {0, 0}}, 233 {MN("msub"), 0x1B008000u, 0x7FE08000u, AA64_FMT_DP3, 0, {0, 0}}, 234 235 /* ----- Data-processing 2-source ----- */ 236 {MN("udiv"), 0x1AC00800u, 0x5FE0FC00u, AA64_FMT_DP2, 0, {0, 0}}, 237 {MN("sdiv"), 0x1AC00C00u, 0x5FE0FC00u, AA64_FMT_DP2, 0, {0, 0}}, 238 {MN("lslv"), 0x1AC02000u, 0x5FE0FC00u, AA64_FMT_DP2, 0, {0, 0}}, 239 {MN("lsrv"), 0x1AC02400u, 0x5FE0FC00u, AA64_FMT_DP2, 0, {0, 0}}, 240 {MN("asrv"), 0x1AC02800u, 0x5FE0FC00u, AA64_FMT_DP2, 0, {0, 0}}, 241 {MN("rorv"), 0x1AC02C00u, 0x5FE0FC00u, AA64_FMT_DP2, 0, {0, 0}}, 242 243 /* ----- Conditional select ----- 244 * CSET Rd, cond ≡ CSINC Rd, ZR, ZR, invert(cond). 245 * CSETM Rd, cond ≡ CSINV Rd, ZR, ZR, invert(cond). 246 * These aliases are expressible as fixed Rn/Rm=ZR masks, so put them 247 * before the canonical conditional-select rows. */ 248 {MN("cset"), 249 0x1A9F07E0u, 250 0x7FE00C00u | (0x1Fu << 16) | (0x1Fu << 5), 251 AA64_FMT_CONDSEL, 252 AA64_ASMFL_ALIAS, 253 {0, 0}}, 254 {MN("csetm"), 255 0x5A9F03E0u, 256 0x7FE00C00u | (0x1Fu << 16) | (0x1Fu << 5), 257 AA64_FMT_CONDSEL, 258 AA64_ASMFL_ALIAS, 259 {0, 0}}, 260 {MN("csel"), 0x1A800000u, 0x7FE00C00u, AA64_FMT_CONDSEL, 0, {0, 0}}, 261 {MN("csinc"), 0x1A800400u, 0x7FE00C00u, AA64_FMT_CONDSEL, 0, {0, 0}}, 262 {MN("csinv"), 0x5A800000u, 0x7FE00C00u, AA64_FMT_CONDSEL, 0, {0, 0}}, 263 {MN("csneg"), 0x5A800400u, 0x7FE00C00u, AA64_FMT_CONDSEL, 0, {0, 0}}, 264 265 /* ----- Unconditional branch (register) ----- 266 * RET aliases its no-operand spelling to RET X30 (Rn=11110). The 267 * tighter row matches when Rn=30 and prints "ret" without operands; 268 * the looser row below catches RET Xn for other Rn. */ 269 {MN("ret"), 270 0xD65F03C0u, 271 0xFFFFFFFFu, 272 AA64_FMT_BR_REG, 273 AA64_ASMFL_ALIAS | AA64_ASMFL_NORN, 274 {0, 0}}, 275 {MN("br"), 0xD61F0000u, 0xFFFFFC1Fu, AA64_FMT_BR_REG, 0, {0, 0}}, 276 {MN("blr"), 0xD63F0000u, 0xFFFFFC1Fu, AA64_FMT_BR_REG, 0, {0, 0}}, 277 {MN("ret"), 0xD65F0000u, 0xFFFFFC1Fu, AA64_FMT_BR_REG, 0, {0, 0}}, 278 279 /* ----- PC-relative addressing ----- */ 280 {MN("adr"), 0x10000000u, 0x9F000000u, AA64_FMT_PCREL_ADR, 0, {0, 0}}, 281 {MN("adrp"), 0x90000000u, 0x9F000000u, AA64_FMT_PCREL_ADR, 0, {0, 0}}, 282 283 /* ----- Add/Sub immediate ----- 284 * CMP/CMN immediate are the Rd=ZR aliases of SUBS/ADDS immediate. */ 285 {MN("cmn"), 286 0x3100001Fu, 287 0x7F00001Fu, 288 AA64_FMT_ADDSUB_IMM, 289 AA64_ASMFL_ALIAS, 290 {0, 0}}, 291 {MN("cmp"), 292 0x7100001Fu, 293 0x7F00001Fu, 294 AA64_FMT_ADDSUB_IMM, 295 AA64_ASMFL_ALIAS, 296 {0, 0}}, 297 {MN("add"), 0x11000000u, 0x7F000000u, AA64_FMT_ADDSUB_IMM, 0, {0, 0}}, 298 {MN("adds"), 0x31000000u, 0x7F000000u, AA64_FMT_ADDSUB_IMM, 0, {0, 0}}, 299 {MN("sub"), 0x51000000u, 0x7F000000u, AA64_FMT_ADDSUB_IMM, 0, {0, 0}}, 300 {MN("subs"), 0x71000000u, 0x7F000000u, AA64_FMT_ADDSUB_IMM, 0, {0, 0}}, 301 302 /* ----- Load/store, unsigned 12-bit immediate (scaled) ----- 303 * Mask: family bits 29:27 + 25:24 + size(31:30) + V(26) + opc(23:22). 304 * 305 * Signed sub-word loads (LDRSB/LDRSH/LDRSW) share this exact format but 306 * select opc=10 (sign-extend to X) or opc=11 (sign-extend to W). They 307 * are listed BEFORE the unsigned STR/LDR rows so first-match-wins picks 308 * the signed spelling (the masks are disjoint by opc, so the relative 309 * order is not load-bearing, but keeping them first documents intent). 310 * size=00 opc=10 LDRSB Xt ; size=00 opc=11 LDRSB Wt 311 * size=01 opc=10 LDRSH Xt ; size=01 opc=11 LDRSH Wt 312 * size=10 opc=10 LDRSW Xt (no Wt form: that opc=11 slot is PRFUM). */ 313 {MN("ldrsb"), 0x39800000u, 0xFFC00000u, AA64_FMT_LDST_UIMM, 0, {0, 0}}, 314 {MN("ldrsb"), 0x39C00000u, 0xFFC00000u, AA64_FMT_LDST_UIMM, 0, {0, 0}}, 315 {MN("ldrsh"), 0x79800000u, 0xFFC00000u, AA64_FMT_LDST_UIMM, 0, {0, 0}}, 316 {MN("ldrsh"), 0x79C00000u, 0xFFC00000u, AA64_FMT_LDST_UIMM, 0, {0, 0}}, 317 {MN("ldrsw"), 0xB9800000u, 0xFFC00000u, AA64_FMT_LDST_UIMM, 0, {0, 0}}, 318 {MN("strb"), 0x39000000u, 0xFFC00000u, AA64_FMT_LDST_UIMM, 0, {0, 0}}, 319 {MN("ldrb"), 0x39400000u, 0xFFC00000u, AA64_FMT_LDST_UIMM, 0, {0, 0}}, 320 {MN("strh"), 0x79000000u, 0xFFC00000u, AA64_FMT_LDST_UIMM, 0, {0, 0}}, 321 {MN("ldrh"), 0x79400000u, 0xFFC00000u, AA64_FMT_LDST_UIMM, 0, {0, 0}}, 322 {MN("str"), 0xB9000000u, 0xFFC00000u, AA64_FMT_LDST_UIMM, 0, {0, 0}}, /* 32 323 */ 324 {MN("ldr"), 0xB9400000u, 0xFFC00000u, AA64_FMT_LDST_UIMM, 0, {0, 0}}, 325 {MN("str"), 326 0xF9000000u, 327 0xFFC00000u, 328 AA64_FMT_LDST_UIMM, 329 AA64_ASMFL_SF1, 330 {0, 0}}, /* 64 */ 331 {MN("ldr"), 332 0xF9400000u, 333 0xFFC00000u, 334 AA64_FMT_LDST_UIMM, 335 AA64_ASMFL_SF1, 336 {0, 0}}, 337 /* SIMD/FP scaled loads/stores (V=1). size 0..2 select B/H/S; size=3 338 * selects D; the 128-bit Q form uses size=00 with opc bit 1 set and 339 * is not yet emitted by codegen. */ 340 {MN("str"), 0x3D000000u, 0xFFC00000u, AA64_FMT_LDST_UIMM, 0, {0, 0}}, /* B 341 */ 342 {MN("ldr"), 0x3D400000u, 0xFFC00000u, AA64_FMT_LDST_UIMM, 0, {0, 0}}, 343 {MN("str"), 0x7D000000u, 0xFFC00000u, AA64_FMT_LDST_UIMM, 0, {0, 0}}, /* H 344 */ 345 {MN("ldr"), 0x7D400000u, 0xFFC00000u, AA64_FMT_LDST_UIMM, 0, {0, 0}}, 346 {MN("str"), 0xBD000000u, 0xFFC00000u, AA64_FMT_LDST_UIMM, 0, {0, 0}}, /* S 347 */ 348 {MN("ldr"), 0xBD400000u, 0xFFC00000u, AA64_FMT_LDST_UIMM, 0, {0, 0}}, 349 {MN("str"), 350 0xFD000000u, 351 0xFFC00000u, 352 AA64_FMT_LDST_UIMM, 353 AA64_ASMFL_SF1, 354 {0, 0}}, /* D */ 355 {MN("ldr"), 356 0xFD400000u, 357 0xFFC00000u, 358 AA64_FMT_LDST_UIMM, 359 AA64_ASMFL_SF1, 360 {0, 0}}, 361 362 /* ----- Load/store, unscaled signed 9-bit immediate (LDUR/STUR) ----- 363 * V=0 first, V=1 next. Per-row mask narrows size+V+opc; family mask 364 * pins the high family bits + the SIMM9-vs-other-variant selector. */ 365 {MN("sturb"), 0x38000000u, 0xFFE00C00u, AA64_FMT_LDST_SIMM9, 0, {0, 0}}, 366 {MN("ldurb"), 0x38400000u, 0xFFE00C00u, AA64_FMT_LDST_SIMM9, 0, {0, 0}}, 367 {MN("sturh"), 0x78000000u, 0xFFE00C00u, AA64_FMT_LDST_SIMM9, 0, {0, 0}}, 368 {MN("ldurh"), 0x78400000u, 0xFFE00C00u, AA64_FMT_LDST_SIMM9, 0, {0, 0}}, 369 /* Signed unscaled loads (opc=10 → 64-bit Xt, opc=11 → 32-bit Wt). The 370 * printer keys the register width on opc; size selects the mnemonic. */ 371 {MN("ldursb"), 0x38800000u, 0xFFE00C00u, AA64_FMT_LDST_SIMM9, 0, {0, 0}}, 372 {MN("ldursb"), 0x38C00000u, 0xFFE00C00u, AA64_FMT_LDST_SIMM9, 0, {0, 0}}, 373 {MN("ldursh"), 0x78800000u, 0xFFE00C00u, AA64_FMT_LDST_SIMM9, 0, {0, 0}}, 374 {MN("ldursh"), 0x78C00000u, 0xFFE00C00u, AA64_FMT_LDST_SIMM9, 0, {0, 0}}, 375 {MN("ldursw"), 0xB8800000u, 0xFFE00C00u, AA64_FMT_LDST_SIMM9, 0, {0, 0}}, 376 {MN("stur"), 377 0xB8000000u, 378 0xFFE00C00u, 379 AA64_FMT_LDST_SIMM9, 380 0, 381 {0, 0}}, /* 32 */ 382 {MN("ldur"), 0xB8400000u, 0xFFE00C00u, AA64_FMT_LDST_SIMM9, 0, {0, 0}}, 383 {MN("stur"), 384 0xF8000000u, 385 0xFFE00C00u, 386 AA64_FMT_LDST_SIMM9, 387 AA64_ASMFL_SF1, 388 {0, 0}}, 389 {MN("ldur"), 390 0xF8400000u, 391 0xFFE00C00u, 392 AA64_FMT_LDST_SIMM9, 393 AA64_ASMFL_SF1, 394 {0, 0}}, 395 {MN("stur"), 396 0x3C000000u, 397 0xFFE00C00u, 398 AA64_FMT_LDST_SIMM9, 399 0, 400 {0, 0}}, /* B */ 401 {MN("ldur"), 0x3C400000u, 0xFFE00C00u, AA64_FMT_LDST_SIMM9, 0, {0, 0}}, 402 {MN("stur"), 403 0x7C000000u, 404 0xFFE00C00u, 405 AA64_FMT_LDST_SIMM9, 406 0, 407 {0, 0}}, /* H */ 408 {MN("ldur"), 0x7C400000u, 0xFFE00C00u, AA64_FMT_LDST_SIMM9, 0, {0, 0}}, 409 {MN("stur"), 410 0xBC000000u, 411 0xFFE00C00u, 412 AA64_FMT_LDST_SIMM9, 413 0, 414 {0, 0}}, /* S */ 415 {MN("ldur"), 0xBC400000u, 0xFFE00C00u, AA64_FMT_LDST_SIMM9, 0, {0, 0}}, 416 {MN("stur"), 417 0xFC000000u, 418 0xFFE00C00u, 419 AA64_FMT_LDST_SIMM9, 420 AA64_ASMFL_SF1, 421 {0, 0}}, /* D */ 422 {MN("ldur"), 423 0xFC400000u, 424 0xFFE00C00u, 425 AA64_FMT_LDST_SIMM9, 426 AA64_ASMFL_SF1, 427 {0, 0}}, 428 429 /* ----- Load/store exclusive + acquire/release ordered ----- 430 * Family bits[29:24]=001000, o1[21]=0 (single register). The word/dword 431 * mnemonics leave size bit30 free (mask 0xBFE08000) so one row decodes 432 * both Wt and Xt; the byte/half mnemonics pin the full size (0xFFE08000). 433 * print_ldst_excl keys the register width on size and the operand shape on 434 * L[22]/o2[23]. */ 435 {MN("ldxr"), 0x88400000u, 0xBFE08000u, AA64_FMT_LDST_EXCL, 0, {0, 0}}, 436 {MN("ldaxr"), 0x88408000u, 0xBFE08000u, AA64_FMT_LDST_EXCL, 0, {0, 0}}, 437 {MN("ldar"), 0x88C08000u, 0xBFE08000u, AA64_FMT_LDST_EXCL, 0, {0, 0}}, 438 {MN("stxr"), 0x88000000u, 0xBFE08000u, AA64_FMT_LDST_EXCL, 0, {0, 0}}, 439 {MN("stlxr"), 0x88008000u, 0xBFE08000u, AA64_FMT_LDST_EXCL, 0, {0, 0}}, 440 {MN("stlr"), 0x88808000u, 0xBFE08000u, AA64_FMT_LDST_EXCL, 0, {0, 0}}, 441 {MN("ldxrb"), 0x08400000u, 0xFFE08000u, AA64_FMT_LDST_EXCL, 0, {0, 0}}, 442 {MN("ldxrh"), 0x48400000u, 0xFFE08000u, AA64_FMT_LDST_EXCL, 0, {0, 0}}, 443 {MN("stxrb"), 0x08000000u, 0xFFE08000u, AA64_FMT_LDST_EXCL, 0, {0, 0}}, 444 {MN("stxrh"), 0x48000000u, 0xFFE08000u, AA64_FMT_LDST_EXCL, 0, {0, 0}}, 445 {MN("ldaxrb"), 0x08408000u, 0xFFE08000u, AA64_FMT_LDST_EXCL, 0, {0, 0}}, 446 {MN("ldaxrh"), 0x48408000u, 0xFFE08000u, AA64_FMT_LDST_EXCL, 0, {0, 0}}, 447 {MN("ldarb"), 0x08C08000u, 0xFFE08000u, AA64_FMT_LDST_EXCL, 0, {0, 0}}, 448 {MN("ldarh"), 0x48C08000u, 0xFFE08000u, AA64_FMT_LDST_EXCL, 0, {0, 0}}, 449 {MN("stlxrb"), 0x08008000u, 0xFFE08000u, AA64_FMT_LDST_EXCL, 0, {0, 0}}, 450 {MN("stlxrh"), 0x48008000u, 0xFFE08000u, AA64_FMT_LDST_EXCL, 0, {0, 0}}, 451 {MN("stlrb"), 0x08808000u, 0xFFE08000u, AA64_FMT_LDST_EXCL, 0, {0, 0}}, 452 {MN("stlrh"), 0x48808000u, 0xFFE08000u, AA64_FMT_LDST_EXCL, 0, {0, 0}}, 453 454 /* ----- Load/store pair, pre-indexed (opc=10 X / opc=01 D) ----- */ 455 {MN("stp"), 456 0xA9800000u, 457 0xFFC00000u, 458 AA64_FMT_LDSTP_PRE, 459 AA64_ASMFL_SF1, 460 {0, 0}}, 461 {MN("ldp"), 462 0xA9C00000u, 463 0xFFC00000u, 464 AA64_FMT_LDSTP_PRE, 465 AA64_ASMFL_SF1, 466 {0, 0}}, 467 {MN("stp"), 0x6D800000u, 0xFFC00000u, AA64_FMT_LDSTP_PRE, 0, {0, 0}}, /* D 468 */ 469 {MN("ldp"), 0x6DC00000u, 0xFFC00000u, AA64_FMT_LDSTP_PRE, 0, {0, 0}}, 470 {MN("stp"), 471 0xAD800000u, 472 0xFFC00000u, 473 AA64_FMT_LDSTP_PRE, 474 AA64_ASMFL_SF1, 475 {0, 0}}, /* Q */ 476 {MN("ldp"), 477 0xADC00000u, 478 0xFFC00000u, 479 AA64_FMT_LDSTP_PRE, 480 AA64_ASMFL_SF1, 481 {0, 0}}, 482 483 /* ----- Load/store pair, signed-offset ----- */ 484 {MN("stp"), 485 0xA9000000u, 486 0xFFC00000u, 487 AA64_FMT_LDSTP_SOFF, 488 AA64_ASMFL_SF1, 489 {0, 0}}, 490 {MN("ldp"), 491 0xA9400000u, 492 0xFFC00000u, 493 AA64_FMT_LDSTP_SOFF, 494 AA64_ASMFL_SF1, 495 {0, 0}}, 496 {MN("stp"), 0x6D000000u, 0xFFC00000u, AA64_FMT_LDSTP_SOFF, 0, {0, 0}}, /* D 497 */ 498 {MN("ldp"), 0x6D400000u, 0xFFC00000u, AA64_FMT_LDSTP_SOFF, 0, {0, 0}}, 499 {MN("stp"), 500 0xAD000000u, 501 0xFFC00000u, 502 AA64_FMT_LDSTP_SOFF, 503 AA64_ASMFL_SF1, 504 {0, 0}}, /* Q */ 505 {MN("ldp"), 506 0xAD400000u, 507 0xFFC00000u, 508 AA64_FMT_LDSTP_SOFF, 509 AA64_ASMFL_SF1, 510 {0, 0}}, 511 512 /* ----- Load/store pair, post-indexed (opc=10 X / opc=01 D) ----- */ 513 {MN("stp"), 514 0xA8800000u, 515 0xFFC00000u, 516 AA64_FMT_LDSTP_POST, 517 AA64_ASMFL_SF1, 518 {0, 0}}, 519 {MN("ldp"), 520 0xA8C00000u, 521 0xFFC00000u, 522 AA64_FMT_LDSTP_POST, 523 AA64_ASMFL_SF1, 524 {0, 0}}, 525 526 /* ----- Unconditional branch (immediate) ----- */ 527 {MN("b"), 0x14000000u, 0xFC000000u, AA64_FMT_BR_IMM, 0, {0, 0}}, 528 {MN("bl"), 0x94000000u, 0xFC000000u, AA64_FMT_BR_IMM, 0, {0, 0}}, 529 530 /* ----- Conditional branch (immediate) ----- */ 531 {MN("b.cond"), 0x54000000u, 0xFF000010u, AA64_FMT_BR_COND, 0, {0, 0}}, 532 533 /* ----- Compare-and-branch ----- */ 534 {MN("cbz"), 0x34000000u, 0x7F000000u, AA64_FMT_CB, 0, {0, 0}}, 535 {MN("cbnz"), 0x35000000u, 0x7F000000u, AA64_FMT_CB, 0, {0, 0}}, 536 537 /* ----- Exception generation ----- */ 538 {MN("svc"), 0xD4000001u, 0xFFE0001Fu, AA64_FMT_EXCEPT, 0, {0, 0}}, 539 {MN("brk"), 0xD4200000u, 0xFFE0001Fu, AA64_FMT_EXCEPT, 0, {0, 0}}, 540 {MN("hlt"), 0xD4400000u, 0xFFE0001Fu, AA64_FMT_EXCEPT, 0, {0, 0}}, 541 542 /* ----- Hint ----- */ 543 {MN("nop"), 0xD503201Fu, 0xFFFFFFFFu, AA64_FMT_HINT, 0, {0, 0}}, 544 545 /* ----- Memory barriers (DMB / DSB / ISB / CLREX) ----- 546 * Mask covers everything but CRm at bits[11:8]. */ 547 {MN("dmb"), 0xD50330BFu, 0xFFFFF0FFu, AA64_FMT_BARRIER, 0, {0, 0}}, 548 {MN("dsb"), 0xD503309Fu, 0xFFFFF0FFu, AA64_FMT_BARRIER, 0, {0, 0}}, 549 {MN("isb"), 0xD50330DFu, 0xFFFFF0FFu, AA64_FMT_BARRIER, 0, {0, 0}}, 550 {MN("clrex"), 0xD503305Fu, 0xFFFFF0FFu, AA64_FMT_BARRIER, 0, {0, 0}}, 551 552 /* ----- System-register move (MRS read / MSR write, register form) ----- 553 * The selector op0:op1:CRn:CRm:op2 and Rt decode from the word; the mask 554 * pins bits[31:20] (fixes L and op0's high bit), leaving the rest. */ 555 {MN("mrs"), 556 AA64_MRS_MATCH, 557 AA64_SYSREG_MOVE_MASK, 558 AA64_FMT_SYSREG, 559 0, 560 {0, 0}}, 561 {MN("msr"), 562 AA64_MSR_MATCH, 563 AA64_SYSREG_MOVE_MASK, 564 AA64_FMT_SYSREG, 565 0, 566 {0, 0}}, 567 568 /* ----- Data-processing (1 source): RBIT / REV16 / REV / CLZ ----- 569 * sf (bit31) free; opcode2 in bits[15:10] selects the operation. REV has 570 * a 32-bit (opcode2=000010) and 64-bit (000011) encoding, both "rev". */ 571 {MN("rbit"), 0x5AC00000u, 0x7FFFFC00u, AA64_FMT_DP1, 0, {0, 0}}, 572 {MN("rev16"), 0x5AC00400u, 0x7FFFFC00u, AA64_FMT_DP1, 0, {0, 0}}, 573 {MN("rev"), 0x5AC00800u, 0x7FFFFC00u, AA64_FMT_DP1, 0, {0, 0}}, 574 {MN("rev"), 0x5AC00C00u, 0x7FFFFC00u, AA64_FMT_DP1, 0, {0, 0}}, 575 {MN("clz"), 0x5AC01000u, 0x7FFFFC00u, AA64_FMT_DP1, 0, {0, 0}}, 576 577 /* ----- Bitfield move (SBFM / UBFM) ----- 578 * sf/N free (read for W/X); opc (bits30:29) selects sbfm vs ubfm. Aliases 579 * (lsl/lsr/asr/ubfx/sxtb/...) are selected by the bitfield printer. */ 580 {MN("sbfm"), 0x13000000u, 0x7F800000u, AA64_FMT_BITFIELD, 0, {0, 0}}, 581 {MN("ubfm"), 0x53000000u, 0x7F800000u, AA64_FMT_BITFIELD, 0, {0, 0}}, 582 583 /* ----- Logical, immediate (AND / ORR / EOR / ANDS, bitmask form) ----- 584 * sf (bit31) and the N:immr:imms bitmask fields free; opc (bits30:29) 585 * selects the operation. Family bits[28:23]=100100. */ 586 {MN("and"), 0x12000000u, 0x7F800000u, AA64_FMT_LOG_IMM, 0, {0, 0}}, 587 {MN("orr"), 0x32000000u, 0x7F800000u, AA64_FMT_LOG_IMM, 0, {0, 0}}, 588 {MN("eor"), 0x52000000u, 0x7F800000u, AA64_FMT_LOG_IMM, 0, {0, 0}}, 589 {MN("ands"), 0x72000000u, 0x7F800000u, AA64_FMT_LOG_IMM, 0, {0, 0}}, 590 591 /* ----- Load/store, register offset [Xn, Xm{, LSL #s}] (V=0) ----- 592 * Family bits[29:24]=111000, bit21=1, bits[11:10]=10; per-size opc rows. */ 593 {MN("strb"), 0x38200800u, 0xFFE00C00u, AA64_FMT_LDST_REGOFF, 0, {0, 0}}, 594 {MN("ldrb"), 0x38600800u, 0xFFE00C00u, AA64_FMT_LDST_REGOFF, 0, {0, 0}}, 595 {MN("strh"), 0x78200800u, 0xFFE00C00u, AA64_FMT_LDST_REGOFF, 0, {0, 0}}, 596 {MN("ldrh"), 0x78600800u, 0xFFE00C00u, AA64_FMT_LDST_REGOFF, 0, {0, 0}}, 597 {MN("str"), 0xB8200800u, 0xFFE00C00u, AA64_FMT_LDST_REGOFF, 0, {0, 0}}, 598 {MN("ldr"), 0xB8600800u, 0xFFE00C00u, AA64_FMT_LDST_REGOFF, 0, {0, 0}}, 599 {MN("str"), 0xF8200800u, 0xFFE00C00u, AA64_FMT_LDST_REGOFF, 0, {0, 0}}, 600 {MN("ldr"), 0xF8600800u, 0xFFE00C00u, AA64_FMT_LDST_REGOFF, 0, {0, 0}}, 601 /* Signed sub-word loads share the format but use opc=10 (Xt) / opc=11 (Wt) 602 * — the destination width comes from opc, not size (handled in 603 * print_ldst_regoff). size=10 opc=10 is LDRSW (no Wt form; that opc=11 slot 604 * is PRFM, left undecoded). */ 605 {MN("ldrsb"), 0x38A00800u, 0xFFE00C00u, AA64_FMT_LDST_REGOFF, 0, {0, 0}}, 606 {MN("ldrsb"), 0x38E00800u, 0xFFE00C00u, AA64_FMT_LDST_REGOFF, 0, {0, 0}}, 607 {MN("ldrsh"), 0x78A00800u, 0xFFE00C00u, AA64_FMT_LDST_REGOFF, 0, {0, 0}}, 608 {MN("ldrsh"), 0x78E00800u, 0xFFE00C00u, AA64_FMT_LDST_REGOFF, 0, {0, 0}}, 609 {MN("ldrsw"), 0xB8A00800u, 0xFFE00C00u, AA64_FMT_LDST_REGOFF, 0, {0, 0}}, 610 611 /* ----- FP data-processing (2 source): FMUL / FDIV / FADD / FSUB ----- 612 * ftype (bits23:22) free, read for the s/d/h register prefix. */ 613 {MN("fmul"), 0x1E200800u, 0xFF20FC00u, AA64_FMT_FP_DP2, 0, {0, 0}}, 614 {MN("fdiv"), 0x1E201800u, 0xFF20FC00u, AA64_FMT_FP_DP2, 0, {0, 0}}, 615 {MN("fadd"), 0x1E202800u, 0xFF20FC00u, AA64_FMT_FP_DP2, 0, {0, 0}}, 616 {MN("fsub"), 0x1E203800u, 0xFF20FC00u, AA64_FMT_FP_DP2, 0, {0, 0}}, 617 {MN("fmax"), 0x1E204800u, 0xFF20FC00u, AA64_FMT_FP_DP2, 0, {0, 0}}, 618 {MN("fmin"), 0x1E205800u, 0xFF20FC00u, AA64_FMT_FP_DP2, 0, {0, 0}}, 619 {MN("fnmul"), 0x1E208800u, 0xFF20FC00u, AA64_FMT_FP_DP2, 0, {0, 0}}, 620 621 /* ----- FP compare (FCMP, register form) ----- */ 622 {MN("fcmp"), 0x1E202000u, 0xFF20FC1Fu, AA64_FMT_FP_CMP, 0, {0, 0}}, 623 624 /* ----- FP precision convert (FCVT single<->double<->half) ----- */ 625 {MN("fcvt"), 0x1E224000u, 0xFF3E7C00u, AA64_FMT_FP_CVT, 0, {0, 0}}, 626 627 /* ----- FP data-processing (1 source): FMOV / FABS / FNEG / FSQRT ----- */ 628 {MN("fmov"), 0x1E204000u, 0xFF3FFC00u, AA64_FMT_FP_DP1, 0, {0, 0}}, 629 {MN("fabs"), 0x1E20C000u, 0xFF3FFC00u, AA64_FMT_FP_DP1, 0, {0, 0}}, 630 {MN("fneg"), 0x1E214000u, 0xFF3FFC00u, AA64_FMT_FP_DP1, 0, {0, 0}}, 631 {MN("fsqrt"), 0x1E21C000u, 0xFF3FFC00u, AA64_FMT_FP_DP1, 0, {0, 0}}, 632 633 /* ----- FP<->int convert + FMOV gpr<->fp ----- 634 * sf (bit31) and ftype free; opcode (bits20:16) selects op + direction. */ 635 {MN("scvtf"), 0x1E220000u, 0x7F3FFC00u, AA64_FMT_FP_INT_CVT, 0, {0, 0}}, 636 {MN("ucvtf"), 0x1E230000u, 0x7F3FFC00u, AA64_FMT_FP_INT_CVT, 0, {0, 0}}, 637 {MN("fcvtzs"), 0x1E380000u, 0x7F3FFC00u, AA64_FMT_FP_INT_CVT, 0, {0, 0}}, 638 {MN("fcvtzu"), 0x1E390000u, 0x7F3FFC00u, AA64_FMT_FP_INT_CVT, 0, {0, 0}}, 639 {MN("fmov"), 0x1E260000u, 0x7F3FFC00u, AA64_FMT_FP_INT_CVT, 0, {0, 0}}, 640 {MN("fmov"), 0x1E270000u, 0x7F3FFC00u, AA64_FMT_FP_INT_CVT, 0, {0, 0}}, 641 }; 642 643 #undef MN 644 645 const u32 aa64_insn_table_n = 646 (u32)(sizeof aa64_insn_table / sizeof aa64_insn_table[0]); 647 648 const AA64InsnDesc* aa64_disasm_find(u32 word) { 649 for (u32 i = 0; i < aa64_insn_table_n; ++i) { 650 const AA64InsnDesc* d = &aa64_insn_table[i]; 651 if ((word & d->mask) == d->match) return d; 652 } 653 return NULL; 654 } 655 656 /* ===================================================================== 657 * Operand print — one helper per format. 658 * 659 * Format choices for immediates: 660 * - branch displacements, signed add/sub imm, signed ldur/stur ofs: 661 * signed decimal. 662 * - MOVZ/MOVK halfword, logical bitmask, exception generation #imm: 663 * 0x-prefixed hex. 664 * 665 * Register naming: ZR alias for x31 in places where the encoding treats 666 * Rd/Rn=31 as the zero register (logical/arith), SP where it treats 31 667 * as the stack pointer (add/sub imm, ldr/str-uimm Rn, ldp/stp Rn). 668 * 669 * vaddr is folded into PC-relative branch operands when nonzero. */ 670 671 static void emit_reg(StrBuf* sb, u32 r, int sf, int sp_means_sp) { 672 if (r == 31u) { 673 if (sp_means_sp) 674 strbuf_puts(sb, "sp"); 675 else if (sf) 676 strbuf_puts(sb, "xzr"); 677 else 678 strbuf_puts(sb, "wzr"); 679 return; 680 } 681 strbuf_putc(sb, sf ? 'x' : 'w'); 682 strbuf_put_u64(sb, (u64)r); 683 } 684 685 static void emit_vreg(StrBuf* sb, u32 r, char prefix) { 686 strbuf_putc(sb, prefix); 687 strbuf_put_u64(sb, (u64)r); 688 } 689 690 static void emit_cond(StrBuf* sb, u32 cond) { 691 strbuf_puts(sb, aa64_cond_name(cond)); 692 } 693 694 /* Sign-extend an n-bit value held in the low bits of v to i64. */ 695 static i64 sext(u64 v, u32 nbits) { 696 u64 mask = (nbits >= 64u) ? ~0ull : ((1ull << nbits) - 1ull); 697 v &= mask; 698 u64 sign = (nbits == 0u) ? 0ull : (1ull << (nbits - 1u)); 699 if (v & sign) v |= ~mask; 700 return (i64)v; 701 } 702 703 static void print_movewide(StrBuf* sb, u32 w) { 704 AA64MoveWide f = aa64_movewide_unpack(w); 705 emit_reg(sb, f.Rd, (int)f.sf, /*sp_means_sp=*/0); 706 strbuf_puts(sb, ", "); 707 strbuf_put_hex_u64(sb, (u64)f.imm16); 708 if (f.hw) { 709 strbuf_puts(sb, ", lsl "); 710 strbuf_put_u64(sb, (u64)(f.hw * 16u)); 711 } 712 } 713 714 static void print_logsr(StrBuf* sb, u32 w, const AA64InsnDesc* d) { 715 AA64LogSR f = aa64_logsr_unpack(w); 716 if (d->flags & AA64_ASMFL_ALIAS) { 717 /* MOV / MVN: Rd, Rm */ 718 emit_reg(sb, f.Rd, (int)f.sf, 0); 719 strbuf_puts(sb, ", "); 720 emit_reg(sb, f.Rm, (int)f.sf, 0); 721 return; 722 } 723 emit_reg(sb, f.Rd, (int)f.sf, 0); 724 strbuf_puts(sb, ", "); 725 emit_reg(sb, f.Rn, (int)f.sf, 0); 726 strbuf_puts(sb, ", "); 727 emit_reg(sb, f.Rm, (int)f.sf, 0); 728 if (f.imm6 || f.shift) { 729 static const char* sh[4] = {"lsl", "lsr", "asr", "ror"}; 730 strbuf_puts(sb, ", "); 731 strbuf_puts(sb, sh[f.shift & 3u]); 732 strbuf_puts(sb, " #"); 733 strbuf_put_u64(sb, (u64)f.imm6); 734 } 735 } 736 737 static void print_addsubsr(StrBuf* sb, u32 w, const AA64InsnDesc* d) { 738 AA64AddSubSR f = aa64_addsubsr_unpack(w); 739 if (d->flags & AA64_ASMFL_ALIAS) { 740 /* NEG / NEGS / CMP / CMN. */ 741 if (d->mnemonic.s[0] == 'c') { 742 /* CMP / CMN — print Rn, Rm */ 743 emit_reg(sb, f.Rn, (int)f.sf, 0); 744 strbuf_puts(sb, ", "); 745 emit_reg(sb, f.Rm, (int)f.sf, 0); 746 } else { 747 /* NEG / NEGS — print Rd, Rm */ 748 emit_reg(sb, f.Rd, (int)f.sf, 0); 749 strbuf_puts(sb, ", "); 750 emit_reg(sb, f.Rm, (int)f.sf, 0); 751 } 752 return; 753 } 754 emit_reg(sb, f.Rd, (int)f.sf, 0); 755 strbuf_puts(sb, ", "); 756 emit_reg(sb, f.Rn, (int)f.sf, 0); 757 strbuf_puts(sb, ", "); 758 emit_reg(sb, f.Rm, (int)f.sf, 0); 759 if (f.imm6 || f.shift) { 760 static const char* sh[4] = {"lsl", "lsr", "asr", "rsv"}; 761 strbuf_puts(sb, ", "); 762 strbuf_puts(sb, sh[f.shift & 3u]); 763 strbuf_puts(sb, " #"); 764 strbuf_put_u64(sb, (u64)f.imm6); 765 } 766 } 767 768 static void print_dp3(StrBuf* sb, u32 w, const AA64InsnDesc* d) { 769 AA64DP3 f = aa64_dp3_unpack(w); 770 /* MUL / MNEG alias drop Ra (which is ZR for the alias). */ 771 if (d->flags & AA64_ASMFL_ALIAS) { 772 emit_reg(sb, f.Rd, (int)f.sf, 0); 773 strbuf_puts(sb, ", "); 774 emit_reg(sb, f.Rn, (int)f.sf, 0); 775 strbuf_puts(sb, ", "); 776 emit_reg(sb, f.Rm, (int)f.sf, 0); 777 return; 778 } 779 emit_reg(sb, f.Rd, (int)f.sf, 0); 780 strbuf_puts(sb, ", "); 781 emit_reg(sb, f.Rn, (int)f.sf, 0); 782 strbuf_puts(sb, ", "); 783 emit_reg(sb, f.Rm, (int)f.sf, 0); 784 strbuf_puts(sb, ", "); 785 emit_reg(sb, f.Ra, (int)f.sf, 0); 786 } 787 788 static void print_dp2(StrBuf* sb, u32 w) { 789 AA64DP2 f = aa64_dp2_unpack(w); 790 emit_reg(sb, f.Rd, (int)f.sf, 0); 791 strbuf_puts(sb, ", "); 792 emit_reg(sb, f.Rn, (int)f.sf, 0); 793 strbuf_puts(sb, ", "); 794 emit_reg(sb, f.Rm, (int)f.sf, 0); 795 } 796 797 static void print_condsel(StrBuf* sb, u32 w, const AA64InsnDesc* d) { 798 AA64CondSel f = aa64_condsel_unpack(w); 799 if (d->flags & AA64_ASMFL_ALIAS) { 800 /* CSET / CSETM: Rd, cond. Encoded condition is inverted. */ 801 emit_reg(sb, f.Rd, (int)f.sf, 0); 802 strbuf_puts(sb, ", "); 803 emit_cond(sb, f.cond ^ 1u); 804 return; 805 } 806 emit_reg(sb, f.Rd, (int)f.sf, 0); 807 strbuf_puts(sb, ", "); 808 emit_reg(sb, f.Rn, (int)f.sf, 0); 809 strbuf_puts(sb, ", "); 810 emit_reg(sb, f.Rm, (int)f.sf, 0); 811 strbuf_puts(sb, ", "); 812 emit_cond(sb, f.cond); 813 } 814 815 static void print_brreg(StrBuf* sb, u32 w, const AA64InsnDesc* d) { 816 AA64BrReg f = aa64_brreg_unpack(w); 817 if (d->flags & AA64_ASMFL_NORN) return; /* RET (with implicit X30) */ 818 emit_reg(sb, f.Rn, /*sf=*/1, 0); 819 } 820 821 static void print_pcrel(StrBuf* sb, u32 w, u64 vaddr) { 822 AA64PCRelAdr f = aa64_pcrel_adr_unpack(w); 823 emit_reg(sb, f.Rd, /*sf=*/1, 0); 824 strbuf_puts(sb, ", "); 825 i64 imm = sext(((u64)f.immhi << 2) | (u64)f.immlo, 21); 826 if (f.op == AA64_ADR_OP_ADRP) imm <<= 12; 827 if (vaddr) { 828 u64 base = (f.op == AA64_ADR_OP_ADRP) ? (vaddr & ~0xfffull) : vaddr; 829 strbuf_put_hex_u64(sb, base + (u64)imm); 830 } else { 831 strbuf_puts(sb, "#"); 832 strbuf_put_i64(sb, imm); 833 } 834 } 835 836 static void print_addsubimm(StrBuf* sb, u32 w) { 837 AA64AddSubImm f = aa64_addsubimm_unpack(w); 838 if (f.S && f.Rd == AA64_ZR) { 839 /* CMP/CMN immediate aliases: Rn|SP, #imm. */ 840 emit_reg(sb, f.Rn, (int)f.sf, 1); 841 strbuf_puts(sb, ", #"); 842 strbuf_put_u64(sb, (u64)f.imm12); 843 if (f.sh) strbuf_puts(sb, ", lsl #12"); 844 return; 845 } 846 /* For these encodings, Rd/Rn=31 means SP. */ 847 emit_reg(sb, f.Rd, (int)f.sf, 1); 848 strbuf_puts(sb, ", "); 849 emit_reg(sb, f.Rn, (int)f.sf, 1); 850 strbuf_puts(sb, ", #"); 851 strbuf_put_u64(sb, (u64)f.imm12); 852 if (f.sh) strbuf_puts(sb, ", lsl #12"); 853 } 854 855 static u32 ldst_log2_size(const AA64InsnDesc* d, u32 size_field) { 856 (void)d; 857 return size_field & 3u; 858 } 859 860 static void print_ldst_uimm(StrBuf* sb, u32 w, const AA64InsnDesc* d) { 861 AA64LdStUimm f = aa64_ldst_uimm_unpack(w); 862 u32 sz = ldst_log2_size(d, f.size); 863 /* Pick reg prefix: V=0 picks W/X; V=1 picks B/H/S/D by size. 864 * For V=0 the destination width follows opc: the signed sub-word loads 865 * (opc=10 LDRS_X, opc=11 LDRS_W) name an X or W register independent of 866 * the access size; the plain STR/LDR (opc<=01) use X only for size=11. */ 867 if (f.V == 0) { 868 int sf = (f.opc == AA64_LDST_OPC_LDRS_X) ? 1 869 : (f.opc == AA64_LDST_OPC_LDRS_W) ? 0 870 : (int)(sz == 3u); 871 emit_reg(sb, f.Rt, sf, 0); 872 } else { 873 char p = (sz == 0u) ? 'b' : (sz == 1u) ? 'h' : (sz == 2u) ? 's' : 'd'; 874 emit_vreg(sb, f.Rt, p); 875 } 876 strbuf_puts(sb, ", ["); 877 emit_reg(sb, f.Rn, /*sf=*/1, 1); 878 u32 byte_off = f.imm12 << sz; 879 if (byte_off) { 880 strbuf_puts(sb, ", #"); 881 strbuf_put_u64(sb, (u64)byte_off); 882 } 883 strbuf_putc(sb, ']'); 884 } 885 886 static void print_ldst_simm9(StrBuf* sb, u32 w, const AA64InsnDesc* d) { 887 AA64LdStSimm9 f = aa64_ldst_simm9_unpack(w); 888 u32 sz = f.size & 3u; 889 (void)d; 890 if (f.V == 0) { 891 /* opc 00/01 = STUR/LDUR (register width from size); opc 10/11 = signed 892 * load LDURS* (width from opc: 10 sign-extends to 64-bit Xt, 11 to 893 * 32-bit Wt). */ 894 int sf = (f.opc == 2u) ? 1 : (f.opc == 3u) ? 0 : (int)(sz == 3u); 895 emit_reg(sb, f.Rt, sf, 0); 896 } else { 897 char p = (sz == 0u) ? 'b' : (sz == 1u) ? 'h' : (sz == 2u) ? 's' : 'd'; 898 emit_vreg(sb, f.Rt, p); 899 } 900 strbuf_puts(sb, ", ["); 901 emit_reg(sb, f.Rn, /*sf=*/1, 1); 902 i64 off = sext((u64)f.imm9, 9); 903 if (off) { 904 strbuf_puts(sb, ", #"); 905 strbuf_put_i64(sb, off); 906 } 907 strbuf_putc(sb, ']'); 908 } 909 910 /* Load/store exclusive (LDXR/LDAXR/STXR/STLXR) and acquire/release ordered 911 * (LDAR/STLR), incl. byte/half variants. Fields: size[31:30] picks the 912 * transfer register width (Wt for byte/half/word, Xt for dword); o2[23] and 913 * L[22] select the form; Rs[20:16] is the store-exclusive status register 914 * (Ws). A store-exclusive (L=0, o2=0) prints `Ws, Rt, [Xn]`; everything else 915 * (loads + store-release) prints `Rt, [Xn]`. */ 916 static void print_ldst_excl(StrBuf* sb, u32 w) { 917 u32 size = (w >> 30) & 3u; 918 u32 o2 = (w >> 23) & 1u; 919 u32 L = (w >> 22) & 1u; 920 u32 Rs = (w >> 16) & 0x1fu; 921 u32 Rn = (w >> 5) & 0x1fu; 922 u32 Rt = w & 0x1fu; 923 int sf = (size == 3u); 924 int store_excl = (L == 0u && o2 == 0u); 925 if (store_excl) { 926 emit_reg(sb, Rs, /*sf=*/0, 0); /* Ws: status result, always 32-bit */ 927 strbuf_puts(sb, ", "); 928 } 929 emit_reg(sb, Rt, sf, 0); 930 strbuf_puts(sb, ", ["); 931 emit_reg(sb, Rn, /*sf=*/1, 1); 932 strbuf_putc(sb, ']'); 933 } 934 935 static void print_ldstp_common(StrBuf* sb, AA64LdStPPre f, int pre) { 936 /* opc=10 → 64-bit X; opc=00 → 32-bit W; opc=01 (V=1) → D (FP); 937 * opc=00 (V=1) → S; opc=10 (V=1) → Q (not yet emitted). */ 938 i64 scale; 939 int is_fp = (f.V == 1); 940 char fp_prefix = 's'; 941 int sf = 1; 942 if (is_fp) { 943 if (f.opc == 0) { 944 fp_prefix = 's'; 945 scale = 4; 946 } else if (f.opc == 1) { 947 fp_prefix = 'd'; 948 scale = 8; 949 } else { 950 fp_prefix = 'q'; 951 scale = 16; 952 } 953 } else { 954 sf = (f.opc == 2); 955 scale = sf ? 8 : 4; 956 } 957 if (is_fp) { 958 emit_vreg(sb, f.Rt, fp_prefix); 959 strbuf_puts(sb, ", "); 960 emit_vreg(sb, f.Rt2, fp_prefix); 961 } else { 962 emit_reg(sb, f.Rt, sf, 0); 963 strbuf_puts(sb, ", "); 964 emit_reg(sb, f.Rt2, sf, 0); 965 } 966 strbuf_puts(sb, ", ["); 967 emit_reg(sb, f.Rn, /*sf=*/1, 1); 968 i64 byte_off = sext((u64)f.imm7, 7) * scale; 969 if (byte_off) { 970 strbuf_puts(sb, ", #"); 971 strbuf_put_i64(sb, byte_off); 972 } 973 strbuf_putc(sb, ']'); 974 if (pre) strbuf_putc(sb, '!'); 975 } 976 977 static void print_ldstp_pre(StrBuf* sb, u32 w) { 978 print_ldstp_common(sb, aa64_ldstp_pre_unpack(w), /*pre=*/1); 979 } 980 static void print_ldstp_soff(StrBuf* sb, u32 w) { 981 print_ldstp_common(sb, aa64_ldstp_soff_unpack(w), /*pre=*/0); 982 } 983 984 /* Post-indexed reuses the pre/soff field layout (same encoder struct). The 985 * common printer renders `[Rn]` (no offset inside brackets) when pre=0; the 986 * "#imm, post" form is rendered by appending after the closing bracket. */ 987 static void print_ldstp_post(StrBuf* sb, u32 w) { 988 AA64LdStPPre f = aa64_ldstp_pre_unpack(w); /* same field layout */ 989 i64 scale = (f.V == 1) ? (f.opc == 0 ? 4 : (f.opc == 1 ? 8 : 16)) 990 : (f.opc == 2 ? 8 : 4); 991 /* Render Rt, Rt2, [Rn], #imm — same prefix as the soff form, then a 992 * post-bracket displacement. Reuse the common printer for the prefix 993 * by zeroing imm7 in a copy so it omits the `, #imm` inside `[..]`. */ 994 AA64LdStPPre stripped = f; 995 stripped.imm7 = 0; 996 print_ldstp_common(sb, stripped, /*pre=*/0); 997 i64 byte_off = sext((u64)f.imm7, 7) * scale; 998 if (byte_off) { 999 strbuf_puts(sb, ", #"); 1000 strbuf_put_i64(sb, byte_off); 1001 } 1002 } 1003 1004 static void print_br_imm(StrBuf* sb, u32 w, u64 vaddr) { 1005 AA64BrImm f = aa64_brimm_unpack(w); 1006 i64 ofs = sext((u64)f.imm26, 26) * 4; 1007 if (vaddr) { 1008 strbuf_put_hex_u64(sb, vaddr + (u64)ofs); 1009 } else { 1010 strbuf_puts(sb, "#"); 1011 strbuf_put_i64(sb, ofs); 1012 } 1013 } 1014 1015 static void print_br_cond(StrBuf* sb, u32 w, u64 vaddr, const AA64InsnDesc* d) { 1016 AA64BrCond f = aa64_brcond_unpack(w); 1017 (void)d; 1018 /* mnemonic is "b.cond"; we'll print cond as a suffix on the target. 1019 * The b.cond row keeps a single mnemonic for printing — for the asm 1020 * spelling to be canonical the writer will need to emit b.<cc>, which 1021 * is the printer's job at the dispatcher level (see aa64_print_operands). */ 1022 emit_cond(sb, f.cond); 1023 strbuf_putc(sb, ' '); 1024 i64 ofs = sext((u64)f.imm19, 19) * 4; 1025 if (vaddr) { 1026 strbuf_put_hex_u64(sb, vaddr + (u64)ofs); 1027 } else { 1028 strbuf_puts(sb, "#"); 1029 strbuf_put_i64(sb, ofs); 1030 } 1031 } 1032 1033 static void print_cb(StrBuf* sb, u32 w, u64 vaddr) { 1034 AA64CB f = aa64_cb_unpack(w); 1035 emit_reg(sb, f.Rt, (int)f.sf, 0); 1036 strbuf_puts(sb, ", "); 1037 i64 ofs = sext((u64)f.imm19, 19) * 4; 1038 if (vaddr) { 1039 strbuf_put_hex_u64(sb, vaddr + (u64)ofs); 1040 } else { 1041 strbuf_puts(sb, "#"); 1042 strbuf_put_i64(sb, ofs); 1043 } 1044 } 1045 1046 static void print_except(StrBuf* sb, u32 w) { 1047 AA64Except f = aa64_except_unpack(w); 1048 strbuf_puts(sb, "#"); 1049 strbuf_put_hex_u64(sb, (u64)f.imm16); 1050 } 1051 1052 static void print_barrier(StrBuf* sb, u32 w, const AA64InsnDesc* desc) { 1053 AA64Barrier f = aa64_barrier_unpack(w); 1054 /* ISB and CLREX with the default CRm=SY (15) print without an 1055 * operand. DMB/DSB always carry an option. */ 1056 int is_isb = (f.op2 == AA64_BARRIER_OP2_ISB); 1057 int is_clrex = (f.op2 == AA64_BARRIER_OP2_CLREX); 1058 if ((is_isb || is_clrex) && f.CRm == AA64_BARRIER_OPT_SY) return; 1059 const char* opt = NULL; 1060 switch (f.CRm) { 1061 case AA64_BARRIER_OPT_OSHLD: 1062 opt = "oshld"; 1063 break; 1064 case AA64_BARRIER_OPT_OSHST: 1065 opt = "oshst"; 1066 break; 1067 case AA64_BARRIER_OPT_OSH: 1068 opt = "osh"; 1069 break; 1070 case AA64_BARRIER_OPT_NSHLD: 1071 opt = "nshld"; 1072 break; 1073 case AA64_BARRIER_OPT_NSHST: 1074 opt = "nshst"; 1075 break; 1076 case AA64_BARRIER_OPT_NSH: 1077 opt = "nsh"; 1078 break; 1079 case AA64_BARRIER_OPT_ISHLD: 1080 opt = "ishld"; 1081 break; 1082 case AA64_BARRIER_OPT_ISHST: 1083 opt = "ishst"; 1084 break; 1085 case AA64_BARRIER_OPT_ISH: 1086 opt = "ish"; 1087 break; 1088 case AA64_BARRIER_OPT_LD: 1089 opt = (desc && desc->mnemonic.len >= 2 && desc->mnemonic.s[0] == 'd' && 1090 desc->mnemonic.s[1] == 'm') 1091 ? "ld" 1092 : NULL; 1093 break; 1094 case AA64_BARRIER_OPT_ST: 1095 opt = (desc && desc->mnemonic.len >= 2 && desc->mnemonic.s[0] == 'd' && 1096 desc->mnemonic.s[1] == 'm') 1097 ? "st" 1098 : NULL; 1099 break; 1100 case AA64_BARRIER_OPT_SY: 1101 opt = "sy"; 1102 break; 1103 default: 1104 break; 1105 } 1106 strbuf_putc(sb, ' '); 1107 if (opt) { 1108 strbuf_puts(sb, opt); 1109 } else { 1110 strbuf_puts(sb, "#"); 1111 strbuf_put_u64(sb, (u64)f.CRm); 1112 } 1113 } 1114 1115 static void print_sysreg_name(StrBuf* sb, u32 op0, u32 op1, u32 crn, u32 crm, 1116 u32 op2) { 1117 const char* name = aa64_sysreg_name(op0, op1, crn, crm, op2); 1118 if (name) { 1119 strbuf_puts(sb, name); 1120 return; 1121 } 1122 strbuf_putc(sb, 's'); 1123 strbuf_put_u64(sb, (u64)op0); 1124 strbuf_putc(sb, '_'); 1125 strbuf_put_u64(sb, (u64)op1); 1126 strbuf_puts(sb, "_c"); 1127 strbuf_put_u64(sb, (u64)crn); 1128 strbuf_puts(sb, "_c"); 1129 strbuf_put_u64(sb, (u64)crm); 1130 strbuf_putc(sb, '_'); 1131 strbuf_put_u64(sb, (u64)op2); 1132 } 1133 1134 static void print_sysreg(StrBuf* sb, u32 w) { 1135 int is_read = (int)((w >> 21) & 1u); 1136 u32 op0 = 2u | ((w >> 19) & 1u); 1137 u32 op1 = (w >> 16) & 7u; 1138 u32 crn = (w >> 12) & 0xfu; 1139 u32 crm = (w >> 8) & 0xfu; 1140 u32 op2 = (w >> 5) & 7u; 1141 u32 rt = w & 0x1fu; 1142 if (is_read) { 1143 emit_reg(sb, rt, /*sf=*/1, /*sp_means_sp=*/0); 1144 strbuf_puts(sb, ", "); 1145 print_sysreg_name(sb, op0, op1, crn, crm, op2); 1146 } else { 1147 print_sysreg_name(sb, op0, op1, crn, crm, op2); 1148 strbuf_puts(sb, ", "); 1149 emit_reg(sb, rt, /*sf=*/1, /*sp_means_sp=*/0); 1150 } 1151 } 1152 1153 /* FP scalar size from the 2-bit ftype field: 00=single, 01=double, 11=half. */ 1154 static char aa64_ftype_prefix(u32 ftype) { 1155 return ftype == 0u ? 's' : (ftype == 1u ? 'd' : 'h'); 1156 } 1157 1158 /* FADD/FSUB/FMUL/FDIV: Vd, Vn, Vm (all the same ftype). */ 1159 static void print_fp_dp2(StrBuf* sb, u32 w) { 1160 char p = aa64_ftype_prefix((w >> 22) & 3u); 1161 emit_vreg(sb, w & 0x1fu, p); 1162 strbuf_puts(sb, ", "); 1163 emit_vreg(sb, (w >> 5) & 0x1fu, p); 1164 strbuf_puts(sb, ", "); 1165 emit_vreg(sb, (w >> 16) & 0x1fu, p); 1166 } 1167 1168 /* FMOV(reg)/FNEG/FABS/FSQRT: Vd, Vn (same ftype). */ 1169 static void print_fp_dp1(StrBuf* sb, u32 w) { 1170 char p = aa64_ftype_prefix((w >> 22) & 3u); 1171 emit_vreg(sb, w & 0x1fu, p); 1172 strbuf_puts(sb, ", "); 1173 emit_vreg(sb, (w >> 5) & 0x1fu, p); 1174 } 1175 1176 /* FCMP: Vn, Vm. */ 1177 static void print_fp_cmp(StrBuf* sb, u32 w) { 1178 char p = aa64_ftype_prefix((w >> 22) & 3u); 1179 emit_vreg(sb, (w >> 5) & 0x1fu, p); 1180 strbuf_puts(sb, ", "); 1181 emit_vreg(sb, (w >> 16) & 0x1fu, p); 1182 } 1183 1184 /* FCVT precision change: Vd has the destination type (opc, bits 16:15), Vn the 1185 * source type (ftype, bits 23:22). */ 1186 static void print_fp_cvt(StrBuf* sb, u32 w) { 1187 char src = aa64_ftype_prefix((w >> 22) & 3u); 1188 char dst = aa64_ftype_prefix((w >> 15) & 3u); 1189 emit_vreg(sb, w & 0x1fu, dst); 1190 strbuf_puts(sb, ", "); 1191 emit_vreg(sb, (w >> 5) & 0x1fu, src); 1192 } 1193 1194 /* SCVTF/UCVTF/FCVTZS/FCVTZU and FMOV gpr<->fp. The opcode (bits 20:16) 1195 * selects the direction: fcvtzs/fcvtzu and fmov-to-gpr produce a GPR dst with 1196 * an FP src; scvtf/ucvtf and fmov-to-fp produce an FP dst with a GPR src. 1197 * sf (bit 31) is the GPR width, ftype (bits 23:22) the FP size. */ 1198 static void print_fp_int_cvt(StrBuf* sb, u32 w) { 1199 u32 opcode = (w >> 16) & 0x1fu; 1200 int sf = (int)((w >> 31) & 1u); 1201 char fp = aa64_ftype_prefix((w >> 22) & 3u); 1202 u32 rd = w & 0x1fu, rn = (w >> 5) & 0x1fu; 1203 int gpr_dst = (opcode == 0x18u /*fcvtzs*/ || opcode == 0x19u /*fcvtzu*/ || 1204 opcode == 0x06u /*fmov fp->gpr*/); 1205 if (gpr_dst) { 1206 emit_reg(sb, rd, sf, 0); 1207 strbuf_puts(sb, ", "); 1208 emit_vreg(sb, rn, fp); 1209 } else { 1210 emit_vreg(sb, rd, fp); 1211 strbuf_puts(sb, ", "); 1212 emit_reg(sb, rn, sf, 0); 1213 } 1214 } 1215 1216 /* RBIT/REV16/REV32/REV/CLZ: Rd, Rn (sf = bit 31). */ 1217 static void print_dp1(StrBuf* sb, u32 w) { 1218 int sf = (int)((w >> 31) & 1u); 1219 emit_reg(sb, w & 0x1fu, sf, 0); 1220 strbuf_puts(sb, ", "); 1221 emit_reg(sb, (w >> 5) & 0x1fu, sf, 0); 1222 } 1223 1224 /* SBFM/UBFM: Rd, Rn, #immr, #imms. */ 1225 static int bitfield_width(u32 word, u32* width) { 1226 u32 sf = (word >> 31) & 1u; 1227 u32 N = (word >> 22) & 1u; 1228 if (N != sf) return 0; 1229 *width = sf ? 64u : 32u; 1230 return 1; 1231 } 1232 1233 /* SBFM/UBFM shift aliases. UBFM is LSR when imms is the top bit index and LSL 1234 * when immr == imms+1 (the encoder's `lsl #s` form); SBFM is ASR when imms is 1235 * the top bit index. */ 1236 const char* aa64_bitfield_shift_alias(u32 word, u32* shift) { 1237 u32 opc = (word >> 29) & 3u; /* 0 = SBFM, 2 = UBFM */ 1238 u32 immr = (word >> 16) & 0x3fu; 1239 u32 imms = (word >> 10) & 0x3fu; 1240 u32 width, top; 1241 if (!bitfield_width(word, &width)) return NULL; 1242 top = width - 1u; 1243 if (opc == 2u) { /* UBFM */ 1244 if (imms == top) { 1245 *shift = immr; 1246 return "lsr"; 1247 } 1248 if (immr == imms + 1u) { 1249 *shift = top - imms; 1250 return "lsl"; 1251 } 1252 } else if (opc == 0u) { /* SBFM */ 1253 if (imms == top) { 1254 *shift = immr; 1255 return "asr"; 1256 } 1257 } 1258 return NULL; 1259 } 1260 1261 const char* aa64_bitfield_extend_alias(u32 word) { 1262 u32 sf = (word >> 31) & 1u; 1263 u32 opc = (word >> 29) & 3u; /* 0 = SBFM, 2 = UBFM */ 1264 u32 immr = (word >> 16) & 0x3fu; 1265 u32 imms = (word >> 10) & 0x3fu; 1266 u32 width; 1267 if (!bitfield_width(word, &width) || immr != 0u) return NULL; 1268 (void)width; 1269 if (opc == 0u) { 1270 if (imms == 7u) return "sxtb"; 1271 if (imms == 15u) return "sxth"; 1272 if (sf && imms == 31u) return "sxtw"; 1273 } else if (opc == 2u && !sf) { 1274 if (imms == 7u) return "uxtb"; 1275 if (imms == 15u) return "uxth"; 1276 } 1277 return NULL; 1278 } 1279 1280 const char* aa64_bitfield_extract_alias(u32 word, u32* lsb, u32* width_out) { 1281 u32 opc = (word >> 29) & 3u; /* 0 = SBFM, 2 = UBFM */ 1282 u32 immr = (word >> 16) & 0x3fu; 1283 u32 imms = (word >> 10) & 0x3fu; 1284 u32 width; 1285 if (!bitfield_width(word, &width)) return NULL; 1286 (void)width; 1287 if (opc != 0u && opc != 2u) return NULL; 1288 if (immr > imms) return NULL; 1289 *lsb = immr; 1290 *width_out = imms - immr + 1u; 1291 return opc == 0u ? "sbfx" : "ubfx"; 1292 } 1293 1294 static void print_bitfield(StrBuf* sb, u32 w) { 1295 int sf = (int)((w >> 31) & 1u); 1296 u32 shift, lsb, width; 1297 if (aa64_bitfield_shift_alias(w, &shift)) { 1298 /* lsl/lsr/asr Rd, Rn, #shift (mnemonic chosen by the disasm mnemonic 1299 * writer via the same helper). */ 1300 emit_reg(sb, w & 0x1fu, sf, 0); 1301 strbuf_puts(sb, ", "); 1302 emit_reg(sb, (w >> 5) & 0x1fu, sf, 0); 1303 strbuf_puts(sb, ", #"); 1304 strbuf_put_u64(sb, (u64)shift); 1305 return; 1306 } 1307 if (aa64_bitfield_extend_alias(w)) { 1308 /* sxtb/sxth/sxtw/uxtb/uxth Rd, Wn. The source is spelled Wn even when 1309 * the underlying SBFM has sf=1 (sxtb/sxth/sxtw into Xd). */ 1310 emit_reg(sb, w & 0x1fu, sf, 0); 1311 strbuf_puts(sb, ", "); 1312 emit_reg(sb, (w >> 5) & 0x1fu, 0, 0); 1313 return; 1314 } 1315 if (aa64_bitfield_extract_alias(w, &lsb, &width)) { 1316 emit_reg(sb, w & 0x1fu, sf, 0); 1317 strbuf_puts(sb, ", "); 1318 emit_reg(sb, (w >> 5) & 0x1fu, sf, 0); 1319 strbuf_puts(sb, ", #"); 1320 strbuf_put_u64(sb, (u64)lsb); 1321 strbuf_puts(sb, ", #"); 1322 strbuf_put_u64(sb, (u64)width); 1323 return; 1324 } 1325 emit_reg(sb, w & 0x1fu, sf, 0); 1326 strbuf_puts(sb, ", "); 1327 emit_reg(sb, (w >> 5) & 0x1fu, sf, 0); 1328 strbuf_puts(sb, ", #"); 1329 strbuf_put_u64(sb, (u64)((w >> 16) & 0x3fu)); 1330 strbuf_puts(sb, ", #"); 1331 strbuf_put_u64(sb, (u64)((w >> 10) & 0x3fu)); 1332 } 1333 1334 /* Decode an AArch64 logical-immediate (N:immr:imms) bitmask to its value, per 1335 * the ARM ARM DecodeBitMasks. Inverse of aa64_logimm_encode (isa.h). */ 1336 static u64 aa64_logimm_decode(u32 N, u32 immr, u32 imms, int sf) { 1337 u32 combined = (N << 6) | ((~imms) & 0x3fu); 1338 int len = -1; 1339 u32 esize, levels, S, R, i; 1340 u64 welem, elem, elt_mask, result; 1341 for (int b = 6; b >= 0; --b) { 1342 if (combined & (1u << b)) { 1343 len = b; 1344 break; 1345 } 1346 } 1347 if (len < 1) return 0; /* reserved encoding */ 1348 esize = 1u << (u32)len; 1349 levels = esize - 1u; 1350 S = imms & levels; 1351 R = immr & levels; 1352 welem = (S + 1u >= 64u) ? ~(u64)0 : (((u64)1 << (S + 1u)) - 1u); 1353 elt_mask = (esize >= 64u) ? ~(u64)0 : (((u64)1 << esize) - 1u); 1354 elem = 1355 (R == 0u) ? welem : (((welem >> R) | (welem << (esize - R))) & elt_mask); 1356 result = 0; 1357 for (i = 0; i < 64u; i += esize) result |= elem << i; 1358 return sf ? result : (result & 0xFFFFFFFFu); 1359 } 1360 1361 static void print_logimm(StrBuf* sb, u32 w) { 1362 int sf = (int)((w >> 31) & 1u); 1363 u32 opc = (w >> 29) & 3u; /* 0=AND 1=ORR 2=EOR 3=ANDS */ 1364 u32 N = (w >> 22) & 1u; 1365 u32 immr = (w >> 16) & 0x3fu; 1366 u32 imms = (w >> 10) & 0x3fu; 1367 /* AND/ORR/EOR use the SP-or-GP destination; ANDS uses ZR. Rn is always GP. */ 1368 emit_reg(sb, w & 0x1fu, sf, opc != 3u); 1369 strbuf_puts(sb, ", "); 1370 emit_reg(sb, (w >> 5) & 0x1fu, sf, 0); 1371 strbuf_puts(sb, ", #"); 1372 strbuf_put_hex_u64(sb, aa64_logimm_decode(N, immr, imms, sf)); 1373 } 1374 1375 /* Register-offset load/store: Rt, [Xn, Xm{, LSL #s}]. Rt width follows the 1376 * size field (X for 64-bit accesses, W otherwise); the index extend is LSL 1377 * for option=011 (UXTX), with shift amount S ? size : 0. */ 1378 static void print_ldst_regoff(StrBuf* sb, u32 w) { 1379 u32 size = (w >> 30) & 3u; 1380 u32 opc = (w >> 22) & 3u; 1381 u32 option = (w >> 13) & 7u; 1382 u32 s_bit = (w >> 12) & 1u; 1383 /* Destination width: LDR/STR (opc 00/01) are 64-bit only when size==11; 1384 * the signed sub-word loads pick width from opc — opc=10 sign-extends to 1385 * Xt, opc=11 to Wt — independent of size. */ 1386 int xt = (opc == 2u) ? 1 : (opc == 3u) ? 0 : (size == 3u); 1387 emit_reg(sb, w & 0x1fu, xt, 0); 1388 strbuf_puts(sb, ", ["); 1389 emit_reg(sb, (w >> 5) & 0x1fu, /*sf=*/1, /*sp_means_sp=*/1); 1390 strbuf_puts(sb, ", "); 1391 /* UXTW/SXTW use a W index; UXTX/SXTX (LSL) use an X index. */ 1392 emit_reg(sb, (w >> 16) & 0x1fu, (option & 1u) ? 1 : 0, 0); 1393 if (option != 3u) { 1394 /* Register-offset extends: only UXTW(010)/SXTW(110)/SXTX(111) are valid 1395 * besides LSL/UXTX(011). */ 1396 static const char* ext[8] = {0, 0, "uxtw", 0, 0, 0, "sxtw", "sxtx"}; 1397 const char* e = ext[option]; 1398 if (e) { 1399 strbuf_puts(sb, ", "); 1400 strbuf_puts(sb, e); 1401 if (s_bit) { 1402 strbuf_puts(sb, " #"); 1403 strbuf_put_u64(sb, (u64)size); 1404 } 1405 } 1406 } else if (s_bit) { 1407 strbuf_puts(sb, ", lsl #"); 1408 strbuf_put_u64(sb, (u64)size); 1409 } 1410 strbuf_putc(sb, ']'); 1411 } 1412 1413 void aa64_print_operands(StrBuf* sb, const AA64InsnDesc* desc, u32 word, 1414 u64 vaddr) { 1415 switch ((AA64Format)desc->fmt) { 1416 case AA64_FMT_MOVEWIDE: 1417 print_movewide(sb, word); 1418 break; 1419 case AA64_FMT_LOG_SR: 1420 print_logsr(sb, word, desc); 1421 break; 1422 case AA64_FMT_ADDSUB_SR: 1423 print_addsubsr(sb, word, desc); 1424 break; 1425 case AA64_FMT_DP3: 1426 print_dp3(sb, word, desc); 1427 break; 1428 case AA64_FMT_DP2: 1429 print_dp2(sb, word); 1430 break; 1431 case AA64_FMT_CONDSEL: 1432 print_condsel(sb, word, desc); 1433 break; 1434 case AA64_FMT_BR_REG: 1435 print_brreg(sb, word, desc); 1436 break; 1437 case AA64_FMT_PCREL_ADR: 1438 print_pcrel(sb, word, vaddr); 1439 break; 1440 case AA64_FMT_ADDSUB_IMM: 1441 print_addsubimm(sb, word); 1442 break; 1443 case AA64_FMT_LDST_UIMM: 1444 print_ldst_uimm(sb, word, desc); 1445 break; 1446 case AA64_FMT_LDSTP_PRE: 1447 print_ldstp_pre(sb, word); 1448 break; 1449 case AA64_FMT_LDSTP_SOFF: 1450 print_ldstp_soff(sb, word); 1451 break; 1452 case AA64_FMT_LDSTP_POST: 1453 print_ldstp_post(sb, word); 1454 break; 1455 case AA64_FMT_LDST_SIMM9: 1456 print_ldst_simm9(sb, word, desc); 1457 break; 1458 case AA64_FMT_LDST_EXCL: 1459 print_ldst_excl(sb, word); 1460 break; 1461 case AA64_FMT_BR_IMM: 1462 print_br_imm(sb, word, vaddr); 1463 break; 1464 case AA64_FMT_BR_COND: 1465 print_br_cond(sb, word, vaddr, desc); 1466 break; 1467 case AA64_FMT_CB: 1468 print_cb(sb, word, vaddr); 1469 break; 1470 case AA64_FMT_EXCEPT: 1471 print_except(sb, word); 1472 break; 1473 case AA64_FMT_HINT: 1474 break; /* no operands for NOP */ 1475 case AA64_FMT_BARRIER: 1476 print_barrier(sb, word, desc); 1477 break; 1478 case AA64_FMT_DP1: 1479 print_dp1(sb, word); 1480 break; 1481 case AA64_FMT_BITFIELD: 1482 print_bitfield(sb, word); 1483 break; 1484 case AA64_FMT_LOG_IMM: 1485 print_logimm(sb, word); 1486 break; 1487 case AA64_FMT_SYSREG: 1488 print_sysreg(sb, word); 1489 break; 1490 case AA64_FMT_LDST_REGOFF: 1491 print_ldst_regoff(sb, word); 1492 break; 1493 case AA64_FMT_FP_DP2: 1494 print_fp_dp2(sb, word); 1495 break; 1496 case AA64_FMT_FP_DP1: 1497 print_fp_dp1(sb, word); 1498 break; 1499 case AA64_FMT_FP_CMP: 1500 print_fp_cmp(sb, word); 1501 break; 1502 case AA64_FMT_FP_CVT: 1503 print_fp_cvt(sb, word); 1504 break; 1505 case AA64_FMT_FP_INT_CVT: 1506 print_fp_int_cvt(sb, word); 1507 break; 1508 } 1509 }