Screw convention; moving include files alongside source files.
They now will show up in xcode/etc.
This commit is contained in:
45
src/xenia/cpu/codegen/emit.h
Normal file
45
src/xenia/cpu/codegen/emit.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_CPU_CODEGEN_EMIT_H_
|
||||
#define XENIA_CPU_CODEGEN_EMIT_H_
|
||||
|
||||
#include <xenia/cpu/ppc/instr.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace codegen {
|
||||
|
||||
|
||||
void RegisterEmitCategoryALU();
|
||||
void RegisterEmitCategoryControl();
|
||||
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, (InstrEmitFn)InstrEmit_##name);
|
||||
|
||||
#define XEINSTRNOTIMPLEMENTED()
|
||||
//#define XEINSTRNOTIMPLEMENTED XEASSERTALWAYS
|
||||
|
||||
|
||||
} // namespace codegen
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_CODEGEN_EMIT_H_
|
||||
1624
src/xenia/cpu/codegen/emit_alu.cc
Normal file
1624
src/xenia/cpu/codegen/emit_alu.cc
Normal file
File diff suppressed because it is too large
Load Diff
824
src/xenia/cpu/codegen/emit_control.cc
Normal file
824
src/xenia/cpu/codegen/emit_control.cc
Normal file
@@ -0,0 +1,824 @@
|
||||
/*
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <xenia/cpu/codegen/emit.h>
|
||||
|
||||
#include <xenia/cpu/codegen/function_generator.h>
|
||||
#include <xenia/cpu/ppc/state.h>
|
||||
|
||||
|
||||
using namespace llvm;
|
||||
using namespace xe::cpu::codegen;
|
||||
using namespace xe::cpu::ppc;
|
||||
using namespace xe::cpu::sdb;
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace codegen {
|
||||
|
||||
|
||||
int XeEmitIndirectBranchTo(
|
||||
FunctionGenerator& g, IRBuilder<>& b, const char* src, uint32_t cia,
|
||||
bool lk, uint32_t reg) {
|
||||
// TODO(benvanik): run a DFA pass to see if we can detect whether this is
|
||||
// a normal function return that is pulling the LR from the stack that
|
||||
// it set in the prolog. If so, we can omit the dynamic check!
|
||||
|
||||
// NOTE: we avoid spilling registers until we know that the target is not
|
||||
// a basic block within this function.
|
||||
|
||||
Value* target;
|
||||
switch (reg) {
|
||||
case kXEPPCRegLR:
|
||||
target = g.lr_value();
|
||||
break;
|
||||
case kXEPPCRegCTR:
|
||||
target = g.ctr_value();
|
||||
break;
|
||||
default:
|
||||
XEASSERTALWAYS();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Dynamic test when branching to LR, which is usually used for the return.
|
||||
// We only do this if LK=0 as returns wouldn't set LR.
|
||||
// Ideally it's a return and we can just do a simple ret and be done.
|
||||
// If it's not, we fall through to the full indirection logic.
|
||||
if (!lk && reg == kXEPPCRegLR) {
|
||||
BasicBlock* next_block = g.GetNextBasicBlock();
|
||||
BasicBlock* mismatch_bb = BasicBlock::Create(*g.context(), "lr_mismatch",
|
||||
g.gen_fn(), next_block);
|
||||
Value* lr_cmp = b.CreateICmpEQ(target, ++(g.gen_fn()->arg_begin()));
|
||||
// The return block will spill registers for us.
|
||||
b.CreateCondBr(lr_cmp, g.GetReturnBasicBlock(), mismatch_bb);
|
||||
b.SetInsertPoint(mismatch_bb);
|
||||
}
|
||||
|
||||
// Defer to the generator, which will do fancy things.
|
||||
bool likely_local = !lk && reg == kXEPPCRegCTR;
|
||||
return g.GenerateIndirectionBranch(cia, target, lk, likely_local);
|
||||
}
|
||||
|
||||
int XeEmitBranchTo(
|
||||
FunctionGenerator& g, IRBuilder<>& b, const char* src, uint32_t cia,
|
||||
bool lk) {
|
||||
// Get the basic block and switch behavior based on outgoing type.
|
||||
FunctionBlock* fn_block = g.fn_block();
|
||||
switch (fn_block->outgoing_type) {
|
||||
case FunctionBlock::kTargetBlock:
|
||||
{
|
||||
BasicBlock* target_bb = g.GetBasicBlock(fn_block->outgoing_address);
|
||||
XEASSERTNOTNULL(target_bb);
|
||||
b.CreateBr(target_bb);
|
||||
break;
|
||||
}
|
||||
case FunctionBlock::kTargetFunction:
|
||||
{
|
||||
// Spill all registers to memory.
|
||||
// TODO(benvanik): only spill ones used by the target function? Use
|
||||
// calling convention flags on the function to not spill temp
|
||||
// registers?
|
||||
g.SpillRegisters();
|
||||
|
||||
XEASSERTNOTNULL(fn_block->outgoing_function);
|
||||
Function* target_fn = g.GetFunction(fn_block->outgoing_function);
|
||||
Function::arg_iterator args = g.gen_fn()->arg_begin();
|
||||
Value* state_ptr = args;
|
||||
BasicBlock* next_bb = g.GetNextBasicBlock();
|
||||
if (!lk || !next_bb) {
|
||||
// Tail. No need to refill the local register values, just return.
|
||||
// We optimize this by passing in the LR from our parent instead of the
|
||||
// next instruction. This allows the return from our callee to pop
|
||||
// all the way up.
|
||||
b.CreateCall2(target_fn, state_ptr, ++args);
|
||||
b.CreateRetVoid();
|
||||
} else {
|
||||
// Will return here eventually.
|
||||
// Refill registers from state.
|
||||
b.CreateCall2(target_fn, state_ptr, b.getInt64(cia + 4));
|
||||
g.FillRegisters();
|
||||
b.CreateBr(next_bb);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FunctionBlock::kTargetLR:
|
||||
{
|
||||
// An indirect jump.
|
||||
printf("INDIRECT JUMP VIA LR: %.8X\n", cia);
|
||||
return XeEmitIndirectBranchTo(g, b, src, cia, lk, kXEPPCRegLR);
|
||||
}
|
||||
case FunctionBlock::kTargetCTR:
|
||||
{
|
||||
// An indirect jump.
|
||||
printf("INDIRECT JUMP VIA CTR: %.8X\n", cia);
|
||||
return XeEmitIndirectBranchTo(g, b, src, cia, lk, kXEPPCRegCTR);
|
||||
}
|
||||
default:
|
||||
case FunctionBlock::kTargetNone:
|
||||
XEASSERTALWAYS();
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
XEDISASMR(bx, 0x48000000, I )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("b", "Branch", i.I.LK ? InstrDisasm::kLR : 0);
|
||||
uint32_t nia;
|
||||
if (i.I.AA) {
|
||||
nia = XEEXTS26(i.I.LI << 2);
|
||||
} else {
|
||||
nia = i.address + XEEXTS26(i.I.LI << 2);
|
||||
}
|
||||
d.AddUImmOperand(nia, 4);
|
||||
return d.Finish();
|
||||
}
|
||||
XEEMITTER(bx, 0x48000000, I )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
// if AA then
|
||||
// NIA <- EXTS(LI || 0b00)
|
||||
// else
|
||||
// NIA <- CIA + EXTS(LI || 0b00)
|
||||
// if LK then
|
||||
// LR <- CIA + 4
|
||||
|
||||
uint32_t nia;
|
||||
if (i.I.AA) {
|
||||
nia = XEEXTS26(i.I.LI << 2);
|
||||
} else {
|
||||
nia = i.address + XEEXTS26(i.I.LI << 2);
|
||||
}
|
||||
if (i.I.LK) {
|
||||
g.update_lr_value(b.getInt32(i.address + 4));
|
||||
}
|
||||
|
||||
return XeEmitBranchTo(g, b, "bx", i.address, i.I.LK);
|
||||
}
|
||||
|
||||
XEDISASMR(bcx, 0x40000000, B )(InstrData& i, InstrDisasm& d) {
|
||||
// TODO(benvanik): mnemonics
|
||||
d.Init("bc", "Branch Conditional", i.B.LK ? InstrDisasm::kLR : 0);
|
||||
if (!XESELECTBITS(i.B.BO, 2, 2)) {
|
||||
d.AddCTR(InstrRegister::kReadWrite);
|
||||
}
|
||||
if (!XESELECTBITS(i.B.BO, 4, 4)) {
|
||||
d.AddCR(i.B.BI >> 2, InstrRegister::kRead);
|
||||
}
|
||||
d.AddUImmOperand(i.B.BO, 1);
|
||||
d.AddUImmOperand(i.B.BI, 1);
|
||||
return d.Finish();
|
||||
}
|
||||
XEEMITTER(bcx, 0x40000000, B )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
// if ¬BO[2] then
|
||||
// CTR <- CTR - 1
|
||||
// ctr_ok <- BO[2] | ((CTR[0:63] != 0) XOR BO[3])
|
||||
// cond_ok <- BO[0] | (CR[BI+32] ≡ BO[1])
|
||||
// if ctr_ok & cond_ok then
|
||||
// if AA then
|
||||
// NIA <- EXTS(BD || 0b00)
|
||||
// else
|
||||
// NIA <- CIA + EXTS(BD || 0b00)
|
||||
// if LK then
|
||||
// LR <- CIA + 4
|
||||
|
||||
// NOTE: the condition bits are reversed!
|
||||
// 01234 (docs)
|
||||
// 43210 (real)
|
||||
|
||||
// TODO(benvanik): this may be wrong and overwrite LRs when not desired!
|
||||
// The docs say always, though...
|
||||
if (i.B.LK) {
|
||||
g.update_lr_value(b.getInt32(i.address + 4));
|
||||
}
|
||||
|
||||
Value* ctr_ok = NULL;
|
||||
if (XESELECTBITS(i.B.BO, 2, 2)) {
|
||||
// Ignore ctr.
|
||||
} else {
|
||||
// Decrement counter.
|
||||
Value* ctr = g.ctr_value();
|
||||
ctr = b.CreateSub(ctr, b.getInt64(1));
|
||||
g.update_ctr_value(ctr);
|
||||
|
||||
// Ctr check.
|
||||
if (XESELECTBITS(i.B.BO, 1, 1)) {
|
||||
ctr_ok = b.CreateICmpEQ(ctr, b.getInt64(0));
|
||||
} else {
|
||||
ctr_ok = b.CreateICmpNE(ctr, b.getInt64(0));
|
||||
}
|
||||
}
|
||||
|
||||
Value* cond_ok = NULL;
|
||||
if (XESELECTBITS(i.B.BO, 4, 4)) {
|
||||
// Ignore cond.
|
||||
} else {
|
||||
Value* cr = g.cr_value(i.B.BI >> 2);
|
||||
cr = b.CreateAnd(cr, 1 << (i.B.BI & 3));
|
||||
if (XESELECTBITS(i.B.BO, 3, 3)) {
|
||||
cond_ok = b.CreateICmpNE(cr, b.getInt64(0));
|
||||
} else {
|
||||
cond_ok = b.CreateICmpEQ(cr, b.getInt64(0));
|
||||
}
|
||||
}
|
||||
|
||||
// We do a bit of optimization here to make the llvm assembly easier to read.
|
||||
Value* ok = NULL;
|
||||
if (ctr_ok && cond_ok) {
|
||||
ok = b.CreateAnd(ctr_ok, cond_ok);
|
||||
} else if (ctr_ok) {
|
||||
ok = ctr_ok;
|
||||
} else if (cond_ok) {
|
||||
ok = cond_ok;
|
||||
}
|
||||
|
||||
// Handle unconditional branches without extra fluff.
|
||||
BasicBlock* original_bb = b.GetInsertBlock();
|
||||
if (ok) {
|
||||
char name[32];
|
||||
xesnprintfa(name, XECOUNT(name), "loc_%.8X_bcx", i.address);
|
||||
BasicBlock* next_block = g.GetNextBasicBlock();
|
||||
BasicBlock* branch_bb = BasicBlock::Create(*g.context(), name, g.gen_fn(),
|
||||
next_block);
|
||||
|
||||
b.CreateCondBr(ok, branch_bb, next_block);
|
||||
b.SetInsertPoint(branch_bb);
|
||||
}
|
||||
|
||||
// Note that this occurs entirely within the branch true block.
|
||||
uint32_t nia;
|
||||
if (i.B.AA) {
|
||||
nia = XEEXTS26(i.B.BD << 2);
|
||||
} else {
|
||||
nia = i.address + XEEXTS26(i.B.BD << 2);
|
||||
}
|
||||
if (XeEmitBranchTo(g, b, "bcx", i.address, i.B.LK)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
b.SetInsertPoint(original_bb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
XEDISASMR(bcctrx, 0x4C000420, XL )(InstrData& i, InstrDisasm& d) {
|
||||
// TODO(benvanik): mnemonics
|
||||
d.Init("bcctr", "Branch Conditional to Count Register",
|
||||
i.XL.LK ? InstrDisasm::kLR : 0);
|
||||
if (!XESELECTBITS(i.XL.BO, 4, 4)) {
|
||||
d.AddCR(i.XL.BI >> 2, InstrRegister::kRead);
|
||||
}
|
||||
d.AddUImmOperand(i.XL.BO, 1);
|
||||
d.AddUImmOperand(i.XL.BI, 1);
|
||||
d.AddCTR(InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
XEEMITTER(bcctrx, 0x4C000420, XL )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
// cond_ok <- BO[0] | (CR[BI+32] ≡ BO[1])
|
||||
// if cond_ok then
|
||||
// NIA <- CTR[0:61] || 0b00
|
||||
// if LK then
|
||||
// LR <- CIA + 4
|
||||
|
||||
// NOTE: the condition bits are reversed!
|
||||
// 01234 (docs)
|
||||
// 43210 (real)
|
||||
|
||||
// TODO(benvanik): this may be wrong and overwrite LRs when not desired!
|
||||
// The docs say always, though...
|
||||
if (i.XL.LK) {
|
||||
g.update_lr_value(b.getInt32(i.address + 4));
|
||||
}
|
||||
|
||||
Value* cond_ok = NULL;
|
||||
if (XESELECTBITS(i.XL.BO, 4, 4)) {
|
||||
// Ignore cond.
|
||||
} else {
|
||||
Value* cr = g.cr_value(i.XL.BI >> 2);
|
||||
cr = b.CreateAnd(cr, 1 << (i.XL.BI & 3));
|
||||
if (XESELECTBITS(i.XL.BO, 3, 3)) {
|
||||
cond_ok = b.CreateICmpNE(cr, b.getInt64(0));
|
||||
} else {
|
||||
cond_ok = b.CreateICmpEQ(cr, b.getInt64(0));
|
||||
}
|
||||
}
|
||||
|
||||
// We do a bit of optimization here to make the llvm assembly easier to read.
|
||||
Value* ok = NULL;
|
||||
if (cond_ok) {
|
||||
ok = cond_ok;
|
||||
}
|
||||
|
||||
// Handle unconditional branches without extra fluff.
|
||||
BasicBlock* original_bb = b.GetInsertBlock();
|
||||
if (ok) {
|
||||
char name[32];
|
||||
xesnprintfa(name, XECOUNT(name), "loc_%.8X_bcctrx", i.address);
|
||||
BasicBlock* next_block = g.GetNextBasicBlock();
|
||||
XEASSERTNOTNULL(next_block);
|
||||
BasicBlock* branch_bb = BasicBlock::Create(*g.context(), name, g.gen_fn(),
|
||||
next_block);
|
||||
|
||||
b.CreateCondBr(ok, branch_bb, next_block);
|
||||
b.SetInsertPoint(branch_bb);
|
||||
}
|
||||
|
||||
// Note that this occurs entirely within the branch true block.
|
||||
if (XeEmitBranchTo(g, b, "bcctrx", i.address, i.XL.LK)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
b.SetInsertPoint(original_bb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEDISASMR(bclrx, 0x4C000020, XL )(InstrData& i, InstrDisasm& d) {
|
||||
std::string name = "bclr";
|
||||
if (i.code == 0x4E800020) {
|
||||
name = "blr";
|
||||
}
|
||||
d.Init(name, "Branch Conditional to Link Register",
|
||||
i.XL.LK ? InstrDisasm::kLR : 0);
|
||||
if (!XESELECTBITS(i.B.BO, 2, 2)) {
|
||||
d.AddCTR(InstrRegister::kReadWrite);
|
||||
}
|
||||
if (!XESELECTBITS(i.B.BO, 4, 4)) {
|
||||
d.AddCR(i.B.BI >> 2, InstrRegister::kRead);
|
||||
}
|
||||
d.AddUImmOperand(i.XL.BO, 1);
|
||||
d.AddUImmOperand(i.XL.BI, 1);
|
||||
d.AddLR(InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
XEEMITTER(bclrx, 0x4C000020, XL )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
// if ¬BO[2] then
|
||||
// CTR <- CTR - 1
|
||||
// ctr_ok <- BO[2] | ((CTR[0:63] != 0) XOR BO[3]
|
||||
// cond_ok <- BO[0] | (CR[BI+32] ≡ BO[1])
|
||||
// if ctr_ok & cond_ok then
|
||||
// NIA <- LR[0:61] || 0b00
|
||||
// if LK then
|
||||
// LR <- CIA + 4
|
||||
|
||||
// NOTE: the condition bits are reversed!
|
||||
// 01234 (docs)
|
||||
// 43210 (real)
|
||||
|
||||
// TODO(benvanik): this may be wrong and overwrite LRs when not desired!
|
||||
// The docs say always, though...
|
||||
if (i.XL.LK) {
|
||||
g.update_lr_value(b.getInt32(i.address + 4));
|
||||
}
|
||||
|
||||
Value* ctr_ok = NULL;
|
||||
if (XESELECTBITS(i.XL.BO, 2, 2)) {
|
||||
// Ignore ctr.
|
||||
} else {
|
||||
// Decrement counter.
|
||||
Value* ctr = g.ctr_value();
|
||||
ctr = b.CreateSub(ctr, b.getInt64(1));
|
||||
|
||||
// Ctr check.
|
||||
if (XESELECTBITS(i.XL.BO, 1, 1)) {
|
||||
ctr_ok = b.CreateICmpEQ(ctr, b.getInt64(0));
|
||||
} else {
|
||||
ctr_ok = b.CreateICmpNE(ctr, b.getInt64(0));
|
||||
}
|
||||
}
|
||||
|
||||
Value* cond_ok = NULL;
|
||||
if (XESELECTBITS(i.XL.BO, 4, 4)) {
|
||||
// Ignore cond.
|
||||
} else {
|
||||
Value* cr = g.cr_value(i.XL.BI >> 2);
|
||||
cr = b.CreateAnd(cr, 1 << (i.XL.BI & 3));
|
||||
if (XESELECTBITS(i.XL.BO, 3, 3)) {
|
||||
cond_ok = b.CreateICmpNE(cr, b.getInt64(0));
|
||||
} else {
|
||||
cond_ok = b.CreateICmpEQ(cr, b.getInt64(0));
|
||||
}
|
||||
}
|
||||
|
||||
// We do a bit of optimization here to make the llvm assembly easier to read.
|
||||
Value* ok = NULL;
|
||||
if (ctr_ok && cond_ok) {
|
||||
ok = b.CreateAnd(ctr_ok, cond_ok);
|
||||
} else if (ctr_ok) {
|
||||
ok = ctr_ok;
|
||||
} else if (cond_ok) {
|
||||
ok = cond_ok;
|
||||
}
|
||||
|
||||
// Handle unconditional branches without extra fluff.
|
||||
BasicBlock* original_bb = b.GetInsertBlock();
|
||||
if (ok) {
|
||||
char name[32];
|
||||
xesnprintfa(name, XECOUNT(name), "loc_%.8X_bclrx", i.address);
|
||||
BasicBlock* next_block = g.GetNextBasicBlock();
|
||||
XEASSERTNOTNULL(next_block);
|
||||
BasicBlock* branch_bb = BasicBlock::Create(*g.context(), name, g.gen_fn(),
|
||||
next_block);
|
||||
|
||||
b.CreateCondBr(ok, branch_bb, next_block);
|
||||
b.SetInsertPoint(branch_bb);
|
||||
}
|
||||
|
||||
// Note that this occurs entirely within the branch true block.
|
||||
if (XeEmitBranchTo(g, b, "bclrx", i.address, i.XL.LK)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
b.SetInsertPoint(original_bb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Condition register logical (A-23)
|
||||
|
||||
XEEMITTER(crand, 0x4C000202, XL )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(crandc, 0x4C000102, XL )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(creqv, 0x4C000242, XL )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(crnand, 0x4C0001C2, XL )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(crnor, 0x4C000042, XL )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(cror, 0x4C000382, XL )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(crorc, 0x4C000342, XL )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(crxor, 0x4C000182, XL )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(mcrf, 0x4C000000, XL )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
// System linkage (A-24)
|
||||
|
||||
XEEMITTER(sc, 0x44000002, SC )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
// Trap (A-25)
|
||||
|
||||
int XeEmitTrap(FunctionGenerator& g, IRBuilder<>& b, InstrData& i,
|
||||
Value* va, Value* vb, uint32_t TO) {
|
||||
// if (a < b) & TO[0] then TRAP
|
||||
// if (a > b) & TO[1] then TRAP
|
||||
// if (a = b) & TO[2] then TRAP
|
||||
// if (a <u b) & TO[3] then TRAP
|
||||
// if (a >u b) & TO[4] then TRAP
|
||||
// Bits swapped:
|
||||
// 01234
|
||||
// 43210
|
||||
|
||||
if (!TO) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
BasicBlock* after_bb = BasicBlock::Create(*g.context(), "", g.gen_fn(),
|
||||
g.GetNextBasicBlock());
|
||||
BasicBlock* trap_bb = BasicBlock::Create(*g.context(), "", g.gen_fn(),
|
||||
after_bb);
|
||||
|
||||
// Create the basic blocks (so we can chain).
|
||||
std::vector<BasicBlock*> bbs;
|
||||
if (TO & (1 << 4)) {
|
||||
bbs.push_back(BasicBlock::Create(*g.context(), "", g.gen_fn(), trap_bb));
|
||||
}
|
||||
if (TO & (1 << 3)) {
|
||||
bbs.push_back(BasicBlock::Create(*g.context(), "", g.gen_fn(), trap_bb));
|
||||
}
|
||||
if (TO & (1 << 2)) {
|
||||
bbs.push_back(BasicBlock::Create(*g.context(), "", g.gen_fn(), trap_bb));
|
||||
}
|
||||
if (TO & (1 << 1)) {
|
||||
bbs.push_back(BasicBlock::Create(*g.context(), "", g.gen_fn(), trap_bb));
|
||||
}
|
||||
if (TO & (1 << 0)) {
|
||||
bbs.push_back(BasicBlock::Create(*g.context(), "", g.gen_fn(), trap_bb));
|
||||
}
|
||||
bbs.push_back(after_bb);
|
||||
|
||||
// Jump to the first bb.
|
||||
b.CreateBr(bbs.front());
|
||||
|
||||
// Setup each basic block.
|
||||
std::vector<BasicBlock*>::iterator it = bbs.begin();
|
||||
if (TO & (1 << 4)) {
|
||||
// a < b
|
||||
BasicBlock* bb = *(it++);
|
||||
b.SetInsertPoint(bb);
|
||||
Value* cmp = b.CreateICmpSLT(va, vb);
|
||||
b.CreateCondBr(cmp, trap_bb, *it);
|
||||
}
|
||||
if (TO & (1 << 3)) {
|
||||
// a > b
|
||||
BasicBlock* bb = *(it++);
|
||||
b.SetInsertPoint(bb);
|
||||
Value* cmp = b.CreateICmpSGT(va, vb);
|
||||
b.CreateCondBr(cmp, trap_bb, *it);
|
||||
}
|
||||
if (TO & (1 << 2)) {
|
||||
// a = b
|
||||
BasicBlock* bb = *(it++);
|
||||
b.SetInsertPoint(bb);
|
||||
Value* cmp = b.CreateICmpEQ(va, vb);
|
||||
b.CreateCondBr(cmp, trap_bb, *it);
|
||||
}
|
||||
if (TO & (1 << 1)) {
|
||||
// a <u b
|
||||
BasicBlock* bb = *(it++);
|
||||
b.SetInsertPoint(bb);
|
||||
Value* cmp = b.CreateICmpULT(va, vb);
|
||||
b.CreateCondBr(cmp, trap_bb, *it);
|
||||
}
|
||||
if (TO & (1 << 0)) {
|
||||
// a >u b
|
||||
BasicBlock* bb = *(it++);
|
||||
b.SetInsertPoint(bb);
|
||||
Value* cmp = b.CreateICmpUGT(va, vb);
|
||||
b.CreateCondBr(cmp, trap_bb, *it);
|
||||
}
|
||||
|
||||
// Create trap BB.
|
||||
b.SetInsertPoint(trap_bb);
|
||||
g.SpillRegisters();
|
||||
// TODO(benvanik): use @llvm.debugtrap? could make debugging better
|
||||
b.CreateCall2(g.gen_module()->getFunction("XeTrap"),
|
||||
g.gen_fn()->arg_begin(),
|
||||
b.getInt32(i.address));
|
||||
b.CreateBr(after_bb);
|
||||
|
||||
// Resume.
|
||||
b.SetInsertPoint(after_bb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEDISASMR(td, 0x7C000088, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("td", "Trap Doubleword", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
XEEMITTER(td, 0x7C000088, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
// a <- (RA)
|
||||
// b <- (RB)
|
||||
// if (a < b) & TO[0] then TRAP
|
||||
// if (a > b) & TO[1] then TRAP
|
||||
// if (a = b) & TO[2] then TRAP
|
||||
// if (a <u b) & TO[3] then TRAP
|
||||
// if (a >u b) & TO[4] then TRAP
|
||||
return XeEmitTrap(g, b, i,
|
||||
g.gpr_value(i.X.RA),
|
||||
g.gpr_value(i.X.RB),
|
||||
i.X.RT);
|
||||
}
|
||||
|
||||
XEDISASMR(tdi, 0x08000000, D )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("tdi", "Trap Doubleword Immediate", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RA, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
XEEMITTER(tdi, 0x08000000, D )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
// a <- (RA)
|
||||
// if (a < EXTS(SI)) & TO[0] then TRAP
|
||||
// if (a > EXTS(SI)) & TO[1] then TRAP
|
||||
// if (a = EXTS(SI)) & TO[2] then TRAP
|
||||
// if (a <u EXTS(SI)) & TO[3] then TRAP
|
||||
// if (a >u EXTS(SI)) & TO[4] then TRAP
|
||||
return XeEmitTrap(g, b, i,
|
||||
g.gpr_value(i.D.RA),
|
||||
b.getInt64(XEEXTS16(i.D.DS)),
|
||||
i.D.RT);
|
||||
}
|
||||
|
||||
XEDISASMR(tw, 0x7C000008, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("tw", "Trap Word", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
XEEMITTER(tw, 0x7C000008, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
// a <- EXTS((RA)[32:63])
|
||||
// b <- EXTS((RB)[32:63])
|
||||
// if (a < b) & TO[0] then TRAP
|
||||
// if (a > b) & TO[1] then TRAP
|
||||
// if (a = b) & TO[2] then TRAP
|
||||
// if (a <u b) & TO[3] then TRAP
|
||||
// if (a >u b) & TO[4] then TRAP
|
||||
return XeEmitTrap(g, b, i,
|
||||
b.CreateSExt(b.CreateTrunc(g.gpr_value(i.X.RA),
|
||||
b.getInt32Ty()),
|
||||
b.getInt64Ty()),
|
||||
b.CreateSExt(b.CreateTrunc(g.gpr_value(i.X.RB),
|
||||
b.getInt32Ty()),
|
||||
b.getInt64Ty()),
|
||||
i.X.RT);
|
||||
}
|
||||
|
||||
XEDISASMR(twi, 0x0C000000, D )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("twi", "Trap Word Immediate", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RA, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
XEEMITTER(twi, 0x0C000000, D )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
// a <- EXTS((RA)[32:63])
|
||||
// if (a < EXTS(SI)) & TO[0] then TRAP
|
||||
// if (a > EXTS(SI)) & TO[1] then TRAP
|
||||
// if (a = EXTS(SI)) & TO[2] then TRAP
|
||||
// if (a <u EXTS(SI)) & TO[3] then TRAP
|
||||
// if (a >u EXTS(SI)) & TO[4] then TRAP
|
||||
return XeEmitTrap(g, b, i,
|
||||
b.CreateSExt(b.CreateTrunc(g.gpr_value(i.D.RA),
|
||||
b.getInt32Ty()),
|
||||
b.getInt64Ty()),
|
||||
b.getInt64(XEEXTS16(i.D.DS)),
|
||||
i.D.RT);
|
||||
}
|
||||
|
||||
|
||||
// Processor control (A-26)
|
||||
|
||||
XEEMITTER(mfcr, 0x7C000026, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEDISASMR(mfspr, 0x7C0002A6, XFX)(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("mfspr", "Move From Special Purpose Register", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.XFX.RT, InstrRegister::kWrite);
|
||||
const uint32_t n = ((i.XFX.spr & 0x1F) << 5) | ((i.XFX.spr >> 5) & 0x1F);
|
||||
switch (n) {
|
||||
case 1:
|
||||
d.AddRegOperand(InstrRegister::kXER, 0, InstrRegister::kRead);
|
||||
break;
|
||||
case 8:
|
||||
d.AddRegOperand(InstrRegister::kLR, 0, InstrRegister::kRead);
|
||||
break;
|
||||
case 9:
|
||||
d.AddRegOperand(InstrRegister::kCTR, 0, InstrRegister::kRead);
|
||||
break;
|
||||
}
|
||||
return d.Finish();
|
||||
}
|
||||
XEEMITTER(mfspr, 0x7C0002A6, XFX)(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
// n <- spr[5:9] || spr[0:4]
|
||||
// if length(SPR(n)) = 64 then
|
||||
// RT <- SPR(n)
|
||||
// else
|
||||
// RT <- i32.0 || SPR(n)
|
||||
|
||||
const uint32_t n = ((i.XFX.spr & 0x1F) << 5) | ((i.XFX.spr >> 5) & 0x1F);
|
||||
Value* v = NULL;
|
||||
switch (n) {
|
||||
case 1:
|
||||
// XER
|
||||
v = g.xer_value();
|
||||
break;
|
||||
case 8:
|
||||
// LR
|
||||
v = g.lr_value();
|
||||
break;
|
||||
case 9:
|
||||
// CTR
|
||||
v = g.ctr_value();
|
||||
break;
|
||||
default:
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
g.update_gpr_value(i.XFX.RT, v);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(mftb, 0x7C0002E6, XFX)(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(mtcrf, 0x7C000120, XFX)(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEDISASMR(mtspr, 0x7C0003A6, XFX)(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("mtspr", "Move To Special Purpose Register", 0);
|
||||
const uint32_t n = ((i.XFX.spr & 0x1F) << 5) | ((i.XFX.spr >> 5) & 0x1F);
|
||||
switch (n) {
|
||||
case 1:
|
||||
d.AddRegOperand(InstrRegister::kXER, 0, InstrRegister::kWrite);
|
||||
break;
|
||||
case 8:
|
||||
d.AddRegOperand(InstrRegister::kLR, 0, InstrRegister::kWrite);
|
||||
break;
|
||||
case 9:
|
||||
d.AddRegOperand(InstrRegister::kCTR, 0, InstrRegister::kWrite);
|
||||
break;
|
||||
}
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.XFX.RT, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
XEEMITTER(mtspr, 0x7C0003A6, XFX)(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
// n <- spr[5:9] || spr[0:4]
|
||||
// if length(SPR(n)) = 64 then
|
||||
// SPR(n) <- (RS)
|
||||
// else
|
||||
// SPR(n) <- (RS)[32:63]
|
||||
|
||||
Value* v = g.gpr_value(i.XFX.RT);
|
||||
|
||||
const uint32_t n = ((i.XFX.spr & 0x1F) << 5) | ((i.XFX.spr >> 5) & 0x1F);
|
||||
switch (n) {
|
||||
case 1:
|
||||
// XER
|
||||
g.update_xer_value(v);
|
||||
break;
|
||||
case 8:
|
||||
// LR
|
||||
g.update_lr_value(v);
|
||||
break;
|
||||
case 9:
|
||||
// CTR
|
||||
g.update_ctr_value(v);
|
||||
break;
|
||||
default:
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void RegisterEmitCategoryControl() {
|
||||
XEREGISTERINSTR(bx, 0x48000000);
|
||||
XEREGISTERINSTR(bcx, 0x40000000);
|
||||
XEREGISTERINSTR(bcctrx, 0x4C000420);
|
||||
XEREGISTERINSTR(bclrx, 0x4C000020);
|
||||
XEREGISTEREMITTER(crand, 0x4C000202);
|
||||
XEREGISTEREMITTER(crandc, 0x4C000102);
|
||||
XEREGISTEREMITTER(creqv, 0x4C000242);
|
||||
XEREGISTEREMITTER(crnand, 0x4C0001C2);
|
||||
XEREGISTEREMITTER(crnor, 0x4C000042);
|
||||
XEREGISTEREMITTER(cror, 0x4C000382);
|
||||
XEREGISTEREMITTER(crorc, 0x4C000342);
|
||||
XEREGISTEREMITTER(crxor, 0x4C000182);
|
||||
XEREGISTEREMITTER(mcrf, 0x4C000000);
|
||||
XEREGISTEREMITTER(sc, 0x44000002);
|
||||
XEREGISTERINSTR(td, 0x7C000088);
|
||||
XEREGISTERINSTR(tdi, 0x08000000);
|
||||
XEREGISTERINSTR(tw, 0x7C000008);
|
||||
XEREGISTERINSTR(twi, 0x0C000000);
|
||||
XEREGISTEREMITTER(mfcr, 0x7C000026);
|
||||
XEREGISTERINSTR(mfspr, 0x7C0002A6);
|
||||
XEREGISTEREMITTER(mftb, 0x7C0002E6);
|
||||
XEREGISTEREMITTER(mtcrf, 0x7C000120);
|
||||
XEREGISTERINSTR(mtspr, 0x7C0003A6);
|
||||
}
|
||||
|
||||
|
||||
} // namespace codegen
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
304
src/xenia/cpu/codegen/emit_fpu.cc
Normal file
304
src/xenia/cpu/codegen/emit_fpu.cc
Normal file
@@ -0,0 +1,304 @@
|
||||
/*
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <xenia/cpu/codegen/emit.h>
|
||||
|
||||
#include <xenia/cpu/codegen/function_generator.h>
|
||||
|
||||
|
||||
using namespace llvm;
|
||||
using namespace xe::cpu::codegen;
|
||||
using namespace xe::cpu::ppc;
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace codegen {
|
||||
|
||||
|
||||
// Floating-point arithmetic (A-8)
|
||||
|
||||
XEEMITTER(faddx, 0xFC00002A, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(faddsx, 0xEC00002A, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fdivx, 0xFC000024, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fdivsx, 0xEC000024, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fmulx, 0xFC000032, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fmulsx, 0xEC000032, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fresx, 0xEC000030, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(frsqrtex, 0xFC000034, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fsubx, 0xFC000028, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fsubsx, 0xEC000028, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fselx, 0xFC00002E, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fsqrtx, 0xFC00002C, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fsqrtsx, 0xEC00002C, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
// Floating-point multiply-add (A-9)
|
||||
|
||||
XEEMITTER(fmaddx, 0xFC00003A, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fmaddsx, 0xEC00003A, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fmsubx, 0xFC000038, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fmsubsx, 0xEC000038, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fnmaddx, 0xFC00003E, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fnmaddsx, 0xEC00003E, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fnmsubx, 0xFC00003C, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fnmsubsx, 0xEC00003C, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
// Floating-point rounding and conversion (A-10)
|
||||
|
||||
XEEMITTER(fcfidx, 0xFC00069C, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fctidx, 0xFC00065C, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fctidzx, 0xFC00065E, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fctiwx, 0xFC00001C, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fctiwzx, 0xFC00001E, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(frspx, 0xFC000018, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
// Floating-point compare (A-11)
|
||||
|
||||
XEEMITTER(fcmpo, 0xFC000040, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEDISASMR(fcmpu, 0xFC000000, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("fcmpu", "Floating Compare Unordered",
|
||||
(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 d.Finish();
|
||||
}
|
||||
XEEMITTER(fcmpu, 0xFC000000, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
// if (FRA) is a NaN or (FRB) is a NaN then
|
||||
// c <- 0b0001
|
||||
// else if (FRA) < (FRB) then
|
||||
// c <- 0b1000
|
||||
// else if (FRA) > (FRB) then
|
||||
// c <- 0b0100
|
||||
// else {
|
||||
// c <- 0b0010
|
||||
// }
|
||||
// FPCC <- c
|
||||
// CR[4*BF:4*BF+3] <- c
|
||||
// if (FRA) is an SNaN or (FRB) is an SNaN then
|
||||
// VXSNAN <- 1
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
// Floating-point status and control register (A
|
||||
|
||||
XEEMITTER(mcrfs, 0xFC000080, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(mffsx, 0xFC00048E, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(mtfsb0x, 0xFC00008C, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(mtfsb1x, 0xFC00004C, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(mtfsfx, 0xFC00058E, XFL)(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(mtfsfix, 0xFC00010C, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
// Floating-point move (A-21)
|
||||
|
||||
XEEMITTER(fabsx, 0xFC000210, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fmrx, 0xFC000090, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fnabsx, 0xFC000110, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fnegx, 0xFC000050, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
void RegisterEmitCategoryFPU() {
|
||||
XEREGISTEREMITTER(faddx, 0xFC00002A);
|
||||
XEREGISTEREMITTER(faddsx, 0xEC00002A);
|
||||
XEREGISTEREMITTER(fdivx, 0xFC000024);
|
||||
XEREGISTEREMITTER(fdivsx, 0xEC000024);
|
||||
XEREGISTEREMITTER(fmulx, 0xFC000032);
|
||||
XEREGISTEREMITTER(fmulsx, 0xEC000032);
|
||||
XEREGISTEREMITTER(fresx, 0xEC000030);
|
||||
XEREGISTEREMITTER(frsqrtex, 0xFC000034);
|
||||
XEREGISTEREMITTER(fsubx, 0xFC000028);
|
||||
XEREGISTEREMITTER(fsubsx, 0xEC000028);
|
||||
XEREGISTEREMITTER(fselx, 0xFC00002E);
|
||||
XEREGISTEREMITTER(fsqrtx, 0xFC00002C);
|
||||
XEREGISTEREMITTER(fsqrtsx, 0xEC00002C);
|
||||
XEREGISTEREMITTER(fmaddx, 0xFC00003A);
|
||||
XEREGISTEREMITTER(fmaddsx, 0xEC00003A);
|
||||
XEREGISTEREMITTER(fmsubx, 0xFC000038);
|
||||
XEREGISTEREMITTER(fmsubsx, 0xEC000038);
|
||||
XEREGISTEREMITTER(fnmaddx, 0xFC00003E);
|
||||
XEREGISTEREMITTER(fnmaddsx, 0xEC00003E);
|
||||
XEREGISTEREMITTER(fnmsubx, 0xFC00003C);
|
||||
XEREGISTEREMITTER(fnmsubsx, 0xEC00003C);
|
||||
XEREGISTEREMITTER(fcfidx, 0xFC00069C);
|
||||
XEREGISTEREMITTER(fctidx, 0xFC00065C);
|
||||
XEREGISTEREMITTER(fctidzx, 0xFC00065E);
|
||||
XEREGISTEREMITTER(fctiwx, 0xFC00001C);
|
||||
XEREGISTEREMITTER(fctiwzx, 0xFC00001E);
|
||||
XEREGISTEREMITTER(frspx, 0xFC000018);
|
||||
XEREGISTEREMITTER(fcmpo, 0xFC000040);
|
||||
XEREGISTEREMITTER(fcmpu, 0xFC000000);
|
||||
XEREGISTEREMITTER(mcrfs, 0xFC000080);
|
||||
XEREGISTEREMITTER(mffsx, 0xFC00048E);
|
||||
XEREGISTEREMITTER(mtfsb0x, 0xFC00008C);
|
||||
XEREGISTEREMITTER(mtfsb1x, 0xFC00004C);
|
||||
XEREGISTEREMITTER(mtfsfx, 0xFC00058E);
|
||||
XEREGISTEREMITTER(mtfsfix, 0xFC00010C);
|
||||
XEREGISTEREMITTER(fabsx, 0xFC000210);
|
||||
XEREGISTEREMITTER(fmrx, 0xFC000090);
|
||||
XEREGISTEREMITTER(fnabsx, 0xFC000110);
|
||||
XEREGISTEREMITTER(fnegx, 0xFC000050);
|
||||
}
|
||||
|
||||
|
||||
} // namespace codegen
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
1679
src/xenia/cpu/codegen/emit_memory.cc
Normal file
1679
src/xenia/cpu/codegen/emit_memory.cc
Normal file
File diff suppressed because it is too large
Load Diff
1057
src/xenia/cpu/codegen/function_generator.cc
Normal file
1057
src/xenia/cpu/codegen/function_generator.cc
Normal file
File diff suppressed because it is too large
Load Diff
144
src/xenia/cpu/codegen/function_generator.h
Normal file
144
src/xenia/cpu/codegen/function_generator.h
Normal file
@@ -0,0 +1,144 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_CPU_CODEGEN_FUNCTION_GENERATOR_H_
|
||||
#define XENIA_CPU_CODEGEN_FUNCTION_GENERATOR_H_
|
||||
|
||||
#include <llvm/IR/Attributes.h>
|
||||
#include <llvm/IR/DataLayout.h>
|
||||
#include <llvm/IR/DerivedTypes.h>
|
||||
#include <llvm/IR/IRBuilder.h>
|
||||
#include <llvm/IR/LLVMContext.h>
|
||||
#include <llvm/IR/Module.h>
|
||||
|
||||
#include <xenia/cpu/sdb.h>
|
||||
#include <xenia/cpu/ppc/instr.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace codegen {
|
||||
|
||||
|
||||
class FunctionGenerator {
|
||||
public:
|
||||
FunctionGenerator(
|
||||
xe_memory_ref memory, sdb::SymbolDatabase* sdb, sdb::FunctionSymbol* fn,
|
||||
llvm::LLVMContext* context, llvm::Module* gen_module,
|
||||
llvm::Function* gen_fn);
|
||||
~FunctionGenerator();
|
||||
|
||||
sdb::SymbolDatabase* sdb();
|
||||
sdb::FunctionSymbol* fn();
|
||||
llvm::LLVMContext* context();
|
||||
llvm::Module* gen_module();
|
||||
llvm::Function* gen_fn();
|
||||
sdb::FunctionBlock* fn_block();
|
||||
|
||||
void PushInsertPoint();
|
||||
void PopInsertPoint();
|
||||
|
||||
void GenerateBasicBlocks();
|
||||
llvm::BasicBlock* GetBasicBlock(uint32_t address);
|
||||
llvm::BasicBlock* GetNextBasicBlock();
|
||||
llvm::BasicBlock* GetReturnBasicBlock();
|
||||
|
||||
llvm::Function* GetFunction(sdb::FunctionSymbol* fn);
|
||||
|
||||
int GenerateIndirectionBranch(uint32_t cia, llvm::Value* target,
|
||||
bool lk, bool likely_local);
|
||||
|
||||
llvm::Value* LoadStateValue(uint32_t offset, llvm::Type* type,
|
||||
const char* name = "");
|
||||
void StoreStateValue(uint32_t offset, llvm::Type* type, llvm::Value* value);
|
||||
|
||||
llvm::Value* cia_value();
|
||||
|
||||
llvm::Value* SetupLocal(llvm::Type* type, const char* name);
|
||||
void FillRegisters();
|
||||
void SpillRegisters();
|
||||
|
||||
llvm::Value* xer_value();
|
||||
void update_xer_value(llvm::Value* value);
|
||||
void update_xer_with_overflow(llvm::Value* value);
|
||||
void update_xer_with_carry(llvm::Value* value);
|
||||
void update_xer_with_overflow_and_carry(llvm::Value* value);
|
||||
|
||||
llvm::Value* lr_value();
|
||||
void update_lr_value(llvm::Value* value);
|
||||
|
||||
llvm::Value* ctr_value();
|
||||
void update_ctr_value(llvm::Value* value);
|
||||
|
||||
llvm::Value* cr_value(uint32_t n);
|
||||
void update_cr_value(uint32_t n, llvm::Value* value);
|
||||
void update_cr_with_cond(uint32_t n, llvm::Value* lhs, llvm::Value* rhs,
|
||||
bool is_signed);
|
||||
|
||||
llvm::Value* gpr_value(uint32_t n);
|
||||
void update_gpr_value(uint32_t n, llvm::Value* value);
|
||||
llvm::Value* fpr_value(uint32_t n);
|
||||
void update_fpr_value(uint32_t n, llvm::Value* value);
|
||||
|
||||
llvm::Value* GetMembase();
|
||||
llvm::Value* GetMemoryAddress(uint32_t cia, llvm::Value* addr);
|
||||
llvm::Value* ReadMemory(
|
||||
uint32_t cia, llvm::Value* addr, uint32_t size, bool acquire = false);
|
||||
void WriteMemory(
|
||||
uint32_t cia, llvm::Value* addr, uint32_t size, llvm::Value* value,
|
||||
bool release = false);
|
||||
|
||||
private:
|
||||
void GenerateSharedBlocks();
|
||||
int PrepareBasicBlock(sdb::FunctionBlock* block);
|
||||
void GenerateBasicBlock(sdb::FunctionBlock* block);
|
||||
void SetupLocals();
|
||||
|
||||
xe_memory_ref memory_;
|
||||
sdb::SymbolDatabase* sdb_;
|
||||
sdb::FunctionSymbol* fn_;
|
||||
llvm::LLVMContext* context_;
|
||||
llvm::Module* gen_module_;
|
||||
llvm::Function* gen_fn_;
|
||||
sdb::FunctionBlock* fn_block_;
|
||||
llvm::BasicBlock* return_block_;
|
||||
llvm::BasicBlock* internal_indirection_block_;
|
||||
llvm::BasicBlock* external_indirection_block_;
|
||||
llvm::BasicBlock* bb_;
|
||||
llvm::IRBuilder<>* builder_;
|
||||
|
||||
std::vector<std::pair<llvm::BasicBlock*, llvm::BasicBlock::iterator> >
|
||||
insert_points_;
|
||||
|
||||
std::map<uint32_t, llvm::BasicBlock*> bbs_;
|
||||
|
||||
// Address of the instruction being generated.
|
||||
uint32_t cia_;
|
||||
|
||||
ppc::InstrAccessBits access_bits_;
|
||||
struct {
|
||||
llvm::Value* indirection_target;
|
||||
llvm::Value* indirection_cia;
|
||||
|
||||
llvm::Value* xer;
|
||||
llvm::Value* lr;
|
||||
llvm::Value* ctr;
|
||||
llvm::Value* cr[8];
|
||||
llvm::Value* gpr[32];
|
||||
llvm::Value* fpr[32];
|
||||
} locals_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace codegen
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_CODEGEN_FUNCTION_GENERATOR_H_
|
||||
335
src/xenia/cpu/codegen/module_generator.cc
Normal file
335
src/xenia/cpu/codegen/module_generator.cc
Normal file
@@ -0,0 +1,335 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <xenia/cpu/codegen/module_generator.h>
|
||||
|
||||
#include <llvm/DIBuilder.h>
|
||||
#include <llvm/Linker.h>
|
||||
#include <llvm/PassManager.h>
|
||||
#include <llvm/DebugInfo.h>
|
||||
#include <llvm/Analysis/Verifier.h>
|
||||
#include <llvm/ExecutionEngine/ExecutionEngine.h>
|
||||
#include <llvm/IR/Attributes.h>
|
||||
#include <llvm/IR/DataLayout.h>
|
||||
#include <llvm/IR/DerivedTypes.h>
|
||||
#include <llvm/IR/IRBuilder.h>
|
||||
#include <llvm/IR/LLVMContext.h>
|
||||
#include <llvm/IR/Module.h>
|
||||
#include <llvm/Transforms/IPO.h>
|
||||
#include <llvm/Transforms/IPO/PassManagerBuilder.h>
|
||||
|
||||
#include <xenia/cpu/cpu-private.h>
|
||||
#include <xenia/cpu/ppc.h>
|
||||
#include <xenia/cpu/codegen/function_generator.h>
|
||||
|
||||
|
||||
using namespace llvm;
|
||||
using namespace xe;
|
||||
using namespace xe::cpu;
|
||||
using namespace xe::cpu::codegen;
|
||||
using namespace xe::cpu::sdb;
|
||||
using namespace xe::kernel;
|
||||
|
||||
|
||||
ModuleGenerator::ModuleGenerator(
|
||||
xe_memory_ref memory, ExportResolver* export_resolver,
|
||||
const char* module_name, const char* module_path, SymbolDatabase* sdb,
|
||||
LLVMContext* context, Module* gen_module, ExecutionEngine* engine) {
|
||||
memory_ = xe_memory_retain(memory);
|
||||
export_resolver_ = export_resolver;
|
||||
module_name_ = xestrdupa(module_name);
|
||||
module_path_ = xestrdupa(module_path);
|
||||
sdb_ = sdb;
|
||||
context_ = context;
|
||||
gen_module_ = gen_module;
|
||||
engine_ = engine;
|
||||
di_builder_ = NULL;
|
||||
}
|
||||
|
||||
ModuleGenerator::~ModuleGenerator() {
|
||||
for (std::map<uint32_t, CodegenFunction*>::iterator it =
|
||||
functions_.begin(); it != functions_.end(); ++it) {
|
||||
delete it->second;
|
||||
}
|
||||
|
||||
delete di_builder_;
|
||||
xe_free(module_path_);
|
||||
xe_free(module_name_);
|
||||
xe_memory_release(memory_);
|
||||
}
|
||||
|
||||
int ModuleGenerator::Generate() {
|
||||
std::string error_message;
|
||||
|
||||
// Setup a debug info builder.
|
||||
// This is used when creating any debug info. We may want to go more
|
||||
// fine grained than this, but for now it's something.
|
||||
char dir[XE_MAX_PATH];
|
||||
XEIGNORE(xestrcpya(dir, XECOUNT(dir), module_path_));
|
||||
char* slash = xestrrchra(dir, '/');
|
||||
if (slash) {
|
||||
*(slash + 1) = 0;
|
||||
}
|
||||
di_builder_ = new DIBuilder(*gen_module_);
|
||||
di_builder_->createCompileUnit(
|
||||
dwarf::DW_LANG_C99, //0x8010,
|
||||
StringRef(module_name_),
|
||||
StringRef(dir),
|
||||
StringRef("xenia"),
|
||||
true,
|
||||
StringRef(""),
|
||||
0);
|
||||
cu_ = (MDNode*)di_builder_->getCU();
|
||||
|
||||
// Add export wrappers.
|
||||
//
|
||||
|
||||
// Add all functions.
|
||||
// We do two passes - the first creates the function signature and global
|
||||
// value (so that we can call it), the second actually builds the function.
|
||||
std::vector<FunctionSymbol*> functions;
|
||||
if (!sdb_->GetAllFunctions(functions)) {
|
||||
XELOGI(XT("Beginning prep of %ld functions..."), functions.size());
|
||||
for (std::vector<FunctionSymbol*>::iterator it = functions.begin();
|
||||
it != functions.end(); ++it) {
|
||||
FunctionSymbol* fn = *it;
|
||||
switch (fn->type) {
|
||||
case FunctionSymbol::User:
|
||||
PrepareFunction(fn);
|
||||
break;
|
||||
case FunctionSymbol::Kernel:
|
||||
if (fn->kernel_export && fn->kernel_export->is_implemented) {
|
||||
AddPresentImport(fn);
|
||||
} else {
|
||||
AddMissingImport(fn);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
XEASSERTALWAYS();
|
||||
break;
|
||||
}
|
||||
}
|
||||
XELOGI(XT("Function prep complete"));
|
||||
}
|
||||
|
||||
// Build out all the user functions.
|
||||
size_t n = 0;
|
||||
XELOGI(XT("Beginning generation of %ld functions..."), functions.size());
|
||||
for (std::map<uint32_t, CodegenFunction*>::iterator it =
|
||||
functions_.begin(); it != functions_.end(); ++it, ++n) {
|
||||
FunctionSymbol* symbol = it->second->symbol;
|
||||
XELOGI(XT("Generating %ld/%ld %.8X %s"),
|
||||
n, functions_.size(), symbol->start_address, symbol->name());
|
||||
BuildFunction(it->second);
|
||||
}
|
||||
XELOGI(XT("Function generation complete"));
|
||||
|
||||
di_builder_->finalize();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ModuleGenerator::AddFunctionsToMap(
|
||||
std::tr1::unordered_map<uint32_t, llvm::Function*>& map) {
|
||||
for (std::map<uint32_t, CodegenFunction*>::iterator it = functions_.begin();
|
||||
it != functions_.end(); ++it) {
|
||||
map.insert(std::pair<uint32_t, Function*>(it->first, it->second->function));
|
||||
}
|
||||
}
|
||||
|
||||
ModuleGenerator::CodegenFunction* ModuleGenerator::GetCodegenFunction(
|
||||
uint32_t address) {
|
||||
std::map<uint32_t, CodegenFunction*>::iterator it = functions_.find(address);
|
||||
if (it != functions_.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Function* ModuleGenerator::CreateFunctionDefinition(const char* name) {
|
||||
Module* m = gen_module_;
|
||||
LLVMContext& context = m->getContext();
|
||||
|
||||
std::vector<Type*> args;
|
||||
args.push_back(PointerType::getUnqual(Type::getInt8Ty(context)));
|
||||
args.push_back(Type::getInt64Ty(context));
|
||||
Type* return_type = Type::getVoidTy(context);
|
||||
|
||||
FunctionType* ft = FunctionType::get(return_type,
|
||||
ArrayRef<Type*>(args), false);
|
||||
Function* f = cast<Function>(m->getOrInsertFunction(
|
||||
StringRef(name), ft));
|
||||
f->setVisibility(GlobalValue::DefaultVisibility);
|
||||
|
||||
// Indicate that the function will never be unwound with an exception.
|
||||
// If we ever support native exception handling we may need to remove this.
|
||||
f->doesNotThrow();
|
||||
|
||||
// May be worth trying the X86_FastCall, as we only need state in a register.
|
||||
//f->setCallingConv(CallingConv::Fast);
|
||||
f->setCallingConv(CallingConv::C);
|
||||
|
||||
Function::arg_iterator fn_args = f->arg_begin();
|
||||
// 'state'
|
||||
Value* fn_arg = fn_args++;
|
||||
fn_arg->setName("state");
|
||||
f->setDoesNotAlias(1);
|
||||
f->setDoesNotCapture(1);
|
||||
// 'state' should try to be in a register, if possible.
|
||||
// TODO(benvanik): verify that's a good idea.
|
||||
// f->getArgumentList().begin()->addAttr(
|
||||
// Attribute::get(context, AttrBuilder().addAttribute(Attribute::InReg)));
|
||||
|
||||
// 'lr'
|
||||
fn_arg = fn_args++;
|
||||
fn_arg->setName("lr");
|
||||
|
||||
return f;
|
||||
};
|
||||
|
||||
void ModuleGenerator::AddMissingImport(FunctionSymbol* fn) {
|
||||
Module *m = gen_module_;
|
||||
LLVMContext& context = m->getContext();
|
||||
|
||||
// Create the function (and setup args/attributes/etc).
|
||||
Function* f = CreateFunctionDefinition(fn->name());
|
||||
|
||||
BasicBlock* block = BasicBlock::Create(context, "entry", f);
|
||||
IRBuilder<> b(block);
|
||||
|
||||
if (FLAGS_trace_kernel_calls) {
|
||||
Value* traceKernelCall = m->getFunction("XeTraceKernelCall");
|
||||
b.CreateCall4(
|
||||
traceKernelCall,
|
||||
f->arg_begin(),
|
||||
b.getInt64(fn->start_address),
|
||||
++f->arg_begin(),
|
||||
b.getInt64((uint64_t)fn->kernel_export));
|
||||
}
|
||||
|
||||
b.CreateRetVoid();
|
||||
|
||||
OptimizeFunction(m, f);
|
||||
|
||||
//GlobalAlias *alias = new GlobalAlias(f->getType(), GlobalValue::InternalLinkage, name, f, m);
|
||||
// printf(" F %.8X %.8X %.3X (%3d) %s %s\n",
|
||||
// info->value_address, info->thunk_address, info->ordinal,
|
||||
// info->ordinal, implemented ? " " : "!!", name);
|
||||
// For values:
|
||||
// printf(" V %.8X %.3X (%3d) %s %s\n",
|
||||
// info->value_address, info->ordinal, info->ordinal,
|
||||
// implemented ? " " : "!!", name);
|
||||
}
|
||||
|
||||
void ModuleGenerator::AddPresentImport(FunctionSymbol* fn) {
|
||||
Module *m = gen_module_;
|
||||
LLVMContext& context = m->getContext();
|
||||
|
||||
const DataLayout* dl = engine_->getDataLayout();
|
||||
Type* intPtrTy = dl->getIntPtrType(context);
|
||||
Type* int8PtrTy = PointerType::getUnqual(Type::getInt8Ty(context));
|
||||
|
||||
// Add the externs.
|
||||
// We have both the shim function pointer and the shim data pointer.
|
||||
char shim_name[256];
|
||||
xesnprintfa(shim_name, XECOUNT(shim_name),
|
||||
"__shim_%s", fn->kernel_export->name);
|
||||
char shim_data_name[256];
|
||||
xesnprintfa(shim_data_name, XECOUNT(shim_data_name),
|
||||
"__shim_data_%s", fn->kernel_export->name);
|
||||
std::vector<Type*> shimArgs;
|
||||
shimArgs.push_back(int8PtrTy);
|
||||
shimArgs.push_back(int8PtrTy);
|
||||
FunctionType* shimTy = FunctionType::get(
|
||||
Type::getVoidTy(context), shimArgs, false);
|
||||
Function* shim = Function::Create(
|
||||
shimTy, Function::ExternalLinkage, shim_name, m);
|
||||
|
||||
GlobalVariable* gv = new GlobalVariable(
|
||||
*m, int8PtrTy, true, GlobalValue::ExternalLinkage, 0,
|
||||
shim_data_name);
|
||||
|
||||
// TODO(benvanik): don't initialize on startup - move to exec_module
|
||||
gv->setInitializer(ConstantExpr::getIntToPtr(
|
||||
ConstantInt::get(intPtrTy,
|
||||
(uintptr_t)fn->kernel_export->function_data.shim_data),
|
||||
int8PtrTy));
|
||||
engine_->addGlobalMapping(shim,
|
||||
(void*)fn->kernel_export->function_data.shim);
|
||||
|
||||
// Create the function (and setup args/attributes/etc).
|
||||
Function* f = CreateFunctionDefinition(fn->name());
|
||||
|
||||
BasicBlock* block = BasicBlock::Create(context, "entry", f);
|
||||
IRBuilder<> b(block);
|
||||
|
||||
if (FLAGS_trace_kernel_calls) {
|
||||
Value* traceKernelCall = m->getFunction("XeTraceKernelCall");
|
||||
b.CreateCall4(
|
||||
traceKernelCall,
|
||||
f->arg_begin(),
|
||||
b.getInt64(fn->start_address),
|
||||
++f->arg_begin(),
|
||||
b.getInt64((uint64_t)fn->kernel_export));
|
||||
}
|
||||
|
||||
b.CreateCall2(
|
||||
shim,
|
||||
f->arg_begin(),
|
||||
b.CreateLoad(gv));
|
||||
|
||||
b.CreateRetVoid();
|
||||
|
||||
OptimizeFunction(m, f);
|
||||
}
|
||||
|
||||
void ModuleGenerator::PrepareFunction(FunctionSymbol* fn) {
|
||||
// Create the function (and setup args/attributes/etc).
|
||||
Function* f = CreateFunctionDefinition(fn->name());
|
||||
|
||||
// Setup our codegen wrapper to keep all the pointers together.
|
||||
CodegenFunction* cgf = new CodegenFunction();
|
||||
cgf->symbol = fn;
|
||||
cgf->function_type = f->getFunctionType();
|
||||
cgf->function = f;
|
||||
functions_.insert(std::pair<uint32_t, CodegenFunction*>(
|
||||
fn->start_address, cgf));
|
||||
}
|
||||
|
||||
void ModuleGenerator::BuildFunction(CodegenFunction* cgf) {
|
||||
FunctionSymbol* fn = cgf->symbol;
|
||||
|
||||
// Setup the generation context.
|
||||
FunctionGenerator fgen(
|
||||
memory_, sdb_, fn, context_, gen_module_, cgf->function);
|
||||
|
||||
// Run through and generate each basic block.
|
||||
fgen.GenerateBasicBlocks();
|
||||
|
||||
// Run the optimizer on the function.
|
||||
// Doing this here keeps the size of the IR small and speeds up the later
|
||||
// passes.
|
||||
OptimizeFunction(gen_module_, cgf->function);
|
||||
}
|
||||
|
||||
void ModuleGenerator::OptimizeFunction(Module* m, Function* fn) {
|
||||
FunctionPassManager pm(m);
|
||||
//fn->dump();
|
||||
if (FLAGS_optimize_ir_functions) {
|
||||
PassManagerBuilder pmb;
|
||||
pmb.OptLevel = 3;
|
||||
pmb.SizeLevel = 0;
|
||||
pmb.Inliner = createFunctionInliningPass();
|
||||
pmb.Vectorize = true;
|
||||
pmb.LoopVectorize = true;
|
||||
pmb.populateFunctionPassManager(pm);
|
||||
}
|
||||
pm.add(createVerifierPass());
|
||||
pm.run(*fn);
|
||||
}
|
||||
91
src/xenia/cpu/codegen/module_generator.h
Normal file
91
src/xenia/cpu/codegen/module_generator.h
Normal file
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_CPU_CODEGEN_MODULE_GENERATOR_H_
|
||||
#define XENIA_CPU_CODEGEN_MODULE_GENERATOR_H_
|
||||
|
||||
#include <xenia/common.h>
|
||||
#include <xenia/core.h>
|
||||
|
||||
#include <xenia/cpu/sdb.h>
|
||||
#include <xenia/core/memory.h>
|
||||
#include <xenia/kernel/export.h>
|
||||
|
||||
|
||||
namespace llvm {
|
||||
class DIBuilder;
|
||||
class ExecutionEngine;
|
||||
class Function;
|
||||
class FunctionType;
|
||||
class LLVMContext;
|
||||
class Module;
|
||||
class MDNode;
|
||||
}
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace codegen {
|
||||
|
||||
|
||||
class ModuleGenerator {
|
||||
public:
|
||||
ModuleGenerator(
|
||||
xe_memory_ref memory, kernel::ExportResolver* export_resolver,
|
||||
const char* module_name, const char* module_path,
|
||||
sdb::SymbolDatabase* sdb,
|
||||
llvm::LLVMContext* context, llvm::Module* gen_module,
|
||||
llvm::ExecutionEngine* engine);
|
||||
~ModuleGenerator();
|
||||
|
||||
int Generate();
|
||||
|
||||
void AddFunctionsToMap(
|
||||
std::tr1::unordered_map<uint32_t, llvm::Function*>& map);
|
||||
|
||||
private:
|
||||
class CodegenFunction {
|
||||
public:
|
||||
sdb::FunctionSymbol* symbol;
|
||||
llvm::FunctionType* function_type;
|
||||
llvm::Function* function;
|
||||
};
|
||||
|
||||
CodegenFunction* GetCodegenFunction(uint32_t address);
|
||||
|
||||
void AddImports();
|
||||
llvm::Function* CreateFunctionDefinition(const char* name);
|
||||
void AddMissingImport(sdb::FunctionSymbol* fn);
|
||||
void AddPresentImport(sdb::FunctionSymbol* fn);
|
||||
void PrepareFunction(sdb::FunctionSymbol* fn);
|
||||
void BuildFunction(CodegenFunction* cgf);
|
||||
void OptimizeFunction(llvm::Module* m, llvm::Function* fn);
|
||||
|
||||
xe_memory_ref memory_;
|
||||
kernel::ExportResolver* export_resolver_;
|
||||
char* module_name_;
|
||||
char* module_path_;
|
||||
sdb::SymbolDatabase* sdb_;
|
||||
|
||||
llvm::LLVMContext* context_;
|
||||
llvm::Module* gen_module_;
|
||||
llvm::ExecutionEngine* engine_;
|
||||
llvm::DIBuilder* di_builder_;
|
||||
llvm::MDNode* cu_;
|
||||
|
||||
std::map<uint32_t, CodegenFunction*> functions_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace codegen
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_CODEGEN_MODULE_GENERATOR_H_
|
||||
14
src/xenia/cpu/codegen/sources.gypi
Normal file
14
src/xenia/cpu/codegen/sources.gypi
Normal file
@@ -0,0 +1,14 @@
|
||||
# Copyright 2013 Ben Vanik. All Rights Reserved.
|
||||
{
|
||||
'sources': [
|
||||
'emit.h',
|
||||
'emit_alu.cc',
|
||||
'emit_control.cc',
|
||||
'emit_fpu.cc',
|
||||
'emit_memory.cc',
|
||||
'function_generator.cc',
|
||||
'function_generator.h',
|
||||
'module_generator.cc',
|
||||
'module_generator.h',
|
||||
],
|
||||
}
|
||||
30
src/xenia/cpu/cpu-private.h
Normal file
30
src/xenia/cpu/cpu-private.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_CPU_PRIVATE_H_
|
||||
#define XENIA_CPU_PRIVATE_H_
|
||||
|
||||
#include <gflags/gflags.h>
|
||||
|
||||
|
||||
DECLARE_bool(trace_instructions);
|
||||
DECLARE_bool(trace_user_calls);
|
||||
DECLARE_bool(trace_kernel_calls);
|
||||
|
||||
DECLARE_string(load_module_map);
|
||||
|
||||
DECLARE_string(dump_path);
|
||||
DECLARE_bool(dump_module_bitcode);
|
||||
DECLARE_bool(dump_module_map);
|
||||
|
||||
DECLARE_bool(optimize_ir_modules);
|
||||
DECLARE_bool(optimize_ir_functions);
|
||||
|
||||
|
||||
#endif // XENIA_CPU_PRIVATE_H_
|
||||
41
src/xenia/cpu/cpu.cc
Normal file
41
src/xenia/cpu/cpu.cc
Normal file
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <xenia/cpu/cpu-private.h>
|
||||
|
||||
|
||||
// Tracing:
|
||||
DEFINE_bool(trace_instructions, false,
|
||||
"Trace all instructions.");
|
||||
DEFINE_bool(trace_user_calls, false,
|
||||
"Trace all user function calls.");
|
||||
DEFINE_bool(trace_kernel_calls, false,
|
||||
"Trace all kernel function calls.");
|
||||
|
||||
|
||||
// Debugging:
|
||||
DEFINE_string(load_module_map, "",
|
||||
"Loads a .map for symbol names and to diff with the generated symbol "
|
||||
"database.");
|
||||
|
||||
|
||||
// Dumping:
|
||||
DEFINE_string(dump_path, "build/",
|
||||
"Directory that dump files are placed into.");
|
||||
DEFINE_bool(dump_module_bitcode, true,
|
||||
"Writes the module bitcode both before and after optimizations.");
|
||||
DEFINE_bool(dump_module_map, true,
|
||||
"Dumps the module symbol database.");
|
||||
|
||||
|
||||
// Optimizations:
|
||||
DEFINE_bool(optimize_ir_modules, true,
|
||||
"Whether to run LLVM optimizations on modules.");
|
||||
DEFINE_bool(optimize_ir_functions, true,
|
||||
"Whether to run LLVM optimizations on functions.");
|
||||
15
src/xenia/cpu/cpu.h
Normal file
15
src/xenia/cpu/cpu.h
Normal file
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_CPU_CPU_H_
|
||||
#define XENIA_CPU_CPU_H_
|
||||
|
||||
#include <xenia/cpu/processor.h>
|
||||
|
||||
#endif // XENIA_CPU_CPU_H_
|
||||
326
src/xenia/cpu/exec_module.cc
Normal file
326
src/xenia/cpu/exec_module.cc
Normal file
@@ -0,0 +1,326 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <xenia/cpu/exec_module.h>
|
||||
|
||||
#include <llvm/Linker.h>
|
||||
#include <llvm/PassManager.h>
|
||||
#include <llvm/Analysis/Verifier.h>
|
||||
#include <llvm/Bitcode/ReaderWriter.h>
|
||||
#include <llvm/ExecutionEngine/GenericValue.h>
|
||||
#include <llvm/ExecutionEngine/ExecutionEngine.h>
|
||||
#include <llvm/IR/Constants.h>
|
||||
#include <llvm/IR/DataLayout.h>
|
||||
#include <llvm/IR/DerivedTypes.h>
|
||||
#include <llvm/IR/LLVMContext.h>
|
||||
#include <llvm/IR/Module.h>
|
||||
#include <llvm/Support/Host.h>
|
||||
#include <llvm/Support/MemoryBuffer.h>
|
||||
#include <llvm/Support/raw_ostream.h>
|
||||
#include <llvm/Support/system_error.h>
|
||||
#include <llvm/Support/Threading.h>
|
||||
#include <llvm/Transforms/IPO.h>
|
||||
#include <llvm/Transforms/IPO/PassManagerBuilder.h>
|
||||
|
||||
#include <xenia/cpu/cpu-private.h>
|
||||
#include <xenia/cpu/llvm_exports.h>
|
||||
#include <xenia/cpu/sdb.h>
|
||||
#include <xenia/cpu/codegen/module_generator.h>
|
||||
#include <xenia/cpu/ppc/instr.h>
|
||||
#include <xenia/cpu/ppc/state.h>
|
||||
#include <xenia/cpu/xethunk/xethunk.h>
|
||||
|
||||
|
||||
using namespace llvm;
|
||||
using namespace xe;
|
||||
using namespace xe::cpu;
|
||||
using namespace xe::cpu::codegen;
|
||||
using namespace xe::cpu::sdb;
|
||||
using namespace xe::kernel;
|
||||
|
||||
|
||||
ExecModule::ExecModule(
|
||||
xe_memory_ref memory, shared_ptr<ExportResolver> export_resolver,
|
||||
const char* module_name, const char* module_path,
|
||||
shared_ptr<llvm::ExecutionEngine>& engine) {
|
||||
memory_ = xe_memory_retain(memory);
|
||||
export_resolver_ = export_resolver;
|
||||
module_name_ = xestrdupa(module_name);
|
||||
module_path_ = xestrdupa(module_path);
|
||||
engine_ = engine;
|
||||
|
||||
context_ = shared_ptr<LLVMContext>(new LLVMContext());
|
||||
}
|
||||
|
||||
ExecModule::~ExecModule() {
|
||||
if (gen_module_) {
|
||||
Uninit();
|
||||
engine_->removeModule(gen_module_.get());
|
||||
}
|
||||
|
||||
xe_free(module_path_);
|
||||
xe_free(module_name_);
|
||||
xe_memory_release(memory_);
|
||||
}
|
||||
|
||||
int ExecModule::PrepareXex(xe_xex2_ref xex) {
|
||||
sdb_ = shared_ptr<sdb::SymbolDatabase>(
|
||||
new sdb::XexSymbolDatabase(memory_, export_resolver_.get(), xex));
|
||||
|
||||
int result_code = Prepare();
|
||||
if (result_code) {
|
||||
return result_code;
|
||||
}
|
||||
|
||||
// Import variables.
|
||||
// TODO??
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ExecModule::PrepareRawBinary(uint32_t start_address, uint32_t end_address) {
|
||||
sdb_ = shared_ptr<sdb::SymbolDatabase>(
|
||||
new sdb::RawSymbolDatabase(memory_, export_resolver_.get(),
|
||||
start_address, end_address));
|
||||
|
||||
return Prepare();
|
||||
}
|
||||
|
||||
int ExecModule::Prepare() {
|
||||
int result_code = 1;
|
||||
std::string error_message;
|
||||
|
||||
char file_name[XE_MAX_PATH];
|
||||
|
||||
OwningPtr<MemoryBuffer> shared_module_buffer;
|
||||
auto_ptr<Module> shared_module;
|
||||
auto_ptr<raw_ostream> outs;
|
||||
|
||||
PassManager pm;
|
||||
PassManagerBuilder pmb;
|
||||
|
||||
// TODO(benvanik): embed the bc file into the emulator.
|
||||
const char *thunk_path = "src/xenia/cpu/xethunk/xethunk.bc";
|
||||
|
||||
// Calculate a cache path based on the module, the CPU version, and other
|
||||
// bits.
|
||||
// TODO(benvanik): cache path calculation.
|
||||
//const char *cache_path = "build/generated.bc";
|
||||
|
||||
// Check the cache to see if the bitcode exists.
|
||||
// If it does, load that module directly. In the future we could also cache
|
||||
// on linked binaries but that requires more safety around versioning.
|
||||
// TODO(benvanik): check cache for module bitcode and load.
|
||||
// if (path_exists(cache_key)) {
|
||||
// exec_module = load_bitcode(cache_key);
|
||||
// sdb = load_symbol_table(cache_key);
|
||||
// }
|
||||
|
||||
// If not found in cache, generate a new module.
|
||||
if (!gen_module_.get()) {
|
||||
// Load shared bitcode files.
|
||||
// These contain globals and common thunk code that are used by the
|
||||
// generated code.
|
||||
XEEXPECTZERO(MemoryBuffer::getFile(thunk_path, shared_module_buffer));
|
||||
shared_module = auto_ptr<Module>(ParseBitcodeFile(
|
||||
&*shared_module_buffer, *context_, &error_message));
|
||||
XEEXPECTNOTNULL(shared_module.get());
|
||||
|
||||
// Analyze the module and add its symbols to the symbol database.
|
||||
XEEXPECTZERO(sdb_->Analyze());
|
||||
|
||||
// Load a specified module map and diff.
|
||||
if (FLAGS_load_module_map.size()) {
|
||||
sdb_->ReadMap(FLAGS_load_module_map.c_str());
|
||||
}
|
||||
|
||||
// Dump the symbol database.
|
||||
if (FLAGS_dump_module_map) {
|
||||
xesnprintfa(file_name, XECOUNT(file_name),
|
||||
"%s%s.map", FLAGS_dump_path.c_str(), module_name_);
|
||||
sdb_->WriteMap(file_name);
|
||||
}
|
||||
|
||||
// Initialize the module.
|
||||
gen_module_ = shared_ptr<Module>(
|
||||
new Module(module_name_, *context_.get()));
|
||||
// TODO(benavnik): addModuleFlag?
|
||||
|
||||
// Inject globals.
|
||||
// This should be done ASAP to ensure that JITed functions can use the
|
||||
// constant addresses.
|
||||
XEEXPECTZERO(InjectGlobals());
|
||||
|
||||
// Link shared module into generated module.
|
||||
// This gives us a single module that we can optimize and prevents the need
|
||||
// for foreward declarations.
|
||||
Linker::LinkModules(gen_module_.get(), shared_module.get(), 0,
|
||||
&error_message);
|
||||
|
||||
// Build the module from the source code.
|
||||
codegen_ = auto_ptr<ModuleGenerator>(new ModuleGenerator(
|
||||
memory_, export_resolver_.get(), module_name_, module_path_,
|
||||
sdb_.get(), context_.get(), gen_module_.get(),
|
||||
engine_.get()));
|
||||
XEEXPECTZERO(codegen_->Generate());
|
||||
|
||||
// Write to cache.
|
||||
// TODO(benvanik): cache stuff
|
||||
|
||||
// Dump pre-optimized module to disk.
|
||||
if (FLAGS_dump_module_bitcode) {
|
||||
xesnprintfa(file_name, XECOUNT(file_name),
|
||||
"%s%s-preopt.bc", FLAGS_dump_path.c_str(), module_name_);
|
||||
outs = auto_ptr<raw_ostream>(new raw_fd_ostream(
|
||||
file_name, error_message, raw_fd_ostream::F_Binary));
|
||||
XEEXPECTTRUE(error_message.empty());
|
||||
WriteBitcodeToFile(gen_module_.get(), *outs);
|
||||
}
|
||||
}
|
||||
|
||||
// Link optimizations.
|
||||
XEEXPECTZERO(gen_module_->MaterializeAllPermanently(&error_message));
|
||||
|
||||
// Reset target triple (ignore what's in xethunk).
|
||||
gen_module_->setTargetTriple(llvm::sys::getDefaultTargetTriple());
|
||||
|
||||
// Run full module optimizations.
|
||||
pm.add(new DataLayout(gen_module_.get()));
|
||||
if (FLAGS_optimize_ir_modules) {
|
||||
pm.add(createVerifierPass());
|
||||
pmb.OptLevel = 3;
|
||||
pmb.SizeLevel = 0;
|
||||
pmb.Inliner = createFunctionInliningPass();
|
||||
pmb.Vectorize = true;
|
||||
pmb.LoopVectorize = true;
|
||||
pmb.populateModulePassManager(pm);
|
||||
pmb.populateLTOPassManager(pm, false, true);
|
||||
}
|
||||
pm.add(createVerifierPass());
|
||||
pm.run(*gen_module_);
|
||||
|
||||
// Dump post-optimized module to disk.
|
||||
if (FLAGS_optimize_ir_modules && FLAGS_dump_module_bitcode) {
|
||||
xesnprintfa(file_name, XECOUNT(file_name),
|
||||
"%s%s.bc", FLAGS_dump_path.c_str(), module_name_);
|
||||
outs = auto_ptr<raw_ostream>(new raw_fd_ostream(
|
||||
file_name, error_message, raw_fd_ostream::F_Binary));
|
||||
XEEXPECTTRUE(error_message.empty());
|
||||
WriteBitcodeToFile(gen_module_.get(), *outs);
|
||||
}
|
||||
|
||||
// TODO(benvanik): experiment with LLD to see if we can write out a dll.
|
||||
|
||||
// Initialize the module.
|
||||
XEEXPECTZERO(Init());
|
||||
|
||||
// Force JIT of all functions.
|
||||
// for (Module::iterator it = gen_module_->begin(); it != gen_module_->end();
|
||||
// ++it) {
|
||||
// Function* fn = it;
|
||||
// if (!fn->isDeclaration()) {
|
||||
// engine_->getPointerToFunction(fn);
|
||||
// }
|
||||
// }
|
||||
|
||||
result_code = 0;
|
||||
XECLEANUP:
|
||||
return result_code;
|
||||
}
|
||||
|
||||
void ExecModule::AddFunctionsToMap(FunctionMap& map) {
|
||||
codegen_->AddFunctionsToMap(map);
|
||||
}
|
||||
|
||||
int ExecModule::InjectGlobals() {
|
||||
LLVMContext& context = *context_.get();
|
||||
const DataLayout* dl = engine_->getDataLayout();
|
||||
Type* intPtrTy = dl->getIntPtrType(context);
|
||||
Type* int8PtrTy = PointerType::getUnqual(Type::getInt8Ty(context));
|
||||
GlobalVariable* gv;
|
||||
|
||||
// xe_memory_base
|
||||
// This is the base void* pointer to the memory space.
|
||||
gv = new GlobalVariable(
|
||||
*gen_module_,
|
||||
int8PtrTy,
|
||||
true,
|
||||
GlobalValue::ExternalLinkage,
|
||||
0,
|
||||
"xe_memory_base");
|
||||
// Align to 64b - this makes SSE faster.
|
||||
gv->setAlignment(64);
|
||||
gv->setInitializer(ConstantExpr::getIntToPtr(
|
||||
ConstantInt::get(intPtrTy, (uintptr_t)xe_memory_addr(memory_, 0)),
|
||||
int8PtrTy));
|
||||
|
||||
SetupLlvmExports(gen_module_.get(), dl, engine_.get());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ExecModule::Init() {
|
||||
// Setup all kernel variables.
|
||||
std::vector<VariableSymbol*> variables;
|
||||
if (sdb_->GetAllVariables(variables)) {
|
||||
return 1;
|
||||
}
|
||||
uint8_t* mem = xe_memory_addr(memory_, 0);
|
||||
for (std::vector<VariableSymbol*>::iterator it = variables.begin();
|
||||
it != variables.end(); ++it) {
|
||||
VariableSymbol* var = *it;
|
||||
if (!var->kernel_export) {
|
||||
continue;
|
||||
}
|
||||
KernelExport* kernel_export = var->kernel_export;
|
||||
|
||||
// Grab, if available.
|
||||
uint32_t* slot = (uint32_t*)(mem + var->address);
|
||||
if (kernel_export->type == KernelExport::Function) {
|
||||
// Not exactly sure what this should be...
|
||||
// TODO(benvanik): find out what import variables are.
|
||||
} else {
|
||||
if (kernel_export->is_implemented) {
|
||||
// Implemented - replace with pointer.
|
||||
*slot = XESWAP32BE(kernel_export->variable_ptr);
|
||||
} else {
|
||||
// Not implemented - write with a dummy value.
|
||||
*slot = XESWAP32BE(0xDEADBEEF);
|
||||
XELOGCPU(XT("WARNING: imported a variable with no value: %s"),
|
||||
kernel_export->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Run static initializers. I'm not sure we'll have any, but who knows.
|
||||
engine_->runStaticConstructorsDestructors(gen_module_.get(), false);
|
||||
|
||||
// Grab the init function and call it.
|
||||
Function* xe_module_init = gen_module_->getFunction("xe_module_init");
|
||||
std::vector<GenericValue> args;
|
||||
GenericValue ret = engine_->runFunction(xe_module_init, args);
|
||||
|
||||
return static_cast<int>(ret.IntVal.getSExtValue());
|
||||
}
|
||||
|
||||
int ExecModule::Uninit() {
|
||||
// Grab function and call it.
|
||||
Function* xe_module_uninit = gen_module_->getFunction("xe_module_uninit");
|
||||
std::vector<GenericValue> args;
|
||||
engine_->runFunction(xe_module_uninit, args);
|
||||
|
||||
// Run static destructors.
|
||||
engine_->runStaticConstructorsDestructors(gen_module_.get(), true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ExecModule::Dump() {
|
||||
sdb_->Dump(stdout);
|
||||
}
|
||||
83
src/xenia/cpu/exec_module.h
Normal file
83
src/xenia/cpu/exec_module.h
Normal file
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_CPU_USERMODULE_H_
|
||||
#define XENIA_CPU_USERMODULE_H_
|
||||
|
||||
#include <xenia/common.h>
|
||||
#include <xenia/core.h>
|
||||
|
||||
#include <xenia/cpu/sdb.h>
|
||||
#include <xenia/kernel/export.h>
|
||||
#include <xenia/kernel/xex2.h>
|
||||
|
||||
|
||||
namespace llvm {
|
||||
class ExecutionEngine;
|
||||
class Function;
|
||||
class LLVMContext;
|
||||
class Module;
|
||||
}
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace codegen {
|
||||
class ModuleGenerator;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
|
||||
|
||||
typedef std::tr1::unordered_map<uint32_t, llvm::Function*> FunctionMap;
|
||||
|
||||
|
||||
class ExecModule {
|
||||
public:
|
||||
ExecModule(
|
||||
xe_memory_ref memory, shared_ptr<kernel::ExportResolver> export_resolver,
|
||||
const char* module_name, const char* module_path,
|
||||
shared_ptr<llvm::ExecutionEngine>& engine);
|
||||
~ExecModule();
|
||||
|
||||
int PrepareXex(xe_xex2_ref xex);
|
||||
int PrepareRawBinary(uint32_t start_address, uint32_t end_address);
|
||||
|
||||
void AddFunctionsToMap(FunctionMap& map);
|
||||
|
||||
void Dump();
|
||||
|
||||
private:
|
||||
int Prepare();
|
||||
int InjectGlobals();
|
||||
int Init();
|
||||
int Uninit();
|
||||
|
||||
xe_memory_ref memory_;
|
||||
shared_ptr<kernel::ExportResolver> export_resolver_;
|
||||
char* module_name_;
|
||||
char* module_path_;
|
||||
shared_ptr<llvm::ExecutionEngine> engine_;
|
||||
shared_ptr<sdb::SymbolDatabase> sdb_;
|
||||
shared_ptr<llvm::LLVMContext> context_;
|
||||
shared_ptr<llvm::Module> gen_module_;
|
||||
auto_ptr<codegen::ModuleGenerator> codegen_;
|
||||
|
||||
FunctionMap fns_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_USERMODULE_H_
|
||||
176
src/xenia/cpu/llvm_exports.cc
Normal file
176
src/xenia/cpu/llvm_exports.cc
Normal file
@@ -0,0 +1,176 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <xenia/cpu/llvm_exports.h>
|
||||
|
||||
#include <llvm/ExecutionEngine/ExecutionEngine.h>
|
||||
#include <llvm/IR/Constants.h>
|
||||
#include <llvm/IR/DataLayout.h>
|
||||
#include <llvm/IR/DerivedTypes.h>
|
||||
#include <llvm/IR/LLVMContext.h>
|
||||
#include <llvm/IR/Module.h>
|
||||
|
||||
#include <xenia/cpu/sdb.h>
|
||||
#include <xenia/cpu/ppc/instr.h>
|
||||
#include <xenia/cpu/ppc/state.h>
|
||||
#include <xenia/kernel/export.h>
|
||||
|
||||
|
||||
using namespace llvm;
|
||||
using namespace xe;
|
||||
using namespace xe::cpu;
|
||||
using namespace xe::cpu::sdb;
|
||||
using namespace xe::kernel;
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
|
||||
void XeTrap(xe_ppc_state_t* state, uint32_t cia) {
|
||||
XELOGE(XT("TRAP"));
|
||||
XEASSERTALWAYS();
|
||||
}
|
||||
|
||||
void XeIndirectBranch(xe_ppc_state_t* state, uint64_t target, uint64_t br_ia) {
|
||||
XELOGCPU(XT("INDIRECT BRANCH %.8X -> %.8X"),
|
||||
(uint32_t)br_ia, (uint32_t)target);
|
||||
XEASSERTALWAYS();
|
||||
}
|
||||
|
||||
void XeInvalidInstruction(xe_ppc_state_t* state, uint32_t cia, uint32_t data) {
|
||||
ppc::InstrData i;
|
||||
i.address = cia;
|
||||
i.code = data;
|
||||
i.type = ppc::GetInstrType(i.code);
|
||||
|
||||
if (!i.type) {
|
||||
XELOGCPU(XT("INVALID INSTRUCTION %.8X: %.8X ???"),
|
||||
i.address, i.code);
|
||||
} else if (i.type->disassemble) {
|
||||
ppc::InstrDisasm d;
|
||||
i.type->disassemble(i, d);
|
||||
std::string disasm;
|
||||
d.Dump(disasm);
|
||||
XELOGCPU(XT("INVALID INSTRUCTION %.8X: %.8X %s"),
|
||||
i.address, i.code, disasm.c_str());
|
||||
} else {
|
||||
XELOGCPU(XT("INVALID INSTRUCTION %.8X: %.8X %s"),
|
||||
i.address, i.code, i.type->name);
|
||||
}
|
||||
}
|
||||
|
||||
void XeAccessViolation(xe_ppc_state_t* state, uint32_t cia, uint64_t ea) {
|
||||
XELOGE(XT("INVALID ACCESS %.8X: tried to touch %.8X"),
|
||||
cia, (uint32_t)ea);
|
||||
XEASSERTALWAYS();
|
||||
}
|
||||
|
||||
void XeTraceKernelCall(xe_ppc_state_t* state, uint64_t cia, uint64_t call_ia,
|
||||
KernelExport* kernel_export) {
|
||||
XELOGCPU(XT("TRACE: %.8X -> k.%.8X (%s)"),
|
||||
(uint32_t)call_ia - 4, (uint32_t)cia,
|
||||
kernel_export ? kernel_export->name : "unknown");
|
||||
}
|
||||
|
||||
void XeTraceUserCall(xe_ppc_state_t* state, uint64_t cia, uint64_t call_ia,
|
||||
FunctionSymbol* fn) {
|
||||
XELOGCPU(XT("TRACE: %.8X -> u.%.8X (%s)"),
|
||||
(uint32_t)call_ia - 4, (uint32_t)cia, fn->name());
|
||||
}
|
||||
|
||||
void XeTraceInstruction(xe_ppc_state_t* state, uint32_t cia, uint32_t data) {
|
||||
ppc::InstrType* type = ppc::GetInstrType(data);
|
||||
XELOGCPU(XT("TRACE: %.8X %.8X %s %s"),
|
||||
cia, data,
|
||||
type && type->emit ? " " : "X",
|
||||
type ? type->name : "<unknown>");
|
||||
|
||||
if (cia == 0x82014468) {
|
||||
printf("BREAKBREAKBREAK\n");
|
||||
}
|
||||
|
||||
// TODO(benvanik): better disassembly, printing of current register values/etc
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
void xe::cpu::SetupLlvmExports(llvm::Module* module,
|
||||
const llvm::DataLayout* dl,
|
||||
llvm::ExecutionEngine* engine) {
|
||||
LLVMContext& context = module->getContext();
|
||||
Type* int8PtrTy = PointerType::getUnqual(Type::getInt8Ty(context));
|
||||
|
||||
// Control methods:
|
||||
std::vector<Type*> trapArgs;
|
||||
trapArgs.push_back(int8PtrTy);
|
||||
trapArgs.push_back(Type::getInt32Ty(context));
|
||||
FunctionType* trapTy = FunctionType::get(
|
||||
Type::getVoidTy(context), trapArgs, false);
|
||||
engine->addGlobalMapping(Function::Create(
|
||||
trapTy, Function::ExternalLinkage, "XeTrap",
|
||||
module), (void*)&XeTrap);
|
||||
|
||||
std::vector<Type*> indirectBranchArgs;
|
||||
indirectBranchArgs.push_back(int8PtrTy);
|
||||
indirectBranchArgs.push_back(Type::getInt64Ty(context));
|
||||
indirectBranchArgs.push_back(Type::getInt64Ty(context));
|
||||
FunctionType* indirectBranchTy = FunctionType::get(
|
||||
Type::getVoidTy(context), indirectBranchArgs, false);
|
||||
engine->addGlobalMapping(Function::Create(
|
||||
indirectBranchTy, Function::ExternalLinkage, "XeIndirectBranch",
|
||||
module), (void*)&XeIndirectBranch);
|
||||
|
||||
// Debugging methods:
|
||||
std::vector<Type*> invalidInstructionArgs;
|
||||
invalidInstructionArgs.push_back(int8PtrTy);
|
||||
invalidInstructionArgs.push_back(Type::getInt32Ty(context));
|
||||
invalidInstructionArgs.push_back(Type::getInt32Ty(context));
|
||||
FunctionType* invalidInstructionTy = FunctionType::get(
|
||||
Type::getVoidTy(context), invalidInstructionArgs, false);
|
||||
engine->addGlobalMapping(Function::Create(
|
||||
invalidInstructionTy, Function::ExternalLinkage, "XeInvalidInstruction",
|
||||
module), (void*)&XeInvalidInstruction);
|
||||
|
||||
std::vector<Type*> accessViolationArgs;
|
||||
accessViolationArgs.push_back(int8PtrTy);
|
||||
accessViolationArgs.push_back(Type::getInt32Ty(context));
|
||||
accessViolationArgs.push_back(Type::getInt64Ty(context));
|
||||
FunctionType* accessViolationTy = FunctionType::get(
|
||||
Type::getVoidTy(context), accessViolationArgs, false);
|
||||
engine->addGlobalMapping(Function::Create(
|
||||
accessViolationTy, Function::ExternalLinkage, "XeAccessViolation",
|
||||
module), (void*)&XeAccessViolation);
|
||||
|
||||
// Tracing methods:
|
||||
std::vector<Type*> traceCallArgs;
|
||||
traceCallArgs.push_back(int8PtrTy);
|
||||
traceCallArgs.push_back(Type::getInt64Ty(context));
|
||||
traceCallArgs.push_back(Type::getInt64Ty(context));
|
||||
traceCallArgs.push_back(Type::getInt64Ty(context));
|
||||
FunctionType* traceCallTy = FunctionType::get(
|
||||
Type::getVoidTy(context), traceCallArgs, false);
|
||||
std::vector<Type*> traceInstructionArgs;
|
||||
traceInstructionArgs.push_back(int8PtrTy);
|
||||
traceInstructionArgs.push_back(Type::getInt32Ty(context));
|
||||
traceInstructionArgs.push_back(Type::getInt32Ty(context));
|
||||
FunctionType* traceInstructionTy = FunctionType::get(
|
||||
Type::getVoidTy(context), traceInstructionArgs, false);
|
||||
|
||||
engine->addGlobalMapping(Function::Create(
|
||||
traceCallTy, Function::ExternalLinkage, "XeTraceKernelCall",
|
||||
module), (void*)&XeTraceKernelCall);
|
||||
engine->addGlobalMapping(Function::Create(
|
||||
traceCallTy, Function::ExternalLinkage, "XeTraceUserCall",
|
||||
module), (void*)&XeTraceUserCall);
|
||||
engine->addGlobalMapping(Function::Create(
|
||||
traceInstructionTy, Function::ExternalLinkage, "XeTraceInstruction",
|
||||
module), (void*)&XeTraceInstruction);
|
||||
}
|
||||
38
src/xenia/cpu/llvm_exports.h
Normal file
38
src/xenia/cpu/llvm_exports.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_CPU_LLVM_EXPORTS_H_
|
||||
#define XENIA_CPU_LLVM_EXPORTS_H_
|
||||
|
||||
#include <xenia/common.h>
|
||||
#include <xenia/core.h>
|
||||
|
||||
|
||||
namespace llvm {
|
||||
class ExecutionEngine;
|
||||
class LLVMContext;
|
||||
class Module;
|
||||
class DataLayout;
|
||||
}
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
|
||||
|
||||
void SetupLlvmExports(llvm::Module* module,
|
||||
const llvm::DataLayout* dl,
|
||||
llvm::ExecutionEngine* engine);
|
||||
|
||||
|
||||
} // cpu
|
||||
} // xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_LLVM_EXPORTS_H_
|
||||
18
src/xenia/cpu/ppc.h
Normal file
18
src/xenia/cpu/ppc.h
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_CPU_PPC_H_
|
||||
#define XENIA_CPU_PPC_H_
|
||||
|
||||
#include <xenia/common.h>
|
||||
|
||||
#include <xenia/cpu/ppc/instr.h>
|
||||
#include <xenia/cpu/ppc/state.h>
|
||||
|
||||
#endif // XENIA_CPU_PPC_H_
|
||||
407
src/xenia/cpu/ppc/instr.cc
Normal file
407
src/xenia/cpu/ppc/instr.cc
Normal file
@@ -0,0 +1,407 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <xenia/cpu/ppc/instr.h>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include <xenia/cpu/ppc/instr_tables.h>
|
||||
|
||||
|
||||
using namespace xe::cpu::ppc;
|
||||
|
||||
|
||||
void InstrAccessBits::Clear() {
|
||||
spr = cr = gpr = fpr = 0;
|
||||
}
|
||||
|
||||
void InstrAccessBits::Extend(InstrAccessBits& other) {
|
||||
spr |= other.spr;
|
||||
cr |= other.cr;
|
||||
gpr |= other.gpr;
|
||||
fpr |= other.fpr;
|
||||
}
|
||||
|
||||
void InstrAccessBits::MarkAccess(InstrRegister& reg) {
|
||||
uint64_t bits = 0;
|
||||
if (reg.access & InstrRegister::kRead) {
|
||||
bits |= 0x1;
|
||||
}
|
||||
if (reg.access & InstrRegister::kWrite) {
|
||||
bits |= 0x2;
|
||||
}
|
||||
|
||||
switch (reg.set) {
|
||||
case InstrRegister::kXER:
|
||||
spr |= bits << (2 * 0);
|
||||
break;
|
||||
case InstrRegister::kLR:
|
||||
spr |= bits << (2 * 1);
|
||||
break;
|
||||
case InstrRegister::kCTR:
|
||||
spr |= bits << (2 * 2);
|
||||
break;
|
||||
case InstrRegister::kCR:
|
||||
cr |= bits << (2 * reg.ordinal);
|
||||
break;
|
||||
case InstrRegister::kFPSCR:
|
||||
spr |= bits << (2 * 3);
|
||||
break;
|
||||
case InstrRegister::kGPR:
|
||||
gpr |= bits << (2 * reg.ordinal);
|
||||
break;
|
||||
case InstrRegister::kFPR:
|
||||
fpr |= bits << (2 * reg.ordinal);
|
||||
break;
|
||||
default:
|
||||
case InstrRegister::kVMX:
|
||||
XEASSERTALWAYS();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void InstrAccessBits::Dump(std::string& out_str) {
|
||||
std::stringstream str;
|
||||
if (spr) {
|
||||
uint64_t spr_t = spr;
|
||||
if (spr_t & 0x3) {
|
||||
str << "XER [";
|
||||
str << ((spr_t & 1) ? "R" : " ");
|
||||
str << ((spr_t & 2) ? "W" : " ");
|
||||
str << "] ";
|
||||
}
|
||||
spr_t >>= 2;
|
||||
if (spr_t & 0x3) {
|
||||
str << "LR [";
|
||||
str << ((spr_t & 1) ? "R" : " ");
|
||||
str << ((spr_t & 2) ? "W" : " ");
|
||||
str << "] ";
|
||||
}
|
||||
spr_t >>= 2;
|
||||
if (spr_t & 0x3) {
|
||||
str << "CTR [";
|
||||
str << ((spr_t & 1) ? "R" : " ");
|
||||
str << ((spr_t & 2) ? "W" : " ");
|
||||
str << "] ";
|
||||
}
|
||||
spr_t >>= 2;
|
||||
if (spr_t & 0x3) {
|
||||
str << "FPCSR [";
|
||||
str << ((spr_t & 1) ? "R" : " ");
|
||||
str << ((spr_t & 2) ? "W" : " ");
|
||||
str << "] ";
|
||||
}
|
||||
spr_t >>= 2;
|
||||
}
|
||||
|
||||
if (cr) {
|
||||
uint64_t cr_t = cr;
|
||||
for (size_t n = 0; n < 8; n++) {
|
||||
if (cr_t & 0x3) {
|
||||
str << "cr" << n << " [";
|
||||
str << ((cr_t & 1) ? "R" : " ");
|
||||
str << ((cr_t & 2) ? "W" : " ");
|
||||
str << "] ";
|
||||
}
|
||||
cr_t >>= 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (gpr) {
|
||||
uint64_t gpr_t = gpr;
|
||||
for (size_t n = 0; n < 32; n++) {
|
||||
if (gpr_t & 0x3) {
|
||||
str << "r" << n << " [";
|
||||
str << ((gpr_t & 1) ? "R" : " ");
|
||||
str << ((gpr_t & 2) ? "W" : " ");
|
||||
str << "] ";
|
||||
}
|
||||
gpr_t >>= 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (fpr) {
|
||||
uint64_t fpr_t = fpr;
|
||||
for (size_t n = 0; n < 32; n++) {
|
||||
if (fpr_t & 0x3) {
|
||||
str << "f" << n << " [";
|
||||
str << ((fpr_t & 1) ? "R" : " ");
|
||||
str << ((fpr_t & 2) ? "W" : " ");
|
||||
str << "] ";
|
||||
}
|
||||
fpr_t >>= 2;
|
||||
}
|
||||
}
|
||||
|
||||
out_str = str.str();
|
||||
}
|
||||
|
||||
|
||||
void InstrDisasm::Init(std::string name, std::string info, uint32_t flags) {
|
||||
operands.clear();
|
||||
special_registers.clear();
|
||||
access_bits.Clear();
|
||||
|
||||
if (flags & InstrDisasm::kOE) {
|
||||
name += "o";
|
||||
InstrRegister i = {
|
||||
InstrRegister::kXER, 0, InstrRegister::kReadWrite
|
||||
};
|
||||
special_registers.push_back(i);
|
||||
}
|
||||
if (flags & InstrDisasm::kRc) {
|
||||
name += ".";
|
||||
InstrRegister i = {
|
||||
InstrRegister::kCR, 0, InstrRegister::kWrite
|
||||
};
|
||||
special_registers.push_back(i);
|
||||
}
|
||||
if (flags & InstrDisasm::kCA) {
|
||||
InstrRegister i = {
|
||||
InstrRegister::kXER, 0, InstrRegister::kReadWrite
|
||||
};
|
||||
special_registers.push_back(i);
|
||||
}
|
||||
if (flags & InstrDisasm::kLR) {
|
||||
name += "l";
|
||||
InstrRegister i = {
|
||||
InstrRegister::kLR, 0, InstrRegister::kWrite
|
||||
};
|
||||
special_registers.push_back(i);
|
||||
}
|
||||
|
||||
XEIGNORE(xestrcpya(this->name, XECOUNT(this->name), name.c_str()));
|
||||
|
||||
XEIGNORE(xestrcpya(this->info, XECOUNT(this->info), info.c_str()));
|
||||
}
|
||||
|
||||
void InstrDisasm::AddLR(InstrRegister::Access access) {
|
||||
InstrRegister i = {
|
||||
InstrRegister::kLR, 0, access
|
||||
};
|
||||
special_registers.push_back(i);
|
||||
}
|
||||
|
||||
void InstrDisasm::AddCTR(InstrRegister::Access access) {
|
||||
InstrRegister i = {
|
||||
InstrRegister::kCTR, 0, access
|
||||
};
|
||||
special_registers.push_back(i);
|
||||
}
|
||||
|
||||
void InstrDisasm::AddCR(uint32_t bf, InstrRegister::Access access) {
|
||||
InstrRegister i = {
|
||||
InstrRegister::kCR, bf, access
|
||||
};
|
||||
special_registers.push_back(i);
|
||||
}
|
||||
|
||||
void InstrDisasm::AddRegOperand(
|
||||
InstrRegister::RegisterSet set, uint32_t ordinal,
|
||||
InstrRegister::Access access, std::string display) {
|
||||
InstrRegister i = {
|
||||
set, ordinal, access
|
||||
};
|
||||
InstrOperand o;
|
||||
o.type = InstrOperand::kRegister;
|
||||
o.reg = i;
|
||||
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, "0x%.2X", (uint8_t)value);
|
||||
break;
|
||||
case 2:
|
||||
xesnprintfa(o.display, max_count, "0x%.4X", (uint16_t)value);
|
||||
break;
|
||||
case 4:
|
||||
xesnprintfa(o.display, max_count, "0x%.8X", (uint32_t)value);
|
||||
break;
|
||||
case 8:
|
||||
xesnprintfa(o.display, max_count, "0x%.16llX", value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
operands.push_back(o);
|
||||
}
|
||||
|
||||
int InstrDisasm::Finish() {
|
||||
for (std::vector<InstrOperand>::iterator it = operands.begin();
|
||||
it != operands.end(); ++it) {
|
||||
if (it->type == InstrOperand::kRegister) {
|
||||
access_bits.MarkAccess(it->reg);
|
||||
}
|
||||
}
|
||||
for (std::vector<InstrRegister>::iterator it = special_registers.begin();
|
||||
it != special_registers.end(); ++it) {
|
||||
access_bits.MarkAccess(*it);
|
||||
}
|
||||
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* slot = NULL;
|
||||
switch (code >> 26) {
|
||||
case 4:
|
||||
// Opcode = 4, index = bits 5-0 (6)
|
||||
slot = &xe::cpu::ppc::tables::instr_table_4[XESELECTBITS(code, 0, 5)];
|
||||
break;
|
||||
case 19:
|
||||
// Opcode = 19, index = bits 10-1 (10)
|
||||
slot = &xe::cpu::ppc::tables::instr_table_19[XESELECTBITS(code, 1, 10)];
|
||||
break;
|
||||
case 30:
|
||||
// Opcode = 30, index = bits 4-1 (4)
|
||||
slot = &xe::cpu::ppc::tables::instr_table_30[XESELECTBITS(code, 1, 4)];
|
||||
break;
|
||||
case 31:
|
||||
// Opcode = 31, index = bits 10-1 (10)
|
||||
slot = &xe::cpu::ppc::tables::instr_table_31[XESELECTBITS(code, 1, 10)];
|
||||
break;
|
||||
case 58:
|
||||
// Opcode = 58, index = bits 1-0 (2)
|
||||
slot = &xe::cpu::ppc::tables::instr_table_58[XESELECTBITS(code, 0, 1)];
|
||||
break;
|
||||
case 59:
|
||||
// Opcode = 59, index = bits 5-1 (5)
|
||||
slot = &xe::cpu::ppc::tables::instr_table_59[XESELECTBITS(code, 1, 5)];
|
||||
break;
|
||||
case 62:
|
||||
// Opcode = 62, index = bits 1-0 (2)
|
||||
slot = &xe::cpu::ppc::tables::instr_table_62[XESELECTBITS(code, 0, 1)];
|
||||
break;
|
||||
case 63:
|
||||
// Opcode = 63, index = bits 10-1 (10)
|
||||
slot = &xe::cpu::ppc::tables::instr_table_63[XESELECTBITS(code, 1, 10)];
|
||||
break;
|
||||
default:
|
||||
slot = &xe::cpu::ppc::tables::instr_table[XESELECTBITS(code, 26, 31)];
|
||||
break;
|
||||
}
|
||||
if (!slot || !slot->opcode) {
|
||||
return NULL;
|
||||
}
|
||||
return slot;
|
||||
}
|
||||
|
||||
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) {
|
||||
return 1;
|
||||
}
|
||||
XEASSERTNULL(instr_type->emit);
|
||||
instr_type->emit = emit;
|
||||
return 0;
|
||||
}
|
||||
328
src/xenia/cpu/ppc/instr.h
Normal file
328
src/xenia/cpu/ppc/instr.h
Normal file
@@ -0,0 +1,328 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_CPU_PPC_INSTR_H_
|
||||
#define XENIA_CPU_PPC_INSTR_H_
|
||||
|
||||
#include <xenia/common.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace ppc {
|
||||
|
||||
|
||||
// TODO(benvanik): rename these
|
||||
typedef enum {
|
||||
kXEPPCInstrFormatI = 0,
|
||||
kXEPPCInstrFormatB = 1,
|
||||
kXEPPCInstrFormatSC = 2,
|
||||
kXEPPCInstrFormatD = 3,
|
||||
kXEPPCInstrFormatDS = 4,
|
||||
kXEPPCInstrFormatX = 5,
|
||||
kXEPPCInstrFormatXL = 6,
|
||||
kXEPPCInstrFormatXFX = 7,
|
||||
kXEPPCInstrFormatXFL = 8,
|
||||
kXEPPCInstrFormatXS = 9,
|
||||
kXEPPCInstrFormatXO = 10,
|
||||
kXEPPCInstrFormatA = 11,
|
||||
kXEPPCInstrFormatM = 12,
|
||||
kXEPPCInstrFormatMD = 13,
|
||||
kXEPPCInstrFormatMDS = 14,
|
||||
kXEPPCInstrFormatVA = 15,
|
||||
kXEPPCInstrFormatVX = 16,
|
||||
kXEPPCInstrFormatVXR = 17,
|
||||
} xe_ppc_instr_format_e;
|
||||
|
||||
typedef enum {
|
||||
kXEPPCInstrTypeGeneral = (1 << 0),
|
||||
kXEPPCInstrTypeBranch = (1 << 1),
|
||||
kXEPPCInstrTypeBranchCond = kXEPPCInstrTypeBranch | (1 << 2),
|
||||
kXEPPCInstrTypeBranchAlways = kXEPPCInstrTypeBranch | (1 << 3),
|
||||
kXEPPCInstrTypeSyscall = (1 << 4),
|
||||
} xe_ppc_instr_type_e;
|
||||
|
||||
typedef enum {
|
||||
kXEPPCInstrFlagReserved = 0,
|
||||
} xe_ppc_instr_flag_e;
|
||||
|
||||
|
||||
class InstrType;
|
||||
|
||||
|
||||
static inline int32_t XEEXTS16(uint32_t v) {
|
||||
return (int32_t)((int16_t)v);
|
||||
}
|
||||
static inline int32_t XEEXTS26(uint32_t v) {
|
||||
return v & 0x02000000 ? (int32_t)v | 0xFC000000 : (int32_t)(v);
|
||||
}
|
||||
static inline uint64_t XEMASK(uint32_t mstart, uint32_t mstop) {
|
||||
// if mstart ≤ mstop then
|
||||
// mask[mstart:mstop] = ones
|
||||
// mask[all other bits] = zeros
|
||||
// else
|
||||
// mask[mstart:63] = ones
|
||||
// mask[0:mstop] = ones
|
||||
// mask[all other bits] = zeros
|
||||
uint64_t value =
|
||||
(UINT64_MAX >> mstart) ^ ((mstop >= 63) ? 0 : UINT64_MAX >> (mstop + 1));
|
||||
return mstart <= mstop ? value : ~value;
|
||||
}
|
||||
|
||||
|
||||
typedef struct {
|
||||
InstrType* type;
|
||||
uint32_t address;
|
||||
|
||||
union {
|
||||
uint32_t code;
|
||||
|
||||
// kXEPPCInstrFormatI
|
||||
struct {
|
||||
uint32_t LK : 1;
|
||||
uint32_t AA : 1;
|
||||
uint32_t LI : 24;
|
||||
uint32_t : 6;
|
||||
} I;
|
||||
// kXEPPCInstrFormatB
|
||||
struct {
|
||||
uint32_t LK : 1;
|
||||
uint32_t AA : 1;
|
||||
uint32_t BD : 14;
|
||||
uint32_t BI : 5;
|
||||
uint32_t BO : 5;
|
||||
uint32_t : 6;
|
||||
} B;
|
||||
|
||||
// kXEPPCInstrFormatSC
|
||||
// kXEPPCInstrFormatD
|
||||
struct {
|
||||
uint32_t DS : 16;
|
||||
uint32_t RA : 5;
|
||||
uint32_t RT : 5;
|
||||
uint32_t : 6;
|
||||
} D;
|
||||
// kXEPPCInstrFormatDS
|
||||
struct {
|
||||
uint32_t : 2;
|
||||
uint32_t DS : 14;
|
||||
uint32_t RA : 5;
|
||||
uint32_t RT : 5;
|
||||
uint32_t : 6;
|
||||
} DS;
|
||||
// kXEPPCInstrFormatX
|
||||
struct {
|
||||
uint32_t Rc : 1;
|
||||
uint32_t : 10;
|
||||
uint32_t RB : 5;
|
||||
uint32_t RA : 5;
|
||||
uint32_t RT : 5;
|
||||
uint32_t : 6;
|
||||
} X;
|
||||
// kXEPPCInstrFormatXL
|
||||
struct {
|
||||
uint32_t LK : 1;
|
||||
uint32_t : 10;
|
||||
uint32_t BB : 5;
|
||||
uint32_t BI : 5;
|
||||
uint32_t BO : 5;
|
||||
uint32_t : 6;
|
||||
} XL;
|
||||
// kXEPPCInstrFormatXFX
|
||||
struct {
|
||||
uint32_t : 1;
|
||||
uint32_t : 10;
|
||||
uint32_t spr : 10;
|
||||
uint32_t RT : 5;
|
||||
uint32_t : 6;
|
||||
} XFX;
|
||||
// kXEPPCInstrFormatXFL
|
||||
// kXEPPCInstrFormatXS
|
||||
struct {
|
||||
uint32_t Rc : 1;
|
||||
uint32_t SH5 : 1;
|
||||
uint32_t : 9;
|
||||
uint32_t SH : 5;
|
||||
uint32_t RA : 5;
|
||||
uint32_t RT : 5;
|
||||
uint32_t : 6;
|
||||
} XS;
|
||||
// kXEPPCInstrFormatXO
|
||||
struct {
|
||||
uint32_t Rc : 1;
|
||||
uint32_t : 9;
|
||||
uint32_t OE : 1;
|
||||
uint32_t RB : 5;
|
||||
uint32_t RA : 5;
|
||||
uint32_t RT : 5;
|
||||
uint32_t : 6;
|
||||
} XO;
|
||||
// kXEPPCInstrFormatA
|
||||
// kXEPPCInstrFormatM
|
||||
struct {
|
||||
uint32_t Rc : 1;
|
||||
uint32_t ME : 5;
|
||||
uint32_t MB : 5;
|
||||
uint32_t SH : 5;
|
||||
uint32_t RA : 5;
|
||||
uint32_t RT : 5;
|
||||
uint32_t : 6;
|
||||
} M;
|
||||
// kXEPPCInstrFormatMD
|
||||
struct {
|
||||
uint32_t Rc : 1;
|
||||
uint32_t SH5 : 1;
|
||||
uint32_t : 3;
|
||||
uint32_t MB5 : 1;
|
||||
uint32_t MB : 5;
|
||||
uint32_t SH : 5;
|
||||
uint32_t RA : 5;
|
||||
uint32_t RT : 5;
|
||||
uint32_t : 6;
|
||||
} MD;
|
||||
// kXEPPCInstrFormatMDS
|
||||
struct {
|
||||
uint32_t Rc : 1;
|
||||
uint32_t : 4;
|
||||
uint32_t MB5 : 1;
|
||||
uint32_t MB : 5;
|
||||
uint32_t RB : 5;
|
||||
uint32_t RA : 5;
|
||||
uint32_t RT : 5;
|
||||
uint32_t : 6;
|
||||
} MDS;
|
||||
// kXEPPCInstrFormatVA
|
||||
// kXEPPCInstrFormatVX
|
||||
// kXEPPCInstrFormatVXR
|
||||
};
|
||||
} 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 InstrAccessBits {
|
||||
public:
|
||||
InstrAccessBits() : spr(0), cr(0), gpr(0), fpr(0) {}
|
||||
|
||||
// Bitmasks derived from the accesses to registers.
|
||||
// Format is 2 bits for each register, even bits indicating reads and odds
|
||||
// indicating writes.
|
||||
uint64_t spr; // fpcsr/ctr/lr/xer
|
||||
uint64_t cr; // cr7/6/5/4/3/2/1/0
|
||||
uint64_t gpr; // r31-0
|
||||
uint64_t fpr; // f31-0
|
||||
|
||||
void Clear();
|
||||
void Extend(InstrAccessBits& other);
|
||||
void MarkAccess(InstrRegister& reg);
|
||||
void Dump(std::string& out_str);
|
||||
};
|
||||
|
||||
|
||||
class InstrDisasm {
|
||||
public:
|
||||
enum Flags {
|
||||
kOE = 1 << 0,
|
||||
kRc = 1 << 1,
|
||||
kCA = 1 << 2,
|
||||
kLR = 1 << 4,
|
||||
};
|
||||
|
||||
char name[16];
|
||||
char info[64];
|
||||
std::vector<InstrOperand> operands;
|
||||
std::vector<InstrRegister> special_registers;
|
||||
InstrAccessBits access_bits;
|
||||
|
||||
void Init(std::string name, std::string info, uint32_t flags);
|
||||
void AddLR(InstrRegister::Access access);
|
||||
void AddCTR(InstrRegister::Access access);
|
||||
void AddCR(uint32_t bf, InstrRegister::Access access);
|
||||
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();
|
||||
|
||||
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;
|
||||
uint32_t format; // xe_ppc_instr_format_e
|
||||
uint32_t type; // xe_ppc_instr_type_e
|
||||
uint32_t flags; // xe_ppc_instr_flag_e
|
||||
char name[16];
|
||||
|
||||
InstrDisassembleFn disassemble;
|
||||
InstrEmitFn emit;
|
||||
};
|
||||
|
||||
InstrType* GetInstrType(uint32_t code);
|
||||
int RegisterInstrDisassemble(uint32_t code, InstrDisassembleFn disassemble);
|
||||
int RegisterInstrEmit(uint32_t code, InstrEmitFn emit);
|
||||
|
||||
|
||||
} // namespace ppc
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_PPC_INSTR_H_
|
||||
|
||||
342
src/xenia/cpu/ppc/instr_tables.h
Normal file
342
src/xenia/cpu/ppc/instr_tables.h
Normal file
@@ -0,0 +1,342 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_CPU_PPC_INSTR_TABLE_H_
|
||||
#define XENIA_CPU_PPC_INSTR_TABLE_H_
|
||||
|
||||
#include <xenia/cpu/ppc/instr.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace ppc {
|
||||
namespace tables {
|
||||
|
||||
|
||||
static InstrType* instr_table_prep(
|
||||
InstrType* unprep, int unprep_count, int a, int b) {
|
||||
int prep_count = pow(2.0, b - a + 1);
|
||||
InstrType* prep = (InstrType*)xe_calloc(prep_count * sizeof(InstrType));
|
||||
for (int n = 0; n < unprep_count; n++) {
|
||||
int ordinal = XESELECTBITS(unprep[n].opcode, a, b);
|
||||
prep[ordinal] = unprep[n];
|
||||
}
|
||||
return prep;
|
||||
}
|
||||
|
||||
|
||||
#define EMPTY(slot) {0}
|
||||
#define INSTRUCTION(name, opcode, format, type, flag) { \
|
||||
opcode, \
|
||||
kXEPPCInstrFormat##format, \
|
||||
kXEPPCInstrType##type, \
|
||||
flag, \
|
||||
#name, \
|
||||
}
|
||||
#define FLAG(t) kXEPPCInstrFlag##t
|
||||
|
||||
|
||||
// This table set is constructed from:
|
||||
// pem_64bit_v3.0.2005jul15.pdf, A.2
|
||||
// PowerISA_V2.06B_V2_PUBLIC.pdf
|
||||
|
||||
// Opcode = 4, index = bits 5-0 (6)
|
||||
static InstrType instr_table_4_unprep[] = {
|
||||
// TODO: all of the vector ops
|
||||
INSTRUCTION(vperm, 0x1000002B, VA , General , 0),
|
||||
};
|
||||
static InstrType* instr_table_4 = instr_table_prep(
|
||||
instr_table_4_unprep, XECOUNT(instr_table_4_unprep), 0, 5);
|
||||
|
||||
// Opcode = 19, index = bits 10-1 (10)
|
||||
static InstrType instr_table_19_unprep[] = {
|
||||
INSTRUCTION(mcrf, 0x4C000000, XL , General , 0),
|
||||
INSTRUCTION(bclrx, 0x4C000020, XL , BranchCond , 0),
|
||||
INSTRUCTION(crnor, 0x4C000042, XL , General , 0),
|
||||
INSTRUCTION(crandc, 0x4C000102, XL , General , 0),
|
||||
INSTRUCTION(isync, 0x4C00012C, XL , General , 0),
|
||||
INSTRUCTION(crxor, 0x4C000182, XL , General , 0),
|
||||
INSTRUCTION(crnand, 0x4C0001C2, XL , General , 0),
|
||||
INSTRUCTION(crand, 0x4C000202, XL , General , 0),
|
||||
INSTRUCTION(creqv, 0x4C000242, XL , General , 0),
|
||||
INSTRUCTION(crorc, 0x4C000342, XL , General , 0),
|
||||
INSTRUCTION(cror, 0x4C000382, XL , General , 0),
|
||||
INSTRUCTION(bcctrx, 0x4C000420, XL , BranchCond , 0),
|
||||
};
|
||||
static InstrType* instr_table_19 = instr_table_prep(
|
||||
instr_table_19_unprep, XECOUNT(instr_table_19_unprep), 1, 10);
|
||||
|
||||
// Opcode = 30, index = bits 4-1 (4)
|
||||
static InstrType instr_table_30_unprep[] = {
|
||||
INSTRUCTION(rldiclx, 0x78000000, MD , General , 0),
|
||||
INSTRUCTION(rldicrx, 0x78000004, MD , General , 0),
|
||||
INSTRUCTION(rldicx, 0x78000008, MD , General , 0),
|
||||
INSTRUCTION(rldimix, 0x7800000C, MD , General , 0),
|
||||
INSTRUCTION(rldclx, 0x78000010, MDS, General , 0),
|
||||
INSTRUCTION(rldcrx, 0x78000012, MDS, General , 0),
|
||||
};
|
||||
static InstrType* instr_table_30 = instr_table_prep(
|
||||
instr_table_30_unprep, XECOUNT(instr_table_30_unprep), 1, 4);
|
||||
|
||||
// Opcode = 31, index = bits 10-1 (10)
|
||||
static InstrType instr_table_31_unprep[] = {
|
||||
INSTRUCTION(cmp, 0x7C000000, X , General , 0),
|
||||
INSTRUCTION(tw, 0x7C000008, X , General , 0),
|
||||
INSTRUCTION(lvsl, 0x7C00000C, X , General , 0),
|
||||
INSTRUCTION(lvebx, 0x7C00000E, X , General , 0),
|
||||
INSTRUCTION(subfcx, 0x7C000010, XO , General , 0),
|
||||
INSTRUCTION(mulhdux, 0x7C000012, XO , General , 0),
|
||||
INSTRUCTION(addcx, 0X7C000014, XO , General , 0),
|
||||
INSTRUCTION(mulhwux, 0x7C000016, XO , General , 0),
|
||||
INSTRUCTION(mfcr, 0x7C000026, X , General , 0),
|
||||
INSTRUCTION(lwarx, 0x7C000028, X , General , 0),
|
||||
INSTRUCTION(ldx, 0x7C00002A, X , General , 0),
|
||||
INSTRUCTION(lwzx, 0x7C00002E, X , General , 0),
|
||||
INSTRUCTION(slwx, 0x7C000030, X , General , 0),
|
||||
INSTRUCTION(cntlzwx, 0x7C000034, X , General , 0),
|
||||
INSTRUCTION(sldx, 0x7C000036, X , General , 0),
|
||||
INSTRUCTION(andx, 0x7C000038, X , General , 0),
|
||||
INSTRUCTION(cmpl, 0x7C000040, X , General , 0),
|
||||
INSTRUCTION(lvsr, 0x7C00004C, X , General , 0),
|
||||
INSTRUCTION(lvehx, 0x7C00004E, X , General , 0),
|
||||
INSTRUCTION(subfx, 0x7C000050, XO , General , 0),
|
||||
INSTRUCTION(ldux, 0x7C00006A, X , General , 0),
|
||||
INSTRUCTION(dcbst, 0x7C00006C, X , General , 0),
|
||||
INSTRUCTION(lwzux, 0x7C00006E, X , General , 0),
|
||||
INSTRUCTION(cntlzdx, 0x7C000074, X , General , 0),
|
||||
INSTRUCTION(andcx, 0x7C000078, X , General , 0),
|
||||
INSTRUCTION(td, 0x7C000088, X , General , 0),
|
||||
INSTRUCTION(lvewx, 0x7C00008E, X , General , 0),
|
||||
INSTRUCTION(mulhdx, 0x7C000092, XO , General , 0),
|
||||
INSTRUCTION(mulhwx, 0x7C000096, XO , General , 0),
|
||||
INSTRUCTION(ldarx, 0x7C0000A8, X , General , 0),
|
||||
INSTRUCTION(dcbf, 0x7C0000AC, X , General , 0),
|
||||
INSTRUCTION(lbzx, 0x7C0000AE, X , General , 0),
|
||||
INSTRUCTION(lvx, 0x7C0000CE, X , General , 0),
|
||||
INSTRUCTION(negx, 0x7C0000D0, XO , General , 0),
|
||||
INSTRUCTION(lbzux, 0x7C0000EE, X , General , 0),
|
||||
INSTRUCTION(norx, 0x7C0000F8, X , General , 0),
|
||||
INSTRUCTION(stvebx, 0x7C00010E, X , General , 0),
|
||||
INSTRUCTION(subfex, 0x7C000110, XO , General , 0),
|
||||
INSTRUCTION(addex, 0x7C000114, XO , General , 0),
|
||||
INSTRUCTION(mtcrf, 0x7C000120, XFX, General , 0),
|
||||
INSTRUCTION(stdx, 0x7C00012A, X , General , 0),
|
||||
INSTRUCTION(stwcx, 0x7C00012D, X , General , 0),
|
||||
INSTRUCTION(stwx, 0x7C00012E, X , General , 0),
|
||||
INSTRUCTION(stvehx, 0x7C00014E, X , General , 0),
|
||||
INSTRUCTION(stdux, 0x7C00016A, X , General , 0),
|
||||
INSTRUCTION(stwux, 0x7C00016E, X , General , 0),
|
||||
INSTRUCTION(stvewx, 0x7C00018E, X , General , 0),
|
||||
INSTRUCTION(subfzex, 0x7C000190, XO , General , 0),
|
||||
INSTRUCTION(addzex, 0x7C000194, XO , General , 0),
|
||||
INSTRUCTION(stdcx, 0x7C0001AD, X , General , 0),
|
||||
INSTRUCTION(stbx, 0x7C0001AE, X , General , 0),
|
||||
INSTRUCTION(stvx, 0x7C0001CE, X , General , 0),
|
||||
INSTRUCTION(subfmex, 0x7C0001D0, XO , General , 0),
|
||||
INSTRUCTION(mulldx, 0x7C0001D2, XO , General , 0),
|
||||
INSTRUCTION(addmex, 0x7C0001D4, XO , General , 0),
|
||||
INSTRUCTION(mullwx, 0x7C0001D6, XO , General , 0),
|
||||
INSTRUCTION(dcbtst, 0x7C0001EC, X , General , 0),
|
||||
INSTRUCTION(stbux, 0x7C0001EE, X , General , 0),
|
||||
INSTRUCTION(addx, 0x7C000214, XO , General , 0),
|
||||
INSTRUCTION(dcbt, 0x7C00022C, X , General , 0),
|
||||
INSTRUCTION(lhzx, 0x7C00022E, X , General , 0),
|
||||
INSTRUCTION(eqvx, 0x7C000238, X , General , 0),
|
||||
INSTRUCTION(eciwx, 0x7C00026C, X , General , 0),
|
||||
INSTRUCTION(lhzux, 0x7C00026E, X , General , 0),
|
||||
INSTRUCTION(xorx, 0x7C000278, X , General , 0),
|
||||
INSTRUCTION(mfspr, 0x7C0002A6, XFX, General , 0),
|
||||
INSTRUCTION(lwax, 0x7C0002AA, X , General , 0),
|
||||
INSTRUCTION(lhax, 0x7C0002AE, X , General , 0),
|
||||
INSTRUCTION(lvxl, 0x7C0002CE, X , General , 0),
|
||||
INSTRUCTION(mftb, 0x7C0002E6, XFX, General , 0),
|
||||
INSTRUCTION(lwaux, 0x7C0002EA, X , General , 0),
|
||||
INSTRUCTION(lhaux, 0x7C0002EE, X , General , 0),
|
||||
INSTRUCTION(sthx, 0x7C00032E, X , General , 0),
|
||||
INSTRUCTION(orcx, 0x7C000338, X , General , 0),
|
||||
INSTRUCTION(ecowx, 0x7C00036C, X , General , 0),
|
||||
INSTRUCTION(sthux, 0x7C00036E, X , General , 0),
|
||||
INSTRUCTION(orx, 0x7C000378, X , General , 0),
|
||||
INSTRUCTION(divdux, 0x7C000392, XO , General , 0),
|
||||
INSTRUCTION(divwux, 0x7C000396, XO , General , 0),
|
||||
INSTRUCTION(mtspr, 0x7C0003A6, XFX, General , 0),
|
||||
INSTRUCTION(nandx, 0x7C0003B8, X , General , 0),
|
||||
INSTRUCTION(stvxl, 0x7C0003CE, X , General , 0),
|
||||
INSTRUCTION(divdx, 0x7C0003D2, XO , General , 0),
|
||||
INSTRUCTION(divwx, 0x7C0003D6, XO , General , 0),
|
||||
INSTRUCTION(lvlx, 0x7C00040E, X , General , 0),
|
||||
INSTRUCTION(ldbrx, 0x7C000428, X , General , 0),
|
||||
INSTRUCTION(lswx, 0x7C00042A, X , General , 0),
|
||||
INSTRUCTION(lwbrx, 0x7C00042C, X , General , 0),
|
||||
INSTRUCTION(lfsx, 0x7C00042E, X , General , 0),
|
||||
INSTRUCTION(srwx, 0x7C000430, X , General , 0),
|
||||
INSTRUCTION(srdx, 0x7C000436, X , General , 0),
|
||||
INSTRUCTION(lfsux, 0x7C00046E, X , General , 0),
|
||||
INSTRUCTION(lswi, 0x7C0004AA, X , General , 0),
|
||||
INSTRUCTION(sync, 0x7C0004AC, X , General , 0),
|
||||
INSTRUCTION(lfdx, 0x7C0004AE, X , General , 0),
|
||||
INSTRUCTION(lfdux, 0x7C0004EE, X , General , 0),
|
||||
INSTRUCTION(stdbrx, 0x7C000528, X , General , 0),
|
||||
INSTRUCTION(stswx, 0x7C00052A, X , General , 0),
|
||||
INSTRUCTION(stwbrx, 0x7C00052C, X , General , 0),
|
||||
INSTRUCTION(stfsx, 0x7C00052E, X , General , 0),
|
||||
INSTRUCTION(stfsux, 0x7C00056E, X , General , 0),
|
||||
INSTRUCTION(stswi, 0x7C0005AA, X , General , 0),
|
||||
INSTRUCTION(stfdx, 0x7C0005AE, X , General , 0),
|
||||
INSTRUCTION(stfdux, 0x7C0005EE, X , General , 0),
|
||||
INSTRUCTION(lhbrx, 0x7C00062C, X , General , 0),
|
||||
INSTRUCTION(srawx, 0x7C000630, X , General , 0),
|
||||
INSTRUCTION(sradx, 0x7C000634, X , General , 0),
|
||||
INSTRUCTION(srawix, 0x7C000670, X , General , 0),
|
||||
INSTRUCTION(sradix, 0x7C000674, XS , General , 0), // TODO
|
||||
INSTRUCTION(eieio, 0x7C0006AC, X , General , 0),
|
||||
INSTRUCTION(sthbrx, 0x7C00072C, X , General , 0),
|
||||
INSTRUCTION(extshx, 0x7C000734, X , General , 0),
|
||||
INSTRUCTION(extsbx, 0x7C000774, X , General , 0),
|
||||
INSTRUCTION(icbi, 0x7C0007AC, X , General , 0),
|
||||
INSTRUCTION(stfiwx, 0x7C0007AE, X , General , 0),
|
||||
INSTRUCTION(extswx, 0x7C0007B4, X , General , 0),
|
||||
INSTRUCTION(dcbz, 0x7C0007EC, X , General , 0), // 0x7C2007EC = DCBZ128
|
||||
};
|
||||
static InstrType* instr_table_31 = instr_table_prep(
|
||||
instr_table_31_unprep, XECOUNT(instr_table_31_unprep), 1, 10);
|
||||
|
||||
// Opcode = 58, index = bits 1-0 (2)
|
||||
static InstrType instr_table_58_unprep[] = {
|
||||
INSTRUCTION(ld, 0xE8000000, DS , General , 0),
|
||||
INSTRUCTION(ldu, 0xE8000001, DS , General , 0),
|
||||
INSTRUCTION(lwa, 0xE8000002, DS , General , 0),
|
||||
};
|
||||
static InstrType* instr_table_58 = instr_table_prep(
|
||||
instr_table_58_unprep, XECOUNT(instr_table_58_unprep), 0, 1);
|
||||
|
||||
// Opcode = 59, index = bits 5-1 (5)
|
||||
static InstrType instr_table_59_unprep[] = {
|
||||
INSTRUCTION(fdivsx, 0xEC000024, A , General , 0),
|
||||
INSTRUCTION(fsubsx, 0xEC000028, A , General , 0),
|
||||
INSTRUCTION(faddsx, 0xEC00002A, A , General , 0),
|
||||
INSTRUCTION(fsqrtsx, 0xEC00002C, A , General , 0),
|
||||
INSTRUCTION(fresx, 0xEC000030, A , General , 0),
|
||||
INSTRUCTION(fmulsx, 0xEC000032, A , General , 0),
|
||||
INSTRUCTION(fmsubsx, 0xEC000038, A , General , 0),
|
||||
INSTRUCTION(fmaddsx, 0xEC00003A, A , General , 0),
|
||||
INSTRUCTION(fnmsubsx, 0xEC00003C, A , General , 0),
|
||||
INSTRUCTION(fnmaddsx, 0xEC00003E, A , General , 0),
|
||||
};
|
||||
static InstrType* instr_table_59 = instr_table_prep(
|
||||
instr_table_59_unprep, XECOUNT(instr_table_59_unprep), 1, 5);
|
||||
|
||||
// Opcode = 62, index = bits 1-0 (2)
|
||||
static InstrType instr_table_62_unprep[] = {
|
||||
INSTRUCTION(std, 0xF8000000, DS , General , 0),
|
||||
INSTRUCTION(stdu, 0xF8000001, DS , General , 0),
|
||||
};
|
||||
static InstrType* instr_table_62 = instr_table_prep(
|
||||
instr_table_62_unprep, XECOUNT(instr_table_62_unprep), 0, 1);
|
||||
|
||||
// Opcode = 63, index = bits 10-1 (10)
|
||||
static InstrType instr_table_63_unprep[] = {
|
||||
INSTRUCTION(fcmpu, 0xFC000000, X , General , 0),
|
||||
INSTRUCTION(frspx, 0xFC000018, X , General , 0),
|
||||
INSTRUCTION(fctiwx, 0xFC00001C, X , General , 0),
|
||||
INSTRUCTION(fctiwzx, 0xFC00001E, X , General , 0),
|
||||
INSTRUCTION(fdivx, 0xFC000024, A , General , 0),
|
||||
INSTRUCTION(fsubx, 0xFC000028, A , General , 0),
|
||||
INSTRUCTION(faddx, 0xFC00002A, A , General , 0),
|
||||
INSTRUCTION(fsqrtx, 0xFC00002C, A , General , 0),
|
||||
INSTRUCTION(fselx, 0xFC00002E, A , General , 0),
|
||||
INSTRUCTION(fmulx, 0xFC000032, A , General , 0),
|
||||
INSTRUCTION(frsqrtex, 0xFC000034, A , General , 0),
|
||||
INSTRUCTION(fmsubx, 0xFC000038, A , General , 0),
|
||||
INSTRUCTION(fmaddx, 0xFC00003A, A , General , 0),
|
||||
INSTRUCTION(fnmsubx, 0xFC00003C, A , General , 0),
|
||||
INSTRUCTION(fnmaddx, 0xFC00003E, A , General , 0),
|
||||
INSTRUCTION(fcmpo, 0xFC000040, X , General , 0),
|
||||
INSTRUCTION(mtfsb1x, 0xFC00004C, X , General , 0),
|
||||
INSTRUCTION(fnegx, 0xFC000050, X , General , 0),
|
||||
INSTRUCTION(mcrfs, 0xFC000080, X , General , 0),
|
||||
INSTRUCTION(mtfsb0x, 0xFC00008C, X , General , 0),
|
||||
INSTRUCTION(fmrx, 0xFC000090, X , General , 0),
|
||||
INSTRUCTION(mtfsfix, 0xFC00010C, X , General , 0),
|
||||
INSTRUCTION(fnabsx, 0xFC000110, X , General , 0),
|
||||
INSTRUCTION(fabsx, 0xFC000210, X , General , 0),
|
||||
INSTRUCTION(mffsx, 0xFC00048E, X , General , 0),
|
||||
INSTRUCTION(mtfsfx, 0xFC00058E, XFL, General , 0),
|
||||
INSTRUCTION(fctidx, 0xFC00065C, X , General , 0),
|
||||
INSTRUCTION(fctidzx, 0xFC00065E, X , General , 0),
|
||||
INSTRUCTION(fcfidx, 0xFC00069C, X , General , 0),
|
||||
};
|
||||
static InstrType* instr_table_63 = instr_table_prep(
|
||||
instr_table_63_unprep, XECOUNT(instr_table_63_unprep), 1, 10);
|
||||
|
||||
// Main table, index = bits 31-26 (6) : (code >> 26)
|
||||
static InstrType instr_table_unprep[64] = {
|
||||
INSTRUCTION(tdi, 0x08000000, D , General , 0),
|
||||
INSTRUCTION(twi, 0x0C000000, D , General , 0),
|
||||
INSTRUCTION(mulli, 0x1C000000, D , General , 0),
|
||||
INSTRUCTION(subficx, 0x20000000, D , General , 0),
|
||||
INSTRUCTION(cmpli, 0x28000000, D , General , 0),
|
||||
INSTRUCTION(cmpi, 0x2C000000, D , General , 0),
|
||||
INSTRUCTION(addic, 0x30000000, D , General , 0),
|
||||
INSTRUCTION(addicx, 0x34000000, D , General , 0),
|
||||
INSTRUCTION(addi, 0x38000000, D , General , 0),
|
||||
INSTRUCTION(addis, 0x3C000000, D , General , 0),
|
||||
INSTRUCTION(bcx, 0x40000000, B , BranchCond , 0),
|
||||
INSTRUCTION(sc, 0x44000002, SC , Syscall , 0),
|
||||
INSTRUCTION(bx, 0x48000000, I , BranchAlways , 0),
|
||||
INSTRUCTION(rlwimix, 0x50000000, M , General , 0),
|
||||
INSTRUCTION(rlwinmx, 0x54000000, M , General , 0),
|
||||
INSTRUCTION(rlwnmx, 0x5C000000, M , General , 0),
|
||||
INSTRUCTION(ori, 0x60000000, D , General , 0),
|
||||
INSTRUCTION(oris, 0x64000000, D , General , 0),
|
||||
INSTRUCTION(xori, 0x68000000, D , General , 0),
|
||||
INSTRUCTION(xoris, 0x6C000000, D , General , 0),
|
||||
INSTRUCTION(andix, 0x70000000, D , General , 0),
|
||||
INSTRUCTION(andisx, 0x74000000, D , General , 0),
|
||||
INSTRUCTION(lwz, 0x80000000, D , General , 0),
|
||||
INSTRUCTION(lwzu, 0x84000000, D , General , 0),
|
||||
INSTRUCTION(lbz, 0x88000000, D , General , 0),
|
||||
INSTRUCTION(lbzu, 0x8C000000, D , General , 0),
|
||||
INSTRUCTION(stw, 0x90000000, D , General , 0),
|
||||
INSTRUCTION(stwu, 0x94000000, D , General , 0),
|
||||
INSTRUCTION(stb, 0x98000000, D , General , 0),
|
||||
INSTRUCTION(stbu, 0x9C000000, D , General , 0),
|
||||
INSTRUCTION(lhz, 0xA0000000, D , General , 0),
|
||||
INSTRUCTION(lhzu, 0xA4000000, D , General , 0),
|
||||
INSTRUCTION(lha, 0xA8000000, D , General , 0),
|
||||
INSTRUCTION(lhau, 0xAC000000, D , General , 0),
|
||||
INSTRUCTION(sth, 0xB0000000, D , General , 0),
|
||||
INSTRUCTION(sthu, 0xB4000000, D , General , 0),
|
||||
INSTRUCTION(lmw, 0xB8000000, D , General , 0),
|
||||
INSTRUCTION(stmw, 0xBC000000, D , General , 0),
|
||||
INSTRUCTION(lfs, 0xC0000000, D , General , 0),
|
||||
INSTRUCTION(lfsu, 0xC4000000, D , General , 0),
|
||||
INSTRUCTION(lfd, 0xC8000000, D , General , 0),
|
||||
INSTRUCTION(lfdu, 0xCC000000, D , General , 0),
|
||||
INSTRUCTION(stfs, 0xD0000000, D , General , 0),
|
||||
INSTRUCTION(stfsu, 0xD4000000, D , General , 0),
|
||||
INSTRUCTION(stfd, 0xD8000000, D , General , 0),
|
||||
INSTRUCTION(stfdu, 0xDC000000, D , General , 0),
|
||||
};
|
||||
static InstrType* instr_table = instr_table_prep(
|
||||
instr_table_unprep, XECOUNT(instr_table_unprep), 26, 31);
|
||||
|
||||
|
||||
#undef FLAG
|
||||
#undef INSTRUCTION
|
||||
#undef EMPTY
|
||||
|
||||
|
||||
} // namespace tables
|
||||
} // namespace ppc
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_PPC_INSTR_TABLE_H_
|
||||
10
src/xenia/cpu/ppc/sources.gypi
Normal file
10
src/xenia/cpu/ppc/sources.gypi
Normal file
@@ -0,0 +1,10 @@
|
||||
# Copyright 2013 Ben Vanik. All Rights Reserved.
|
||||
{
|
||||
'sources': [
|
||||
'instr.cc',
|
||||
'instr.h',
|
||||
'instr_tables.h',
|
||||
'state.cc',
|
||||
'state.h',
|
||||
],
|
||||
}
|
||||
47
src/xenia/cpu/ppc/state.cc
Normal file
47
src/xenia/cpu/ppc/state.cc
Normal file
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <xenia/common.h>
|
||||
#include <xenia/core.h>
|
||||
#include <xenia/cpu/ppc/state.h>
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
uint64_t ParseInt64(const char* value) {
|
||||
return xestrtoulla(value, NULL, 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void xe_ppc_state::SetRegFromString(const char* name, const char* value) {
|
||||
int n;
|
||||
if (sscanf(name, "r%d", &n) == 1) {
|
||||
this->r[n] = ParseInt64(value);
|
||||
} else {
|
||||
printf("Unrecognized register name: %s\n", name);
|
||||
}
|
||||
}
|
||||
|
||||
bool xe_ppc_state::CompareRegWithString(
|
||||
const char* name, const char* value,
|
||||
char* out_value, size_t out_value_size) {
|
||||
int n;
|
||||
if (sscanf(name, "r%d", &n) == 1) {
|
||||
uint64_t expected = ParseInt64(value);
|
||||
if (this->r[n] != expected) {
|
||||
xesnprintfa(out_value, out_value_size, "%016llX", this->r[n]);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
printf("Unrecognized register name: %s\n", name);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
169
src/xenia/cpu/ppc/state.h
Normal file
169
src/xenia/cpu/ppc/state.h
Normal file
@@ -0,0 +1,169 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_CPU_PPC_STATE_H_
|
||||
#define XENIA_CPU_PPC_STATE_H_
|
||||
|
||||
|
||||
/**
|
||||
* NOTE: this file is included by xethunk and as such should have a *MINIMAL*
|
||||
* set of dependencies!
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
#ifdef XE_THUNK
|
||||
#define XECACHEALIGN __attribute__ ((aligned(8)))
|
||||
#define XECACHEALIGN64 __attribute__ ((aligned(64)))
|
||||
#endif
|
||||
|
||||
|
||||
// namespace FPRF {
|
||||
// enum FPRF_e {
|
||||
// QUIET_NAN = 0x00088000,
|
||||
// NEG_INFINITY = 0x00090000,
|
||||
// NEG_NORMALIZED = 0x00010000,
|
||||
// NEG_DENORMALIZED = 0x00018000,
|
||||
// NEG_ZERO = 0x00048000,
|
||||
// POS_ZERO = 0x00040000,
|
||||
// POS_DENORMALIZED = 0x00028000,
|
||||
// POS_NORMALIZED = 0x00020000,
|
||||
// POS_INFINITY = 0x000A0000,
|
||||
// };
|
||||
// } // FPRF
|
||||
|
||||
|
||||
#define kXEPPCRegLR 0xFFFF0001
|
||||
#define kXEPPCRegCTR 0xFFFF0002
|
||||
|
||||
|
||||
typedef struct XECACHEALIGN xe_float4 {
|
||||
union {
|
||||
struct {
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
float w;
|
||||
};
|
||||
float f4[4];
|
||||
struct {
|
||||
uint64_t low;
|
||||
uint64_t high;
|
||||
};
|
||||
};
|
||||
} xe_float4_t;
|
||||
|
||||
|
||||
typedef struct XECACHEALIGN64 xe_ppc_state {
|
||||
uint32_t cia; // Current PC (CIA)
|
||||
uint32_t nia; // Next PC (NIA)
|
||||
uint64_t xer; // XER register
|
||||
uint64_t lr; // Link register
|
||||
uint64_t ctr; // Count register
|
||||
|
||||
uint64_t r[32]; // General purpose registers
|
||||
xe_float4_t v[128]; // VMX128 vector registers
|
||||
double f[32]; // Floating-point registers
|
||||
|
||||
union {
|
||||
uint32_t value;
|
||||
struct {
|
||||
uint8_t lt :1; // Negative (LT) - result is negative
|
||||
uint8_t gt :1; // Positive (GT) - result is positive (and not zero)
|
||||
uint8_t eq :1; // Zero (EQ) - result is zero or a stwcx/stdcx completed successfully
|
||||
uint8_t so :1; // Summary Overflow (SO) - copy of XER[SO]
|
||||
} cr0;
|
||||
struct {
|
||||
uint8_t fx :1; // FP exception summary - copy of FPSCR[FX]
|
||||
uint8_t fex :1; // FP enabled exception summary - copy of FPSCR[FEX]
|
||||
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;
|
||||
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 {
|
||||
uint32_t value;
|
||||
struct {
|
||||
uint8_t fx :1; // FP exception summary -- sticky
|
||||
uint8_t fex :1; // FP enabled exception summary
|
||||
uint8_t vx :1; // FP invalid operation exception summary
|
||||
uint8_t ox :1; // FP overflow exception -- sticky
|
||||
uint8_t ux :1; // FP underflow exception -- sticky
|
||||
uint8_t zx :1; // FP zero divide exception -- sticky
|
||||
uint8_t xx :1; // FP inexact exception -- sticky
|
||||
uint8_t vxsnan :1; // FP invalid op exception: SNaN -- sticky
|
||||
uint8_t vxisi :1; // FP invalid op exception: infinity - infinity -- sticky
|
||||
uint8_t vxidi :1; // FP invalid op exception: infinity / infinity -- sticky
|
||||
uint8_t vxzdz :1; // FP invalid op exception: 0 / 0 -- sticky
|
||||
uint8_t vximz :1; // FP invalid op exception: infinity * 0 -- sticky
|
||||
uint8_t vxvc :1; // FP invalid op exception: invalid compare -- sticky
|
||||
uint8_t fr :1; // FP fraction rounded
|
||||
uint8_t fi :1; // FP fraction inexact
|
||||
uint8_t fprf_c :1; // FP result class
|
||||
uint8_t fprf_lt :1; // FP result less than or negative (FL or <)
|
||||
uint8_t fprf_gt :1; // FP result greater than or positive (FG or >)
|
||||
uint8_t fprf_eq :1; // FP result equal or zero (FE or =)
|
||||
uint8_t fprf_un :1; // FP result unordered or NaN (FU or ?)
|
||||
uint8_t reserved :1;
|
||||
uint8_t vxsoft :1; // FP invalid op exception: software request -- sticky
|
||||
uint8_t vxsqrt :1; // FP invalid op exception: invalid sqrt -- sticky
|
||||
uint8_t vxcvi :1; // FP invalid op exception: invalid integer convert -- sticky
|
||||
uint8_t ve :1; // FP invalid op exception enable
|
||||
uint8_t oe :1; // IEEE floating-point overflow exception enable
|
||||
uint8_t ue :1; // IEEE floating-point underflow exception enable
|
||||
uint8_t ze :1; // IEEE floating-point zero divide exception enable
|
||||
uint8_t xe :1; // IEEE floating-point inexact exception enable
|
||||
uint8_t ni :1; // Floating-point non-IEEE mode
|
||||
uint8_t rn :2; // FP rounding control: 00 = nearest
|
||||
// 01 = toward zero
|
||||
// 10 = toward +infinity
|
||||
// 11 = toward -infinity
|
||||
} bits;
|
||||
} fpscr; // Floating-point status and control register
|
||||
|
||||
// uint32_t get_fprf() {
|
||||
// return fpscr.value & 0x000F8000;
|
||||
// }
|
||||
// void set_fprf(const uint32_t v) {
|
||||
// fpscr.value = (fpscr.value & ~0x000F8000) | v;
|
||||
// }
|
||||
|
||||
// Runtime-specific data pointer. Used on callbacks to get access to the
|
||||
// current runtime and its data.
|
||||
uint8_t* membase;
|
||||
void* processor;
|
||||
void* thread_state;
|
||||
void* runtime;
|
||||
|
||||
void SetRegFromString(const char* name, const char* value);
|
||||
bool CompareRegWithString(const char* name, const char* value,
|
||||
char* out_value, size_t out_value_size);
|
||||
} xe_ppc_state_t;
|
||||
|
||||
|
||||
#endif // XENIA_CPU_PPC_STATE_H_
|
||||
250
src/xenia/cpu/processor.cc
Normal file
250
src/xenia/cpu/processor.cc
Normal file
@@ -0,0 +1,250 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <xenia/cpu/processor.h>
|
||||
|
||||
#include <llvm/ExecutionEngine/ExecutionEngine.h>
|
||||
#include <llvm/ExecutionEngine/GenericValue.h>
|
||||
#include <llvm/ExecutionEngine/Interpreter.h>
|
||||
#include <llvm/ExecutionEngine/JIT.h>
|
||||
#include <llvm/IR/LLVMContext.h>
|
||||
#include <llvm/IR/Module.h>
|
||||
#include <llvm/Support/ManagedStatic.h>
|
||||
#include <llvm/Support/TargetSelect.h>
|
||||
|
||||
#include <xenia/cpu/codegen/emit.h>
|
||||
|
||||
|
||||
using namespace llvm;
|
||||
using namespace xe;
|
||||
using namespace xe::cpu;
|
||||
using namespace xe::kernel;
|
||||
|
||||
|
||||
namespace {
|
||||
void InitializeIfNeeded();
|
||||
void CleanupOnShutdown();
|
||||
|
||||
void InitializeIfNeeded() {
|
||||
static bool has_initialized = false;
|
||||
if (has_initialized) {
|
||||
return;
|
||||
}
|
||||
has_initialized = true;
|
||||
|
||||
// TODO(benvanik): only do this once
|
||||
LLVMLinkInInterpreter();
|
||||
LLVMLinkInJIT();
|
||||
InitializeNativeTarget();
|
||||
|
||||
llvm_start_multithreaded();
|
||||
|
||||
// TODO(benvanik): only do this once
|
||||
codegen::RegisterEmitCategoryALU();
|
||||
codegen::RegisterEmitCategoryControl();
|
||||
codegen::RegisterEmitCategoryFPU();
|
||||
codegen::RegisterEmitCategoryMemory();
|
||||
|
||||
atexit(CleanupOnShutdown);
|
||||
}
|
||||
|
||||
void CleanupOnShutdown() {
|
||||
llvm_shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Processor::Processor(xe_pal_ref pal, xe_memory_ref memory) {
|
||||
pal_ = xe_pal_retain(pal);
|
||||
memory_ = xe_memory_retain(memory);
|
||||
|
||||
InitializeIfNeeded();
|
||||
}
|
||||
|
||||
Processor::~Processor() {
|
||||
// Cleanup all modules.
|
||||
for (std::vector<ExecModule*>::iterator it = modules_.begin();
|
||||
it != modules_.end(); ++it) {
|
||||
delete *it;
|
||||
}
|
||||
|
||||
engine_.reset();
|
||||
|
||||
xe_memory_release(memory_);
|
||||
xe_pal_release(pal_);
|
||||
}
|
||||
|
||||
xe_pal_ref Processor::pal() {
|
||||
return xe_pal_retain(pal_);
|
||||
}
|
||||
|
||||
xe_memory_ref Processor::memory() {
|
||||
return xe_memory_retain(memory_);
|
||||
}
|
||||
|
||||
int Processor::Setup() {
|
||||
XEASSERTNULL(engine_);
|
||||
|
||||
dummy_context_ = auto_ptr<LLVMContext>(new LLVMContext());
|
||||
Module* dummy_module = new Module("dummy", *dummy_context_.get());
|
||||
|
||||
std::string error_message;
|
||||
|
||||
EngineBuilder builder(dummy_module);
|
||||
builder.setEngineKind(EngineKind::JIT);
|
||||
builder.setErrorStr(&error_message);
|
||||
builder.setOptLevel(CodeGenOpt::None);
|
||||
//builder.setOptLevel(CodeGenOpt::Aggressive);
|
||||
//builder.setTargetOptions();
|
||||
builder.setAllocateGVsWithCode(false);
|
||||
//builder.setUseMCJIT(true);
|
||||
|
||||
engine_ = shared_ptr<ExecutionEngine>(builder.create());
|
||||
if (!engine_) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Processor::LoadBinary(const xechar_t* path, uint32_t start_address,
|
||||
shared_ptr<ExportResolver> export_resolver) {
|
||||
ExecModule* exec_module = NULL;
|
||||
const xechar_t* name = xestrrchr(path, '/') + 1;
|
||||
|
||||
// TODO(benvanik): map file from filesystem
|
||||
xe_mmap_ref mmap = xe_mmap_open(pal_, kXEFileModeRead, path, 0, 0);
|
||||
if (!mmap) {
|
||||
return NULL;
|
||||
}
|
||||
void* addr = xe_mmap_get_addr(mmap);
|
||||
size_t length = xe_mmap_get_length(mmap);
|
||||
|
||||
int result_code = 1;
|
||||
|
||||
XEEXPECTZERO(xe_copy_memory(xe_memory_addr(memory_, start_address),
|
||||
xe_memory_get_length(memory_),
|
||||
addr, length));
|
||||
|
||||
// Prepare the module.
|
||||
char name_a[XE_MAX_PATH];
|
||||
XEEXPECTTRUE(xestrnarrow(name_a, XECOUNT(name_a), name));
|
||||
char path_a[XE_MAX_PATH];
|
||||
XEEXPECTTRUE(xestrnarrow(path_a, XECOUNT(path_a), path));
|
||||
|
||||
exec_module = new ExecModule(
|
||||
memory_, export_resolver, name_a, path_a, engine_);
|
||||
|
||||
if (exec_module->PrepareRawBinary(start_address, start_address + length)) {
|
||||
delete exec_module;
|
||||
return 1;
|
||||
}
|
||||
|
||||
exec_module->AddFunctionsToMap(all_fns_);
|
||||
modules_.push_back(exec_module);
|
||||
|
||||
exec_module->Dump();
|
||||
|
||||
result_code = 0;
|
||||
XECLEANUP:
|
||||
if (result_code) {
|
||||
delete exec_module;
|
||||
}
|
||||
xe_mmap_release(mmap);
|
||||
return result_code;
|
||||
}
|
||||
|
||||
int Processor::PrepareModule(const char* name, const char* path,
|
||||
xe_xex2_ref xex,
|
||||
shared_ptr<ExportResolver> export_resolver) {
|
||||
ExecModule* exec_module = new ExecModule(
|
||||
memory_, export_resolver, name, path,
|
||||
engine_);
|
||||
|
||||
if (exec_module->PrepareXex(xex)) {
|
||||
delete exec_module;
|
||||
return 1;
|
||||
}
|
||||
|
||||
exec_module->AddFunctionsToMap(all_fns_);
|
||||
modules_.push_back(exec_module);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t Processor::CreateCallback(void (*callback)(void* data), void* data) {
|
||||
// TODO(benvanik): implement callback creation.
|
||||
return 0;
|
||||
}
|
||||
|
||||
ThreadState* Processor::AllocThread(uint32_t stack_size,
|
||||
uint32_t thread_state_address) {
|
||||
ThreadState* thread_state = new ThreadState(
|
||||
this, stack_size, thread_state_address);
|
||||
return thread_state;
|
||||
}
|
||||
|
||||
void Processor::DeallocThread(ThreadState* thread_state) {
|
||||
delete thread_state;
|
||||
}
|
||||
|
||||
int Processor::Execute(ThreadState* thread_state, uint32_t address) {
|
||||
// Find the function to execute.
|
||||
Function* f = GetFunction(address);
|
||||
if (!f) {
|
||||
XELOGCPU(XT("Failed to find function %.8X to execute."), address);
|
||||
return 1;
|
||||
}
|
||||
|
||||
xe_ppc_state_t* ppc_state = thread_state->ppc_state();
|
||||
|
||||
// This could be set to anything to give us a unique identifier to track
|
||||
// re-entrancy/etc.
|
||||
uint32_t lr = 0xBEBEBEBE;
|
||||
|
||||
// Setup registers.
|
||||
ppc_state->lr = lr;
|
||||
|
||||
// Args:
|
||||
// - i8* state
|
||||
// - i64 lr
|
||||
std::vector<GenericValue> args;
|
||||
args.push_back(PTOGV(ppc_state));
|
||||
GenericValue lr_arg;
|
||||
lr_arg.IntVal = APInt(64, lr);
|
||||
args.push_back(lr_arg);
|
||||
GenericValue ret = engine_->runFunction(f, args);
|
||||
// return (uint32_t)ret.IntVal.getSExtValue();
|
||||
|
||||
// Faster, somewhat.
|
||||
// Messes with the stack in such a way as to cause Xcode to behave oddly.
|
||||
// typedef void (*fnptr)(xe_ppc_state_t*, uint64_t);
|
||||
// fnptr ptr = (fnptr)engine_->getPointerToFunction(f);
|
||||
// ptr(ppc_state, lr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t Processor::Execute(ThreadState* thread_state, uint32_t address,
|
||||
uint64_t arg0) {
|
||||
xe_ppc_state_t* ppc_state = thread_state->ppc_state();
|
||||
ppc_state->r[3] = arg0;
|
||||
if (Execute(thread_state, address)) {
|
||||
return 0xDEADBABE;
|
||||
}
|
||||
return ppc_state->r[3];
|
||||
}
|
||||
|
||||
Function* Processor::GetFunction(uint32_t address) {
|
||||
FunctionMap::iterator it = all_fns_.find(address);
|
||||
if (it != all_fns_.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
75
src/xenia/cpu/processor.h
Normal file
75
src/xenia/cpu/processor.h
Normal file
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_CPU_PROCESSOR_H_
|
||||
#define XENIA_CPU_PROCESSOR_H_
|
||||
|
||||
#include <xenia/core.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <xenia/cpu/exec_module.h>
|
||||
#include <xenia/cpu/thread_state.h>
|
||||
#include <xenia/kernel/export.h>
|
||||
#include <xenia/kernel/xex2.h>
|
||||
|
||||
|
||||
namespace llvm {
|
||||
class ExecutionEngine;
|
||||
class Function;
|
||||
}
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
|
||||
|
||||
class Processor {
|
||||
public:
|
||||
Processor(xe_pal_ref pal, xe_memory_ref memory);
|
||||
~Processor();
|
||||
|
||||
xe_pal_ref pal();
|
||||
xe_memory_ref memory();
|
||||
|
||||
int Setup();
|
||||
|
||||
int LoadBinary(const xechar_t* path, uint32_t start_address,
|
||||
shared_ptr<kernel::ExportResolver> export_resolver);
|
||||
|
||||
int PrepareModule(const char* name, const char* path, xe_xex2_ref xex,
|
||||
shared_ptr<kernel::ExportResolver> export_resolver);
|
||||
|
||||
uint32_t CreateCallback(void (*callback)(void* data), void* data);
|
||||
|
||||
ThreadState* AllocThread(uint32_t stack_size, uint32_t thread_state_address);
|
||||
void DeallocThread(ThreadState* thread_state);
|
||||
int Execute(ThreadState* thread_state, uint32_t address);
|
||||
uint64_t Execute(ThreadState* thread_state, uint32_t address, uint64_t arg0);
|
||||
|
||||
private:
|
||||
llvm::Function* GetFunction(uint32_t address);
|
||||
|
||||
xe_pal_ref pal_;
|
||||
xe_memory_ref memory_;
|
||||
shared_ptr<llvm::ExecutionEngine> engine_;
|
||||
|
||||
auto_ptr<llvm::LLVMContext> dummy_context_;
|
||||
|
||||
std::vector<ExecModule*> modules_;
|
||||
|
||||
FunctionMap all_fns_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_PROCESSOR_H_
|
||||
18
src/xenia/cpu/sdb.h
Normal file
18
src/xenia/cpu/sdb.h
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_CPU_SDB_H_
|
||||
#define XENIA_CPU_SDB_H_
|
||||
|
||||
#include <xenia/cpu/sdb/raw_symbol_database.h>
|
||||
#include <xenia/cpu/sdb/symbol.h>
|
||||
#include <xenia/cpu/sdb/symbol_database.h>
|
||||
#include <xenia/cpu/sdb/xex_symbol_database.h>
|
||||
|
||||
#endif // XENIA_CPU_SDB_H_
|
||||
41
src/xenia/cpu/sdb/raw_symbol_database.cc
Normal file
41
src/xenia/cpu/sdb/raw_symbol_database.cc
Normal file
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <xenia/cpu/sdb/raw_symbol_database.h>
|
||||
|
||||
#include <xenia/cpu/ppc/instr.h>
|
||||
|
||||
|
||||
using namespace std;
|
||||
using namespace xe;
|
||||
using namespace xe::cpu;
|
||||
using namespace xe::cpu::ppc;
|
||||
using namespace xe::cpu::sdb;
|
||||
using namespace xe::kernel;
|
||||
|
||||
|
||||
RawSymbolDatabase::RawSymbolDatabase(
|
||||
xe_memory_ref memory, ExportResolver* export_resolver,
|
||||
uint32_t start_address, uint32_t end_address) :
|
||||
SymbolDatabase(memory, export_resolver) {
|
||||
start_address_ = start_address;
|
||||
end_address_ = end_address;
|
||||
}
|
||||
|
||||
RawSymbolDatabase::~RawSymbolDatabase() {
|
||||
}
|
||||
|
||||
uint32_t RawSymbolDatabase::GetEntryPoint() {
|
||||
return start_address_;
|
||||
}
|
||||
|
||||
bool RawSymbolDatabase::IsValueInTextRange(uint32_t value) {
|
||||
return value >= start_address_ && value < end_address_;
|
||||
}
|
||||
|
||||
42
src/xenia/cpu/sdb/raw_symbol_database.h
Normal file
42
src/xenia/cpu/sdb/raw_symbol_database.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_CPU_SDB_RAW_SYMBOL_DATABASE_H_
|
||||
#define XENIA_CPU_SDB_RAW_SYMBOL_DATABASE_H_
|
||||
|
||||
#include <xenia/cpu/sdb/symbol_database.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace sdb {
|
||||
|
||||
|
||||
class RawSymbolDatabase : public SymbolDatabase {
|
||||
public:
|
||||
RawSymbolDatabase(xe_memory_ref memory,
|
||||
kernel::ExportResolver* export_resolver,
|
||||
uint32_t start_address, uint32_t end_address);
|
||||
virtual ~RawSymbolDatabase();
|
||||
|
||||
private:
|
||||
virtual uint32_t GetEntryPoint();
|
||||
virtual bool IsValueInTextRange(uint32_t value);
|
||||
|
||||
uint32_t start_address_;
|
||||
uint32_t end_address_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace sdb
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_SDB_RAW_SYMBOL_DATABASE_H_
|
||||
13
src/xenia/cpu/sdb/sources.gypi
Normal file
13
src/xenia/cpu/sdb/sources.gypi
Normal file
@@ -0,0 +1,13 @@
|
||||
# Copyright 2013 Ben Vanik. All Rights Reserved.
|
||||
{
|
||||
'sources': [
|
||||
'raw_symbol_database.cc',
|
||||
'raw_symbol_database.h',
|
||||
'symbol.cc',
|
||||
'symbol.h',
|
||||
'symbol_database.cc',
|
||||
'symbol_database.h',
|
||||
'xex_symbol_database.cc',
|
||||
'xex_symbol_database.h',
|
||||
]
|
||||
}
|
||||
122
src/xenia/cpu/sdb/symbol.cc
Normal file
122
src/xenia/cpu/sdb/symbol.cc
Normal file
@@ -0,0 +1,122 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <xenia/cpu/sdb/symbol.h>
|
||||
|
||||
#include <xenia/cpu/ppc/instr.h>
|
||||
|
||||
|
||||
using namespace std;
|
||||
using namespace xe;
|
||||
using namespace xe::cpu;
|
||||
using namespace xe::cpu::ppc;
|
||||
using namespace xe::cpu::sdb;
|
||||
using namespace xe::kernel;
|
||||
|
||||
|
||||
Symbol::Symbol(SymbolType type) :
|
||||
symbol_type(type),
|
||||
name_(NULL) {
|
||||
}
|
||||
|
||||
Symbol::~Symbol() {
|
||||
xe_free(name_);
|
||||
}
|
||||
|
||||
const char* Symbol::name() {
|
||||
return name_;
|
||||
}
|
||||
|
||||
void Symbol::set_name(const char* value) {
|
||||
if (name_ == value) {
|
||||
return;
|
||||
}
|
||||
if (name_) {
|
||||
xe_free(name_);
|
||||
}
|
||||
if (value) {
|
||||
name_ = xestrdupa(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
FunctionBlock::FunctionBlock() :
|
||||
start_address(0), end_address(0),
|
||||
outgoing_type(kTargetUnknown), outgoing_address(0),
|
||||
outgoing_function(0) {
|
||||
}
|
||||
|
||||
|
||||
FunctionSymbol::FunctionSymbol() :
|
||||
Symbol(Function),
|
||||
start_address(0), end_address(0),
|
||||
type(Unknown), flags(0),
|
||||
kernel_export(0), ee(0) {
|
||||
}
|
||||
|
||||
FunctionSymbol::~FunctionSymbol() {
|
||||
for (std::map<uint32_t, FunctionBlock*>::iterator it = blocks.begin();
|
||||
it != blocks.end(); ++it) {
|
||||
delete it->second;
|
||||
}
|
||||
}
|
||||
|
||||
FunctionBlock* FunctionSymbol::GetBlock(uint32_t address) {
|
||||
std::map<uint32_t, FunctionBlock*>::iterator it = blocks.find(address);
|
||||
if (it != blocks.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FunctionBlock* FunctionSymbol::SplitBlock(uint32_t address) {
|
||||
// Scan to find the block that contains the address.
|
||||
for (std::map<uint32_t, FunctionBlock*>::iterator it = blocks.begin();
|
||||
it != blocks.end(); ++it) {
|
||||
FunctionBlock* block = it->second;
|
||||
if (address == block->start_address) {
|
||||
// No need for a split.
|
||||
return block;
|
||||
} else if (address >= block->start_address &&
|
||||
address <= block->end_address + 4) {
|
||||
// Inside this block.
|
||||
// Since we know we are starting inside of the block we split downwards.
|
||||
FunctionBlock* new_block = new FunctionBlock();
|
||||
new_block->start_address = address;
|
||||
new_block->end_address = block->end_address;
|
||||
new_block->outgoing_type = block->outgoing_type;
|
||||
new_block->outgoing_address = block->outgoing_address;
|
||||
new_block->outgoing_block = block->outgoing_block;
|
||||
blocks.insert(std::pair<uint32_t, FunctionBlock*>(address, new_block));
|
||||
// Patch up old block.
|
||||
block->end_address = address - 4;
|
||||
block->outgoing_type = FunctionBlock::kTargetNone;
|
||||
block->outgoing_address = 0;
|
||||
block->outgoing_block = NULL;
|
||||
return new_block;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
VariableSymbol::VariableSymbol() :
|
||||
Symbol(Variable),
|
||||
address(0),
|
||||
kernel_export(0) {
|
||||
}
|
||||
|
||||
VariableSymbol::~VariableSymbol() {
|
||||
}
|
||||
|
||||
|
||||
ExceptionEntrySymbol::ExceptionEntrySymbol() :
|
||||
Symbol(ExceptionEntry),
|
||||
address(0), function(0) {
|
||||
}
|
||||
151
src/xenia/cpu/sdb/symbol.h
Normal file
151
src/xenia/cpu/sdb/symbol.h
Normal file
@@ -0,0 +1,151 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_CPU_SDB_SYMBOL_H_
|
||||
#define XENIA_CPU_SDB_SYMBOL_H_
|
||||
|
||||
#include <xenia/core.h>
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include <xenia/kernel/export.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace sdb {
|
||||
|
||||
|
||||
class FunctionSymbol;
|
||||
class VariableSymbol;
|
||||
|
||||
|
||||
class FunctionCall {
|
||||
public:
|
||||
uint32_t address;
|
||||
FunctionSymbol* source;
|
||||
FunctionSymbol* target;
|
||||
};
|
||||
|
||||
class VariableAccess {
|
||||
public:
|
||||
uint32_t address;
|
||||
FunctionSymbol* source;
|
||||
VariableSymbol* target;
|
||||
};
|
||||
|
||||
class Symbol {
|
||||
public:
|
||||
enum SymbolType {
|
||||
Function = 0,
|
||||
Variable = 1,
|
||||
ExceptionEntry = 2,
|
||||
};
|
||||
|
||||
virtual ~Symbol();
|
||||
|
||||
SymbolType symbol_type;
|
||||
|
||||
const char* name();
|
||||
void set_name(const char* value);
|
||||
|
||||
protected:
|
||||
Symbol(SymbolType type);
|
||||
|
||||
char* name_;
|
||||
};
|
||||
|
||||
class ExceptionEntrySymbol;
|
||||
|
||||
class FunctionBlock {
|
||||
public:
|
||||
enum TargetType {
|
||||
kTargetUnknown = 0,
|
||||
kTargetBlock = 1,
|
||||
kTargetFunction = 2,
|
||||
kTargetLR = 3,
|
||||
kTargetCTR = 4,
|
||||
kTargetNone = 5,
|
||||
};
|
||||
|
||||
FunctionBlock();
|
||||
|
||||
uint32_t start_address;
|
||||
uint32_t end_address;
|
||||
|
||||
std::vector<FunctionBlock*> incoming_blocks;
|
||||
|
||||
TargetType outgoing_type;
|
||||
uint32_t outgoing_address;
|
||||
union {
|
||||
FunctionSymbol* outgoing_function;
|
||||
FunctionBlock* outgoing_block;
|
||||
};
|
||||
};
|
||||
|
||||
class FunctionSymbol : public Symbol {
|
||||
public:
|
||||
enum FunctionType {
|
||||
Unknown = 0,
|
||||
Kernel = 1,
|
||||
User = 2,
|
||||
};
|
||||
enum Flags {
|
||||
kFlagSaveGprLr = 1 << 1,
|
||||
kFlagRestGprLr = 1 << 2,
|
||||
};
|
||||
|
||||
FunctionSymbol();
|
||||
virtual ~FunctionSymbol();
|
||||
|
||||
FunctionBlock* GetBlock(uint32_t address);
|
||||
FunctionBlock* SplitBlock(uint32_t address);
|
||||
|
||||
uint32_t start_address;
|
||||
uint32_t end_address;
|
||||
FunctionType type;
|
||||
uint32_t flags;
|
||||
|
||||
kernel::KernelExport* kernel_export;
|
||||
ExceptionEntrySymbol* ee;
|
||||
|
||||
std::vector<FunctionCall*> incoming_calls;
|
||||
std::vector<FunctionCall*> outgoing_calls;
|
||||
std::vector<VariableAccess*> variable_accesses;
|
||||
|
||||
std::map<uint32_t, FunctionBlock*> blocks;
|
||||
};
|
||||
|
||||
class VariableSymbol : public Symbol {
|
||||
public:
|
||||
VariableSymbol();
|
||||
virtual ~VariableSymbol();
|
||||
|
||||
uint32_t address;
|
||||
|
||||
kernel::KernelExport* kernel_export;
|
||||
};
|
||||
|
||||
class ExceptionEntrySymbol : public Symbol {
|
||||
public:
|
||||
ExceptionEntrySymbol();
|
||||
virtual ~ExceptionEntrySymbol() {}
|
||||
|
||||
uint32_t address;
|
||||
FunctionSymbol* function;
|
||||
};
|
||||
|
||||
|
||||
} // namespace sdb
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_SDB_SYMBOL_H_
|
||||
748
src/xenia/cpu/sdb/symbol_database.cc
Normal file
748
src/xenia/cpu/sdb/symbol_database.cc
Normal file
@@ -0,0 +1,748 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <xenia/cpu/sdb/symbol_database.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
#include <xenia/cpu/ppc/instr.h>
|
||||
|
||||
|
||||
using namespace std;
|
||||
using namespace xe;
|
||||
using namespace xe::cpu;
|
||||
using namespace xe::cpu::ppc;
|
||||
using namespace xe::cpu::sdb;
|
||||
using namespace xe::kernel;
|
||||
|
||||
|
||||
SymbolDatabase::SymbolDatabase(xe_memory_ref memory,
|
||||
ExportResolver* export_resolver) {
|
||||
memory_ = xe_memory_retain(memory);
|
||||
export_resolver_ = export_resolver;
|
||||
}
|
||||
|
||||
SymbolDatabase::~SymbolDatabase() {
|
||||
for (SymbolMap::iterator it = symbols_.begin(); it != symbols_.end(); ++it) {
|
||||
delete it->second;
|
||||
}
|
||||
|
||||
xe_memory_release(memory_);
|
||||
}
|
||||
|
||||
int SymbolDatabase::Analyze() {
|
||||
// Iteratively run passes over the db.
|
||||
// This uses a queue to do a breadth-first search of all accessible
|
||||
// functions. Callbacks and such likely won't be hit.
|
||||
|
||||
// Queue entry point of the application.
|
||||
FunctionSymbol* fn = GetOrInsertFunction(GetEntryPoint());
|
||||
fn->set_name("start");
|
||||
|
||||
// Keep pumping the queue until there's nothing left to do.
|
||||
FlushQueue();
|
||||
|
||||
// Do a pass over the functions to fill holes. A few times. Just to be safe.
|
||||
for (size_t n = 0; n < 4; n++) {
|
||||
if (!FillHoles()) {
|
||||
break;
|
||||
}
|
||||
FlushQueue();
|
||||
}
|
||||
|
||||
// Run a pass over all functions and link up their extended data.
|
||||
// This can only be performed after we have all functions and basic blocks.
|
||||
bool needs_another_pass = false;
|
||||
do {
|
||||
needs_another_pass = false;
|
||||
for (SymbolMap::iterator it = symbols_.begin(); it != symbols_.end();
|
||||
++it) {
|
||||
if (it->second->symbol_type == Symbol::Function) {
|
||||
if (fn->type == FunctionSymbol::Unknown) {
|
||||
XELOGE(XT("UNKNOWN FN %.8X"), fn->start_address);
|
||||
}
|
||||
if (CompleteFunctionGraph(static_cast<FunctionSymbol*>(it->second))) {
|
||||
needs_another_pass = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (needs_another_pass) {
|
||||
FlushQueue();
|
||||
}
|
||||
} while (needs_another_pass);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Symbol* SymbolDatabase::GetSymbol(uint32_t address) {
|
||||
SymbolMap::iterator i = symbols_.find(address);
|
||||
if (i != symbols_.end()) {
|
||||
return i->second;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ExceptionEntrySymbol* SymbolDatabase::GetOrInsertExceptionEntry(
|
||||
uint32_t address) {
|
||||
SymbolMap::iterator i = symbols_.find(address);
|
||||
if (i != symbols_.end() && i->second->symbol_type == Symbol::Function) {
|
||||
return static_cast<ExceptionEntrySymbol*>(i->second);
|
||||
}
|
||||
|
||||
ExceptionEntrySymbol* ee = new ExceptionEntrySymbol();
|
||||
ee->address = address;
|
||||
symbols_.insert(SymbolMap::value_type(address, ee));
|
||||
return ee;
|
||||
}
|
||||
|
||||
FunctionSymbol* SymbolDatabase::GetOrInsertFunction(uint32_t address) {
|
||||
FunctionSymbol* fn = GetFunction(address);
|
||||
if (fn) {
|
||||
return fn;
|
||||
}
|
||||
|
||||
// Ignore values outside of the .text range.
|
||||
if (!IsValueInTextRange(address)) {
|
||||
XELOGSDB(XT("Ignoring function outside of .text: %.8X"), address);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fn = new FunctionSymbol();
|
||||
fn->start_address = address;
|
||||
function_count_++;
|
||||
symbols_.insert(SymbolMap::value_type(address, fn));
|
||||
scan_queue_.push_back(fn);
|
||||
return fn;
|
||||
}
|
||||
|
||||
VariableSymbol* SymbolDatabase::GetOrInsertVariable(uint32_t address) {
|
||||
VariableSymbol* var = GetVariable(address);
|
||||
if (var) {
|
||||
return var;
|
||||
}
|
||||
|
||||
var = new VariableSymbol();
|
||||
var->address = address;
|
||||
variable_count_++;
|
||||
symbols_.insert(SymbolMap::value_type(address, var));
|
||||
return var;
|
||||
}
|
||||
|
||||
FunctionSymbol* SymbolDatabase::GetFunction(uint32_t address) {
|
||||
SymbolMap::iterator i = symbols_.find(address);
|
||||
if (i != symbols_.end() && i->second->symbol_type == Symbol::Function) {
|
||||
return static_cast<FunctionSymbol*>(i->second);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
VariableSymbol* SymbolDatabase::GetVariable(uint32_t address) {
|
||||
SymbolMap::iterator i = symbols_.find(address);
|
||||
if (i != symbols_.end() && i->second->symbol_type == Symbol::Variable) {
|
||||
return static_cast<VariableSymbol*>(i->second);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int SymbolDatabase::GetAllVariables(std::vector<VariableSymbol*>& variables) {
|
||||
for (SymbolMap::iterator it = symbols_.begin(); it != symbols_.end(); ++it) {
|
||||
if (it->second->symbol_type == Symbol::Variable) {
|
||||
variables.push_back(static_cast<VariableSymbol*>(it->second));
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SymbolDatabase::GetAllFunctions(vector<FunctionSymbol*>& functions) {
|
||||
for (SymbolMap::iterator it = symbols_.begin(); it != symbols_.end(); ++it) {
|
||||
if (it->second->symbol_type == Symbol::Function) {
|
||||
functions.push_back(static_cast<FunctionSymbol*>(it->second));
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SymbolDatabase::AnalyzeFunction(FunctionSymbol* fn) {
|
||||
// Ignore functions already analyzed.
|
||||
if (fn->blocks.size()) {
|
||||
return 0;
|
||||
}
|
||||
// Ignore kernel thunks.
|
||||
if (fn->type == FunctionSymbol::Kernel) {
|
||||
return 0;
|
||||
}
|
||||
// Ignore bad inserts?
|
||||
if (fn->start_address == fn->end_address) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// This is a simple basic block analyizer. It walks the start address to the
|
||||
// end address looking for branches. Each span of instructions between
|
||||
// branches is considered a basic block, and the blocks are linked up to
|
||||
// create a CFG for the function. When the last blr (that has no branches
|
||||
// to after it) is found the function is considered ended. If this is before
|
||||
// the expected end address then the function address range is split up and
|
||||
// the second half is treated as another function.
|
||||
|
||||
// TODO(benvanik): special branch checks:
|
||||
// bl to _XamLoaderTerminateTitle should be treated as b
|
||||
// bl to KeBugCheck should be treated as b, and b KeBugCheck should die
|
||||
|
||||
// TODO(benvanik): identify thunks:
|
||||
// These look like:
|
||||
// li r5, 0
|
||||
// [etc]
|
||||
// b some_function
|
||||
// Can probably be detected by lack of use of LR?
|
||||
|
||||
uint8_t* p = xe_memory_addr(memory_, 0);
|
||||
|
||||
if (XEGETUINT32LE(p + fn->start_address) == 0) {
|
||||
// Function starts with 0x00000000 - we want to skip this and split.
|
||||
symbols_.erase(fn->start_address);
|
||||
// Scan ahead until the first non-zero or the end of the valid range.
|
||||
size_t next_addr = fn->start_address + 4;
|
||||
while (true) {
|
||||
if (!IsValueInTextRange(next_addr)) {
|
||||
// Ran out of the range. Abort.
|
||||
delete fn;
|
||||
return 0;
|
||||
}
|
||||
if (XEGETUINT32LE(p + next_addr)) {
|
||||
// Not a zero, maybe valid!
|
||||
break;
|
||||
}
|
||||
next_addr += 4;
|
||||
}
|
||||
if (!GetFunction(next_addr + 4)) {
|
||||
fn->start_address = next_addr;
|
||||
symbols_.insert(SymbolMap::value_type(fn->start_address, fn));
|
||||
scan_queue_.push_back(fn);
|
||||
} else {
|
||||
delete fn;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
XELOGSDB(XT("Analyzing function %.8X..."), fn->start_address);
|
||||
|
||||
// Set a default name, if it hasn't been named already.
|
||||
if (!fn->name()) {
|
||||
char name[32];
|
||||
xesnprintfa(name, XECOUNT(name), "sub_%.8X", fn->start_address);
|
||||
fn->set_name(name);
|
||||
}
|
||||
|
||||
// Set type, if needed. We assume user if not set.
|
||||
if (fn->type == FunctionSymbol::Unknown) {
|
||||
fn->type = FunctionSymbol::User;
|
||||
}
|
||||
|
||||
InstrData i;
|
||||
FunctionBlock* block = NULL;
|
||||
uint32_t furthest_target = fn->start_address;
|
||||
uint32_t addr = fn->start_address;
|
||||
while (true) {
|
||||
i.code = XEGETUINT32BE(p + addr);
|
||||
i.type = ppc::GetInstrType(i.code);
|
||||
i.address = addr;
|
||||
|
||||
// If we fetched 0 assume that we somehow hit one of the awesome
|
||||
// 'no really we meant to end after that bl' functions.
|
||||
if (!i.code) {
|
||||
XELOGSDB(XT("function end %.8X (0x00000000 read)"), addr);
|
||||
break;
|
||||
}
|
||||
|
||||
// Create a new basic block, if needed.
|
||||
if (!block) {
|
||||
block = new FunctionBlock();
|
||||
block->start_address = addr;
|
||||
block->end_address = addr;
|
||||
fn->blocks.insert(std::pair<uint32_t, FunctionBlock*>(
|
||||
block->start_address, block));
|
||||
}
|
||||
|
||||
bool ends_block = false;
|
||||
bool ends_fn = false;
|
||||
if (!i.type) {
|
||||
// Invalid instruction.
|
||||
// We can just ignore it because there's (very little)/no chance it'll
|
||||
// affect flow control.
|
||||
XELOGSDB(XT("Invalid instruction at %.8X: %.8X"), addr, i.code);
|
||||
} else if (i.code == 0x4E800020) {
|
||||
// blr -- unconditional branch to LR.
|
||||
// This is generally a return.
|
||||
block->outgoing_type = FunctionBlock::kTargetLR;
|
||||
if (furthest_target > addr) {
|
||||
// Remaining targets within function, not end.
|
||||
XELOGSDB(XT("ignoring blr %.8X (branch to %.8X)"), addr,
|
||||
furthest_target);
|
||||
} else {
|
||||
// Function end point.
|
||||
XELOGSDB(XT("function end %.8X"), addr);
|
||||
ends_fn = true;
|
||||
}
|
||||
ends_block = true;
|
||||
} else if (i.code == 0x4E800420) {
|
||||
// bctr -- unconditional branch to CTR.
|
||||
// This is generally a jump to a function pointer (non-return).
|
||||
block->outgoing_type = FunctionBlock::kTargetCTR;
|
||||
if (furthest_target > addr) {
|
||||
// Remaining targets within function, not end.
|
||||
XELOGSDB(XT("ignoring bctr %.8X (branch to %.8X)"), addr,
|
||||
furthest_target);
|
||||
} else {
|
||||
// Function end point.
|
||||
XELOGSDB(XT("function end %.8X"), addr);
|
||||
ends_fn = true;
|
||||
}
|
||||
ends_block = true;
|
||||
} else if (i.type->opcode == 0x48000000) {
|
||||
// b/ba/bl/bla
|
||||
uint32_t target = XEEXTS26(i.I.LI << 2) + (i.I.AA ? 0 : (int32_t)addr);
|
||||
block->outgoing_address = target;
|
||||
|
||||
if (i.I.LK) {
|
||||
XELOGSDB(XT("bl %.8X -> %.8X"), addr, target);
|
||||
|
||||
// Queue call target if needed.
|
||||
GetOrInsertFunction(target);
|
||||
} else {
|
||||
XELOGSDB(XT("b %.8X -> %.8X"), addr, target);
|
||||
// If the target is back into the function and there's no further target
|
||||
// we are at the end of a function.
|
||||
if (target >= fn->start_address &&
|
||||
target < addr && furthest_target <= addr) {
|
||||
XELOGSDB(XT("function end %.8X (back b)"), addr);
|
||||
ends_fn = true;
|
||||
}
|
||||
|
||||
// If the target is a __restgprlr_* method it's the end of a function.
|
||||
// Note that sometimes functions stick this in a basic block *inside*
|
||||
// of the function somewhere, so ensure we don't have any branches over
|
||||
// it.
|
||||
if (!ends_fn &&
|
||||
furthest_target <= addr && IsRestGprLr(target)) {
|
||||
XELOGSDB(XT("function end %.8X (__restgprlr_*)"), addr);
|
||||
ends_fn = true;
|
||||
}
|
||||
|
||||
if (!ends_fn) {
|
||||
furthest_target = MAX(furthest_target, target);
|
||||
|
||||
// TODO(benvanik): perhaps queue up for a speculative check? I think
|
||||
// we are running over tail-call functions here that branch to
|
||||
// somewhere else.
|
||||
//GetOrInsertFunction(target);
|
||||
}
|
||||
}
|
||||
ends_block = true;
|
||||
} else if (i.type->opcode == 0x40000000) {
|
||||
// bc/bca/bcl/bcla
|
||||
uint32_t target = XEEXTS16(i.B.BD << 2) + (i.B.AA ? 0 : (int32_t)addr);
|
||||
block->outgoing_address = target;
|
||||
if (i.B.LK) {
|
||||
XELOGSDB(XT("bcl %.8X -> %.8X"), addr, target);
|
||||
|
||||
// Queue call target if needed.
|
||||
// TODO(benvanik): see if this is correct - not sure anyone makes
|
||||
// function calls with bcl.
|
||||
//GetOrInsertFunction(target);
|
||||
} else {
|
||||
XELOGSDB(XT("bc %.8X -> %.8X"), addr, target);
|
||||
|
||||
// TODO(benvanik): GetOrInsertFunction? it's likely a BB
|
||||
|
||||
furthest_target = MAX(furthest_target, target);
|
||||
}
|
||||
ends_block = true;
|
||||
} else if (i.type->opcode == 0x4C000020) {
|
||||
// bclr/bclrl
|
||||
block->outgoing_type = FunctionBlock::kTargetLR;
|
||||
if (i.XL.LK) {
|
||||
XELOGSDB(XT("bclrl %.8X"), addr);
|
||||
} else {
|
||||
XELOGSDB(XT("bclr %.8X"), addr);
|
||||
}
|
||||
ends_block = true;
|
||||
} else if (i.type->opcode == 0x4C000420) {
|
||||
// bcctr/bcctrl
|
||||
block->outgoing_type = FunctionBlock::kTargetCTR;
|
||||
if (i.XL.LK) {
|
||||
XELOGSDB(XT("bcctrl %.8X"), addr);
|
||||
} else {
|
||||
XELOGSDB(XT("bcctr %.8X"), addr);
|
||||
}
|
||||
ends_block = true;
|
||||
}
|
||||
|
||||
block->end_address = addr;
|
||||
if (ends_block) {
|
||||
// This instruction is the end of a basic block.
|
||||
// Finish up the one we are working on. The next loop around will create
|
||||
// a new one to scribble into.
|
||||
block = NULL;
|
||||
}
|
||||
|
||||
if (ends_fn) {
|
||||
break;
|
||||
}
|
||||
|
||||
addr += 4;
|
||||
if (fn->end_address && addr > fn->end_address) {
|
||||
// Hmm....
|
||||
XELOGSDB(XT("Ran over function bounds! %.8X-%.8X"),
|
||||
fn->start_address, fn->end_address);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (addr + 4 < fn->end_address) {
|
||||
// Ran under the expected value - since we probably got the initial bounds
|
||||
// from someplace valid (like method hints) this may indicate an error.
|
||||
// It's also possible that we guessed in hole-filling and there's another
|
||||
// function below this one.
|
||||
XELOGSDB(XT("Function ran under: %.8X-%.8X ended at %.8X"),
|
||||
fn->start_address, fn->end_address, addr + 4);
|
||||
}
|
||||
fn->end_address = addr;
|
||||
|
||||
// If there's spare bits at the end, split the function.
|
||||
// TODO(benvanik): splitting?
|
||||
|
||||
// TODO(benvanik): find and record stack information
|
||||
// - look for __savegprlr_* and __restgprlr_*
|
||||
// - if present, flag function as needing a stack
|
||||
// - record prolog/epilog lengths/stack size/etc
|
||||
|
||||
XELOGSDB(XT("Finished analyzing %.8X"), fn->start_address);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SymbolDatabase::CompleteFunctionGraph(FunctionSymbol* fn) {
|
||||
// Find variable accesses.
|
||||
// TODO(benvanik): data analysis to find variable accesses.
|
||||
|
||||
// A list of function targets that were undefined.
|
||||
// This will run another analysis pass and it'd be best to avoid this.
|
||||
std::vector<uint32_t> new_fns;
|
||||
|
||||
// For each basic block:
|
||||
// - find outgoing target block or function
|
||||
for (std::map<uint32_t, FunctionBlock*>::iterator it = fn->blocks.begin();
|
||||
it != fn->blocks.end(); ++it) {
|
||||
FunctionBlock* block = it->second;
|
||||
|
||||
// If we have some address try to see what it is.
|
||||
if (block->outgoing_address) {
|
||||
if (block->outgoing_address >= fn->start_address &&
|
||||
block->outgoing_address <= fn->end_address) {
|
||||
// Branch into a block in this function.
|
||||
block->outgoing_type = FunctionBlock::kTargetBlock;
|
||||
block->outgoing_block = fn->GetBlock(block->outgoing_address);
|
||||
if (!block->outgoing_block) {
|
||||
// Block target not found - we may need to split.
|
||||
block->outgoing_block = fn->SplitBlock(block->outgoing_address);
|
||||
}
|
||||
if (!block->outgoing_block) {
|
||||
XELOGE(XT("block target not found: %.8X"), block->outgoing_address);
|
||||
XEASSERTALWAYS();
|
||||
}
|
||||
} else {
|
||||
// Function call.
|
||||
block->outgoing_type = FunctionBlock::kTargetFunction;
|
||||
block->outgoing_function = GetFunction(block->outgoing_address);
|
||||
if (!block->outgoing_function) {
|
||||
XELOGE(XT("call target not found: %.8X -> %.8X"),
|
||||
block->end_address, block->outgoing_address);
|
||||
new_fns.push_back(block->outgoing_address);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (new_fns.size()) {
|
||||
XELOGW(XT("Repeat analysis required to find %d new functions"),
|
||||
(uint32_t)new_fns.size());
|
||||
for (std::vector<uint32_t>::iterator it = new_fns.begin();
|
||||
it != new_fns.end(); ++it) {
|
||||
GetOrInsertFunction(*it);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace {
|
||||
typedef struct {
|
||||
uint32_t start_address;
|
||||
uint32_t end_address;
|
||||
} HoleInfo;
|
||||
}
|
||||
|
||||
bool SymbolDatabase::FillHoles() {
|
||||
// If 4b, check if 0x00000000 and ignore (alignment padding)
|
||||
// If 8b, check if first value is within .text and ignore (EH entry)
|
||||
// Else, add to scan queue as function?
|
||||
|
||||
std::vector<HoleInfo> holes;
|
||||
std::vector<uint32_t> ees;
|
||||
|
||||
uint32_t previous = 0;
|
||||
for (SymbolMap::iterator it = symbols_.begin(); it != symbols_.end(); ++it) {
|
||||
switch (it->second->symbol_type) {
|
||||
case Symbol::Function:
|
||||
{
|
||||
FunctionSymbol* fn = static_cast<FunctionSymbol*>(it->second);
|
||||
if (previous && (int)(fn->start_address - previous) > 0) {
|
||||
// Hole!
|
||||
uint32_t* p = (uint32_t*)xe_memory_addr(memory_, previous);
|
||||
size_t hole_length = fn->start_address - previous;
|
||||
if (hole_length == 4) {
|
||||
// Likely a pointer or 0.
|
||||
if (*p == 0) {
|
||||
// Skip - just a zero.
|
||||
} else if (IsValueInTextRange(XEGETUINT32BE(p))) {
|
||||
// An address - probably an indirection data value.
|
||||
}
|
||||
} else if (hole_length == 8) {
|
||||
// Possibly an exception handler entry.
|
||||
// They look like [some value in .text] + [some pointer].
|
||||
if (*p == 0 || IsValueInTextRange(XEGETUINT32BE(p))) {
|
||||
// Skip!
|
||||
ees.push_back(previous);
|
||||
} else {
|
||||
// Probably legit.
|
||||
HoleInfo hole_info = {previous, fn->start_address};
|
||||
holes.push_back(hole_info);
|
||||
}
|
||||
} else {
|
||||
// Probably legit.
|
||||
HoleInfo hole_info = {previous, fn->start_address};
|
||||
holes.push_back(hole_info);
|
||||
}
|
||||
}
|
||||
previous = fn->end_address + 4;
|
||||
}
|
||||
break;
|
||||
case Symbol::Variable:
|
||||
case Symbol::ExceptionEntry:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (std::vector<uint32_t>::iterator it = ees.begin(); it != ees.end();
|
||||
++it) {
|
||||
ExceptionEntrySymbol* ee = GetOrInsertExceptionEntry(*it);
|
||||
ee->function = GetFunction(ee->address + 8);
|
||||
if (ee->function) {
|
||||
ee->function->ee = ee;
|
||||
}
|
||||
uint32_t* p = (uint32_t*)xe_memory_addr(memory_, ee->address);
|
||||
uint32_t handler_addr = XEGETUINT32BE(p);
|
||||
if (handler_addr) {
|
||||
GetOrInsertFunction(handler_addr);
|
||||
}
|
||||
uint32_t data_addr = XEGETUINT32BE(p + 1);
|
||||
if (data_addr) {
|
||||
VariableSymbol* var = GetOrInsertVariable(data_addr);
|
||||
char name[128];
|
||||
if (ee->function) {
|
||||
xesnprintfa(name, XECOUNT(name), "__ee_data_%s", ee->function->name());
|
||||
} else {
|
||||
xesnprintfa(name, XECOUNT(name), "__ee_data_%.8X", *it);
|
||||
}
|
||||
var->set_name(name);
|
||||
}
|
||||
}
|
||||
|
||||
bool any_functions_added = false;
|
||||
for (std::vector<HoleInfo>::iterator it = holes.begin(); it != holes.end();
|
||||
++it) {
|
||||
FunctionSymbol* fn = GetOrInsertFunction(it->start_address);
|
||||
if (!fn->end_address) {
|
||||
fn->end_address = it->end_address;
|
||||
any_functions_added = true;
|
||||
}
|
||||
}
|
||||
|
||||
return any_functions_added;
|
||||
}
|
||||
|
||||
int SymbolDatabase::FlushQueue() {
|
||||
while (scan_queue_.size()) {
|
||||
FunctionSymbol* fn = scan_queue_.front();
|
||||
scan_queue_.pop_front();
|
||||
if (AnalyzeFunction(fn)) {
|
||||
XELOGSDB(XT("Aborting analysis!"));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool SymbolDatabase::IsRestGprLr(uint32_t addr) {
|
||||
FunctionSymbol* fn = GetFunction(addr);
|
||||
return fn && (fn->flags & FunctionSymbol::kFlagRestGprLr);
|
||||
}
|
||||
|
||||
void SymbolDatabase::ReadMap(const char* file_name) {
|
||||
std::ifstream infile(file_name);
|
||||
|
||||
// Skip until ' Address'. Skip the next blank line.
|
||||
std::string line;
|
||||
while (std::getline(infile, line)) {
|
||||
if (line.find(" Address") == 0) {
|
||||
// Skip the next line.
|
||||
std::getline(infile, line);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::stringstream sstream;
|
||||
std::string ignore;
|
||||
std::string name;
|
||||
std::string addr_str;
|
||||
std::string type_str;
|
||||
while (std::getline(infile, line)) {
|
||||
// Remove newline.
|
||||
while (line.size() &&
|
||||
(line[line.size() - 1] == '\r' ||
|
||||
line[line.size() - 1] == '\n')) {
|
||||
line.erase(line.end() - 1);
|
||||
}
|
||||
|
||||
// End when we hit the first whitespace.
|
||||
if (line.size() == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Line is [ws][ignore][ws][name][ws][hex addr][ws][(f)][ws][library]
|
||||
sstream.clear();
|
||||
sstream.str(line);
|
||||
sstream >> std::ws;
|
||||
sstream >> ignore;
|
||||
sstream >> std::ws;
|
||||
sstream >> name;
|
||||
sstream >> std::ws;
|
||||
sstream >> addr_str;
|
||||
sstream >> std::ws;
|
||||
sstream >> type_str;
|
||||
|
||||
uint32_t addr = (uint32_t)strtol(addr_str.c_str(), NULL, 16);
|
||||
if (!addr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Symbol* symbol = GetSymbol(addr);
|
||||
if (symbol) {
|
||||
// Symbol found - set name.
|
||||
// We could check the type, but it's not needed.
|
||||
symbol->set_name(name.c_str());
|
||||
} else {
|
||||
if (type_str == "f") {
|
||||
// Function was not found via analysis.
|
||||
// We don't want to add it here as that would make us require maps to
|
||||
// get working.
|
||||
XELOGSDB(XT("MAP DIFF: function %.8X %s not found during analysis"),
|
||||
addr, name.c_str());
|
||||
} else {
|
||||
// Add a new variable.
|
||||
// This is just helpful, but changes no behavior.
|
||||
VariableSymbol* var = GetOrInsertVariable(addr);
|
||||
var->set_name(name.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SymbolDatabase::WriteMap(const char* file_name) {
|
||||
FILE* file = fopen(file_name, "wt");
|
||||
Dump(file);
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
void SymbolDatabase::Dump(FILE* file) {
|
||||
uint32_t previous = 0;
|
||||
for (SymbolMap::iterator it = symbols_.begin(); it != symbols_.end(); ++it) {
|
||||
switch (it->second->symbol_type) {
|
||||
case Symbol::Function:
|
||||
{
|
||||
FunctionSymbol* fn = static_cast<FunctionSymbol*>(it->second);
|
||||
if (previous && (int)(fn->start_address - previous) > 0) {
|
||||
if (fn->start_address - previous > 4 ||
|
||||
*((uint32_t*)xe_memory_addr(memory_, previous)) != 0) {
|
||||
fprintf(file, "%.8X-%.8X (%5d) h\n", previous, fn->start_address,
|
||||
fn->start_address - previous);
|
||||
}
|
||||
}
|
||||
fprintf(file, "%.8X-%.8X (%5d) f %s\n",
|
||||
fn->start_address,
|
||||
fn->end_address + 4,
|
||||
fn->end_address - fn->start_address + 4,
|
||||
fn->name() ? fn->name() : "<unknown>");
|
||||
previous = fn->end_address + 4;
|
||||
DumpFunctionBlocks(file, fn);
|
||||
}
|
||||
break;
|
||||
case Symbol::Variable:
|
||||
{
|
||||
VariableSymbol* var = static_cast<VariableSymbol*>(it->second);
|
||||
fprintf(file, "%.8X v %s\n", var->address,
|
||||
var->name() ? var->name() : "<unknown>");
|
||||
}
|
||||
break;
|
||||
case Symbol::ExceptionEntry:
|
||||
{
|
||||
ExceptionEntrySymbol* ee = static_cast<ExceptionEntrySymbol*>(
|
||||
it->second);
|
||||
fprintf(file, "%.8X-%.8X (%5d) e of %.8X\n",
|
||||
ee->address, ee->address + 8, 8,
|
||||
ee->function ? ee->function->start_address : 0);
|
||||
previous = ee->address + 8 + 4;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SymbolDatabase::DumpFunctionBlocks(FILE* file, FunctionSymbol* fn) {
|
||||
for (std::map<uint32_t, FunctionBlock*>::iterator it = fn->blocks.begin();
|
||||
it != fn->blocks.end(); ++it) {
|
||||
FunctionBlock* block = it->second;
|
||||
fprintf(file, " bb %.8X-%.8X",
|
||||
block->start_address, block->end_address + 4);
|
||||
switch (block->outgoing_type) {
|
||||
case FunctionBlock::kTargetUnknown:
|
||||
fprintf(file, " ?\n");
|
||||
break;
|
||||
case FunctionBlock::kTargetBlock:
|
||||
fprintf(file, " branch %.8X\n", block->outgoing_block->start_address);
|
||||
break;
|
||||
case FunctionBlock::kTargetFunction:
|
||||
fprintf(file, " call %.8X %s\n",
|
||||
block->outgoing_function->start_address,
|
||||
block->outgoing_function->name());
|
||||
break;
|
||||
case FunctionBlock::kTargetLR:
|
||||
fprintf(file, " branch lr\n");
|
||||
break;
|
||||
case FunctionBlock::kTargetCTR:
|
||||
fprintf(file, " branch ctr\n");
|
||||
break;
|
||||
case FunctionBlock::kTargetNone:
|
||||
fprintf(file, "\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
77
src/xenia/cpu/sdb/symbol_database.h
Normal file
77
src/xenia/cpu/sdb/symbol_database.h
Normal file
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_CPU_SDB_SYMBOL_DATABASE_H_
|
||||
#define XENIA_CPU_SDB_SYMBOL_DATABASE_H_
|
||||
|
||||
#include <xenia/core.h>
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include <xenia/kernel/export.h>
|
||||
#include <xenia/cpu/sdb/symbol.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace sdb {
|
||||
|
||||
|
||||
class SymbolDatabase {
|
||||
public:
|
||||
SymbolDatabase(xe_memory_ref memory, kernel::ExportResolver* export_resolver);
|
||||
virtual ~SymbolDatabase();
|
||||
|
||||
virtual int Analyze();
|
||||
|
||||
Symbol* GetSymbol(uint32_t address);
|
||||
ExceptionEntrySymbol* GetOrInsertExceptionEntry(uint32_t address);
|
||||
FunctionSymbol* GetOrInsertFunction(uint32_t address);
|
||||
VariableSymbol* GetOrInsertVariable(uint32_t address);
|
||||
FunctionSymbol* GetFunction(uint32_t address);
|
||||
VariableSymbol* GetVariable(uint32_t address);
|
||||
|
||||
int GetAllVariables(std::vector<VariableSymbol*>& variables);
|
||||
int GetAllFunctions(std::vector<FunctionSymbol*>& functions);
|
||||
|
||||
void ReadMap(const char* file_name);
|
||||
void WriteMap(const char* file_name);
|
||||
void Dump(FILE* file);
|
||||
void DumpFunctionBlocks(FILE* file, FunctionSymbol* fn);
|
||||
|
||||
protected:
|
||||
typedef std::map<uint32_t, Symbol*> SymbolMap;
|
||||
typedef std::list<FunctionSymbol*> FunctionList;
|
||||
|
||||
int AnalyzeFunction(FunctionSymbol* fn);
|
||||
int CompleteFunctionGraph(FunctionSymbol* fn);
|
||||
bool FillHoles();
|
||||
int FlushQueue();
|
||||
|
||||
bool IsRestGprLr(uint32_t addr);
|
||||
virtual uint32_t GetEntryPoint() = 0;
|
||||
virtual bool IsValueInTextRange(uint32_t value) = 0;
|
||||
|
||||
xe_memory_ref memory_;
|
||||
kernel::ExportResolver* export_resolver_;
|
||||
size_t function_count_;
|
||||
size_t variable_count_;
|
||||
SymbolMap symbols_;
|
||||
FunctionList scan_queue_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace sdb
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_SDB_SYMBOL_DATABASE_H_
|
||||
307
src/xenia/cpu/sdb/xex_symbol_database.cc
Normal file
307
src/xenia/cpu/sdb/xex_symbol_database.cc
Normal file
@@ -0,0 +1,307 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <xenia/cpu/sdb/xex_symbol_database.h>
|
||||
|
||||
#include <xenia/cpu/ppc/instr.h>
|
||||
|
||||
|
||||
using namespace std;
|
||||
using namespace xe;
|
||||
using namespace xe::cpu;
|
||||
using namespace xe::cpu::ppc;
|
||||
using namespace xe::cpu::sdb;
|
||||
using namespace xe::kernel;
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
|
||||
// IMAGE_CE_RUNTIME_FUNCTION_ENTRY
|
||||
// http://msdn.microsoft.com/en-us/library/ms879748.aspx
|
||||
typedef struct IMAGE_XBOX_RUNTIME_FUNCTION_ENTRY_t {
|
||||
uint32_t FuncStart; // Virtual address
|
||||
union {
|
||||
struct {
|
||||
uint32_t PrologLen : 8; // # of prolog instructions (size = x4)
|
||||
uint32_t FuncLen : 22; // # of instructions total (size = x4)
|
||||
uint32_t ThirtyTwoBit : 1; // Always 1
|
||||
uint32_t ExceptionFlag : 1; // 1 if PDATA_EH in .text -- unknown if used
|
||||
} Flags;
|
||||
uint32_t FlagsValue; // To make byte swapping easier
|
||||
};
|
||||
} IMAGE_XBOX_RUNTIME_FUNCTION_ENTRY;
|
||||
|
||||
|
||||
class PEMethodInfo {
|
||||
public:
|
||||
uint32_t address;
|
||||
size_t total_length; // in bytes
|
||||
size_t prolog_length; // in bytes
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
XexSymbolDatabase::XexSymbolDatabase(
|
||||
xe_memory_ref memory, ExportResolver* export_resolver, xe_xex2_ref xex) :
|
||||
SymbolDatabase(memory, export_resolver) {
|
||||
xex_ = xe_xex2_retain(xex);
|
||||
}
|
||||
|
||||
XexSymbolDatabase::~XexSymbolDatabase() {
|
||||
xe_xex2_release(xex_);
|
||||
}
|
||||
|
||||
int XexSymbolDatabase::Analyze() {
|
||||
const xe_xex2_header_t* header = xe_xex2_get_header(xex_);
|
||||
|
||||
// Find __savegprlr_* and __restgprlr_*.
|
||||
FindGplr();
|
||||
|
||||
// Add each import thunk.
|
||||
for (size_t n = 0; n < header->import_library_count; n++) {
|
||||
AddImports(&header->import_libraries[n]);
|
||||
}
|
||||
|
||||
// Add each export root.
|
||||
// TODO(benvanik): exports.
|
||||
// - insert fn or variable
|
||||
// - queue fn
|
||||
|
||||
// Add method hints, if available.
|
||||
// Not all XEXs have these.
|
||||
AddMethodHints();
|
||||
|
||||
return SymbolDatabase::Analyze();
|
||||
}
|
||||
|
||||
int XexSymbolDatabase::FindGplr() {
|
||||
// Special stack save/restore functions.
|
||||
// __savegprlr_14 to __savegprlr_31
|
||||
// __restgprlr_14 to __restgprlr_31
|
||||
// http://research.microsoft.com/en-us/um/redmond/projects/invisible/src/crt/md/ppc/xxx.s.htm
|
||||
// It'd be nice to stash these away and mark them as such to allow for
|
||||
// special codegen.
|
||||
static const uint32_t code_values[] = {
|
||||
0x68FFC1F9, // __savegprlr_14
|
||||
0x70FFE1F9, // __savegprlr_15
|
||||
0x78FF01FA, // __savegprlr_16
|
||||
0x80FF21FA, // __savegprlr_17
|
||||
0x88FF41FA, // __savegprlr_18
|
||||
0x90FF61FA, // __savegprlr_19
|
||||
0x98FF81FA, // __savegprlr_20
|
||||
0xA0FFA1FA, // __savegprlr_21
|
||||
0xA8FFC1FA, // __savegprlr_22
|
||||
0xB0FFE1FA, // __savegprlr_23
|
||||
0xB8FF01FB, // __savegprlr_24
|
||||
0xC0FF21FB, // __savegprlr_25
|
||||
0xC8FF41FB, // __savegprlr_26
|
||||
0xD0FF61FB, // __savegprlr_27
|
||||
0xD8FF81FB, // __savegprlr_28
|
||||
0xE0FFA1FB, // __savegprlr_29
|
||||
0xE8FFC1FB, // __savegprlr_30
|
||||
0xF0FFE1FB, // __savegprlr_31
|
||||
0xF8FF8191,
|
||||
0x2000804E,
|
||||
0x68FFC1E9, // __restgprlr_14
|
||||
0x70FFE1E9, // __restgprlr_15
|
||||
0x78FF01EA, // __restgprlr_16
|
||||
0x80FF21EA, // __restgprlr_17
|
||||
0x88FF41EA, // __restgprlr_18
|
||||
0x90FF61EA, // __restgprlr_19
|
||||
0x98FF81EA, // __restgprlr_20
|
||||
0xA0FFA1EA, // __restgprlr_21
|
||||
0xA8FFC1EA, // __restgprlr_22
|
||||
0xB0FFE1EA, // __restgprlr_23
|
||||
0xB8FF01EB, // __restgprlr_24
|
||||
0xC0FF21EB, // __restgprlr_25
|
||||
0xC8FF41EB, // __restgprlr_26
|
||||
0xD0FF61EB, // __restgprlr_27
|
||||
0xD8FF81EB, // __restgprlr_28
|
||||
0xE0FFA1EB, // __restgprlr_29
|
||||
0xE8FFC1EB, // __restgprlr_30
|
||||
0xF0FFE1EB, // __restgprlr_31
|
||||
0xF8FF8181,
|
||||
0xA603887D,
|
||||
0x2000804E,
|
||||
};
|
||||
|
||||
uint32_t gplr_start = 0;
|
||||
const xe_xex2_header_t* header = xe_xex2_get_header(xex_);
|
||||
for (size_t n = 0, i = 0; n < header->section_count; n++) {
|
||||
const xe_xex2_section_t* section = &header->sections[n];
|
||||
const size_t start_address =
|
||||
header->exe_address + (i * xe_xex2_section_length);
|
||||
const size_t end_address =
|
||||
start_address + (section->info.page_count * xe_xex2_section_length);
|
||||
if (section->info.type == XEX_SECTION_CODE) {
|
||||
gplr_start = xe_memory_search_aligned(
|
||||
memory_, start_address, end_address,
|
||||
code_values, XECOUNT(code_values));
|
||||
if (gplr_start) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
i += section->info.page_count;
|
||||
}
|
||||
if (!gplr_start) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Add function stubs.
|
||||
char name[32];
|
||||
uint32_t address = gplr_start;
|
||||
for (int n = 14; n <= 31; n++) {
|
||||
xesnprintfa(name, XECOUNT(name), "__savegprlr_%d", n);
|
||||
FunctionSymbol* fn = GetOrInsertFunction(address);
|
||||
fn->end_address = fn->start_address + (31 - n) * 4 + 2 * 4;
|
||||
fn->set_name(name);
|
||||
fn->type = FunctionSymbol::User;
|
||||
fn->flags |= FunctionSymbol::kFlagSaveGprLr;
|
||||
address += 4;
|
||||
}
|
||||
address = gplr_start + 20 * 4;
|
||||
for (int n = 14; n <= 31; n++) {
|
||||
xesnprintfa(name, XECOUNT(name), "__restgprlr_%d", n);
|
||||
FunctionSymbol* fn = GetOrInsertFunction(address);
|
||||
fn->end_address = fn->start_address + (31 - n) * 4 + 3 * 4;
|
||||
fn->set_name(name);
|
||||
fn->type = FunctionSymbol::User;
|
||||
fn->flags |= FunctionSymbol::kFlagRestGprLr;
|
||||
address += 4;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int XexSymbolDatabase::AddImports(const xe_xex2_import_library_t* library) {
|
||||
xe_xex2_import_info_t* import_infos;
|
||||
size_t import_info_count;
|
||||
if (xe_xex2_get_import_infos(xex_, library, &import_infos,
|
||||
&import_info_count)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
char name[128];
|
||||
for (size_t n = 0; n < import_info_count; n++) {
|
||||
const xe_xex2_import_info_t* info = &import_infos[n];
|
||||
|
||||
KernelExport* kernel_export = export_resolver_->GetExportByOrdinal(
|
||||
library->name, info->ordinal);
|
||||
|
||||
VariableSymbol* var = GetOrInsertVariable(info->value_address);
|
||||
if (kernel_export) {
|
||||
if (info->thunk_address) {
|
||||
xesnprintfa(name, XECOUNT(name), "__imp_%s", kernel_export->name);
|
||||
} else {
|
||||
xesnprintfa(name, XECOUNT(name), "%s", kernel_export->name);
|
||||
}
|
||||
} else {
|
||||
xesnprintfa(name, XECOUNT(name), "__imp_%s_%.3X", library->name,
|
||||
info->ordinal);
|
||||
}
|
||||
var->set_name(name);
|
||||
var->kernel_export = kernel_export;
|
||||
if (info->thunk_address) {
|
||||
FunctionSymbol* fn = GetOrInsertFunction(info->thunk_address);
|
||||
fn->end_address = fn->start_address + 16 - 4;
|
||||
fn->type = FunctionSymbol::Kernel;
|
||||
fn->kernel_export = kernel_export;
|
||||
if (kernel_export) {
|
||||
xesnprintfa(name, XECOUNT(name), "%s", kernel_export->name);
|
||||
} else {
|
||||
xesnprintfa(name, XECOUNT(name), "__kernel_%s_%.3X", library->name,
|
||||
info->ordinal);
|
||||
}
|
||||
fn->set_name(name);
|
||||
}
|
||||
}
|
||||
|
||||
xe_free(import_infos);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int XexSymbolDatabase::AddMethodHints() {
|
||||
uint8_t* mem = xe_memory_addr(memory_, 0);
|
||||
|
||||
const IMAGE_XBOX_RUNTIME_FUNCTION_ENTRY* entry = NULL;
|
||||
|
||||
// Find pdata, which contains the exception handling entries.
|
||||
const PESection* pdata = xe_xex2_get_pe_section(xex_, ".pdata");
|
||||
if (!pdata) {
|
||||
// No exception data to go on.
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Resolve.
|
||||
const uint8_t* p = mem + pdata->address;
|
||||
|
||||
// Entry count = pdata size / sizeof(entry).
|
||||
size_t entry_count = pdata->size / sizeof(IMAGE_XBOX_RUNTIME_FUNCTION_ENTRY);
|
||||
if (!entry_count) {
|
||||
// Empty?
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Allocate output.
|
||||
PEMethodInfo* method_infos = (PEMethodInfo*)xe_calloc(
|
||||
entry_count * sizeof(PEMethodInfo));
|
||||
if (!method_infos) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Parse entries.
|
||||
// NOTE: entries are in memory as big endian, so pull them out and swap the
|
||||
// values before using them.
|
||||
entry = (const IMAGE_XBOX_RUNTIME_FUNCTION_ENTRY*)p;
|
||||
IMAGE_XBOX_RUNTIME_FUNCTION_ENTRY temp_entry;
|
||||
for (size_t n = 0; n < entry_count; n++, entry++) {
|
||||
PEMethodInfo* method_info = &method_infos[n];
|
||||
method_info->address = XESWAP32BE(entry->FuncStart);
|
||||
|
||||
// The bitfield needs to be swapped by hand.
|
||||
temp_entry.FlagsValue = XESWAP32BE(entry->FlagsValue);
|
||||
method_info->total_length = temp_entry.Flags.FuncLen * 4;
|
||||
method_info->prolog_length = temp_entry.Flags.PrologLen * 4;
|
||||
}
|
||||
|
||||
for (size_t n = 0; n < entry_count; n++) {
|
||||
PEMethodInfo* method_info = &method_infos[n];
|
||||
FunctionSymbol* fn = GetOrInsertFunction(method_info->address);
|
||||
fn->end_address = method_info->address + method_info->total_length - 4;
|
||||
fn->type = FunctionSymbol::User;
|
||||
// TODO(benvanik): something with prolog_length?
|
||||
}
|
||||
|
||||
xe_free(method_infos);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t XexSymbolDatabase::GetEntryPoint() {
|
||||
const xe_xex2_header_t* header = xe_xex2_get_header(xex_);
|
||||
return header->exe_entry_point;
|
||||
};
|
||||
|
||||
bool XexSymbolDatabase::IsValueInTextRange(uint32_t value) {
|
||||
const xe_xex2_header_t* header = xe_xex2_get_header(xex_);
|
||||
for (size_t n = 0, i = 0; n < header->section_count; n++) {
|
||||
const xe_xex2_section_t* section = &header->sections[n];
|
||||
const size_t start_address =
|
||||
header->exe_address + (i * xe_xex2_section_length);
|
||||
const size_t end_address =
|
||||
start_address + (section->info.page_count * xe_xex2_section_length);
|
||||
if (value >= start_address && value < end_address) {
|
||||
return section->info.type == XEX_SECTION_CODE;
|
||||
}
|
||||
i += section->info.page_count;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
49
src/xenia/cpu/sdb/xex_symbol_database.h
Normal file
49
src/xenia/cpu/sdb/xex_symbol_database.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_CPU_SDB_XEX_SYMBOL_DATABASE_H_
|
||||
#define XENIA_CPU_SDB_XEX_SYMBOL_DATABASE_H_
|
||||
|
||||
#include <xenia/cpu/sdb/symbol_database.h>
|
||||
|
||||
#include <xenia/kernel/xex2.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace sdb {
|
||||
|
||||
|
||||
class XexSymbolDatabase : public SymbolDatabase {
|
||||
public:
|
||||
XexSymbolDatabase(xe_memory_ref memory,
|
||||
kernel::ExportResolver* export_resolver,
|
||||
xe_xex2_ref xex);
|
||||
virtual ~XexSymbolDatabase();
|
||||
|
||||
virtual int Analyze();
|
||||
|
||||
private:
|
||||
int FindGplr();
|
||||
int AddImports(const xe_xex2_import_library_t *library);
|
||||
int AddMethodHints();
|
||||
|
||||
virtual uint32_t GetEntryPoint();
|
||||
virtual bool IsValueInTextRange(uint32_t value);
|
||||
|
||||
xe_xex2_ref xex_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace sdb
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_SDB_XEX_SYMBOL_DATABASE_H_
|
||||
23
src/xenia/cpu/sources.gypi
Normal file
23
src/xenia/cpu/sources.gypi
Normal file
@@ -0,0 +1,23 @@
|
||||
# Copyright 2013 Ben Vanik. All Rights Reserved.
|
||||
{
|
||||
'sources': [
|
||||
'cpu-private.h',
|
||||
'cpu.cc',
|
||||
'cpu.h',
|
||||
'exec_module.cc',
|
||||
'exec_module.h',
|
||||
'llvm_exports.cc',
|
||||
'llvm_exports.h',
|
||||
'ppc.h',
|
||||
'processor.cc',
|
||||
'processor.h',
|
||||
'thread_state.cc',
|
||||
'thread_state.h',
|
||||
],
|
||||
|
||||
'includes': [
|
||||
'codegen/sources.gypi',
|
||||
'ppc/sources.gypi',
|
||||
'sdb/sources.gypi',
|
||||
],
|
||||
}
|
||||
47
src/xenia/cpu/thread_state.cc
Normal file
47
src/xenia/cpu/thread_state.cc
Normal file
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <xenia/cpu/thread_state.h>
|
||||
|
||||
#include <xenia/core/memory.h>
|
||||
#include <xenia/cpu/processor.h>
|
||||
|
||||
|
||||
using namespace xe;
|
||||
using namespace xe::cpu;
|
||||
|
||||
|
||||
ThreadState::ThreadState(
|
||||
Processor* processor,
|
||||
uint32_t stack_size, uint32_t thread_state_address) :
|
||||
stack_size_(stack_size), thread_state_address_(thread_state_address) {
|
||||
memory_ = processor->memory();
|
||||
|
||||
stack_address_ = xe_memory_heap_alloc(memory_, 0, stack_size, 0);
|
||||
|
||||
xe_zero_struct(&ppc_state_, sizeof(ppc_state_));
|
||||
|
||||
// Stash pointers to common structures that callbacks may need.
|
||||
ppc_state_.membase = xe_memory_addr(memory_, 0);
|
||||
ppc_state_.processor = processor;
|
||||
ppc_state_.thread_state = this;
|
||||
|
||||
// Set initial registers.
|
||||
ppc_state_.r[1] = stack_address_;
|
||||
ppc_state_.r[13] = thread_state_address_;
|
||||
}
|
||||
|
||||
ThreadState::~ThreadState() {
|
||||
xe_memory_heap_free(memory_, stack_address_, 0);
|
||||
xe_memory_release(memory_);
|
||||
}
|
||||
|
||||
xe_ppc_state_t* ThreadState::ppc_state() {
|
||||
return &ppc_state_;
|
||||
}
|
||||
49
src/xenia/cpu/thread_state.h
Normal file
49
src/xenia/cpu/thread_state.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_CPU_THREAD_STATE_H_
|
||||
#define XENIA_CPU_THREAD_STATE_H_
|
||||
|
||||
#include <xenia/core.h>
|
||||
|
||||
#include <xenia/cpu/ppc.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
|
||||
|
||||
class Processor;
|
||||
|
||||
|
||||
class ThreadState {
|
||||
public:
|
||||
ThreadState(Processor* processor,
|
||||
uint32_t stack_size, uint32_t thread_state_address);
|
||||
~ThreadState();
|
||||
|
||||
xe_ppc_state_t* ppc_state();
|
||||
|
||||
private:
|
||||
uint32_t stack_size_;
|
||||
uint32_t thread_state_address;
|
||||
xe_memory_ref memory_;
|
||||
|
||||
uint32_t stack_address_;
|
||||
uint32_t thread_state_address_;
|
||||
|
||||
xe_ppc_state_t ppc_state_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_THREAD_STATE_H_
|
||||
BIN
src/xenia/cpu/xethunk/xethunk.bc
Normal file
BIN
src/xenia/cpu/xethunk/xethunk.bc
Normal file
Binary file not shown.
39
src/xenia/cpu/xethunk/xethunk.c
Normal file
39
src/xenia/cpu/xethunk/xethunk.c
Normal file
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
/**
|
||||
* This file is compiled with clang to produce LLVM bitcode.
|
||||
* When the emulator goes to build a full module it then imports this code into
|
||||
* the generated module to provide globals/other shared values.
|
||||
*
|
||||
* Changes to this file require building a new version and checking it into the
|
||||
* repo on a machine that has clang.
|
||||
*
|
||||
* # rebuild the xethunk.bc/.ll files:
|
||||
* xb xethunk
|
||||
*/
|
||||
|
||||
// NOTE: only headers in this directory should be included.
|
||||
#include "xethunk.h"
|
||||
|
||||
|
||||
// Global memory base.
|
||||
// Dereference + PPC address to manipulate memory. Note that it's stored in
|
||||
// big-endian!
|
||||
extern char* xe_memory_base;
|
||||
|
||||
|
||||
int xe_module_init() {
|
||||
// TODO(benvanik): setup call table, etc?
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void xe_module_uninit() {
|
||||
}
|
||||
20
src/xenia/cpu/xethunk/xethunk.h
Normal file
20
src/xenia/cpu/xethunk/xethunk.h
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
/**
|
||||
* This file is shared between xethunk and the loader to pass structures
|
||||
* between the two. Since this file is compiled with the LLVM clang it cannot
|
||||
* include any other files.
|
||||
*/
|
||||
|
||||
#ifndef XENIA_CPU_XETHUNK_H_
|
||||
#define XENIA_CPU_XETHUNK_H_
|
||||
|
||||
|
||||
#endif // XENIA_CPU_XETHUNK_H_
|
||||
11
src/xenia/cpu/xethunk/xethunk.ll
Normal file
11
src/xenia/cpu/xethunk/xethunk.ll
Normal file
@@ -0,0 +1,11 @@
|
||||
; ModuleID = 'src/cpu/xethunk/xethunk.bc'
|
||||
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"
|
||||
target triple = "x86_64-apple-macosx10.8.0"
|
||||
|
||||
define i32 @xe_module_init() nounwind uwtable ssp {
|
||||
ret i32 0
|
||||
}
|
||||
|
||||
define void @xe_module_uninit() nounwind uwtable ssp {
|
||||
ret void
|
||||
}
|
||||
Reference in New Issue
Block a user