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