From 775c97bf535ab43d6013ae16a5b1fc7e441d7e8c Mon Sep 17 00:00:00 2001 From: Ben Vanik Date: Mon, 28 Jan 2013 21:37:03 -0800 Subject: [PATCH] Beginning work on disassembler functions. Ideally, this would be automated, but it's easier to just do it manually. --- include/xenia/cpu/ppc/instr.h | 85 ++++++++++++- include/xenia/cpu/ppc/state.h | 24 +++- src/cpu/codegen/emit.h | 6 +- src/cpu/codegen/emit_alu.cc | 27 ++-- src/cpu/codegen/function_generator.cc | 14 ++- src/cpu/ppc/instr.cc | 170 +++++++++++++++++++++++++- 6 files changed, 306 insertions(+), 20 deletions(-) diff --git a/include/xenia/cpu/ppc/instr.h b/include/xenia/cpu/ppc/instr.h index 937c97c8f..d4a03a896 100644 --- a/include/xenia/cpu/ppc/instr.h +++ b/include/xenia/cpu/ppc/instr.h @@ -12,6 +12,9 @@ #include +#include +#include + namespace xe { namespace cpu { @@ -184,6 +187,82 @@ typedef struct { }; } 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 operands; + std::vector 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 { public: uint32_t opcode; @@ -192,11 +271,13 @@ public: uint32_t flags; // xe_ppc_instr_flag_e char name[16]; - void* emit; + InstrDisassembleFn disassemble; + InstrEmitFn emit; }; 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 diff --git a/include/xenia/cpu/ppc/state.h b/include/xenia/cpu/ppc/state.h index e15ec6086..9fffab26b 100644 --- a/include/xenia/cpu/ppc/state.h +++ b/include/xenia/cpu/ppc/state.h @@ -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 ox :1; // FP overflow exception - copy of FPSCR[OX] } cr1; - uint8_t cr2 :4; - uint8_t cr3 :4; - uint8_t cr4 :4; - uint8_t cr5 :4; - uint8_t cr6 :4; - uint8_t cr7 :4; + struct { + uint8_t value :4; + } cr2; + struct { + uint8_t value :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 union { diff --git a/src/cpu/codegen/emit.h b/src/cpu/codegen/emit.h index d21a764ca..1b9909ad7 100644 --- a/src/cpu/codegen/emit.h +++ b/src/cpu/codegen/emit.h @@ -24,10 +24,14 @@ void RegisterEmitCategoryFPU(); void RegisterEmitCategoryMemory(); +#define XEDISASMR(name, opcode, format) int InstrDisasm_##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) \ - RegisterInstrEmit(opcode, (void*)InstrEmit_##name) + RegisterInstrEmit(opcode, (InstrEmitFn)InstrEmit_##name); #define XEINSTRNOTIMPLEMENTED() //#define XEINSTRNOTIMPLEMENTED XEASSERTALWAYS diff --git a/src/cpu/codegen/emit_alu.cc b/src/cpu/codegen/emit_alu.cc index 469ca86d0..96aabaa12 100644 --- a/src/cpu/codegen/emit_alu.cc +++ b/src/cpu/codegen/emit_alu.cc @@ -26,6 +26,14 @@ namespace codegen { // 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) { // 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()); Value* v = b.CreateCall2(sadd_with_overflow, 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)); if (i.XO.Rc) { // 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; @@ -291,12 +300,13 @@ XEEMITTER(subfx, 0x7C000050, XO )(FunctionGenerator& g, IRBuilder<>& b, I g.gen_module(), Intrinsic::ssub_with_overflow, b.getInt64Ty()); Value* v = b.CreateCall2(ssub_with_overflow, 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)); if (i.XO.Rc) { // 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; @@ -342,9 +352,10 @@ XEEMITTER(subfex, 0x7C000110, XO )(FunctionGenerator& g, IRBuilder<>& b, I Function* uadd_with_overflow = Intrinsic::getDeclaration( g.gen_module(), Intrinsic::uadd_with_overflow, b.getInt64Ty()); 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)); - 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) { // With XER update. @@ -355,7 +366,7 @@ XEEMITTER(subfex, 0x7C000110, XO )(FunctionGenerator& g, IRBuilder<>& b, I if (i.XO.Rc) { // 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; @@ -908,7 +919,7 @@ XEEMITTER(srwx, 0x7C000430, X )(FunctionGenerator& g, IRBuilder<>& b, I void RegisterEmitCategoryALU() { - XEREGISTEREMITTER(addx, 0x7C000214); + XEREGISTERINSTR(addx, 0x7C000214); XEREGISTEREMITTER(addcx, 0X7C000014); XEREGISTEREMITTER(addex, 0x7C000114); XEREGISTEREMITTER(addi, 0x38000000); diff --git a/src/cpu/codegen/function_generator.cc b/src/cpu/codegen/function_generator.cc index 4cadb3d80..61d88db34 100644 --- a/src/cpu/codegen/function_generator.cc +++ b/src/cpu/codegen/function_generator.cc @@ -260,7 +260,16 @@ void FunctionGenerator::GenerateBasicBlock(FunctionBlock* block, b.getInt32(i.code)); continue; } - printf(" %.8X: %.8X %s\n", ia, i.code, i.type->name); + + 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); + } // TODO(benvanik): debugging information? source/etc? // builder_>SetCurrentDebugLocation(DebugLoc::get( @@ -853,7 +862,8 @@ Value* FunctionGenerator::GetMemoryAddress(Value* addr) { BasicBlock* invalid_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.SetInsertPoint(invalid_bb); diff --git a/src/cpu/ppc/instr.cc b/src/cpu/ppc/instr.cc index 51b6a8c91..d36d73532 100644 --- a/src/cpu/ppc/instr.cc +++ b/src/cpu/ppc/instr.cc @@ -9,12 +9,168 @@ #include +#include + #include "cpu/ppc/instr_tables.h" 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::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* slot = NULL; switch (code >> 26) { @@ -60,7 +216,19 @@ InstrType* xe::cpu::ppc::GetInstrType(uint32_t code) { 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); XEASSERTNOTNULL(instr_type); if (!instr_type) {