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