Beginning work on disassembler functions.
Ideally, this would be automated, but it's easier to just do it manually.
This commit is contained in:
parent
7b62fa96bd
commit
775c97bf53
|
@ -12,6 +12,9 @@
|
||||||
|
|
||||||
#include <xenia/common.h>
|
#include <xenia/common.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace cpu {
|
namespace cpu {
|
||||||
|
@ -184,6 +187,82 @@ typedef struct {
|
||||||
};
|
};
|
||||||
} InstrData;
|
} InstrData;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
enum RegisterSet {
|
||||||
|
kXER,
|
||||||
|
kLR,
|
||||||
|
kCTR,
|
||||||
|
kCR, // 0-7
|
||||||
|
kFPSCR,
|
||||||
|
kGPR, // 0-31
|
||||||
|
kFPR, // 0-31
|
||||||
|
kVMX, // 0-127
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Access {
|
||||||
|
kRead = 1 << 0,
|
||||||
|
kWrite = 1 << 1,
|
||||||
|
kReadWrite = kRead | kWrite,
|
||||||
|
};
|
||||||
|
|
||||||
|
RegisterSet set;
|
||||||
|
uint32_t ordinal;
|
||||||
|
Access access;
|
||||||
|
} InstrRegister;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
enum OperandType {
|
||||||
|
kRegister,
|
||||||
|
kImmediate,
|
||||||
|
};
|
||||||
|
|
||||||
|
OperandType type;
|
||||||
|
union {
|
||||||
|
InstrRegister reg;
|
||||||
|
struct {
|
||||||
|
bool is_signed;
|
||||||
|
uint64_t value;
|
||||||
|
size_t width;
|
||||||
|
} imm;
|
||||||
|
};
|
||||||
|
char display[32];
|
||||||
|
} InstrOperand;
|
||||||
|
|
||||||
|
|
||||||
|
class InstrDisasm {
|
||||||
|
public:
|
||||||
|
enum Flags {
|
||||||
|
kOE = 1 << 0,
|
||||||
|
kRc = 1 << 1,
|
||||||
|
kCA = 1 << 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
char name[16];
|
||||||
|
std::vector<InstrOperand> operands;
|
||||||
|
std::vector<InstrRegister> special_registers;
|
||||||
|
|
||||||
|
void Init(std::string name, uint32_t flags);
|
||||||
|
void AddRegOperand(InstrRegister::RegisterSet set, uint32_t ordinal,
|
||||||
|
InstrRegister::Access access, std::string display = "");
|
||||||
|
void AddSImmOperand(uint64_t value, size_t width, std::string display = "");
|
||||||
|
void AddUImmOperand(uint64_t value, size_t width, std::string display = "");
|
||||||
|
int Finish();
|
||||||
|
|
||||||
|
// TODO(benvanik): fast checks
|
||||||
|
uint64_t reg_mask;
|
||||||
|
uint64_t gpr_mask;
|
||||||
|
uint64_t fpr_mask;
|
||||||
|
|
||||||
|
void Dump(std::string& str, size_t pad = 8);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
typedef int (*InstrDisassembleFn)(InstrData& i, InstrDisasm& d);
|
||||||
|
typedef void* InstrEmitFn;
|
||||||
|
|
||||||
|
|
||||||
class InstrType {
|
class InstrType {
|
||||||
public:
|
public:
|
||||||
uint32_t opcode;
|
uint32_t opcode;
|
||||||
|
@ -192,11 +271,13 @@ public:
|
||||||
uint32_t flags; // xe_ppc_instr_flag_e
|
uint32_t flags; // xe_ppc_instr_flag_e
|
||||||
char name[16];
|
char name[16];
|
||||||
|
|
||||||
void* emit;
|
InstrDisassembleFn disassemble;
|
||||||
|
InstrEmitFn emit;
|
||||||
};
|
};
|
||||||
|
|
||||||
InstrType* GetInstrType(uint32_t code);
|
InstrType* GetInstrType(uint32_t code);
|
||||||
int RegisterInstrEmit(uint32_t code, void* emit);
|
int RegisterInstrDisassemble(uint32_t code, InstrDisassembleFn disassemble);
|
||||||
|
int RegisterInstrEmit(uint32_t code, InstrEmitFn emit);
|
||||||
|
|
||||||
|
|
||||||
} // namespace ppc
|
} // namespace ppc
|
||||||
|
|
|
@ -86,12 +86,24 @@ typedef struct XECACHEALIGN64 xe_ppc_state {
|
||||||
uint8_t vx :1; // FP invalid operation exception summary - copy of FPSCR[VX]
|
uint8_t vx :1; // FP invalid operation exception summary - copy of FPSCR[VX]
|
||||||
uint8_t ox :1; // FP overflow exception - copy of FPSCR[OX]
|
uint8_t ox :1; // FP overflow exception - copy of FPSCR[OX]
|
||||||
} cr1;
|
} cr1;
|
||||||
uint8_t cr2 :4;
|
struct {
|
||||||
uint8_t cr3 :4;
|
uint8_t value :4;
|
||||||
uint8_t cr4 :4;
|
} cr2;
|
||||||
uint8_t cr5 :4;
|
struct {
|
||||||
uint8_t cr6 :4;
|
uint8_t value :4;
|
||||||
uint8_t cr7 :4;
|
} cr3;
|
||||||
|
struct {
|
||||||
|
uint8_t value :4;
|
||||||
|
} cr4;
|
||||||
|
struct {
|
||||||
|
uint8_t value :4;
|
||||||
|
} cr5;
|
||||||
|
struct {
|
||||||
|
uint8_t value :4;
|
||||||
|
} cr6;
|
||||||
|
struct {
|
||||||
|
uint8_t value :4;
|
||||||
|
} cr7;
|
||||||
} cr; // Condition register
|
} cr; // Condition register
|
||||||
|
|
||||||
union {
|
union {
|
||||||
|
|
|
@ -24,10 +24,14 @@ void RegisterEmitCategoryFPU();
|
||||||
void RegisterEmitCategoryMemory();
|
void RegisterEmitCategoryMemory();
|
||||||
|
|
||||||
|
|
||||||
|
#define XEDISASMR(name, opcode, format) int InstrDisasm_##name
|
||||||
#define XEEMITTER(name, opcode, format) int InstrEmit_##name
|
#define XEEMITTER(name, opcode, format) int InstrEmit_##name
|
||||||
|
|
||||||
|
#define XEREGISTERINSTR(name, opcode) \
|
||||||
|
RegisterInstrDisassemble(opcode, (InstrDisassembleFn)InstrDisasm_##name); \
|
||||||
|
RegisterInstrEmit(opcode, (InstrEmitFn)InstrEmit_##name);
|
||||||
#define XEREGISTEREMITTER(name, opcode) \
|
#define XEREGISTEREMITTER(name, opcode) \
|
||||||
RegisterInstrEmit(opcode, (void*)InstrEmit_##name)
|
RegisterInstrEmit(opcode, (InstrEmitFn)InstrEmit_##name);
|
||||||
|
|
||||||
#define XEINSTRNOTIMPLEMENTED()
|
#define XEINSTRNOTIMPLEMENTED()
|
||||||
//#define XEINSTRNOTIMPLEMENTED XEASSERTALWAYS
|
//#define XEINSTRNOTIMPLEMENTED XEASSERTALWAYS
|
||||||
|
|
|
@ -26,6 +26,14 @@ namespace codegen {
|
||||||
|
|
||||||
// Integer arithmetic (A-3)
|
// Integer arithmetic (A-3)
|
||||||
|
|
||||||
|
XEDISASMR(addx, 0x7C000214, XO )(InstrData& i, InstrDisasm& d) {
|
||||||
|
d.Init("add",
|
||||||
|
i.XO.OE ? InstrDisasm::kOE : 0 | i.XO.Rc ? InstrDisasm::kRc : 0);
|
||||||
|
d.AddRegOperand(InstrRegister::kGPR, i.XO.RT, InstrRegister::kWrite);
|
||||||
|
d.AddRegOperand(InstrRegister::kGPR, i.XO.RA, InstrRegister::kRead);
|
||||||
|
d.AddRegOperand(InstrRegister::kGPR, i.XO.RB, InstrRegister::kRead);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
XEEMITTER(addx, 0x7C000214, XO )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
XEEMITTER(addx, 0x7C000214, XO )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||||
// RD <- (RA) + (RB)
|
// RD <- (RA) + (RB)
|
||||||
|
|
||||||
|
@ -37,12 +45,13 @@ XEEMITTER(addx, 0x7C000214, XO )(FunctionGenerator& g, IRBuilder<>& b, I
|
||||||
g.gen_module(), Intrinsic::sadd_with_overflow, b.getInt64Ty());
|
g.gen_module(), Intrinsic::sadd_with_overflow, b.getInt64Ty());
|
||||||
Value* v = b.CreateCall2(sadd_with_overflow,
|
Value* v = b.CreateCall2(sadd_with_overflow,
|
||||||
g.gpr_value(i.XO.RA), g.gpr_value(i.XO.RB));
|
g.gpr_value(i.XO.RA), g.gpr_value(i.XO.RB));
|
||||||
g.update_gpr_value(i.XO.RT, b.CreateExtractValue(v, 0));
|
Value* v0 = b.CreateExtractValue(v, 0);
|
||||||
|
g.update_gpr_value(i.XO.RT, v0);
|
||||||
g.update_xer_with_overflow(b.CreateExtractValue(v, 1));
|
g.update_xer_with_overflow(b.CreateExtractValue(v, 1));
|
||||||
|
|
||||||
if (i.XO.Rc) {
|
if (i.XO.Rc) {
|
||||||
// With cr0 update.
|
// With cr0 update.
|
||||||
g.update_cr_with_cond(0, v, b.getInt64(0), true);
|
g.update_cr_with_cond(0, v0, b.getInt64(0), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -291,12 +300,13 @@ XEEMITTER(subfx, 0x7C000050, XO )(FunctionGenerator& g, IRBuilder<>& b, I
|
||||||
g.gen_module(), Intrinsic::ssub_with_overflow, b.getInt64Ty());
|
g.gen_module(), Intrinsic::ssub_with_overflow, b.getInt64Ty());
|
||||||
Value* v = b.CreateCall2(ssub_with_overflow,
|
Value* v = b.CreateCall2(ssub_with_overflow,
|
||||||
g.gpr_value(i.XO.RB), g.gpr_value(i.XO.RA));
|
g.gpr_value(i.XO.RB), g.gpr_value(i.XO.RA));
|
||||||
g.update_gpr_value(i.XO.RT, b.CreateExtractValue(v, 0));
|
Value* v0 = b.CreateExtractValue(v, 0);
|
||||||
|
g.update_gpr_value(i.XO.RT, v0);
|
||||||
g.update_xer_with_overflow(b.CreateExtractValue(v, 1));
|
g.update_xer_with_overflow(b.CreateExtractValue(v, 1));
|
||||||
|
|
||||||
if (i.XO.Rc) {
|
if (i.XO.Rc) {
|
||||||
// With cr0 update.
|
// With cr0 update.
|
||||||
g.update_cr_with_cond(0, v, b.getInt64(0), true);
|
g.update_cr_with_cond(0, v0, b.getInt64(0), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -342,9 +352,10 @@ XEEMITTER(subfex, 0x7C000110, XO )(FunctionGenerator& g, IRBuilder<>& b, I
|
||||||
Function* uadd_with_overflow = Intrinsic::getDeclaration(
|
Function* uadd_with_overflow = Intrinsic::getDeclaration(
|
||||||
g.gen_module(), Intrinsic::uadd_with_overflow, b.getInt64Ty());
|
g.gen_module(), Intrinsic::uadd_with_overflow, b.getInt64Ty());
|
||||||
Value* v = b.CreateCall2(uadd_with_overflow,
|
Value* v = b.CreateCall2(uadd_with_overflow,
|
||||||
b.CreateNot(g.gpr_value(i.XO.RA)),
|
b.CreateNeg(g.gpr_value(i.XO.RA)),
|
||||||
b.CreateAdd(g.gpr_value(i.XO.RB), ca));
|
b.CreateAdd(g.gpr_value(i.XO.RB), ca));
|
||||||
g.update_gpr_value(i.XO.RT, b.CreateExtractValue(v, 0));
|
Value* v0 = b.CreateExtractValue(v, 0);
|
||||||
|
g.update_gpr_value(i.XO.RT, v0);
|
||||||
|
|
||||||
if (i.XO.OE) {
|
if (i.XO.OE) {
|
||||||
// With XER update.
|
// With XER update.
|
||||||
|
@ -355,7 +366,7 @@ XEEMITTER(subfex, 0x7C000110, XO )(FunctionGenerator& g, IRBuilder<>& b, I
|
||||||
|
|
||||||
if (i.XO.Rc) {
|
if (i.XO.Rc) {
|
||||||
// With cr0 update.
|
// With cr0 update.
|
||||||
g.update_cr_with_cond(0, v, b.getInt64(0), true);
|
g.update_cr_with_cond(0, v0, b.getInt64(0), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -908,7 +919,7 @@ XEEMITTER(srwx, 0x7C000430, X )(FunctionGenerator& g, IRBuilder<>& b, I
|
||||||
|
|
||||||
|
|
||||||
void RegisterEmitCategoryALU() {
|
void RegisterEmitCategoryALU() {
|
||||||
XEREGISTEREMITTER(addx, 0x7C000214);
|
XEREGISTERINSTR(addx, 0x7C000214);
|
||||||
XEREGISTEREMITTER(addcx, 0X7C000014);
|
XEREGISTEREMITTER(addcx, 0X7C000014);
|
||||||
XEREGISTEREMITTER(addex, 0x7C000114);
|
XEREGISTEREMITTER(addex, 0x7C000114);
|
||||||
XEREGISTEREMITTER(addi, 0x38000000);
|
XEREGISTEREMITTER(addi, 0x38000000);
|
||||||
|
|
|
@ -260,7 +260,16 @@ void FunctionGenerator::GenerateBasicBlock(FunctionBlock* block,
|
||||||
b.getInt32(i.code));
|
b.getInt32(i.code));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (i.type->disassemble) {
|
||||||
|
ppc::InstrDisasm d;
|
||||||
|
i.type->disassemble(i, d);
|
||||||
|
std::string disasm;
|
||||||
|
d.Dump(disasm);
|
||||||
|
printf(" %.8X: %.8X %s\n", ia, i.code, disasm.c_str());
|
||||||
|
} else {
|
||||||
printf(" %.8X: %.8X %s\n", ia, i.code, i.type->name);
|
printf(" %.8X: %.8X %s\n", ia, i.code, i.type->name);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(benvanik): debugging information? source/etc?
|
// TODO(benvanik): debugging information? source/etc?
|
||||||
// builder_>SetCurrentDebugLocation(DebugLoc::get(
|
// builder_>SetCurrentDebugLocation(DebugLoc::get(
|
||||||
|
@ -853,7 +862,8 @@ Value* FunctionGenerator::GetMemoryAddress(Value* addr) {
|
||||||
BasicBlock* invalid_bb = BasicBlock::Create(*context_, "", gen_fn_);
|
BasicBlock* invalid_bb = BasicBlock::Create(*context_, "", gen_fn_);
|
||||||
BasicBlock* valid_bb = BasicBlock::Create(*context_, "", gen_fn_);
|
BasicBlock* valid_bb = BasicBlock::Create(*context_, "", gen_fn_);
|
||||||
|
|
||||||
Value* gt = b.CreateICmpUGE(addr, b.getInt64(0x10000000));
|
// The heap starts at 0x1000 - if we write below that we're boned.
|
||||||
|
Value* gt = b.CreateICmpUGE(addr, b.getInt64(0x00001000));
|
||||||
b.CreateCondBr(gt, valid_bb, invalid_bb);
|
b.CreateCondBr(gt, valid_bb, invalid_bb);
|
||||||
|
|
||||||
b.SetInsertPoint(invalid_bb);
|
b.SetInsertPoint(invalid_bb);
|
||||||
|
|
|
@ -9,12 +9,168 @@
|
||||||
|
|
||||||
#include <xenia/cpu/ppc/instr.h>
|
#include <xenia/cpu/ppc/instr.h>
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
#include "cpu/ppc/instr_tables.h"
|
#include "cpu/ppc/instr_tables.h"
|
||||||
|
|
||||||
|
|
||||||
using namespace xe::cpu::ppc;
|
using namespace xe::cpu::ppc;
|
||||||
|
|
||||||
|
|
||||||
|
void InstrDisasm::Init(std::string name, uint32_t flags) {
|
||||||
|
operands.clear();
|
||||||
|
special_registers.clear();
|
||||||
|
|
||||||
|
if (flags & InstrDisasm::kOE) {
|
||||||
|
name += "o";
|
||||||
|
special_registers.push_back((InstrRegister){
|
||||||
|
InstrRegister::kXER, 0, InstrRegister::kReadWrite
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (flags & InstrDisasm::kRc) {
|
||||||
|
name += ".";
|
||||||
|
special_registers.push_back((InstrRegister){
|
||||||
|
InstrRegister::kCR, 0, InstrRegister::kWrite
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (flags & InstrDisasm::kCA) {
|
||||||
|
special_registers.push_back((InstrRegister){
|
||||||
|
InstrRegister::kXER, 0, InstrRegister::kReadWrite
|
||||||
|
});
|
||||||
|
}
|
||||||
|
XEIGNORE(xestrcpya(this->name, XECOUNT(this->name), name.c_str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstrDisasm::AddRegOperand(
|
||||||
|
InstrRegister::RegisterSet set, uint32_t ordinal,
|
||||||
|
InstrRegister::Access access, std::string display) {
|
||||||
|
InstrOperand o;
|
||||||
|
o.type = InstrOperand::kRegister;
|
||||||
|
o.reg = (InstrRegister){
|
||||||
|
set, ordinal, access
|
||||||
|
};
|
||||||
|
if (!display.size()) {
|
||||||
|
std::stringstream display_out;
|
||||||
|
switch (set) {
|
||||||
|
case InstrRegister::kXER:
|
||||||
|
display_out << "XER";
|
||||||
|
break;
|
||||||
|
case InstrRegister::kLR:
|
||||||
|
display_out << "LR";
|
||||||
|
break;
|
||||||
|
case InstrRegister::kCTR:
|
||||||
|
display_out << "CTR";
|
||||||
|
break;
|
||||||
|
case InstrRegister::kCR:
|
||||||
|
display_out << "CR";
|
||||||
|
display_out << ordinal;
|
||||||
|
break;
|
||||||
|
case InstrRegister::kFPSCR:
|
||||||
|
display_out << "FPSCR";
|
||||||
|
break;
|
||||||
|
case InstrRegister::kGPR:
|
||||||
|
display_out << "r";
|
||||||
|
display_out << ordinal;
|
||||||
|
break;
|
||||||
|
case InstrRegister::kFPR:
|
||||||
|
display_out << "f";
|
||||||
|
display_out << ordinal;
|
||||||
|
break;
|
||||||
|
case InstrRegister::kVMX:
|
||||||
|
display_out << "v";
|
||||||
|
display_out << ordinal;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
display = display_out.str();
|
||||||
|
}
|
||||||
|
XEIGNORE(xestrcpya(o.display, XECOUNT(o.display), display.c_str()));
|
||||||
|
operands.push_back(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstrDisasm::AddSImmOperand(uint64_t value, size_t width,
|
||||||
|
std::string display) {
|
||||||
|
InstrOperand o;
|
||||||
|
o.type = InstrOperand::kImmediate;
|
||||||
|
o.imm.is_signed = true;
|
||||||
|
o.imm.value = value;
|
||||||
|
o.imm.width = value;
|
||||||
|
if (display.size()) {
|
||||||
|
XEIGNORE(xestrcpya(o.display, XECOUNT(o.display), display.c_str()));
|
||||||
|
} else {
|
||||||
|
const size_t max_count = XECOUNT(o.display);
|
||||||
|
switch (width) {
|
||||||
|
case 1:
|
||||||
|
xesnprintfa(o.display, max_count, "%d", (int32_t)(int8_t)value);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
xesnprintfa(o.display, max_count, "%d", (int32_t)(int16_t)value);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
xesnprintfa(o.display, max_count, "%d", (int32_t)value);
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
xesnprintfa(o.display, max_count, "%lld", (int64_t)value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
operands.push_back(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstrDisasm::AddUImmOperand(uint64_t value, size_t width,
|
||||||
|
std::string display) {
|
||||||
|
InstrOperand o;
|
||||||
|
o.type = InstrOperand::kImmediate;
|
||||||
|
o.imm.is_signed = false;
|
||||||
|
o.imm.value = value;
|
||||||
|
o.imm.width = value;
|
||||||
|
if (display.size()) {
|
||||||
|
XEIGNORE(xestrcpya(o.display, XECOUNT(o.display), display.c_str()));
|
||||||
|
} else {
|
||||||
|
const size_t max_count = XECOUNT(o.display);
|
||||||
|
switch (width) {
|
||||||
|
case 1:
|
||||||
|
xesnprintfa(o.display, max_count, "%.2X", (uint8_t)value);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
xesnprintfa(o.display, max_count, "%.4X", (uint16_t)value);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
xesnprintfa(o.display, max_count, "%.8X", (uint32_t)value);
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
xesnprintfa(o.display, max_count, "%.16llX", value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
operands.push_back(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
int InstrDisasm::Finish() {
|
||||||
|
// TODO(benvanik): setup fast checks
|
||||||
|
reg_mask = 0;
|
||||||
|
gpr_mask = 0;
|
||||||
|
fpr_mask = 0;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstrDisasm::Dump(std::string& str, size_t pad) {
|
||||||
|
str = name;
|
||||||
|
if (operands.size()) {
|
||||||
|
if (pad && str.size() < pad) {
|
||||||
|
str += std::string(pad - str.size(), ' ');
|
||||||
|
}
|
||||||
|
for (std::vector<InstrOperand>::iterator it = operands.begin();
|
||||||
|
it != operands.end(); ++it) {
|
||||||
|
str += it->display;
|
||||||
|
|
||||||
|
if (it + 1 != operands.end()) {
|
||||||
|
str += ", ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
InstrType* xe::cpu::ppc::GetInstrType(uint32_t code) {
|
InstrType* xe::cpu::ppc::GetInstrType(uint32_t code) {
|
||||||
InstrType* slot = NULL;
|
InstrType* slot = NULL;
|
||||||
switch (code >> 26) {
|
switch (code >> 26) {
|
||||||
|
@ -60,7 +216,19 @@ InstrType* xe::cpu::ppc::GetInstrType(uint32_t code) {
|
||||||
return slot;
|
return slot;
|
||||||
}
|
}
|
||||||
|
|
||||||
int xe::cpu::ppc::RegisterInstrEmit(uint32_t code, void* emit) {
|
int xe::cpu::ppc::RegisterInstrDisassemble(
|
||||||
|
uint32_t code, InstrDisassembleFn disassemble) {
|
||||||
|
InstrType* instr_type = GetInstrType(code);
|
||||||
|
XEASSERTNOTNULL(instr_type);
|
||||||
|
if (!instr_type) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
XEASSERTNULL(instr_type->disassemble);
|
||||||
|
instr_type->disassemble = disassemble;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int xe::cpu::ppc::RegisterInstrEmit(uint32_t code, InstrEmitFn emit) {
|
||||||
InstrType* instr_type = GetInstrType(code);
|
InstrType* instr_type = GetInstrType(code);
|
||||||
XEASSERTNOTNULL(instr_type);
|
XEASSERTNOTNULL(instr_type);
|
||||||
if (!instr_type) {
|
if (!instr_type) {
|
||||||
|
|
Loading…
Reference in New Issue