1 // Copyright (c) 2015- PPSSPP Project.
2
3 // This program is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, version 2.0 or later versions.
6
7 // This program is distributed in the hope that it will be useful,
8 // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // GNU General Public License 2.0 for more details.
11
12 // A copy of the GPL 2.0 should have been included with the program.
13 // If not, see http://www.gnu.org/licenses/
14
15 // Official git repository and contact information can be found at
16 // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17
18 // Basic ARM64 disassembler.
19
20 // No promises of accuracy, mostly just made to debug JIT code.
21 // Does enough to understand what's going on without having to resort to an
22 // external disassembler all the time...
23
24 #include <cstdlib>
25 #include <cstring>
26
27 #include "Common/Arm64Emitter.h"
28 #include "Common/StringUtils.h"
29 #include "Core/Util/DisArm64.h"
30
31 struct Instruction {
32 char text[128];
33 bool undefined;
34 bool badbits;
35 bool oddbits;
36 };
37
38 static const char * const shiftnames[4] = { "lsl", "lsr", "asr", "ror" };
39 static const char * const extendnames[8] = { "uxtb", "uxth", "uxtw", "uxtx", "sxtb", "sxth", "sxtw", "sxtx" };
40 static const char * const condnames[16] = {
41 "eq", // Equal
42 "ne", // Not equal
43 "cs", // Carry Set "HS"
44 "cc", // Carry Clear "LO"
45 "mi", // Minus (Negative)
46 "pl", // Plus
47 "vs", // Overflow
48 "vc", // No Overflow
49 "hi", // Unsigned higher
50 "ls", // Unsigned lower or same
51 "ge", // Signed greater than or equal
52 "lt", // Signed less than
53 "gt", // Signed greater than
54 "le", // Signed less than or equal
55 "al", // Always (unconditional) 14
56 };
57
SignExtend26(int x)58 int SignExtend26(int x) {
59 return (x & 0x02000000) ? (0xFC000000 | x) : (x & 0x3FFFFFF);
60 }
61
SignExtend19(int x)62 int SignExtend19(int x) {
63 return (x & 0x00040000) ? (0xFFF80000 | x) : (x & 0x7FFFF);
64 }
65
SignExtend9(int x)66 int SignExtend9(int x) {
67 return (x & 0x00000100) ? (0xFFFFFE00 | x) : (x & 0x1FF);
68 }
69
SignExtend7(int x)70 int SignExtend7(int x) {
71 return (x & 0x00000040) ? (0xFFFFFF80 | x) : (x & 0x7F);
72 }
73
SignExtend12(int x)74 int SignExtend12(int x) {
75 return (x & 0x00000800) ? (0xFFFFF000 | x) : (x & 0xFFF);
76 }
77
HighestSetBit(int value)78 int HighestSetBit(int value) {
79 int highest = 0;
80 for (int i = 0; i < 32; i++) {
81 if (value & (1 << i))
82 highest = i;
83 }
84 return highest;
85 }
86
LowestSetBit(int value,int maximum=32)87 int LowestSetBit(int value, int maximum = 32) {
88 for (int i = 0; i < maximum; i++) {
89 if (value & (1 << i))
90 return i;
91 }
92 return maximum;
93 }
94
Ones(int len)95 static uint64_t Ones(int len) {
96 if (len == 0x40) {
97 return 0xFFFFFFFFFFFFFFFF;
98 }
99 return (1ULL << len) - 1;
100 }
101
Replicate(uint64_t value,int esize)102 static uint64_t Replicate(uint64_t value, int esize) {
103 uint64_t out = 0;
104 value &= Ones(esize);
105 for (int i = 0; i < 64; i += esize) {
106 out |= value << i;
107 }
108 return out;
109 }
110
ROR(uint64_t value,int amount,int esize)111 static uint64_t ROR(uint64_t value, int amount, int esize) {
112 uint64_t rotated = (value >> amount) | (value << (esize - amount));
113 return rotated & Ones(esize);
114 }
115
DecodeBitMasks(int immN,int imms,int immr,uint64_t * tmask,uint64_t * wmask)116 void DecodeBitMasks(int immN, int imms, int immr, uint64_t *tmask, uint64_t *wmask) {
117 // Compute log2 of element size
118 // 2^len must be in range [2, M]
119 int len = HighestSetBit((immN << 6) | ((~imms) & 0x3f));
120 // if len < 1 then ReservedValue();
121 // assert M >= (1 << len);
122 // Determine S, R and S - R parameters
123 int levels = Ones(len);
124 uint32_t S = imms & levels;
125 uint32_t R = immr & levels;
126 int diff = S - R; // 6-bit subtract with borrow
127 int esize = 1 << len;
128 int d = diff & Ones(len - 1);
129 uint32_t welem = Ones(S + 1);
130 uint32_t telem = Ones(d + 1);
131 if (wmask) {
132 uint64_t rotated = ROR(welem, R, esize);
133 *wmask = Replicate(rotated, esize);
134 }
135 if (tmask) {
136 *tmask = Replicate(telem, esize);
137 }
138 }
139
DataProcessingImmediate(uint32_t w,uint64_t addr,Instruction * instr)140 static void DataProcessingImmediate(uint32_t w, uint64_t addr, Instruction *instr) {
141 int Rd = w & 0x1f;
142 int Rn = (w >> 5) & 0x1f;
143 char r = ((w >> 31) & 1) ? 'x' : 'w';
144 if (((w >> 23) & 0x3f) == 0x25) {
145 // Constant initialization.
146 int imm16 = (w >> 5) & 0xFFFF;
147 int opc = (w >> 29) & 3;
148 int shift = ((w >> 21) & 0x3) * 16;
149 const char *opnames[4] = { "movn", "(undef)", "movz", "movk" };
150 snprintf(instr->text, sizeof(instr->text), "%s %c%d, #0x%04x << %d", opnames[opc], r, Rd, imm16, shift);
151 } else if (((w >> 24) & 0x1F) == 0x10) {
152 // Address generation relative to PC
153 int op = w >> 31;
154 int imm = (SignExtend19(w >> 5) << 2) | ((w >> 29) & 3);
155 if (op & 1) imm <<= 12;
156 u64 daddr = addr + imm;
157 snprintf(instr->text, sizeof(instr->text), "%s x%d, #0x%04x%08x", op ? "adrp" : "adr", Rd, (u32)(daddr >> 32), (u32)(daddr & 0xFFFFFFFF));
158 } else if (((w >> 24) & 0x1F) == 0x11) {
159 // Add/subtract immediate value
160 int op = (w >> 30) & 1;
161 int imm = ((w >> 10) & 0xFFF);
162 int shift = ((w >> 22) & 0x1) * 12;
163 int s = ((w >> 29) & 1);
164 imm <<= shift;
165 if (s && Rd == 31) {
166 snprintf(instr->text, sizeof(instr->text), "cmp %c%d, #%d", r, Rn, imm);
167 } else if (!shift && Rn == 31 && imm == 0) {
168 snprintf(instr->text, sizeof(instr->text), "mov %c%d, sp", r, Rd);
169 } else if (!shift && Rd == 31 && imm == 0) {
170 snprintf(instr->text, sizeof(instr->text), "mov sp, %c%d", r, Rn);
171 } else {
172 snprintf(instr->text, sizeof(instr->text), "%s%s %c%d, %c%d, #%d", op == 0 ? "add" : "sub", s ? "s" : "", r, Rd, r, Rn, imm);
173 }
174 } else if (((w >> 23) & 0x3f) == 0x24) {
175 int immr = (w >> 16) & 0x3f;
176 int imms = (w >> 10) & 0x3f;
177 int N = (w >> 22) & 1;
178 int opc = (w >> 29) & 3;
179 const char *opname[4] = { "and", "orr", "eor", "ands" };
180 uint64_t wmask;
181 DecodeBitMasks(N, imms, immr, NULL, &wmask);
182 if (((w >> 31) & 1) && wmask & 0xFFFFFFFF00000000ULL)
183 snprintf(instr->text, sizeof(instr->text), "%s %c%d, %c%d, #0x%x%08x", opname[opc], r, Rd, r, Rn, (uint32_t)(wmask >> 32), (uint32_t)(wmask & 0xFFFFFFFF));
184 else
185 snprintf(instr->text, sizeof(instr->text), "%s %c%d, %c%d, #0x%x", opname[opc], r, Rd, r, Rn, (uint32_t)wmask);
186 } else if (((w >> 23) & 0x3f) == 0x26) {
187 int N = (w >> 22) & 1;
188 int opc = (w >> 29) & 3;
189 int immr = (w >> 16) & 0x3f;
190 int imms = (w >> 10) & 0x3f;
191 const char *opname[4] = { "sbfm", "bfm", "ubfm" };
192 snprintf(instr->text, sizeof(instr->text), "%s %c%d, %c%d, #%d, #%d", opname[opc], r, Rd, r, Rn, immr, imms);
193 } else {
194 snprintf(instr->text, sizeof(instr->text), "(DPI %08x)", w);
195 }
196 }
197
GetSystemRegName(int o0,int op1,int CRn,int CRm,int op2)198 static const char *GetSystemRegName(int o0, int op1, int CRn, int CRm, int op2) {
199 if (o0 == 3 && op1 == 3 && CRn == 4 && CRm == 2 && op2 == 0) {
200 return "nzcv";
201 } else if (o0 == 3 && op1 == 3 && CRn == 4 && CRm == 4 && op2 == 0) {
202 return "fpsr";
203 } else {
204 return "(unknown)";
205 }
206 }
207
BranchExceptionAndSystem(uint32_t w,uint64_t addr,Instruction * instr,SymbolCallback symbolCallback)208 static void BranchExceptionAndSystem(uint32_t w, uint64_t addr, Instruction *instr, SymbolCallback symbolCallback) {
209 char buffer[128];
210 int Rt = w & 0x1f;
211 int Rn = (w >> 5) & 0x1f;
212 if (((w >> 26) & 0x1F) == 5) {
213 // Unconditional branch / branch+link
214 int offset = SignExtend26(w) << 2;
215 uint64_t target = addr + offset;
216 if (symbolCallback && symbolCallback(buffer, sizeof(buffer), (uint8_t *)(uintptr_t)target)) {
217 snprintf(instr->text, sizeof(instr->text), "b%s %s", (w >> 31) ? "l" : "", buffer);
218 } else {
219 snprintf(instr->text, sizeof(instr->text), "b%s %04x%08x", (w >> 31) ? "l" : "", (uint32_t)(target >> 32), (uint32_t)(target & 0xFFFFFFFF));
220 }
221 } else if (((w >> 25) & 0x3F) == 0x1A) {
222 // Compare and branch
223 int op = (w >> 24) & 1;
224 const char *opname[2] = { "cbz", "cbnz" };
225 char r = ((w >> 31) & 1) ? 'x' : 'w';
226 int offset = SignExtend19(w >> 5);
227 snprintf(instr->text, sizeof(instr->text), "%s %c%d", opname[op], r, Rt);
228 } else if (((w >> 25) & 0x3F) == 0x1B) {
229 // Test and branch
230 snprintf(instr->text, sizeof(instr->text), "(test & branch %08x)", w);
231 } else if (((w >> 25) & 0x7F) == 0x2A) {
232 // Conditional branch
233 int offset = SignExtend19(w >> 5) << 2;
234 uint64_t target = addr + offset;
235 int cond = w & 0xF;
236 snprintf(instr->text, sizeof(instr->text), "b.%s %04x%08x", condnames[cond], (uint32_t)(target >> 32), (uint32_t)(target & 0xFFFFFFFF));
237 } else if ((w >> 24) == 0xD4) {
238 if (((w >> 21) & 0x7) == 1 && Rt == 0) {
239 int imm = (w >> 5) & 0xFFFF;
240 snprintf(instr->text, sizeof(instr->text), "brk #%d", imm);
241 } else {
242 snprintf(instr->text, sizeof(instr->text), "(exception-gen %08x)", w);
243 }
244 } else if (((w >> 20) & 0xFFC) == 0xD50) {
245 bool L = (w >> 21) & 1; // read
246 // Could check them all at once, but feels better to do it like the manual says.
247 int o0 = (w >> 19) & 3;
248 int op1 = (w >> 16) & 7;
249 int CRn = (w >> 12) & 0xF;
250 int CRm = (w >> 8) & 0xf;
251 int op2 = (w >> 5) & 0x7;
252 const char *sysreg = GetSystemRegName(o0, op1, CRn, CRm, op2);
253 if (L) {
254 snprintf(instr->text, sizeof(instr->text), "mrs x%d, %s", Rt, sysreg);
255 } else {
256 snprintf(instr->text, sizeof(instr->text), "msr %s, x%d", sysreg, Rt);
257 }
258
259 } else if (((w >> 25) & 0x7F) == 0x6B) {
260 int op = (w >> 21) & 3;
261 const char *opname[4] = { "b", "bl", "ret", "(unk)" };
262 snprintf(instr->text, sizeof(instr->text), "%s x%d", opname[op], Rn);
263 } else {
264 snprintf(instr->text, sizeof(instr->text), "(BRX ?? %08x)", w);
265 }
266 }
267
Arm64AnalyzeLoadStore(uint64_t addr,uint32_t w,Arm64LSInstructionInfo * info)268 bool Arm64AnalyzeLoadStore(uint64_t addr, uint32_t w, Arm64LSInstructionInfo *info) {
269 *info = {};
270 info->instructionSize = 4;
271 int id = (w >> 25) & 0xF;
272 switch (id) {
273 case 4: case 6: case 0xC: case 0xE:
274 break;
275 default:
276 return false; // not the expected instruction
277 }
278
279 info->size = w >> 30;
280 info->Rt = (w & 0x1F);
281 info->Rn = ((w >> 5) & 0x1F);
282 info->Rm = ((w >> 16) & 0x1F);
283 int opc = (w >> 22) & 0x3;
284 if (opc == 0 || opc == 2) {
285 info->isMemoryWrite = true;
286 }
287
288 if (((w >> 27) & 7) == 7) {
289 int V = (w >> 26) & 1;
290 if (V == 0) {
291 info->isIntegerLoadStore = true;
292 } else {
293 info->isFPLoadStore = true;
294 }
295 } else {
296 info->isPairLoadStore = true;
297 // TODO
298 }
299 return true;
300 }
301
LoadStore(uint32_t w,uint64_t addr,Instruction * instr)302 static void LoadStore(uint32_t w, uint64_t addr, Instruction *instr) {
303 int size = w >> 30;
304 int imm9 = SignExtend9((w >> 12) & 0x1FF);
305 int Rt = (w & 0x1F);
306 int Rn = ((w >> 5) & 0x1F);
307 int Rm = ((w >> 16) & 0x1F);
308 int option = (w >> 13) & 0x7;
309 int opc = (w >> 22) & 0x3;
310 char r = size == 3 ? 'x' : 'w';
311 const char *opname[4] = { "str", "ldr", "str", "ldr" };
312 const char *sizeSuffix[4] = { "b", "h", "", "" };
313
314 if (((w >> 27) & 7) == 7) {
315 int V = (w >> 26) & 1;
316 bool index_unsigned = ((w >> 24) & 3) == 1;
317 bool index_post = !index_unsigned && ((w >> 10) & 3) == 1;
318 bool index_pre = !index_unsigned && ((w >> 10) & 3) == 3;
319 if (V == 0) {
320 const char *signExt = ((opc & 0x2) && size < 3) ? "s" : "";
321 int imm12 = SignExtend12((w >> 10) & 0xFFF) << size;
322 // Integer type
323 if (index_unsigned) {
324 snprintf(instr->text, sizeof(instr->text), "%s%s%s %c%d, [x%d, #%d]", opname[opc], signExt, sizeSuffix[size], r, Rt, Rn, imm12);
325 return;
326 } else if (index_post) {
327 snprintf(instr->text, sizeof(instr->text), "%s%s%s %c%d, [x%d], #%d", opname[opc], signExt, sizeSuffix[size], r, Rt, Rn, SignExtend9(imm9));
328 return;
329 } else if (index_pre) {
330 snprintf(instr->text, sizeof(instr->text), "%s%s%s %c%d, [x%d, #%d]!", opname[opc], signExt, sizeSuffix[size], r, Rt, Rn, SignExtend9(imm9));
331 return;
332 } else {
333 // register offset
334 int S = (w >> 12) & 1;
335 char index_w = (option & 3) == 2 ? 'w' : 'x';
336 // TODO: Needs index support
337 snprintf(instr->text, sizeof(instr->text), "%s%s %c%d, [x%d + %c%d]", opname[opc], sizeSuffix[size], r, Rt, Rn, index_w, Rm);
338 return;
339 }
340 } else {
341 // FP/Vector type
342 if ((opc & 2) && size == 0) {
343 size = 4;
344 }
345 char vr = "bhsdq"[size];
346 int imm12 = SignExtend12((w >> 10) & 0xFFF) << size;
347 if (index_unsigned) {
348 snprintf(instr->text, sizeof(instr->text), "%s %c%d, [x%d, #%d]", opname[opc], vr, Rt, Rn, imm12);
349 return;
350 } else if (index_post) {
351 snprintf(instr->text, sizeof(instr->text), "%s %c%d, [x%d], #%d", opname[opc], vr, Rt, Rn, SignExtend9(imm9));
352 return;
353 } else if (index_pre) {
354 snprintf(instr->text, sizeof(instr->text), "%s %c%d, [x%d, #%d]!", opname[opc], vr, Rt, Rn, SignExtend9(imm9));
355 return;
356 } else {
357 snprintf(instr->text, sizeof(instr->text), "(loadstore-fp-vector %08x)", w);
358 return;
359 }
360 }
361 } else if (((w >> 25) & 0x1D) == 0x14) {
362 // load/store pair
363 int Rt2 = (w >> 10) & 0x1f;
364 bool load = (w >> 22) & 1;
365 int index_type = ((w >> 23) & 3);
366 bool sf = (w >> 31) != 0;
367 bool V = (w >> 26) & 1;
368 int op = (w >> 30);
369
370 int offset = SignExtend7((w >> 15) & 0x7f);
371 if (V) {
372 offset <<= 2;
373 switch (op) {
374 case 0:
375 r = 's';
376 break;
377 case 1:
378 r = 'd';
379 if (index_type == 2)
380 offset <<= 1;
381 break;
382 case 2:
383 r = 'q';
384 if (index_type == 2)
385 offset <<= 2;
386 }
387 } else {
388 r = sf ? 'x' : 'w';
389 offset <<= (sf ? 3 : 2);
390 }
391 if (index_type == 2) {
392 snprintf(instr->text, sizeof(instr->text), "%s %c%d, %c%d, [x%d, #%d]", load ? "ldp" : "stp", r, Rt, r, Rt2, Rn, offset);
393 return;
394 } else if (index_type == 1) {
395 snprintf(instr->text, sizeof(instr->text), "%s %c%d, %c%d, [x%d], #%d", load ? "ldp" : "stp", r, Rt, r, Rt2, Rn, offset);
396 return;
397 } else if (index_type == 3) {
398 snprintf(instr->text, sizeof(instr->text), "%s %c%d, %c%d, [x%d, #%d]!", load ? "ldp" : "stp", r, Rt, r, Rt2, Rn, offset);
399 return;
400 } else if (index_type == 0) {
401 // LDNP/STNP (ldp/stp with non-temporal hint). Automatically signed offset.
402 snprintf(instr->text, sizeof(instr->text), "%s %c%d, %c%d, [x%d, #%d]", load ? "ldnp" : "stnp", r, Rt, r, Rt2, Rn, offset);
403 return;
404 }
405 }
406 snprintf(instr->text, sizeof(instr->text), "(LS %08x)", w);
407 }
408
DataProcessingRegister(uint32_t w,uint64_t addr,Instruction * instr)409 static void DataProcessingRegister(uint32_t w, uint64_t addr, Instruction *instr) {
410 int Rd = w & 0x1F;
411 int Rn = (w >> 5) & 0x1F;
412 int Rm = (w >> 16) & 0x1F;
413 char r = ((w >> 31) & 1) ? 'x' : 'w';
414
415 if (((w >> 21) & 0x2FF) == 0x2D6) {
416 // Data processing
417 int opcode2 = (w >> 16) & 0x1F;
418 int opcode = (w >> 10) & 0x3F;
419 // Data-processing (1 source)
420 const char *opname[8] = { "rbit", "rev16", "rev32", "(unk)", "clz", "cls" };
421 const char *op = opcode2 >= 8 ? "unk" : opname[opcode];
422 snprintf(instr->text, sizeof(instr->text), "%s %c%d, %c%d", op, r, Rd, r, Rn);
423 } else if (((w >> 21) & 0x2FF) == 0x0D6) {
424 const char *opname[32] = {
425 0, 0, "udiv", "sdiv", 0, 0, 0, 0,
426 "lslv", "lsrv", "asrv", "rorv", 0, 0, 0, 0,
427 "crc32b", "crc32h", "crc32w", 0, "crc32cb", "crc32ch", "crc32cw", 0,
428 };
429 int opcode = (w >> 10) & 0x3F;
430 // Data processing (2 source)
431 snprintf(instr->text, sizeof(instr->text), "%s %c%d, %c%d, %c%d", opname[opcode], r, Rd, r, Rn, r, Rm);
432 } else if (((w >> 24) & 0x1f) == 0xA) {
433 // Logical (shifted register)
434 int shift = (w >> 22) & 0x3;
435 int imm6 = (w >> 10) & 0x3f;
436 int N = (w >> 21) & 1;
437 int opc = (((w >> 29) & 3) << 1) | N;
438 const char *opnames[8] = { "and", "bic", "orr", "orn", "eor", "eon", "ands", "bics" };
439 if (opc == 2 && Rn == 31) {
440 // Special case for MOV (which is constructed from an ORR)
441 if (imm6 != 0) {
442 snprintf(instr->text, sizeof(instr->text), "mov %c%d, %c%d, %s #%d", r, Rd, r, Rm, shiftnames[shift], imm6);
443 } else {
444 snprintf(instr->text, sizeof(instr->text), "mov %c%d, %c%d", r, Rd, r, Rm);
445 }
446 } else if (imm6 == 0 && shift == 0) {
447 snprintf(instr->text, sizeof(instr->text), "%s %c%d, %c%d, %c%d", opnames[opc], r, Rd, r, Rn, r, Rm);
448 } else {
449 snprintf(instr->text, sizeof(instr->text), "%s %c%d, %c%d, %c%d, %s #%d", opnames[opc], r, Rd, r, Rn, r, Rm, shiftnames[shift], imm6);
450 }
451 } else if (((w >> 21) & 0xf9) == 0x58) {
452 // Add/sub/cmp (shifted register)
453 bool S = (w >> 29) & 1;
454 int shift = (w >> 22) & 0x3;
455 int imm6 = (w >> 10) & 0x3f;
456 int opc = ((w >> 29) & 3);
457 const char *opnames[8] = { "add", "adds", "sub", "subs"};
458 if (imm6 == 0 && shift == 0) {
459 if (Rd == 31 && opc == 3) {
460 // It's a CMP
461 snprintf(instr->text, sizeof(instr->text), "%s %c%d, %c%d", "cmp", r, Rn, r, Rm);
462 } else if (Rn == 31 && opc == 2) {
463 // It's a NEG
464 snprintf(instr->text, sizeof(instr->text), "%s %c%d, %c%d", "neg", r, Rd, r, Rm);
465 } else {
466 snprintf(instr->text, sizeof(instr->text), "%s %c%d, %c%d, %c%d", opnames[opc], r, Rd, r, Rn, r, Rm);
467 }
468 } else {
469 snprintf(instr->text, sizeof(instr->text), "%s %c%d, %c%d, %c%d, %s #%d", opnames[opc], r, Rd, r, Rn, r, Rm, shiftnames[shift], imm6);
470 }
471 } else if (((w >> 21) & 0xFF) == 0x59) {
472 // Add/sub (extended register)
473 bool S = (w >> 29) & 1;
474 bool sub = (w >> 30) & 1;
475 int option = (w >> 13) & 0x7;
476 int imm3 = (w >> 10) & 0x7;
477 if (Rd == 31 && sub && S) {
478 // It's a CMP
479 snprintf(instr->text, sizeof(instr->text), "%s%s %c%d, %c%d, %s", "cmp", S ? "s" : "", r, Rn, r, Rm, extendnames[option]);
480 } else {
481 snprintf(instr->text, sizeof(instr->text), "%s%s %c%d, %c%d, %c%d, %s", sub ? "sub" : "add", S ? "s" : "", r, Rd, r, Rn, r, Rm, extendnames[option]);
482 }
483 } else if (((w >> 21) & 0xFF) == 0xD6 && ((w >> 12) & 0xF) == 2) {
484 // Variable shifts
485 int opc = (w >> 10) & 3;
486 snprintf(instr->text, sizeof(instr->text), "%sv %c%d, %c%d, %c%d", shiftnames[opc], r, Rd, r, Rn, r, Rm);
487 } else if (((w >> 21) & 0xFF) == 0xD4) {
488 // Conditional select
489 int op = (w >> 30) & 1;
490 int op2 = (w >> 10) & 3;
491 int cond = (w >> 12) & 0xf;
492 const char *opnames[4] = { "csel", "csinc", "csinv", "csneg" };
493 snprintf(instr->text, sizeof(instr->text), "%s %c%d, %c%d, %c%d, %s", opnames[(op << 1) | op2], r, Rd, r, Rn, r, Rm, condnames[cond]);
494 } else if (((w >> 24) & 0x1f) == 0x1b) {
495 // Data processing - 3 source
496 int op31 = (w >> 21) & 0x7;
497 int o0 = (w >> 15) & 1;
498 int Ra = (w >> 10) & 0x1f;
499 const char *opnames[8] = { 0, 0, "maddl", "msubl", "smulh", 0, 0, 0 };
500
501 if (op31 == 0) {
502 // madd/msub supports both 32-bit and 64-bit modes
503 snprintf(instr->text, sizeof(instr->text), "%s %c%d, %c%d, %c%d, %c%d", o0 ? "msub" : "madd", r, Rd, r, Rn, r, Rm, r, Ra);
504 } else {
505 // The rest are 64-bit accumulator, 32-bit operands
506 char sign = (op31 >> 2) ? 'u' : 's';
507 int opn = (op31 & 0x3) << 1 | o0;
508 if (opn < 4 && Ra == 31) {
509 snprintf(instr->text, sizeof(instr->text), "%cmull x%d, w%d, w%d", sign, Rd, Rn, Rm);
510 } else {
511 snprintf(instr->text, sizeof(instr->text), "%c%s x%d, w%d, w%d, x%d", sign, opnames[opn], Rd, Rn, Rm, Ra);
512 }
513 }
514 } else {
515 // Logical (extended register)
516 snprintf(instr->text, sizeof(instr->text), "(DPR %08x)", w);
517 }
518 }
519
GetQ(uint32_t w)520 inline bool GetQ(uint32_t w) { return (w >> 30) & 1; }
GetU(uint32_t w)521 inline bool GetU(uint32_t w) { return (w >> 29) & 1; }
GetArrangement(bool Q,bool sz)522 const char *GetArrangement(bool Q, bool sz) {
523 if (Q == 0 && sz == 0) return "2s";
524 else if (Q == 1 && sz == 0) return "4s";
525 else if (Q == 1 && sz == 1) return "2d";
526 else return "ERROR";
527 }
528
529 // (w >> 25) & 0xF == 7
FPandASIMD1(uint32_t w,uint64_t addr,Instruction * instr)530 static void FPandASIMD1(uint32_t w, uint64_t addr, Instruction *instr) {
531 int Rd = w & 0x1f;
532 int Rn = (w >> 5) & 0x1f;
533 int Rm = (w >> 16) & 0x1f;
534 if (((w >> 21) & 0x4F9) == 0x71) {
535 switch ((w >> 10) & 3) {
536 case 1: case 3: {
537 int opcode = (w >> 11) & 0x1f;
538 int sz = (w >> 22) & 3;
539 int Q = GetQ(w);
540 int U = GetU(w);
541 const char *opnames000[32] = {
542 "shadd", "sqadd", "srhadd", 0,
543 "shsub", "sqsub", "cmgt", "cmge",
544 "sshl", "sqshl", "srshl", "sqrshl",
545 "smax", "smin", "sabd", "saba",
546 "add", "cmtst", "mla", "mul",
547 "smaxp", "sminp", "sqdmulh", "addp",
548 "fmaxnm", "fmla", "fadd", "fmulx",
549 "fcmeq", 0, "fmax", "frecps",
550 };
551 const char *opnames100[32] = {
552 "uhadd", "uqadd", "urhadd", 0,
553 "uhsub", "uqsub", "cmhi", "cmhs",
554 "ushl", "uqshl", "urshl", "uqrshl",
555 "umax", "umin", "uabd", "uaba",
556 "sub", "cmeq", "mls", "pmul",
557 "umaxp", "uminp", "sqrdmulh", "addp",
558 "fmaxnmp", 0, "faddp", "fmul",
559 "fcmge", "facge", "fmaxp", "fdiv",
560 };
561 const char *opnames010[8] = {
562 "fminm", "fmls", "fsub", 0,
563 0, 0, "fmin", "frsqrts",
564 };
565 const char *opnames110[8] = {
566 "fminnmp", 0, "fabd", 0,
567 "fcmgt", "facgt", "fminp", 0,
568 };
569 char r = Q ? 'q' : 'd';
570 const char *opname = nullptr;
571 bool fp = false;
572 bool nosize = false;
573 if (U == 0) {
574 if (opcode < 0x18) {
575 opname = opnames000[opcode];
576 } else if ((sz & 0x2) == 0) {
577 opname = opnames000[opcode];
578 fp = true;
579 } else if ((sz & 0x2) == 2) {
580 opname = opnames010[opcode - 0x18];
581 fp = true;
582 }
583 if (!opname && opcode == 3 && (sz & 2) == 0) {
584 opname = !(sz & 1) ? "and" : "bic";
585 nosize = true;
586 } else if (!opname && opcode == 3 && (sz & 2) == 2) {
587 opname = !(sz & 1) ? "orr" : "orn";
588 nosize = true;
589 }
590 } else if (U == 1) {
591 if (opcode < 0x18) {
592 opname = opnames100[opcode];
593 } else if ((sz & 0x2) == 0) {
594 opname = opnames100[opcode];
595 fp = true;
596 } else if ((sz & 0x2) == 2) {
597 opname = opnames110[opcode - 0x18];
598 fp = true;
599 }
600 if (!opname && opcode == 3 && (sz & 2) == 0) {
601 opname = !(sz & 1) ? "eor" : "bsl";
602 if (!strcmp(opname, "eor"))
603 nosize = true;
604 } else if (!opname && opcode == 3 && (sz & 2) == 2) {
605 opname = !(sz & 1) ? "bit" : "bif";
606 }
607 }
608 int size = (fp ? ((sz & 1) ? 64 : 32) : (8 << sz));
609
610 if (opname != nullptr) {
611 if (!nosize) {
612 snprintf(instr->text, sizeof(instr->text), "%s.%d %c%d, %c%d, %c%d", opname, size, r, Rd, r, Rn, r, Rm);
613 } else {
614 if (!strcmp(opname, "orr") && Rn == Rm) {
615 snprintf(instr->text, sizeof(instr->text), "mov %c%d, %c%d", r, Rd, r, Rn);
616 } else {
617 snprintf(instr->text, sizeof(instr->text), "%s %c%d, %c%d, %c%d", opname, r, Rd, r, Rn, r, Rm);
618 }
619 }
620 } else {
621 snprintf(instr->text, sizeof(instr->text), "(asimd three-same %08x)", w);
622 }
623 break;
624 }
625 case 0:
626 snprintf(instr->text, sizeof(instr->text), "(asimd three-different %08x)", w);
627 break;
628 case 2:
629 if (((w >> 17) & 0xf) == 0) {
630 int opcode = (w >> 12) & 0x1F;
631 int sz = (w >> 22) & 3;
632 int Q = GetQ(w);
633 int U = GetU(w);
634 const char *opname = nullptr;
635 bool narrow = false;
636 if (!U) {
637 switch (opcode) {
638 case 0: opname = "rev64"; break;
639 case 1: opname = "rev16"; break;
640 case 2: opname = "saddlp"; break;
641 case 3: opname = "suqadd"; break;
642 case 4: opname = "cls"; break;
643 case 5: opname = "cnt"; break;
644 case 6: opname = "sadalp"; break;
645 case 7: opname = "sqabs"; break;
646 case 8: opname = "cmgt"; break;
647 case 9: opname = "cmeq"; break;
648 case 0xA: opname = "cmlt"; break;
649 case 0xB: opname = "abs"; break;
650 case 0x12: opname = "xtn"; narrow = true; break;
651 case 0x14: opname = "sqxtn"; narrow = true; break;
652 default:
653 if (!(sz & 0x2)) {
654 switch (opcode) {
655 case 0x16: opname = "fcvtn"; break;
656 case 0x17: opname = "fcvtl"; break;
657 case 0x18: opname = "frintn"; break;
658 case 0x19: opname = "frintm"; break;
659 case 0x1a: opname = "fcvtns"; break;
660 case 0x1b: opname = "fcvtms"; break;
661 case 0x1c: opname = "fcvtas"; break;
662 case 0x1d: opname = "scvtf"; break;
663 }
664 } else {
665 switch (opcode) {
666 case 0xc: opname = "fcmgt"; break;
667 case 0xd: opname = "fcmeq"; break;
668 case 0xe: opname = "fcmlt"; break;
669 case 0xf: opname = "fabs"; break;
670 case 0x18: opname = "frintp"; break;
671 case 0x19: opname = "frintz"; break;
672 case 0x1a: opname = "fcvtps"; break;
673 case 0x1b: opname = "fcvtzs"; break;
674 case 0x1c: opname = "urepce"; break;
675 case 0x1d: opname = "frepce"; break;
676 }
677 }
678 }
679 } else {
680 switch (opcode) {
681 case 0: opname = "rev32"; break;
682 case 2: opname = "uaddlp"; break;
683 case 3: opname = "usqadd"; break;
684 case 4: opname = "clz"; break;
685 case 6: opname = "uadalp"; break;
686 case 7: opname = "sqneg"; break;
687 case 8: opname = "cmge"; break; // with zero
688 case 9: opname = "cmle"; break; // with zero
689 case 0xB: opname = "neg"; break;
690 case 0x12: opname = "sqxtun"; narrow = true; break;
691 case 0x13: opname = "shll"; break;
692 case 0x14: opname = "uqxtn"; narrow = true; break;
693 case 5: if (sz == 0) opname = "not"; else opname = "rbit"; break;
694 default:
695 if (!(sz & 0x2)) {
696 switch (opcode) {
697 case 0x16: opname = "fcvtxn"; break;
698 case 0x18: opname = "frinta"; break;
699 case 0x19: opname = "frintx"; break;
700 case 0x1a: opname = "fcvtnu"; break;
701 case 0x1b: opname = "fcvtmu"; break;
702 case 0x1c: opname = "fcvtau"; break;
703 case 0x1d: opname = "ucvtf"; break;
704 }
705 } else {
706 switch (opcode) {
707 case 0xC: opname = "fcmge"; break; // with zero
708 case 0xD: opname = "fcmge"; break; // with zero
709 case 0xF: opname = "fneg"; break;
710 case 0x19: opname = "frinti"; break;
711 case 0x1a: opname = "fcvtpu"; break;
712 case 0x1b: opname = "fcvtzu"; break;
713 case 0x1c: opname = "ursqrte"; break;
714 case 0x1d: opname = "frsqrte"; break;
715 case 0x1f: opname = "fsqrt"; break;
716 }
717 }
718 }
719 }
720
721 if (opname) {
722 if (narrow) {
723 int esize = 8 << sz;
724 const char *two = ""; // todo
725 snprintf(instr->text, sizeof(instr->text), "%s%s.%d.%d d%d, q%d", opname, two, esize, esize * 2, Rd, Rn);
726 } else {
727 snprintf(instr->text, sizeof(instr->text), "%s", opname);
728 }
729 } else {
730 // Very similar to scalar two-reg misc. can we share code?
731 snprintf(instr->text, sizeof(instr->text), "(asimd vector two-reg misc %08x)", w);
732 }
733 } else if (((w >> 17) & 0xf) == 1) {
734 snprintf(instr->text, sizeof(instr->text), "(asimd across lanes %08x)", w);
735 } else {
736 goto bail;
737 }
738 }
739 } else if (((w >> 21) & 0x4F9) == 0x70) {
740 if (((w >> 10) & 0x21) == 1) {
741 if (((w >> 11) & 3) == 3) {
742 // From GPR
743 snprintf(instr->text, sizeof(instr->text), "(asimd copy gpr %08x)", w);
744 } else {
745 int imm5 = (w >> 16) & 0x1F;
746 int size = LowestSetBit(imm5, 5);
747 int imm4 = (w >> 11) & 0xF;
748 int dst_index = imm5 >> (size + 1);
749 int src_index = imm4 >> size;
750 int op = (w >> 29) & 1;
751 char s = "bhsd"[size];
752 if (op == 0 && imm4 == 0) {
753 // DUP (element)
754 int idxdsize = (imm5 & 8) ? 128 : 64;
755 char r = "dq"[idxdsize == 128];
756 snprintf(instr->text, sizeof(instr->text), "dup %c%d, %c%d.%c[%d]", r, Rd, r, Rn, s, dst_index);
757 } else {
758 int idxdsize = (imm4 & 8) ? 128 : 64;
759 char r = "dq"[idxdsize == 128];
760 snprintf(instr->text, sizeof(instr->text), "ins %c%d.%c[%d], %c%d.%c[%d]", r, Rd, s, dst_index, r, Rn, s, src_index);
761 }
762 }
763 }
764 } else if (((w >> 21) & 0x4F8) == 0x78) {
765 if ((w >> 10) & 1) {
766 if (((w >> 19) & 0xf) == 0) {
767 snprintf(instr->text, sizeof(instr->text), "(asimd modified immediate %08x)", w);
768 } else {
769 bool Q = GetQ(w);
770 bool U = GetU(w);
771 int immh = (w >> 19) & 0xf;
772 int immb = (w >> 16) & 7;
773 int opcode = (w >> 11) & 0x1f;
774 const char *opnamesU0[32] = {
775 "sshr", 0, "ssra", 0,
776 "srshr", 0, "srsra", 0,
777 0, 0, "shl", 0,
778 0, 0, "sqshl", 0,
779 "shrn", "rshrn", "sqshrn", "sqrshrn",
780 "sshll", 0, 0, 0,
781 0, 0, 0, 0,
782 "scvtf", 0, 0, "fcvtzs",
783 };
784 const char *opnamesU1[32] = {
785 "ushr", 0, "usra", 0,
786 "urshr", 0, "ursra", 0,
787 "sri", 0, "sli", 0,
788 "sqslu", 0, "uqshl", 0,
789 "sqshrun", "sqrshrun", "uqshrn", "uqrshrn",
790 "ushll", 0, 0, 0,
791 0, 0, 0, 0,
792 "ucvtf", 0, 0, "fcvtzu",
793 };
794 const char *opname = U ? opnamesU1[opcode] : opnamesU0[opcode];
795 const char *two = Q ? "2" : ""; // TODO: This doesn't apply to all the ops
796 if (opname && (!strcmp(opname, "scvtf") || !strcmp(opname, "ucvtf"))) {
797 int esize = (8 << HighestSetBit(immh));
798 int shift = 2 * esize - ((immh << 3) | immb);
799 int r = Q ? 'q' : 'd';
800 snprintf(instr->text, sizeof(instr->text), "%ccvtf %c%d.s, %c%d.s, #%d", U ? 'u' : 's', r, Rd, r, Rn, shift);
801 } else if (opname && (!strcmp(opname, "ushr") || !strcmp(opname, "sshr"))) {
802 int esize = (8 << HighestSetBit(immh));
803 int shift = esize * 2 - ((immh << 3) | immb);
804 int r = Q ? 'q' : 'd';
805 snprintf(instr->text, sizeof(instr->text), "%s.%d %c%d, %c%d, #%d", opname, esize, r, Rd, r, Rn, shift);
806 } else if (opname && (!strcmp(opname, "rshrn") || !strcmp(opname, "shrn"))) {
807 int esize = (8 << HighestSetBit(immh));
808 int shift = esize * 2 - ((immh << 3) | immb);
809 snprintf(instr->text, sizeof(instr->text), "%s%s.%d.%d d%d, q%d, #%d", opname, two, esize, esize * 2, Rd, Rn, shift);
810 } else if (opname && (!strcmp(opname, "shl"))) {
811 int esize = (8 << HighestSetBit(immh));
812 int r = Q ? 'q' : 'd';
813 int shift = ((immh << 3) | immb) - esize;
814 snprintf(instr->text, sizeof(instr->text), "%s.%d %c%d, %c%d, #%d", opname, esize, r, Rd, r, Rn, shift);
815 } else if (opname) {
816 int esize = (8 << HighestSetBit(immh));
817 int shift = ((immh << 3) | immb) - esize;
818 if (shift == 0 && opcode == 0x14) {
819 snprintf(instr->text, sizeof(instr->text), "%cxtl%s.%d.%d q%d, d%d", U ? 'u' : 's', two, esize * 2, esize, Rd, Rn);
820 } else {
821 snprintf(instr->text, sizeof(instr->text), "%s%s.%d.%d q%d, d%d, #%d", opname, two, esize * 2, esize, Rd, Rn, shift);
822 }
823 } else {
824 snprintf(instr->text, sizeof(instr->text), "(asimd shift-by-immediate %08x)", w);
825 }
826 }
827 } else {
828 bool Q = GetQ(w);
829 bool U = GetU(w);
830 int size = (w >> 22) & 3;
831 bool L = (w >> 21) & 1;
832 bool M = (w >> 20) & 1;
833 bool H = (w >> 11) & 1;
834 int opcode = (w >> 12) & 0xf;
835 if (size & 0x2) {
836 const char *opname = 0;
837 switch (opcode) {
838 case 1: opname = "fmla"; break;
839 case 5: opname = "fmls"; break;
840 case 9: opname = "fmul"; break;
841 }
842 int index;
843 if ((size & 1) == 0) {
844 index = (H << 1) | (int)L;
845 } else {
846 index = H;
847 }
848 char r = Q ? 'q' : 'd';
849 const char *arrangement = GetArrangement(Q, size & 1);
850 snprintf(instr->text, sizeof(instr->text), "%s %c%d, %c%d, %c%d.%s[%d]", opname, r, Rd, r, Rn, r, Rm, arrangement, index);
851 } else {
852 snprintf(instr->text, sizeof(instr->text), "(asimd vector x indexed elem %08x)", w);
853 }
854 }
855 } else {
856 bail:
857 snprintf(instr->text, sizeof(instr->text), "(FP1 %08x)", w);
858 }
859 }
860
861 // (w >> 25) & 0xF == f
FPandASIMD2(uint32_t w,uint64_t addr,Instruction * instr)862 static void FPandASIMD2(uint32_t w, uint64_t addr, Instruction *instr) {
863 int Rd = w & 0x1f;
864 int Rn = (w >> 5) & 0x1f;
865 int Rm = (w >> 16) & 0x1f;
866 int type = (w >> 22) & 0x3;
867 int sf = (w >> 31);
868 if (((w >> 21) & 0x2F9) == 0xF0) {
869 int rmode = (w >> 19) & 3;
870 int opcode = (w >> 16) & 7;
871 int scale = 64 - ((w >> 10) & 0x3f);
872 char fr = type == 1 ? 'd' : 's';
873 char ir = sf == 1 ? 'x' : 'w';
874 char sign = (opcode & 1) ? 'u' : 's';
875 if (rmode == 0) {
876 snprintf(instr->text, sizeof(instr->text), "%ccvtf %c%d, %c%d, #%d", sign, fr, Rd, ir, Rn, scale);
877 } else if (rmode == 3) {
878 snprintf(instr->text, sizeof(instr->text), "fcvtz%c %c%d, %c%d, #%d", sign, ir, Rd, fr, Rn, scale);
879 } else {
880 snprintf(instr->text, sizeof(instr->text), "(float<->fixed %08x)", w);
881 }
882 } else if (((w >> 21) & 0x2F9) == 0xF1) {
883 int opcode = (w >> 16) & 7;
884 if (((w >> 10) & 3) == 0) {
885 if (((w >> 10) & 7) == 4) {
886 uint8_t uimm8 = (w >> 13) & 0xff;
887 float fl_imm = Arm64Gen::FPImm8ToFloat(uimm8);
888 char fr = ((w >> 22) & 1) ? 'd' : 's';
889 snprintf(instr->text, sizeof(instr->text), "fmov %c%d, #%f", fr, Rd, fl_imm);
890 } else if (((w >> 10) & 0xf) == 8) {
891 int opcode2 = w & 0x1f;
892 int e = opcode2 >> 4;
893 int z = (opcode2 >> 3) & 1;
894 char r = type == 1 ? 'd' : 's';
895 if (z) {
896 snprintf(instr->text, sizeof(instr->text), "fcmp %c%d, #0.0", r, Rn);
897 } else {
898 snprintf(instr->text, sizeof(instr->text), "fcmp %c%d, %c%d", r, Rn, r, Rm);
899 }
900 } else if (((w >> 10) & 0x1f) == 0x10) {
901 // snprintf(instr->text, sizeof(instr->text), "(data 1-source %08x)", w);
902 const char *opnames[4] = { "fmov", "fabs", "fneg", "fsqrt" };
903 int opc = (w >> 15) & 0x3;
904 snprintf(instr->text, sizeof(instr->text), "%s s%d, s%d", opnames[opc], Rd, Rn); // TODO: Support doubles too
905 } else if (((w >> 10) & 0x1bf) == 0x180) {
906 // Generalized FMOV
907 char ir = sf ? 'x' : 'w';
908 bool tosimd = (opcode & 0x1);
909 char fr = ((w >> 22) & 1) ? 'd' : 's';
910 if (tosimd) {
911 snprintf(instr->text, sizeof(instr->text), "fmov %c%d, %c%d", fr, Rd, ir, Rn);
912 } else {
913 snprintf(instr->text, sizeof(instr->text), "fmov %c%d, %c%d", ir, Rd, fr, Rn);
914 }
915 } else if (((w >> 10) & 0x3f) == 0x0 && opcode == 0) {
916 char ir = sf ? 'x' : 'w';
917 char roundmode = "npmz"[(w >> 19) & 3];
918 if (opcode & 0x4)
919 roundmode = 'a';
920 char fr = ((w >> 22) & 1) ? 'd' : 's';
921 snprintf(instr->text, sizeof(instr->text), "fcvt%cs %c%d, %c%d", roundmode, ir, Rd, fr, Rn);
922 } else if ((opcode & 6) == 2) {
923 char ir = sf ? 'x' : 'w';
924 char fr = ((w >> 22) & 1) ? 'd' : 's';
925 char sign = (opcode & 1) ? 'u' : 's';
926 snprintf(instr->text, sizeof(instr->text), "%ccvtf %c%d, %c%d", sign, fr, Rd, ir, Rn);
927 }
928 } else if (((w >> 10) & 3) == 1) {
929 snprintf(instr->text, sizeof(instr->text), "(float cond compare %08x)", w);
930 } else if (((w >> 10) & 3) == 2) {
931 int opc = (w >> 12) & 0xf;
932 const char *opnames[9] = { "fmul", "fdiv", "fadd", "fsub", "fmax", "fmin", "fmaxnm", "fminnm", "fnmul" };
933 char r = ((w >> 22) & 1) ? 'd' : 's';
934 snprintf(instr->text, sizeof(instr->text), "%s %c%d, %c%d, %c%d", opnames[opc], r, Rd, r, Rn, r, Rm);
935 } else if (((w >> 10) & 3) == 3) {
936 char fr = ((w >> 22) & 1) ? 'd' : 's';
937 int cond = (w >> 12) & 0xf;
938 snprintf(instr->text, sizeof(instr->text), "fcsel %c%d, %c%d, %c%d, %s", fr, Rd, fr, Rn, fr, Rm, condnames[cond]);
939 }
940 } else if (((w >> 21) & 0x2F8) == 0xF8) {
941 int opcode = ((w >> 15) & 1) | ((w >> 20) & 2);
942 const char *opnames[9] = { "fmadd", "fmsub", "fnmadd", "fnmsub" };
943 int size = (w >> 22) & 1;
944 char r = size ? 'd' : 's';
945 int Ra = (w >> 10) & 0x1f;
946 snprintf(instr->text, sizeof(instr->text), "%s %c%d, %c%d, %c%d, %c%d", opnames[opcode], r, Rd, r, Rn, r, Rm, r, Ra);
947 } else if (((w >> 21) & 0x2F9) == 0x2F1) {
948 if (((w >> 10) & 3) == 0) {
949 snprintf(instr->text, sizeof(instr->text), "(asimd scalar three different %08x)", w);
950 } else if (((w >> 10) & 1) == 1) {
951 snprintf(instr->text, sizeof(instr->text), "(asimd scalar three same %08x)", w);
952 } else if (((w >> 10) & 3) == 2) {
953 // asimd scalar two-reg misc: This is a particularly sad and messy encoding :/
954 if (((w >> 17) & 0xf) == 0) {
955 int sz = (w >> 22) & 3;
956 char r = (sz & 1) ? 'd' : 's';
957 int opcode = (w >> 12) & 0x1f;
958 bool U = ((w >> 29) & 1);
959 const char *opname = NULL;
960 bool zero = false;
961 bool sign_suffix = false;
962 bool sign_prefix = false;
963 if ((sz & 2) == 0) {
964 switch (opcode) {
965 case 0x1a: opname = "fcvtn"; sign_suffix = true; break;
966 case 0x1b: opname = "fcvtm"; sign_suffix = true; break;
967 case 0x1c: opname = "fcvta"; sign_suffix = true; break;
968 case 0x1d: opname = "cvtf"; sign_prefix = true; break;
969 }
970 } else {
971 if (U == 0) {
972 switch (opcode) {
973 case 0xC: opname = "fcmgt"; zero = true; break;
974 case 0xD: opname = "fcmeq"; zero = true; break;
975 case 0xE: opname = "fcmlt"; zero = true; break;
976 case 0x1A: opname = "fcvtp"; sign_suffix = true; break;
977 case 0x1B: opname = "fcvtz"; sign_suffix = true; break;
978 }
979 } else {
980 switch (opcode) {
981 case 0x1A: opname = "fcvtp"; sign_suffix = true; break;
982 case 0x1B: opname = "fcvtz"; sign_suffix = true; break;
983 }
984 }
985 }
986 if (!opname) { // These ignore size.
987 if (U == 0) {
988 switch (opcode) {
989 case 3: opname = "suqadd"; break;
990 case 7: opname = "sqabs"; break;
991 case 8: opname = "cmgt"; zero = true; break;
992 case 9: opname = "cmeq"; zero = true; break;
993 case 0xa: opname = "cmlt"; zero = true; break;
994 case 0xb: opname = "abs"; break;
995 case 0xc: opname = "sqxtn?"; break;
996 }
997 } else {
998 switch (opcode) {
999 case 3: opname = "usqadd"; break;
1000 case 7: opname = "sqneg"; break;
1001 case 8: opname = "cmge"; zero = true; break;
1002 case 9: opname = "cmle"; zero = true; break;
1003 case 0xB: opname = "neg"; break;
1004 case 0x12: opname = "sqxtun?"; break;
1005 case 0x14: opname = "uqxtn?"; break;
1006 }
1007 }
1008 }
1009
1010 if (opname) {
1011 if (sign_suffix) {
1012 char sign = U ? 'u' : 's';
1013 snprintf(instr->text, sizeof(instr->text), "%s%c %c%d, %c%d", opname, sign, r, Rd, r, Rn);
1014 } else if (sign_prefix) {
1015 char sign = U ? 'u' : 's';
1016 snprintf(instr->text, sizeof(instr->text), "%c%s %c%d, %c%d", sign, opname, r, Rd, r, Rn);
1017 } else if (!zero) {
1018 snprintf(instr->text, sizeof(instr->text), "%s %c%d, %c%d", opname, r, Rd, r, Rn);
1019 } else if (zero) {
1020 snprintf(instr->text, sizeof(instr->text), "%s %c%d, #0.0", opname, r, Rd);
1021 }
1022 } else {
1023 snprintf(instr->text, sizeof(instr->text), "(asimd scalar two-reg misc %08x)", w);
1024 }
1025 } else if (((w >> 17) & 0xf) == 8) {
1026 snprintf(instr->text, sizeof(instr->text), "(asimd scalar pair-wise %08x)", w);
1027 } else {
1028 snprintf(instr->text, sizeof(instr->text), "(asimd scalar stuff %08x)", w);
1029 }
1030 } else {
1031 snprintf(instr->text, sizeof(instr->text), "(asimd stuff %08x)", w);
1032 }
1033 } else if (((w >> 21) & 0x2F1) == 0x2F0) {
1034 // many lines
1035 snprintf(instr->text, sizeof(instr->text), "(asimd stuff %08x)", w);
1036 } else {
1037 snprintf(instr->text, sizeof(instr->text), "(FP2 %08x)", w);
1038 }
1039 }
1040
DisassembleInstruction(uint32_t w,uint64_t addr,Instruction * instr,SymbolCallback symbolCallback)1041 static void DisassembleInstruction(uint32_t w, uint64_t addr, Instruction *instr, SymbolCallback symbolCallback) {
1042 memset(instr, 0, sizeof(*instr));
1043
1044 // Identify the main encoding groups. See C3.1 A64 instruction index by encoding
1045 int id = (w >> 25) & 0xF;
1046 switch (id) {
1047 case 0: case 1: case 2: case 3: // 00xx
1048 instr->undefined = true;
1049 break;
1050 case 8: case 9:
1051 DataProcessingImmediate(w, addr, instr);
1052 break;
1053 case 0xA: case 0xB:
1054 BranchExceptionAndSystem(w, addr, instr, symbolCallback);
1055 break;
1056 case 4: case 6: case 0xC: case 0xE:
1057 LoadStore(w, addr, instr);
1058 break;
1059 case 5: case 0xD:
1060 DataProcessingRegister(w, addr, instr);
1061 break;
1062 case 7:
1063 FPandASIMD1(w, addr, instr);
1064 break;
1065 case 0xF:
1066 FPandASIMD2(w, addr, instr);
1067 break;
1068 }
1069 }
1070
Arm64Dis(uint64_t addr,uint32_t w,char * output,int bufsize,bool includeWord,SymbolCallback symbolCallback)1071 void Arm64Dis(uint64_t addr, uint32_t w, char *output, int bufsize, bool includeWord, SymbolCallback symbolCallback) {
1072 Instruction instr;
1073 DisassembleInstruction(w, addr, &instr, symbolCallback);
1074 char temp[256];
1075 if (includeWord) {
1076 snprintf(output, bufsize, "%08x\t%s", w, instr.text);
1077 } else {
1078 snprintf(output, bufsize, "%s", instr.text);
1079 }
1080 if (instr.undefined || instr.badbits || instr.oddbits) {
1081 if (instr.undefined) snprintf(output, bufsize, "%08x\t[undefined instr]", w);
1082 if (instr.badbits) snprintf(output, bufsize, "%08x\t[illegal bits]", w);
1083
1084 // strcat(output, " ? (extra bits)");
1085 if (instr.oddbits) {
1086 snprintf(temp, sizeof(temp), " [unexpected bits %08x]", w);
1087 strcat(output, temp);
1088 }
1089 }
1090 // zap tabs
1091 while (*output) {
1092 if (*output == '\t')
1093 *output = ' ';
1094 output++;
1095 }
1096 }
1097