Add disassembler for all data proc imm operations

This commit is contained in:
rkx1209 2018-01-01 04:18:56 +09:00
parent 713b1e0818
commit da24be5562
5 changed files with 196 additions and 21 deletions

View file

@ -8,7 +8,37 @@ static void UnallocatedOp(uint32_t insn) {
static bool LogicImmDecode(uint64_t *wmask, unsigned int immn, unsigned int imms, unsigned int immr) {
assert (immn < 2 && imms < 64 && immr < 64);
uint64_t mask;
unsigned int e, levels, s, r;
int len = 31 - clz32((immn << 6 | (~imms & 0x3f)));
if (len < 1) {
/* This is the immn == 0, imms == 0x11111x case */
return false;
}
e = 1 << len;
levels = e - 1;
s = imms & levels;
r = immr & levels;
if (s == levels) {
/* <length of run - 1> mustn't be all-ones. */
return false;
}
/* Create the value of one element: s+1 set bits rotated
* by r within the element (which is e bits wide)...
*/
mask = bitmask64(s + 1);
if (r) {
mask = (mask >> r) | (mask << (e - r));
mask &= bitmask64(e);
}
mask = replicate64 (mask, e);
*wmask = mask;
return true;
}
static void DisasPCRelAddr(uint32_t insn, DisasCallback *cb) {
unsigned int rd, page;
uint64_t offset, base;
@ -24,7 +54,7 @@ static void DisasPCRelAddr(uint32_t insn, DisasCallback *cb) {
offset <<= 12;
}
cb->MoviI64 (rd, base + offset);
cb->MoviI64 (rd, base + offset, false, true);
}
static void DisasAddSubImm(uint32_t insn, DisasCallback *cb) {
@ -87,6 +117,121 @@ static void DisasLogImm(uint32_t insn, DisasCallback *cb) {
}
}
static void DisasMovwImm(uint32_t insn, DisasCallback *cb) {
unsigned int is_64bit = extract32(insn, 31, 1);
unsigned int opc = extract32(insn, 29, 2);
uint64_t imm = extract32(insn, 5, 16);
unsigned int pos = extract32(insn, 21, 2) << 4;
unsigned int rd = extract32(insn, 0, 5);
if (!is_64bit && (pos >= 32)) {
UnallocatedOp (insn);
return;
}
switch (opc) {
case 0: /* MOVN */
case 2: /* MOVZ */
imm <<= pos;
if (opc == 0) {
imm = ~imm;
}
cb->MoviI64 (rd, imm, false, is_64bit);
break;
case 3: /* MOVK */
cb->MoviI64 (rd, imm, true, is_64bit);
break;
default:
UnallocatedOp (insn);
break;
}
}
static void DisasBitfield(uint32_t insn, DisasCallback *cb) {
unsigned int is_64bit = extract32(insn, 31, 1);
unsigned int opc = extract32(insn, 29, 2);
unsigned int n = extract32(insn, 22, 1);
unsigned int ri = extract32(insn, 16, 6);
unsigned int si = extract32(insn, 10, 6);
unsigned int rn = extract32(insn, 5, 5);
unsigned int rd = extract32(insn, 0, 5);
unsigned int bitsize = is_64bit ? 64 : 32;
unsigned int pos, len;
if (is_64bit != n || ri >= bitsize || si >= bitsize || opc > 2) {
UnallocatedOp(insn);
return;
}
/* Recognize simple(r) extractions. */
if (si >= ri) {
/* Wd<s-r:0> = Wn<s:r> */
len = (si - ri) + 1;
if (opc == 0) { /* SBFM: ASR, SBFX, SXTB, SXTH, SXTW */
cb->SExtractI64(rd, rn, ri, len, is_64bit);
return;
} else if (opc == 2) { /* UBFM: UBFX, LSR, UXTB, UXTH */
cb->UExtractI64(rd, rn, ri, len, is_64bit);
return;
}
pos = 0;
} else {
/* Handle the ri > si case with a deposit
* Wd<32+s-r,32-r> = Wn<s:0>
*/
len = si + 1;
pos = (bitsize - ri) & (bitsize - 1);
}
if (opc == 0 && len < ri) {
/* SBFM: sign extend the destination field from len to fill
the balance of the word. Let the deposit below insert all
of those sign bits. */
cb->SExtractI64(rd, rn, 0, len, is_64bit);
len = ri;
}
if (opc == 1) { /* BFM, BXFIL */
//TODO: deposit
} else {
/* SBFM or UBFM: We start with zero, and we haven't modified
any bits outside bitsize, therefore the zero-extension
below is unneeded. */
//TODO: ???
//tcg_gen_deposit_z_i64(tcg_rd, tcg_tmp, pos, len);
}
return;
}
static void DisasExtract(uint32_t insn, DisasCallback *cb) {
unsigned int sf = extract32(insn, 31, 1);
unsigned int n = extract32(insn, 22, 1);
unsigned int rm = extract32(insn, 16, 5);
unsigned int imm = extract32(insn, 10, 6);
unsigned int rn = extract32(insn, 5, 5);
unsigned int rd = extract32(insn, 0, 5);
unsigned int op21 = extract32(insn, 29, 2);
unsigned int op0 = extract32(insn, 21, 1);
unsigned int bitsize = sf ? 64 : 32;
if (sf != n || op21 || op0 || imm >= bitsize) {
UnallocatedOp (insn);
} else {
if (imm == 0) {
// TODO: mov rd, rm
//cb->MovI64(rd, rm, sf);
} else if (rm == rn) { /* ROR */
// TODO:
//cb->RorI64(rd, rm, imm)
} else {
// TODO:
//cb->ShriI64(rm, rm, imm)
//cb->ShliI64(rn, rn, imm)
//cb->OrrI64(rd, rm, rn);
}
}
return;
}
static void DisasDataProcImm(uint32_t insn, DisasCallback *cb) {
switch (extract32 (insn, 23, 6)) {
case 0x20: case 0x21: /* PC-rel. addressing */
@ -99,10 +244,13 @@ static void DisasDataProcImm(uint32_t insn, DisasCallback *cb) {
DisasLogImm (insn, cb);
break;
case 0x25: /* Move wide (immediate) */
DisasMovwImm (insn, cb);
break;
case 0x26: /* Bitfield */
DisasBitfield (insn, cb);
break;
case 0x27: /* Extract */
DisasExtract (insn, cb);
break;
default:
UnallocatedOp (insn);

View file

@ -1,37 +1,40 @@
/* nsemu - LGPL - Copyright 2017 rkx1209<rkx1209dev@gmail.com> */
#include "Nsemu.hpp"
Interpreter *Interpreter ::inst = nullptr;
IntprCallback *Interpreter ::disas_cb = nullptr;
Interpreter *Interpreter::inst = nullptr;
IntprCallback *Interpreter::disas_cb = nullptr;
int Interpreter ::SingleStep() {
uint32_t inst = ARMv8 ::ReadInst (PC);
int Interpreter::SingleStep() {
uint32_t inst = ARMv8::ReadInst (PC);
debug_print ("Run Code: 0x%08lx\n", inst);
PC += sizeof(uint32_t);
Disassembler ::DisasA64 (inst, disas_cb);
Disassembler::DisasA64 (inst, disas_cb);
return 0;
}
void Interpreter ::Run() {
void Interpreter::Run() {
debug_print ("Running with Interpreter\n");
while (Cpu ::GetState () == Cpu ::State ::Running) {
while (Cpu::GetState () == Cpu::State::Running) {
SingleStep ();
}
}
void IntprCallback ::MoviI64(unsigned int reg_idx, uint64_t imm) {
debug_print ("Mov: X[%u] = 0x%016lx\n", reg_idx, imm);
void IntprCallback::MoviI64(unsigned int reg_idx, uint64_t imm, bool unchanged, bool bit64) {
char regc = bit64? 'X': 'W';
debug_print ("MOV%c: %c[%u] = 0x%016lx\n", unchanged? 'K' : ' ', regc, reg_idx, imm);
}
/* Add/Sub with Immediate value */
void IntprCallback ::AddiI64(unsigned int rd_idx, unsigned int rn_idx, uint64_t imm, bool setflags, bool bit64) {
void IntprCallback::AddiI64(unsigned int rd_idx, unsigned int rn_idx, uint64_t imm, bool setflags, bool bit64) {
char regc = bit64? 'X': 'W';
debug_print ("Add: %c[%u] = %c[%u] + 0x%016lx (flag: %s)\n", regc, rd_idx, regc, rn_idx, imm, setflags? "update": "no");
}
void IntprCallback ::SubiI64(unsigned int rd_idx, unsigned int rn_idx, uint64_t imm, bool setflags, bool bit64) {
void IntprCallback::SubiI64(unsigned int rd_idx, unsigned int rn_idx, uint64_t imm, bool setflags, bool bit64) {
char regc = bit64? 'X': 'W';
debug_print ("Sub: %c[%u] = %c[%u] + 0x%016lx (flag: %s)\n", regc, rd_idx, regc, rn_idx, imm, setflags? "update": "no");
}
void IntprCallback ::AndiI64(unsigned int rd_idx, unsigned int rn_idx, uint64_t wmask, bool setflags, bool bit64) {}
void IntprCallback ::OrrI64(unsigned int rd_idx, unsigned int rn_idx, uint64_t wmask, bool bit64) {}
void IntprCallback ::EorI64(unsigned int rd_idx, unsigned int rn_idx, uint64_t wmask, bool bit64) {}
void IntprCallback::AndiI64(unsigned int rd_idx, unsigned int rn_idx, uint64_t wmask, bool setflags, bool bit64) {}
void IntprCallback::OrrI64(unsigned int rd_idx, unsigned int rn_idx, uint64_t wmask, bool bit64) {}
void IntprCallback::EorI64(unsigned int rd_idx, unsigned int rn_idx, uint64_t wmask, bool bit64) {}
void IntprCallback::SExtractI64(unsigned int rd_idx, unsigned int rn_idx, unsigned int pos, unsigned int len, bool bit64) {}
void IntprCallback::UExtractI64(unsigned int rd_idx, unsigned int rn_idx, unsigned int pos, unsigned int len, bool bit64) {}

View file

@ -3,7 +3,7 @@
class DisasCallback {
public:
virtual void MoviI64(unsigned int reg_idx, uint64_t imm) = 0;
virtual void MoviI64(unsigned int reg_idx, uint64_t imm, bool unchanged, bool bit64) = 0;
/* Add/Sub with Immediate value */
virtual void AddiI64(unsigned int rd_idx, unsigned int rn_idx, uint64_t imm, bool setflags, bool bit64) = 0;
virtual void SubiI64(unsigned int rd_idx, unsigned int rn_idx, uint64_t imm, bool setflags, bool bit64) = 0;
@ -11,6 +11,10 @@ virtual void SubiI64(unsigned int rd_idx, unsigned int rn_idx, uint64_t imm, boo
virtual void AndiI64(unsigned int rd_idx, unsigned int rn_idx, uint64_t wmask, bool setflags, bool bit64) = 0;
virtual void OrrI64(unsigned int rd_idx, unsigned int rn_idx, uint64_t wmask, bool bit64) = 0;
virtual void EorI64(unsigned int rd_idx, unsigned int rn_idx, uint64_t wmask, bool bit64) = 0;
/* Bitfield Signed/Unsigned Extract... with Immediate value */
virtual void SExtractI64(unsigned int rd_idx, unsigned int rn_idx, unsigned int pos, unsigned int len, bool bit64) = 0;
virtual void UExtractI64(unsigned int rd_idx, unsigned int rn_idx, unsigned int pos, unsigned int len, bool bit64) = 0;
};
namespace Disassembler {

View file

@ -3,7 +3,7 @@
class IntprCallback : public DisasCallback {
public:
void MoviI64(unsigned int reg_idx, uint64_t imm);
void MoviI64(unsigned int reg_idx, uint64_t imm, bool unchanged, bool bit64);
/* Add/Sub with Immediate value */
void AddiI64(unsigned int rd_idx, unsigned int rn_idx, uint64_t imm, bool setflags, bool bit64);
void SubiI64(unsigned int rd_idx, unsigned int rn_idx, uint64_t imm, bool setflags, bool bit64);
@ -11,6 +11,10 @@ void SubiI64(unsigned int rd_idx, unsigned int rn_idx, uint64_t imm, bool setfla
void AndiI64(unsigned int rd_idx, unsigned int rn_idx, uint64_t wmask, bool setflags, bool bit64);
void OrrI64(unsigned int rd_idx, unsigned int rn_idx, uint64_t wmask, bool bit64);
void EorI64(unsigned int rd_idx, unsigned int rn_idx, uint64_t wmask, bool bit64);
/* Bitfield Signed/Unsigned Extract... with Immediate value */
void SExtractI64(unsigned int rd_idx, unsigned int rn_idx, unsigned int pos, unsigned int len, bool bit64);
void UExtractI64(unsigned int rd_idx, unsigned int rn_idx, unsigned int pos, unsigned int len, bool bit64);
};
/* Global Interpreter singleton class .*/

View file

@ -61,10 +61,26 @@ static inline int64_t sextract64(uint64_t bitfield, int from, int len) {
return ((int64_t) (bitfield << (64 - from - len))) >> (64 - len);
}
static inline int clz32(uint32_t val) {
return val? __builtin_clz (val): 32;
static inline int clz32(uint32_t bitfield) {
return bitfield? __builtin_clz (bitfield): 32;
}
static inline int clz64(uint64_t val) {
return val? __builtin_clzll (val): 64;
static inline int clz64(uint64_t bitfield) {
return bitfield? __builtin_clzll (bitfield): 64;
}
/* Return a value with the bottom len bits set (where 0 < len <= 64) */
static inline uint64_t bitmask64(unsigned int length) {
assert(length > 0 && length <= 64);
return ~0ULL >> (64 - length);
}
static uint64_t replicate64(uint64_t mask, unsigned int e) {
assert(e != 0);
while (e < 64) {
mask |= mask << e;
e *= 2;
}
return mask;
}
#endif