2015-03-06 20:42:19 +00:00
|
|
|
// Copyright (c) 2015- PPSSPP Project.
|
|
|
|
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU General Public License as published by
|
|
|
|
// the Free Software Foundation, version 2.0 or later versions.
|
|
|
|
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU General Public License 2.0 for more details.
|
|
|
|
|
|
|
|
// A copy of the GPL 2.0 should have been included with the program.
|
|
|
|
// If not, see http://www.gnu.org/licenses/
|
|
|
|
|
|
|
|
// Official git repository and contact information can be found at
|
|
|
|
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
|
|
|
|
|
|
|
// Basic ARM64 disassembler.
|
2015-03-07 21:27:44 +00:00
|
|
|
|
2015-03-06 20:42:19 +00:00
|
|
|
// No promises of accuracy, mostly just made to debug JIT code.
|
2015-03-07 21:27:44 +00:00
|
|
|
// Contains just enough to sort of understand what's going on without having to resort to an
|
|
|
|
// external disassembler all the time...
|
2015-03-06 20:42:19 +00:00
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
2015-03-15 19:51:08 +00:00
|
|
|
#include "Common/Arm64Emitter.h"
|
2015-03-06 20:42:19 +00:00
|
|
|
#include "Common/StringUtils.h"
|
|
|
|
|
|
|
|
struct Instruction {
|
|
|
|
char text[128];
|
|
|
|
bool undefined;
|
|
|
|
bool badbits;
|
|
|
|
bool oddbits;
|
|
|
|
};
|
|
|
|
|
2015-03-18 15:02:25 +00:00
|
|
|
static const char * const shiftnames[4] = { "lsl", "lsr", "asr", "ror" };
|
|
|
|
static const char * const extendnames[8] = { "uxtb", "uxth", "uxtw", "uxtx", "sxtb", "sxth", "sxtw", "sxtx" };
|
|
|
|
|
2015-03-06 20:42:19 +00:00
|
|
|
int SignExtend26(int x) {
|
2015-03-15 19:51:08 +00:00
|
|
|
return (x & 0x02000000) ? (0xFC000000 | x) : (x & 0x3FFFFFF);
|
2015-03-06 20:42:19 +00:00
|
|
|
}
|
|
|
|
|
2015-03-08 10:26:45 +00:00
|
|
|
int SignExtend19(int x) {
|
2015-03-15 19:51:08 +00:00
|
|
|
return (x & 0x00040000) ? (0xFFF80000 | x) : (x & 0x7FFFF);
|
2015-03-08 10:26:45 +00:00
|
|
|
}
|
|
|
|
|
2015-03-15 19:51:08 +00:00
|
|
|
int SignExtend9(int x) {
|
|
|
|
return (x & 0x00000100) ? (0xFFFFFE00 | x) : (x & 0x1FF);
|
|
|
|
}
|
|
|
|
|
|
|
|
int SignExtend12(int x) {
|
|
|
|
return (x & 0x00000800) ? (0xFFFFF000 | x) : (x & 0xFFF);
|
|
|
|
}
|
|
|
|
|
2015-03-18 13:44:01 +00:00
|
|
|
int HighestSetBit(int value) {
|
|
|
|
int highest = 0;
|
|
|
|
for (int i = 0; i < 32; i++) {
|
|
|
|
if (value & (1 << i))
|
|
|
|
highest = i;
|
|
|
|
}
|
|
|
|
return highest;
|
|
|
|
}
|
|
|
|
|
2015-03-18 15:02:25 +00:00
|
|
|
static uint64_t Ones(int len) {
|
2015-03-18 13:44:01 +00:00
|
|
|
if (len == 0x40) {
|
|
|
|
return 0xFFFFFFFFFFFFFFFF;
|
|
|
|
}
|
|
|
|
return (1ULL << len) - 1;
|
|
|
|
}
|
|
|
|
|
2015-03-18 15:02:25 +00:00
|
|
|
static uint64_t Replicate(uint64_t value, int esize) {
|
2015-03-18 13:44:01 +00:00
|
|
|
uint64_t out = 0;
|
|
|
|
value &= Ones(esize);
|
|
|
|
for (int i = 0; i < 64; i += esize) {
|
|
|
|
out |= value << i;
|
|
|
|
}
|
|
|
|
return out;
|
|
|
|
}
|
|
|
|
|
2015-03-18 15:02:25 +00:00
|
|
|
static uint64_t ROR(uint64_t value, int amount, int esize) {
|
2015-03-18 13:44:01 +00:00
|
|
|
uint64_t rotated = (value >> amount) | (value << (esize - amount));
|
|
|
|
return rotated & Ones(esize);
|
|
|
|
}
|
|
|
|
|
|
|
|
void DecodeBitMasks(int immN, int imms, int immr, uint64_t *tmask, uint64_t *wmask) {
|
|
|
|
// Compute log2 of element size
|
|
|
|
// 2^len must be in range [2, M]
|
|
|
|
int len = HighestSetBit((immN << 6) | ((~imms) & 0x3f));
|
|
|
|
// if len < 1 then ReservedValue();
|
|
|
|
// assert M >= (1 << len);
|
|
|
|
// Determine S, R and S - R parameters
|
|
|
|
int levels = Ones(len);
|
|
|
|
uint32_t S = imms & levels;
|
|
|
|
uint32_t R = immr & levels;
|
|
|
|
int diff = S - R; // 6-bit subtract with borrow
|
|
|
|
int esize = 1 << len;
|
|
|
|
int d = diff & Ones(len - 1);
|
|
|
|
uint32_t welem = Ones(S + 1);
|
|
|
|
uint32_t telem = Ones(d + 1);
|
|
|
|
if (wmask) {
|
|
|
|
uint64_t rotated = ROR(welem, R, esize);
|
|
|
|
*wmask = Replicate(rotated, esize);
|
|
|
|
}
|
|
|
|
if (tmask) {
|
|
|
|
*tmask = Replicate(telem, esize);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-15 19:51:08 +00:00
|
|
|
static const char *conds[16] = {
|
|
|
|
"eq", // Equal
|
|
|
|
"ne", // Not equal
|
|
|
|
"cs", // Carry Set "HS"
|
|
|
|
"cc", // Carry Clear "LO"
|
|
|
|
"mi", // Minus (Negative)
|
|
|
|
"pl", // Plus
|
|
|
|
"vs", // Overflow
|
|
|
|
"vc", // No Overflow
|
|
|
|
"hi", // Unsigned higher
|
|
|
|
"ls", // Unsigned lower or same
|
|
|
|
"ge", // Signed greater than or equal
|
|
|
|
"lt", // Signed less than
|
|
|
|
"gt", // Signed greater than
|
|
|
|
"le", // Signed less than or equal
|
|
|
|
"al", // Always (unconditional) 14
|
|
|
|
};
|
|
|
|
|
2015-03-06 20:42:19 +00:00
|
|
|
static void DataProcessingImmediate(uint32_t w, uint64_t addr, Instruction *instr) {
|
2015-03-08 10:26:45 +00:00
|
|
|
int Rd = w & 0x1f;
|
|
|
|
int Rn = (w >> 5) & 0x1f;
|
|
|
|
char r = ((w >> 31) & 1) ? 'x' : 'w';
|
|
|
|
if (((w >> 23) & 0x3f) == 0x25) {
|
2015-03-15 19:51:08 +00:00
|
|
|
// Constant initialization.
|
2015-03-08 10:26:45 +00:00
|
|
|
int imm16 = (w >> 5) & 0xFFFF;
|
|
|
|
int opc = (w >> 29) & 3;
|
2015-03-15 22:30:13 +00:00
|
|
|
int shift = ((w >> 21) & 0x3) * 16;
|
2015-03-08 10:26:45 +00:00
|
|
|
const char *opnames[4] = { "movn", "(undef)", "movz", "movk" };
|
2015-03-18 13:44:01 +00:00
|
|
|
snprintf(instr->text, sizeof(instr->text), "%s %c%d, #0x%04x << %d", opnames[opc], r, Rd, imm16, shift);
|
2015-03-08 10:26:45 +00:00
|
|
|
} else if (((w >> 24) & 0x1F) == 0x10) {
|
2015-03-15 19:51:08 +00:00
|
|
|
// Address generation relative to PC
|
2015-03-08 10:26:45 +00:00
|
|
|
int op = w >> 31;
|
2015-03-15 19:51:08 +00:00
|
|
|
int imm = SignExtend19(w >> 5);
|
2015-03-08 10:26:45 +00:00
|
|
|
if (op & 1) imm <<= 12;
|
|
|
|
u64 daddr = addr + imm;
|
2015-03-18 13:44:01 +00:00
|
|
|
snprintf(instr->text, sizeof(instr->text), "%s x%d, #0x%04x%08x", op ? "adrp" : "adr", Rd, daddr >> 32, daddr & 0xFFFFFFFF);
|
2015-03-08 10:26:45 +00:00
|
|
|
} else if (((w >> 24) & 0x1F) == 0x11) {
|
2015-03-15 19:51:08 +00:00
|
|
|
// Add/subtract immediate value
|
|
|
|
int op = (w >> 30) & 1;
|
2015-03-08 10:26:45 +00:00
|
|
|
int imm = ((w >> 10) & 0xFFF);
|
2015-03-15 19:51:08 +00:00
|
|
|
int shift = ((w >> 22) & 0x3) * 16;
|
2015-03-18 15:02:25 +00:00
|
|
|
const char *s = ((w >> 29) & 1) ? "s" : "";
|
2015-03-08 10:26:45 +00:00
|
|
|
imm <<= shift;
|
2015-03-18 15:02:25 +00:00
|
|
|
snprintf(instr->text, sizeof(instr->text), "%s%s %c%d, %c%d, #%d", op == 0 ? "add" : "sub", s, r, Rd, r, Rn, imm);
|
2015-03-18 13:44:01 +00:00
|
|
|
} else if (((w >> 23) & 0x3f) == 0x24) {
|
|
|
|
int immr = (w >> 16) & 0x3f;
|
|
|
|
int imms = (w >> 10) & 0x3f;
|
|
|
|
int N = (w >> 22) & 1;
|
|
|
|
int opc = (w >> 29) & 3;
|
|
|
|
const char *opname[4] = { "and", "orr", "eor", "ands" };
|
|
|
|
uint64_t wmask;
|
|
|
|
DecodeBitMasks(N, imms, immr, NULL, &wmask);
|
|
|
|
if (((w >> 31) & 1) && wmask & 0xFFFFFFFF00000000ULL)
|
|
|
|
snprintf(instr->text, sizeof(instr->text), "%s %c%d, %c%d, #0x%x%08x", opname[opc], r, Rd, r, Rn, (wmask >> 32), (wmask & 0xFFFFFFFF));
|
|
|
|
else
|
|
|
|
snprintf(instr->text, sizeof(instr->text), "%s %c%d, %c%d, #0x%x", opname[opc], r, Rd, r, Rn, (uint32_t)wmask);
|
2015-03-08 10:26:45 +00:00
|
|
|
} else {
|
|
|
|
snprintf(instr->text, sizeof(instr->text), "(DPI %08x)", w);
|
|
|
|
}
|
2015-03-06 20:42:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void BranchExceptionAndSystem(uint32_t w, uint64_t addr, Instruction *instr) {
|
2015-03-15 19:51:08 +00:00
|
|
|
int Rt = w & 0x1f;
|
|
|
|
int Rn = (w >> 5) & 0x1f;
|
2015-03-06 20:42:19 +00:00
|
|
|
if (((w >> 26) & 0x1F) == 5) {
|
|
|
|
// Unconditional branch / branch+link
|
2015-03-15 19:51:08 +00:00
|
|
|
int offset = SignExtend26(w) << 2;
|
2015-03-06 20:42:19 +00:00
|
|
|
uint64_t target = addr + offset;
|
2015-03-15 19:51:08 +00:00
|
|
|
snprintf(instr->text, sizeof(instr->text), "b%s %04x%08x", (w >> 31) ? "l" : "", (target >> 32), (target & 0xFFFFFFFF));
|
2015-03-07 21:27:44 +00:00
|
|
|
} else if (((w >> 25) & 0x3F) == 0x1A) {
|
2015-03-15 19:51:08 +00:00
|
|
|
// Compare and branch
|
|
|
|
int op = (w >> 24) & 1;
|
|
|
|
const char *opname[2] = { "cbz", "cbnz" };
|
|
|
|
char r = ((w >> 31) & 1) ? 'x' : 'w';
|
|
|
|
int offset = SignExtend19(w >> 5);
|
|
|
|
snprintf(instr->text, sizeof(instr->text), "%s %c%d", op, r, Rt);
|
2015-03-07 21:27:44 +00:00
|
|
|
} else if (((w >> 25) & 0x3F) == 0x1B) {
|
2015-03-15 19:51:08 +00:00
|
|
|
// Test and branch
|
2015-03-07 21:27:44 +00:00
|
|
|
snprintf(instr->text, sizeof(instr->text), "(test & branch %08x)", w);
|
|
|
|
} else if (((w >> 25) & 0x7F) == 0x2A) {
|
2015-03-15 19:51:08 +00:00
|
|
|
// Conditional branch
|
|
|
|
int offset = SignExtend19(w >> 5) << 2;
|
|
|
|
uint64_t target = addr + offset;
|
|
|
|
int cond = w & 0xF;
|
2015-03-18 13:44:01 +00:00
|
|
|
snprintf(instr->text, sizeof(instr->text), "b.%s %04x%08x", conds[cond], (target >> 32), (target & 0xFFFFFFFF));
|
2015-03-07 21:27:44 +00:00
|
|
|
} else if ((w >> 24) == 0xD4) {
|
|
|
|
snprintf(instr->text, sizeof(instr->text), "(exception-gen %08x)", w);
|
|
|
|
} else if (((w >> 20) & 0xFFC) == 0xD50) {
|
|
|
|
snprintf(instr->text, sizeof(instr->text), "(system-reg %08x)", w);
|
|
|
|
} else if (((w >> 25) & 0x7F) == 0x6B) {
|
2015-03-15 19:51:08 +00:00
|
|
|
int op = (w >> 21) & 3;
|
|
|
|
const char *opname[4] = { "b", "bl", "ret", "(unk)" };
|
|
|
|
snprintf(instr->text, sizeof(instr->text), "%s x%d", opname[op], Rn);
|
2015-03-06 20:42:19 +00:00
|
|
|
} else {
|
2015-03-07 21:27:44 +00:00
|
|
|
snprintf(instr->text, sizeof(instr->text), "(BRX ?? %08x)", w);
|
2015-03-06 20:42:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void LoadStore(uint32_t w, uint64_t addr, Instruction *instr) {
|
2015-03-15 19:51:08 +00:00
|
|
|
int size = w >> 30;
|
|
|
|
int imm9 = SignExtend9((w >> 12) & 0x1FF);
|
|
|
|
int Rt = (w & 0x1F);
|
|
|
|
int Rn = ((w >> 5) & 0x1F);
|
2015-03-15 23:29:20 +00:00
|
|
|
int Rm = ((w >> 16) & 0x1F);
|
|
|
|
int option = (w >> 13) & 0x7;
|
2015-03-15 19:51:08 +00:00
|
|
|
int opc = (w >> 22) & 0x3;
|
2015-03-15 23:29:20 +00:00
|
|
|
char r = size == 3 ? 'x' : 'w';
|
2015-03-16 23:54:56 +00:00
|
|
|
const char *opname[4] = { "str", "ldr", "str", "ldr" };
|
2015-03-15 19:51:08 +00:00
|
|
|
const char *sizeSuffix[4] = { "b", "w", "", "" };
|
|
|
|
|
2015-03-15 23:29:20 +00:00
|
|
|
if (((w >> 21) & 1) == 1) {
|
|
|
|
// register offset
|
|
|
|
snprintf(instr->text, sizeof(instr->text), "%s%s %c%d, [x%d + w%d]", opname[opc], sizeSuffix[size], r, Rt, Rn, Rm);
|
|
|
|
return;
|
|
|
|
} else if (((w >> 27) & 7) == 7) {
|
2015-03-15 19:51:08 +00:00
|
|
|
int V = (w >> 26) & 1;
|
2015-03-16 23:54:56 +00:00
|
|
|
bool index_unsigned = ((w >> 24) & 3) == 1;
|
|
|
|
int imm12 = SignExtend12((w >> 10) & 0xFFF) << size;
|
2015-03-15 19:51:08 +00:00
|
|
|
if (V == 0) {
|
2015-03-16 23:54:56 +00:00
|
|
|
// Integer type
|
2015-03-15 19:51:08 +00:00
|
|
|
if (index_unsigned) {
|
|
|
|
snprintf(instr->text, sizeof(instr->text), "%s%s %c%d, [x%d + %d]", opname[opc], sizeSuffix[size], r, Rt, Rn, imm12);
|
|
|
|
return;
|
|
|
|
}
|
2015-03-16 23:54:56 +00:00
|
|
|
} else {
|
|
|
|
// FP/Vector type
|
|
|
|
char vr = '!';
|
|
|
|
if (opc == 3 && size == 0) {
|
|
|
|
vr = 'q';
|
|
|
|
} else {
|
|
|
|
vr = "bhsd"[size];
|
|
|
|
}
|
|
|
|
if (index_unsigned) {
|
|
|
|
snprintf(instr->text, sizeof(instr->text), "%s %c%d, [x%d + %d]", opname[opc], vr, Rt, Rn, imm12);
|
|
|
|
return;
|
|
|
|
}
|
2015-03-15 19:51:08 +00:00
|
|
|
}
|
|
|
|
}
|
2015-03-06 20:42:19 +00:00
|
|
|
snprintf(instr->text, sizeof(instr->text), "(LS %08x)", w);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void DataProcessingRegister(uint32_t w, uint64_t addr, Instruction *instr) {
|
2015-03-15 19:51:08 +00:00
|
|
|
int Rd = w & 0x1F;
|
|
|
|
int Rn = (w >> 5) & 0x1F;
|
|
|
|
int Rm = (w >> 16) & 0x1F;
|
2015-03-07 21:27:44 +00:00
|
|
|
char r = ((w >> 31) & 1) ? 'x' : 'w';
|
2015-03-06 20:42:19 +00:00
|
|
|
|
2015-03-18 15:02:25 +00:00
|
|
|
if (((w >> 21) & 0x2FF) == 0x2D6) {
|
2015-03-15 19:51:08 +00:00
|
|
|
// Data processing
|
|
|
|
int opcode2 = (w >> 16) & 0x1F;
|
|
|
|
if (opcode2 == 0) {
|
|
|
|
int opcode = (w >> 10) & 0x3F;
|
|
|
|
// Data-processing (1 source)
|
|
|
|
const char *opname[8] = { "rbit", "rev16", "rev", "(unk)", "clz", "cls" };
|
|
|
|
const char *op = opcode2 >= 8 ? "unk" : opname[opcode];
|
|
|
|
snprintf(instr->text, sizeof(instr->text), "%s %c%d, %c%d", op, r, Rn, r, Rm);
|
|
|
|
} else {
|
|
|
|
// Data processing (2 source)
|
|
|
|
snprintf(instr->text, sizeof(instr->text), "(data-proc-2-source %08x)", w);
|
|
|
|
}
|
|
|
|
} else if (((w >> 24) & 0x1f) == 0xA) {
|
|
|
|
// Logical (shifted register)
|
|
|
|
int shift = (w >> 22) & 0x3;
|
|
|
|
int imm6 = (w >> 10) & 0x3f;
|
2015-03-16 23:54:56 +00:00
|
|
|
int N = (w >> 21) & 1;
|
2015-03-15 19:51:08 +00:00
|
|
|
int opc = (((w >> 29) & 3) << 1) | N;
|
|
|
|
const char *opnames[8] = { "and", "bic", "orr", "orn", "eor", "eon", "ands", "bics" };
|
2015-03-18 13:44:01 +00:00
|
|
|
if (opc == 2 && Rn == 31) {
|
2015-03-16 23:54:56 +00:00
|
|
|
// Special case for MOV (which is constructed from an ORR)
|
2015-03-18 15:57:06 +00:00
|
|
|
if (imm6 != 0) {
|
|
|
|
snprintf(instr->text, sizeof(instr->text), "mov %c%d, %c%d, %s #%d", r, Rd, r, Rm, shiftnames[shift], imm6);
|
|
|
|
} else {
|
|
|
|
snprintf(instr->text, sizeof(instr->text), "mov %c%d, %c%d", r, Rd, r, Rm);
|
|
|
|
}
|
2015-03-18 15:02:25 +00:00
|
|
|
} else if (imm6 == 0 && shift == 0) {
|
2015-03-15 19:51:08 +00:00
|
|
|
snprintf(instr->text, sizeof(instr->text), "%s %c%d, %c%d, %c%d", opnames[opc], r, Rd, r, Rn, r, Rm);
|
2015-03-07 21:27:44 +00:00
|
|
|
} else {
|
2015-03-18 15:57:06 +00:00
|
|
|
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);
|
2015-03-07 21:27:44 +00:00
|
|
|
}
|
2015-03-18 15:02:25 +00:00
|
|
|
} else if (((w >> 24) & 0x1f) == 0xB) {
|
|
|
|
// Arithmetic (shifted register)
|
|
|
|
bool S = (w >> 29) & 1;
|
|
|
|
int shift = (w >> 22) & 0x3;
|
|
|
|
int imm6 = (w >> 10) & 0x3f;
|
|
|
|
int opc = ((w >> 29) & 3);
|
|
|
|
const char *opnames[8] = { "add", "adds", "sub", "subs"};
|
|
|
|
if (imm6 == 0 && shift == 0) {
|
|
|
|
if (Rd == 31 && opc == 3) {
|
|
|
|
// It's a CMP
|
|
|
|
snprintf(instr->text, sizeof(instr->text), "%s %c%d, %c%d", "cmp", r, Rn, r, Rm);
|
|
|
|
} else {
|
|
|
|
snprintf(instr->text, sizeof(instr->text), "%s %c%d, %c%d, %c%d", opnames[opc], r, Rd, r, Rn, r, Rm);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
snprintf(instr->text, sizeof(instr->text), "(logical-shifted-register %08x", w);
|
|
|
|
}
|
|
|
|
} else if (((w >> 21) & 0xF) == 9) {
|
|
|
|
// Add/sub (extended register)
|
|
|
|
bool S = (w >> 29) & 1;
|
|
|
|
bool sub = (w >> 30) & 1;
|
|
|
|
int option = (w >> 13) & 0x7;
|
|
|
|
int imm3 = (w >> 10) & 0x7;
|
|
|
|
if (Rd == 31 && sub && S) {
|
|
|
|
// It's a CMP
|
|
|
|
snprintf(instr->text, sizeof(instr->text), "%s%s %c%d, %c%d, %s", "cmp", S ? "s" : "", r, Rn, r, Rm, extendnames[option]);
|
|
|
|
} else {
|
|
|
|
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]);
|
|
|
|
}
|
2015-03-18 15:57:06 +00:00
|
|
|
} else if (((w >> 21) & 0xFF) == 0xD6 && ((w >> 12) & 0xF) == 2) {
|
|
|
|
// Variable shifts
|
|
|
|
int opc = (w >> 10) & 3;
|
|
|
|
snprintf(instr->text, sizeof(instr->text), "%sv %c%d, %c%d, %c%d", shiftnames[opc], r, Rd, r, Rn, r, Rm);
|
2015-03-06 20:42:19 +00:00
|
|
|
} else {
|
2015-03-18 15:02:25 +00:00
|
|
|
// Logical (extended register)
|
2015-03-06 20:42:19 +00:00
|
|
|
snprintf(instr->text, sizeof(instr->text), "(DPR %08x)", w);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void FPandASIMD1(uint32_t w, uint64_t addr, Instruction *instr) {
|
|
|
|
snprintf(instr->text, sizeof(instr->text), "(FP1 %08x)", w);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void FPandASIMD2(uint32_t w, uint64_t addr, Instruction *instr) {
|
2015-03-16 23:54:56 +00:00
|
|
|
int Rd = w & 0x1f;
|
|
|
|
int Rn = (w >> 5) & 0x1f;
|
|
|
|
int Rm = (w >> 16) & 0x1f;
|
|
|
|
int type = (w >> 22) & 0x3;
|
|
|
|
if ((w >> 24) == 0x1E) {
|
2015-03-18 13:44:01 +00:00
|
|
|
if (((w >> 10) & 0xf9f) == 0x810) {
|
2015-03-16 23:54:56 +00:00
|
|
|
const char *opnames[4] = { "fmov", "fabs", "fneg", "fsqrt" };
|
|
|
|
int opc = (w >> 15) & 0x3;
|
2015-03-18 13:44:01 +00:00
|
|
|
snprintf(instr->text, sizeof(instr->text), "%s s%d, s%d", opnames[opc], Rd, Rn); // TODO: Support doubles too
|
2015-03-16 23:54:56 +00:00
|
|
|
} else if (((w >> 10) & 3) == 2) {
|
|
|
|
// FP data-proc (2 source)
|
|
|
|
int opc = (w >> 12) & 0xf;
|
|
|
|
if (type == 0 || type == 1) {
|
|
|
|
const char *opnames[9] = { "fmul", "fdiv", "fadd", "fsub", "fmax", "fmin", "fmaxnm", "fminnm", "fnmul" };
|
2015-03-18 13:44:01 +00:00
|
|
|
char r = 's'; // TODO: Support doubles too
|
2015-03-16 23:54:56 +00:00
|
|
|
snprintf(instr->text, sizeof(instr->text), "%s %c%d, %c%d, %c%d", opnames[opc], r, Rd, r, Rn, r, Rm);
|
|
|
|
} else {
|
|
|
|
snprintf(instr->text, sizeof(instr->text), "(FP2 %08x)", w);
|
|
|
|
}
|
2015-03-18 13:44:01 +00:00
|
|
|
} else {
|
|
|
|
snprintf(instr->text, sizeof(instr->text), "(FP2 %08x)", w);
|
2015-03-16 23:54:56 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
snprintf(instr->text, sizeof(instr->text), "(FP2 %08x)", w);
|
|
|
|
}
|
2015-03-06 20:42:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void DisassembleInstruction(uint32_t w, uint64_t addr, Instruction *instr) {
|
|
|
|
memset(instr, 0, sizeof(*instr));
|
|
|
|
|
|
|
|
// Identify the main encoding groups. See C3.1 A64 instruction index by encoding
|
|
|
|
int id = (w >> 25) & 0xF;
|
|
|
|
switch (id) {
|
|
|
|
case 0: case 1: case 2: case 3: // 00xx
|
|
|
|
instr->undefined = true;
|
|
|
|
break;
|
|
|
|
case 8: case 9:
|
|
|
|
DataProcessingImmediate(w, addr, instr);
|
|
|
|
break;
|
|
|
|
case 0xA: case 0xB:
|
|
|
|
BranchExceptionAndSystem(w, addr, instr);
|
|
|
|
break;
|
|
|
|
case 4: case 6: case 0xC: case 0xE:
|
|
|
|
LoadStore(w, addr, instr);
|
|
|
|
break;
|
|
|
|
case 5: case 0xD:
|
|
|
|
DataProcessingRegister(w, addr, instr);
|
|
|
|
break;
|
|
|
|
case 7:
|
|
|
|
FPandASIMD1(w, addr, instr);
|
|
|
|
break;
|
|
|
|
case 0xF:
|
|
|
|
FPandASIMD2(w, addr, instr);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Arm64Dis(uint64_t addr, uint32_t w, char *output, int bufsize, bool includeWord) {
|
|
|
|
Instruction instr;
|
|
|
|
DisassembleInstruction(w, addr, &instr);
|
|
|
|
char temp[256];
|
|
|
|
if (includeWord) {
|
|
|
|
snprintf(output, bufsize, "%08x\t%s", w, instr.text);
|
|
|
|
} else {
|
|
|
|
snprintf(output, bufsize, "%s", instr.text);
|
|
|
|
}
|
|
|
|
if (instr.undefined || instr.badbits || instr.oddbits) {
|
|
|
|
if (instr.undefined) snprintf(output, bufsize, "%08x\t[undefined instr]", w);
|
|
|
|
if (instr.badbits) snprintf(output, bufsize, "%08x\t[illegal bits]", w);
|
|
|
|
|
|
|
|
// strcat(output, " ? (extra bits)");
|
|
|
|
if (instr.oddbits) {
|
|
|
|
snprintf(temp, sizeof(temp), " [unexpected bits %08x]", w);
|
|
|
|
strcat(output, temp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// zap tabs
|
|
|
|
while (*output) {
|
|
|
|
if (*output == '\t')
|
|
|
|
*output = ' ';
|
|
|
|
output++;
|
|
|
|
}
|
|
|
|
}
|