Initial Alloy implementation.
This is a regression in functionality and performance, but a much better foundation for the future of the project (I think). It can run basic apps under an SSA interpreter but doesn't support some of the features required to do real 360 apps yet.
This commit is contained in:
@@ -14,10 +14,9 @@ using namespace xe;
|
||||
using namespace xe::apu;
|
||||
|
||||
|
||||
AudioDriver::AudioDriver(xe_memory_ref memory) {
|
||||
memory_ = xe_memory_retain(memory);
|
||||
AudioDriver::AudioDriver(Memory* memory) :
|
||||
memory_(memory) {
|
||||
}
|
||||
|
||||
AudioDriver::~AudioDriver() {
|
||||
xe_memory_release(memory_);
|
||||
}
|
||||
|
||||
@@ -21,14 +21,14 @@ class AudioDriver {
|
||||
public:
|
||||
virtual ~AudioDriver();
|
||||
|
||||
xe_memory_ref memory() const { return memory_; }
|
||||
Memory* memory() const { return memory_; }
|
||||
|
||||
virtual void Initialize() = 0;
|
||||
|
||||
protected:
|
||||
AudioDriver(xe_memory_ref memory);
|
||||
AudioDriver(Memory* memory);
|
||||
|
||||
xe_memory_ref memory_;
|
||||
Memory* memory_;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -16,14 +16,12 @@
|
||||
|
||||
using namespace xe;
|
||||
using namespace xe::apu;
|
||||
using namespace xe::cpu::ppc;
|
||||
using namespace xe::cpu;
|
||||
|
||||
|
||||
AudioSystem::AudioSystem(Emulator* emulator) :
|
||||
emulator_(emulator),
|
||||
emulator_(emulator), memory_(emulator->memory()),
|
||||
thread_(0), running_(false), driver_(0) {
|
||||
memory_ = xe_memory_retain(emulator->memory());
|
||||
|
||||
// Create the run loop used for any windows/etc.
|
||||
// This must be done on the thread we create the driver.
|
||||
run_loop_ = xe_run_loop_create();
|
||||
@@ -35,7 +33,6 @@ AudioSystem::~AudioSystem() {
|
||||
xe_thread_release(thread_);
|
||||
|
||||
xe_run_loop_release(run_loop_);
|
||||
xe_memory_release(memory_);
|
||||
}
|
||||
|
||||
X_STATUS AudioSystem::Setup() {
|
||||
@@ -101,7 +98,7 @@ void AudioSystem::Initialize() {
|
||||
void AudioSystem::Shutdown() {
|
||||
}
|
||||
|
||||
bool AudioSystem::HandlesRegister(uint32_t addr) {
|
||||
bool AudioSystem::HandlesRegister(uint64_t addr) {
|
||||
return (addr & 0xFFFF0000) == 0x7FEA0000;
|
||||
}
|
||||
|
||||
@@ -109,7 +106,7 @@ bool AudioSystem::HandlesRegister(uint32_t addr) {
|
||||
// piece of hardware:
|
||||
// https://github.com/Free60Project/libxenon/blob/master/libxenon/drivers/xenon_sound/sound.c
|
||||
|
||||
uint64_t AudioSystem::ReadRegister(uint32_t addr) {
|
||||
uint64_t AudioSystem::ReadRegister(uint64_t addr) {
|
||||
uint32_t r = addr & 0xFFFF;
|
||||
XELOGAPU("ReadRegister(%.4X)", r);
|
||||
// 1800h is read on startup and stored -- context? buffers?
|
||||
@@ -117,7 +114,7 @@ uint64_t AudioSystem::ReadRegister(uint32_t addr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void AudioSystem::WriteRegister(uint32_t addr, uint64_t value) {
|
||||
void AudioSystem::WriteRegister(uint64_t addr, uint64_t value) {
|
||||
uint32_t r = addr & 0xFFFF;
|
||||
XELOGAPU("WriteRegister(%.4X, %.8X)", r, value);
|
||||
// 1804h is written to with 0x02000000 and 0x03000000 around a lock operation
|
||||
|
||||
@@ -29,14 +29,14 @@ public:
|
||||
virtual ~AudioSystem();
|
||||
|
||||
Emulator* emulator() const { return emulator_; }
|
||||
xe_memory_ref memory() const { return memory_; }
|
||||
Memory* memory() const { return memory_; }
|
||||
cpu::Processor* processor() const { return processor_; }
|
||||
|
||||
virtual X_STATUS Setup();
|
||||
|
||||
bool HandlesRegister(uint32_t addr);
|
||||
virtual uint64_t ReadRegister(uint32_t addr);
|
||||
virtual void WriteRegister(uint32_t addr, uint64_t value);
|
||||
bool HandlesRegister(uint64_t addr);
|
||||
virtual uint64_t ReadRegister(uint64_t addr);
|
||||
virtual void WriteRegister(uint64_t addr, uint64_t value);
|
||||
|
||||
protected:
|
||||
virtual void Initialize();
|
||||
@@ -49,13 +49,13 @@ private:
|
||||
}
|
||||
void ThreadStart();
|
||||
|
||||
static bool HandlesRegisterThunk(AudioSystem* as, uint32_t addr) {
|
||||
static bool HandlesRegisterThunk(AudioSystem* as, uint64_t addr) {
|
||||
return as->HandlesRegister(addr);
|
||||
}
|
||||
static uint64_t ReadRegisterThunk(AudioSystem* as, uint32_t addr) {
|
||||
static uint64_t ReadRegisterThunk(AudioSystem* as, uint64_t addr) {
|
||||
return as->ReadRegister(addr);
|
||||
}
|
||||
static void WriteRegisterThunk(AudioSystem* as, uint32_t addr,
|
||||
static void WriteRegisterThunk(AudioSystem* as, uint64_t addr,
|
||||
uint64_t value) {
|
||||
as->WriteRegister(addr, value);
|
||||
}
|
||||
@@ -64,7 +64,7 @@ protected:
|
||||
AudioSystem(Emulator* emulator);
|
||||
|
||||
Emulator* emulator_;
|
||||
xe_memory_ref memory_;
|
||||
Memory* memory_;
|
||||
cpu::Processor* processor_;
|
||||
|
||||
xe_run_loop_ref run_loop_;
|
||||
|
||||
@@ -17,7 +17,7 @@ using namespace xe::apu;
|
||||
using namespace xe::apu::nop;
|
||||
|
||||
|
||||
NopAudioDriver::NopAudioDriver(xe_memory_ref memory) :
|
||||
NopAudioDriver::NopAudioDriver(Memory* memory) :
|
||||
AudioDriver(memory) {
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace nop {
|
||||
|
||||
class NopAudioDriver : public AudioDriver {
|
||||
public:
|
||||
NopAudioDriver(xe_memory_ref memory);
|
||||
NopAudioDriver(Memory* memory);
|
||||
virtual ~NopAudioDriver();
|
||||
|
||||
virtual void Initialize();
|
||||
|
||||
@@ -12,10 +12,14 @@
|
||||
|
||||
#include <xenia/common.h>
|
||||
|
||||
#include <alloy/memory.h>
|
||||
namespace xe {
|
||||
using Memory = alloy::Memory;
|
||||
} // namespace xe
|
||||
|
||||
#include <xenia/core/crc32.h>
|
||||
#include <xenia/core/file.h>
|
||||
#include <xenia/core/hash.h>
|
||||
#include <xenia/memory.h>
|
||||
#include <xenia/core/mmap.h>
|
||||
#include <xenia/core/mutex.h>
|
||||
#include <xenia/core/pal.h>
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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_BACKEND_H_
|
||||
#define XENIA_CPU_BACKEND_H_
|
||||
|
||||
#include <xenia/common.h>
|
||||
|
||||
#include <xenia/memory.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
|
||||
|
||||
class JIT;
|
||||
|
||||
namespace sdb {
|
||||
class SymbolTable;
|
||||
} // namespace sdb
|
||||
|
||||
|
||||
class Backend {
|
||||
public:
|
||||
virtual ~Backend() {}
|
||||
|
||||
virtual JIT* CreateJIT(xe_memory_ref memory, sdb::SymbolTable* sym_table) = 0;
|
||||
|
||||
protected:
|
||||
Backend() {}
|
||||
};
|
||||
|
||||
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_BACKEND_H_
|
||||
@@ -11,8 +11,8 @@
|
||||
#define XENIA_CPU_CPU_H_
|
||||
|
||||
#include <xenia/cpu/processor.h>
|
||||
|
||||
// TODO(benvanik): conditionally include?
|
||||
#include <xenia/cpu/x64/x64_backend.h>
|
||||
#include <xenia/cpu/xenon_runtime.h>
|
||||
#include <xenia/cpu/xenon_thread_state.h>
|
||||
#include <xenia/cpu/xex_module.h>
|
||||
|
||||
#endif // XENIA_CPU_CPU_H_
|
||||
|
||||
@@ -1,160 +0,0 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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 <xenia/cpu/cpu-private.h>
|
||||
#include <xenia/cpu/sdb.h>
|
||||
|
||||
|
||||
using namespace xe;
|
||||
using namespace xe::cpu;
|
||||
using namespace xe::cpu::sdb;
|
||||
|
||||
|
||||
ExecModule::ExecModule(
|
||||
xe_memory_ref memory, ExportResolver* export_resolver,
|
||||
SymbolTable* sym_table,
|
||||
const char* module_name, const char* module_path) {
|
||||
memory_ = xe_memory_retain(memory);
|
||||
export_resolver_ = export_resolver;
|
||||
sym_table_ = sym_table;
|
||||
module_name_ = xestrdupa(module_name);
|
||||
module_path_ = xestrdupa(module_path);
|
||||
}
|
||||
|
||||
ExecModule::~ExecModule() {
|
||||
xe_free(module_path_);
|
||||
xe_free(module_name_);
|
||||
xe_memory_release(memory_);
|
||||
}
|
||||
|
||||
SymbolDatabase* ExecModule::sdb() {
|
||||
return sdb_.get();
|
||||
}
|
||||
|
||||
int ExecModule::PrepareRawBinary(uint32_t start_address, uint32_t end_address) {
|
||||
sdb_ = shared_ptr<sdb::SymbolDatabase>(
|
||||
new sdb::RawSymbolDatabase(memory_, export_resolver_,
|
||||
sym_table_, start_address, end_address));
|
||||
|
||||
code_addr_low_ = start_address;
|
||||
code_addr_high_ = end_address;
|
||||
|
||||
return Prepare();
|
||||
}
|
||||
|
||||
int ExecModule::PrepareXexModule(xe_xex2_ref xex) {
|
||||
sdb_ = shared_ptr<sdb::SymbolDatabase>(
|
||||
new sdb::XexSymbolDatabase(memory_, export_resolver_,
|
||||
sym_table_, xex));
|
||||
|
||||
code_addr_low_ = UINT_MAX;
|
||||
code_addr_high_ = 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) {
|
||||
code_addr_low_ = (uint32_t)MIN(code_addr_low_, start_address);
|
||||
code_addr_high_ = (uint32_t)MAX(code_addr_high_, end_address);
|
||||
}
|
||||
i += section->info.page_count;
|
||||
}
|
||||
|
||||
int result_code = Prepare();
|
||||
if (result_code) {
|
||||
return result_code;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ExecModule::Prepare() {
|
||||
int result_code = 1;
|
||||
char file_name[XE_MAX_PATH];
|
||||
|
||||
// Analyze the module and add its symbols to the symbol database.
|
||||
// This always happens, even if we have a cached copy of the library, as
|
||||
// we may end up needing this information later.
|
||||
// TODO(benvanik): see how much memory this is using - it may be worth
|
||||
// dropping and keeping around a smaller structure for future lookups
|
||||
// instead.
|
||||
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);
|
||||
}
|
||||
|
||||
// If recompiling, setup a recompiler and run.
|
||||
// Note that this will just load the library if it's present and valid.
|
||||
// TODO(benvanik): recompiler logic.
|
||||
|
||||
// Initialize the module.
|
||||
XEEXPECTZERO(Init());
|
||||
|
||||
result_code = 0;
|
||||
XECLEANUP:
|
||||
return result_code;
|
||||
}
|
||||
|
||||
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(0xD000BEEF | (kernel_export->ordinal & 0xFFF) << 16);
|
||||
XELOGCPU("WARNING: imported a variable with no value: %s",
|
||||
kernel_export->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
FunctionSymbol* ExecModule::FindFunctionSymbol(uint32_t address) {
|
||||
return sdb_->GetFunction(address, true);
|
||||
}
|
||||
|
||||
void ExecModule::Dump() {
|
||||
sdb_->Dump(stdout);
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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/export_resolver.h>
|
||||
#include <xenia/cpu/sdb.h>
|
||||
#include <xenia/kernel/xex2.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
|
||||
|
||||
class ExecModule {
|
||||
public:
|
||||
ExecModule(
|
||||
xe_memory_ref memory, ExportResolver* export_resolver,
|
||||
sdb::SymbolTable* sym_table,
|
||||
const char* module_name, const char* module_path);
|
||||
~ExecModule();
|
||||
|
||||
sdb::SymbolDatabase* sdb();
|
||||
|
||||
int PrepareRawBinary(uint32_t start_address, uint32_t end_address);
|
||||
int PrepareXexModule(xe_xex2_ref xex);
|
||||
|
||||
sdb::FunctionSymbol* FindFunctionSymbol(uint32_t address);
|
||||
|
||||
void Dump();
|
||||
|
||||
private:
|
||||
int Prepare();
|
||||
int Init();
|
||||
|
||||
xe_memory_ref memory_;
|
||||
ExportResolver* export_resolver_;
|
||||
sdb::SymbolTable* sym_table_;
|
||||
char* module_name_;
|
||||
char* module_path_;
|
||||
|
||||
shared_ptr<sdb::SymbolDatabase> sdb_;
|
||||
uint32_t code_addr_low_;
|
||||
uint32_t code_addr_high_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_USERMODULE_H_
|
||||
@@ -1,289 +0,0 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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/global_exports.h>
|
||||
|
||||
#include <xenia/logging.h>
|
||||
#include <xenia/cpu/cpu-private.h>
|
||||
#include <xenia/cpu/processor.h>
|
||||
#include <xenia/cpu/sdb.h>
|
||||
#include <xenia/cpu/ppc/instr.h>
|
||||
#include <xenia/cpu/ppc/state.h>
|
||||
#include <xenia/export_resolver.h>
|
||||
|
||||
|
||||
using namespace xe;
|
||||
using namespace xe::cpu;
|
||||
using namespace xe::cpu::sdb;
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
|
||||
void _cdecl XeTrap(
|
||||
xe_ppc_state_t* state, uint64_t cia) {
|
||||
XELOGE("TRAP");
|
||||
XEASSERTALWAYS();
|
||||
}
|
||||
|
||||
void* _cdecl XeIndirectBranch(
|
||||
xe_ppc_state_t* state, uint64_t target, uint64_t br_ia) {
|
||||
// TODO(benvanik): track this statistic - this path is very slow!
|
||||
Processor* processor = state->processor;
|
||||
void* target_ptr = processor->GetFunctionPointer((uint32_t)target);
|
||||
// target_ptr will be null when the given target is not a function.
|
||||
XEASSERTNOTNULL(target_ptr);
|
||||
return target_ptr;
|
||||
}
|
||||
|
||||
void _cdecl XeInvalidInstruction(
|
||||
xe_ppc_state_t* state, uint64_t cia, uint64_t data) {
|
||||
ppc::InstrData i;
|
||||
i.address = (uint32_t)cia;
|
||||
i.code = (uint32_t)data;
|
||||
i.type = ppc::GetInstrType(i.code);
|
||||
|
||||
if (!i.type) {
|
||||
XELOGCPU("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("INVALID INSTRUCTION %.8X: %.8X %s",
|
||||
i.address, i.code, disasm.c_str());
|
||||
} else {
|
||||
XELOGCPU("INVALID INSTRUCTION %.8X: %.8X %s",
|
||||
i.address, i.code, i.type->name);
|
||||
}
|
||||
}
|
||||
|
||||
void _cdecl XeAccessViolation(
|
||||
xe_ppc_state_t* state, uint64_t cia, uint64_t ea) {
|
||||
XELOGE("INVALID ACCESS %.8X: tried to touch %.8X",
|
||||
cia, (uint32_t)ea);
|
||||
XEASSERTALWAYS();
|
||||
}
|
||||
|
||||
void _cdecl XeTraceKernelCall(
|
||||
xe_ppc_state_t* state, uint64_t cia, uint64_t call_ia,
|
||||
KernelExport* kernel_export) {
|
||||
uint32_t thread_id = state->thread_state->thread_id();
|
||||
if (!((1ull << thread_id) & FLAGS_trace_thread_mask)) {
|
||||
return;
|
||||
}
|
||||
|
||||
xe_log_line("", thread_id, "XeTraceKernelCall", 't',
|
||||
"KERNEL CALL: %.8X -> k.%.8X (%s)%s",
|
||||
(uint32_t)call_ia - 4, (uint32_t)cia,
|
||||
kernel_export ? kernel_export->name : "unknown",
|
||||
(kernel_export ? kernel_export->is_implemented : 0) ?
|
||||
"" : " NOT IMPLEMENTED");
|
||||
}
|
||||
|
||||
void _cdecl XeTraceUserCall(
|
||||
xe_ppc_state_t* state, uint64_t cia, uint64_t call_ia,
|
||||
FunctionSymbol* fn) {
|
||||
uint32_t thread_id = state->thread_state->thread_id();
|
||||
if (!((1ull << thread_id) & FLAGS_trace_thread_mask)) {
|
||||
return;
|
||||
}
|
||||
|
||||
xe_log_line("", thread_id, "XeTraceUserCall", 't',
|
||||
"USER CALL %.8X -> u.%.8X (%s)",
|
||||
(uint32_t)call_ia - 4, (uint32_t)cia, fn->name());
|
||||
}
|
||||
|
||||
void _cdecl XeTraceBranch(
|
||||
xe_ppc_state_t* state, uint64_t cia, uint64_t target_ia) {
|
||||
uint32_t thread_id = state->thread_state->thread_id();
|
||||
if (!((1ull << thread_id) & FLAGS_trace_thread_mask)) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (target_ia) {
|
||||
case kXEPPCRegLR:
|
||||
target_ia = state->lr;
|
||||
break;
|
||||
case kXEPPCRegCTR:
|
||||
target_ia = state->ctr;
|
||||
break;
|
||||
}
|
||||
|
||||
xe_log_line("", thread_id, "XeTraceBranch", 't',
|
||||
"BRANCH %.8X -> b.%.8X",
|
||||
(uint32_t)cia, (uint32_t)target_ia);
|
||||
}
|
||||
|
||||
void _cdecl XeTraceFPR(
|
||||
xe_ppc_state_t* state, uint64_t fpr0, uint64_t fpr1, uint64_t fpr2,
|
||||
uint64_t fpr3, uint64_t fpr4) {
|
||||
char buffer[2048];
|
||||
buffer[0] = 0;
|
||||
int offset = 0;
|
||||
|
||||
uint32_t thread_id = state->thread_state->thread_id();
|
||||
if (!((1ull << thread_id) & FLAGS_trace_thread_mask)) {
|
||||
return;
|
||||
}
|
||||
|
||||
offset += xesnprintfa(buffer + offset, XECOUNT(buffer) - offset,
|
||||
"%.8X:", state->cia);
|
||||
|
||||
offset += xesnprintfa(buffer + offset, XECOUNT(buffer) - offset,
|
||||
"\nf%.2d = %e", fpr0, state->f[fpr0]);
|
||||
if (fpr1 != UINT_MAX) {
|
||||
offset += xesnprintfa(buffer + offset, XECOUNT(buffer) - offset,
|
||||
"\nf%.2d = %e", fpr1, state->f[fpr1]);
|
||||
}
|
||||
if (fpr2 != UINT_MAX) {
|
||||
offset += xesnprintfa(buffer + offset, XECOUNT(buffer) - offset,
|
||||
"\nf%.2d = %e", fpr2, state->f[fpr2]);
|
||||
}
|
||||
if (fpr3 != UINT_MAX) {
|
||||
offset += xesnprintfa(buffer + offset, XECOUNT(buffer) - offset,
|
||||
"\nf%.2d = %e", fpr3, state->f[fpr3]);
|
||||
}
|
||||
if (fpr4 != UINT_MAX) {
|
||||
offset += xesnprintfa(buffer + offset, XECOUNT(buffer) - offset,
|
||||
"\nf%.2d = %e", fpr4, state->f[fpr4]);
|
||||
}
|
||||
|
||||
xe_log_line("", thread_id, "XeTraceFPR", 't', buffer);
|
||||
}
|
||||
|
||||
void _cdecl XeTraceVR(
|
||||
xe_ppc_state_t* state, uint64_t vr0, uint64_t vr1, uint64_t vr2,
|
||||
uint64_t vr3, uint64_t vr4) {
|
||||
char buffer[2048];
|
||||
buffer[0] = 0;
|
||||
int offset = 0;
|
||||
|
||||
uint32_t thread_id = state->thread_state->thread_id();
|
||||
if (!((1ull << thread_id) & FLAGS_trace_thread_mask)) {
|
||||
return;
|
||||
}
|
||||
|
||||
offset += xesnprintfa(buffer + offset, XECOUNT(buffer) - offset,
|
||||
"%.8X:", state->cia);
|
||||
|
||||
offset += xesnprintfa(buffer + offset, XECOUNT(buffer) - offset,
|
||||
"\nvr%.3d=[%.8X, %.8X, %.8X, %.8X] [%e, %e, %e, %e]", vr0,
|
||||
state->v[vr0].ix, state->v[vr0].iy, state->v[vr0].iz, state->v[vr0].iw,
|
||||
state->v[vr0].x, state->v[vr0].y, state->v[vr0].z, state->v[vr0].w);
|
||||
if (vr1 != UINT_MAX) {
|
||||
offset += xesnprintfa(buffer + offset, XECOUNT(buffer) - offset,
|
||||
"\nvr%.3d=[%.8X, %.8X, %.8X, %.8X] [%e, %e, %e, %e]", vr1,
|
||||
state->v[vr1].ix, state->v[vr1].iy, state->v[vr1].iz, state->v[vr1].iw,
|
||||
state->v[vr1].x, state->v[vr1].y, state->v[vr1].z, state->v[vr1].w);
|
||||
}
|
||||
if (vr2 != UINT_MAX) {
|
||||
offset += xesnprintfa(buffer + offset, XECOUNT(buffer) - offset,
|
||||
"\nvr%.3d=[%.8X, %.8X, %.8X, %.8X] [%e, %e, %e, %e]", vr2,
|
||||
state->v[vr2].ix, state->v[vr2].iy, state->v[vr2].iz, state->v[vr2].iw,
|
||||
state->v[vr2].x, state->v[vr2].y, state->v[vr2].z, state->v[vr2].w);
|
||||
}
|
||||
if (vr3 != UINT_MAX) {
|
||||
offset += xesnprintfa(buffer + offset, XECOUNT(buffer) - offset,
|
||||
"\nvr%.3d=[%.8X, %.8X, %.8X, %.8X] [%e, %e, %e, %e]", vr3,
|
||||
state->v[vr3].ix, state->v[vr3].iy, state->v[vr3].iz, state->v[vr3].iw,
|
||||
state->v[vr3].x, state->v[vr3].y, state->v[vr3].z, state->v[vr3].w);
|
||||
}
|
||||
if (vr4 != UINT_MAX) {
|
||||
offset += xesnprintfa(buffer + offset, XECOUNT(buffer) - offset,
|
||||
"\nvr%.3d=[%.8X, %.8X, %.8X, %.8X] [%e, %e, %e, %e]", vr4,
|
||||
state->v[vr4].ix, state->v[vr4].iy, state->v[vr4].iz, state->v[vr4].iw,
|
||||
state->v[vr4].x, state->v[vr4].y, state->v[vr4].z, state->v[vr4].w);
|
||||
}
|
||||
|
||||
xe_log_line("", thread_id, "XeTraceVR", 't', buffer);
|
||||
}
|
||||
|
||||
void _cdecl XeTraceInstruction(
|
||||
xe_ppc_state_t* state, uint64_t cia, uint64_t data) {
|
||||
char buffer[2048];
|
||||
buffer[0] = 0;
|
||||
int offset = 0;
|
||||
|
||||
uint32_t thread_id = state->thread_state->thread_id();
|
||||
if (!((1ull << thread_id) & FLAGS_trace_thread_mask)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (FLAGS_trace_registers) {
|
||||
offset += xesnprintfa(buffer + offset, XECOUNT(buffer) - offset,
|
||||
"\n"
|
||||
" lr=%.16llX ctr=%.16llX cr=%.4X xer=%.16llX\n"
|
||||
" r0=%.16llX r1=%.16llX r2=%.16llX r3=%.16llX\n"
|
||||
" r4=%.16llX r5=%.16llX r6=%.16llX r7=%.16llX\n"
|
||||
" r8=%.16llX r9=%.16llX r10=%.16llX r11=%.16llX\n"
|
||||
"r12=%.16llX r13=%.16llX r14=%.16llX r15=%.16llX\n"
|
||||
"r16=%.16llX r17=%.16llX r18=%.16llX r19=%.16llX\n"
|
||||
"r20=%.16llX r21=%.16llX r22=%.16llX r23=%.16llX\n"
|
||||
"r24=%.16llX r25=%.16llX r26=%.16llX r27=%.16llX\n"
|
||||
"r28=%.16llX r29=%.16llX r30=%.16llX r31=%.16llX\n",
|
||||
state->lr, state->ctr, state->cr.value, state->xer,
|
||||
state->r[0], state->r[1], state->r[2], state->r[3],
|
||||
state->r[4], state->r[5], state->r[6], state->r[7],
|
||||
state->r[8], state->r[9], state->r[10], state->r[11],
|
||||
state->r[12], state->r[13], state->r[14], state->r[15],
|
||||
state->r[16], state->r[17], state->r[18], state->r[19],
|
||||
state->r[20], state->r[21], state->r[22], state->r[23],
|
||||
state->r[24], state->r[25], state->r[26], state->r[27],
|
||||
state->r[28], state->r[29], state->r[30], state->r[31]);
|
||||
}
|
||||
|
||||
ppc::InstrData i;
|
||||
i.address = (uint32_t)cia;
|
||||
i.code = (uint32_t)data;
|
||||
i.type = ppc::GetInstrType(i.code);
|
||||
if (i.type && i.type->disassemble) {
|
||||
ppc::InstrDisasm d;
|
||||
i.type->disassemble(i, d);
|
||||
std::string disasm;
|
||||
d.Dump(disasm);
|
||||
offset += xesnprintfa(buffer + offset, XECOUNT(buffer) - offset,
|
||||
"%.8X %.8X %s %s",
|
||||
i.address, i.code,
|
||||
i.type && i.type->emit ? " " : "X",
|
||||
disasm.c_str());
|
||||
} else {
|
||||
offset += xesnprintfa(buffer + offset, XECOUNT(buffer) - offset,
|
||||
"%.8X %.8X %s %s",
|
||||
i.address, i.code,
|
||||
i.type && i.type->emit ? " " : "X",
|
||||
i.type ? i.type->name : "<unknown>");
|
||||
}
|
||||
|
||||
xe_log_line("", thread_id, "XeTraceInstruction", 't', buffer);
|
||||
|
||||
// if (cia == 0x82012074) {
|
||||
// printf("BREAKBREAKBREAK\n");
|
||||
// }
|
||||
|
||||
// TODO(benvanik): better disassembly, printing of current register values/etc
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
void xe::cpu::GetGlobalExports(GlobalExports* global_exports) {
|
||||
global_exports->XeTrap = XeTrap;
|
||||
global_exports->XeIndirectBranch = XeIndirectBranch;
|
||||
global_exports->XeInvalidInstruction = XeInvalidInstruction;
|
||||
global_exports->XeAccessViolation = XeAccessViolation;
|
||||
global_exports->XeTraceKernelCall = XeTraceKernelCall;
|
||||
global_exports->XeTraceUserCall = XeTraceUserCall;
|
||||
global_exports->XeTraceBranch = XeTraceBranch;
|
||||
global_exports->XeTraceFPR = XeTraceFPR;
|
||||
global_exports->XeTraceVR = XeTraceVR;
|
||||
global_exports->XeTraceInstruction = XeTraceInstruction;
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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_GLOBAL_EXPORTS_H_
|
||||
#define XENIA_CPU_GLOBAL_EXPORTS_H_
|
||||
|
||||
#include <xenia/common.h>
|
||||
#include <xenia/core.h>
|
||||
|
||||
#include <xenia/export_resolver.h>
|
||||
#include <xenia/cpu/ppc/state.h>
|
||||
#include <xenia/cpu/sdb/symbol.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
|
||||
|
||||
typedef struct {
|
||||
void (_cdecl *XeTrap)(
|
||||
xe_ppc_state_t* state, uint64_t cia);
|
||||
void* (_cdecl *XeIndirectBranch)(
|
||||
xe_ppc_state_t* state, uint64_t target, uint64_t br_ia);
|
||||
void (_cdecl *XeInvalidInstruction)(
|
||||
xe_ppc_state_t* state, uint64_t cia, uint64_t data);
|
||||
void (_cdecl *XeAccessViolation)(
|
||||
xe_ppc_state_t* state, uint64_t cia, uint64_t ea);
|
||||
void (_cdecl *XeTraceKernelCall)(
|
||||
xe_ppc_state_t* state, uint64_t cia, uint64_t call_ia,
|
||||
KernelExport* kernel_export);
|
||||
void (_cdecl *XeTraceUserCall)(
|
||||
xe_ppc_state_t* state, uint64_t cia, uint64_t call_ia,
|
||||
sdb::FunctionSymbol* fn);
|
||||
void (_cdecl *XeTraceBranch)(
|
||||
xe_ppc_state_t* state, uint64_t cia, uint64_t target_ia);
|
||||
void (_cdecl *XeTraceFPR)(
|
||||
xe_ppc_state_t* state, uint64_t fpr0, uint64_t fpr1, uint64_t fpr2,
|
||||
uint64_t fpr3, uint64_t fpr4);
|
||||
void (_cdecl *XeTraceVR)(
|
||||
xe_ppc_state_t* state, uint64_t vr0, uint64_t vr1, uint64_t vr2,
|
||||
uint64_t vr3, uint64_t vr4);
|
||||
void (_cdecl *XeTraceInstruction)(
|
||||
xe_ppc_state_t* state, uint64_t cia, uint64_t data);
|
||||
} GlobalExports;
|
||||
|
||||
|
||||
void GetGlobalExports(GlobalExports* global_exports);
|
||||
|
||||
|
||||
} // cpu
|
||||
} // xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_GLOBAL_EXPORTS_H_
|
||||
@@ -1,58 +0,0 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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_JIT_H_
|
||||
#define XENIA_CPU_JIT_H_
|
||||
|
||||
#include <xenia/core.h>
|
||||
|
||||
#include <xenia/cpu/ppc.h>
|
||||
#include <xenia/cpu/sdb.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
|
||||
|
||||
class ExecModule;
|
||||
|
||||
|
||||
class JIT {
|
||||
public:
|
||||
virtual ~JIT() {
|
||||
xe_memory_release(memory_);
|
||||
}
|
||||
|
||||
virtual int Setup() = 0;
|
||||
virtual void AddRegisterAccessCallbacks(
|
||||
ppc::RegisterAccessCallbacks callbacks) = 0;
|
||||
|
||||
virtual int InitModule(ExecModule* module) = 0;
|
||||
virtual int UninitModule(ExecModule* module) = 0;
|
||||
|
||||
virtual void* GetFunctionPointer(sdb::FunctionSymbol* fn_symbol) = 0;
|
||||
virtual int Execute(xe_ppc_state_t* ppc_state,
|
||||
sdb::FunctionSymbol* fn_symbol) = 0;
|
||||
|
||||
protected:
|
||||
JIT(xe_memory_ref memory, sdb::SymbolTable* sym_table) {
|
||||
memory_ = xe_memory_retain(memory);
|
||||
sym_table_ = sym_table;
|
||||
}
|
||||
|
||||
xe_memory_ref memory_;
|
||||
sdb::SymbolTable* sym_table_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_JIT_H_
|
||||
@@ -1,19 +0,0 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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/registers.h>
|
||||
#include <xenia/cpu/ppc/state.h>
|
||||
|
||||
#endif // XENIA_CPU_PPC_H_
|
||||
@@ -1,42 +0,0 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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_DISASM_H_
|
||||
#define XENIA_CPU_PPC_DISASM_H_
|
||||
|
||||
#include <xenia/cpu/ppc/instr.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace ppc {
|
||||
|
||||
|
||||
void RegisterDisasmCategoryAltivec();
|
||||
void RegisterDisasmCategoryALU();
|
||||
void RegisterDisasmCategoryControl();
|
||||
void RegisterDisasmCategoryFPU();
|
||||
void RegisterDisasmCategoryMemory();
|
||||
|
||||
|
||||
#define XEDISASMR(name, opcode, format) int InstrDisasm_##name
|
||||
|
||||
#define XEREGISTERINSTR(name, opcode) \
|
||||
RegisterInstrDisassemble(opcode, (InstrDisassembleFn)InstrDisasm_##name);
|
||||
|
||||
#define XEINSTRNOTIMPLEMENTED()
|
||||
//#define XEINSTRNOTIMPLEMENTED XEASSERTALWAYS
|
||||
|
||||
|
||||
} // namespace ppc
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_PPC_DISASM_H_
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,728 +0,0 @@
|
||||
/*
|
||||
******************************************************************************
|
||||
* 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/disasm.h>
|
||||
|
||||
|
||||
using namespace xe::cpu::ppc;
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace ppc {
|
||||
|
||||
|
||||
// Integer arithmetic (A-3)
|
||||
|
||||
XEDISASMR(addx, 0x7C000214, XO )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("add", "Add",
|
||||
(i.XO.OE ? InstrDisasm::kOE : 0) | (i.XO.Rc ? InstrDisasm::kRc : 0));
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.XO.RT, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.XO.RA, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.XO.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(addcx, 0x7C000014, XO )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("addc", "Add Carrying",
|
||||
(i.XO.OE ? InstrDisasm::kOE : 0) | (i.XO.Rc ? InstrDisasm::kRc : 0) |
|
||||
InstrDisasm::kCA);
|
||||
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();
|
||||
}
|
||||
|
||||
XEDISASMR(addex, 0x7C000114, XO )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("adde", "Add Extended",
|
||||
(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();
|
||||
}
|
||||
|
||||
XEDISASMR(addi, 0x38000000, D )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("addi", "Add Immediate", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RT, InstrRegister::kWrite);
|
||||
if (i.D.RA) {
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RA, InstrRegister::kRead);
|
||||
} else {
|
||||
d.AddSImmOperand(0, 4);
|
||||
}
|
||||
d.AddSImmOperand(XEEXTS16(i.D.DS), 2);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(addic, 0x30000000, D )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("addic", "Add Immediate Carrying", InstrDisasm::kCA);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RT, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RA, InstrRegister::kRead);
|
||||
d.AddSImmOperand(XEEXTS16(i.D.DS), 2);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(addicx, 0x34000000, D )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("addic", "Add Immediate Carrying and Record",
|
||||
InstrDisasm::kRc | InstrDisasm::kCA);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RT, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RA, InstrRegister::kRead);
|
||||
d.AddSImmOperand(XEEXTS16(i.D.DS), 2);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(addis, 0x3C000000, D )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("addis", "Add Immediate Shifted", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RT, InstrRegister::kWrite);
|
||||
if (i.D.RA) {
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RA, InstrRegister::kRead);
|
||||
} else {
|
||||
d.AddSImmOperand(0, 4);
|
||||
}
|
||||
d.AddSImmOperand(XEEXTS16(i.D.DS), 2);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(addmex, 0x7C0001D4, XO )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("addme", "Add to Minus One Extended",
|
||||
(i.XO.OE ? InstrDisasm::kOE : 0) | (i.XO.Rc ? InstrDisasm::kRc : 0) |
|
||||
InstrDisasm::kCA);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.XO.RT, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.XO.RA, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(addzex, 0x7C000194, XO )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("addze", "Add to Zero Extended",
|
||||
(i.XO.OE ? InstrDisasm::kOE : 0) | (i.XO.Rc ? InstrDisasm::kRc : 0) |
|
||||
InstrDisasm::kCA);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.XO.RT, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.XO.RA, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(divdx, 0x7C0003D2, XO )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("divd", "Divide Doubleword",
|
||||
(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();
|
||||
}
|
||||
|
||||
XEDISASMR(divdux, 0x7C000392, XO )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("divdu", "Divide Doubleword Unsigned",
|
||||
(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();
|
||||
}
|
||||
|
||||
XEDISASMR(divwx, 0x7C0003D6, XO )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("divw", "Divide Word",
|
||||
(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();
|
||||
}
|
||||
|
||||
XEDISASMR(divwux, 0x7C000396, XO )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("divwu", "Divide Word Unsigned",
|
||||
(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();
|
||||
}
|
||||
|
||||
XEDISASMR(mulhdx, 0x7C000092, XO )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("mulhd", "Multiply High Doubleword", 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();
|
||||
}
|
||||
|
||||
XEDISASMR(mulhdux, 0x7C000012, XO )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("mulhdu", "Multiply High Doubleword Unsigned",
|
||||
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();
|
||||
}
|
||||
|
||||
XEDISASMR(mulhwx, 0x7C000096, XO )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("mulhw", "Multiply High Word", 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();
|
||||
}
|
||||
|
||||
XEDISASMR(mulhwux, 0x7C000016, XO )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("mulhwu", "Multiply High Word Unsigned",
|
||||
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();
|
||||
}
|
||||
|
||||
XEDISASMR(mulldx, 0x7C0001D2, XO )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("mulld", "Multiply Low Doubleword",
|
||||
(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();
|
||||
}
|
||||
|
||||
XEDISASMR(mulli, 0x1C000000, D )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("mulli", "Multiply Low Immediate", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RT, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RA, InstrRegister::kRead);
|
||||
d.AddSImmOperand(XEEXTS16(i.D.DS), 2);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(mullwx, 0x7C0001D6, XO )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("mullw", "Multiply Low Word",
|
||||
(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();
|
||||
}
|
||||
|
||||
XEDISASMR(negx, 0x7C0000D0, XO )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("neg", "Negate",
|
||||
(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);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(subfx, 0x7C000050, XO )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("subf", "Subtract From",
|
||||
(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();
|
||||
}
|
||||
|
||||
XEDISASMR(subfcx, 0x7C000010, XO )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("subfc", "Subtract From Carrying",
|
||||
(i.XO.OE ? InstrDisasm::kOE : 0) | (i.XO.Rc ? InstrDisasm::kRc : 0) |
|
||||
InstrDisasm::kCA);
|
||||
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();
|
||||
}
|
||||
|
||||
XEDISASMR(subficx, 0x20000000, D )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("subfic", "Subtract From Immediate Carrying", InstrDisasm::kCA);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RT, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RA, InstrRegister::kRead);
|
||||
d.AddSImmOperand(XEEXTS16(i.D.DS), 2);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(subfex, 0x7C000110, XO )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("subfe", "Subtract From Extended",
|
||||
(i.XO.OE ? InstrDisasm::kOE : 0) | (i.XO.Rc ? InstrDisasm::kRc : 0) |
|
||||
InstrDisasm::kCA);
|
||||
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();
|
||||
}
|
||||
|
||||
XEDISASMR(subfmex, 0x7C0001D0, XO )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("subfme", "Subtract From Minus One Extended",
|
||||
(i.XO.OE ? InstrDisasm::kOE : 0) | (i.XO.Rc ? InstrDisasm::kRc : 0) |
|
||||
InstrDisasm::kCA);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.XO.RT, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.XO.RA, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(subfzex, 0x7C000190, XO )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("subfze", "Subtract From Zero Extended",
|
||||
(i.XO.OE ? InstrDisasm::kOE : 0) | (i.XO.Rc ? InstrDisasm::kRc : 0) |
|
||||
InstrDisasm::kCA);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.XO.RT, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.XO.RA, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
|
||||
// Integer compare (A-4)
|
||||
|
||||
XEDISASMR(cmp, 0x7C000000, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("cmp", "Compare", 0);
|
||||
d.AddCR(i.X.RT >> 2, InstrRegister::kWrite);
|
||||
d.AddUImmOperand(i.X.RT >> 2, 1);
|
||||
d.AddUImmOperand(i.X.RT & 1, 1);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(cmpi, 0x2C000000, D )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("cmpi", "Compare Immediate", 0);
|
||||
d.AddCR(i.D.RT >> 2, InstrRegister::kWrite);
|
||||
d.AddUImmOperand(i.D.RT >> 2, 1);
|
||||
d.AddUImmOperand(i.D.RT & 1, 1);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RA, InstrRegister::kRead);
|
||||
d.AddSImmOperand(XEEXTS16(i.D.DS), 2);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(cmpl, 0x7C000040, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("cmpl", "Compare Logical", 0);
|
||||
d.AddCR(i.X.RT >> 2, InstrRegister::kWrite);
|
||||
d.AddUImmOperand(i.X.RT >> 2, 1);
|
||||
d.AddUImmOperand(i.X.RT & 1, 1);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(cmpli, 0x28000000, D )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("cmpli", "Compare Logical Immediate", 0);
|
||||
d.AddCR(i.D.RT >> 2, InstrRegister::kWrite);
|
||||
d.AddUImmOperand(i.D.RT >> 2, 1);
|
||||
d.AddUImmOperand(i.D.RT & 1, 1);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RA, InstrRegister::kRead);
|
||||
d.AddSImmOperand(i.D.DS, 2);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
|
||||
// Integer logical (A-5)
|
||||
|
||||
XEDISASMR(andx, 0x7C000038, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("and", "AND", i.X.Rc ? InstrDisasm::kRc : 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RT, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(andcx, 0x7C000078, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("andc", "AND with Complement", i.X.Rc ? InstrDisasm::kRc : 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RT, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(andix, 0x70000000, D )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("andi", "AND Immediate", 0);
|
||||
d.AddCR(0, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RA, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RT, InstrRegister::kRead);
|
||||
d.AddUImmOperand(i.D.DS, 2);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(andisx, 0x74000000, D )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("andi", "AND Immediate Shifted", 0);
|
||||
d.AddCR(0, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RA, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RT, InstrRegister::kRead);
|
||||
d.AddUImmOperand(i.D.DS, 2);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(cntlzdx, 0x7C000074, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("cntlzd", "Count Leading Zeros Doubleword",
|
||||
i.X.Rc ? InstrDisasm::kRc : 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RT, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(cntlzwx, 0x7C000034, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("cntlzw", "Count Leading Zeros Word",
|
||||
i.X.Rc ? InstrDisasm::kRc : 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RT, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(eqvx, 0x7C000238, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("eqv", "Equivalent", i.X.Rc ? InstrDisasm::kRc : 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RT, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(extsbx, 0x7C000774, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("extsb", "Extend Sign Byte", i.X.Rc ? InstrDisasm::kRc : 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RT, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(extshx, 0x7C000734, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("extsh", "Extend Sign Halfword", i.X.Rc ? InstrDisasm::kRc : 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RT, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(extswx, 0x7C0007B4, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("extsw", "Extend Sign Word", i.X.Rc ? InstrDisasm::kRc : 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RT, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(nandx, 0x7C0003B8, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("nand", "NAND", i.X.Rc ? InstrDisasm::kRc : 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RT, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(norx, 0x7C0000F8, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("nor", "NOR", i.X.Rc ? InstrDisasm::kRc : 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RT, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(orx, 0x7C000378, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("or", "OR", i.X.Rc ? InstrDisasm::kRc : 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RT, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(orcx, 0x7C000338, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("orc", "OR with Complement", i.X.Rc ? InstrDisasm::kRc : 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RT, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(ori, 0x60000000, D )(InstrData& i, InstrDisasm& d) {
|
||||
if (!i.D.RA && !i.D.RT && !i.D.DS) {
|
||||
d.Init("no-op", "OR Immediate", 0);
|
||||
} else {
|
||||
d.Init("ori", "OR Immediate", 0);
|
||||
}
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RA, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RT, InstrRegister::kRead);
|
||||
d.AddUImmOperand(i.D.DS, 2);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(oris, 0x64000000, D )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("oris", "OR Immediate Shifted", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RA, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RT, InstrRegister::kRead);
|
||||
d.AddUImmOperand(i.D.DS, 2);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(xorx, 0x7C000278, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("xor", "XOR", i.X.Rc ? InstrDisasm::kRc : 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RT, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(xori, 0x68000000, D )(InstrData& i, InstrDisasm& d) {
|
||||
if (!i.D.RA && !i.D.RT && !i.D.DS) {
|
||||
d.Init("xnop", "XOR Immediate", 0);
|
||||
} else {
|
||||
d.Init("xori", "XOR Immediate", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RA, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RT, InstrRegister::kRead);
|
||||
d.AddUImmOperand(i.D.DS, 2);
|
||||
}
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(xoris, 0x6C000000, D )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("xoris", "XOR Immediate Shifted", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RA, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RT, InstrRegister::kRead);
|
||||
d.AddUImmOperand(i.D.DS, 2);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
|
||||
// Integer rotate (A-6)
|
||||
|
||||
XEDISASMR(rld, 0x78000000, MDS)(InstrData& i, InstrDisasm& d) {
|
||||
if (i.MD.idx == 0) {
|
||||
// XEDISASMR(rldiclx, 0x78000000, MD )
|
||||
d.Init("rldicl", "Rotate Left Doubleword Immediate then Clear Left",
|
||||
i.MD.Rc ? InstrDisasm::kRc : 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.MD.RA, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.MD.RT, InstrRegister::kRead);
|
||||
d.AddUImmOperand((i.MD.SH5 << 5) | i.MD.SH, 1);
|
||||
d.AddUImmOperand((i.MD.MB5 << 5) | i.MD.MB, 1);
|
||||
return d.Finish();
|
||||
} else if (i.MD.idx == 1) {
|
||||
// XEDISASMR(rldicrx, 0x78000004, MD )
|
||||
d.Init("rldicr", "Rotate Left Doubleword Immediate then Clear Right",
|
||||
i.MD.Rc ? InstrDisasm::kRc : 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.MD.RA, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.MD.RT, InstrRegister::kRead);
|
||||
d.AddUImmOperand((i.MD.SH5 << 5) | i.MD.SH, 1);
|
||||
d.AddUImmOperand((i.MD.MB5 << 5) | i.MD.MB, 1);
|
||||
return d.Finish();
|
||||
} else if (i.MD.idx == 2) {
|
||||
// XEDISASMR(rldicx, 0x78000008, MD )
|
||||
const char* name;
|
||||
const char* desc;
|
||||
uint32_t sh = (i.MD.SH5 << 5) | i.MD.SH;
|
||||
uint32_t mb = (i.MD.MB5 << 5) | i.MD.MB;
|
||||
if (mb == 0x3E) {
|
||||
name = "sldi";
|
||||
desc = "Shift Left Immediate";
|
||||
} else {
|
||||
name = "rldic";
|
||||
desc = "Rotate Left Doubleword Immediate then Clear";
|
||||
}
|
||||
d.Init(name, desc,
|
||||
i.MD.Rc ? InstrDisasm::kRc : 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.MD.RA, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.MD.RT, InstrRegister::kRead);
|
||||
d.AddUImmOperand(sh, 1);
|
||||
d.AddUImmOperand(mb, 1);
|
||||
return d.Finish();
|
||||
} else if (i.MDS.idx == 8) {
|
||||
// XEDISASMR(rldclx, 0x78000010, MDS)
|
||||
d.Init("rldcl", "Rotate Left Doubleword then Clear Left",
|
||||
i.MDS.Rc ? InstrDisasm::kRc : 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.MDS.RA, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.MDS.RT, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.MDS.RB, InstrRegister::kRead);
|
||||
d.AddUImmOperand((i.MDS.MB5 << 5) | i.MDS.MB, 1);
|
||||
return d.Finish();
|
||||
} else if (i.MDS.idx == 9) {
|
||||
// XEDISASMR(rldcrx, 0x78000012, MDS)
|
||||
d.Init("rldcr", "Rotate Left Doubleword then Clear Right",
|
||||
i.MDS.Rc ? InstrDisasm::kRc : 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.MDS.RA, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.MDS.RT, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.MDS.RB, InstrRegister::kRead);
|
||||
d.AddUImmOperand((i.MDS.MB5 << 5) | i.MDS.MB, 1);
|
||||
return d.Finish();
|
||||
} else if (i.MD.idx == 3) {
|
||||
// XEDISASMR(rldimix, 0x7800000C, MD )
|
||||
d.Init("rldimi", "Rotate Left Doubleword Immediate then Mask Insert",
|
||||
i.MD.Rc ? InstrDisasm::kRc : 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.MD.RA, InstrRegister::kReadWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.MD.RT, InstrRegister::kRead);
|
||||
d.AddUImmOperand((i.MD.SH5 << 5) | i.MD.SH, 1);
|
||||
d.AddUImmOperand((i.MD.MB5 << 5) | i.MD.MB, 1);
|
||||
return d.Finish();
|
||||
} else {
|
||||
XEASSERTALWAYS();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
XEDISASMR(rlwimix, 0x50000000, M )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("rlwimi", "Rotate Left Word Immediate then Mask Insert",
|
||||
i.M.Rc ? InstrDisasm::kRc : 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.M.RA, InstrRegister::kReadWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.M.RT, InstrRegister::kRead);
|
||||
d.AddUImmOperand(i.M.SH, 1);
|
||||
d.AddUImmOperand(i.M.MB, 1);
|
||||
d.AddUImmOperand(i.M.ME, 1);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(rlwinmx, 0x54000000, M )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("rlwinm", "Rotate Left Word Immediate then AND with Mask",
|
||||
i.M.Rc ? InstrDisasm::kRc : 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.M.RA, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.M.RT, InstrRegister::kRead);
|
||||
d.AddUImmOperand(i.M.SH, 1);
|
||||
d.AddUImmOperand(i.M.MB, 1);
|
||||
d.AddUImmOperand(i.M.ME, 1);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(rlwnmx, 0x5C000000, M )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("rlwnm", "Rotate Left Word then AND with Mask",
|
||||
i.M.Rc ? InstrDisasm::kRc : 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.M.RA, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.M.RT, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.M.SH, InstrRegister::kRead);
|
||||
d.AddUImmOperand(i.M.MB, 1);
|
||||
d.AddUImmOperand(i.M.ME, 1);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
|
||||
// Integer shift (A-7)
|
||||
|
||||
XEDISASMR(sldx, 0x7C000036, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("sld", "Shift Left Doubleword", i.X.Rc ? InstrDisasm::kRc : 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RT, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(slwx, 0x7C000030, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("slw", "Shift Left Word", i.X.Rc ? InstrDisasm::kRc : 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RT, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(sradx, 0x7C000634, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("srad", "Shift Right Algebraic Doubleword",
|
||||
i.X.Rc ? InstrDisasm::kRc : 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RT, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(sradix, 0x7C000674, XS )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("sradi", "Shift Right Algebraic Doubleword Immediate",
|
||||
(i.XS.Rc ? InstrDisasm::kRc : 0) | InstrDisasm::kCA);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.XS.RA, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.XS.RT, InstrRegister::kRead);
|
||||
d.AddUImmOperand((i.XS.SH5 << 5) | i.XS.SH, 1);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(srawx, 0x7C000630, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("sraw", "Shift Right Algebraic Word",
|
||||
(i.X.Rc ? InstrDisasm::kRc : 0) | InstrDisasm::kCA);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RT, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(srawix, 0x7C000670, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("srawi", "Shift Right Algebraic Word Immediate",
|
||||
(i.X.Rc ? InstrDisasm::kRc : 0) | InstrDisasm::kCA);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RT, InstrRegister::kRead);
|
||||
d.AddUImmOperand(i.X.RB, 1);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(srdx, 0x7C000436, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("srd", "Shift Right Doubleword", i.X.Rc ? InstrDisasm::kRc : 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RT, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(srwx, 0x7C000430, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("srw", "Shift Right Word", i.X.Rc ? InstrDisasm::kRc : 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RT, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
|
||||
void RegisterDisasmCategoryALU() {
|
||||
XEREGISTERINSTR(addx, 0x7C000214);
|
||||
XEREGISTERINSTR(addcx, 0X7C000014);
|
||||
XEREGISTERINSTR(addex, 0x7C000114);
|
||||
XEREGISTERINSTR(addi, 0x38000000);
|
||||
XEREGISTERINSTR(addic, 0x30000000);
|
||||
XEREGISTERINSTR(addicx, 0x34000000);
|
||||
XEREGISTERINSTR(addis, 0x3C000000);
|
||||
XEREGISTERINSTR(addmex, 0x7C0001D4);
|
||||
XEREGISTERINSTR(addzex, 0x7C000194);
|
||||
XEREGISTERINSTR(divdx, 0x7C0003D2);
|
||||
XEREGISTERINSTR(divdux, 0x7C000392);
|
||||
XEREGISTERINSTR(divwx, 0x7C0003D6);
|
||||
XEREGISTERINSTR(divwux, 0x7C000396);
|
||||
XEREGISTERINSTR(mulhdx, 0x7C000092);
|
||||
XEREGISTERINSTR(mulhdux, 0x7C000012);
|
||||
XEREGISTERINSTR(mulhwx, 0x7C000096);
|
||||
XEREGISTERINSTR(mulhwux, 0x7C000016);
|
||||
XEREGISTERINSTR(mulldx, 0x7C0001D2);
|
||||
XEREGISTERINSTR(mulli, 0x1C000000);
|
||||
XEREGISTERINSTR(mullwx, 0x7C0001D6);
|
||||
XEREGISTERINSTR(negx, 0x7C0000D0);
|
||||
XEREGISTERINSTR(subfx, 0x7C000050);
|
||||
XEREGISTERINSTR(subfcx, 0x7C000010);
|
||||
XEREGISTERINSTR(subficx, 0x20000000);
|
||||
XEREGISTERINSTR(subfex, 0x7C000110);
|
||||
XEREGISTERINSTR(subfmex, 0x7C0001D0);
|
||||
XEREGISTERINSTR(subfzex, 0x7C000190);
|
||||
XEREGISTERINSTR(cmp, 0x7C000000);
|
||||
XEREGISTERINSTR(cmpi, 0x2C000000);
|
||||
XEREGISTERINSTR(cmpl, 0x7C000040);
|
||||
XEREGISTERINSTR(cmpli, 0x28000000);
|
||||
XEREGISTERINSTR(andx, 0x7C000038);
|
||||
XEREGISTERINSTR(andcx, 0x7C000078);
|
||||
XEREGISTERINSTR(andix, 0x70000000);
|
||||
XEREGISTERINSTR(andisx, 0x74000000);
|
||||
XEREGISTERINSTR(cntlzdx, 0x7C000074);
|
||||
XEREGISTERINSTR(cntlzwx, 0x7C000034);
|
||||
XEREGISTERINSTR(eqvx, 0x7C000238);
|
||||
XEREGISTERINSTR(extsbx, 0x7C000774);
|
||||
XEREGISTERINSTR(extshx, 0x7C000734);
|
||||
XEREGISTERINSTR(extswx, 0x7C0007B4);
|
||||
XEREGISTERINSTR(nandx, 0x7C0003B8);
|
||||
XEREGISTERINSTR(norx, 0x7C0000F8);
|
||||
XEREGISTERINSTR(orx, 0x7C000378);
|
||||
XEREGISTERINSTR(orcx, 0x7C000338);
|
||||
XEREGISTERINSTR(ori, 0x60000000);
|
||||
XEREGISTERINSTR(oris, 0x64000000);
|
||||
XEREGISTERINSTR(xorx, 0x7C000278);
|
||||
XEREGISTERINSTR(xori, 0x68000000);
|
||||
XEREGISTERINSTR(xoris, 0x6C000000);
|
||||
XEREGISTERINSTR(rld, 0x78000000);
|
||||
// XEREGISTERINSTR(rldclx, 0x78000010);
|
||||
// XEREGISTERINSTR(rldcrx, 0x78000012);
|
||||
// XEREGISTERINSTR(rldicx, 0x78000008);
|
||||
// XEREGISTERINSTR(rldiclx, 0x78000000);
|
||||
// XEREGISTERINSTR(rldicrx, 0x78000004);
|
||||
// XEREGISTERINSTR(rldimix, 0x7800000C);
|
||||
XEREGISTERINSTR(rlwimix, 0x50000000);
|
||||
XEREGISTERINSTR(rlwinmx, 0x54000000);
|
||||
XEREGISTERINSTR(rlwnmx, 0x5C000000);
|
||||
XEREGISTERINSTR(sldx, 0x7C000036);
|
||||
XEREGISTERINSTR(slwx, 0x7C000030);
|
||||
XEREGISTERINSTR(sradx, 0x7C000634);
|
||||
XEREGISTERINSTR(sradix, 0x7C000674);
|
||||
XEREGISTERINSTR(srawx, 0x7C000630);
|
||||
XEREGISTERINSTR(srawix, 0x7C000670);
|
||||
XEREGISTERINSTR(srdx, 0x7C000436);
|
||||
XEREGISTERINSTR(srwx, 0x7C000430);
|
||||
}
|
||||
|
||||
|
||||
} // namespace ppc
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
@@ -1,276 +0,0 @@
|
||||
/*
|
||||
******************************************************************************
|
||||
* 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/disasm.h>
|
||||
|
||||
|
||||
using namespace xe::cpu::ppc;
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace ppc {
|
||||
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
XEDISASMR(bclrx, 0x4C000020, XL )(InstrData& i, InstrDisasm& d) {
|
||||
const char* 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();
|
||||
}
|
||||
|
||||
|
||||
// Condition register logical (A-23)
|
||||
|
||||
XEDISASMR(crand, 0x4C000202, XL )(InstrData& i, InstrDisasm& d) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEDISASMR(crandc, 0x4C000102, XL )(InstrData& i, InstrDisasm& d) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEDISASMR(creqv, 0x4C000242, XL )(InstrData& i, InstrDisasm& d) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEDISASMR(crnand, 0x4C0001C2, XL )(InstrData& i, InstrDisasm& d) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEDISASMR(crnor, 0x4C000042, XL )(InstrData& i, InstrDisasm& d) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEDISASMR(cror, 0x4C000382, XL )(InstrData& i, InstrDisasm& d) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEDISASMR(crorc, 0x4C000342, XL )(InstrData& i, InstrDisasm& d) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEDISASMR(crxor, 0x4C000182, XL )(InstrData& i, InstrDisasm& d) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEDISASMR(mcrf, 0x4C000000, XL )(InstrData& i, InstrDisasm& d) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
// System linkage (A-24)
|
||||
|
||||
XEDISASMR(sc, 0x44000002, SC )(InstrData& i, InstrDisasm& d) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
// Trap (A-25)
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
// Processor control (A-26)
|
||||
|
||||
XEDISASMR(mfcr, 0x7C000026, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("mfcr", "Move From Condition Register", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RT, InstrRegister::kWrite);
|
||||
for (int n = 0; n < 8; n++) {
|
||||
d.AddCR(0, InstrRegister::kRead);
|
||||
}
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
XEDISASMR(mftb, 0x7C0002E6, XFX)(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("mftb", "Move From Time Base", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.XFX.RT, InstrRegister::kWrite);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(mtcrf, 0x7C000120, XFX)(InstrData& i, InstrDisasm& d) {
|
||||
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();
|
||||
}
|
||||
|
||||
XEDISASMR(mfmsr, 0x7C0000A6, X)(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("mfmsr", "Move From Machine State Register", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RT, InstrRegister::kWrite);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(mtmsr, 0x7C000124, X)(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("mtmsr", "Move To Machine State Register", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RT, InstrRegister::kRead);
|
||||
d.AddSImmOperand((i.X.RA & 16) ? 1 : 0, 1);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(mtmsrd, 0x7C000164, X)(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("mtmsrd", "Move To Machine State Register Doubleword", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RT, InstrRegister::kRead);
|
||||
d.AddSImmOperand((i.X.RA & 16) ? 1 : 0, 1);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
|
||||
void RegisterDisasmCategoryControl() {
|
||||
XEREGISTERINSTR(bx, 0x48000000);
|
||||
XEREGISTERINSTR(bcx, 0x40000000);
|
||||
XEREGISTERINSTR(bcctrx, 0x4C000420);
|
||||
XEREGISTERINSTR(bclrx, 0x4C000020);
|
||||
XEREGISTERINSTR(crand, 0x4C000202);
|
||||
XEREGISTERINSTR(crandc, 0x4C000102);
|
||||
XEREGISTERINSTR(creqv, 0x4C000242);
|
||||
XEREGISTERINSTR(crnand, 0x4C0001C2);
|
||||
XEREGISTERINSTR(crnor, 0x4C000042);
|
||||
XEREGISTERINSTR(cror, 0x4C000382);
|
||||
XEREGISTERINSTR(crorc, 0x4C000342);
|
||||
XEREGISTERINSTR(crxor, 0x4C000182);
|
||||
XEREGISTERINSTR(mcrf, 0x4C000000);
|
||||
XEREGISTERINSTR(sc, 0x44000002);
|
||||
XEREGISTERINSTR(td, 0x7C000088);
|
||||
XEREGISTERINSTR(tdi, 0x08000000);
|
||||
XEREGISTERINSTR(tw, 0x7C000008);
|
||||
XEREGISTERINSTR(twi, 0x0C000000);
|
||||
XEREGISTERINSTR(mfcr, 0x7C000026);
|
||||
XEREGISTERINSTR(mfspr, 0x7C0002A6);
|
||||
XEREGISTERINSTR(mftb, 0x7C0002E6);
|
||||
XEREGISTERINSTR(mtcrf, 0x7C000120);
|
||||
XEREGISTERINSTR(mtspr, 0x7C0003A6);
|
||||
XEREGISTERINSTR(mfmsr, 0x7C0000A6);
|
||||
XEREGISTERINSTR(mtmsr, 0x7C000124);
|
||||
XEREGISTERINSTR(mtmsrd, 0x7C000164);
|
||||
}
|
||||
|
||||
|
||||
} // namespace ppc
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
@@ -1,413 +0,0 @@
|
||||
/*
|
||||
******************************************************************************
|
||||
* 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/disasm.h>
|
||||
|
||||
|
||||
using namespace xe::cpu::ppc;
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace ppc {
|
||||
|
||||
|
||||
// Floating-point arithmetic (A-8)
|
||||
|
||||
XEDISASMR(faddx, 0xFC00002A, A )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("fadd", "Floating Add [Single]",
|
||||
InstrDisasm::kFP | (i.A.Rc ? InstrDisasm::kRc : 0));
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRT, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRA, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(faddsx, 0xEC00002A, A )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("fadds", "Floating Add [Single]",
|
||||
InstrDisasm::kFP | (i.A.Rc ? InstrDisasm::kRc : 0));
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRT, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRA, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(fdivx, 0xFC000024, A )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("fdiv", "Floating Divide [Single]",
|
||||
InstrDisasm::kFP | (i.A.Rc ? InstrDisasm::kRc : 0));
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRT, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRA, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(fdivsx, 0xEC000024, A )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("fdivs", "Floating Divide [Single]",
|
||||
InstrDisasm::kFP | (i.A.Rc ? InstrDisasm::kRc : 0));
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRT, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRA, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(fmulx, 0xFC000032, A )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("fmul", "Floating Multiply [Single]",
|
||||
InstrDisasm::kFP | (i.A.Rc ? InstrDisasm::kRc : 0));
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRT, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRA, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRC, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(fmulsx, 0xEC000032, A )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("fmuls", "Floating Multiply [Single]",
|
||||
InstrDisasm::kFP | (i.A.Rc ? InstrDisasm::kRc : 0));
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRT, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRA, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRC, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(fresx, 0xEC000030, A )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("fres", "Floating Reciprocal Estimate [Single]",
|
||||
InstrDisasm::kFP | (i.A.Rc ? InstrDisasm::kRc : 0));
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRT, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(frsqrtex, 0xFC000034, A )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("frsqrte", "Floating Reciprocal Square Root Estimate [Single]",
|
||||
InstrDisasm::kFP | (i.A.Rc ? InstrDisasm::kRc : 0));
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRT, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(fsubx, 0xFC000028, A )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("fsub", "Floating Subtract [Single]",
|
||||
InstrDisasm::kFP | (i.A.Rc ? InstrDisasm::kRc : 0));
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRT, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRA, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(fsubsx, 0xEC000028, A )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("fsubs", "Floating Subtract [Single]",
|
||||
InstrDisasm::kFP | (i.A.Rc ? InstrDisasm::kRc : 0));
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRT, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRA, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(fselx, 0xFC00002E, A )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("fsel", "Floating Select",
|
||||
InstrDisasm::kFP | (i.A.Rc ? InstrDisasm::kRc : 0));
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRT, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRA, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRB, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRC, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(fsqrtx, 0xFC00002C, A )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("fsqrt", "Floating Square Root [Single]",
|
||||
InstrDisasm::kFP | (i.A.Rc ? InstrDisasm::kRc : 0));
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRT, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(fsqrtsx, 0xEC00002C, A )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("fsqrts", "Floating Square Root [Single]",
|
||||
InstrDisasm::kFP | (i.A.Rc ? InstrDisasm::kRc : 0));
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRT, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
|
||||
// Floating-point multiply-add (A-9)
|
||||
|
||||
XEDISASMR(fmaddx, 0xFC00003A, A )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("fmadd", "Floating Multiply-Add [Single]",
|
||||
InstrDisasm::kFP | (i.A.Rc ? InstrDisasm::kRc : 0));
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRT, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRA, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRB, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRC, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(fmaddsx, 0xEC00003A, A )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("fmadds", "Floating Multiply-Add [Single]",
|
||||
InstrDisasm::kFP | (i.A.Rc ? InstrDisasm::kRc : 0));
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRT, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRA, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRB, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRC, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(fmsubx, 0xFC000038, A )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("fmsub", "Floating Multiply-Subtract[Single]",
|
||||
InstrDisasm::kFP | (i.A.Rc ? InstrDisasm::kRc : 0));
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRT, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRA, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRB, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRC, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(fmsubsx, 0xEC000038, A )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("fmsubs", "Floating Multiply-Subtract [Single]",
|
||||
InstrDisasm::kFP | (i.A.Rc ? InstrDisasm::kRc : 0));
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRT, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRA, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRB, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRC, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(fnmaddx, 0xFC00003E, A )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("fnmadd", "Floating Negative Multiply-Add [Single]",
|
||||
InstrDisasm::kFP | (i.A.Rc ? InstrDisasm::kRc : 0));
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRT, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRA, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRB, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRC, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(fnmaddsx, 0xEC00003E, A )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("fnmadds", "Floating Negative Multiply-Add [Single]",
|
||||
InstrDisasm::kFP | (i.A.Rc ? InstrDisasm::kRc : 0));
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRT, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRA, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRB, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRC, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(fnmsubx, 0xFC00003C, A )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("fnmsub", "Floating Negative Multiply-Subtract [Single]",
|
||||
InstrDisasm::kFP | (i.A.Rc ? InstrDisasm::kRc : 0));
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRT, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRA, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRB, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRC, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(fnmsubsx, 0xEC00003C, A )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("fnmsubs", "Floating Negative Multiply-Add [Single]",
|
||||
InstrDisasm::kFP | (i.A.Rc ? InstrDisasm::kRc : 0));
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRT, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRA, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRB, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.A.FRC, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
|
||||
// Floating-point rounding and conversion (A-10)
|
||||
|
||||
XEDISASMR(fcfidx, 0xFC00069C, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("fcfid", "Floating Convert From Integer Doubleword",
|
||||
InstrDisasm::kFP | (i.X.Rc ? InstrDisasm::kRc : 0));
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.X.RT, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(fctidx, 0xFC00065C, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("fctid", "Floating Convert To Integer Doubleword",
|
||||
InstrDisasm::kFP | (i.X.Rc ? InstrDisasm::kRc : 0));
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.X.RT, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(fctidzx, 0xFC00065E, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("fctidz",
|
||||
"Floating Convert To Integer Doubleword with round toward Zero",
|
||||
InstrDisasm::kFP | (i.X.Rc ? InstrDisasm::kRc : 0));
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.X.RT, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(fctiwx, 0xFC00001C, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("fctiw", "Floating Convert To Integer Word",
|
||||
InstrDisasm::kFP | (i.X.Rc ? InstrDisasm::kRc : 0));
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.X.RT, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(fctiwzx, 0xFC00001E, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("fctiwz", "Floating Convert To Integer Word with round toward Zero",
|
||||
InstrDisasm::kFP | (i.X.Rc ? InstrDisasm::kRc : 0));
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.X.RT, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(frspx, 0xFC000018, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("frsp", "Floating Round to Single-Precision",
|
||||
InstrDisasm::kFP | (i.X.Rc ? InstrDisasm::kRc : 0));
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.X.RT, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
|
||||
// Floating-point compare (A-11)
|
||||
|
||||
XEDISASMR(fcmpo, 0xFC000040, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("fcmpo", "Floating Compare Ordered", 0);
|
||||
d.AddCR(i.X.RT >> 2, InstrRegister::kWrite);
|
||||
d.AddUImmOperand(i.X.RT >> 2, 1);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.X.RA, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(fcmpu, 0xFC000000, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("fcmpu", "Floating Compare Unordered", 0);
|
||||
d.AddCR(i.X.RT >> 2, InstrRegister::kWrite);
|
||||
d.AddUImmOperand(i.X.RT >> 2, 1);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.X.RA, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
|
||||
// Floating-point status and control register (A
|
||||
|
||||
XEDISASMR(mcrfs, 0xFC000080, X )(InstrData& i, InstrDisasm& d) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEDISASMR(mffsx, 0xFC00048E, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("mffs", "Move from FPSCR",
|
||||
InstrDisasm::kFP | (i.X.Rc ? InstrDisasm::kRc : 0));
|
||||
d.AddFPSCR(InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.X.RT, InstrRegister::kWrite);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(mtfsb0x, 0xFC00008C, X )(InstrData& i, InstrDisasm& d) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEDISASMR(mtfsb1x, 0xFC00004C, X )(InstrData& i, InstrDisasm& d) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEDISASMR(mtfsfx, 0xFC00058E, XFL)(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("mtfsf", "Move to FPSCR Fields",
|
||||
InstrDisasm::kFP | (i.XFL.Rc ? InstrDisasm::kRc : 0));
|
||||
d.AddFPSCR(InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.XFL.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(mtfsfix, 0xFC00010C, X )(InstrData& i, InstrDisasm& d) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
// Floating-point move (A-21)
|
||||
|
||||
XEDISASMR(fabsx, 0xFC000210, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("fabs", "Floating Absolute Value",
|
||||
InstrDisasm::kFP | (i.X.Rc ? InstrDisasm::kRc : 0));
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.X.RT, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(fmrx, 0xFC000090, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("fmr", "Floating Move Register",
|
||||
InstrDisasm::kFP | (i.X.Rc ? InstrDisasm::kRc : 0));
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.X.RT, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(fnabsx, 0xFC000110, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("fnabs", "Floating Negative Absolute Value",
|
||||
InstrDisasm::kFP | (i.X.Rc ? InstrDisasm::kRc : 0));
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.X.RT, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(fnegx, 0xFC000050, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("fneg", "Floating Negate",
|
||||
InstrDisasm::kFP | (i.X.Rc ? InstrDisasm::kRc : 0));
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.X.RT, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
|
||||
void RegisterDisasmCategoryFPU() {
|
||||
XEREGISTERINSTR(faddx, 0xFC00002A);
|
||||
XEREGISTERINSTR(faddsx, 0xEC00002A);
|
||||
XEREGISTERINSTR(fdivx, 0xFC000024);
|
||||
XEREGISTERINSTR(fdivsx, 0xEC000024);
|
||||
XEREGISTERINSTR(fmulx, 0xFC000032);
|
||||
XEREGISTERINSTR(fmulsx, 0xEC000032);
|
||||
XEREGISTERINSTR(fresx, 0xEC000030);
|
||||
XEREGISTERINSTR(frsqrtex, 0xFC000034);
|
||||
XEREGISTERINSTR(fsubx, 0xFC000028);
|
||||
XEREGISTERINSTR(fsubsx, 0xEC000028);
|
||||
XEREGISTERINSTR(fselx, 0xFC00002E);
|
||||
XEREGISTERINSTR(fsqrtx, 0xFC00002C);
|
||||
XEREGISTERINSTR(fsqrtsx, 0xEC00002C);
|
||||
XEREGISTERINSTR(fmaddx, 0xFC00003A);
|
||||
XEREGISTERINSTR(fmaddsx, 0xEC00003A);
|
||||
XEREGISTERINSTR(fmsubx, 0xFC000038);
|
||||
XEREGISTERINSTR(fmsubsx, 0xEC000038);
|
||||
XEREGISTERINSTR(fnmaddx, 0xFC00003E);
|
||||
XEREGISTERINSTR(fnmaddsx, 0xEC00003E);
|
||||
XEREGISTERINSTR(fnmsubx, 0xFC00003C);
|
||||
XEREGISTERINSTR(fnmsubsx, 0xEC00003C);
|
||||
XEREGISTERINSTR(fcfidx, 0xFC00069C);
|
||||
XEREGISTERINSTR(fctidx, 0xFC00065C);
|
||||
XEREGISTERINSTR(fctidzx, 0xFC00065E);
|
||||
XEREGISTERINSTR(fctiwx, 0xFC00001C);
|
||||
XEREGISTERINSTR(fctiwzx, 0xFC00001E);
|
||||
XEREGISTERINSTR(frspx, 0xFC000018);
|
||||
XEREGISTERINSTR(fcmpo, 0xFC000040);
|
||||
XEREGISTERINSTR(fcmpu, 0xFC000000);
|
||||
XEREGISTERINSTR(mcrfs, 0xFC000080);
|
||||
XEREGISTERINSTR(mffsx, 0xFC00048E);
|
||||
XEREGISTERINSTR(mtfsb0x, 0xFC00008C);
|
||||
XEREGISTERINSTR(mtfsb1x, 0xFC00004C);
|
||||
XEREGISTERINSTR(mtfsfx, 0xFC00058E);
|
||||
XEREGISTERINSTR(mtfsfix, 0xFC00010C);
|
||||
XEREGISTERINSTR(fabsx, 0xFC000210);
|
||||
XEREGISTERINSTR(fmrx, 0xFC000090);
|
||||
XEREGISTERINSTR(fnabsx, 0xFC000110);
|
||||
XEREGISTERINSTR(fnegx, 0xFC000050);
|
||||
}
|
||||
|
||||
|
||||
} // namespace ppc
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
@@ -1,930 +0,0 @@
|
||||
/*
|
||||
******************************************************************************
|
||||
* 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/disasm.h>
|
||||
|
||||
|
||||
using namespace xe::cpu::ppc;
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace ppc {
|
||||
|
||||
|
||||
// Integer load (A-13)
|
||||
|
||||
XEDISASMR(lbz, 0x88000000, D )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("lbz", "Load Byte and Zero", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RT, InstrRegister::kWrite);
|
||||
if (i.D.RA) {
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RA, InstrRegister::kRead);
|
||||
} else {
|
||||
d.AddUImmOperand(0, 1);
|
||||
}
|
||||
d.AddSImmOperand(XEEXTS16(i.D.DS), 2);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(lbzu, 0x8C000000, D )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("lbzu", "Load Byte and Zero with Update", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RT, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RA, InstrRegister::kReadWrite);
|
||||
d.AddSImmOperand(XEEXTS16(i.D.DS), 2);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(lbzux, 0x7C0000EE, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("lbzux", "Load Byte and Zero with Update Indexed", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RT, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kReadWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(lbzx, 0x7C0000AE, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("lbzx", "Load Byte and Zero Indexed", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RT, InstrRegister::kWrite);
|
||||
if (i.X.RA) {
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kRead);
|
||||
} else {
|
||||
d.AddUImmOperand(0, 1);
|
||||
}
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(ld, 0xE8000000, DS )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("ld", "Load Doubleword", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.DS.RT, InstrRegister::kWrite);
|
||||
if (i.DS.RA) {
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.DS.RA, InstrRegister::kRead);
|
||||
} else {
|
||||
d.AddUImmOperand(0, 1);
|
||||
}
|
||||
d.AddSImmOperand(XEEXTS16(i.DS.DS << 2), 2);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(ldu, 0xE8000001, DS )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("ldu", "Load Doubleword with Update", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.DS.RT, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.DS.RA, InstrRegister::kReadWrite);
|
||||
d.AddSImmOperand(XEEXTS16(i.DS.DS << 2), 2);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(ldux, 0x7C00006A, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("ldux", "Load Doubleword with Update Indexed", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RT, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kReadWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(ldx, 0x7C00002A, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("ldx", "Load Doubleword Indexed", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RT, InstrRegister::kWrite);
|
||||
if (i.X.RA) {
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kRead);
|
||||
} else {
|
||||
d.AddUImmOperand(0, 1);
|
||||
}
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(lha, 0xA8000000, D )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("lha", "Load Halfword Algebraic", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RT, InstrRegister::kWrite);
|
||||
if (i.D.RA) {
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RA, InstrRegister::kRead);
|
||||
} else {
|
||||
d.AddUImmOperand(0, 1);
|
||||
}
|
||||
d.AddSImmOperand(XEEXTS16(i.D.DS), 2);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(lhau, 0xAC000000, D )(InstrData& i, InstrDisasm& d) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEDISASMR(lhaux, 0x7C0002EE, X )(InstrData& i, InstrDisasm& d) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEDISASMR(lhax, 0x7C0002AE, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("lhax", "Load Halfword Algebraic Indexed", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RT, InstrRegister::kWrite);
|
||||
if (i.X.RA) {
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kRead);
|
||||
} else {
|
||||
d.AddUImmOperand(0, 1);
|
||||
}
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(lhz, 0xA0000000, D )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("lhz", "Load Halfword and Zero", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RT, InstrRegister::kWrite);
|
||||
if (i.D.RA) {
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RA, InstrRegister::kRead);
|
||||
} else {
|
||||
d.AddUImmOperand(0, 1);
|
||||
}
|
||||
d.AddSImmOperand(XEEXTS16(i.D.DS), 2);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(lhzu, 0xA4000000, D )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("lhzu", "Load Halfword and Zero with Update", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RT, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RA, InstrRegister::kReadWrite);
|
||||
d.AddSImmOperand(XEEXTS16(i.D.DS), 2);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(lhzux, 0x7C00026E, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("lhzux", "Load Halfword and Zero with Update Indexed", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RT, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kReadWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(lhzx, 0x7C00022E, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("lhzx", "Load Halfword and Zero Indexed", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RT, InstrRegister::kWrite);
|
||||
if (i.X.RA) {
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kRead);
|
||||
} else {
|
||||
d.AddUImmOperand(0, 1);
|
||||
}
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(lwa, 0xE8000002, DS )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("lwa", "Load Word Algebraic", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.DS.RT, InstrRegister::kWrite);
|
||||
if (i.D.RA) {
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.DS.RA, InstrRegister::kRead);
|
||||
} else {
|
||||
d.AddUImmOperand(0, 1);
|
||||
}
|
||||
d.AddSImmOperand(XEEXTS16(i.DS.DS << 2), 2);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(lwaux, 0x7C0002EA, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("lwaux", "Load Word Algebraic with Update Indexed", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RT, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kReadWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(lwax, 0x7C0002AA, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("lwax", "Load Word Algebraic Indexed", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RT, InstrRegister::kWrite);
|
||||
if (i.X.RA) {
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kRead);
|
||||
} else {
|
||||
d.AddUImmOperand(0, 1);
|
||||
}
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(lwz, 0x80000000, D )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("lwz", "Load Word and Zero", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RT, InstrRegister::kWrite);
|
||||
if (i.D.RA) {
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RA, InstrRegister::kRead);
|
||||
} else {
|
||||
d.AddUImmOperand(0, 1);
|
||||
}
|
||||
d.AddSImmOperand(XEEXTS16(i.D.DS), 2);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(lwzu, 0x84000000, D )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("lwzu", "Load Word and Zero with Udpate", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RT, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RA, InstrRegister::kReadWrite);
|
||||
d.AddSImmOperand(XEEXTS16(i.D.DS), 2);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(lwzux, 0x7C00006E, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("lwzux", "Load Word and Zero with Update Indexed", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RT, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kReadWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(lwzx, 0x7C00002E, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("lwzx", "Load Word and Zero Indexed", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RT, InstrRegister::kWrite);
|
||||
if (i.X.RA) {
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kRead);
|
||||
} else {
|
||||
d.AddUImmOperand(0, 1);
|
||||
}
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
|
||||
// Integer store (A-14)
|
||||
|
||||
XEDISASMR(stb, 0x98000000, D )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("stb", "Store Byte", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RT, InstrRegister::kRead);
|
||||
if (i.D.RA) {
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RA, InstrRegister::kRead);
|
||||
} else {
|
||||
d.AddUImmOperand(0, 1);
|
||||
}
|
||||
d.AddSImmOperand(XEEXTS16(i.D.DS), 2);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(stbu, 0x9C000000, D )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("stbu", "Store Byte with Update", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RT, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RA, InstrRegister::kReadWrite);
|
||||
d.AddSImmOperand(XEEXTS16(i.D.DS), 2);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(stbux, 0x7C0001EE, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("stbux", "Store Byte with Update Indexed", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RT, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kReadWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(stbx, 0x7C0001AE, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("stbx", "Store Byte Indexed", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RT, InstrRegister::kRead);
|
||||
if (i.DS.RA) {
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kRead);
|
||||
} else {
|
||||
d.AddUImmOperand(0, 1);
|
||||
}
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(std, 0xF8000000, DS )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("std", "Store Doubleword", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.DS.RT, InstrRegister::kRead);
|
||||
if (i.DS.RA) {
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.DS.RA, InstrRegister::kRead);
|
||||
} else {
|
||||
d.AddUImmOperand(0, 1);
|
||||
}
|
||||
d.AddSImmOperand(XEEXTS16(i.DS.DS << 2), 2);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(stdu, 0xF8000001, DS )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("stdu", "Store Doubleword with Update", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.DS.RT, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.DS.RA, InstrRegister::kReadWrite);
|
||||
d.AddSImmOperand(XEEXTS16(i.DS.DS << 2), 2);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(stdux, 0x7C00016A, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("stdux", "Store Doubleword with Update Indexed", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RT, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kReadWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(stdx, 0x7C00012A, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("stdx", "Store Doubleword Indexed", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RT, InstrRegister::kRead);
|
||||
if (i.X.RA) {
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kRead);
|
||||
} else {
|
||||
d.AddUImmOperand(0, 1);
|
||||
}
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(sth, 0xB0000000, D )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("sth", "Store Halfword", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RT, InstrRegister::kRead);
|
||||
if (i.D.RA) {
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RA, InstrRegister::kRead);
|
||||
} else {
|
||||
d.AddUImmOperand(0, 1);
|
||||
}
|
||||
d.AddSImmOperand(XEEXTS16(i.D.DS), 2);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(sthu, 0xB4000000, D )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("sthu", "Store Halfword with Update", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RT, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RA, InstrRegister::kReadWrite);
|
||||
d.AddSImmOperand(XEEXTS16(i.D.DS), 2);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(sthux, 0x7C00036E, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("sthux", "Store Halfword with Update Indexed", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RT, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kReadWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(sthx, 0x7C00032E, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("sth", "Store Halfword Indexed", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RT, InstrRegister::kRead);
|
||||
if (i.X.RA) {
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kRead);
|
||||
} else {
|
||||
d.AddUImmOperand(0, 1);
|
||||
}
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(stw, 0x90000000, D )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("stw", "Store Word", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RT, InstrRegister::kRead);
|
||||
if (i.D.RA) {
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RA, InstrRegister::kRead);
|
||||
} else {
|
||||
d.AddUImmOperand(0, 1);
|
||||
}
|
||||
d.AddSImmOperand(XEEXTS16(i.D.DS), 2);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(stwu, 0x94000000, D )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("stwu", "Store Word with Update", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RT, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RA, InstrRegister::kReadWrite);
|
||||
d.AddSImmOperand(XEEXTS16(i.D.DS), 2);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(stwux, 0x7C00016E, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("stwux", "Store Word with Update Indexed", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RT, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kReadWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(stwx, 0x7C00012E, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("stwx", "Store Word Indexed", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RT, InstrRegister::kRead);
|
||||
if (i.X.RA) {
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kRead);
|
||||
} else {
|
||||
d.AddUImmOperand(0, 1);
|
||||
}
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
|
||||
// Integer load and store with byte reverse (A-1
|
||||
|
||||
XEDISASMR(lhbrx, 0x7C00062C, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("lhbrx", "Load Halfword Byte-Reverse Indexed", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RT, InstrRegister::kWrite);
|
||||
if (i.X.RA) {
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kRead);
|
||||
} else {
|
||||
d.AddUImmOperand(0, 1);
|
||||
}
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(lwbrx, 0x7C00042C, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("lwbrx", "Load Word Byte-Reverse Indexed", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RT, InstrRegister::kWrite);
|
||||
if (i.X.RA) {
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kRead);
|
||||
} else {
|
||||
d.AddUImmOperand(0, 1);
|
||||
}
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(ldbrx, 0x7C000428, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("ldbrx", "Load Doubleword Byte-Reverse Indexed", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RT, InstrRegister::kWrite);
|
||||
if (i.X.RA) {
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kRead);
|
||||
} else {
|
||||
d.AddUImmOperand(0, 1);
|
||||
}
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(sthbrx, 0x7C00072C, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("sthbrx", "Store Halfword Byte-Reverse Indexed", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RT, InstrRegister::kRead);
|
||||
if (i.X.RA) {
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kRead);
|
||||
} else {
|
||||
d.AddUImmOperand(0, 1);
|
||||
}
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(stwbrx, 0x7C00052C, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("stwbrx", "Store Word Byte-Reverse Indexed", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RT, InstrRegister::kRead);
|
||||
if (i.X.RA) {
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kRead);
|
||||
} else {
|
||||
d.AddUImmOperand(0, 1);
|
||||
}
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(stdbrx, 0x7C000528, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("stdbrx", "Store Doubleword Byte-Reverse Indexed", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RT, InstrRegister::kRead);
|
||||
if (i.X.RA) {
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kRead);
|
||||
} else {
|
||||
d.AddUImmOperand(0, 1);
|
||||
}
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
|
||||
// Integer load and store multiple (A-16)
|
||||
|
||||
XEDISASMR(lmw, 0xB8000000, D )(InstrData& i, InstrDisasm& d) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEDISASMR(stmw, 0xBC000000, D )(InstrData& i, InstrDisasm& d) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
// Integer load and store string (A-17)
|
||||
|
||||
XEDISASMR(lswi, 0x7C0004AA, X )(InstrData& i, InstrDisasm& d) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEDISASMR(lswx, 0x7C00042A, X )(InstrData& i, InstrDisasm& d) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEDISASMR(stswi, 0x7C0005AA, X )(InstrData& i, InstrDisasm& d) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEDISASMR(stswx, 0x7C00052A, X )(InstrData& i, InstrDisasm& d) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
// Memory synchronization (A-18)
|
||||
|
||||
XEDISASMR(eieio, 0x7C0006AC, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("eieio", "Enforce In-Order Execution of I/O Instruction", 0);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(isync, 0x4C00012C, XL )(InstrData& i, InstrDisasm& d) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEDISASMR(ldarx, 0x7C0000A8, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("ldarx", "Load Doubleword And Reserve Indexed", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RT, InstrRegister::kWrite);
|
||||
if (i.X.RA) {
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kRead);
|
||||
} else {
|
||||
d.AddUImmOperand(0, 1);
|
||||
}
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(lwarx, 0x7C000028, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("lwarx", "Load Word And Reserve Indexed", 0);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RT, InstrRegister::kWrite);
|
||||
if (i.X.RA) {
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kRead);
|
||||
} else {
|
||||
d.AddUImmOperand(0, 1);
|
||||
}
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(stdcx, 0x7C0001AD, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("stdcx", "Store Doubleword Conditional Indexed",
|
||||
InstrDisasm::kRc);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RT, InstrRegister::kRead);
|
||||
if (i.D.RA) {
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kRead);
|
||||
} else {
|
||||
d.AddUImmOperand(0, 1);
|
||||
}
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(stwcx, 0x7C00012D, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("stwcx", "Store Word Conditional Indexed",
|
||||
InstrDisasm::kRc);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RT, InstrRegister::kRead);
|
||||
if (i.D.RA) {
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kRead);
|
||||
} else {
|
||||
d.AddUImmOperand(0, 1);
|
||||
}
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(sync, 0x7C0004AC, X )(InstrData& i, InstrDisasm& d) {
|
||||
const char* name;
|
||||
const char* desc;
|
||||
int L = i.X.RT & 3;
|
||||
switch (L) {
|
||||
case 0:
|
||||
name = "hwsync";
|
||||
desc = "Synchronize (heavyweight)";
|
||||
break;
|
||||
case 1:
|
||||
name = "lwsync";
|
||||
desc = "Synchronize (lightweight)";
|
||||
break;
|
||||
default:
|
||||
case 2:
|
||||
case 3:
|
||||
name = "sync";
|
||||
desc = "Synchronize";
|
||||
break;
|
||||
}
|
||||
d.Init(name, desc, 0);
|
||||
d.AddUImmOperand(L, 1);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
|
||||
// Floating-point load (A-19)
|
||||
|
||||
XEDISASMR(lfd, 0xC8000000, D )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("lfd", "Load Floating-Point Double", 0);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.D.RT, InstrRegister::kWrite);
|
||||
if (i.D.RA) {
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RA, InstrRegister::kRead);
|
||||
} else {
|
||||
d.AddUImmOperand(0, 1);
|
||||
}
|
||||
d.AddSImmOperand(XEEXTS16(i.D.DS), 2);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(lfdu, 0xCC000000, D )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("lfdu", "Load Floating-Point Double with Update", 0);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.D.RT, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RA, InstrRegister::kReadWrite);
|
||||
d.AddSImmOperand(XEEXTS16(i.D.DS), 2);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(lfdux, 0x7C0004EE, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("lfdux", "Load Floating-Point Double with Update Indexed", 0);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.X.RT, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kReadWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(lfdx, 0x7C0004AE, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("lfdx", "Load Floating-Point Double Indexed", 0);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.X.RT, InstrRegister::kWrite);
|
||||
if (i.X.RA) {
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kRead);
|
||||
} else {
|
||||
d.AddUImmOperand(0, 1);
|
||||
}
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(lfs, 0xC0000000, D )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("lfs", "Load Floating-Point Single", 0);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.D.RT, InstrRegister::kWrite);
|
||||
if (i.D.RA) {
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RA, InstrRegister::kRead);
|
||||
} else {
|
||||
d.AddUImmOperand(0, 1);
|
||||
}
|
||||
d.AddSImmOperand(XEEXTS16(i.D.DS), 2);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(lfsu, 0xC4000000, D )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("lfsu", "Load Floating-Point Single with Update", 0);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.D.RT, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RA, InstrRegister::kReadWrite);
|
||||
d.AddSImmOperand(XEEXTS16(i.D.DS), 2);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(lfsux, 0x7C00046E, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("lfsux", "Load Floating-Point Single with Update Indexed", 0);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.X.RT, InstrRegister::kWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kReadWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(lfsx, 0x7C00042E, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("lfsx", "Load Floating-Point Single Indexed", 0);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.X.RT, InstrRegister::kWrite);
|
||||
if (i.X.RA) {
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kRead);
|
||||
} else {
|
||||
d.AddUImmOperand(0, 1);
|
||||
}
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
|
||||
// Floating-point store (A-20)
|
||||
|
||||
XEDISASMR(stfd, 0xD8000000, D )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("stfd", "Store Floating-Point Double", 0);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.D.RT, InstrRegister::kRead);
|
||||
if (i.D.RA) {
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RA, InstrRegister::kRead);
|
||||
} else {
|
||||
d.AddUImmOperand(0, 1);
|
||||
}
|
||||
d.AddSImmOperand(XEEXTS16(i.D.DS), 2);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(stfdu, 0xDC000000, D )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("stfdu", "Store Floating-Point Double with Update", 0);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.D.RT, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RA, InstrRegister::kReadWrite);
|
||||
d.AddSImmOperand(XEEXTS16(i.D.DS), 2);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(stfdux, 0x7C0005EE, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("stfdux", "Store Floating-Point Double with Update Indexed", 0);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.X.RT, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kReadWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(stfdx, 0x7C0005AE, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("stfdx", "Store Floating-Point Double Indexed", 0);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.X.RT, InstrRegister::kRead);
|
||||
if (i.X.RA) {
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kRead);
|
||||
} else {
|
||||
d.AddUImmOperand(0, 1);
|
||||
}
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(stfiwx, 0x7C0007AE, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("stfiwx", "Store Floating-Point as Integer Word Indexed", 0);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.X.RT, InstrRegister::kRead);
|
||||
if (i.X.RA) {
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kRead);
|
||||
} else {
|
||||
d.AddUImmOperand(0, 1);
|
||||
}
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(stfs, 0xD0000000, D )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("stfs", "Store Floating-Point Single", 0);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.D.RT, InstrRegister::kRead);
|
||||
if (i.D.RA) {
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RA, InstrRegister::kRead);
|
||||
} else {
|
||||
d.AddUImmOperand(0, 1);
|
||||
}
|
||||
d.AddSImmOperand(XEEXTS16(i.D.DS), 2);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(stfsu, 0xD4000000, D )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("stfsu", "Store Floating-Point Single with Update", 0);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.D.RT, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.D.RA, InstrRegister::kReadWrite);
|
||||
d.AddSImmOperand(XEEXTS16(i.D.DS), 2);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(stfsux, 0x7C00056E, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("stfsux", "Store Floating-Point Single with Update Indexed", 0);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.X.RT, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kReadWrite);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(stfsx, 0x7C00052E, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("stfsx", "Store Floating-Point Single Indexed", 0);
|
||||
d.AddRegOperand(InstrRegister::kFPR, i.X.RT, InstrRegister::kRead);
|
||||
if (i.X.RA) {
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kRead);
|
||||
} else {
|
||||
d.AddUImmOperand(0, 1);
|
||||
}
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
|
||||
// Cache management (A-27)
|
||||
|
||||
XEDISASMR(dcbf, 0x7C0000AC, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("dcbf", "Data Cache Block Flush", 0);
|
||||
/*
|
||||
switch (i.X.RT & 3)
|
||||
{
|
||||
case 0: "dcbf";
|
||||
case 1: "dcbfl"
|
||||
case 2: RESERVED
|
||||
case 3: "dcbflp"
|
||||
}
|
||||
*/
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kRead);
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(dcbst, 0x7C00006C, X )(InstrData& i, InstrDisasm& d) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEDISASMR(dcbt, 0x7C00022C, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("dcbt", "Data Cache Block Touch", 0);
|
||||
// TODO
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(dcbtst, 0x7C0001EC, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("dcbtst", "Data Cache Block Touch for Store", 0);
|
||||
// TODO
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(dcbz, 0x7C0007EC, X )(InstrData& i, InstrDisasm& d) {
|
||||
// or dcbz128 0x7C2007EC
|
||||
d.Init("dcbz", "Data Cache Block set to Zero", 0);
|
||||
if (i.X.RA) {
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kRead);
|
||||
} else {
|
||||
d.AddUImmOperand(0, 1);
|
||||
}
|
||||
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
|
||||
return d.Finish();
|
||||
}
|
||||
|
||||
XEDISASMR(icbi, 0x7C0007AC, X )(InstrData& i, InstrDisasm& d) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
void RegisterDisasmCategoryMemory() {
|
||||
XEREGISTERINSTR(lbz, 0x88000000);
|
||||
XEREGISTERINSTR(lbzu, 0x8C000000);
|
||||
XEREGISTERINSTR(lbzux, 0x7C0000EE);
|
||||
XEREGISTERINSTR(lbzx, 0x7C0000AE);
|
||||
XEREGISTERINSTR(ld, 0xE8000000);
|
||||
XEREGISTERINSTR(ldu, 0xE8000001);
|
||||
XEREGISTERINSTR(ldux, 0x7C00006A);
|
||||
XEREGISTERINSTR(ldx, 0x7C00002A);
|
||||
XEREGISTERINSTR(lha, 0xA8000000);
|
||||
XEREGISTERINSTR(lhau, 0xAC000000);
|
||||
XEREGISTERINSTR(lhaux, 0x7C0002EE);
|
||||
XEREGISTERINSTR(lhax, 0x7C0002AE);
|
||||
XEREGISTERINSTR(lhz, 0xA0000000);
|
||||
XEREGISTERINSTR(lhzu, 0xA4000000);
|
||||
XEREGISTERINSTR(lhzux, 0x7C00026E);
|
||||
XEREGISTERINSTR(lhzx, 0x7C00022E);
|
||||
XEREGISTERINSTR(lwa, 0xE8000002);
|
||||
XEREGISTERINSTR(lwaux, 0x7C0002EA);
|
||||
XEREGISTERINSTR(lwax, 0x7C0002AA);
|
||||
XEREGISTERINSTR(lwz, 0x80000000);
|
||||
XEREGISTERINSTR(lwzu, 0x84000000);
|
||||
XEREGISTERINSTR(lwzux, 0x7C00006E);
|
||||
XEREGISTERINSTR(lwzx, 0x7C00002E);
|
||||
XEREGISTERINSTR(stb, 0x98000000);
|
||||
XEREGISTERINSTR(stbu, 0x9C000000);
|
||||
XEREGISTERINSTR(stbux, 0x7C0001EE);
|
||||
XEREGISTERINSTR(stbx, 0x7C0001AE);
|
||||
XEREGISTERINSTR(std, 0xF8000000);
|
||||
XEREGISTERINSTR(stdu, 0xF8000001);
|
||||
XEREGISTERINSTR(stdux, 0x7C00016A);
|
||||
XEREGISTERINSTR(stdx, 0x7C00012A);
|
||||
XEREGISTERINSTR(sth, 0xB0000000);
|
||||
XEREGISTERINSTR(sthu, 0xB4000000);
|
||||
XEREGISTERINSTR(sthux, 0x7C00036E);
|
||||
XEREGISTERINSTR(sthx, 0x7C00032E);
|
||||
XEREGISTERINSTR(stw, 0x90000000);
|
||||
XEREGISTERINSTR(stwu, 0x94000000);
|
||||
XEREGISTERINSTR(stwux, 0x7C00016E);
|
||||
XEREGISTERINSTR(stwx, 0x7C00012E);
|
||||
XEREGISTERINSTR(lhbrx, 0x7C00062C);
|
||||
XEREGISTERINSTR(lwbrx, 0x7C00042C);
|
||||
XEREGISTERINSTR(ldbrx, 0x7C000428);
|
||||
XEREGISTERINSTR(sthbrx, 0x7C00072C);
|
||||
XEREGISTERINSTR(stwbrx, 0x7C00052C);
|
||||
XEREGISTERINSTR(stdbrx, 0x7C000528);
|
||||
XEREGISTERINSTR(lmw, 0xB8000000);
|
||||
XEREGISTERINSTR(stmw, 0xBC000000);
|
||||
XEREGISTERINSTR(lswi, 0x7C0004AA);
|
||||
XEREGISTERINSTR(lswx, 0x7C00042A);
|
||||
XEREGISTERINSTR(stswi, 0x7C0005AA);
|
||||
XEREGISTERINSTR(stswx, 0x7C00052A);
|
||||
XEREGISTERINSTR(eieio, 0x7C0006AC);
|
||||
XEREGISTERINSTR(isync, 0x4C00012C);
|
||||
XEREGISTERINSTR(ldarx, 0x7C0000A8);
|
||||
XEREGISTERINSTR(lwarx, 0x7C000028);
|
||||
XEREGISTERINSTR(stdcx, 0x7C0001AD);
|
||||
XEREGISTERINSTR(stwcx, 0x7C00012D);
|
||||
XEREGISTERINSTR(sync, 0x7C0004AC);
|
||||
XEREGISTERINSTR(lfd, 0xC8000000);
|
||||
XEREGISTERINSTR(lfdu, 0xCC000000);
|
||||
XEREGISTERINSTR(lfdux, 0x7C0004EE);
|
||||
XEREGISTERINSTR(lfdx, 0x7C0004AE);
|
||||
XEREGISTERINSTR(lfs, 0xC0000000);
|
||||
XEREGISTERINSTR(lfsu, 0xC4000000);
|
||||
XEREGISTERINSTR(lfsux, 0x7C00046E);
|
||||
XEREGISTERINSTR(lfsx, 0x7C00042E);
|
||||
XEREGISTERINSTR(stfd, 0xD8000000);
|
||||
XEREGISTERINSTR(stfdu, 0xDC000000);
|
||||
XEREGISTERINSTR(stfdux, 0x7C0005EE);
|
||||
XEREGISTERINSTR(stfdx, 0x7C0005AE);
|
||||
XEREGISTERINSTR(stfiwx, 0x7C0007AE);
|
||||
XEREGISTERINSTR(stfs, 0xD0000000);
|
||||
XEREGISTERINSTR(stfsu, 0xD4000000);
|
||||
XEREGISTERINSTR(stfsux, 0x7C00056E);
|
||||
XEREGISTERINSTR(stfsx, 0x7C00052E);
|
||||
XEREGISTERINSTR(dcbf, 0x7C0000AC);
|
||||
XEREGISTERINSTR(dcbst, 0x7C00006C);
|
||||
XEREGISTERINSTR(dcbt, 0x7C00022C);
|
||||
XEREGISTERINSTR(dcbtst, 0x7C0001EC);
|
||||
XEREGISTERINSTR(dcbz, 0x7C0007EC);
|
||||
XEREGISTERINSTR(icbi, 0x7C0007AC);
|
||||
}
|
||||
|
||||
|
||||
} // namespace ppc
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
@@ -1,502 +0,0 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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 InstrOperand::Dump(std::string& out_str) {
|
||||
if (display) {
|
||||
out_str += display;
|
||||
return;
|
||||
}
|
||||
|
||||
char buffer[32];
|
||||
const size_t max_count = XECOUNT(buffer);
|
||||
switch (type) {
|
||||
case InstrOperand::kRegister:
|
||||
switch (reg.set) {
|
||||
case InstrRegister::kXER:
|
||||
xesnprintfa(buffer, max_count, "XER");
|
||||
break;
|
||||
case InstrRegister::kLR:
|
||||
xesnprintfa(buffer, max_count, "LR");
|
||||
break;
|
||||
case InstrRegister::kCTR:
|
||||
xesnprintfa(buffer, max_count, "CTR");
|
||||
break;
|
||||
case InstrRegister::kCR:
|
||||
xesnprintfa(buffer, max_count, "CR%d", reg.ordinal);
|
||||
break;
|
||||
case InstrRegister::kFPSCR:
|
||||
xesnprintfa(buffer, max_count, "FPSCR");
|
||||
break;
|
||||
case InstrRegister::kGPR:
|
||||
xesnprintfa(buffer, max_count, "r%d", reg.ordinal);
|
||||
break;
|
||||
case InstrRegister::kFPR:
|
||||
xesnprintfa(buffer, max_count, "f%d", reg.ordinal);
|
||||
break;
|
||||
case InstrRegister::kVMX:
|
||||
xesnprintfa(buffer, max_count, "vr%d", reg.ordinal);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case InstrOperand::kImmediate:
|
||||
switch (imm.width) {
|
||||
case 1:
|
||||
if (imm.is_signed) {
|
||||
xesnprintfa(buffer, max_count, "%d", (int32_t)(int8_t)imm.value);
|
||||
} else {
|
||||
xesnprintfa(buffer, max_count, "0x%.2X", (uint8_t)imm.value);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (imm.is_signed) {
|
||||
xesnprintfa(buffer, max_count, "%d", (int32_t)(int16_t)imm.value);
|
||||
} else {
|
||||
xesnprintfa(buffer, max_count, "0x%.4X", (uint16_t)imm.value);
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
if (imm.is_signed) {
|
||||
xesnprintfa(buffer, max_count, "%d", (int32_t)imm.value);
|
||||
} else {
|
||||
xesnprintfa(buffer, max_count, "0x%.8X", (uint32_t)imm.value);
|
||||
}
|
||||
break;
|
||||
case 8:
|
||||
if (imm.is_signed) {
|
||||
xesnprintfa(buffer, max_count, "%lld", (int64_t)imm.value);
|
||||
} else {
|
||||
xesnprintfa(buffer, max_count, "0x%.16llX", imm.value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
out_str += buffer;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
vr31_0 |= other.vr31_0;
|
||||
vr63_32 |= other.vr63_32;
|
||||
vr95_64 |= other.vr95_64;
|
||||
vr127_96 |= other.vr127_96;
|
||||
}
|
||||
|
||||
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;
|
||||
case InstrRegister::kVMX:
|
||||
if (reg.ordinal < 32) {
|
||||
vr31_0 |= bits << (2 * reg.ordinal);
|
||||
} else if (reg.ordinal < 64) {
|
||||
vr63_32 |= bits << (2 * (reg.ordinal - 32));
|
||||
} else if (reg.ordinal < 96) {
|
||||
vr95_64 |= bits << (2 * (reg.ordinal - 64));
|
||||
} else {
|
||||
vr127_96 |= bits << (2 * (reg.ordinal - 96));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (vr31_0) {
|
||||
uint64_t vr31_0_t = vr31_0;
|
||||
for (size_t n = 0; n < 32; n++) {
|
||||
if (vr31_0_t & 0x3) {
|
||||
str << "vr" << n << " [";
|
||||
str << ((vr31_0_t & 1) ? "R" : " ");
|
||||
str << ((vr31_0_t & 2) ? "W" : " ");
|
||||
str << "] ";
|
||||
}
|
||||
vr31_0_t >>= 2;
|
||||
}
|
||||
}
|
||||
if (vr63_32) {
|
||||
uint64_t vr63_32_t = vr63_32;
|
||||
for (size_t n = 0; n < 32; n++) {
|
||||
if (vr63_32_t & 0x3) {
|
||||
str << "vr" << (n + 32) << " [";
|
||||
str << ((vr63_32_t & 1) ? "R" : " ");
|
||||
str << ((vr63_32_t & 2) ? "W" : " ");
|
||||
str << "] ";
|
||||
}
|
||||
vr63_32_t >>= 2;
|
||||
}
|
||||
}
|
||||
if (vr95_64) {
|
||||
uint64_t vr95_64_t = vr95_64;
|
||||
for (size_t n = 0; n < 32; n++) {
|
||||
if (vr95_64_t & 0x3) {
|
||||
str << "vr" << (n + 64) << " [";
|
||||
str << ((vr95_64_t & 1) ? "R" : " ");
|
||||
str << ((vr95_64_t & 2) ? "W" : " ");
|
||||
str << "] ";
|
||||
}
|
||||
vr95_64_t >>= 2;
|
||||
}
|
||||
}
|
||||
if (vr127_96) {
|
||||
uint64_t vr127_96_t = vr127_96;
|
||||
for (size_t n = 0; n < 32; n++) {
|
||||
if (vr127_96_t & 0x3) {
|
||||
str << "vr" << (n + 96) << " [";
|
||||
str << ((vr127_96_t & 1) ? "R" : " ");
|
||||
str << ((vr127_96_t & 2) ? "W" : " ");
|
||||
str << "] ";
|
||||
}
|
||||
vr127_96_t >>= 2;
|
||||
}
|
||||
}
|
||||
|
||||
out_str = str.str();
|
||||
}
|
||||
|
||||
|
||||
void InstrDisasm::Init(const char* name, const char* info, uint32_t flags) {
|
||||
operands.clear();
|
||||
special_registers.clear();
|
||||
access_bits.Clear();
|
||||
|
||||
this->name = name;
|
||||
this->info = info;
|
||||
this->flags = flags;
|
||||
|
||||
if (flags & InstrDisasm::kOE) {
|
||||
InstrRegister i = {
|
||||
InstrRegister::kXER, 0, InstrRegister::kReadWrite
|
||||
};
|
||||
special_registers.push_back(i);
|
||||
}
|
||||
if (flags & InstrDisasm::kRc) {
|
||||
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) {
|
||||
InstrRegister i = {
|
||||
InstrRegister::kLR, 0, InstrRegister::kWrite
|
||||
};
|
||||
special_registers.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
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::AddFPSCR(InstrRegister::Access access) {
|
||||
InstrRegister i = {
|
||||
InstrRegister::kFPSCR, 0, access
|
||||
};
|
||||
special_registers.push_back(i);
|
||||
}
|
||||
|
||||
void InstrDisasm::AddRegOperand(
|
||||
InstrRegister::RegisterSet set, uint32_t ordinal,
|
||||
InstrRegister::Access access, const char* display) {
|
||||
InstrRegister i = {
|
||||
set, ordinal, access
|
||||
};
|
||||
InstrOperand o;
|
||||
o.type = InstrOperand::kRegister;
|
||||
o.reg = i;
|
||||
o.display = display;
|
||||
operands.push_back(o);
|
||||
}
|
||||
|
||||
void InstrDisasm::AddSImmOperand(uint64_t value, size_t width,
|
||||
const char* display) {
|
||||
InstrOperand o;
|
||||
o.type = InstrOperand::kImmediate;
|
||||
o.imm.is_signed = true;
|
||||
o.imm.value = value;
|
||||
o.imm.width = width;
|
||||
o.display = display;
|
||||
operands.push_back(o);
|
||||
}
|
||||
|
||||
void InstrDisasm::AddUImmOperand(uint64_t value, size_t width,
|
||||
const char* display) {
|
||||
InstrOperand o;
|
||||
o.type = InstrOperand::kImmediate;
|
||||
o.imm.is_signed = false;
|
||||
o.imm.value = value;
|
||||
o.imm.width = width;
|
||||
o.display = display;
|
||||
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& out_str, size_t pad) {
|
||||
out_str = name;
|
||||
if (flags & InstrDisasm::kOE) {
|
||||
out_str += "o";
|
||||
}
|
||||
if (flags & InstrDisasm::kRc) {
|
||||
out_str += ".";
|
||||
}
|
||||
if (flags & InstrDisasm::kLR) {
|
||||
out_str += "l";
|
||||
}
|
||||
|
||||
if (operands.size()) {
|
||||
if (pad && out_str.size() < pad) {
|
||||
out_str += std::string(pad - out_str.size(), ' ');
|
||||
}
|
||||
for (std::vector<InstrOperand>::iterator it = operands.begin();
|
||||
it != operands.end(); ++it) {
|
||||
it->Dump(out_str);
|
||||
if (it + 1 != operands.end()) {
|
||||
out_str += ", ";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
InstrType* xe::cpu::ppc::GetInstrType(uint32_t code) {
|
||||
// Fast lookup via tables.
|
||||
InstrType* slot = NULL;
|
||||
switch (code >> 26) {
|
||||
case 4:
|
||||
// Opcode = 4, index = bits 10-0 (10)
|
||||
slot = xe::cpu::ppc::tables::instr_table_4[XESELECTBITS(code, 0, 10)];
|
||||
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)
|
||||
// Special cased to an uber instruction.
|
||||
slot = xe::cpu::ppc::tables::instr_table_30[XESELECTBITS(code, 0, 0)];
|
||||
// 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 slot;
|
||||
}
|
||||
|
||||
// Slow lookup via linear scan.
|
||||
// This is primarily due to laziness. It could be made fast like the others.
|
||||
for (size_t n = 0; n < XECOUNT(xe::cpu::ppc::tables::instr_table_scan);
|
||||
n++) {
|
||||
slot = &(xe::cpu::ppc::tables::instr_table_scan[n]);
|
||||
if (slot->opcode == (code & slot->opcode_mask)) {
|
||||
return slot;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -1,531 +0,0 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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,
|
||||
kXEPPCInstrFormatVXA = 15,
|
||||
kXEPPCInstrFormatVX = 16,
|
||||
kXEPPCInstrFormatVXR = 17,
|
||||
kXEPPCInstrFormatVX128 = 18,
|
||||
kXEPPCInstrFormatVX128_1 = 19,
|
||||
kXEPPCInstrFormatVX128_2 = 20,
|
||||
kXEPPCInstrFormatVX128_3 = 21,
|
||||
kXEPPCInstrFormatVX128_4 = 22,
|
||||
kXEPPCInstrFormatVX128_5 = 23,
|
||||
kXEPPCInstrFormatVX128_P = 24,
|
||||
kXEPPCInstrFormatVX128_R = 25,
|
||||
kXEPPCInstrFormatXDSS = 26,
|
||||
} xe_ppc_instr_format_e;
|
||||
|
||||
typedef enum {
|
||||
kXEPPCInstrMaskVXR = 0xFC0003FF,
|
||||
kXEPPCInstrMaskVXA = 0xFC00003F,
|
||||
kXEPPCInstrMaskVX128 = 0xFC0003D0,
|
||||
kXEPPCInstrMaskVX128_1 = 0xFC0007F3,
|
||||
kXEPPCInstrMaskVX128_2 = 0xFC000210,
|
||||
kXEPPCInstrMaskVX128_3 = 0xFC0007F0,
|
||||
kXEPPCInstrMaskVX128_4 = 0xFC000730,
|
||||
kXEPPCInstrMaskVX128_5 = 0xFC000010,
|
||||
kXEPPCInstrMaskVX128_P = 0xFC000630,
|
||||
kXEPPCInstrMaskVX128_R = 0xFC000390,
|
||||
} xe_ppc_instr_mask_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 uint32_t XEEXTZ16(uint32_t v) {
|
||||
return (uint32_t)((uint16_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
|
||||
mstart &= 0x3F;
|
||||
mstop &= 0x3F;
|
||||
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
|
||||
struct {
|
||||
uint32_t Rc : 1;
|
||||
uint32_t : 10;
|
||||
uint32_t RB : 5;
|
||||
uint32_t : 1;
|
||||
uint32_t FM : 8;
|
||||
uint32_t : 1;
|
||||
uint32_t : 6;
|
||||
} XFL;
|
||||
// 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
|
||||
struct {
|
||||
uint32_t Rc : 1;
|
||||
uint32_t XO : 5;
|
||||
uint32_t FRC : 5;
|
||||
uint32_t FRB : 5;
|
||||
uint32_t FRA : 5;
|
||||
uint32_t FRT : 5;
|
||||
uint32_t : 6;
|
||||
} A;
|
||||
// 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 idx : 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 idx : 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;
|
||||
// kXEPPCInstrFormatVXA
|
||||
struct {
|
||||
uint32_t : 6;
|
||||
uint32_t VC : 5;
|
||||
uint32_t VB : 5;
|
||||
uint32_t VA : 5;
|
||||
uint32_t VD : 5;
|
||||
uint32_t : 6;
|
||||
} VXA;
|
||||
// kXEPPCInstrFormatVX
|
||||
struct {
|
||||
uint32_t : 11;
|
||||
uint32_t VB : 5;
|
||||
uint32_t VA : 5;
|
||||
uint32_t VD : 5;
|
||||
uint32_t : 6;
|
||||
} VX;
|
||||
// kXEPPCInstrFormatVXR
|
||||
struct {
|
||||
uint32_t : 10;
|
||||
uint32_t Rc : 1;
|
||||
uint32_t VB : 5;
|
||||
uint32_t VA : 5;
|
||||
uint32_t VD : 5;
|
||||
uint32_t : 6;
|
||||
} VXR;
|
||||
// kXEPPCInstrFormatVX128
|
||||
struct {
|
||||
// VD128 = VD128l | (VD128h << 5)
|
||||
// VA128 = VA128l | (VA128h << 5) | (VA128H << 6)
|
||||
// VB128 = VB128l | (VB128h << 5)
|
||||
uint32_t VB128h : 2;
|
||||
uint32_t VD128h : 2;
|
||||
uint32_t : 1;
|
||||
uint32_t VA128h : 1;
|
||||
uint32_t : 4;
|
||||
uint32_t VA128H : 1;
|
||||
uint32_t VB128l : 5;
|
||||
uint32_t VA128l : 5;
|
||||
uint32_t VD128l : 5;
|
||||
uint32_t : 6;
|
||||
} VX128;
|
||||
// kXEPPCInstrFormatVX128_1
|
||||
struct {
|
||||
// VD128 = VD128l | (VD128h << 5)
|
||||
uint32_t : 2;
|
||||
uint32_t VD128h : 2;
|
||||
uint32_t : 7;
|
||||
uint32_t RB : 5;
|
||||
uint32_t RA : 5;
|
||||
uint32_t VD128l : 5;
|
||||
uint32_t : 6;
|
||||
} VX128_1;
|
||||
// kXEPPCInstrFormatVX128_2
|
||||
struct {
|
||||
// VD128 = VD128l | (VD128h << 5)
|
||||
// VA128 = VA128l | (VA128h << 5) | (VA128H << 6)
|
||||
// VB128 = VB128l | (VB128h << 5)
|
||||
uint32_t VB128h : 2;
|
||||
uint32_t VD128h : 2;
|
||||
uint32_t : 1;
|
||||
uint32_t VA128h : 1;
|
||||
uint32_t VC : 3;
|
||||
uint32_t : 1;
|
||||
uint32_t VA128H : 1;
|
||||
uint32_t VB128l : 5;
|
||||
uint32_t VA128l : 5;
|
||||
uint32_t VD128l : 5;
|
||||
uint32_t : 6;
|
||||
} VX128_2;
|
||||
// kXEPPCInstrFormatVX128_3
|
||||
struct {
|
||||
// VD128 = VD128l | (VD128h << 5)
|
||||
// VB128 = VB128l | (VB128h << 5)
|
||||
uint32_t VB128h : 2;
|
||||
uint32_t VD128h : 2;
|
||||
uint32_t : 7;
|
||||
uint32_t VB128l : 5;
|
||||
uint32_t IMM : 5;
|
||||
uint32_t VD128l : 5;
|
||||
uint32_t : 6;
|
||||
} VX128_3;
|
||||
// kXEPPCInstrFormatVX128_4
|
||||
struct {
|
||||
// VD128 = VD128l | (VD128h << 5)
|
||||
// VB128 = VB128l | (VB128h << 5)
|
||||
uint32_t VB128h : 2;
|
||||
uint32_t VD128h : 2;
|
||||
uint32_t : 2;
|
||||
uint32_t z : 2;
|
||||
uint32_t : 3;
|
||||
uint32_t VB128l : 5;
|
||||
uint32_t IMM : 5;
|
||||
uint32_t VD128l : 5;
|
||||
uint32_t : 6;
|
||||
} VX128_4;
|
||||
// kXEPPCInstrFormatVX128_5
|
||||
struct {
|
||||
// VD128 = VD128l | (VD128h << 5)
|
||||
// VA128 = VA128l | (VA128h << 5) | (VA128H << 6)
|
||||
// VB128 = VB128l | (VB128h << 5)
|
||||
uint32_t VB128h : 2;
|
||||
uint32_t VD128h : 2;
|
||||
uint32_t : 1;
|
||||
uint32_t VA128h : 1;
|
||||
uint32_t SH : 4;
|
||||
uint32_t VA128H : 1;
|
||||
uint32_t VB128l : 5;
|
||||
uint32_t VA128l : 5;
|
||||
uint32_t VD128l : 5;
|
||||
uint32_t : 6;
|
||||
} VX128_5;
|
||||
// kXEPPCInstrFormatVX128_P
|
||||
struct {
|
||||
// VD128 = VD128l | (VD128h << 5)
|
||||
// VB128 = VB128l | (VB128h << 5)
|
||||
// PERM = PERMl | (PERMh << 5)
|
||||
uint32_t VB128h : 2;
|
||||
uint32_t VD128h : 2;
|
||||
uint32_t : 2;
|
||||
uint32_t PERMh : 3;
|
||||
uint32_t : 2;
|
||||
uint32_t VB128l : 5;
|
||||
uint32_t PERMl : 5;
|
||||
uint32_t VD128l : 5;
|
||||
uint32_t : 6;
|
||||
} VX128_P;
|
||||
// kXEPPCInstrFormatVX128_R
|
||||
struct {
|
||||
// VD128 = VD128l | (VD128h << 5)
|
||||
// VA128 = VA128l | (VA128h << 5) | (VA128H << 6)
|
||||
// VB128 = VB128l | (VB128h << 5)
|
||||
uint32_t VB128h : 2;
|
||||
uint32_t VD128h : 2;
|
||||
uint32_t : 1;
|
||||
uint32_t VA128h : 1;
|
||||
uint32_t Rc : 1;
|
||||
uint32_t : 3;
|
||||
uint32_t VA128H : 1;
|
||||
uint32_t VB128l : 5;
|
||||
uint32_t VA128l : 5;
|
||||
uint32_t VD128l : 5;
|
||||
uint32_t : 6;
|
||||
} VX128_R;
|
||||
// kXEPPCInstrFormatXDSS
|
||||
struct {
|
||||
} XDSS;
|
||||
};
|
||||
} 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;
|
||||
const char* display;
|
||||
union {
|
||||
InstrRegister reg;
|
||||
struct {
|
||||
bool is_signed;
|
||||
uint64_t value;
|
||||
size_t width;
|
||||
} imm;
|
||||
};
|
||||
|
||||
void Dump(std::string& out_str);
|
||||
} InstrOperand;
|
||||
|
||||
|
||||
class InstrAccessBits {
|
||||
public:
|
||||
InstrAccessBits() :
|
||||
spr(0), cr(0), gpr(0), fpr(0),
|
||||
vr31_0(0), vr63_32(0), vr95_64(0), vr127_96(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
|
||||
uint64_t vr31_0;
|
||||
uint64_t vr63_32;
|
||||
uint64_t vr95_64;
|
||||
uint64_t vr127_96;
|
||||
|
||||
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,
|
||||
kFP = 1 << 5,
|
||||
kVMX = 1 << 6,
|
||||
};
|
||||
|
||||
const char* name;
|
||||
const char* info;
|
||||
uint32_t flags;
|
||||
std::vector<InstrOperand> operands;
|
||||
std::vector<InstrRegister> special_registers;
|
||||
InstrAccessBits access_bits;
|
||||
|
||||
void Init(const char* name, const char* info, uint32_t flags);
|
||||
void AddLR(InstrRegister::Access access);
|
||||
void AddCTR(InstrRegister::Access access);
|
||||
void AddCR(uint32_t bf, InstrRegister::Access access);
|
||||
void AddFPSCR(InstrRegister::Access access);
|
||||
void AddRegOperand(InstrRegister::RegisterSet set, uint32_t ordinal,
|
||||
InstrRegister::Access access, const char* display = NULL);
|
||||
void AddSImmOperand(uint64_t value, size_t width, const char* display = NULL);
|
||||
void AddUImmOperand(uint64_t value, size_t width, const char* display = NULL);
|
||||
int Finish();
|
||||
|
||||
void Dump(std::string& out_str, size_t pad = 13);
|
||||
};
|
||||
|
||||
|
||||
typedef int (*InstrDisassembleFn)(InstrData& i, InstrDisasm& d);
|
||||
typedef void* InstrEmitFn;
|
||||
|
||||
|
||||
class InstrType {
|
||||
public:
|
||||
uint32_t opcode;
|
||||
uint32_t opcode_mask; // Only used for certain opcodes (altivec, etc).
|
||||
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_
|
||||
|
||||
@@ -1,643 +0,0 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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 = (int)pow(2.0, b - a + 1);
|
||||
InstrType** prep = (InstrType**)xe_calloc(prep_count * sizeof(void*));
|
||||
for (int n = 0; n < unprep_count; n++) {
|
||||
int ordinal = XESELECTBITS(unprep[n].opcode, a, b);
|
||||
prep[ordinal] = &unprep[n];
|
||||
}
|
||||
return prep;
|
||||
}
|
||||
|
||||
static InstrType** instr_table_prep_63(
|
||||
InstrType* unprep, int unprep_count, int a, int b) {
|
||||
// Special handling for A format instructions.
|
||||
int prep_count = (int)pow(2.0, b - a + 1);
|
||||
InstrType** prep = (InstrType**)xe_calloc(prep_count * sizeof(void*));
|
||||
for (int n = 0; n < unprep_count; n++) {
|
||||
int ordinal = XESELECTBITS(unprep[n].opcode, a, b);
|
||||
if (unprep[n].format == kXEPPCInstrFormatA) {
|
||||
// Must splat this into all of the slots that it could be in.
|
||||
for (int m = 0; m < 32; m++) {
|
||||
prep[ordinal + (m << 5)] = &unprep[n];
|
||||
}
|
||||
} else {
|
||||
prep[ordinal] = &unprep[n];
|
||||
}
|
||||
}
|
||||
return prep;
|
||||
}
|
||||
|
||||
|
||||
#define EMPTY(slot) {0}
|
||||
#define INSTRUCTION(name, opcode, format, type, flag) { \
|
||||
opcode, \
|
||||
0, \
|
||||
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 11-0 (6)
|
||||
static InstrType instr_table_4_unprep[] = {
|
||||
// TODO: all of the vector ops
|
||||
INSTRUCTION(mfvscr, 0x10000604, VX , General , 0),
|
||||
INSTRUCTION(mtvscr, 0x10000644, VX , General , 0),
|
||||
INSTRUCTION(vaddcuw, 0x10000180, VX , General , 0),
|
||||
INSTRUCTION(vaddfp, 0x1000000A, VX , General , 0),
|
||||
INSTRUCTION(vaddsbs, 0x10000300, VX , General , 0),
|
||||
INSTRUCTION(vaddshs, 0x10000340, VX , General , 0),
|
||||
INSTRUCTION(vaddsws, 0x10000380, VX , General , 0),
|
||||
INSTRUCTION(vaddubm, 0x10000000, VX , General , 0),
|
||||
INSTRUCTION(vaddubs, 0x10000200, VX , General , 0),
|
||||
INSTRUCTION(vadduhm, 0x10000040, VX , General , 0),
|
||||
INSTRUCTION(vadduhs, 0x10000240, VX , General , 0),
|
||||
INSTRUCTION(vadduwm, 0x10000080, VX , General , 0),
|
||||
INSTRUCTION(vadduws, 0x10000280, VX , General , 0),
|
||||
INSTRUCTION(vand, 0x10000404, VX , General , 0),
|
||||
INSTRUCTION(vandc, 0x10000444, VX , General , 0),
|
||||
INSTRUCTION(vavgsb, 0x10000502, VX , General , 0),
|
||||
INSTRUCTION(vavgsh, 0x10000542, VX , General , 0),
|
||||
INSTRUCTION(vavgsw, 0x10000582, VX , General , 0),
|
||||
INSTRUCTION(vavgub, 0x10000402, VX , General , 0),
|
||||
INSTRUCTION(vavguh, 0x10000442, VX , General , 0),
|
||||
INSTRUCTION(vavguw, 0x10000482, VX , General , 0),
|
||||
INSTRUCTION(vcfsx, 0x1000034A, VX , General , 0),
|
||||
INSTRUCTION(vcfux, 0x1000030A, VX , General , 0),
|
||||
INSTRUCTION(vctsxs, 0x100003CA, VX , General , 0),
|
||||
INSTRUCTION(vctuxs, 0x1000038A, VX , General , 0),
|
||||
INSTRUCTION(vexptefp, 0x1000018A, VX , General , 0),
|
||||
INSTRUCTION(vlogefp, 0x100001CA, VX , General , 0),
|
||||
INSTRUCTION(vmaxfp, 0x1000040A, VX , General , 0),
|
||||
INSTRUCTION(vmaxsb, 0x10000102, VX , General , 0),
|
||||
INSTRUCTION(vmaxsh, 0x10000142, VX , General , 0),
|
||||
INSTRUCTION(vmaxsw, 0x10000182, VX , General , 0),
|
||||
INSTRUCTION(vmaxub, 0x10000002, VX , General , 0),
|
||||
INSTRUCTION(vmaxuh, 0x10000042, VX , General , 0),
|
||||
INSTRUCTION(vmaxuw, 0x10000082, VX , General , 0),
|
||||
INSTRUCTION(vminfp, 0x1000044A, VX , General , 0),
|
||||
INSTRUCTION(vminsb, 0x10000302, VX , General , 0),
|
||||
INSTRUCTION(vminsh, 0x10000342, VX , General , 0),
|
||||
INSTRUCTION(vminsw, 0x10000382, VX , General , 0),
|
||||
INSTRUCTION(vminub, 0x10000202, VX , General , 0),
|
||||
INSTRUCTION(vminuh, 0x10000242, VX , General , 0),
|
||||
INSTRUCTION(vminuw, 0x10000282, VX , General , 0),
|
||||
INSTRUCTION(vmrghb, 0x1000000C, VX , General , 0),
|
||||
INSTRUCTION(vmrghh, 0x1000004C, VX , General , 0),
|
||||
INSTRUCTION(vmrghw, 0x1000008C, VX , General , 0),
|
||||
INSTRUCTION(vmrglb, 0x1000010C, VX , General , 0),
|
||||
INSTRUCTION(vmrglh, 0x1000014C, VX , General , 0),
|
||||
INSTRUCTION(vmrglw, 0x1000018C, VX , General , 0),
|
||||
INSTRUCTION(vmulesb, 0x10000308, VX , General , 0),
|
||||
INSTRUCTION(vmulesh, 0x10000348, VX , General , 0),
|
||||
INSTRUCTION(vmuleub, 0x10000208, VX , General , 0),
|
||||
INSTRUCTION(vmuleuh, 0x10000248, VX , General , 0),
|
||||
INSTRUCTION(vmulosb, 0x10000108, VX , General , 0),
|
||||
INSTRUCTION(vmulosh, 0x10000148, VX , General , 0),
|
||||
INSTRUCTION(vmuloub, 0x10000008, VX , General , 0),
|
||||
INSTRUCTION(vmulouh, 0x10000048, VX , General , 0),
|
||||
INSTRUCTION(vnor, 0x10000504, VX , General , 0),
|
||||
INSTRUCTION(vor, 0x10000484, VX , General , 0),
|
||||
INSTRUCTION(vpkpx, 0x1000030E, VX , General , 0),
|
||||
INSTRUCTION(vpkshss, 0x1000018E, VX , General , 0),
|
||||
INSTRUCTION(vpkshus, 0x1000010E, VX , General , 0),
|
||||
INSTRUCTION(vpkswss, 0x100001CE, VX , General , 0),
|
||||
INSTRUCTION(vpkswus, 0x1000014E, VX , General , 0),
|
||||
INSTRUCTION(vpkuhum, 0x1000000E, VX , General , 0),
|
||||
INSTRUCTION(vpkuhus, 0x1000008E, VX , General , 0),
|
||||
INSTRUCTION(vpkuwum, 0x1000004E, VX , General , 0),
|
||||
INSTRUCTION(vpkuwus, 0x100000CE, VX , General , 0),
|
||||
INSTRUCTION(vrefp, 0x1000010A, VX , General , 0),
|
||||
INSTRUCTION(vrfim, 0x100002CA, VX , General , 0),
|
||||
INSTRUCTION(vrfin, 0x1000020A, VX , General , 0),
|
||||
INSTRUCTION(vrfip, 0x1000028A, VX , General , 0),
|
||||
INSTRUCTION(vrfiz, 0x1000024A, VX , General , 0),
|
||||
INSTRUCTION(vrlb, 0x10000004, VX , General , 0),
|
||||
INSTRUCTION(vrlh, 0x10000044, VX , General , 0),
|
||||
INSTRUCTION(vrlw, 0x10000084, VX , General , 0),
|
||||
INSTRUCTION(vrsqrtefp, 0x1000014A, VX , General , 0),
|
||||
INSTRUCTION(vsl, 0x100001C4, VX , General , 0),
|
||||
INSTRUCTION(vslb, 0x10000104, VX , General , 0),
|
||||
INSTRUCTION(vslh, 0x10000144, VX , General , 0),
|
||||
INSTRUCTION(vslo, 0x1000040C, VX , General , 0),
|
||||
INSTRUCTION(vslw, 0x10000184, VX , General , 0),
|
||||
INSTRUCTION(vspltb, 0x1000020C, VX , General , 0),
|
||||
INSTRUCTION(vsplth, 0x1000024C, VX , General , 0),
|
||||
INSTRUCTION(vspltisb, 0x1000030C, VX , General , 0),
|
||||
INSTRUCTION(vspltish, 0x1000034C, VX , General , 0),
|
||||
INSTRUCTION(vspltisw, 0x1000038C, VX , General , 0),
|
||||
INSTRUCTION(vspltw, 0x1000028C, VX , General , 0),
|
||||
INSTRUCTION(vsr, 0x100002C4, VX , General , 0),
|
||||
INSTRUCTION(vsrab, 0x10000304, VX , General , 0),
|
||||
INSTRUCTION(vsrah, 0x10000344, VX , General , 0),
|
||||
INSTRUCTION(vsraw, 0x10000384, VX , General , 0),
|
||||
INSTRUCTION(vsrb, 0x10000204, VX , General , 0),
|
||||
INSTRUCTION(vsrh, 0x10000244, VX , General , 0),
|
||||
INSTRUCTION(vsro, 0x1000044C, VX , General , 0),
|
||||
INSTRUCTION(vsrw, 0x10000284, VX , General , 0),
|
||||
INSTRUCTION(vsubcuw, 0x10000580, VX , General , 0),
|
||||
INSTRUCTION(vsubfp, 0x1000004A, VX , General , 0),
|
||||
INSTRUCTION(vsubsbs, 0x10000700, VX , General , 0),
|
||||
INSTRUCTION(vsubshs, 0x10000740, VX , General , 0),
|
||||
INSTRUCTION(vsubsws, 0x10000780, VX , General , 0),
|
||||
INSTRUCTION(vsububm, 0x10000400, VX , General , 0),
|
||||
INSTRUCTION(vsububs, 0x10000600, VX , General , 0),
|
||||
INSTRUCTION(vsubuhm, 0x10000440, VX , General , 0),
|
||||
INSTRUCTION(vsubuhs, 0x10000640, VX , General , 0),
|
||||
INSTRUCTION(vsubuwm, 0x10000480, VX , General , 0),
|
||||
INSTRUCTION(vsubuws, 0x10000680, VX , General , 0),
|
||||
INSTRUCTION(vsumsws, 0x10000788, VX , General , 0),
|
||||
INSTRUCTION(vsum2sws, 0x10000688, VX , General , 0),
|
||||
INSTRUCTION(vsum4sbs, 0x10000708, VX , General , 0),
|
||||
INSTRUCTION(vsum4shs, 0x10000648, VX , General , 0),
|
||||
INSTRUCTION(vsum4ubs, 0x10000608, VX , General , 0),
|
||||
INSTRUCTION(vupkhpx, 0x1000034E, VX , General , 0),
|
||||
INSTRUCTION(vupkhsb, 0x1000020E, VX , General , 0),
|
||||
INSTRUCTION(vupkhsh, 0x1000024E, VX , General , 0),
|
||||
INSTRUCTION(vupklpx, 0x100003CE, VX , General , 0),
|
||||
INSTRUCTION(vupklsb, 0x1000028E, VX , General , 0),
|
||||
INSTRUCTION(vupklsh, 0x100002CE, VX , General , 0),
|
||||
INSTRUCTION(vxor, 0x100004C4, VX , General , 0),
|
||||
};
|
||||
static InstrType** instr_table_4 = instr_table_prep(
|
||||
instr_table_4_unprep, XECOUNT(instr_table_4_unprep), 0, 11);
|
||||
|
||||
// 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[] = {
|
||||
// Decoding these instrunctions in this table is difficult because the
|
||||
// index bits are kind of random. This is special cased by an uber
|
||||
// instruction handler.
|
||||
INSTRUCTION(rld, 0x78000000, MD , General , 0),
|
||||
// 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), 0, 0);
|
||||
|
||||
// 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(mfmsr, 0x7C0000A6, X , 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(mtmsr, 0x7C000124, X , 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(mtmsrd, 0x7C000164, 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
|
||||
INSTRUCTION(dst, 0x7C0002AC, XDSS, General , 0),
|
||||
INSTRUCTION(dstst, 0x7C0002EC, XDSS, General , 0),
|
||||
INSTRUCTION(dss, 0x7C00066C, XDSS, General , 0),
|
||||
INSTRUCTION(lvebx, 0x7C00000E, X , General , 0),
|
||||
INSTRUCTION(lvehx, 0x7C00004E, X , General , 0),
|
||||
INSTRUCTION(lvewx, 0x7C00008E, X , General , 0),
|
||||
INSTRUCTION(lvsl, 0x7C00000C, X , General , 0),
|
||||
INSTRUCTION(lvsr, 0x7C00004C, X , General , 0),
|
||||
INSTRUCTION(lvx, 0x7C0000CE, X , General , 0),
|
||||
INSTRUCTION(lvxl, 0x7C0002CE, X , General , 0),
|
||||
INSTRUCTION(stvebx, 0x7C00010E, X , General , 0),
|
||||
INSTRUCTION(stvehx, 0x7C00014E, X , General , 0),
|
||||
INSTRUCTION(stvewx, 0x7C00018E, X , General , 0),
|
||||
INSTRUCTION(stvx, 0x7C0001CE, X , General , 0),
|
||||
INSTRUCTION(stvxl, 0x7C0003CE, X , General , 0),
|
||||
INSTRUCTION(lvlx, 0x7C00040E, X , General , 0),
|
||||
INSTRUCTION(lvlxl, 0x7C00060E, X , General , 0),
|
||||
INSTRUCTION(lvrx, 0x7C00044E, X , General , 0),
|
||||
INSTRUCTION(lvrxl, 0x7C00064E, X , General , 0),
|
||||
INSTRUCTION(stvlx, 0x7C00050E, X , General , 0),
|
||||
INSTRUCTION(stvlxl, 0x7C00070E, X , General , 0),
|
||||
INSTRUCTION(stvrx, 0x7C00054E, X , General , 0),
|
||||
INSTRUCTION(stvrxl, 0x7C00074E, X , General , 0),
|
||||
};
|
||||
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)
|
||||
// NOTE: the A format instructions need some special handling because
|
||||
// they only use 6bits to identify their index.
|
||||
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_63(
|
||||
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);
|
||||
|
||||
// Altivec instructions.
|
||||
// TODO(benvanik): build a table like the other instructions.
|
||||
// This table is looked up via linear scan of opcodes.
|
||||
#define SCAN_INSTRUCTION(name, opcode, format, type, flag) { \
|
||||
opcode, \
|
||||
kXEPPCInstrMask##format, \
|
||||
kXEPPCInstrFormat##format, \
|
||||
kXEPPCInstrType##type, \
|
||||
flag, \
|
||||
#name, \
|
||||
}
|
||||
#define OP(x) ((((uint32_t)(x)) & 0x3f) << 26)
|
||||
#define VX128(op, xop) (OP(op) | (((uint32_t)(xop)) & 0x3d0))
|
||||
#define VX128_1(op, xop) (OP(op) | (((uint32_t)(xop)) & 0x7f3))
|
||||
#define VX128_2(op, xop) (OP(op) | (((uint32_t)(xop)) & 0x210))
|
||||
#define VX128_3(op, xop) (OP(op) | (((uint32_t)(xop)) & 0x7f0))
|
||||
#define VX128_4(op, xop) (OP(op) | (((uint32_t)(xop)) & 0x730))
|
||||
#define VX128_5(op, xop) (OP(op) | (((uint32_t)(xop)) & 0x10))
|
||||
#define VX128_P(op, xop) (OP(op) | (((uint32_t)(xop)) & 0x630))
|
||||
#define VX128_R(op, xop) (OP(op) | (((uint32_t)(xop)) & 0x390))
|
||||
static InstrType instr_table_scan[] = {
|
||||
SCAN_INSTRUCTION(vcmpbfp, 0x100003C6, VXR , General , 0),
|
||||
SCAN_INSTRUCTION(vcmpeqfp, 0x100000C6, VXR , General , 0),
|
||||
SCAN_INSTRUCTION(vcmpequb, 0x10000006, VXR , General , 0),
|
||||
SCAN_INSTRUCTION(vcmpequh, 0x10000046, VXR , General , 0),
|
||||
SCAN_INSTRUCTION(vcmpequw, 0x10000086, VXR , General , 0),
|
||||
SCAN_INSTRUCTION(vcmpgefp, 0x100001C6, VXR , General , 0),
|
||||
SCAN_INSTRUCTION(vcmpgtfp, 0x100002C6, VXR , General , 0),
|
||||
SCAN_INSTRUCTION(vcmpgtsb, 0x10000306, VXR , General , 0),
|
||||
SCAN_INSTRUCTION(vcmpgtsh, 0x10000346, VXR , General , 0),
|
||||
SCAN_INSTRUCTION(vcmpgtsw, 0x10000386, VXR , General , 0),
|
||||
SCAN_INSTRUCTION(vcmpgtub, 0x10000206, VXR , General , 0),
|
||||
SCAN_INSTRUCTION(vcmpgtuh, 0x10000246, VXR , General , 0),
|
||||
SCAN_INSTRUCTION(vcmpgtuw, 0x10000286, VXR , General , 0),
|
||||
SCAN_INSTRUCTION(vmaddfp, 0x1000002E, VXA , General , 0),
|
||||
SCAN_INSTRUCTION(vmhaddshs, 0x10000020, VXA , General , 0),
|
||||
SCAN_INSTRUCTION(vmhraddshs, 0x10000021, VXA , General , 0),
|
||||
SCAN_INSTRUCTION(vmladduhm, 0x10000022, VXA , General , 0),
|
||||
SCAN_INSTRUCTION(vmsummbm, 0x10000025, VXA , General , 0),
|
||||
SCAN_INSTRUCTION(vmsumshm, 0x10000028, VXA , General , 0),
|
||||
SCAN_INSTRUCTION(vmsumshs, 0x10000029, VXA , General , 0),
|
||||
SCAN_INSTRUCTION(vmsumubm, 0x10000024, VXA , General , 0),
|
||||
SCAN_INSTRUCTION(vmsumuhm, 0x10000026, VXA , General , 0),
|
||||
SCAN_INSTRUCTION(vmsumuhs, 0x10000027, VXA , General , 0),
|
||||
SCAN_INSTRUCTION(vnmsubfp, 0x1000002F, VXA , General , 0),
|
||||
SCAN_INSTRUCTION(vperm, 0x1000002B, VXA , General , 0),
|
||||
SCAN_INSTRUCTION(vsel, 0x1000002A, VXA , General , 0),
|
||||
SCAN_INSTRUCTION(vsldoi, 0x1000002C, VXA , General , 0),
|
||||
SCAN_INSTRUCTION(lvsl128, VX128_1(4, 3), VX128_1 , General , 0),
|
||||
SCAN_INSTRUCTION(lvsr128, VX128_1(4, 67), VX128_1 , General , 0),
|
||||
SCAN_INSTRUCTION(lvewx128, VX128_1(4, 131), VX128_1 , General , 0),
|
||||
SCAN_INSTRUCTION(lvx128, VX128_1(4, 195), VX128_1 , General , 0),
|
||||
SCAN_INSTRUCTION(stvewx128, VX128_1(4, 387), VX128_1 , General , 0),
|
||||
SCAN_INSTRUCTION(stvx128, VX128_1(4, 451), VX128_1 , General , 0),
|
||||
SCAN_INSTRUCTION(lvxl128, VX128_1(4, 707), VX128_1 , General , 0),
|
||||
SCAN_INSTRUCTION(stvxl128, VX128_1(4, 963), VX128_1 , General , 0),
|
||||
SCAN_INSTRUCTION(lvlx128, VX128_1(4, 1027), VX128_1 , General , 0),
|
||||
SCAN_INSTRUCTION(lvrx128, VX128_1(4, 1091), VX128_1 , General , 0),
|
||||
SCAN_INSTRUCTION(stvlx128, VX128_1(4, 1283), VX128_1 , General , 0),
|
||||
SCAN_INSTRUCTION(stvrx128, VX128_1(4, 1347), VX128_1 , General , 0),
|
||||
SCAN_INSTRUCTION(lvlxl128, VX128_1(4, 1539), VX128_1 , General , 0),
|
||||
SCAN_INSTRUCTION(lvrxl128, VX128_1(4, 1603), VX128_1 , General , 0),
|
||||
SCAN_INSTRUCTION(stvlxl128, VX128_1(4, 1795), VX128_1 , General , 0),
|
||||
SCAN_INSTRUCTION(stvrxl128, VX128_1(4, 1859), VX128_1 , General , 0),
|
||||
SCAN_INSTRUCTION(vsldoi128, VX128_5(4, 16), VX128_5 , General , 0),
|
||||
SCAN_INSTRUCTION(vperm128, VX128_2(5, 0), VX128_2 , General , 0),
|
||||
SCAN_INSTRUCTION(vaddfp128, VX128(5, 16), VX128 , General , 0),
|
||||
SCAN_INSTRUCTION(vsubfp128, VX128(5, 80), VX128 , General , 0),
|
||||
SCAN_INSTRUCTION(vmulfp128, VX128(5, 144), VX128 , General , 0),
|
||||
SCAN_INSTRUCTION(vmaddfp128, VX128(5, 208), VX128 , General , 0),
|
||||
SCAN_INSTRUCTION(vmaddcfp128, VX128(5, 272), VX128 , General , 0),
|
||||
SCAN_INSTRUCTION(vnmsubfp128, VX128(5, 336), VX128 , General , 0),
|
||||
SCAN_INSTRUCTION(vmsum3fp128, VX128(5, 400), VX128 , General , 0),
|
||||
SCAN_INSTRUCTION(vmsum4fp128, VX128(5, 464), VX128 , General , 0),
|
||||
SCAN_INSTRUCTION(vpkshss128, VX128(5, 512), VX128 , General , 0),
|
||||
SCAN_INSTRUCTION(vand128, VX128(5, 528), VX128 , General , 0),
|
||||
SCAN_INSTRUCTION(vpkshus128, VX128(5, 576), VX128 , General , 0),
|
||||
SCAN_INSTRUCTION(vandc128, VX128(5, 592), VX128 , General , 0),
|
||||
SCAN_INSTRUCTION(vpkswss128, VX128(5, 640), VX128 , General , 0),
|
||||
SCAN_INSTRUCTION(vnor128, VX128(5, 656), VX128 , General , 0),
|
||||
SCAN_INSTRUCTION(vpkswus128, VX128(5, 704), VX128 , General , 0),
|
||||
SCAN_INSTRUCTION(vor128, VX128(5, 720), VX128 , General , 0),
|
||||
SCAN_INSTRUCTION(vpkuhum128, VX128(5, 768), VX128 , General , 0),
|
||||
SCAN_INSTRUCTION(vxor128, VX128(5, 784), VX128 , General , 0),
|
||||
SCAN_INSTRUCTION(vpkuhus128, VX128(5, 832), VX128 , General , 0),
|
||||
SCAN_INSTRUCTION(vsel128, VX128(5, 848), VX128 , General , 0),
|
||||
SCAN_INSTRUCTION(vpkuwum128, VX128(5, 896), VX128 , General , 0),
|
||||
SCAN_INSTRUCTION(vslo128, VX128(5, 912), VX128 , General , 0),
|
||||
SCAN_INSTRUCTION(vpkuwus128, VX128(5, 960), VX128 , General , 0),
|
||||
SCAN_INSTRUCTION(vsro128, VX128(5, 976), VX128 , General , 0),
|
||||
SCAN_INSTRUCTION(vpermwi128, VX128_P(6, 528), VX128_P , General , 0),
|
||||
SCAN_INSTRUCTION(vcfpsxws128, VX128_3(6, 560), VX128_3 , General , 0),
|
||||
SCAN_INSTRUCTION(vcfpuxws128, VX128_3(6, 624), VX128_3 , General , 0),
|
||||
SCAN_INSTRUCTION(vcsxwfp128, VX128_3(6, 688), VX128_3 , General , 0),
|
||||
SCAN_INSTRUCTION(vcuxwfp128, VX128_3(6, 752), VX128_3 , General , 0),
|
||||
SCAN_INSTRUCTION(vrfim128, VX128_3(6, 816), VX128_3 , General , 0),
|
||||
SCAN_INSTRUCTION(vrfin128, VX128_3(6, 880), VX128_3 , General , 0),
|
||||
SCAN_INSTRUCTION(vrfip128, VX128_3(6, 944), VX128_3 , General , 0),
|
||||
SCAN_INSTRUCTION(vrfiz128, VX128_3(6, 1008), VX128_3 , General , 0),
|
||||
SCAN_INSTRUCTION(vpkd3d128, VX128_4(6, 1552), VX128_4 , General , 0),
|
||||
SCAN_INSTRUCTION(vrefp128, VX128_3(6, 1584), VX128_3 , General , 0),
|
||||
SCAN_INSTRUCTION(vrsqrtefp128, VX128_3(6, 1648), VX128_3 , General , 0),
|
||||
SCAN_INSTRUCTION(vexptefp128, VX128_3(6, 1712), VX128_3 , General , 0),
|
||||
SCAN_INSTRUCTION(vlogefp128, VX128_3(6, 1776), VX128_3 , General , 0),
|
||||
SCAN_INSTRUCTION(vrlimi128, VX128_4(6, 1808), VX128_4 , General , 0),
|
||||
SCAN_INSTRUCTION(vspltw128, VX128_3(6, 1840), VX128_3 , General , 0),
|
||||
SCAN_INSTRUCTION(vspltisw128, VX128_3(6, 1904), VX128_3 , General , 0),
|
||||
SCAN_INSTRUCTION(vupkd3d128, VX128_3(6, 2032), VX128_3 , General , 0),
|
||||
SCAN_INSTRUCTION(vcmpeqfp128, VX128_R(6, 0), VX128_R , General , 0),
|
||||
SCAN_INSTRUCTION(vrlw128, VX128(6, 80), VX128 , General , 0),
|
||||
SCAN_INSTRUCTION(vcmpgefp128, VX128_R(6, 128), VX128_R , General , 0),
|
||||
SCAN_INSTRUCTION(vslw128, VX128(6, 208), VX128 , General , 0),
|
||||
SCAN_INSTRUCTION(vcmpgtfp128, VX128_R(6, 256), VX128_R , General , 0),
|
||||
SCAN_INSTRUCTION(vsraw128, VX128(6, 336), VX128 , General , 0),
|
||||
SCAN_INSTRUCTION(vcmpbfp128, VX128_R(6, 384), VX128_R , General , 0),
|
||||
SCAN_INSTRUCTION(vsrw128, VX128(6, 464), VX128 , General , 0),
|
||||
SCAN_INSTRUCTION(vcmpequw128, VX128_R(6, 512), VX128_R , General , 0),
|
||||
SCAN_INSTRUCTION(vmaxfp128, VX128(6, 640), VX128 , General , 0),
|
||||
SCAN_INSTRUCTION(vminfp128, VX128(6, 704), VX128 , General , 0),
|
||||
SCAN_INSTRUCTION(vmrghw128, VX128(6, 768), VX128 , General , 0),
|
||||
SCAN_INSTRUCTION(vmrglw128, VX128(6, 832), VX128 , General , 0),
|
||||
SCAN_INSTRUCTION(vupkhsb128, VX128(6, 896), VX128 , General , 0),
|
||||
SCAN_INSTRUCTION(vupklsb128, VX128(6, 960), VX128 , General , 0),
|
||||
};
|
||||
#undef OP
|
||||
#undef VX128
|
||||
#undef VX128_1
|
||||
#undef VX128_2
|
||||
#undef VX128_3
|
||||
#undef VX128_4
|
||||
#undef VX128_5
|
||||
#undef VX128_P
|
||||
|
||||
|
||||
#undef FLAG
|
||||
#undef INSTRUCTION
|
||||
#undef EMPTY
|
||||
|
||||
|
||||
} // namespace tables
|
||||
} // namespace ppc
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_PPC_INSTR_TABLE_H_
|
||||
@@ -1,40 +0,0 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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_REGISTERS_H_
|
||||
#define XENIA_CPU_PPC_REGISTERS_H_
|
||||
|
||||
#include <xenia/common.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace ppc {
|
||||
|
||||
|
||||
typedef bool (*RegisterHandlesCallback)(void* context, uint32_t addr);
|
||||
typedef uint64_t (*RegisterReadCallback)(void* context, uint32_t addr);
|
||||
typedef void (*RegisterWriteCallback)(void* context, uint32_t addr,
|
||||
uint64_t value);
|
||||
|
||||
|
||||
typedef struct {
|
||||
void* context;
|
||||
RegisterHandlesCallback handles;
|
||||
RegisterReadCallback read;
|
||||
RegisterWriteCallback write;
|
||||
} RegisterAccessCallbacks;
|
||||
|
||||
|
||||
} // namespace ppc
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_PPC_REGISTERS_H_
|
||||
@@ -1,17 +0,0 @@
|
||||
# Copyright 2013 Ben Vanik. All Rights Reserved.
|
||||
{
|
||||
'sources': [
|
||||
'disasm.h',
|
||||
'disasm_altivec.cc',
|
||||
'disasm_alu.cc',
|
||||
'disasm_control.cc',
|
||||
'disasm_fpu.cc',
|
||||
'disasm_memory.cc',
|
||||
'instr.cc',
|
||||
'instr.h',
|
||||
'instr_tables.h',
|
||||
'registers.h',
|
||||
'state.cc',
|
||||
'state.h',
|
||||
],
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
@@ -1,167 +0,0 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
XEDECLARECLASS2(xe, cpu, Processor);
|
||||
XEDECLARECLASS2(xe, cpu, ThreadState);
|
||||
|
||||
|
||||
// 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;
|
||||
};
|
||||
struct {
|
||||
uint32_t ix;
|
||||
uint32_t iy;
|
||||
uint32_t iz;
|
||||
uint32_t iw;
|
||||
};
|
||||
float f4[4];
|
||||
uint32_t i4[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
|
||||
|
||||
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
|
||||
|
||||
uint64_t r[32]; // General purpose registers
|
||||
xe_float4_t v[128]; // VMX128 vector registers
|
||||
double f[32]; // Floating-point registers
|
||||
|
||||
// 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;
|
||||
xe::cpu::Processor* processor;
|
||||
xe::cpu::ThreadState* thread_state;
|
||||
|
||||
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_
|
||||
@@ -10,14 +10,16 @@
|
||||
#include <xenia/cpu/processor.h>
|
||||
|
||||
#include <xenia/emulator.h>
|
||||
#include <xenia/cpu/jit.h>
|
||||
#include <xenia/cpu/ppc/disasm.h>
|
||||
#include <xenia/cpu/ppc/state.h>
|
||||
#include <xenia/cpu/xenon_memory.h>
|
||||
#include <xenia/cpu/xenon_runtime.h>
|
||||
|
||||
|
||||
using namespace alloy;
|
||||
using namespace alloy::backend;
|
||||
using namespace alloy::frontend::ppc;
|
||||
using namespace alloy::runtime;
|
||||
using namespace xe;
|
||||
using namespace xe::cpu;
|
||||
using namespace xe::cpu::sdb;
|
||||
|
||||
|
||||
namespace {
|
||||
@@ -31,11 +33,11 @@ namespace {
|
||||
}
|
||||
has_initialized = true;
|
||||
|
||||
ppc::RegisterDisasmCategoryAltivec();
|
||||
ppc::RegisterDisasmCategoryALU();
|
||||
ppc::RegisterDisasmCategoryControl();
|
||||
ppc::RegisterDisasmCategoryFPU();
|
||||
ppc::RegisterDisasmCategoryMemory();
|
||||
//ppc::RegisterDisasmCategoryAltivec();
|
||||
//ppc::RegisterDisasmCategoryALU();
|
||||
//ppc::RegisterDisasmCategoryControl();
|
||||
//ppc::RegisterDisasmCategoryFPU();
|
||||
//ppc::RegisterDisasmCategoryMemory();
|
||||
|
||||
atexit(CleanupOnShutdown);
|
||||
}
|
||||
@@ -45,212 +47,109 @@ namespace {
|
||||
}
|
||||
|
||||
|
||||
Processor::Processor(Emulator* emulator, Backend* backend) :
|
||||
sym_table_(NULL), jit_(NULL),
|
||||
interrupt_thread_lock_(NULL), interrupt_thread_state_(NULL) {
|
||||
emulator_ = emulator;
|
||||
backend_ = backend;
|
||||
memory_ = xe_memory_retain(emulator->memory());
|
||||
export_resolver_ = emulator->export_resolver();
|
||||
|
||||
sym_lock_ = xe_mutex_alloc(10000);
|
||||
|
||||
Processor::Processor(Emulator* emulator) :
|
||||
emulator_(emulator), export_resolver_(emulator->export_resolver()),
|
||||
runtime_(0), memory_(emulator->memory()),
|
||||
interrupt_thread_lock_(NULL), interrupt_thread_state_(NULL),
|
||||
interrupt_thread_block_(0) {
|
||||
InitializeIfNeeded();
|
||||
}
|
||||
|
||||
Processor::~Processor() {
|
||||
// Cleanup all modules.
|
||||
for (std::vector<ExecModule*>::iterator it = modules_.begin();
|
||||
it != modules_.end(); ++it) {
|
||||
ExecModule* exec_module = *it;
|
||||
if (jit_) {
|
||||
jit_->UninitModule(exec_module);
|
||||
}
|
||||
delete exec_module;
|
||||
if (interrupt_thread_block_) {
|
||||
memory_->HeapFree(interrupt_thread_block_, 2048);
|
||||
delete interrupt_thread_state_;
|
||||
xe_mutex_free(interrupt_thread_lock_);
|
||||
}
|
||||
modules_.clear();
|
||||
|
||||
xe_memory_heap_free(memory_, interrupt_thread_block_, 2048);
|
||||
DeallocThread(interrupt_thread_state_);
|
||||
xe_mutex_free(interrupt_thread_lock_);
|
||||
|
||||
delete jit_;
|
||||
delete sym_table_;
|
||||
xe_mutex_free(sym_lock_);
|
||||
|
||||
xe_memory_release(memory_);
|
||||
delete runtime_;
|
||||
}
|
||||
|
||||
int Processor::Setup() {
|
||||
XEASSERTNULL(jit_);
|
||||
XEASSERTNULL(runtime_);
|
||||
|
||||
interrupt_thread_lock_ = xe_mutex_alloc(10000);
|
||||
interrupt_thread_state_ = AllocThread(16 * 1024, 0, 0);
|
||||
interrupt_thread_block_ = xe_memory_heap_alloc(
|
||||
memory_, 0, 2048, XE_MEMORY_FLAG_ZERO);
|
||||
interrupt_thread_state_->ppc_state()->r[13] = interrupt_thread_block_;
|
||||
|
||||
sym_table_ = new SymbolTable();
|
||||
|
||||
jit_ = backend_->CreateJIT(memory_, sym_table_);
|
||||
if (jit_->Setup()) {
|
||||
XELOGE("Unable to create JIT");
|
||||
runtime_ = new XenonRuntime(memory_, export_resolver_);
|
||||
if (!runtime_) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
Backend* backend = 0;
|
||||
// Backend* backend = new alloy::backend::ivm::IVMBackend(
|
||||
// runtime);
|
||||
// Backend* backend = new alloy::backend::x64::X64Backend(
|
||||
// runtime);
|
||||
int result = runtime_->Initialize(backend);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
interrupt_thread_lock_ = xe_mutex_alloc(10000);
|
||||
interrupt_thread_state_ = new XenonThreadState(
|
||||
runtime_, 0, 16 * 1024, 0);
|
||||
interrupt_thread_block_ = memory_->HeapAlloc(
|
||||
0, 2048, MEMORY_FLAG_ZERO);
|
||||
interrupt_thread_state_->context()->r[13] = interrupt_thread_block_;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Processor::AddRegisterAccessCallbacks(
|
||||
ppc::RegisterAccessCallbacks callbacks) {
|
||||
XEASSERTNOTNULL(jit_);
|
||||
jit_->AddRegisterAccessCallbacks(callbacks);
|
||||
xe::cpu::RegisterAccessCallbacks callbacks) {
|
||||
runtime_->AddRegisterAccessCallbacks(callbacks);
|
||||
}
|
||||
|
||||
int Processor::LoadRawBinary(const xechar_t* path, uint32_t start_address) {
|
||||
ExecModule* exec_module = NULL;
|
||||
const xechar_t* name = xestrrchr(path, XE_PATH_SEPARATOR) + 1;
|
||||
|
||||
// TODO(benvanik): map file from filesystem API, not via platform API.
|
||||
xe_mmap_ref mmap = xe_mmap_open(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;
|
||||
|
||||
// Place the data into memory at the desired address.
|
||||
XEEXPECTNOTZERO(xe_memory_heap_alloc(
|
||||
memory_, start_address, (uint32_t)length, XE_MEMORY_FLAG_ZERO));
|
||||
XEEXPECTZERO(xe_copy_memory(
|
||||
xe_memory_addr(memory_, start_address), length, addr, length));
|
||||
|
||||
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));
|
||||
|
||||
// Prepare the module.
|
||||
// This will analyze it, generate code (if needed), and adds methods to
|
||||
// the function table.
|
||||
exec_module = new ExecModule(
|
||||
memory_, export_resolver_, sym_table_, name_a, path_a);
|
||||
XEEXPECTZERO(exec_module->PrepareRawBinary(
|
||||
start_address, start_address + (uint32_t)length));
|
||||
|
||||
// Initialize the module and prepare it for execution.
|
||||
XEEXPECTZERO(jit_->InitModule(exec_module));
|
||||
|
||||
xe_mutex_lock(sym_lock_);
|
||||
modules_.push_back(exec_module);
|
||||
xe_mutex_unlock(sym_lock_);
|
||||
|
||||
result_code = 0;
|
||||
XECLEANUP:
|
||||
if (result_code) {
|
||||
xe_memory_heap_free(memory_, start_address, (uint32_t)length);
|
||||
delete exec_module;
|
||||
}
|
||||
xe_mmap_release(mmap);
|
||||
return result_code;
|
||||
}
|
||||
|
||||
int Processor::LoadXexModule(const char* name, const char* path,
|
||||
xe_xex2_ref xex) {
|
||||
int result_code = 1;
|
||||
|
||||
// Prepare the module.
|
||||
// This will analyze it, generate code (if needed), and adds methods to
|
||||
// the function table.
|
||||
ExecModule* exec_module = new ExecModule(
|
||||
memory_, export_resolver_, sym_table_, name, path);
|
||||
XEEXPECTZERO(exec_module->PrepareXexModule(xex));
|
||||
|
||||
// Initialize the module and prepare it for execution.
|
||||
XEEXPECTZERO(jit_->InitModule(exec_module));
|
||||
|
||||
xe_mutex_lock(sym_lock_);
|
||||
modules_.push_back(exec_module);
|
||||
xe_mutex_unlock(sym_lock_);
|
||||
|
||||
result_code = 0;
|
||||
XECLEANUP:
|
||||
if (result_code) {
|
||||
delete exec_module;
|
||||
}
|
||||
return result_code;
|
||||
}
|
||||
|
||||
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,
|
||||
uint32_t thread_id) {
|
||||
ThreadState* thread_state = new ThreadState(
|
||||
this, stack_size, thread_state_address, thread_id);
|
||||
return thread_state;
|
||||
}
|
||||
|
||||
void Processor::DeallocThread(ThreadState* thread_state) {
|
||||
delete thread_state;
|
||||
}
|
||||
|
||||
int Processor::Execute(ThreadState* thread_state, uint32_t address) {
|
||||
int Processor::Execute(XenonThreadState* thread_state, uint64_t address) {
|
||||
// Attempt to get the function.
|
||||
FunctionSymbol* fn_symbol = GetFunction(address);
|
||||
if (!fn_symbol) {
|
||||
Function* fn;
|
||||
if (runtime_->ResolveFunction(address, &fn)) {
|
||||
// Symbol not found in any module.
|
||||
XELOGCPU("Execute(%.8X): failed to find function", address);
|
||||
return 1;
|
||||
}
|
||||
|
||||
xe_ppc_state_t* ppc_state = thread_state->ppc_state();
|
||||
PPCContext* context = thread_state->context();
|
||||
|
||||
// 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;
|
||||
context->lr = lr;
|
||||
|
||||
// Execute the function.
|
||||
return jit_->Execute(ppc_state, fn_symbol);
|
||||
fn->Call(thread_state);
|
||||
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;
|
||||
uint64_t Processor::Execute(
|
||||
XenonThreadState* thread_state, uint64_t address, uint64_t arg0) {
|
||||
PPCContext* context = thread_state->context();
|
||||
context->r[3] = arg0;
|
||||
if (Execute(thread_state, address)) {
|
||||
return 0xDEADBABE;
|
||||
}
|
||||
return ppc_state->r[3];
|
||||
return context->r[3];
|
||||
}
|
||||
|
||||
uint64_t Processor::Execute(ThreadState* thread_state, uint32_t address,
|
||||
uint64_t arg0, uint64_t arg1) {
|
||||
xe_ppc_state_t* ppc_state = thread_state->ppc_state();
|
||||
ppc_state->r[3] = arg0;
|
||||
ppc_state->r[4] = arg1;
|
||||
uint64_t Processor::Execute(
|
||||
XenonThreadState* thread_state, uint64_t address, uint64_t arg0,
|
||||
uint64_t arg1) {
|
||||
PPCContext* context = thread_state->context();
|
||||
context->r[3] = arg0;
|
||||
context->r[4] = arg1;
|
||||
if (Execute(thread_state, address)) {
|
||||
return 0xDEADBABE;
|
||||
}
|
||||
return ppc_state->r[3];
|
||||
return context->r[3];
|
||||
}
|
||||
|
||||
uint64_t Processor::ExecuteInterrupt(uint32_t cpu,
|
||||
uint32_t address,
|
||||
uint64_t arg0, uint64_t arg1) {
|
||||
uint64_t Processor::ExecuteInterrupt(
|
||||
uint32_t cpu, uint64_t address, uint64_t arg0, uint64_t arg1) {
|
||||
// Acquire lock on interrupt thread (we can only dispatch one at a time).
|
||||
xe_mutex_lock(interrupt_thread_lock_);
|
||||
|
||||
// Set 0x10C(r13) to the current CPU ID.
|
||||
uint8_t* p = xe_memory_addr(memory_, 0);
|
||||
uint8_t* p = memory_->membase();
|
||||
XESETUINT8BE(p + interrupt_thread_block_ + 0x10C, cpu);
|
||||
|
||||
// Execute interrupt.
|
||||
@@ -259,43 +158,3 @@ uint64_t Processor::ExecuteInterrupt(uint32_t cpu,
|
||||
xe_mutex_unlock(interrupt_thread_lock_);
|
||||
return result;
|
||||
}
|
||||
|
||||
FunctionSymbol* Processor::GetFunction(uint32_t address) {
|
||||
// Attempt to grab the function symbol from the global lookup table.
|
||||
// The symbol table takes a lock so it should be safe.
|
||||
FunctionSymbol* fn_symbol = sym_table_->GetFunction(address);
|
||||
if (fn_symbol) {
|
||||
return fn_symbol;
|
||||
}
|
||||
|
||||
// Search all modules for the function symbol.
|
||||
// Each module will see if the address is within its code range and if the
|
||||
// symbol is not found (likely) it will do analysis on it.
|
||||
// TODO(benvanik): make this more efficient. Could use a binary search or
|
||||
// something more clever.
|
||||
xe_mutex_lock(sym_lock_);
|
||||
for (std::vector<ExecModule*>::iterator it = modules_.begin();
|
||||
it != modules_.end(); ++it) {
|
||||
fn_symbol = (*it)->FindFunctionSymbol(address);
|
||||
if (fn_symbol) {
|
||||
xe_mutex_unlock(sym_lock_);
|
||||
return fn_symbol;
|
||||
}
|
||||
}
|
||||
xe_mutex_unlock(sym_lock_);
|
||||
|
||||
// Not found at all? That seems wrong...
|
||||
XEASSERTALWAYS();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void* Processor::GetFunctionPointer(uint32_t address) {
|
||||
// Attempt to get the function.
|
||||
FunctionSymbol* fn_symbol = GetFunction(address);
|
||||
if (!fn_symbol || fn_symbol->type == FunctionSymbol::Unknown) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Grab the pointer.
|
||||
return jit_->GetFunctionPointer(fn_symbol);
|
||||
}
|
||||
|
||||
@@ -11,71 +11,60 @@
|
||||
#define XENIA_CPU_PROCESSOR_H_
|
||||
|
||||
#include <xenia/core.h>
|
||||
#include <alloy/runtime/register_access.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <xenia/cpu/backend.h>
|
||||
#include <xenia/cpu/exec_module.h>
|
||||
#include <xenia/cpu/thread_state.h>
|
||||
#include <xenia/cpu/sdb/symbol_table.h>
|
||||
|
||||
|
||||
XEDECLARECLASS1(xe, Emulator);
|
||||
XEDECLARECLASS1(xe, ExportResolver);
|
||||
XEDECLARECLASS2(xe, cpu, XenonMemory);
|
||||
XEDECLARECLASS2(xe, cpu, XenonRuntime);
|
||||
XEDECLARECLASS2(xe, cpu, XenonThreadState);
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
|
||||
class JIT;
|
||||
using RegisterAccessCallbacks = alloy::runtime::RegisterAccessCallbacks;
|
||||
using RegisterHandlesCallback = alloy::runtime::RegisterHandlesCallback;
|
||||
using RegisterReadCallback = alloy::runtime::RegisterReadCallback;
|
||||
using RegisterWriteCallback = alloy::runtime::RegisterWriteCallback;
|
||||
|
||||
|
||||
class Processor {
|
||||
public:
|
||||
Processor(Emulator* emulator, Backend* backend);
|
||||
Processor(Emulator* emulator);
|
||||
~Processor();
|
||||
|
||||
xe_memory_ref memory() const { return memory_; }
|
||||
ExportResolver* export_resolver() const { return export_resolver_; }
|
||||
XenonRuntime* runtime() const { return runtime_; }
|
||||
Memory* memory() const { return memory_; }
|
||||
|
||||
int Setup();
|
||||
|
||||
void AddRegisterAccessCallbacks(ppc::RegisterAccessCallbacks callbacks);
|
||||
void AddRegisterAccessCallbacks(RegisterAccessCallbacks callbacks);
|
||||
|
||||
int LoadRawBinary(const xechar_t* path, uint32_t start_address);
|
||||
int LoadXexModule(const char* name, const char* path, xe_xex2_ref xex);
|
||||
|
||||
uint32_t CreateCallback(void (*callback)(void* data), void* data);
|
||||
|
||||
ThreadState* AllocThread(uint32_t stack_size, uint32_t thread_state_address,
|
||||
uint32_t thread_id);
|
||||
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);
|
||||
uint64_t Execute(ThreadState* thread_state, uint32_t address,
|
||||
uint64_t arg0, uint64_t arg1);
|
||||
int Execute(
|
||||
XenonThreadState* thread_state, uint64_t address);
|
||||
uint64_t Execute(
|
||||
XenonThreadState* thread_state, uint64_t address, uint64_t arg0);
|
||||
uint64_t Execute(
|
||||
XenonThreadState* thread_state, uint64_t address, uint64_t arg0,
|
||||
uint64_t arg1);
|
||||
|
||||
uint64_t ExecuteInterrupt(
|
||||
uint32_t cpu, uint32_t address, uint64_t arg0, uint64_t arg1);
|
||||
|
||||
sdb::FunctionSymbol* GetFunction(uint32_t address);
|
||||
void* GetFunctionPointer(uint32_t address);
|
||||
uint32_t cpu, uint64_t address, uint64_t arg0, uint64_t arg1);
|
||||
|
||||
private:
|
||||
Emulator* emulator_;
|
||||
Backend* backend_;
|
||||
xe_memory_ref memory_;
|
||||
ExportResolver* export_resolver_;
|
||||
Emulator* emulator_;
|
||||
ExportResolver* export_resolver_;
|
||||
|
||||
sdb::SymbolTable* sym_table_;
|
||||
JIT* jit_;
|
||||
std::vector<ExecModule*> modules_;
|
||||
xe_mutex_t* sym_lock_;
|
||||
XenonRuntime* runtime_;
|
||||
Memory* memory_;
|
||||
|
||||
xe_mutex_t* interrupt_thread_lock_;
|
||||
ThreadState* interrupt_thread_state_;
|
||||
uint32_t interrupt_thread_block_;
|
||||
xe_mutex_t* interrupt_thread_lock_;
|
||||
XenonThreadState* interrupt_thread_state_;
|
||||
uint64_t interrupt_thread_block_;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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/symbol_table.h>
|
||||
#include <xenia/cpu/sdb/xex_symbol_database.h>
|
||||
|
||||
#endif // XENIA_CPU_SDB_H_
|
||||
@@ -1,41 +0,0 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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;
|
||||
|
||||
|
||||
RawSymbolDatabase::RawSymbolDatabase(
|
||||
xe_memory_ref memory, ExportResolver* export_resolver,
|
||||
SymbolTable* sym_table,
|
||||
uint32_t start_address, uint32_t end_address) :
|
||||
SymbolDatabase(memory, export_resolver, sym_table) {
|
||||
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_;
|
||||
}
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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,
|
||||
ExportResolver* export_resolver,
|
||||
SymbolTable* sym_table,
|
||||
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_
|
||||
@@ -1,15 +0,0 @@
|
||||
# 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',
|
||||
'symbol_table.cc',
|
||||
'symbol_table.h',
|
||||
'xex_symbol_database.cc',
|
||||
'xex_symbol_database.h',
|
||||
]
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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;
|
||||
|
||||
|
||||
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),
|
||||
impl_value(NULL) {
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void FunctionSymbol::AddCall(FunctionSymbol* source, FunctionSymbol* target) {
|
||||
source->outgoing_calls.push_back(FunctionCall(0, source, target));
|
||||
target->incoming_calls.push_back(FunctionCall(0, source, target));
|
||||
}
|
||||
|
||||
|
||||
VariableSymbol::VariableSymbol() :
|
||||
Symbol(Variable),
|
||||
address(0),
|
||||
kernel_export(0) {
|
||||
}
|
||||
|
||||
VariableSymbol::~VariableSymbol() {
|
||||
}
|
||||
|
||||
|
||||
ExceptionEntrySymbol::ExceptionEntrySymbol() :
|
||||
Symbol(ExceptionEntry),
|
||||
address(0), function(0) {
|
||||
}
|
||||
@@ -1,170 +0,0 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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/export_resolver.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace sdb {
|
||||
|
||||
|
||||
class FunctionSymbol;
|
||||
class VariableSymbol;
|
||||
|
||||
|
||||
class FunctionCall {
|
||||
public:
|
||||
uint32_t address;
|
||||
FunctionSymbol* source;
|
||||
FunctionSymbol* target;
|
||||
|
||||
FunctionCall(uint32_t address, FunctionSymbol* source,
|
||||
FunctionSymbol* target) :
|
||||
address(address), source(source), target(target) {}
|
||||
};
|
||||
|
||||
class VariableAccess {
|
||||
public:
|
||||
uint32_t address;
|
||||
FunctionSymbol* source;
|
||||
VariableSymbol* target;
|
||||
|
||||
VariableAccess(uint32_t address, FunctionSymbol* source,
|
||||
VariableSymbol* target) :
|
||||
address(address), source(source), target(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,
|
||||
kFlagSaveFpr = 1 << 3,
|
||||
kFlagRestFpr = 1 << 4,
|
||||
kFlagSaveVmx = 1 << 5,
|
||||
kFlagRestVmx = 1 << 6,
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
KernelExport* kernel_export;
|
||||
ExceptionEntrySymbol* ee;
|
||||
|
||||
// Implementation-specific value. This could be a JIT'ed function ref
|
||||
// or some other structure. Never freed by the SDB.
|
||||
void* impl_value;
|
||||
size_t impl_size;
|
||||
|
||||
std::vector<FunctionCall> incoming_calls;
|
||||
std::vector<FunctionCall> outgoing_calls;
|
||||
std::vector<VariableAccess> variable_accesses;
|
||||
|
||||
std::map<uint32_t, FunctionBlock*> blocks;
|
||||
|
||||
static void AddCall(FunctionSymbol* source, FunctionSymbol* target);
|
||||
};
|
||||
|
||||
class VariableSymbol : public Symbol {
|
||||
public:
|
||||
VariableSymbol();
|
||||
virtual ~VariableSymbol();
|
||||
|
||||
uint32_t address;
|
||||
|
||||
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_
|
||||
@@ -1,826 +0,0 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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;
|
||||
|
||||
|
||||
SymbolDatabase::SymbolDatabase(xe_memory_ref memory,
|
||||
ExportResolver* export_resolver,
|
||||
SymbolTable* sym_table) :
|
||||
function_count_(0), variable_count_(0) {
|
||||
memory_ = xe_memory_retain(memory);
|
||||
export_resolver_ = export_resolver;
|
||||
sym_table_ = sym_table;
|
||||
}
|
||||
|
||||
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("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* opt_call_source) {
|
||||
FunctionSymbol* fn = GetFunction(address);
|
||||
if (fn) {
|
||||
if (opt_call_source) {
|
||||
FunctionSymbol::AddCall(opt_call_source, fn);
|
||||
}
|
||||
return fn;
|
||||
}
|
||||
|
||||
// Ignore values outside of the .text range.
|
||||
if (!IsValueInTextRange(address)) {
|
||||
XELOGSDB("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);
|
||||
|
||||
if (opt_call_source) {
|
||||
FunctionSymbol::AddCall(opt_call_source, 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, bool analyze) {
|
||||
XEASSERT(address);
|
||||
|
||||
SymbolMap::iterator i = symbols_.find(address);
|
||||
if (i != symbols_.end() && i->second->symbol_type == Symbol::Function) {
|
||||
return static_cast<FunctionSymbol*>(i->second);
|
||||
}
|
||||
|
||||
// Missing function - analyze on demand.
|
||||
// TODO(benvanik): track this for statistics.
|
||||
FunctionSymbol* fn = new FunctionSymbol();
|
||||
fn->start_address = address;
|
||||
function_count_++;
|
||||
symbols_.insert(SymbolMap::value_type(address, fn));
|
||||
scan_queue_.push_back(fn);
|
||||
|
||||
if (analyze) {
|
||||
FlushQueue();
|
||||
}
|
||||
|
||||
return fn;
|
||||
}
|
||||
|
||||
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.
|
||||
uint32_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("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;
|
||||
bool starts_with_mfspr_lr = false;
|
||||
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("function end %.8X (0x00000000 read)", addr);
|
||||
break;
|
||||
}
|
||||
|
||||
// Check if the function starts with a mfspr lr, as that's a good indication
|
||||
// of whether or not this is a normal function with a prolog/epilog.
|
||||
// Some valid leaf functions won't have this, but most will.
|
||||
if (addr == fn->start_address &&
|
||||
i.type &&
|
||||
i.type->opcode == 0x7C0002A6 &&
|
||||
(((i.XFX.spr & 0x1F) << 5) | ((i.XFX.spr >> 5) & 0x1F)) == 8) {
|
||||
starts_with_mfspr_lr = true;
|
||||
}
|
||||
|
||||
// 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("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("ignoring blr %.8X (branch to %.8X)", addr, furthest_target);
|
||||
} else {
|
||||
// Function end point.
|
||||
XELOGSDB("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("ignoring bctr %.8X (branch to %.8X)", addr,
|
||||
furthest_target);
|
||||
} else {
|
||||
// Function end point.
|
||||
XELOGSDB("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("bl %.8X -> %.8X", addr, target);
|
||||
block->outgoing_type = FunctionBlock::kTargetFunction;
|
||||
|
||||
// Queue call target if needed.
|
||||
GetOrInsertFunction(target);
|
||||
} else {
|
||||
XELOGSDB("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("function end %.8X (back b)", addr);
|
||||
ends_fn = true;
|
||||
}
|
||||
|
||||
// If the target is not a branch and it goes to before the current
|
||||
// address it's definitely a tail call.
|
||||
if (!ends_fn &&
|
||||
target < addr) {
|
||||
XELOGSDB("function end %.8X (back b before addr)", 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("function end %.8X (__restgprlr_*)", addr);
|
||||
ends_fn = true;
|
||||
}
|
||||
|
||||
// Heuristic: if there's an unconditional branch in the first block of
|
||||
// the function it's likely a thunk.
|
||||
// Ex:
|
||||
// li r3, 0
|
||||
// b KeBugCheck
|
||||
// This check may hit on functions that jump over data code, so only
|
||||
// trigger this check in leaf functions (no mfspr lr/prolog).
|
||||
if (!ends_fn &&
|
||||
!starts_with_mfspr_lr &&
|
||||
fn->blocks.size() == 1) {
|
||||
XELOGSDB("HEURISTIC: ending at simple leaf thunk %.8X", addr);
|
||||
ends_fn = true;
|
||||
}
|
||||
|
||||
// Heuristic: if this is an unconditional branch at the end of the
|
||||
// function (nothing jumps over us) and we are jumping forward there's
|
||||
// a good chance it's a tail call.
|
||||
// This may not be true if the code is jumping over data/etc.
|
||||
// TODO(benvanik): figure out how to do this reliably. This check as is
|
||||
// is too aggressive and turns a lot of valid branches into tail calls.
|
||||
// It seems like a lot of functions end up with some prologue bit then
|
||||
// jump deep inside only to jump back towards the top soon after. May
|
||||
// need something more complex than just a simple 1-pass system to
|
||||
// detect these, unless more signals can be found.
|
||||
/*
|
||||
if (!ends_fn &&
|
||||
target > addr &&
|
||||
furthest_target < addr) {
|
||||
XELOGSDB("HEURISTIC: ending at tail call branch %.8X", 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("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("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("bclrl %.8X", addr);
|
||||
} else {
|
||||
XELOGSDB("bclr %.8X", addr);
|
||||
}
|
||||
ends_block = true;
|
||||
} else if (i.type->opcode == 0x4C000420) {
|
||||
// bcctr/bcctrl
|
||||
block->outgoing_type = FunctionBlock::kTargetCTR;
|
||||
if (i.XL.LK) {
|
||||
XELOGSDB("bcctrl %.8X", addr);
|
||||
} else {
|
||||
XELOGSDB("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("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("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
|
||||
|
||||
// TODO(benvanik): queue for add without expensive locks?
|
||||
sym_table_->AddFunction(fn->start_address, fn);
|
||||
|
||||
// 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 (block->outgoing_address) {
|
||||
// If we have some address try to see what it is.
|
||||
if (block->outgoing_type == FunctionBlock::kTargetUnknown) {
|
||||
if (block->outgoing_address > fn->start_address &&
|
||||
block->outgoing_address <= fn->end_address) {
|
||||
// Branch into a block in this function.
|
||||
// Note that we make branches to the start address act as function
|
||||
// calls, as they are almost always recursion cases.
|
||||
block->outgoing_type = FunctionBlock::kTargetBlock;
|
||||
} else {
|
||||
// Function call.
|
||||
block->outgoing_type = FunctionBlock::kTargetFunction;
|
||||
}
|
||||
}
|
||||
|
||||
// If we were already specified, use that.
|
||||
switch (block->outgoing_type) {
|
||||
case 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("block target not found: %.8X", block->outgoing_address);
|
||||
XEASSERTALWAYS();
|
||||
}
|
||||
break;
|
||||
case FunctionBlock::kTargetFunction:
|
||||
block->outgoing_function = GetFunction(block->outgoing_address);
|
||||
XEASSERTNOTNULL(block->outgoing_function);
|
||||
FunctionSymbol::AddCall(fn, block->outgoing_function);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
XELOGSDB("Finished analyzing %.8X", fn->start_address);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SymbolDatabase::CompleteFunctionGraph(FunctionSymbol* fn) {
|
||||
// Find variable accesses.
|
||||
// TODO(benvanik): data analysis to find variable accesses.
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(benvanik): re-evaluate if this is worth it.
|
||||
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("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)strtoul(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("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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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/export_resolver.h>
|
||||
#include <xenia/cpu/sdb/symbol.h>
|
||||
#include <xenia/cpu/sdb/symbol_table.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace sdb {
|
||||
|
||||
|
||||
class SymbolDatabase {
|
||||
public:
|
||||
SymbolDatabase(xe_memory_ref memory, ExportResolver* export_resolver,
|
||||
SymbolTable* sym_table);
|
||||
virtual ~SymbolDatabase();
|
||||
|
||||
virtual int Analyze();
|
||||
|
||||
Symbol* GetSymbol(uint32_t address);
|
||||
ExceptionEntrySymbol* GetOrInsertExceptionEntry(uint32_t address);
|
||||
FunctionSymbol* GetOrInsertFunction(
|
||||
uint32_t address, FunctionSymbol* opt_call_source = NULL);
|
||||
VariableSymbol* GetOrInsertVariable(uint32_t address);
|
||||
FunctionSymbol* GetFunction(uint32_t address, bool analyze = false);
|
||||
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_;
|
||||
ExportResolver* export_resolver_;
|
||||
SymbolTable* sym_table_;
|
||||
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_
|
||||
@@ -1,41 +0,0 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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_table.h>
|
||||
|
||||
|
||||
using namespace xe;
|
||||
using namespace xe::cpu;
|
||||
using namespace xe::cpu::sdb;
|
||||
|
||||
|
||||
SymbolTable::SymbolTable() {
|
||||
lock_ = xe_mutex_alloc(10000);
|
||||
XEASSERTNOTNULL(lock_);
|
||||
}
|
||||
|
||||
SymbolTable::~SymbolTable() {
|
||||
xe_mutex_free(lock_);
|
||||
lock_ = NULL;
|
||||
}
|
||||
|
||||
int SymbolTable::AddFunction(uint32_t address, FunctionSymbol* symbol) {
|
||||
xe_mutex_lock(lock_);
|
||||
map_[address] = symbol;
|
||||
xe_mutex_unlock(lock_);
|
||||
return 0;
|
||||
}
|
||||
|
||||
FunctionSymbol* SymbolTable::GetFunction(uint32_t address) {
|
||||
xe_mutex_lock(lock_);
|
||||
FunctionMap::const_iterator it = map_.find(address);
|
||||
FunctionSymbol* result = it != map_.end() ? it->second : NULL;
|
||||
xe_mutex_unlock(lock_);
|
||||
return result;
|
||||
}
|
||||
@@ -1,772 +0,0 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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;
|
||||
|
||||
|
||||
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;
|
||||
uint32_t total_length; // in bytes
|
||||
uint32_t prolog_length; // in bytes
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
XexSymbolDatabase::XexSymbolDatabase(
|
||||
xe_memory_ref memory, ExportResolver* export_resolver,
|
||||
SymbolTable* sym_table, xe_xex2_ref xex) :
|
||||
SymbolDatabase(memory, export_resolver, sym_table) {
|
||||
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_* and the others.
|
||||
FindSaveRest();
|
||||
|
||||
// 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::FindSaveRest() {
|
||||
// Special stack save/restore functions.
|
||||
// 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.
|
||||
// __savegprlr_14 to __savegprlr_31
|
||||
// __restgprlr_14 to __restgprlr_31
|
||||
static const uint32_t gprlr_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,
|
||||
};
|
||||
// __savefpr_14 to __savefpr_31
|
||||
// __restfpr_14 to __restfpr_31
|
||||
static const uint32_t fpr_code_values[] = {
|
||||
0x70FFCCD9, // __savefpr_14
|
||||
0x78FFECD9, // __savefpr_15
|
||||
0x80FF0CDA, // __savefpr_16
|
||||
0x88FF2CDA, // __savefpr_17
|
||||
0x90FF4CDA, // __savefpr_18
|
||||
0x98FF6CDA, // __savefpr_19
|
||||
0xA0FF8CDA, // __savefpr_20
|
||||
0xA8FFACDA, // __savefpr_21
|
||||
0xB0FFCCDA, // __savefpr_22
|
||||
0xB8FFECDA, // __savefpr_23
|
||||
0xC0FF0CDB, // __savefpr_24
|
||||
0xC8FF2CDB, // __savefpr_25
|
||||
0xD0FF4CDB, // __savefpr_26
|
||||
0xD8FF6CDB, // __savefpr_27
|
||||
0xE0FF8CDB, // __savefpr_28
|
||||
0xE8FFACDB, // __savefpr_29
|
||||
0xF0FFCCDB, // __savefpr_30
|
||||
0xF8FFECDB, // __savefpr_31
|
||||
0x2000804E,
|
||||
0x70FFCCC9, // __restfpr_14
|
||||
0x78FFECC9, // __restfpr_15
|
||||
0x80FF0CCA, // __restfpr_16
|
||||
0x88FF2CCA, // __restfpr_17
|
||||
0x90FF4CCA, // __restfpr_18
|
||||
0x98FF6CCA, // __restfpr_19
|
||||
0xA0FF8CCA, // __restfpr_20
|
||||
0xA8FFACCA, // __restfpr_21
|
||||
0xB0FFCCCA, // __restfpr_22
|
||||
0xB8FFECCA, // __restfpr_23
|
||||
0xC0FF0CCB, // __restfpr_24
|
||||
0xC8FF2CCB, // __restfpr_25
|
||||
0xD0FF4CCB, // __restfpr_26
|
||||
0xD8FF6CCB, // __restfpr_27
|
||||
0xE0FF8CCB, // __restfpr_28
|
||||
0xE8FFACCB, // __restfpr_29
|
||||
0xF0FFCCCB, // __restfpr_30
|
||||
0xF8FFECCB, // __restfpr_31
|
||||
0x2000804E,
|
||||
};
|
||||
// __savevmx_14 to __savevmx_31
|
||||
// __savevmx_64 to __savevmx_127
|
||||
// __restvmx_14 to __restvmx_31
|
||||
// __restvmx_64 to __restvmx_127
|
||||
static const uint32_t vmx_code_values[] = {
|
||||
0xE0FE6039, // __savevmx_14
|
||||
0xCE61CB7D,
|
||||
0xF0FE6039,
|
||||
0xCE61EB7D,
|
||||
0x00FF6039,
|
||||
0xCE610B7E,
|
||||
0x10FF6039,
|
||||
0xCE612B7E,
|
||||
0x20FF6039,
|
||||
0xCE614B7E,
|
||||
0x30FF6039,
|
||||
0xCE616B7E,
|
||||
0x40FF6039,
|
||||
0xCE618B7E,
|
||||
0x50FF6039,
|
||||
0xCE61AB7E,
|
||||
0x60FF6039,
|
||||
0xCE61CB7E,
|
||||
0x70FF6039,
|
||||
0xCE61EB7E,
|
||||
0x80FF6039,
|
||||
0xCE610B7F,
|
||||
0x90FF6039,
|
||||
0xCE612B7F,
|
||||
0xA0FF6039,
|
||||
0xCE614B7F,
|
||||
0xB0FF6039,
|
||||
0xCE616B7F,
|
||||
0xC0FF6039,
|
||||
0xCE618B7F,
|
||||
0xD0FF6039,
|
||||
0xCE61AB7F,
|
||||
0xE0FF6039,
|
||||
0xCE61CB7F,
|
||||
0xF0FF6039, // __savevmx_31
|
||||
0xCE61EB7F,
|
||||
0x2000804E,
|
||||
|
||||
0x00FC6039, // __savevmx_64
|
||||
0xCB610B10,
|
||||
0x10FC6039,
|
||||
0xCB612B10,
|
||||
0x20FC6039,
|
||||
0xCB614B10,
|
||||
0x30FC6039,
|
||||
0xCB616B10,
|
||||
0x40FC6039,
|
||||
0xCB618B10,
|
||||
0x50FC6039,
|
||||
0xCB61AB10,
|
||||
0x60FC6039,
|
||||
0xCB61CB10,
|
||||
0x70FC6039,
|
||||
0xCB61EB10,
|
||||
0x80FC6039,
|
||||
0xCB610B11,
|
||||
0x90FC6039,
|
||||
0xCB612B11,
|
||||
0xA0FC6039,
|
||||
0xCB614B11,
|
||||
0xB0FC6039,
|
||||
0xCB616B11,
|
||||
0xC0FC6039,
|
||||
0xCB618B11,
|
||||
0xD0FC6039,
|
||||
0xCB61AB11,
|
||||
0xE0FC6039,
|
||||
0xCB61CB11,
|
||||
0xF0FC6039,
|
||||
0xCB61EB11,
|
||||
0x00FD6039,
|
||||
0xCB610B12,
|
||||
0x10FD6039,
|
||||
0xCB612B12,
|
||||
0x20FD6039,
|
||||
0xCB614B12,
|
||||
0x30FD6039,
|
||||
0xCB616B12,
|
||||
0x40FD6039,
|
||||
0xCB618B12,
|
||||
0x50FD6039,
|
||||
0xCB61AB12,
|
||||
0x60FD6039,
|
||||
0xCB61CB12,
|
||||
0x70FD6039,
|
||||
0xCB61EB12,
|
||||
0x80FD6039,
|
||||
0xCB610B13,
|
||||
0x90FD6039,
|
||||
0xCB612B13,
|
||||
0xA0FD6039,
|
||||
0xCB614B13,
|
||||
0xB0FD6039,
|
||||
0xCB616B13,
|
||||
0xC0FD6039,
|
||||
0xCB618B13,
|
||||
0xD0FD6039,
|
||||
0xCB61AB13,
|
||||
0xE0FD6039,
|
||||
0xCB61CB13,
|
||||
0xF0FD6039,
|
||||
0xCB61EB13,
|
||||
0x00FE6039,
|
||||
0xCF610B10,
|
||||
0x10FE6039,
|
||||
0xCF612B10,
|
||||
0x20FE6039,
|
||||
0xCF614B10,
|
||||
0x30FE6039,
|
||||
0xCF616B10,
|
||||
0x40FE6039,
|
||||
0xCF618B10,
|
||||
0x50FE6039,
|
||||
0xCF61AB10,
|
||||
0x60FE6039,
|
||||
0xCF61CB10,
|
||||
0x70FE6039,
|
||||
0xCF61EB10,
|
||||
0x80FE6039,
|
||||
0xCF610B11,
|
||||
0x90FE6039,
|
||||
0xCF612B11,
|
||||
0xA0FE6039,
|
||||
0xCF614B11,
|
||||
0xB0FE6039,
|
||||
0xCF616B11,
|
||||
0xC0FE6039,
|
||||
0xCF618B11,
|
||||
0xD0FE6039,
|
||||
0xCF61AB11,
|
||||
0xE0FE6039,
|
||||
0xCF61CB11,
|
||||
0xF0FE6039,
|
||||
0xCF61EB11,
|
||||
0x00FF6039,
|
||||
0xCF610B12,
|
||||
0x10FF6039,
|
||||
0xCF612B12,
|
||||
0x20FF6039,
|
||||
0xCF614B12,
|
||||
0x30FF6039,
|
||||
0xCF616B12,
|
||||
0x40FF6039,
|
||||
0xCF618B12,
|
||||
0x50FF6039,
|
||||
0xCF61AB12,
|
||||
0x60FF6039,
|
||||
0xCF61CB12,
|
||||
0x70FF6039,
|
||||
0xCF61EB12,
|
||||
0x80FF6039,
|
||||
0xCF610B13,
|
||||
0x90FF6039,
|
||||
0xCF612B13,
|
||||
0xA0FF6039,
|
||||
0xCF614B13,
|
||||
0xB0FF6039,
|
||||
0xCF616B13,
|
||||
0xC0FF6039,
|
||||
0xCF618B13,
|
||||
0xD0FF6039,
|
||||
0xCF61AB13,
|
||||
0xE0FF6039,
|
||||
0xCF61CB13,
|
||||
0xF0FF6039, // __savevmx_127
|
||||
0xCF61EB13,
|
||||
0x2000804E,
|
||||
|
||||
0xE0FE6039, // __restvmx_14
|
||||
0xCE60CB7D,
|
||||
0xF0FE6039,
|
||||
0xCE60EB7D,
|
||||
0x00FF6039,
|
||||
0xCE600B7E,
|
||||
0x10FF6039,
|
||||
0xCE602B7E,
|
||||
0x20FF6039,
|
||||
0xCE604B7E,
|
||||
0x30FF6039,
|
||||
0xCE606B7E,
|
||||
0x40FF6039,
|
||||
0xCE608B7E,
|
||||
0x50FF6039,
|
||||
0xCE60AB7E,
|
||||
0x60FF6039,
|
||||
0xCE60CB7E,
|
||||
0x70FF6039,
|
||||
0xCE60EB7E,
|
||||
0x80FF6039,
|
||||
0xCE600B7F,
|
||||
0x90FF6039,
|
||||
0xCE602B7F,
|
||||
0xA0FF6039,
|
||||
0xCE604B7F,
|
||||
0xB0FF6039,
|
||||
0xCE606B7F,
|
||||
0xC0FF6039,
|
||||
0xCE608B7F,
|
||||
0xD0FF6039,
|
||||
0xCE60AB7F,
|
||||
0xE0FF6039,
|
||||
0xCE60CB7F,
|
||||
0xF0FF6039, // __restvmx_31
|
||||
0xCE60EB7F,
|
||||
0x2000804E,
|
||||
|
||||
0x00FC6039, // __restvmx_64
|
||||
0xCB600B10,
|
||||
0x10FC6039,
|
||||
0xCB602B10,
|
||||
0x20FC6039,
|
||||
0xCB604B10,
|
||||
0x30FC6039,
|
||||
0xCB606B10,
|
||||
0x40FC6039,
|
||||
0xCB608B10,
|
||||
0x50FC6039,
|
||||
0xCB60AB10,
|
||||
0x60FC6039,
|
||||
0xCB60CB10,
|
||||
0x70FC6039,
|
||||
0xCB60EB10,
|
||||
0x80FC6039,
|
||||
0xCB600B11,
|
||||
0x90FC6039,
|
||||
0xCB602B11,
|
||||
0xA0FC6039,
|
||||
0xCB604B11,
|
||||
0xB0FC6039,
|
||||
0xCB606B11,
|
||||
0xC0FC6039,
|
||||
0xCB608B11,
|
||||
0xD0FC6039,
|
||||
0xCB60AB11,
|
||||
0xE0FC6039,
|
||||
0xCB60CB11,
|
||||
0xF0FC6039,
|
||||
0xCB60EB11,
|
||||
0x00FD6039,
|
||||
0xCB600B12,
|
||||
0x10FD6039,
|
||||
0xCB602B12,
|
||||
0x20FD6039,
|
||||
0xCB604B12,
|
||||
0x30FD6039,
|
||||
0xCB606B12,
|
||||
0x40FD6039,
|
||||
0xCB608B12,
|
||||
0x50FD6039,
|
||||
0xCB60AB12,
|
||||
0x60FD6039,
|
||||
0xCB60CB12,
|
||||
0x70FD6039,
|
||||
0xCB60EB12,
|
||||
0x80FD6039,
|
||||
0xCB600B13,
|
||||
0x90FD6039,
|
||||
0xCB602B13,
|
||||
0xA0FD6039,
|
||||
0xCB604B13,
|
||||
0xB0FD6039,
|
||||
0xCB606B13,
|
||||
0xC0FD6039,
|
||||
0xCB608B13,
|
||||
0xD0FD6039,
|
||||
0xCB60AB13,
|
||||
0xE0FD6039,
|
||||
0xCB60CB13,
|
||||
0xF0FD6039,
|
||||
0xCB60EB13,
|
||||
0x00FE6039,
|
||||
0xCF600B10,
|
||||
0x10FE6039,
|
||||
0xCF602B10,
|
||||
0x20FE6039,
|
||||
0xCF604B10,
|
||||
0x30FE6039,
|
||||
0xCF606B10,
|
||||
0x40FE6039,
|
||||
0xCF608B10,
|
||||
0x50FE6039,
|
||||
0xCF60AB10,
|
||||
0x60FE6039,
|
||||
0xCF60CB10,
|
||||
0x70FE6039,
|
||||
0xCF60EB10,
|
||||
0x80FE6039,
|
||||
0xCF600B11,
|
||||
0x90FE6039,
|
||||
0xCF602B11,
|
||||
0xA0FE6039,
|
||||
0xCF604B11,
|
||||
0xB0FE6039,
|
||||
0xCF606B11,
|
||||
0xC0FE6039,
|
||||
0xCF608B11,
|
||||
0xD0FE6039,
|
||||
0xCF60AB11,
|
||||
0xE0FE6039,
|
||||
0xCF60CB11,
|
||||
0xF0FE6039,
|
||||
0xCF60EB11,
|
||||
0x00FF6039,
|
||||
0xCF600B12,
|
||||
0x10FF6039,
|
||||
0xCF602B12,
|
||||
0x20FF6039,
|
||||
0xCF604B12,
|
||||
0x30FF6039,
|
||||
0xCF606B12,
|
||||
0x40FF6039,
|
||||
0xCF608B12,
|
||||
0x50FF6039,
|
||||
0xCF60AB12,
|
||||
0x60FF6039,
|
||||
0xCF60CB12,
|
||||
0x70FF6039,
|
||||
0xCF60EB12,
|
||||
0x80FF6039,
|
||||
0xCF600B13,
|
||||
0x90FF6039,
|
||||
0xCF602B13,
|
||||
0xA0FF6039,
|
||||
0xCF604B13,
|
||||
0xB0FF6039,
|
||||
0xCF606B13,
|
||||
0xC0FF6039,
|
||||
0xCF608B13,
|
||||
0xD0FF6039,
|
||||
0xCF60AB13,
|
||||
0xE0FF6039,
|
||||
0xCF60CB13,
|
||||
0xF0FF6039, // __restvmx_127
|
||||
0xCF60EB13,
|
||||
0x2000804E,
|
||||
};
|
||||
|
||||
|
||||
|
||||
uint32_t gplr_start = 0;
|
||||
uint32_t fpr_start = 0;
|
||||
uint32_t vmx_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) {
|
||||
if (!gplr_start) {
|
||||
gplr_start = xe_memory_search_aligned(
|
||||
memory_, start_address, end_address,
|
||||
gprlr_code_values, XECOUNT(gprlr_code_values));
|
||||
}
|
||||
if (!fpr_start) {
|
||||
fpr_start = xe_memory_search_aligned(
|
||||
memory_, start_address, end_address,
|
||||
fpr_code_values, XECOUNT(fpr_code_values));
|
||||
}
|
||||
if (!vmx_start) {
|
||||
vmx_start = xe_memory_search_aligned(
|
||||
memory_, start_address, end_address,
|
||||
vmx_code_values, XECOUNT(vmx_code_values));
|
||||
}
|
||||
if (gplr_start && fpr_start && vmx_start) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
i += section->info.page_count;
|
||||
}
|
||||
|
||||
// Add function stubs.
|
||||
char name[32];
|
||||
if (gplr_start) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
if (fpr_start) {
|
||||
uint32_t address = fpr_start;
|
||||
for (int n = 14; n <= 31; n++) {
|
||||
xesnprintfa(name, XECOUNT(name), "__savefpr_%d", n);
|
||||
FunctionSymbol* fn = GetOrInsertFunction(address);
|
||||
fn->end_address = fn->start_address + (31 - n) * 4 + 1 * 4;
|
||||
fn->set_name(name);
|
||||
fn->type = FunctionSymbol::User;
|
||||
fn->flags |= FunctionSymbol::kFlagSaveFpr;
|
||||
address += 4;
|
||||
}
|
||||
address = fpr_start + (18 * 4) + (1 * 4);
|
||||
for (int n = 14; n <= 31; n++) {
|
||||
xesnprintfa(name, XECOUNT(name), "__restfpr_%d", n);
|
||||
FunctionSymbol* fn = GetOrInsertFunction(address);
|
||||
fn->end_address = fn->start_address + (31 - n) * 4 + 1 * 4;
|
||||
fn->set_name(name);
|
||||
fn->type = FunctionSymbol::User;
|
||||
fn->flags |= FunctionSymbol::kFlagRestFpr;
|
||||
address += 4;
|
||||
}
|
||||
}
|
||||
if (vmx_start) {
|
||||
// vmx is:
|
||||
// 14-31 save
|
||||
// 64-127 save
|
||||
// 14-31 rest
|
||||
// 64-127 rest
|
||||
uint32_t address = vmx_start;
|
||||
for (int n = 14; n <= 31; n++) {
|
||||
xesnprintfa(name, XECOUNT(name), "__savevmx_%d", n);
|
||||
FunctionSymbol* fn = GetOrInsertFunction(address);
|
||||
fn->set_name(name);
|
||||
fn->type = FunctionSymbol::User;
|
||||
fn->flags |= FunctionSymbol::kFlagSaveVmx;
|
||||
address += 2 * 4;
|
||||
}
|
||||
address += 4;
|
||||
for (int n = 64; n <= 127; n++) {
|
||||
xesnprintfa(name, XECOUNT(name), "__savevmx_%d", n);
|
||||
FunctionSymbol* fn = GetOrInsertFunction(address);
|
||||
fn->set_name(name);
|
||||
fn->type = FunctionSymbol::User;
|
||||
fn->flags |= FunctionSymbol::kFlagSaveVmx;
|
||||
address += 2 * 4;
|
||||
}
|
||||
address = vmx_start + (18 * 2 * 4) + (1 * 4) + (64 * 2 * 4) + (1 * 4);
|
||||
for (int n = 14; n <= 31; n++) {
|
||||
xesnprintfa(name, XECOUNT(name), "__restvmx_%d", n);
|
||||
FunctionSymbol* fn = GetOrInsertFunction(address);
|
||||
fn->set_name(name);
|
||||
fn->type = FunctionSymbol::User;
|
||||
fn->flags |= FunctionSymbol::kFlagRestVmx;
|
||||
address += 2 * 4;
|
||||
}
|
||||
address += 4;
|
||||
for (int n = 64; n <= 127; n++) {
|
||||
xesnprintfa(name, XECOUNT(name), "__restvmx_%d", n);
|
||||
FunctionSymbol* fn = GetOrInsertFunction(address);
|
||||
fn->set_name(name);
|
||||
fn->type = FunctionSymbol::User;
|
||||
fn->flags |= FunctionSymbol::kFlagSaveVmx;
|
||||
address += 2 * 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];
|
||||
if (method_info->address != 0) {
|
||||
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;
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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,
|
||||
ExportResolver* export_resolver,
|
||||
SymbolTable* sym_table, xe_xex2_ref xex);
|
||||
virtual ~XexSymbolDatabase();
|
||||
|
||||
virtual int Analyze();
|
||||
|
||||
private:
|
||||
int FindSaveRest();
|
||||
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_
|
||||
@@ -1,25 +1,18 @@
|
||||
# Copyright 2013 Ben Vanik. All Rights Reserved.
|
||||
{
|
||||
'sources': [
|
||||
'backend.h',
|
||||
'cpu-private.h',
|
||||
'cpu.cc',
|
||||
'cpu.h',
|
||||
'exec_module.cc',
|
||||
'exec_module.h',
|
||||
'global_exports.cc',
|
||||
'global_exports.h',
|
||||
'jit.h',
|
||||
'ppc.h',
|
||||
'processor.cc',
|
||||
'processor.h',
|
||||
'thread_state.cc',
|
||||
'thread_state.h',
|
||||
],
|
||||
|
||||
'includes': [
|
||||
'ppc/sources.gypi',
|
||||
'sdb/sources.gypi',
|
||||
'x64/sources.gypi',
|
||||
'xenon_memory.cc',
|
||||
'xenon_memory.h',
|
||||
'xenon_runtime.cc',
|
||||
'xenon_runtime.h',
|
||||
'xenon_thread_state.cc',
|
||||
'xenon_thread_state.h',
|
||||
'xex_module.cc',
|
||||
'xex_module.h',
|
||||
],
|
||||
}
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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/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, uint32_t thread_id) :
|
||||
stack_size_(stack_size), thread_state_address_(thread_state_address),
|
||||
thread_id_(thread_id) {
|
||||
memory_ = processor->memory();
|
||||
|
||||
stack_address_ = xe_memory_heap_alloc(
|
||||
memory_, 0, stack_size, XE_MEMORY_FLAG_ZERO);
|
||||
|
||||
// Allocate with 64b alignment.
|
||||
ppc_state_ = (xe_ppc_state_t*)xe_malloc_aligned(sizeof(xe_ppc_state_t));
|
||||
XEASSERT(((uint64_t)ppc_state_ & 0xF) == 0);
|
||||
xe_zero_struct(ppc_state_, sizeof(xe_ppc_state_t));
|
||||
|
||||
// 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_ + stack_size;
|
||||
ppc_state_->r[13] = thread_state_address_;
|
||||
}
|
||||
|
||||
ThreadState::~ThreadState() {
|
||||
xe_free_aligned(ppc_state_);
|
||||
xe_memory_heap_free(memory_, stack_address_, 0);
|
||||
xe_memory_release(memory_);
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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,
|
||||
uint32_t thread_id);
|
||||
~ThreadState();
|
||||
|
||||
uint32_t thread_id() const { return thread_id_; }
|
||||
xe_ppc_state_t* ppc_state() const { return 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_;
|
||||
uint32_t thread_id_;
|
||||
|
||||
// NOTE: must be 64b aligned for SSE ops.
|
||||
xe_ppc_state_t* ppc_state_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_THREAD_STATE_H_
|
||||
@@ -1,17 +0,0 @@
|
||||
# Copyright 2013 Ben Vanik. All Rights Reserved.
|
||||
{
|
||||
'sources': [
|
||||
'x64_backend.cc',
|
||||
'x64_backend.h',
|
||||
'x64_emit.h',
|
||||
'x64_emit_altivec.cc',
|
||||
'x64_emit_alu.cc',
|
||||
'x64_emit_control.cc',
|
||||
'x64_emit_fpu.cc',
|
||||
'x64_emit_memory.cc',
|
||||
'x64_emitter.cc',
|
||||
'x64_emitter.h',
|
||||
'x64_jit.cc',
|
||||
'x64_jit.h',
|
||||
],
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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/x64/x64_backend.h>
|
||||
|
||||
#include <xenia/cpu/sdb/symbol_table.h>
|
||||
#include <xenia/cpu/x64/x64_emit.h>
|
||||
#include <xenia/cpu/x64/x64_jit.h>
|
||||
|
||||
|
||||
using namespace xe;
|
||||
using namespace xe::cpu;
|
||||
using namespace xe::cpu::sdb;
|
||||
using namespace xe::cpu::x64;
|
||||
|
||||
|
||||
namespace {
|
||||
void InitializeIfNeeded();
|
||||
void CleanupOnShutdown();
|
||||
|
||||
void InitializeIfNeeded() {
|
||||
static bool has_initialized = false;
|
||||
if (has_initialized) {
|
||||
return;
|
||||
}
|
||||
has_initialized = true;
|
||||
|
||||
X64RegisterEmitCategoryAltivec();
|
||||
X64RegisterEmitCategoryALU();
|
||||
X64RegisterEmitCategoryControl();
|
||||
X64RegisterEmitCategoryFPU();
|
||||
X64RegisterEmitCategoryMemory();
|
||||
|
||||
atexit(CleanupOnShutdown);
|
||||
}
|
||||
|
||||
void CleanupOnShutdown() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
X64Backend::X64Backend() :
|
||||
Backend() {
|
||||
InitializeIfNeeded();
|
||||
}
|
||||
|
||||
X64Backend::~X64Backend() {
|
||||
}
|
||||
|
||||
JIT* X64Backend::CreateJIT(xe_memory_ref memory, SymbolTable* sym_table) {
|
||||
return new X64JIT(memory, sym_table);
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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_X64_X64_BACKEND_H_
|
||||
#define XENIA_CPU_X64_X64_BACKEND_H_
|
||||
|
||||
#include <xenia/common.h>
|
||||
|
||||
#include <xenia/cpu/backend.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace x64 {
|
||||
|
||||
|
||||
class X64Backend : public Backend {
|
||||
public:
|
||||
X64Backend();
|
||||
virtual ~X64Backend();
|
||||
|
||||
virtual JIT* CreateJIT(xe_memory_ref memory, sdb::SymbolTable* sym_table);
|
||||
|
||||
protected:
|
||||
};
|
||||
|
||||
|
||||
} // namespace x64
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_X64_X64_BACKEND_H_
|
||||
@@ -1,44 +0,0 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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_X64_X64_EMIT_H_
|
||||
#define XENIA_CPU_X64_X64_EMIT_H_
|
||||
|
||||
#include <xenia/cpu/ppc/instr.h>
|
||||
#include <xenia/cpu/ppc/state.h>
|
||||
#include <xenia/cpu/x64/x64_emitter.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace x64 {
|
||||
|
||||
|
||||
void X64RegisterEmitCategoryAltivec();
|
||||
void X64RegisterEmitCategoryALU();
|
||||
void X64RegisterEmitCategoryControl();
|
||||
void X64RegisterEmitCategoryFPU();
|
||||
void X64RegisterEmitCategoryMemory();
|
||||
|
||||
|
||||
#define XEEMITTER(name, opcode, format) int InstrEmit_##name
|
||||
|
||||
#define XEREGISTERINSTR(name, opcode) \
|
||||
RegisterInstrEmit(opcode, (InstrEmitFn)InstrEmit_##name);
|
||||
|
||||
#define XEINSTRNOTIMPLEMENTED()
|
||||
//#define XEINSTRNOTIMPLEMENTED XEASSERTALWAYS
|
||||
|
||||
|
||||
} // namespace x64
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_X64_X64_EMIT_H_
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,730 +0,0 @@
|
||||
/*
|
||||
******************************************************************************
|
||||
* 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/x64/x64_emit.h>
|
||||
|
||||
#include <xenia/cpu/cpu-private.h>
|
||||
|
||||
|
||||
using namespace xe::cpu;
|
||||
using namespace xe::cpu::ppc;
|
||||
using namespace xe::cpu::sdb;
|
||||
|
||||
using namespace AsmJit;
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace x64 {
|
||||
|
||||
|
||||
int XeEmitIndirectBranchTo(
|
||||
X64Emitter& e, X86Compiler& c, 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.
|
||||
|
||||
GpVar target;
|
||||
switch (reg) {
|
||||
case kXEPPCRegLR:
|
||||
target = e.lr_value();
|
||||
break;
|
||||
case kXEPPCRegCTR:
|
||||
target = e.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) {
|
||||
// The return block will spill registers for us.
|
||||
// TODO(benvanik): 'lr_mismatch' debug info.
|
||||
// Note: we need to test on *only* the 32-bit target, as the target ptr may
|
||||
// have garbage in the upper 32 bits.
|
||||
c.cmp(target.r32(), c.getGpArg(1).r32());
|
||||
// TODO(benvanik): evaluate hint here.
|
||||
c.je(e.GetReturnLabel(), kCondHintLikely);
|
||||
}
|
||||
|
||||
// Defer to the generator, which will do fancy things.
|
||||
bool likely_local = !lk && reg == kXEPPCRegCTR;
|
||||
return e.GenerateIndirectionBranch(cia, target, lk, likely_local);
|
||||
}
|
||||
|
||||
int XeEmitBranchTo(
|
||||
X64Emitter& e, X86Compiler& c, const char* src, uint32_t cia,
|
||||
bool lk, GpVar* condition = NULL) {
|
||||
FunctionBlock* fn_block = e.fn_block();
|
||||
|
||||
// Fast-path for branches to other blocks.
|
||||
// Only valid when not tracing branches.
|
||||
if (!FLAGS_trace_branches &&
|
||||
fn_block->outgoing_type == FunctionBlock::kTargetBlock) {
|
||||
XEASSERT(!lk);
|
||||
Label target_label = e.GetBlockLabel(fn_block->outgoing_address);
|
||||
if (condition) {
|
||||
// Fast test -- if condition passed then jump to target.
|
||||
// TODO(benvanik): need to spill here? somehow?
|
||||
c.test((*condition).r8(), (*condition).r8());
|
||||
c.jnz(target_label);
|
||||
} else {
|
||||
// TODO(benvanik): need to spill here?
|
||||
//e.SpillRegisters();
|
||||
c.jmp(target_label);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Only branch of conditionals when we have one.
|
||||
Label post_jump_label;
|
||||
if (condition) {
|
||||
// TODO(benvanik): add debug info for this?
|
||||
post_jump_label = c.newLabel();
|
||||
c.test((*condition).r8(), (*condition).r8());
|
||||
// TODO(benvanik): experiment with various hints?
|
||||
c.jz(post_jump_label, kCondHintNone);
|
||||
}
|
||||
|
||||
e.TraceBranch(cia);
|
||||
|
||||
// Get the basic block and switch behavior based on outgoing type.
|
||||
int result = 0;
|
||||
switch (fn_block->outgoing_type) {
|
||||
case FunctionBlock::kTargetBlock:
|
||||
// Often taken care of above, when not tracing branches.
|
||||
XEASSERT(!lk);
|
||||
c.jmp(e.GetBlockLabel(fn_block->outgoing_address));
|
||||
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?
|
||||
e.SpillRegisters();
|
||||
|
||||
XEASSERTNOTNULL(fn_block->outgoing_function);
|
||||
// TODO(benvanik): check to see if this is the last block in the function.
|
||||
// This would enable tail calls/etc.
|
||||
bool is_end = false;
|
||||
if (!lk || is_end) {
|
||||
// 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.
|
||||
e.CallFunction(fn_block->outgoing_function, c.getGpArg(1), true);
|
||||
// No ret needed - we jumped!
|
||||
} else {
|
||||
// Will return here eventually.
|
||||
// Refill registers from state.
|
||||
GpVar lr(c.newGpVar());
|
||||
c.mov(lr, imm(cia + 4));
|
||||
e.CallFunction(fn_block->outgoing_function, lr, false);
|
||||
e.FillRegisters();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FunctionBlock::kTargetLR:
|
||||
{
|
||||
// An indirect jump.
|
||||
printf("INDIRECT JUMP VIA LR: %.8X\n", cia);
|
||||
result = XeEmitIndirectBranchTo(e, c, src, cia, lk, kXEPPCRegLR);
|
||||
break;
|
||||
}
|
||||
case FunctionBlock::kTargetCTR:
|
||||
{
|
||||
// An indirect jump.
|
||||
printf("INDIRECT JUMP VIA CTR: %.8X\n", cia);
|
||||
result = XeEmitIndirectBranchTo(e, c, src, cia, lk, kXEPPCRegCTR);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
case FunctionBlock::kTargetNone:
|
||||
XEASSERTALWAYS();
|
||||
result = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (condition) {
|
||||
c.bind(post_jump_label);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
XEEMITTER(bx, 0x48000000, I )(X64Emitter& e, X86Compiler& c, 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) {
|
||||
e.update_lr_value(imm(i.address + 4));
|
||||
}
|
||||
|
||||
return XeEmitBranchTo(e, c, "bx", i.address, i.I.LK);
|
||||
}
|
||||
|
||||
XEEMITTER(bcx, 0x40000000, B )(X64Emitter& e, X86Compiler& c, 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) {
|
||||
e.update_lr_value(imm(i.address + 4));
|
||||
}
|
||||
|
||||
// TODO(benvanik): optimize to just use x64 ops.
|
||||
// Need to handle the case where both ctr and cond set.
|
||||
|
||||
GpVar ctr_ok;
|
||||
if (XESELECTBITS(i.B.BO, 2, 2)) {
|
||||
// Ignore ctr.
|
||||
} else {
|
||||
// Decrement counter.
|
||||
GpVar ctr(c.newGpVar());
|
||||
c.mov(ctr, e.ctr_value());
|
||||
c.dec(ctr);
|
||||
e.update_ctr_value(ctr);
|
||||
|
||||
// Ctr check.
|
||||
c.cmp(ctr, imm(0));
|
||||
ctr_ok = c.newGpVar();
|
||||
if (XESELECTBITS(i.B.BO, 1, 1)) {
|
||||
c.setz(ctr_ok.r8());
|
||||
} else {
|
||||
c.setnz(ctr_ok.r8());
|
||||
}
|
||||
}
|
||||
|
||||
GpVar cond_ok;
|
||||
if (XESELECTBITS(i.B.BO, 4, 4)) {
|
||||
// Ignore cond.
|
||||
} else {
|
||||
GpVar cr(c.newGpVar());
|
||||
c.mov(cr, e.cr_value(i.XL.BI >> 2));
|
||||
c.and_(cr, imm(1 << (i.XL.BI & 3)));
|
||||
c.cmp(cr, imm(0));
|
||||
cond_ok = c.newGpVar();
|
||||
if (XESELECTBITS(i.XL.BO, 3, 3)) {
|
||||
c.setnz(cond_ok.r8());
|
||||
} else {
|
||||
c.setz(cond_ok.r8());
|
||||
}
|
||||
}
|
||||
|
||||
// We do a bit of optimization here to make the llvm assembly easier to read.
|
||||
GpVar* ok = NULL;
|
||||
if (ctr_ok.getId() != kInvalidValue && cond_ok.getId() != kInvalidValue) {
|
||||
c.and_(ctr_ok, cond_ok);
|
||||
ok = &ctr_ok;
|
||||
} else if (ctr_ok.getId() != kInvalidValue) {
|
||||
ok = &ctr_ok;
|
||||
} else if (cond_ok.getId() != kInvalidValue) {
|
||||
ok = &cond_ok;
|
||||
}
|
||||
|
||||
uint32_t nia;
|
||||
if (i.B.AA) {
|
||||
nia = XEEXTS26(i.B.BD << 2);
|
||||
} else {
|
||||
nia = i.address + XEEXTS26(i.B.BD << 2);
|
||||
}
|
||||
if (XeEmitBranchTo(e, c, "bcx", i.address, i.B.LK, ok)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(bcctrx, 0x4C000420, XL )(X64Emitter& e, X86Compiler& c, 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) {
|
||||
e.update_lr_value(imm(i.address + 4));
|
||||
}
|
||||
|
||||
GpVar cond_ok;
|
||||
if (XESELECTBITS(i.XL.BO, 4, 4)) {
|
||||
// Ignore cond.
|
||||
} else {
|
||||
GpVar cr(c.newGpVar());
|
||||
c.mov(cr, e.cr_value(i.XL.BI >> 2));
|
||||
c.and_(cr, imm(1 << (i.XL.BI & 3)));
|
||||
c.cmp(cr, imm(0));
|
||||
cond_ok = c.newGpVar();
|
||||
if (XESELECTBITS(i.XL.BO, 3, 3)) {
|
||||
c.setnz(cond_ok.r8());
|
||||
} else {
|
||||
c.setz(cond_ok.r8());
|
||||
}
|
||||
}
|
||||
|
||||
// We do a bit of optimization here to make the llvm assembly easier to read.
|
||||
GpVar* ok = NULL;
|
||||
if (cond_ok.getId() != kInvalidValue) {
|
||||
ok = &cond_ok;
|
||||
}
|
||||
|
||||
if (XeEmitBranchTo(e, c, "bcctrx", i.address, i.XL.LK, ok)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(bclrx, 0x4C000020, XL )(X64Emitter& e, X86Compiler& c, 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) {
|
||||
e.update_lr_value(imm(i.address + 4));
|
||||
}
|
||||
|
||||
GpVar ctr_ok;
|
||||
if (XESELECTBITS(i.XL.BO, 2, 2)) {
|
||||
// Ignore ctr.
|
||||
} else {
|
||||
// Decrement counter.
|
||||
GpVar ctr(c.newGpVar());
|
||||
c.mov(ctr, e.ctr_value());
|
||||
c.dec(ctr);
|
||||
e.update_ctr_value(ctr);
|
||||
|
||||
// Ctr check.
|
||||
c.cmp(ctr, imm(0));
|
||||
ctr_ok = c.newGpVar();
|
||||
if (XESELECTBITS(i.XL.BO, 1, 1)) {
|
||||
c.setz(ctr_ok.r8());
|
||||
} else {
|
||||
c.setnz(ctr_ok.r8());
|
||||
}
|
||||
}
|
||||
|
||||
GpVar cond_ok;
|
||||
if (XESELECTBITS(i.XL.BO, 4, 4)) {
|
||||
// Ignore cond.
|
||||
} else {
|
||||
GpVar cr(c.newGpVar());
|
||||
c.mov(cr, e.cr_value(i.XL.BI >> 2));
|
||||
c.and_(cr, imm(1 << (i.XL.BI & 3)));
|
||||
c.cmp(cr, imm(0));
|
||||
cond_ok = c.newGpVar();
|
||||
if (XESELECTBITS(i.XL.BO, 3, 3)) {
|
||||
c.setnz(cond_ok.r8());
|
||||
} else {
|
||||
c.setz(cond_ok.r8());
|
||||
}
|
||||
}
|
||||
|
||||
// We do a bit of optimization here to make the llvm assembly easier to read.
|
||||
GpVar* ok = NULL;
|
||||
if (ctr_ok.getId() != kInvalidValue && cond_ok.getId() != kInvalidValue) {
|
||||
c.and_(ctr_ok, cond_ok);
|
||||
ok = &ctr_ok;
|
||||
} else if (ctr_ok.getId() != kInvalidValue) {
|
||||
ok = &ctr_ok;
|
||||
} else if (cond_ok.getId() != kInvalidValue) {
|
||||
ok = &cond_ok;
|
||||
}
|
||||
|
||||
if (XeEmitBranchTo(e, c, "bclrx", i.address, i.XL.LK, ok)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Condition register logical (A-23)
|
||||
|
||||
XEEMITTER(crand, 0x4C000202, XL )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(crandc, 0x4C000102, XL )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(creqv, 0x4C000242, XL )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(crnand, 0x4C0001C2, XL )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(crnor, 0x4C000042, XL )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(cror, 0x4C000382, XL )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(crorc, 0x4C000342, XL )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(crxor, 0x4C000182, XL )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(mcrf, 0x4C000000, XL )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
// System linkage (A-24)
|
||||
|
||||
XEEMITTER(sc, 0x44000002, SC )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
// Trap (A-25)
|
||||
|
||||
int XeEmitTrap(X64Emitter& e, X86Compiler& c, InstrData& i,
|
||||
GpVar& va, GpVar& 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;
|
||||
}
|
||||
|
||||
XELOGCPU("twi not implemented - instruction ignored");
|
||||
|
||||
// TODO(benvanik): port from LLVM
|
||||
|
||||
// BasicBlock* after_bb = BasicBlock::Create(*e.context(), "", e.fn(),
|
||||
// e.GetNextBasicBlock());
|
||||
// BasicBlock* trap_bb = BasicBlock::Create(*e.context(), "", e.fn(),
|
||||
// after_bb);
|
||||
|
||||
// // Create the basic blocks (so we can chain).
|
||||
// std::vector<BasicBlock*> bbs;
|
||||
// if (TO & (1 << 4)) {
|
||||
// bbs.push_back(BasicBlock::Create(*e.context(), "", e.fn(), trap_bb));
|
||||
// }
|
||||
// if (TO & (1 << 3)) {
|
||||
// bbs.push_back(BasicBlock::Create(*e.context(), "", e.fn(), trap_bb));
|
||||
// }
|
||||
// if (TO & (1 << 2)) {
|
||||
// bbs.push_back(BasicBlock::Create(*e.context(), "", e.fn(), trap_bb));
|
||||
// }
|
||||
// if (TO & (1 << 1)) {
|
||||
// bbs.push_back(BasicBlock::Create(*e.context(), "", e.fn(), trap_bb));
|
||||
// }
|
||||
// if (TO & (1 << 0)) {
|
||||
// bbs.push_back(BasicBlock::Create(*e.context(), "", e.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);
|
||||
// GpVar cmp = b.CreateICmpSLT(va, vb);
|
||||
// b.CreateCondBr(cmp, trap_bb, *it);
|
||||
// }
|
||||
// if (TO & (1 << 3)) {
|
||||
// // a > b
|
||||
// BasicBlock* bb = *(it++);
|
||||
// b.SetInsertPoint(bb);
|
||||
// GpVar cmp = b.CreateICmpSGT(va, vb);
|
||||
// b.CreateCondBr(cmp, trap_bb, *it);
|
||||
// }
|
||||
// if (TO & (1 << 2)) {
|
||||
// // a = b
|
||||
// BasicBlock* bb = *(it++);
|
||||
// b.SetInsertPoint(bb);
|
||||
// GpVar cmp = b.CreateICmpEQ(va, vb);
|
||||
// b.CreateCondBr(cmp, trap_bb, *it);
|
||||
// }
|
||||
// if (TO & (1 << 1)) {
|
||||
// // a <u b
|
||||
// BasicBlock* bb = *(it++);
|
||||
// b.SetInsertPoint(bb);
|
||||
// GpVar cmp = b.CreateICmpULT(va, vb);
|
||||
// b.CreateCondBr(cmp, trap_bb, *it);
|
||||
// }
|
||||
// if (TO & (1 << 0)) {
|
||||
// // a >u b
|
||||
// BasicBlock* bb = *(it++);
|
||||
// b.SetInsertPoint(bb);
|
||||
// GpVar cmp = b.CreateICmpUGT(va, vb);
|
||||
// b.CreateCondBr(cmp, trap_bb, *it);
|
||||
// }
|
||||
|
||||
// // Create trap BB.
|
||||
// b.SetInsertPoint(trap_bb);
|
||||
// e.SpillRegisters();
|
||||
// // TODO(benvanik): use @llvm.debugtrap? could make debugging better
|
||||
// b.CreateCall2(e.gen_module()->getFunction("XeTrap"),
|
||||
// e.fn()->arg_begin(),
|
||||
// e.get_uint64(i.address));
|
||||
// b.CreateBr(after_bb);
|
||||
|
||||
// // Resume.
|
||||
// b.SetInsertPoint(after_bb);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(td, 0x7C000088, X )(X64Emitter& e, X86Compiler& c, 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
|
||||
GpVar va = e.gpr_value(i.X.RA);
|
||||
GpVar vb = e.gpr_value(i.X.RA);
|
||||
return XeEmitTrap(e, c, i, va, vb, i.X.RT);
|
||||
}
|
||||
|
||||
XEEMITTER(tdi, 0x08000000, D )(X64Emitter& e, X86Compiler& c, 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
|
||||
GpVar va = e.gpr_value(i.D.RA);
|
||||
GpVar vb(c.newGpVar());
|
||||
c.mov(vb, imm(XEEXTS16(i.D.DS)));
|
||||
return XeEmitTrap(e, c, i, va, vb, i.D.RT);
|
||||
}
|
||||
|
||||
XEEMITTER(tw, 0x7C000008, X )(X64Emitter& e, X86Compiler& c, 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
|
||||
GpVar va = e.sign_extend(e.gpr_value(i.X.RA), 4, 8);
|
||||
GpVar vb = e.sign_extend(e.gpr_value(i.X.RA), 4, 8);
|
||||
return XeEmitTrap(e, c, i, va, vb, i.X.RT);
|
||||
}
|
||||
|
||||
XEEMITTER(twi, 0x0C000000, D )(X64Emitter& e, X86Compiler& c, 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
|
||||
GpVar va = e.sign_extend(e.gpr_value(i.D.RA), 4, 8);
|
||||
GpVar vb(c.newGpVar());
|
||||
c.mov(vb, imm(XEEXTS16(i.D.DS)));
|
||||
return XeEmitTrap(e, c, i, va, vb, i.D.RT);
|
||||
}
|
||||
|
||||
|
||||
// Processor control (A-26)
|
||||
|
||||
XEEMITTER(mfcr, 0x7C000026, X )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(mfspr, 0x7C0002A6, XFX)(X64Emitter& e, X86Compiler& c, 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);
|
||||
GpVar v;
|
||||
switch (n) {
|
||||
case 1:
|
||||
// XER
|
||||
v = e.xer_value();
|
||||
break;
|
||||
case 8:
|
||||
// LR
|
||||
v = e.lr_value();
|
||||
break;
|
||||
case 9:
|
||||
// CTR
|
||||
v = e.ctr_value();
|
||||
break;
|
||||
// 268 + 269 = TB + TBU
|
||||
default:
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
e.update_gpr_value(i.XFX.RT, v);
|
||||
|
||||
e.clear_constant_gpr_value(i.XFX.RT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(mftb, 0x7C0002E6, XFX)(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||||
LARGE_INTEGER counter;
|
||||
if (QueryPerformanceCounter(&counter)) {
|
||||
e.update_gpr_value(i.XFX.RT, e.get_uint64(counter.QuadPart));
|
||||
} else {
|
||||
e.update_gpr_value(i.XFX.RT, e.get_uint64(0));
|
||||
}
|
||||
|
||||
e.clear_constant_gpr_value(i.XFX.RT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(mtcrf, 0x7C000120, XFX)(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(mtspr, 0x7C0003A6, XFX)(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||||
// n <- spr[5:9] || spr[0:4]
|
||||
// if length(SPR(n)) = 64 then
|
||||
// SPR(n) <- (RS)
|
||||
// else
|
||||
// SPR(n) <- (RS)[32:63]
|
||||
|
||||
GpVar v = e.gpr_value(i.XFX.RT);
|
||||
|
||||
const uint32_t n = ((i.XFX.spr & 0x1F) << 5) | ((i.XFX.spr >> 5) & 0x1F);
|
||||
switch (n) {
|
||||
case 1:
|
||||
// XER
|
||||
e.update_xer_value(v);
|
||||
break;
|
||||
case 8:
|
||||
// LR
|
||||
e.update_lr_value(v);
|
||||
break;
|
||||
case 9:
|
||||
// CTR
|
||||
e.update_ctr_value(v);
|
||||
break;
|
||||
default:
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void X64RegisterEmitCategoryControl() {
|
||||
XEREGISTERINSTR(bx, 0x48000000);
|
||||
XEREGISTERINSTR(bcx, 0x40000000);
|
||||
XEREGISTERINSTR(bcctrx, 0x4C000420);
|
||||
XEREGISTERINSTR(bclrx, 0x4C000020);
|
||||
XEREGISTERINSTR(crand, 0x4C000202);
|
||||
XEREGISTERINSTR(crandc, 0x4C000102);
|
||||
XEREGISTERINSTR(creqv, 0x4C000242);
|
||||
XEREGISTERINSTR(crnand, 0x4C0001C2);
|
||||
XEREGISTERINSTR(crnor, 0x4C000042);
|
||||
XEREGISTERINSTR(cror, 0x4C000382);
|
||||
XEREGISTERINSTR(crorc, 0x4C000342);
|
||||
XEREGISTERINSTR(crxor, 0x4C000182);
|
||||
XEREGISTERINSTR(mcrf, 0x4C000000);
|
||||
XEREGISTERINSTR(sc, 0x44000002);
|
||||
XEREGISTERINSTR(td, 0x7C000088);
|
||||
XEREGISTERINSTR(tdi, 0x08000000);
|
||||
XEREGISTERINSTR(tw, 0x7C000008);
|
||||
XEREGISTERINSTR(twi, 0x0C000000);
|
||||
XEREGISTERINSTR(mfcr, 0x7C000026);
|
||||
XEREGISTERINSTR(mfspr, 0x7C0002A6);
|
||||
XEREGISTERINSTR(mftb, 0x7C0002E6);
|
||||
XEREGISTERINSTR(mtcrf, 0x7C000120);
|
||||
XEREGISTERINSTR(mtspr, 0x7C0003A6);
|
||||
}
|
||||
|
||||
|
||||
} // namespace x64
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
@@ -1,952 +0,0 @@
|
||||
/*
|
||||
******************************************************************************
|
||||
* 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/x64/x64_emit.h>
|
||||
|
||||
#include <xenia/cpu/cpu-private.h>
|
||||
|
||||
|
||||
using namespace xe::cpu;
|
||||
using namespace xe::cpu::ppc;
|
||||
|
||||
using namespace AsmJit;
|
||||
|
||||
|
||||
// Good source of information:
|
||||
// http://mamedev.org/source/src/emu/cpu/powerpc/ppc_ops.c
|
||||
// The correctness of that code is not reflected here yet -_-
|
||||
|
||||
|
||||
// Enable rounding numbers to single precision as required.
|
||||
// This adds a bunch of work per operation and I'm not sure it's required.
|
||||
#define ROUND_TO_SINGLE
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace x64 {
|
||||
|
||||
|
||||
// Floating-point arithmetic (A-8)
|
||||
|
||||
XEEMITTER(faddx, 0xFC00002A, A )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||||
// frD <- (frA) + (frB)
|
||||
|
||||
XmmVar v(c.newXmmVar());
|
||||
c.movq(v, e.fpr_value(i.A.FRA));
|
||||
c.addsd(v, e.fpr_value(i.A.FRB));
|
||||
e.update_fpr_value(i.A.FRT, v);
|
||||
|
||||
// TODO(benvanik): update status/control register.
|
||||
|
||||
if (i.A.Rc) {
|
||||
// With cr0 update.
|
||||
XEASSERTALWAYS();
|
||||
//e.update_cr_with_cond(0, v);
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
e.TraceFPR(i.A.FRT, i.A.FRA, i.A.FRB);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(faddsx, 0xEC00002A, A )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||||
// frD <- (frA) + (frB)
|
||||
|
||||
XmmVar v(c.newXmmVar());
|
||||
c.movq(v, e.fpr_value(i.A.FRA));
|
||||
c.addsd(v, e.fpr_value(i.A.FRB));
|
||||
#if defined(ROUND_TO_SINGLE)
|
||||
// TODO(benvanik): check rounding mode? etc?
|
||||
// This converts to a single then back to a double to approximate the
|
||||
// rounding on the 360.
|
||||
c.cvtsd2ss(v, v);
|
||||
c.cvtss2sd(v, v);
|
||||
#endif // ROUND_TO_SINGLE
|
||||
e.update_fpr_value(i.A.FRT, v);
|
||||
|
||||
// TODO(benvanik): update status/control register.
|
||||
|
||||
if (i.A.Rc) {
|
||||
// With cr0 update.
|
||||
XEASSERTALWAYS();
|
||||
//e.update_cr_with_cond(0, v);
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
e.TraceFPR(i.A.FRT, i.A.FRA, i.A.FRB);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(fdivx, 0xFC000024, A )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||||
// frD <- frA / frB
|
||||
|
||||
XmmVar v(c.newXmmVar());
|
||||
c.movq(v, e.fpr_value(i.A.FRA));
|
||||
c.divsd(v, e.fpr_value(i.A.FRB));
|
||||
e.update_fpr_value(i.A.FRT, v);
|
||||
|
||||
// TODO(benvanik): update status/control register.
|
||||
|
||||
if (i.A.Rc) {
|
||||
// With cr0 update.
|
||||
XEASSERTALWAYS();
|
||||
//e.update_cr_with_cond(0, v);
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
e.TraceFPR(i.A.FRT, i.A.FRA, i.A.FRB);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(fdivsx, 0xEC000024, A )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||||
// frD <- frA / frB
|
||||
|
||||
XmmVar v(c.newXmmVar());
|
||||
c.movq(v, e.fpr_value(i.A.FRA));
|
||||
c.divsd(v, e.fpr_value(i.A.FRB));
|
||||
#if defined(ROUND_TO_SINGLE)
|
||||
// TODO(benvanik): check rounding mode? etc?
|
||||
// This converts to a single then back to a double to approximate the
|
||||
// rounding on the 360.
|
||||
c.cvtsd2ss(v, v);
|
||||
c.cvtss2sd(v, v);
|
||||
#endif // ROUND_TO_SINGLE
|
||||
e.update_fpr_value(i.A.FRT, v);
|
||||
|
||||
// TODO(benvanik): update status/control register.
|
||||
|
||||
if (i.A.Rc) {
|
||||
// With cr0 update.
|
||||
XEASSERTALWAYS();
|
||||
//e.update_cr_with_cond(0, v);
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
e.TraceFPR(i.A.FRT, i.A.FRA, i.A.FRB);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(fmulx, 0xFC000032, A )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||||
// frD <- (frA) x (frC)
|
||||
|
||||
XmmVar v(c.newXmmVar());
|
||||
c.movq(v, e.fpr_value(i.A.FRA));
|
||||
c.mulsd(v, e.fpr_value(i.A.FRC));
|
||||
e.update_fpr_value(i.A.FRT, v);
|
||||
|
||||
// TODO(benvanik): update status/control register.
|
||||
|
||||
if (i.A.Rc) {
|
||||
// With cr0 update.
|
||||
XEASSERTALWAYS();
|
||||
//e.update_cr_with_cond(0, v);
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
e.TraceFPR(i.A.FRT, i.A.FRA, i.A.FRC);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(fmulsx, 0xEC000032, A )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||||
// frD <- (frA) x (frC)
|
||||
|
||||
XmmVar v(c.newXmmVar());
|
||||
c.movq(v, e.fpr_value(i.A.FRA));
|
||||
c.mulsd(v, e.fpr_value(i.A.FRC));
|
||||
#if defined(ROUND_TO_SINGLE)
|
||||
// TODO(benvanik): check rounding mode? etc?
|
||||
// This converts to a single then back to a double to approximate the
|
||||
// rounding on the 360.
|
||||
c.cvtsd2ss(v, v);
|
||||
c.cvtss2sd(v, v);
|
||||
#endif // ROUND_TO_SINGLE
|
||||
e.update_fpr_value(i.A.FRT, v);
|
||||
|
||||
// TODO(benvanik): update status/control register.
|
||||
|
||||
if (i.A.Rc) {
|
||||
// With cr0 update.
|
||||
XEASSERTALWAYS();
|
||||
//e.update_cr_with_cond(0, v);
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
e.TraceFPR(i.A.FRT, i.A.FRA, i.A.FRC);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(fresx, 0xEC000030, A )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(frsqrtex, 0xFC000034, A )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fsubx, 0xFC000028, A )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||||
// frD <- (frA) - (frB)
|
||||
|
||||
XmmVar v(c.newXmmVar());
|
||||
c.movq(v, e.fpr_value(i.A.FRA));
|
||||
c.subsd(v, e.fpr_value(i.A.FRB));
|
||||
#if defined(ROUND_TO_SINGLE)
|
||||
// TODO(benvanik): check rounding mode? etc?
|
||||
// This converts to a single then back to a double to approximate the
|
||||
// rounding on the 360.
|
||||
c.cvtsd2ss(v, v);
|
||||
c.cvtss2sd(v, v);
|
||||
#endif // ROUND_TO_SINGLE
|
||||
e.update_fpr_value(i.A.FRT, v);
|
||||
|
||||
// TODO(benvanik): update status/control register.
|
||||
|
||||
if (i.A.Rc) {
|
||||
// With cr0 update.
|
||||
XEASSERTALWAYS();
|
||||
//e.update_cr_with_cond(0, v);
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
e.TraceFPR(i.A.FRT, i.A.FRA, i.A.FRB);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(fsubsx, 0xEC000028, A )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||||
// frD <- (frA) - (frB)
|
||||
|
||||
XmmVar v(c.newXmmVar());
|
||||
c.movq(v, e.fpr_value(i.A.FRA));
|
||||
c.subsd(v, e.fpr_value(i.A.FRB));
|
||||
e.update_fpr_value(i.A.FRT, v);
|
||||
|
||||
// TODO(benvanik): update status/control register.
|
||||
|
||||
if (i.A.Rc) {
|
||||
// With cr0 update.
|
||||
XEASSERTALWAYS();
|
||||
//e.update_cr_with_cond(0, v);
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
e.TraceFPR(i.A.FRT, i.A.FRA, i.A.FRB);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(fselx, 0xFC00002E, A )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||||
// if (frA) >= 0.0
|
||||
// then frD <- (frC)
|
||||
// else frD <- (frB)
|
||||
|
||||
XmmVar v(c.newXmmVar());
|
||||
GpVar zero(c.newGpVar());
|
||||
c.mov(zero, imm(0));
|
||||
c.movq(v, zero);
|
||||
c.comisd(e.fpr_value(i.A.FRA), v);
|
||||
|
||||
// TODO(benvanik): find a way to do this without jumps.
|
||||
Label choose_b(c.newLabel());
|
||||
Label done(c.newLabel());
|
||||
c.jl(choose_b);
|
||||
c.movq(v, e.fpr_value(i.A.FRC));
|
||||
c.jmp(done);
|
||||
c.bind(choose_b);
|
||||
c.movq(v, e.fpr_value(i.A.FRB));
|
||||
c.bind(done);
|
||||
e.update_fpr_value(i.A.FRT, v);
|
||||
|
||||
// TODO(benvanik): update status/control register.
|
||||
|
||||
if (i.A.Rc) {
|
||||
// With cr0 update.
|
||||
XEASSERTALWAYS();
|
||||
//e.update_cr_with_cond(0, v);
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
e.TraceFPR(i.A.FRT, i.A.FRA, i.A.FRB, i.A.FRC);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(fsqrtx, 0xFC00002C, A )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||||
// Double precision:
|
||||
// frD <- sqrt(frB)
|
||||
|
||||
XmmVar v(c.newXmmVar());
|
||||
c.movq(v, e.fpr_value(i.A.FRA));
|
||||
c.sqrtsd(v, v);
|
||||
e.update_fpr_value(i.A.FRT, v);
|
||||
|
||||
// TODO(benvanik): update status/control register.
|
||||
|
||||
if (i.A.Rc) {
|
||||
// With cr0 update.
|
||||
XEASSERTALWAYS();
|
||||
//e.update_cr_with_cond(0, v);
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
e.TraceFPR(i.A.FRT, i.A.FRB);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(fsqrtsx, 0xEC00002C, A )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||||
// Single precision:
|
||||
// frD <- sqrt(frB)
|
||||
|
||||
XmmVar v(c.newXmmVar());
|
||||
c.movq(v, e.fpr_value(i.A.FRA));
|
||||
c.sqrtsd(v, v);
|
||||
#if defined(ROUND_TO_SINGLE)
|
||||
// TODO(benvanik): check rounding mode? etc?
|
||||
// This converts to a single then back to a double to approximate the
|
||||
// rounding on the 360.
|
||||
c.cvtsd2ss(v, v);
|
||||
c.cvtss2sd(v, v);
|
||||
#endif // ROUND_TO_SINGLE
|
||||
e.update_fpr_value(i.A.FRT, v);
|
||||
|
||||
// TODO(benvanik): update status/control register.
|
||||
|
||||
if (i.A.Rc) {
|
||||
// With cr0 update.
|
||||
XEASSERTALWAYS();
|
||||
//e.update_cr_with_cond(0, v);
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
e.TraceFPR(i.A.FRT, i.A.FRB);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Floating-point multiply-add (A-9)
|
||||
|
||||
XEEMITTER(fmaddx, 0xFC00003A, A )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||||
// frD <- (frA x frC) + frB
|
||||
|
||||
XmmVar v(c.newXmmVar());
|
||||
// TODO(benvanik): I'm sure there's an SSE op for this.
|
||||
// NOTE: we do (frB - [frA x frC]) as that's pretty much the same.
|
||||
c.movq(v, e.fpr_value(i.A.FRA));
|
||||
c.mulsd(v, e.fpr_value(i.A.FRC));
|
||||
c.addsd(v, e.fpr_value(i.A.FRB));
|
||||
e.update_fpr_value(i.A.FRT, v);
|
||||
|
||||
// TODO(benvanik): update status/control register.
|
||||
|
||||
if (i.A.Rc) {
|
||||
// With cr0 update.
|
||||
XEASSERTALWAYS();
|
||||
//e.update_cr_with_cond(0, v);
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
e.TraceFPR(i.A.FRT, i.A.FRA, i.A.FRB, i.A.FRC);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(fmaddsx, 0xEC00003A, A )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||||
// frD <- (frA x frC) + frB
|
||||
|
||||
XmmVar v(c.newXmmVar());
|
||||
// TODO(benvanik): I'm sure there's an SSE op for this.
|
||||
// NOTE: we do (frB - [frA x frC]) as that's pretty much the same.
|
||||
c.movq(v, e.fpr_value(i.A.FRA));
|
||||
c.mulsd(v, e.fpr_value(i.A.FRC));
|
||||
c.addsd(v, e.fpr_value(i.A.FRB));
|
||||
#if defined(ROUND_TO_SINGLE)
|
||||
// TODO(benvanik): check rounding mode? etc?
|
||||
// This converts to a single then back to a double to approximate the
|
||||
// rounding on the 360.
|
||||
c.cvtsd2ss(v, v);
|
||||
c.cvtss2sd(v, v);
|
||||
#endif // ROUND_TO_SINGLE
|
||||
e.update_fpr_value(i.A.FRT, v);
|
||||
|
||||
// TODO(benvanik): update status/control register.
|
||||
|
||||
if (i.A.Rc) {
|
||||
// With cr0 update.
|
||||
XEASSERTALWAYS();
|
||||
//e.update_cr_with_cond(0, v);
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
e.TraceFPR(i.A.FRT, i.A.FRA, i.A.FRB, i.A.FRC);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(fmsubx, 0xFC000038, A )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||||
// frD <- (frA x frC) - frB
|
||||
|
||||
XmmVar v(c.newXmmVar());
|
||||
// TODO(benvanik): I'm sure there's an SSE op for this.
|
||||
// NOTE: we do (frB - [frA x frC]) as that's pretty much the same.
|
||||
c.movq(v, e.fpr_value(i.A.FRA));
|
||||
c.mulsd(v, e.fpr_value(i.A.FRC));
|
||||
c.subsd(v, e.fpr_value(i.A.FRB));
|
||||
e.update_fpr_value(i.A.FRT, v);
|
||||
|
||||
// TODO(benvanik): update status/control register.
|
||||
|
||||
if (i.A.Rc) {
|
||||
// With cr0 update.
|
||||
XEASSERTALWAYS();
|
||||
//e.update_cr_with_cond(0, v);
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
e.TraceFPR(i.A.FRT, i.A.FRA, i.A.FRB, i.A.FRC);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(fmsubsx, 0xEC000038, A )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||||
// frD <- (frA x frC) - frB
|
||||
|
||||
XmmVar v(c.newXmmVar());
|
||||
// TODO(benvanik): I'm sure there's an SSE op for this.
|
||||
// NOTE: we do (frB - [frA x frC]) as that's pretty much the same.
|
||||
c.movq(v, e.fpr_value(i.A.FRA));
|
||||
c.mulsd(v, e.fpr_value(i.A.FRC));
|
||||
c.subsd(v, e.fpr_value(i.A.FRB));
|
||||
#if defined(ROUND_TO_SINGLE)
|
||||
// TODO(benvanik): check rounding mode? etc?
|
||||
// This converts to a single then back to a double to approximate the
|
||||
// rounding on the 360.
|
||||
c.cvtsd2ss(v, v);
|
||||
c.cvtss2sd(v, v);
|
||||
#endif // ROUND_TO_SINGLE
|
||||
e.update_fpr_value(i.A.FRT, v);
|
||||
|
||||
// TODO(benvanik): update status/control register.
|
||||
|
||||
if (i.A.Rc) {
|
||||
// With cr0 update.
|
||||
XEASSERTALWAYS();
|
||||
//e.update_cr_with_cond(0, v);
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
e.TraceFPR(i.A.FRT, i.A.FRA, i.A.FRB, i.A.FRC);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(fnmaddx, 0xFC00003E, A )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fnmaddsx, 0xEC00003E, A )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fnmsubx, 0xFC00003C, A )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||||
// frD <- -([frA x frC] - frB)
|
||||
|
||||
XmmVar a_mul_c(c.newXmmVar());
|
||||
// TODO(benvanik): I'm sure there's an SSE op for this.
|
||||
// NOTE: we do (frB - [frA x frC]) as that's pretty much the same.
|
||||
c.movq(a_mul_c, e.fpr_value(i.A.FRA));
|
||||
c.mulsd(a_mul_c, e.fpr_value(i.A.FRC));
|
||||
XmmVar v(c.newXmmVar());
|
||||
c.movq(v, e.fpr_value(i.A.FRB));
|
||||
c.subsd(v, a_mul_c);
|
||||
e.update_fpr_value(i.A.FRT, v);
|
||||
|
||||
// TODO(benvanik): update status/control register.
|
||||
|
||||
if (i.A.Rc) {
|
||||
// With cr0 update.
|
||||
XEASSERTALWAYS();
|
||||
//e.update_cr_with_cond(0, v);
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
e.TraceFPR(i.A.FRT, i.A.FRA, i.A.FRB, i.A.FRC);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(fnmsubsx, 0xEC00003C, A )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||||
// frD <- -([frA x frC] - frB)
|
||||
|
||||
XmmVar a_mul_c(c.newXmmVar());
|
||||
// TODO(benvanik): I'm sure there's an SSE op for this.
|
||||
// NOTE: we do (frB - [frA x frC]) as that's pretty much the same.
|
||||
c.movq(a_mul_c, e.fpr_value(i.A.FRA));
|
||||
c.mulsd(a_mul_c, e.fpr_value(i.A.FRC));
|
||||
XmmVar v(c.newXmmVar());
|
||||
c.movq(v, e.fpr_value(i.A.FRB));
|
||||
c.subsd(v, a_mul_c);
|
||||
#if defined(ROUND_TO_SINGLE)
|
||||
// TODO(benvanik): check rounding mode? etc?
|
||||
// This converts to a single then back to a double to approximate the
|
||||
// rounding on the 360.
|
||||
c.cvtsd2ss(v, v);
|
||||
c.cvtss2sd(v, v);
|
||||
#endif // ROUND_TO_SINGLE
|
||||
e.update_fpr_value(i.A.FRT, v);
|
||||
|
||||
// TODO(benvanik): update status/control register.
|
||||
|
||||
if (i.A.Rc) {
|
||||
// With cr0 update.
|
||||
XEASSERTALWAYS();
|
||||
//e.update_cr_with_cond(0, v);
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
e.TraceFPR(i.A.FRT, i.A.FRA, i.A.FRB, i.A.FRC);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Floating-point rounding and conversion (A-10)
|
||||
|
||||
XEEMITTER(fcfidx, 0xFC00069C, X )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||||
// frD <- signed_int64_to_double( frB )
|
||||
|
||||
XmmVar frb(c.newXmmVar());
|
||||
c.movq(frb, e.fpr_value(i.A.FRB));
|
||||
c.save(frb);
|
||||
XmmVar v(c.newXmmVar());
|
||||
c.cvtsi2sd(v, frb.m64());
|
||||
e.update_fpr_value(i.A.FRT, v);
|
||||
|
||||
// TODO(benvanik): update status/control register.
|
||||
|
||||
if (i.A.Rc) {
|
||||
// With cr0 update.
|
||||
XEASSERTALWAYS();
|
||||
//e.update_cr_with_cond(0, v);
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
e.TraceFPR(i.X.RT, i.X.RB);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(fctidx, 0xFC00065C, X )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||||
// frD <- double_to_signed_int64( frB )
|
||||
|
||||
// UNTESTED: ensure this is correct.
|
||||
//XEASSERTALWAYS();
|
||||
//c.int3();
|
||||
|
||||
Label over_max(c.newLabel());
|
||||
Label under_min(c.newLabel());
|
||||
Label done(c.newLabel());
|
||||
GpVar tmp(c.newGpVar());
|
||||
XmmVar xmm_tmp(c.newXmmVar());
|
||||
|
||||
// TODO(benvanik): pull from FPSCR[RN]
|
||||
// http://www.rz.uni-karlsruhe.de/rz/docs/VTune/reference/vc148.htm
|
||||
// Round to zero (truncate).
|
||||
GpVar mxcsr(c.newGpVar());
|
||||
c.save(mxcsr);
|
||||
c.stmxcsr(mxcsr.m32());
|
||||
c.or_(mxcsr, imm(0x6000));
|
||||
c.save(mxcsr);
|
||||
c.ldmxcsr(mxcsr.m32());
|
||||
|
||||
XmmVar v(c.newXmmVar());
|
||||
c.movq(v, e.fpr_value(i.X.RB));
|
||||
// Max value: 2^63 - 1
|
||||
c.mov(tmp, imm(0x43e0000000000000));
|
||||
c.movq(xmm_tmp, tmp);
|
||||
c.comisd(v, xmm_tmp);
|
||||
c.jl(over_max);
|
||||
// Min value: -2^63
|
||||
c.mov(tmp, imm(0xc3e0000000000000));
|
||||
c.movq(xmm_tmp, tmp);
|
||||
c.comisd(v, xmm_tmp);
|
||||
c.jl(under_min);
|
||||
c.save(v);
|
||||
c.cvtsd2si(tmp, v.m64());
|
||||
c.movq(v, tmp);
|
||||
c.jmp(done);
|
||||
c.bind(over_max);
|
||||
c.mov(tmp, imm(0x7FFFFFFFFFFFFFFF));
|
||||
c.movq(v, tmp);
|
||||
c.jmp(done);
|
||||
c.bind(under_min);
|
||||
c.mov(tmp, imm(0x8000000000000000));
|
||||
c.movq(v, tmp);
|
||||
c.bind(done);
|
||||
e.update_fpr_value(i.X.RT, v);
|
||||
|
||||
// TODO(benvanik): update status/control register.
|
||||
|
||||
if (i.X.Rc) {
|
||||
// With cr0 update.
|
||||
XEASSERTALWAYS();
|
||||
//e.update_cr_with_cond(0, v);
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
e.TraceFPR(i.X.RT, i.X.RB);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(fctidzx, 0xFC00065E, X )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||||
// TODO(benvanik): assuming round to zero is always set, is that ok?
|
||||
return InstrEmit_fctidx(e, c, i);
|
||||
}
|
||||
|
||||
XEEMITTER(fctiwx, 0xFC00001C, X )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||||
// frD <- double_to_signed_int32( frB )
|
||||
|
||||
// UNTESTED: ensure this is correct.
|
||||
//XEASSERTALWAYS();
|
||||
//c.int3();
|
||||
|
||||
Label over_max(c.newLabel());
|
||||
Label under_min(c.newLabel());
|
||||
Label done(c.newLabel());
|
||||
GpVar tmp(c.newGpVar());
|
||||
XmmVar xmm_tmp(c.newXmmVar());
|
||||
|
||||
// TODO(benvanik): pull from FPSCR[RN]
|
||||
// http://www.rz.uni-karlsruhe.de/rz/docs/VTune/reference/vc148.htm
|
||||
// Round to zero (truncate).
|
||||
GpVar mxcsr(c.newGpVar());
|
||||
c.save(mxcsr);
|
||||
c.stmxcsr(mxcsr.m32());
|
||||
c.or_(mxcsr, imm(0x6000));
|
||||
c.save(mxcsr);
|
||||
c.ldmxcsr(mxcsr.m32());
|
||||
|
||||
XmmVar v(c.newXmmVar());
|
||||
c.movq(v, e.fpr_value(i.X.RB));
|
||||
// Max value: 2^31 - 1
|
||||
c.mov(tmp, imm(0x41efffffffe00000));
|
||||
c.movq(xmm_tmp, tmp);
|
||||
c.comisd(v, xmm_tmp);
|
||||
c.jl(over_max);
|
||||
// Min value: -2^31
|
||||
c.mov(tmp, imm(0xc1e0000000000000));
|
||||
c.movq(xmm_tmp, tmp);
|
||||
c.comisd(v, xmm_tmp);
|
||||
c.jg(under_min);
|
||||
c.save(v);
|
||||
c.cvtsd2si(tmp, v.m64());
|
||||
c.movq(v, tmp);
|
||||
c.jmp(done);
|
||||
c.bind(over_max);
|
||||
c.mov(tmp, imm(0x7FFFFFFF));
|
||||
c.movq(v, tmp);
|
||||
c.jmp(done);
|
||||
c.bind(under_min);
|
||||
c.mov(tmp, imm(0x80000000));
|
||||
c.movq(v, tmp);
|
||||
c.bind(done);
|
||||
e.update_fpr_value(i.X.RT, v);
|
||||
|
||||
// TODO(benvanik): update status/control register.
|
||||
|
||||
if (i.A.Rc) {
|
||||
// With cr0 update.
|
||||
XEASSERTALWAYS();
|
||||
//e.update_cr_with_cond(0, v);
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
e.TraceFPR(i.X.RT, i.X.RB);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(fctiwzx, 0xFC00001E, X )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||||
// TODO(benvanik): assuming round to zero is always set, is that ok?
|
||||
return InstrEmit_fctiwx(e, c, i);
|
||||
}
|
||||
|
||||
XEEMITTER(frspx, 0xFC000018, X )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||||
// frD <- Round_single(frB)
|
||||
|
||||
#if defined(ROUND_TO_SINGLE)
|
||||
XmmVar v(c.newXmmVar());
|
||||
// TODO(benvanik): check rounding mode? etc?
|
||||
// This converts to a single then back to a double to approximate the
|
||||
// rounding on the 360.
|
||||
c.cvtsd2ss(v, e.fpr_value(i.X.RB));
|
||||
c.cvtss2sd(v, v);
|
||||
#else
|
||||
XmmVar v(e.fpr_value(i.X.RB));
|
||||
#endif // ROUND_TO_SINGLE
|
||||
e.update_fpr_value(i.X.RT, v);
|
||||
|
||||
// TODO(benvanik): update status/control register.
|
||||
|
||||
if (i.X.Rc) {
|
||||
// With cr0 update.
|
||||
XEASSERTALWAYS();
|
||||
//e.update_cr_with_cond(0, v);
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
e.TraceFPR(i.X.RT, i.X.RB);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Floating-point compare (A-11)
|
||||
|
||||
int InstrEmit_fcmpx_(X64Emitter& e, X86Compiler& c, InstrData& i, bool ordered) {
|
||||
// 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
|
||||
|
||||
GpVar cc(c.newGpVar());
|
||||
c.xor_(cc, cc);
|
||||
|
||||
c.ucomisd(e.fpr_value(i.X.RA), e.fpr_value(i.X.RB));
|
||||
|
||||
GpVar gt(c.newGpVar());
|
||||
c.mov(gt, imm(0x1)); // nan/etc via PF
|
||||
c.cmovp(cc, gt);
|
||||
c.mov(gt, imm(0x2)); // ==
|
||||
c.cmove(cc, gt);
|
||||
if (i.X.RA != i.X.RB) {
|
||||
c.mov(gt, imm(0x8)); // <
|
||||
c.cmovl(cc, gt);
|
||||
c.mov(gt, imm(0x4)); // >
|
||||
c.cmovg(cc, gt);
|
||||
}
|
||||
|
||||
// TODO(benvanik): update FPCC for mffsx/etc
|
||||
|
||||
const uint32_t crf = i.X.RT >> 2;
|
||||
e.update_cr_value(crf, cc);
|
||||
|
||||
// TODO(benvanik): update VXSNAN
|
||||
|
||||
return 0;
|
||||
}
|
||||
XEEMITTER(fcmpo, 0xFC000040, X )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||||
return InstrEmit_fcmpx_(e, c, i, true);
|
||||
}
|
||||
XEEMITTER(fcmpu, 0xFC000000, X )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||||
return InstrEmit_fcmpx_(e, c, i, false);
|
||||
}
|
||||
|
||||
|
||||
// Floating-point status and control register (A
|
||||
|
||||
XEEMITTER(mcrfs, 0xFC000080, X )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(mffsx, 0xFC00048E, X )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(mtfsb0x, 0xFC00008C, X )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(mtfsb1x, 0xFC00004C, X )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(mtfsfx, 0xFC00058E, XFL)(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(mtfsfix, 0xFC00010C, X )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
// Floating-point move (A-21)
|
||||
|
||||
XEEMITTER(fabsx, 0xFC000210, X )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||||
// frD <- abs(frB)
|
||||
|
||||
XmmVar v(c.newXmmVar());
|
||||
c.movq(v, e.fpr_value(i.X.RB));
|
||||
// AND with 0 in the sign bit and 1 everywhere else.
|
||||
GpVar gp_bit(c.newGpVar());
|
||||
c.mov(gp_bit, imm(0x7FFFFFFFFFFFFFFF));
|
||||
XmmVar bit(c.newXmmVar());
|
||||
c.movq(bit, gp_bit);
|
||||
c.andpd(v, bit);
|
||||
e.update_fpr_value(i.X.RT, v);
|
||||
|
||||
if (i.X.Rc) {
|
||||
// With cr0 update.
|
||||
XEASSERTALWAYS();
|
||||
//e.update_cr_with_cond(0, v);
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
e.TraceFPR(i.X.RT, i.X.RB);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(fmrx, 0xFC000090, X )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||||
// frD <- (frB)
|
||||
|
||||
XmmVar v(c.newXmmVar());
|
||||
c.movq(v, e.fpr_value(i.X.RB));
|
||||
e.update_fpr_value(i.X.RT, v);
|
||||
|
||||
if (i.X.Rc) {
|
||||
// With cr0 update.
|
||||
XEASSERTALWAYS();
|
||||
//e.update_cr_with_cond(0, v);
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
e.TraceFPR(i.X.RT, i.X.RB);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(fnabsx, 0xFC000110, X )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fnegx, 0xFC000050, X )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||||
// frD <- ¬ frB[0] || frB[1-63]
|
||||
|
||||
XmmVar v(c.newXmmVar());
|
||||
c.movq(v, e.fpr_value(i.X.RB));
|
||||
// XOR with 1 in the sign bit.
|
||||
GpVar gp_bit(c.newGpVar());
|
||||
c.mov(gp_bit, imm(0x8000000000000000));
|
||||
XmmVar bit(c.newXmmVar());
|
||||
c.movq(bit, gp_bit);
|
||||
c.xorpd(v, bit);
|
||||
e.update_fpr_value(i.X.RT, v);
|
||||
|
||||
if (i.X.Rc) {
|
||||
// With cr0 update.
|
||||
XEASSERTALWAYS();
|
||||
//e.update_cr_with_cond(0, v);
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
e.TraceFPR(i.X.RT, i.X.RB);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void X64RegisterEmitCategoryFPU() {
|
||||
XEREGISTERINSTR(faddx, 0xFC00002A);
|
||||
XEREGISTERINSTR(faddsx, 0xEC00002A);
|
||||
XEREGISTERINSTR(fdivx, 0xFC000024);
|
||||
XEREGISTERINSTR(fdivsx, 0xEC000024);
|
||||
XEREGISTERINSTR(fmulx, 0xFC000032);
|
||||
XEREGISTERINSTR(fmulsx, 0xEC000032);
|
||||
XEREGISTERINSTR(fresx, 0xEC000030);
|
||||
XEREGISTERINSTR(frsqrtex, 0xFC000034);
|
||||
XEREGISTERINSTR(fsubx, 0xFC000028);
|
||||
XEREGISTERINSTR(fsubsx, 0xEC000028);
|
||||
XEREGISTERINSTR(fselx, 0xFC00002E);
|
||||
XEREGISTERINSTR(fsqrtx, 0xFC00002C);
|
||||
XEREGISTERINSTR(fsqrtsx, 0xEC00002C);
|
||||
XEREGISTERINSTR(fmaddx, 0xFC00003A);
|
||||
XEREGISTERINSTR(fmaddsx, 0xEC00003A);
|
||||
XEREGISTERINSTR(fmsubx, 0xFC000038);
|
||||
XEREGISTERINSTR(fmsubsx, 0xEC000038);
|
||||
XEREGISTERINSTR(fnmaddx, 0xFC00003E);
|
||||
XEREGISTERINSTR(fnmaddsx, 0xEC00003E);
|
||||
XEREGISTERINSTR(fnmsubx, 0xFC00003C);
|
||||
XEREGISTERINSTR(fnmsubsx, 0xEC00003C);
|
||||
XEREGISTERINSTR(fcfidx, 0xFC00069C);
|
||||
XEREGISTERINSTR(fctidx, 0xFC00065C);
|
||||
XEREGISTERINSTR(fctidzx, 0xFC00065E);
|
||||
XEREGISTERINSTR(fctiwx, 0xFC00001C);
|
||||
XEREGISTERINSTR(fctiwzx, 0xFC00001E);
|
||||
XEREGISTERINSTR(frspx, 0xFC000018);
|
||||
XEREGISTERINSTR(fcmpo, 0xFC000040);
|
||||
XEREGISTERINSTR(fcmpu, 0xFC000000);
|
||||
XEREGISTERINSTR(mcrfs, 0xFC000080);
|
||||
XEREGISTERINSTR(mffsx, 0xFC00048E);
|
||||
XEREGISTERINSTR(mtfsb0x, 0xFC00008C);
|
||||
XEREGISTERINSTR(mtfsb1x, 0xFC00004C);
|
||||
XEREGISTERINSTR(mtfsfx, 0xFC00058E);
|
||||
XEREGISTERINSTR(mtfsfix, 0xFC00010C);
|
||||
XEREGISTERINSTR(fabsx, 0xFC000210);
|
||||
XEREGISTERINSTR(fmrx, 0xFC000090);
|
||||
XEREGISTERINSTR(fnabsx, 0xFC000110);
|
||||
XEREGISTERINSTR(fnegx, 0xFC000050);
|
||||
}
|
||||
|
||||
|
||||
} // namespace x64
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,186 +0,0 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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_X64_X64_EMITTER_H_
|
||||
#define XENIA_CPU_X64_X64_EMITTER_H_
|
||||
|
||||
#include <xenia/cpu/global_exports.h>
|
||||
#include <xenia/cpu/ppc.h>
|
||||
#include <xenia/cpu/sdb.h>
|
||||
|
||||
#include <asmjit/asmjit.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace x64 {
|
||||
|
||||
|
||||
// Typedef for all generated functions.
|
||||
typedef void (*x64_function_t)(xe_ppc_state_t* ppc_state, uint64_t lr);
|
||||
|
||||
|
||||
class X64Emitter {
|
||||
public:
|
||||
X64Emitter(xe_memory_ref memory);
|
||||
~X64Emitter();
|
||||
|
||||
void AddRegisterAccessCallbacks(ppc::RegisterAccessCallbacks callbacks);
|
||||
|
||||
void Lock();
|
||||
void Unlock();
|
||||
|
||||
uint32_t cpu_feature_mask() const { return cpu_feature_mask_; }
|
||||
|
||||
int PrepareFunction(sdb::FunctionSymbol* symbol);
|
||||
int MakeFunction(sdb::FunctionSymbol* symbol);
|
||||
|
||||
AsmJit::X86Compiler& compiler();
|
||||
sdb::FunctionSymbol* symbol();
|
||||
sdb::FunctionBlock* fn_block();
|
||||
|
||||
AsmJit::Label& GetReturnLabel();
|
||||
AsmJit::Label& GetBlockLabel(uint32_t address);
|
||||
int CallFunction(sdb::FunctionSymbol* target_symbol, AsmJit::GpVar& lr,
|
||||
bool tail);
|
||||
|
||||
void TraceKernelCall();
|
||||
void TraceUserCall();
|
||||
void TraceInstruction(ppc::InstrData& i);
|
||||
void TraceInvalidInstruction(ppc::InstrData& i);
|
||||
void TraceBranch(uint32_t cia);
|
||||
void TraceFPR(uint32_t fpr0, uint32_t fpr1 = UINT_MAX,
|
||||
uint32_t fpr2 = UINT_MAX, uint32_t fpr3 = UINT_MAX,
|
||||
uint32_t fpr4 = UINT_MAX);
|
||||
void TraceVR(uint32_t vr0, uint32_t vr1 = UINT_MAX, uint32_t vr2 = UINT_MAX,
|
||||
uint32_t vr3 = UINT_MAX, uint32_t vr4 = UINT_MAX);
|
||||
|
||||
int GenerateIndirectionBranch(uint32_t cia, AsmJit::GpVar& target,
|
||||
bool lk, bool likely_local);
|
||||
|
||||
AsmJit::GpVar read_gpu_register(uint32_t r);
|
||||
void write_gpu_register(uint32_t r, AsmJit::GpVar& v);
|
||||
|
||||
void FillRegisters();
|
||||
void SpillRegisters();
|
||||
|
||||
bool get_constant_gpr_value(uint32_t n, uint64_t* value);
|
||||
void set_constant_gpr_value(uint32_t n, uint64_t value);
|
||||
void clear_constant_gpr_value(uint32_t n);
|
||||
void clear_all_constant_gpr_values();
|
||||
|
||||
bool check_constant_gpr_read(uint32_t addr, AsmJit::GpVar* reg);
|
||||
bool check_constant_gpr_write(uint32_t addr, AsmJit::GpVar& reg);
|
||||
|
||||
AsmJit::GpVar xer_value();
|
||||
void update_xer_value(AsmJit::GpVar& value);
|
||||
void update_xer_with_overflow(AsmJit::GpVar& value);
|
||||
void update_xer_with_carry(AsmJit::GpVar& value);
|
||||
void update_xer_with_overflow_and_carry(AsmJit::GpVar& value);
|
||||
|
||||
AsmJit::GpVar lr_value();
|
||||
void update_lr_value(AsmJit::GpVar& value);
|
||||
void update_lr_value(AsmJit::Imm& imm);
|
||||
|
||||
AsmJit::GpVar ctr_value();
|
||||
void update_ctr_value(AsmJit::GpVar& value);
|
||||
|
||||
AsmJit::GpVar cr_value(uint32_t n);
|
||||
void update_cr_value(uint32_t n, AsmJit::GpVar& value);
|
||||
void update_cr_with_cond(uint32_t n, AsmJit::GpVar& lhs,
|
||||
bool is_signed = true);
|
||||
void update_cr_with_cond(uint32_t n, AsmJit::GpVar& lhs, AsmJit::GpVar& rhs,
|
||||
bool is_signed = true);
|
||||
|
||||
AsmJit::GpVar gpr_value(uint32_t n);
|
||||
void update_gpr_value(uint32_t n, AsmJit::GpVar& value);
|
||||
AsmJit::XmmVar fpr_value(uint32_t n);
|
||||
void update_fpr_value(uint32_t n, AsmJit::XmmVar& value);
|
||||
AsmJit::XmmVar vr_value(uint32_t n);
|
||||
void update_vr_value(uint32_t n, AsmJit::XmmVar& value);
|
||||
|
||||
AsmJit::GpVar TouchMemoryAddress(uint32_t cia, AsmJit::GpVar& addr);
|
||||
AsmJit::GpVar ReadMemory(
|
||||
uint32_t cia, AsmJit::GpVar& addr, uint32_t size, bool acquire = false,
|
||||
bool bswap = true);
|
||||
void WriteMemory(
|
||||
uint32_t cia, AsmJit::GpVar& addr, uint32_t size, AsmJit::GpVar& value,
|
||||
bool release = false, bool bswap = true);
|
||||
void ByteSwapXmm(AsmJit::XmmVar& value);
|
||||
AsmJit::XmmVar ReadMemoryXmm(
|
||||
uint32_t cia, AsmJit::GpVar& addr, uint32_t alignment);
|
||||
void WriteMemoryXmm(
|
||||
uint32_t cia, AsmJit::GpVar& addr, uint32_t alignment,
|
||||
AsmJit::XmmVar& value);
|
||||
|
||||
AsmJit::GpVar get_uint64(uint64_t value);
|
||||
AsmJit::GpVar sign_extend(AsmJit::GpVar& value, int from_size, int to_size);
|
||||
AsmJit::GpVar zero_extend(AsmJit::GpVar& value, int from_size, int to_size);
|
||||
AsmJit::GpVar trunc(AsmJit::GpVar& value, int size);
|
||||
|
||||
private:
|
||||
static void* OnDemandCompileTrampoline(
|
||||
X64Emitter* emitter, sdb::FunctionSymbol* symbol);
|
||||
void* OnDemandCompile(sdb::FunctionSymbol* symbol);
|
||||
int MakeUserFunction();
|
||||
int MakePresentImportFunction();
|
||||
int MakeMissingImportFunction();
|
||||
|
||||
void GenerateSharedBlocks();
|
||||
int PrepareBasicBlock(sdb::FunctionBlock* block);
|
||||
void GenerateBasicBlock(sdb::FunctionBlock* block);
|
||||
void SetupLocals();
|
||||
|
||||
xe_memory_ref memory_;
|
||||
GlobalExports global_exports_;
|
||||
xe_mutex_t* lock_;
|
||||
uint32_t cpu_feature_mask_;
|
||||
|
||||
std::vector<ppc::RegisterAccessCallbacks> access_callbacks_;
|
||||
|
||||
static uint64_t reserved_addr_;
|
||||
|
||||
AsmJit::Logger* logger_;
|
||||
AsmJit::X86Assembler assembler_;
|
||||
AsmJit::X86Compiler compiler_;
|
||||
|
||||
sdb::FunctionSymbol* symbol_;
|
||||
sdb::FunctionBlock* fn_block_;
|
||||
AsmJit::Label return_block_;
|
||||
AsmJit::Label internal_indirection_block_;
|
||||
AsmJit::Label external_indirection_block_;
|
||||
|
||||
std::map<uint32_t, AsmJit::Label> bbs_;
|
||||
|
||||
ppc::InstrAccessBits access_bits_;
|
||||
struct {
|
||||
bool is_constant;
|
||||
uint64_t value;
|
||||
} gpr_values_[32];
|
||||
struct {
|
||||
AsmJit::GpVar indirection_target;
|
||||
AsmJit::GpVar indirection_cia;
|
||||
|
||||
AsmJit::GpVar xer;
|
||||
AsmJit::GpVar lr;
|
||||
AsmJit::GpVar ctr;
|
||||
AsmJit::GpVar cr[8];
|
||||
AsmJit::GpVar gpr[32];
|
||||
AsmJit::XmmVar fpr[32];
|
||||
AsmJit::XmmVar vr[128];
|
||||
} locals_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace x64
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_X64_X64_EMITTER_H_
|
||||
@@ -1,191 +0,0 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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/x64/x64_jit.h>
|
||||
|
||||
#include <xenia/cpu/cpu-private.h>
|
||||
#include <xenia/cpu/exec_module.h>
|
||||
#include <xenia/cpu/sdb.h>
|
||||
|
||||
#include <asmjit/asmjit.h>
|
||||
|
||||
|
||||
using namespace xe;
|
||||
using namespace xe::cpu;
|
||||
using namespace xe::cpu::ppc;
|
||||
using namespace xe::cpu::sdb;
|
||||
using namespace xe::cpu::x64;
|
||||
|
||||
using namespace AsmJit;
|
||||
|
||||
|
||||
X64JIT::X64JIT(xe_memory_ref memory, SymbolTable* sym_table) :
|
||||
JIT(memory, sym_table),
|
||||
emitter_(NULL) {
|
||||
jit_lock_ = xe_mutex_alloc(10000);
|
||||
}
|
||||
|
||||
X64JIT::~X64JIT() {
|
||||
delete emitter_;
|
||||
xe_mutex_free(jit_lock_);
|
||||
}
|
||||
|
||||
int X64JIT::Setup() {
|
||||
int result_code = 1;
|
||||
|
||||
// Check processor for support.
|
||||
result_code = CheckProcessor();
|
||||
if (result_code) {
|
||||
XELOGE("Processor check failed, aborting...");
|
||||
}
|
||||
XEEXPECTZERO(result_code);
|
||||
|
||||
// Create the emitter used to generate functions.
|
||||
emitter_ = new X64Emitter(memory_);
|
||||
|
||||
result_code = 0;
|
||||
XECLEANUP:
|
||||
return result_code;
|
||||
}
|
||||
|
||||
void X64JIT::AddRegisterAccessCallbacks(RegisterAccessCallbacks callbacks) {
|
||||
emitter_->AddRegisterAccessCallbacks(callbacks);
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct BitDescription {
|
||||
uint32_t mask;
|
||||
const char* description;
|
||||
};
|
||||
static const BitDescription x86Features[] = {
|
||||
{ kX86FeatureRdtsc , "RDTSC" },
|
||||
{ kX86FeatureRdtscP , "RDTSCP" },
|
||||
{ kX86FeatureCMov , "CMOV" },
|
||||
{ kX86FeatureCmpXchg8B , "CMPXCHG8B" },
|
||||
{ kX86FeatureCmpXchg16B , "CMPXCHG16B" },
|
||||
{ kX86FeatureClFlush , "CLFLUSH" },
|
||||
{ kX86FeaturePrefetch , "PREFETCH" },
|
||||
{ kX86FeatureLahfSahf , "LAHF/SAHF" },
|
||||
{ kX86FeatureFXSR , "FXSAVE/FXRSTOR" },
|
||||
{ kX86FeatureFFXSR , "FXSAVE/FXRSTOR Optimizations" },
|
||||
{ kX86FeatureMmx , "MMX" },
|
||||
{ kX86FeatureMmxExt , "MMX Extensions" },
|
||||
{ kX86Feature3dNow , "3dNow!" },
|
||||
{ kX86Feature3dNowExt , "3dNow! Extensions" },
|
||||
{ kX86FeatureSse , "SSE" },
|
||||
{ kX86FeatureSse2 , "SSE2" },
|
||||
{ kX86FeatureSse3 , "SSE3" },
|
||||
{ kX86FeatureSsse3 , "SSSE3" },
|
||||
{ kX86FeatureSse4A , "SSE4A" },
|
||||
{ kX86FeatureSse41 , "SSE4.1" },
|
||||
{ kX86FeatureSse42 , "SSE4.2" },
|
||||
{ kX86FeatureAvx , "AVX" },
|
||||
{ kX86FeatureMSse , "Misaligned SSE" },
|
||||
{ kX86FeatureMonitorMWait , "MONITOR/MWAIT" },
|
||||
{ kX86FeatureMovBE , "MOVBE" },
|
||||
{ kX86FeaturePopCnt , "POPCNT" },
|
||||
{ kX86FeatureLzCnt , "LZCNT" },
|
||||
{ kX86FeaturePclMulDQ , "PCLMULDQ" },
|
||||
{ kX86FeatureMultiThreading , "Multi-Threading" },
|
||||
{ kX86FeatureExecuteDisableBit , "Execute-Disable Bit" },
|
||||
{ kX86Feature64Bit , "64-Bit Processor" },
|
||||
{ 0, NULL },
|
||||
};
|
||||
}
|
||||
|
||||
int X64JIT::CheckProcessor() {
|
||||
const CpuInfo* cpu = CpuInfo::getGlobal();
|
||||
const X86CpuInfo* x86Cpu = static_cast<const X86CpuInfo*>(cpu);
|
||||
const uint32_t mask = cpu->getFeatures();
|
||||
|
||||
// TODO(benvanik): ensure features we want are supported.
|
||||
|
||||
DumpCPUInfo();
|
||||
if (!(mask & kX86FeatureSse41)) {
|
||||
XELOGW("CPU does not support SSE4.1+ instructions!");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void X64JIT::DumpCPUInfo() {
|
||||
const CpuInfo* cpu = CpuInfo::getGlobal();
|
||||
const X86CpuInfo* x86Cpu = static_cast<const X86CpuInfo*>(cpu);
|
||||
const uint32_t mask = cpu->getFeatures();
|
||||
|
||||
XELOGCPU("Processor Info:");
|
||||
XELOGCPU(" Vendor string : %s", cpu->getVendorString());
|
||||
XELOGCPU(" Brand string : %s", cpu->getBrandString());
|
||||
XELOGCPU(" Family : %u", cpu->getFamily());
|
||||
XELOGCPU(" Model : %u", cpu->getModel());
|
||||
XELOGCPU(" Stepping : %u", cpu->getStepping());
|
||||
XELOGCPU(" Number of Processors : %u", cpu->getNumberOfProcessors());
|
||||
XELOGCPU(" Features : 0x%08X", cpu->getFeatures());
|
||||
XELOGCPU(" Bugs : 0x%08X", cpu->getBugs());
|
||||
XELOGCPU(" Processor Type : %u", x86Cpu->getProcessorType());
|
||||
XELOGCPU(" Brand Index : %u", x86Cpu->getBrandIndex());
|
||||
XELOGCPU(" CL Flush Cache Line : %u", x86Cpu->getFlushCacheLineSize());
|
||||
XELOGCPU(" Max logical Processors: %u", x86Cpu->getMaxLogicalProcessors());
|
||||
XELOGCPU(" APIC Physical ID : %u", x86Cpu->getApicPhysicalId());
|
||||
XELOGCPU(" Features:");
|
||||
for (const BitDescription* d = x86Features; d->mask; d++) {
|
||||
if (mask & d->mask) {
|
||||
XELOGCPU(" %s", d->description);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int X64JIT::InitModule(ExecModule* module) {
|
||||
// TODO(benvanik): precompile interesting functions (kernel calls, etc).
|
||||
// TODO(benvanik): warn on unimplemented instructions.
|
||||
// TODO(benvanik): dump instruction use report.
|
||||
// TODO(benvanik): dump kernel use report.
|
||||
// TODO(benvanik): check for cached code/patches/etc.
|
||||
return 0;
|
||||
}
|
||||
|
||||
int X64JIT::UninitModule(ExecModule* module) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void* X64JIT::GetFunctionPointer(sdb::FunctionSymbol* fn_symbol) {
|
||||
// Check function.
|
||||
// TODO(benvanik): make this lock-free (or spin).
|
||||
xe_mutex_lock(jit_lock_);
|
||||
x64_function_t fn_ptr = (x64_function_t)fn_symbol->impl_value;
|
||||
if (!fn_ptr) {
|
||||
// Function hasn't been prepped yet - make it now inline.
|
||||
// The emitter will lock and do other fancy things, if required.
|
||||
if (emitter_->PrepareFunction(fn_symbol)) {
|
||||
xe_mutex_unlock(jit_lock_);
|
||||
return NULL;
|
||||
}
|
||||
fn_ptr = (x64_function_t)fn_symbol->impl_value;
|
||||
XEASSERTNOTNULL(fn_ptr);
|
||||
}
|
||||
xe_mutex_unlock(jit_lock_);
|
||||
return fn_ptr;
|
||||
}
|
||||
|
||||
int X64JIT::Execute(xe_ppc_state_t* ppc_state, FunctionSymbol* fn_symbol) {
|
||||
XELOGCPU("Execute(%.8X): %s...", fn_symbol->start_address, fn_symbol->name());
|
||||
|
||||
x64_function_t fn_ptr = (x64_function_t)GetFunctionPointer(fn_symbol);
|
||||
if (!fn_ptr) {
|
||||
XELOGCPU("Execute(%.8X): unable to make function %s",
|
||||
fn_symbol->start_address, fn_symbol->name());
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Call into the function. This will compile it if needed.
|
||||
fn_ptr(ppc_state, ppc_state->lr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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_X64_X64_JIT_H_
|
||||
#define XENIA_CPU_X64_X64_JIT_H_
|
||||
|
||||
#include <xenia/core.h>
|
||||
|
||||
#include <xenia/cpu/jit.h>
|
||||
#include <xenia/cpu/ppc.h>
|
||||
#include <xenia/cpu/sdb.h>
|
||||
#include <xenia/cpu/x64/x64_emitter.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace x64 {
|
||||
|
||||
|
||||
class X64JIT : public JIT {
|
||||
public:
|
||||
X64JIT(xe_memory_ref memory, sdb::SymbolTable* sym_table);
|
||||
virtual ~X64JIT();
|
||||
|
||||
virtual int Setup();
|
||||
virtual void AddRegisterAccessCallbacks(
|
||||
ppc::RegisterAccessCallbacks callbacks);
|
||||
|
||||
virtual int InitModule(ExecModule* module);
|
||||
virtual int UninitModule(ExecModule* module);
|
||||
|
||||
virtual void* GetFunctionPointer(sdb::FunctionSymbol* fn_symbol);
|
||||
virtual int Execute(xe_ppc_state_t* ppc_state,
|
||||
sdb::FunctionSymbol* fn_symbol);
|
||||
|
||||
protected:
|
||||
int CheckProcessor();
|
||||
void DumpCPUInfo();
|
||||
|
||||
xe_mutex_t* jit_lock_;
|
||||
X64Emitter* emitter_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace x64
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_X64_X64_JIT_H_
|
||||
@@ -7,10 +7,15 @@
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <xenia/memory.h>
|
||||
#include <xenia/cpu/xenon_memory.h>
|
||||
|
||||
#include <alloy/runtime/tracing.h>
|
||||
|
||||
#include <gflags/gflags.h>
|
||||
#include <xenia/core/mutex.h>
|
||||
|
||||
using namespace alloy;
|
||||
using namespace alloy::runtime;
|
||||
using namespace xe::cpu;
|
||||
|
||||
// TODO(benvanik): move xbox.h out
|
||||
#include <xenia/xbox.h>
|
||||
@@ -34,6 +39,7 @@
|
||||
#endif // XE_DEBUG
|
||||
#include <third_party/dlmalloc/malloc.c.h>
|
||||
|
||||
|
||||
DEFINE_bool(
|
||||
log_heap, false,
|
||||
"Log heap structure on alloc/free.");
|
||||
@@ -60,12 +66,12 @@ DEFINE_bool(
|
||||
* we don't have to emulate a TLB. It'd be really cool to pass through page
|
||||
* sizes or use madvice to let the OS know what to expect.
|
||||
*
|
||||
* We create our own heap of committed memory that lives at XE_MEMORY_HEAP_LOW
|
||||
* to XE_MEMORY_HEAP_HIGH - all normal user allocations come from there. Since
|
||||
* the Xbox has no paging, we know that the size of this heap will never need
|
||||
* to be larger than ~512MB (realistically, smaller than that). We place it far
|
||||
* away from the XEX data and keep the memory around it uncommitted so that we
|
||||
* have some warning if things go astray.
|
||||
* We create our own heap of committed memory that lives at
|
||||
* XENON_MEMORY_HEAP_LOW to XENON_MEMORY_HEAP_HIGH - all normal user allocations
|
||||
* come from there. Since the Xbox has no paging, we know that the size of this
|
||||
* heap will never need to be larger than ~512MB (realistically, smaller than
|
||||
* that). We place it far away from the XEX data and keep the memory around it
|
||||
* uncommitted so that we have some warning if things go astray.
|
||||
*
|
||||
* For XEX/GPU/etc data we allow placement allocations (base_address != 0) and
|
||||
* commit the requested memory as needed. This bypasses the standard heap, but
|
||||
@@ -77,143 +83,136 @@ DEFINE_bool(
|
||||
* this.
|
||||
*/
|
||||
|
||||
#define XE_MEMORY_PHYSICAL_HEAP_LOW 0x00010000
|
||||
#define XE_MEMORY_PHYSICAL_HEAP_HIGH 0x20000000
|
||||
#define XE_MEMORY_VIRTUAL_HEAP_LOW 0x20000000
|
||||
#define XE_MEMORY_VIRTUAL_HEAP_HIGH 0x40000000
|
||||
#define XENON_MEMORY_PHYSICAL_HEAP_LOW 0x00010000
|
||||
#define XENON_MEMORY_PHYSICAL_HEAP_HIGH 0x20000000
|
||||
#define XENON_MEMORY_VIRTUAL_HEAP_LOW 0x20000000
|
||||
#define XENON_MEMORY_VIRTUAL_HEAP_HIGH 0x40000000
|
||||
|
||||
|
||||
typedef struct {
|
||||
xe_memory_ref memory;
|
||||
bool physical;
|
||||
xe_mutex_t* mutex;
|
||||
size_t size;
|
||||
uint8_t* ptr;
|
||||
mspace space;
|
||||
class xe::cpu::XenonMemoryHeap {
|
||||
public:
|
||||
XenonMemoryHeap(XenonMemory* memory, bool is_physical);
|
||||
~XenonMemoryHeap();
|
||||
|
||||
int Initialize(uint64_t low, uint64_t high);
|
||||
|
||||
uint64_t Alloc(uint64_t base_address, size_t size,
|
||||
uint32_t flags, uint32_t alignment);
|
||||
uint64_t Free(uint64_t address, size_t size);
|
||||
|
||||
int Initialize(xe_memory_ref memory, uint32_t low, uint32_t high,
|
||||
bool physical);
|
||||
void Cleanup();
|
||||
void Dump();
|
||||
uint32_t Alloc(uint32_t base_address,
|
||||
uint32_t size, uint32_t flags,
|
||||
uint32_t alignment);
|
||||
uint32_t Free(uint32_t address, uint32_t size);
|
||||
|
||||
private:
|
||||
static uint32_t next_heap_id_;
|
||||
static void DumpHandler(
|
||||
void* start, void* end, size_t used_bytes, void* context);
|
||||
} xe_memory_heap_t;
|
||||
|
||||
|
||||
struct xe_memory {
|
||||
xe_ref_t ref;
|
||||
|
||||
size_t system_page_size;
|
||||
|
||||
HANDLE mapping;
|
||||
uint8_t* mapping_base;
|
||||
union {
|
||||
struct {
|
||||
uint8_t* v00000000;
|
||||
uint8_t* v40000000;
|
||||
uint8_t* v80000000;
|
||||
uint8_t* vA0000000;
|
||||
uint8_t* vC0000000;
|
||||
uint8_t* vE0000000;
|
||||
};
|
||||
uint8_t* all_views[6];
|
||||
} views;
|
||||
|
||||
xe_memory_heap_t virtual_heap;
|
||||
xe_memory_heap_t physical_heap;
|
||||
private:
|
||||
XenonMemory* memory_;
|
||||
uint32_t heap_id_;
|
||||
bool is_physical_;
|
||||
Mutex* lock_;
|
||||
size_t size_;
|
||||
uint8_t* ptr_;
|
||||
mspace space_;
|
||||
};
|
||||
uint32_t XenonMemoryHeap::next_heap_id_ = 1;
|
||||
|
||||
|
||||
int xe_memory_map_views(xe_memory_ref memory, uint8_t* mapping_base);
|
||||
void xe_memory_unmap_views(xe_memory_ref memory);
|
||||
XenonMemory::XenonMemory() :
|
||||
mapping_(0), mapping_base_(0),
|
||||
Memory() {
|
||||
virtual_heap_ = new XenonMemoryHeap(this, false);
|
||||
physical_heap_ = new XenonMemoryHeap(this, true);
|
||||
}
|
||||
|
||||
XenonMemory::~XenonMemory() {
|
||||
if (mapping_base_) {
|
||||
// GPU writeback.
|
||||
VirtualFree(
|
||||
Translate(0xC0000000), 0x00100000,
|
||||
MEM_DECOMMIT);
|
||||
}
|
||||
|
||||
xe_memory_ref xe_memory_create(xe_memory_options_t options) {
|
||||
xe_memory_ref memory = (xe_memory_ref)xe_calloc(sizeof(xe_memory));
|
||||
xe_ref_init((xe_ref)memory);
|
||||
delete physical_heap_;
|
||||
delete virtual_heap_;
|
||||
|
||||
SYSTEM_INFO si;
|
||||
GetSystemInfo(&si);
|
||||
memory->system_page_size = si.dwPageSize;
|
||||
// Unmap all views and close mapping.
|
||||
if (mapping_) {
|
||||
UnmapViews();
|
||||
CloseHandle(mapping_);
|
||||
mapping_base_ = 0;
|
||||
mapping_ = 0;
|
||||
|
||||
alloy::tracing::WriteEvent(EventType::MemoryDeinit({
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
int XenonMemory::Initialize() {
|
||||
int result = Memory::Initialize();
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
result = 1;
|
||||
|
||||
// Create main page file-backed mapping. This is all reserved but
|
||||
// uncommitted (so it shouldn't expand page file).
|
||||
memory->mapping = CreateFileMapping(
|
||||
mapping_ = CreateFileMapping(
|
||||
INVALID_HANDLE_VALUE,
|
||||
NULL,
|
||||
PAGE_READWRITE | SEC_RESERVE,
|
||||
1, 0, // entire 4gb space
|
||||
NULL);
|
||||
if (!memory->mapping) {
|
||||
if (!mapping_) {
|
||||
XELOGE("Unable to reserve the 4gb guest address space.");
|
||||
XEASSERTNOTNULL(memory->mapping);
|
||||
XEASSERTNOTNULL(mapping_);
|
||||
XEFAIL();
|
||||
}
|
||||
|
||||
// Attempt to create our views. This may fail at the first address
|
||||
// we pick, so try a few times.
|
||||
memory->mapping_base = 0;
|
||||
mapping_base_ = 0;
|
||||
for (size_t n = 32; n < 64; n++) {
|
||||
uint8_t* mapping_base = (uint8_t*)(1ull << n);
|
||||
if (!xe_memory_map_views(memory, mapping_base)) {
|
||||
memory->mapping_base = mapping_base;
|
||||
if (!MapViews(mapping_base)) {
|
||||
mapping_base_ = mapping_base;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!memory->mapping_base) {
|
||||
if (!mapping_base_) {
|
||||
XELOGE("Unable to find a continuous block in the 64bit address space.");
|
||||
XEASSERTALWAYS();
|
||||
XEFAIL();
|
||||
}
|
||||
membase_ = mapping_base_;
|
||||
|
||||
alloy::tracing::WriteEvent(EventType::MemoryInit({
|
||||
}));
|
||||
|
||||
// Prepare heaps.
|
||||
memory->virtual_heap.Initialize(
|
||||
memory, XE_MEMORY_VIRTUAL_HEAP_LOW, XE_MEMORY_VIRTUAL_HEAP_HIGH,
|
||||
false);
|
||||
memory->physical_heap.Initialize(
|
||||
memory, XE_MEMORY_PHYSICAL_HEAP_LOW, XE_MEMORY_PHYSICAL_HEAP_HIGH,
|
||||
true);
|
||||
virtual_heap_->Initialize(
|
||||
XENON_MEMORY_VIRTUAL_HEAP_LOW, XENON_MEMORY_VIRTUAL_HEAP_HIGH);
|
||||
physical_heap_->Initialize(
|
||||
XENON_MEMORY_PHYSICAL_HEAP_LOW, XENON_MEMORY_PHYSICAL_HEAP_HIGH);
|
||||
|
||||
// GPU writeback.
|
||||
VirtualAlloc(
|
||||
memory->mapping_base + 0xC0000000, 0x00100000,
|
||||
Translate(0xC0000000 + system_page_size_),
|
||||
0x00100000 - system_page_size_,
|
||||
MEM_COMMIT, PAGE_READWRITE);
|
||||
|
||||
return memory;
|
||||
return 0;
|
||||
|
||||
XECLEANUP:
|
||||
xe_memory_release(memory);
|
||||
return NULL;
|
||||
return result;
|
||||
}
|
||||
|
||||
void xe_memory_dealloc(xe_memory_ref memory) {
|
||||
// GPU writeback.
|
||||
VirtualFree(
|
||||
memory->mapping_base + 0xC0000000, 0x00100000,
|
||||
MEM_DECOMMIT);
|
||||
|
||||
// Cleanup heaps.
|
||||
memory->virtual_heap.Cleanup();
|
||||
memory->physical_heap.Cleanup();
|
||||
|
||||
// Unmap all views and close mapping.
|
||||
if (memory->mapping) {
|
||||
xe_memory_unmap_views(memory);
|
||||
CloseHandle(memory->mapping);
|
||||
}
|
||||
}
|
||||
|
||||
int xe_memory_map_views(xe_memory_ref memory, uint8_t* mapping_base) {
|
||||
int XenonMemory::MapViews(uint8_t* mapping_base) {
|
||||
static struct {
|
||||
uint32_t virtual_address_start;
|
||||
uint32_t virtual_address_end;
|
||||
uint32_t target_address;
|
||||
uint64_t virtual_address_start;
|
||||
uint64_t virtual_address_end;
|
||||
uint64_t target_address;
|
||||
} map_info[] = {
|
||||
0x00000000, 0x3FFFFFFF, 0x00000000, // (1024mb) - virtual 4k pages
|
||||
0x40000000, 0x7FFFFFFF, 0x40000000, // (1024mb) - virtual 64k pages
|
||||
@@ -222,112 +221,67 @@ int xe_memory_map_views(xe_memory_ref memory, uint8_t* mapping_base) {
|
||||
0xC0000000, 0xDFFFFFFF, 0x00000000, // - physical 16mb pages
|
||||
0xE0000000, 0xFFFFFFFF, 0x00000000, // - physical 4k pages
|
||||
};
|
||||
XEASSERT(XECOUNT(map_info) == XECOUNT(memory->views.all_views));
|
||||
XEASSERT(XECOUNT(map_info) == XECOUNT(views_.all_views));
|
||||
for (size_t n = 0; n < XECOUNT(map_info); n++) {
|
||||
memory->views.all_views[n] = (uint8_t*)MapViewOfFileEx(
|
||||
memory->mapping,
|
||||
views_.all_views[n] = (uint8_t*)MapViewOfFileEx(
|
||||
mapping_,
|
||||
FILE_MAP_ALL_ACCESS,
|
||||
0x00000000, map_info[n].target_address,
|
||||
0x00000000, (DWORD)map_info[n].target_address,
|
||||
map_info[n].virtual_address_end - map_info[n].virtual_address_start + 1,
|
||||
mapping_base + map_info[n].virtual_address_start);
|
||||
XEEXPECTNOTNULL(memory->views.all_views[n]);
|
||||
XEEXPECTNOTNULL(views_.all_views[n]);
|
||||
}
|
||||
return 0;
|
||||
|
||||
XECLEANUP:
|
||||
xe_memory_unmap_views(memory);
|
||||
UnmapViews();
|
||||
return 1;
|
||||
}
|
||||
|
||||
void xe_memory_unmap_views(xe_memory_ref memory) {
|
||||
for (size_t n = 0; n < XECOUNT(memory->views.all_views); n++) {
|
||||
if (memory->views.all_views[n]) {
|
||||
UnmapViewOfFile(memory->views.all_views[n]);
|
||||
void XenonMemory::UnmapViews() {
|
||||
for (size_t n = 0; n < XECOUNT(views_.all_views); n++) {
|
||||
if (views_.all_views[n]) {
|
||||
UnmapViewOfFile(views_.all_views[n]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
xe_memory_ref xe_memory_retain(xe_memory_ref memory) {
|
||||
xe_ref_retain((xe_ref)memory);
|
||||
return memory;
|
||||
}
|
||||
|
||||
void xe_memory_release(xe_memory_ref memory) {
|
||||
xe_ref_release((xe_ref)memory, (xe_ref_dealloc_t)xe_memory_dealloc);
|
||||
}
|
||||
|
||||
uint8_t *xe_memory_addr(xe_memory_ref memory, size_t guest_addr) {
|
||||
return memory->mapping_base + guest_addr;
|
||||
}
|
||||
|
||||
void xe_memory_copy(xe_memory_ref memory,
|
||||
uint32_t dest, uint32_t src, uint32_t size) {
|
||||
uint8_t* pdest = memory->mapping_base + dest;
|
||||
uint8_t* psrc = memory->mapping_base + src;
|
||||
XEIGNORE(xe_copy_memory(pdest, size, psrc, size));
|
||||
}
|
||||
|
||||
uint32_t xe_memory_search_aligned(xe_memory_ref memory, size_t start,
|
||||
size_t end, const uint32_t *values,
|
||||
const size_t value_count) {
|
||||
XEASSERT(start <= end);
|
||||
const uint32_t *p = (const uint32_t*)xe_memory_addr(memory, start);
|
||||
const uint32_t *pe = (const uint32_t*)xe_memory_addr(memory, end);
|
||||
while (p != pe) {
|
||||
if (*p == values[0]) {
|
||||
const uint32_t *pc = p + 1;
|
||||
size_t matched = 1;
|
||||
for (size_t n = 1; n < value_count; n++, pc++) {
|
||||
if (*pc != values[n]) {
|
||||
break;
|
||||
}
|
||||
matched++;
|
||||
}
|
||||
if (matched == value_count) {
|
||||
return (uint32_t)((uint8_t*)p - memory->mapping_base);
|
||||
}
|
||||
}
|
||||
p++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t xe_memory_heap_alloc(
|
||||
xe_memory_ref memory, uint32_t base_address, uint32_t size,
|
||||
uint32_t flags, uint32_t alignment) {
|
||||
uint64_t XenonMemory::HeapAlloc(
|
||||
uint64_t base_address, size_t size, uint32_t flags,
|
||||
uint32_t alignment) {
|
||||
// If we were given a base address we are outside of the normal heap and
|
||||
// will place wherever asked (so long as it doesn't overlap the heap).
|
||||
if (!base_address) {
|
||||
// Normal allocation from the managed heap.
|
||||
uint32_t result;
|
||||
if (flags & XE_MEMORY_FLAG_PHYSICAL) {
|
||||
result = memory->physical_heap.Alloc(
|
||||
uint64_t result;
|
||||
if (flags & MEMORY_FLAG_PHYSICAL) {
|
||||
result = physical_heap_->Alloc(
|
||||
base_address, size, flags, alignment);
|
||||
} else {
|
||||
result = memory->virtual_heap.Alloc(
|
||||
result = virtual_heap_->Alloc(
|
||||
base_address, size, flags, alignment);
|
||||
}
|
||||
if (result) {
|
||||
if (flags & XE_MEMORY_FLAG_ZERO) {
|
||||
xe_zero_struct(memory->mapping_base + result, size);
|
||||
if (flags & MEMORY_FLAG_ZERO) {
|
||||
xe_zero_struct(Translate(result), size);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
if (base_address >= XE_MEMORY_VIRTUAL_HEAP_LOW &&
|
||||
base_address < XE_MEMORY_VIRTUAL_HEAP_HIGH) {
|
||||
if (base_address >= XENON_MEMORY_VIRTUAL_HEAP_LOW &&
|
||||
base_address < XENON_MEMORY_VIRTUAL_HEAP_HIGH) {
|
||||
// Overlapping managed heap.
|
||||
XEASSERTALWAYS();
|
||||
return 0;
|
||||
}
|
||||
if (base_address >= XE_MEMORY_PHYSICAL_HEAP_LOW &&
|
||||
base_address < XE_MEMORY_PHYSICAL_HEAP_HIGH) {
|
||||
if (base_address >= XENON_MEMORY_PHYSICAL_HEAP_LOW &&
|
||||
base_address < XENON_MEMORY_PHYSICAL_HEAP_HIGH) {
|
||||
// Overlapping managed heap.
|
||||
XEASSERTALWAYS();
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t* p = memory->mapping_base + base_address;
|
||||
uint8_t* p = Translate(base_address);
|
||||
// TODO(benvanik): check if address range is in use with a query.
|
||||
|
||||
void* pv = VirtualAlloc(p, size, MEM_COMMIT, PAGE_READWRITE);
|
||||
@@ -337,48 +291,37 @@ uint32_t xe_memory_heap_alloc(
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (flags & XE_MEMORY_FLAG_ZERO) {
|
||||
if (flags & MEMORY_FLAG_ZERO) {
|
||||
xe_zero_struct(pv, size);
|
||||
}
|
||||
|
||||
alloy::tracing::WriteEvent(EventType::MemoryHeapAlloc({
|
||||
0, flags, base_address, size,
|
||||
}));
|
||||
|
||||
return base_address;
|
||||
}
|
||||
}
|
||||
|
||||
int xe_memory_heap_free(
|
||||
xe_memory_ref memory, uint32_t address, uint32_t size) {
|
||||
if (address >= XE_MEMORY_VIRTUAL_HEAP_LOW &&
|
||||
address < XE_MEMORY_VIRTUAL_HEAP_HIGH) {
|
||||
return memory->virtual_heap.Free(address, size);
|
||||
} else if (address >= XE_MEMORY_PHYSICAL_HEAP_LOW &&
|
||||
address < XE_MEMORY_PHYSICAL_HEAP_HIGH) {
|
||||
return memory->physical_heap.Free(address, size);
|
||||
int XenonMemory::HeapFree(uint64_t address, size_t size) {
|
||||
if (address >= XENON_MEMORY_VIRTUAL_HEAP_LOW &&
|
||||
address < XENON_MEMORY_VIRTUAL_HEAP_HIGH) {
|
||||
return virtual_heap_->Free(address, size) ? 0 : 1;
|
||||
} else if (address >= XENON_MEMORY_PHYSICAL_HEAP_LOW &&
|
||||
address < XENON_MEMORY_PHYSICAL_HEAP_HIGH) {
|
||||
return physical_heap_->Free(address, size) ? 0 : 1;
|
||||
} else {
|
||||
// A placed address. Decommit.
|
||||
uint8_t* p = memory->mapping_base + address;
|
||||
alloy::tracing::WriteEvent(EventType::MemoryHeapFree({
|
||||
0, address,
|
||||
}));
|
||||
uint8_t* p = Translate(address);
|
||||
return VirtualFree(p, size, MEM_DECOMMIT) ? 0 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
bool xe_memory_is_valid(xe_memory_ref memory, uint32_t address) {
|
||||
uint8_t* p = memory->mapping_base + address;
|
||||
if ((address >= XE_MEMORY_VIRTUAL_HEAP_LOW &&
|
||||
address < XE_MEMORY_VIRTUAL_HEAP_HIGH) ||
|
||||
(address >= XE_MEMORY_PHYSICAL_HEAP_LOW &&
|
||||
address < XE_MEMORY_PHYSICAL_HEAP_HIGH)) {
|
||||
// Within heap range, ask dlmalloc.
|
||||
size_t heap_guard_size = FLAGS_heap_guard_pages * 4096;
|
||||
p -= heap_guard_size;
|
||||
return mspace_usable_size(p) > 0;
|
||||
} else {
|
||||
// Maybe -- could Query here (though that may be expensive).
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
int xe_memory_protect(
|
||||
xe_memory_ref memory, uint32_t address, uint32_t size, uint32_t access) {
|
||||
uint8_t* p = memory->mapping_base + address;
|
||||
int XenonMemory::Protect(uint64_t address, size_t size, uint32_t access) {
|
||||
uint8_t* p = Translate(address);
|
||||
|
||||
size_t heap_guard_size = FLAGS_heap_guard_pages * 4096;
|
||||
p += heap_guard_size;
|
||||
@@ -393,91 +336,67 @@ int xe_memory_protect(
|
||||
return VirtualProtect(p, size, new_protect, &old_protect) == TRUE ? 0 : 1;
|
||||
}
|
||||
|
||||
uint32_t XenonMemory::QueryProtect(uint64_t address) {
|
||||
uint8_t* p = Translate(address);
|
||||
MEMORY_BASIC_INFORMATION info;
|
||||
size_t info_size = VirtualQuery((void*)p, &info, sizeof(info));
|
||||
if (!info_size) {
|
||||
return 0;
|
||||
}
|
||||
return info.Protect;
|
||||
}
|
||||
|
||||
int xe_memory_heap_t::Initialize(
|
||||
xe_memory_ref memory, uint32_t low, uint32_t high, bool physical) {
|
||||
this->memory = memory;
|
||||
this->physical = physical;
|
||||
|
||||
// Lock used around heap allocs/frees.
|
||||
mutex = xe_mutex_alloc(10000);
|
||||
if (!mutex) {
|
||||
return 1;
|
||||
XenonMemoryHeap::XenonMemoryHeap(XenonMemory* memory, bool is_physical) :
|
||||
memory_(memory), is_physical_(is_physical) {
|
||||
heap_id_ = next_heap_id_++;
|
||||
lock_ = AllocMutex(10000);
|
||||
}
|
||||
|
||||
XenonMemoryHeap::~XenonMemoryHeap() {
|
||||
if (lock_ && space_) {
|
||||
LockMutex(lock_);
|
||||
destroy_mspace(space_);
|
||||
space_ = NULL;
|
||||
UnlockMutex(lock_);
|
||||
}
|
||||
if (lock_) {
|
||||
FreeMutex(lock_);
|
||||
lock_ = NULL;
|
||||
}
|
||||
|
||||
if (ptr_) {
|
||||
XEIGNORE(VirtualFree(ptr_, 0, MEM_RELEASE));
|
||||
|
||||
alloy::tracing::WriteEvent(EventType::MemoryHeapDeinit({
|
||||
heap_id_,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
int XenonMemoryHeap::Initialize(uint64_t low, uint64_t high) {
|
||||
// Commit the memory where our heap will live and allocate it.
|
||||
// TODO(benvanik): replace dlmalloc with an implementation that can commit
|
||||
// as it goes.
|
||||
size = high - low;
|
||||
ptr = memory->views.v00000000 + low;
|
||||
size_ = high - low;
|
||||
ptr_ = memory_->views_.v00000000 + low;
|
||||
void* heap_result = VirtualAlloc(
|
||||
ptr, size, MEM_COMMIT, PAGE_READWRITE);
|
||||
ptr_, size_, MEM_COMMIT, PAGE_READWRITE);
|
||||
if (!heap_result) {
|
||||
return 1;
|
||||
}
|
||||
space = create_mspace_with_base(ptr, size, 0);
|
||||
space_ = create_mspace_with_base(ptr_, size_, 0);
|
||||
|
||||
alloy::tracing::WriteEvent(EventType::MemoryHeapInit({
|
||||
heap_id_, low, high, is_physical_,
|
||||
}));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void xe_memory_heap_t::Cleanup() {
|
||||
if (mutex && space) {
|
||||
xe_mutex_lock(mutex);
|
||||
destroy_mspace(space);
|
||||
space = NULL;
|
||||
xe_mutex_unlock(mutex);
|
||||
}
|
||||
if (mutex) {
|
||||
xe_mutex_free(mutex);
|
||||
mutex = NULL;
|
||||
}
|
||||
|
||||
XEIGNORE(VirtualFree(ptr, 0, MEM_RELEASE));
|
||||
}
|
||||
|
||||
void xe_memory_heap_t::Dump() {
|
||||
XELOGI("xe_memory_heap::Dump - %s",
|
||||
physical ? "physical" : "virtual");
|
||||
if (FLAGS_heap_guard_pages) {
|
||||
XELOGI(" (heap guard pages enabled, stats will be wrong)");
|
||||
}
|
||||
struct mallinfo info = mspace_mallinfo(space);
|
||||
XELOGI(" arena: %lld", info.arena);
|
||||
XELOGI(" ordblks: %lld", info.ordblks);
|
||||
XELOGI(" hblks: %lld", info.hblks);
|
||||
XELOGI(" hblkhd: %lld", info.hblkhd);
|
||||
XELOGI(" usmblks: %lld", info.usmblks);
|
||||
XELOGI(" uordblks: %lld", info.uordblks);
|
||||
XELOGI(" fordblks: %lld", info.fordblks);
|
||||
XELOGI(" keepcost: %lld", info.keepcost);
|
||||
mspace_inspect_all(space, DumpHandler, this);
|
||||
}
|
||||
|
||||
void xe_memory_heap_t::DumpHandler(
|
||||
void* start, void* end, size_t used_bytes, void* context) {
|
||||
xe_memory_heap_t* heap = (xe_memory_heap_t*)context;
|
||||
xe_memory_ref memory = heap->memory;
|
||||
size_t heap_guard_size = FLAGS_heap_guard_pages * 4096;
|
||||
uint64_t start_addr = (uint64_t)start + heap_guard_size;
|
||||
uint64_t end_addr = (uint64_t)end - heap_guard_size;
|
||||
uint32_t guest_start =
|
||||
(uint32_t)(start_addr - (uintptr_t)memory->mapping_base);
|
||||
uint32_t guest_end =
|
||||
(uint32_t)(end_addr - (uintptr_t)memory->mapping_base);
|
||||
if (used_bytes > 0) {
|
||||
XELOGI(" - %.8X-%.8X (%10db) %.16llX-%.16llX - %9db used",
|
||||
guest_start, guest_end, (guest_end - guest_start),
|
||||
start_addr, end_addr,
|
||||
used_bytes);
|
||||
} else {
|
||||
XELOGI(" - %.16llX-%.16llX - %9db used",
|
||||
start_addr, end_addr, used_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t xe_memory_heap_t::Alloc(
|
||||
uint32_t base_address, uint32_t size, uint32_t flags,
|
||||
uint32_t alignment) {
|
||||
XEIGNORE(xe_mutex_lock(mutex));
|
||||
uint64_t XenonMemoryHeap::Alloc(
|
||||
uint64_t base_address, size_t size, uint32_t flags, uint32_t alignment) {
|
||||
XEIGNORE(LockMutex(lock_));
|
||||
size_t alloc_size = size;
|
||||
size_t heap_guard_size = FLAGS_heap_guard_pages * 4096;
|
||||
if (heap_guard_size) {
|
||||
@@ -485,7 +404,7 @@ uint32_t xe_memory_heap_t::Alloc(
|
||||
alloc_size = (uint32_t)XEROUNDUP(size, heap_guard_size);
|
||||
}
|
||||
uint8_t* p = (uint8_t*)mspace_memalign(
|
||||
space,
|
||||
space_,
|
||||
alignment,
|
||||
alloc_size + heap_guard_size * 2);
|
||||
if (FLAGS_heap_guard_pages) {
|
||||
@@ -502,26 +421,26 @@ uint32_t xe_memory_heap_t::Alloc(
|
||||
if (FLAGS_log_heap) {
|
||||
Dump();
|
||||
}
|
||||
XEIGNORE(xe_mutex_unlock(mutex));
|
||||
XEIGNORE(UnlockMutex(lock_));
|
||||
if (!p) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (physical) {
|
||||
if (is_physical_) {
|
||||
// If physical, we need to commit the memory in the physical address ranges
|
||||
// so that it can be accessed.
|
||||
VirtualAlloc(
|
||||
memory->views.vA0000000 + (p - memory->views.v00000000),
|
||||
memory_->views_.vA0000000 + (p - memory_->views_.v00000000),
|
||||
size,
|
||||
MEM_COMMIT,
|
||||
PAGE_READWRITE);
|
||||
VirtualAlloc(
|
||||
memory->views.vC0000000 + (p - memory->views.v00000000),
|
||||
memory_->views_.vC0000000 + (p - memory_->views_.v00000000),
|
||||
size,
|
||||
MEM_COMMIT,
|
||||
PAGE_READWRITE);
|
||||
VirtualAlloc(
|
||||
memory->views.vE0000000 + (p - memory->views.v00000000),
|
||||
memory_->views_.vE0000000 + (p - memory_->views_.v00000000),
|
||||
size,
|
||||
MEM_COMMIT,
|
||||
PAGE_READWRITE);
|
||||
@@ -536,11 +455,18 @@ uint32_t xe_memory_heap_t::Alloc(
|
||||
memset(p, 0, alloc_size);
|
||||
}
|
||||
|
||||
return (uint32_t)((uintptr_t)p - (uintptr_t)memory->mapping_base);
|
||||
uint64_t address =
|
||||
(uint64_t)((uintptr_t)p - (uintptr_t)memory_->mapping_base_);
|
||||
|
||||
alloy::tracing::WriteEvent(EventType::MemoryHeapAlloc({
|
||||
heap_id_, flags, address, size,
|
||||
}));
|
||||
|
||||
return address;
|
||||
}
|
||||
|
||||
uint32_t xe_memory_heap_t::Free(uint32_t address, uint32_t size) {
|
||||
uint8_t* p = memory->mapping_base + address;
|
||||
uint64_t XenonMemoryHeap::Free(uint64_t address, size_t size) {
|
||||
uint8_t* p = memory_->Translate(address);
|
||||
|
||||
// Heap allocated address.
|
||||
size_t heap_guard_size = FLAGS_heap_guard_pages * 4096;
|
||||
@@ -553,10 +479,10 @@ uint32_t xe_memory_heap_t::Free(uint32_t address, uint32_t size) {
|
||||
|
||||
if (FLAGS_scribble_heap) {
|
||||
// Trash the memory so that we can see bad read-before-write bugs easier.
|
||||
memset(p, 0xDC, size);
|
||||
memset(p + heap_guard_size, 0xDC, size);
|
||||
}
|
||||
|
||||
XEIGNORE(xe_mutex_lock(mutex));
|
||||
XEIGNORE(LockMutex(lock_));
|
||||
if (FLAGS_heap_guard_pages) {
|
||||
DWORD old_protect;
|
||||
VirtualProtect(
|
||||
@@ -566,37 +492,71 @@ uint32_t xe_memory_heap_t::Free(uint32_t address, uint32_t size) {
|
||||
p + heap_guard_size + real_size, heap_guard_size,
|
||||
PAGE_READWRITE, &old_protect);
|
||||
}
|
||||
mspace_free(space, p);
|
||||
mspace_free(space_, p);
|
||||
if (FLAGS_log_heap) {
|
||||
Dump();
|
||||
}
|
||||
XEIGNORE(xe_mutex_unlock(mutex));
|
||||
XEIGNORE(UnlockMutex(lock_));
|
||||
|
||||
if (physical) {
|
||||
if (is_physical_) {
|
||||
// If physical, decommit from physical ranges too.
|
||||
VirtualFree(
|
||||
memory->views.vA0000000 + (p - memory->views.v00000000),
|
||||
memory_->views_.vA0000000 + (p - memory_->views_.v00000000),
|
||||
size,
|
||||
MEM_DECOMMIT);
|
||||
VirtualFree(
|
||||
memory->views.vC0000000 + (p - memory->views.v00000000),
|
||||
memory_->views_.vC0000000 + (p - memory_->views_.v00000000),
|
||||
size,
|
||||
MEM_DECOMMIT);
|
||||
VirtualFree(
|
||||
memory->views.vE0000000 + (p - memory->views.v00000000),
|
||||
memory_->views_.vE0000000 + (p - memory_->views_.v00000000),
|
||||
size,
|
||||
MEM_DECOMMIT);
|
||||
}
|
||||
|
||||
return (uint32_t)real_size;
|
||||
alloy::tracing::WriteEvent(EventType::MemoryHeapFree({
|
||||
heap_id_, address,
|
||||
}));
|
||||
|
||||
return (uint64_t)real_size;
|
||||
}
|
||||
|
||||
uint32_t xe_memory_query_protect(xe_memory_ref memory, uint32_t address) {
|
||||
uint8_t* p = memory->mapping_base + address;
|
||||
MEMORY_BASIC_INFORMATION info;
|
||||
size_t info_size = VirtualQuery((void*)p, &info, sizeof(info));
|
||||
if (!info_size) {
|
||||
return 0;
|
||||
void XenonMemoryHeap::Dump() {
|
||||
XELOGI("XenonMemoryHeap::Dump - %s",
|
||||
is_physical_ ? "physical" : "virtual");
|
||||
if (FLAGS_heap_guard_pages) {
|
||||
XELOGI(" (heap guard pages enabled, stats will be wrong)");
|
||||
}
|
||||
return info.Protect;
|
||||
struct mallinfo info = mspace_mallinfo(space_);
|
||||
XELOGI(" arena: %lld", info.arena);
|
||||
XELOGI(" ordblks: %lld", info.ordblks);
|
||||
XELOGI(" hblks: %lld", info.hblks);
|
||||
XELOGI(" hblkhd: %lld", info.hblkhd);
|
||||
XELOGI(" usmblks: %lld", info.usmblks);
|
||||
XELOGI(" uordblks: %lld", info.uordblks);
|
||||
XELOGI(" fordblks: %lld", info.fordblks);
|
||||
XELOGI(" keepcost: %lld", info.keepcost);
|
||||
mspace_inspect_all(space_, DumpHandler, this);
|
||||
}
|
||||
|
||||
void XenonMemoryHeap::DumpHandler(
|
||||
void* start, void* end, size_t used_bytes, void* context) {
|
||||
/*xe_memory_heap_t* heap = (xe_memory_heap_t*)context;
|
||||
xe_memory_ref memory = heap->memory;
|
||||
size_t heap_guard_size = FLAGS_heap_guard_pages * 4096;
|
||||
uint64_t start_addr = (uint64_t)start + heap_guard_size;
|
||||
uint64_t end_addr = (uint64_t)end - heap_guard_size;
|
||||
uint32_t guest_start =
|
||||
(uint32_t)(start_addr - (uintptr_t)memory->mapping_base);
|
||||
uint32_t guest_end =
|
||||
(uint32_t)(end_addr - (uintptr_t)memory->mapping_base);
|
||||
if (used_bytes > 0) {
|
||||
XELOGI(" - %.8X-%.8X (%10db) %.16llX-%.16llX - %9db used",
|
||||
guest_start, guest_end, (guest_end - guest_start),
|
||||
start_addr, end_addr,
|
||||
used_bytes);
|
||||
} else {
|
||||
XELOGI(" - %.16llX-%.16llX - %9db used",
|
||||
start_addr, end_addr, used_bytes);
|
||||
}*/
|
||||
}
|
||||
69
src/xenia/cpu/xenon_memory.h
Normal file
69
src/xenia/cpu/xenon_memory.h
Normal file
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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_XENON_MEMORY_H_
|
||||
#define XENIA_CPU_XENON_MEMORY_H_
|
||||
|
||||
#include <alloy/memory.h>
|
||||
|
||||
#include <xenia/core.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
|
||||
class XenonMemoryHeap;
|
||||
|
||||
|
||||
class XenonMemory : public alloy::Memory {
|
||||
public:
|
||||
XenonMemory();
|
||||
virtual ~XenonMemory();
|
||||
|
||||
virtual int Initialize();
|
||||
|
||||
virtual uint64_t HeapAlloc(
|
||||
uint64_t base_address, size_t size, uint32_t flags,
|
||||
uint32_t alignment = 0x20);
|
||||
virtual int HeapFree(uint64_t address, size_t size);
|
||||
|
||||
virtual int Protect(uint64_t address, size_t size, uint32_t access);
|
||||
virtual uint32_t QueryProtect(uint64_t address);
|
||||
|
||||
private:
|
||||
int MapViews(uint8_t* mapping_base);
|
||||
void UnmapViews();
|
||||
|
||||
private:
|
||||
HANDLE mapping_;
|
||||
uint8_t* mapping_base_;
|
||||
union {
|
||||
struct {
|
||||
uint8_t* v00000000;
|
||||
uint8_t* v40000000;
|
||||
uint8_t* v80000000;
|
||||
uint8_t* vA0000000;
|
||||
uint8_t* vC0000000;
|
||||
uint8_t* vE0000000;
|
||||
};
|
||||
uint8_t* all_views[6];
|
||||
} views_;
|
||||
|
||||
XenonMemoryHeap* virtual_heap_;
|
||||
XenonMemoryHeap* physical_heap_;
|
||||
|
||||
friend class XenonMemoryHeap;
|
||||
};
|
||||
|
||||
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_XENON_MEMORY_H_
|
||||
48
src/xenia/cpu/xenon_runtime.cc
Normal file
48
src/xenia/cpu/xenon_runtime.cc
Normal file
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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/xenon_runtime.h>
|
||||
|
||||
#include <alloy/frontend/ppc/ppc_frontend.h>
|
||||
#include <alloy/runtime/tracing.h>
|
||||
|
||||
#include <xenia/cpu/xenon_thread_state.h>
|
||||
|
||||
using namespace alloy;
|
||||
using namespace alloy::frontend::ppc;
|
||||
using namespace alloy::runtime;
|
||||
using namespace xe;
|
||||
using namespace xe::cpu;
|
||||
|
||||
|
||||
XenonRuntime::XenonRuntime(
|
||||
alloy::Memory* memory, ExportResolver* export_resolver) :
|
||||
export_resolver_(export_resolver),
|
||||
Runtime(memory) {
|
||||
}
|
||||
|
||||
XenonRuntime::~XenonRuntime() {
|
||||
alloy::tracing::WriteEvent(EventType::Deinit({
|
||||
}));
|
||||
}
|
||||
|
||||
int XenonRuntime::Initialize(backend::Backend* backend) {
|
||||
PPCFrontend* frontend = new PPCFrontend(this);
|
||||
// TODO(benvanik): set options/etc.
|
||||
|
||||
int result = Runtime::Initialize(frontend, backend);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
alloy::tracing::WriteEvent(EventType::Init({
|
||||
}));
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -1,44 +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_SDB_SYMBOL_TABLE_H_
|
||||
#define XENIA_CPU_SDB_SYMBOL_TABLE_H_
|
||||
|
||||
#include <xenia/core.h>
|
||||
|
||||
#include <xenia/cpu/sdb/symbol.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace sdb {
|
||||
|
||||
|
||||
class SymbolTable {
|
||||
public:
|
||||
SymbolTable();
|
||||
~SymbolTable();
|
||||
|
||||
int AddFunction(uint32_t address, sdb::FunctionSymbol* symbol);
|
||||
sdb::FunctionSymbol* GetFunction(uint32_t address);
|
||||
|
||||
private:
|
||||
// TODO(benvanik): replace with a better data structure.
|
||||
xe_mutex_t* lock_;
|
||||
typedef std::tr1::unordered_map<uint32_t, sdb::FunctionSymbol*> FunctionMap;
|
||||
FunctionMap map_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace sdb
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_SDB_SYMBOL_TABLE_H_
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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_XENON_RUNTIME_H_
|
||||
#define XENIA_CPU_XENON_RUNTIME_H_
|
||||
|
||||
#include <alloy/runtime/runtime.h>
|
||||
|
||||
#include <xenia/core.h>
|
||||
#include <xenia/cpu/xenon_thread_state.h>
|
||||
|
||||
XEDECLARECLASS1(xe, ExportResolver);
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
|
||||
class XenonThreadState;
|
||||
|
||||
|
||||
class XenonRuntime : public alloy::runtime::Runtime {
|
||||
public:
|
||||
XenonRuntime(Memory* memory, ExportResolver* export_resolver);
|
||||
virtual ~XenonRuntime();
|
||||
|
||||
ExportResolver* export_resolver() const { return export_resolver_; }
|
||||
|
||||
virtual int Initialize(alloy::backend::Backend* backend = 0);
|
||||
|
||||
private:
|
||||
ExportResolver* export_resolver_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_XENON_RUNTIME_H_
|
||||
57
src/xenia/cpu/xenon_thread_state.cc
Normal file
57
src/xenia/cpu/xenon_thread_state.cc
Normal file
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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/xenon_thread_state.h>
|
||||
|
||||
#include <alloy/runtime/tracing.h>
|
||||
|
||||
#include <xenia/cpu/xenon_runtime.h>
|
||||
|
||||
using namespace alloy;
|
||||
using namespace alloy::frontend;
|
||||
using namespace alloy::frontend::ppc;
|
||||
using namespace alloy::runtime;
|
||||
using namespace xe::cpu;
|
||||
|
||||
|
||||
XenonThreadState::XenonThreadState(
|
||||
XenonRuntime* runtime, uint32_t thread_id,
|
||||
size_t stack_size, uint64_t thread_state_address) :
|
||||
stack_size_(stack_size), thread_state_address_(thread_state_address),
|
||||
ThreadState(runtime, thread_id) {
|
||||
stack_address_ = memory_->HeapAlloc(
|
||||
0, stack_size, MEMORY_FLAG_ZERO);
|
||||
|
||||
// Allocate with 64b alignment.
|
||||
context_ = (PPCContext*)xe_malloc_aligned(sizeof(PPCContext));
|
||||
XEASSERT(((uint64_t)context_ & 0xF) == 0);
|
||||
xe_zero_struct(context_, sizeof(PPCContext));
|
||||
|
||||
// Stash pointers to common structures that callbacks may need.
|
||||
context_->membase = memory_->membase();
|
||||
context_->runtime = runtime;
|
||||
context_->thread_state = this;
|
||||
|
||||
// Set initial registers.
|
||||
context_->r[1] = stack_address_ + stack_size;
|
||||
context_->r[13] = thread_state_address_;
|
||||
|
||||
raw_context_ = context_;
|
||||
|
||||
alloy::tracing::WriteEvent(EventType::ThreadInit({
|
||||
}));
|
||||
}
|
||||
|
||||
XenonThreadState::~XenonThreadState() {
|
||||
alloy::tracing::WriteEvent(EventType::ThreadDeinit({
|
||||
}));
|
||||
|
||||
xe_free_aligned(context_);
|
||||
memory_->HeapFree(stack_address_, stack_size_);
|
||||
}
|
||||
49
src/xenia/cpu/xenon_thread_state.h
Normal file
49
src/xenia/cpu/xenon_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_XENON_THREAD_STATE_H_
|
||||
#define XENIA_CPU_XENON_THREAD_STATE_H_
|
||||
|
||||
#include <alloy/frontend/ppc/ppc_context.h>
|
||||
#include <alloy/runtime/thread_state.h>
|
||||
#include <xenia/core.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
|
||||
class XenonRuntime;
|
||||
|
||||
|
||||
class XenonThreadState : public alloy::runtime::ThreadState {
|
||||
public:
|
||||
XenonThreadState(XenonRuntime* runtime, uint32_t thread_id,
|
||||
size_t stack_size, uint64_t thread_state_address);
|
||||
virtual ~XenonThreadState();
|
||||
|
||||
alloy::frontend::ppc::PPCContext* context() const { return context_; }
|
||||
|
||||
private:
|
||||
size_t stack_size_;
|
||||
uint64_t thread_state_address;
|
||||
|
||||
uint32_t thread_id_;
|
||||
uint64_t stack_address_;
|
||||
uint64_t thread_state_address_;
|
||||
|
||||
// NOTE: must be 64b aligned for SSE ops.
|
||||
alloy::frontend::ppc::PPCContext* context_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_XENON_THREAD_STATE_H_
|
||||
488
src/xenia/cpu/xex_module.cc
Normal file
488
src/xenia/cpu/xex_module.cc
Normal file
@@ -0,0 +1,488 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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/xex_module.h>
|
||||
|
||||
#include <alloy/runtime/tracing.h>
|
||||
|
||||
#include <xenia/cpu/xenon_runtime.h>
|
||||
#include <xenia/export_resolver.h>
|
||||
|
||||
using namespace alloy;
|
||||
using namespace alloy::runtime;
|
||||
using namespace xe::cpu;
|
||||
|
||||
|
||||
XexModule::XexModule(
|
||||
XenonRuntime* runtime) :
|
||||
runtime_(runtime),
|
||||
name_(0), path_(0), xex_(0),
|
||||
base_address_(0), low_address_(0), high_address_(0),
|
||||
Module(runtime->memory()) {
|
||||
}
|
||||
|
||||
XexModule::~XexModule() {
|
||||
xe_xex2_release(xex_);
|
||||
xe_free(name_);
|
||||
xe_free(path_);
|
||||
}
|
||||
|
||||
int XexModule::Load(const char* name, const char* path, xe_xex2_ref xex) {
|
||||
int result;
|
||||
|
||||
xex_ = xe_xex2_retain(xex);
|
||||
const xe_xex2_header_t* header = xe_xex2_get_header(xex);
|
||||
|
||||
// Scan and find the low/high addresses.
|
||||
// All code sections are continuous, so this should be easy.
|
||||
low_address_ = UINT_MAX;
|
||||
high_address_ = 0;
|
||||
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) {
|
||||
low_address_ = (uint32_t)MIN(low_address_, start_address);
|
||||
high_address_ = (uint32_t)MAX(high_address_, end_address);
|
||||
}
|
||||
i += section->info.page_count;
|
||||
}
|
||||
|
||||
// Add all imports (variables/functions).
|
||||
for (size_t n = 0; n < header->import_library_count; n++) {
|
||||
result = SetupLibraryImports(&header->import_libraries[n]);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// Find __savegprlr_* and __restgprlr_* and the others.
|
||||
// We can flag these for special handling (inlining/etc).
|
||||
result = FindSaveRest();
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Setup debug info.
|
||||
name_ = xestrdupa(name);
|
||||
path_ = xestrdupa(path);
|
||||
// TODO(benvanik): debug info
|
||||
// TODO(benvanik): load map file.
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int XexModule::SetupLibraryImports(const xe_xex2_import_library_t* library) {
|
||||
ExportResolver* export_resolver = runtime_->export_resolver();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
uint8_t* membase = memory_->membase();
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
VariableInfo* var_info;
|
||||
DeclareVariable(info->value_address, &var_info);
|
||||
//var->set_name(name);
|
||||
var_info->set_status(SymbolInfo::STATUS_DECLARED);
|
||||
DefineVariable(var_info);
|
||||
//var->kernel_export = kernel_export;
|
||||
var_info->set_status(SymbolInfo::STATUS_DEFINED);
|
||||
|
||||
// Grab, if available.
|
||||
uint32_t* slot = (uint32_t*)(membase + info->value_address);
|
||||
if (kernel_export->type == KernelExport::Function) {
|
||||
// Not exactly sure what this should be...
|
||||
// TODO(benvanik): find out what import variables are.
|
||||
XELOGW("kernel import variable not defined");
|
||||
} 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(0xD000BEEF | (kernel_export->ordinal & 0xFFF) << 16);
|
||||
XELOGCPU("WARNING: imported a variable with no value: %s",
|
||||
kernel_export->name);
|
||||
}
|
||||
}
|
||||
|
||||
if (info->thunk_address) {
|
||||
if (kernel_export) {
|
||||
xesnprintfa(name, XECOUNT(name), "%s", kernel_export->name);
|
||||
} else {
|
||||
xesnprintfa(name, XECOUNT(name), "__kernel_%s_%.3X", library->name,
|
||||
info->ordinal);
|
||||
}
|
||||
|
||||
FunctionInfo* fn_info;
|
||||
DeclareFunction(info->thunk_address, &fn_info);
|
||||
fn_info->set_end_address(info->thunk_address + 16 - 4);
|
||||
//fn->type = FunctionSymbol::Kernel;
|
||||
//fn->kernel_export = kernel_export;
|
||||
//fn->set_name(name);
|
||||
fn_info->set_status(SymbolInfo::STATUS_DECLARED);
|
||||
|
||||
DefineFunction(fn_info);
|
||||
Function* fn = new ExternFunction(
|
||||
info->thunk_address,
|
||||
(ExternFunction::Handler)kernel_export->function_data.shim,
|
||||
kernel_export->function_data.shim_data,
|
||||
NULL);
|
||||
fn_info->set_function(fn);
|
||||
fn_info->set_status(SymbolInfo::STATUS_DEFINED);
|
||||
}
|
||||
}
|
||||
|
||||
xe_free(import_infos);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool XexModule::ContainsAddress(uint64_t address) {
|
||||
return address >= low_address_ && address < high_address_;
|
||||
}
|
||||
|
||||
int XexModule::FindSaveRest() {
|
||||
// Special stack save/restore functions.
|
||||
// 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.
|
||||
// __savegprlr_14 to __savegprlr_31
|
||||
// __restgprlr_14 to __restgprlr_31
|
||||
static const uint32_t gprlr_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,
|
||||
};
|
||||
// __savefpr_14 to __savefpr_31
|
||||
// __restfpr_14 to __restfpr_31
|
||||
static const uint32_t fpr_code_values[] = {
|
||||
0x70FFCCD9, // __savefpr_14
|
||||
0x78FFECD9, // __savefpr_15
|
||||
0x80FF0CDA, // __savefpr_16
|
||||
0x88FF2CDA, // __savefpr_17
|
||||
0x90FF4CDA, // __savefpr_18
|
||||
0x98FF6CDA, // __savefpr_19
|
||||
0xA0FF8CDA, // __savefpr_20
|
||||
0xA8FFACDA, // __savefpr_21
|
||||
0xB0FFCCDA, // __savefpr_22
|
||||
0xB8FFECDA, // __savefpr_23
|
||||
0xC0FF0CDB, // __savefpr_24
|
||||
0xC8FF2CDB, // __savefpr_25
|
||||
0xD0FF4CDB, // __savefpr_26
|
||||
0xD8FF6CDB, // __savefpr_27
|
||||
0xE0FF8CDB, // __savefpr_28
|
||||
0xE8FFACDB, // __savefpr_29
|
||||
0xF0FFCCDB, // __savefpr_30
|
||||
0xF8FFECDB, // __savefpr_31
|
||||
0x2000804E,
|
||||
0x70FFCCC9, // __restfpr_14
|
||||
0x78FFECC9, // __restfpr_15
|
||||
0x80FF0CCA, // __restfpr_16
|
||||
0x88FF2CCA, // __restfpr_17
|
||||
0x90FF4CCA, // __restfpr_18
|
||||
0x98FF6CCA, // __restfpr_19
|
||||
0xA0FF8CCA, // __restfpr_20
|
||||
0xA8FFACCA, // __restfpr_21
|
||||
0xB0FFCCCA, // __restfpr_22
|
||||
0xB8FFECCA, // __restfpr_23
|
||||
0xC0FF0CCB, // __restfpr_24
|
||||
0xC8FF2CCB, // __restfpr_25
|
||||
0xD0FF4CCB, // __restfpr_26
|
||||
0xD8FF6CCB, // __restfpr_27
|
||||
0xE0FF8CCB, // __restfpr_28
|
||||
0xE8FFACCB, // __restfpr_29
|
||||
0xF0FFCCCB, // __restfpr_30
|
||||
0xF8FFECCB, // __restfpr_31
|
||||
0x2000804E,
|
||||
};
|
||||
// __savevmx_14 to __savevmx_31
|
||||
// __savevmx_64 to __savevmx_127
|
||||
// __restvmx_14 to __restvmx_31
|
||||
// __restvmx_64 to __restvmx_127
|
||||
static const uint32_t vmx_code_values[] = {
|
||||
0xE0FE6039, // __savevmx_14
|
||||
0xCE61CB7D, 0xF0FE6039, 0xCE61EB7D, 0x00FF6039, 0xCE610B7E, 0x10FF6039,
|
||||
0xCE612B7E, 0x20FF6039, 0xCE614B7E, 0x30FF6039, 0xCE616B7E, 0x40FF6039,
|
||||
0xCE618B7E, 0x50FF6039, 0xCE61AB7E, 0x60FF6039, 0xCE61CB7E, 0x70FF6039,
|
||||
0xCE61EB7E, 0x80FF6039, 0xCE610B7F, 0x90FF6039, 0xCE612B7F, 0xA0FF6039,
|
||||
0xCE614B7F, 0xB0FF6039, 0xCE616B7F, 0xC0FF6039, 0xCE618B7F, 0xD0FF6039,
|
||||
0xCE61AB7F, 0xE0FF6039, 0xCE61CB7F,
|
||||
0xF0FF6039, // __savevmx_31
|
||||
0xCE61EB7F,
|
||||
0x2000804E,
|
||||
|
||||
0x00FC6039, // __savevmx_64
|
||||
0xCB610B10, 0x10FC6039, 0xCB612B10, 0x20FC6039, 0xCB614B10, 0x30FC6039,
|
||||
0xCB616B10, 0x40FC6039, 0xCB618B10, 0x50FC6039, 0xCB61AB10, 0x60FC6039,
|
||||
0xCB61CB10, 0x70FC6039, 0xCB61EB10, 0x80FC6039, 0xCB610B11, 0x90FC6039,
|
||||
0xCB612B11, 0xA0FC6039, 0xCB614B11, 0xB0FC6039, 0xCB616B11, 0xC0FC6039,
|
||||
0xCB618B11, 0xD0FC6039, 0xCB61AB11, 0xE0FC6039, 0xCB61CB11, 0xF0FC6039,
|
||||
0xCB61EB11, 0x00FD6039, 0xCB610B12, 0x10FD6039, 0xCB612B12, 0x20FD6039,
|
||||
0xCB614B12, 0x30FD6039, 0xCB616B12, 0x40FD6039, 0xCB618B12, 0x50FD6039,
|
||||
0xCB61AB12, 0x60FD6039, 0xCB61CB12, 0x70FD6039, 0xCB61EB12, 0x80FD6039,
|
||||
0xCB610B13, 0x90FD6039, 0xCB612B13, 0xA0FD6039, 0xCB614B13, 0xB0FD6039,
|
||||
0xCB616B13, 0xC0FD6039, 0xCB618B13, 0xD0FD6039, 0xCB61AB13, 0xE0FD6039,
|
||||
0xCB61CB13, 0xF0FD6039, 0xCB61EB13, 0x00FE6039, 0xCF610B10, 0x10FE6039,
|
||||
0xCF612B10, 0x20FE6039, 0xCF614B10, 0x30FE6039, 0xCF616B10, 0x40FE6039,
|
||||
0xCF618B10, 0x50FE6039, 0xCF61AB10, 0x60FE6039, 0xCF61CB10, 0x70FE6039,
|
||||
0xCF61EB10, 0x80FE6039, 0xCF610B11, 0x90FE6039, 0xCF612B11, 0xA0FE6039,
|
||||
0xCF614B11, 0xB0FE6039, 0xCF616B11, 0xC0FE6039, 0xCF618B11, 0xD0FE6039,
|
||||
0xCF61AB11, 0xE0FE6039, 0xCF61CB11, 0xF0FE6039, 0xCF61EB11, 0x00FF6039,
|
||||
0xCF610B12, 0x10FF6039, 0xCF612B12, 0x20FF6039, 0xCF614B12, 0x30FF6039,
|
||||
0xCF616B12, 0x40FF6039, 0xCF618B12, 0x50FF6039, 0xCF61AB12, 0x60FF6039,
|
||||
0xCF61CB12, 0x70FF6039, 0xCF61EB12, 0x80FF6039, 0xCF610B13, 0x90FF6039,
|
||||
0xCF612B13, 0xA0FF6039, 0xCF614B13, 0xB0FF6039, 0xCF616B13, 0xC0FF6039,
|
||||
0xCF618B13, 0xD0FF6039, 0xCF61AB13, 0xE0FF6039, 0xCF61CB13,
|
||||
0xF0FF6039, // __savevmx_127
|
||||
0xCF61EB13,
|
||||
0x2000804E,
|
||||
|
||||
0xE0FE6039, // __restvmx_14
|
||||
0xCE60CB7D, 0xF0FE6039, 0xCE60EB7D, 0x00FF6039, 0xCE600B7E, 0x10FF6039,
|
||||
0xCE602B7E, 0x20FF6039, 0xCE604B7E, 0x30FF6039, 0xCE606B7E, 0x40FF6039,
|
||||
0xCE608B7E, 0x50FF6039, 0xCE60AB7E, 0x60FF6039, 0xCE60CB7E, 0x70FF6039,
|
||||
0xCE60EB7E, 0x80FF6039, 0xCE600B7F, 0x90FF6039, 0xCE602B7F, 0xA0FF6039,
|
||||
0xCE604B7F, 0xB0FF6039, 0xCE606B7F, 0xC0FF6039, 0xCE608B7F, 0xD0FF6039,
|
||||
0xCE60AB7F, 0xE0FF6039, 0xCE60CB7F, 0xF0FF6039, // __restvmx_31
|
||||
0xCE60EB7F,
|
||||
0x2000804E,
|
||||
|
||||
0x00FC6039, // __restvmx_64
|
||||
0xCB600B10, 0x10FC6039, 0xCB602B10, 0x20FC6039, 0xCB604B10, 0x30FC6039,
|
||||
0xCB606B10, 0x40FC6039, 0xCB608B10, 0x50FC6039, 0xCB60AB10, 0x60FC6039,
|
||||
0xCB60CB10, 0x70FC6039, 0xCB60EB10, 0x80FC6039, 0xCB600B11, 0x90FC6039,
|
||||
0xCB602B11, 0xA0FC6039, 0xCB604B11, 0xB0FC6039, 0xCB606B11, 0xC0FC6039,
|
||||
0xCB608B11, 0xD0FC6039, 0xCB60AB11, 0xE0FC6039, 0xCB60CB11, 0xF0FC6039,
|
||||
0xCB60EB11, 0x00FD6039, 0xCB600B12, 0x10FD6039, 0xCB602B12, 0x20FD6039,
|
||||
0xCB604B12, 0x30FD6039, 0xCB606B12, 0x40FD6039, 0xCB608B12, 0x50FD6039,
|
||||
0xCB60AB12, 0x60FD6039, 0xCB60CB12, 0x70FD6039, 0xCB60EB12, 0x80FD6039,
|
||||
0xCB600B13, 0x90FD6039, 0xCB602B13, 0xA0FD6039, 0xCB604B13, 0xB0FD6039,
|
||||
0xCB606B13, 0xC0FD6039, 0xCB608B13, 0xD0FD6039, 0xCB60AB13, 0xE0FD6039,
|
||||
0xCB60CB13, 0xF0FD6039, 0xCB60EB13, 0x00FE6039, 0xCF600B10, 0x10FE6039,
|
||||
0xCF602B10, 0x20FE6039, 0xCF604B10, 0x30FE6039, 0xCF606B10, 0x40FE6039,
|
||||
0xCF608B10, 0x50FE6039, 0xCF60AB10, 0x60FE6039, 0xCF60CB10, 0x70FE6039,
|
||||
0xCF60EB10, 0x80FE6039, 0xCF600B11, 0x90FE6039, 0xCF602B11, 0xA0FE6039,
|
||||
0xCF604B11, 0xB0FE6039, 0xCF606B11, 0xC0FE6039, 0xCF608B11, 0xD0FE6039,
|
||||
0xCF60AB11, 0xE0FE6039, 0xCF60CB11, 0xF0FE6039, 0xCF60EB11, 0x00FF6039,
|
||||
0xCF600B12, 0x10FF6039, 0xCF602B12, 0x20FF6039, 0xCF604B12, 0x30FF6039,
|
||||
0xCF606B12, 0x40FF6039, 0xCF608B12, 0x50FF6039, 0xCF60AB12, 0x60FF6039,
|
||||
0xCF60CB12, 0x70FF6039, 0xCF60EB12, 0x80FF6039, 0xCF600B13, 0x90FF6039,
|
||||
0xCF602B13, 0xA0FF6039, 0xCF604B13, 0xB0FF6039, 0xCF606B13, 0xC0FF6039,
|
||||
0xCF608B13, 0xD0FF6039, 0xCF60AB13, 0xE0FF6039, 0xCF60CB13,
|
||||
0xF0FF6039, // __restvmx_127
|
||||
0xCF60EB13,
|
||||
0x2000804E,
|
||||
};
|
||||
|
||||
// TODO(benvanik): these are almost always sequential, if present.
|
||||
// It'd be smarter to search around the other ones to prevent
|
||||
// 3 full module scans.
|
||||
uint64_t gplr_start = 0;
|
||||
uint64_t fpr_start = 0;
|
||||
uint64_t vmx_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) {
|
||||
if (!gplr_start) {
|
||||
gplr_start = memory_->SearchAligned(
|
||||
start_address, end_address,
|
||||
gprlr_code_values, XECOUNT(gprlr_code_values));
|
||||
}
|
||||
if (!fpr_start) {
|
||||
fpr_start = memory_->SearchAligned(
|
||||
start_address, end_address,
|
||||
fpr_code_values, XECOUNT(fpr_code_values));
|
||||
}
|
||||
if (!vmx_start) {
|
||||
vmx_start = memory_->SearchAligned(
|
||||
start_address, end_address,
|
||||
vmx_code_values, XECOUNT(vmx_code_values));
|
||||
}
|
||||
if (gplr_start && fpr_start && vmx_start) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
i += section->info.page_count;
|
||||
}
|
||||
|
||||
// Add function stubs.
|
||||
char name[32];
|
||||
if (gplr_start) {
|
||||
uint64_t address = gplr_start;
|
||||
for (int n = 14; n <= 31; n++) {
|
||||
xesnprintfa(name, XECOUNT(name), "__savegprlr_%d", n);
|
||||
FunctionInfo* symbol_info;
|
||||
DeclareFunction(address, &symbol_info);
|
||||
symbol_info->set_end_address(address + (31 - n) * 4 + 2 * 4);
|
||||
// TODO(benvanik): set name
|
||||
// TODO(benvanik): set type fn->type = FunctionSymbol::User;
|
||||
// TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagSaveGprLr;
|
||||
symbol_info->set_status(SymbolInfo::STATUS_DECLARED);
|
||||
address += 4;
|
||||
}
|
||||
address = gplr_start + 20 * 4;
|
||||
for (int n = 14; n <= 31; n++) {
|
||||
xesnprintfa(name, XECOUNT(name), "__restgprlr_%d", n);
|
||||
FunctionInfo* symbol_info;
|
||||
DeclareFunction(address, &symbol_info);
|
||||
symbol_info->set_end_address(address + (31 - n) * 4 + 3 * 4);
|
||||
// TODO(benvanik): set name
|
||||
// TODO(benvanik): set type fn->type = FunctionSymbol::User;
|
||||
// TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagRestGprLr;
|
||||
symbol_info->set_status(SymbolInfo::STATUS_DECLARED);
|
||||
address += 4;
|
||||
}
|
||||
}
|
||||
if (fpr_start) {
|
||||
uint64_t address = fpr_start;
|
||||
for (int n = 14; n <= 31; n++) {
|
||||
xesnprintfa(name, XECOUNT(name), "__savefpr_%d", n);
|
||||
FunctionInfo* symbol_info;
|
||||
DeclareFunction(address, &symbol_info);
|
||||
symbol_info->set_end_address(address + (31 - n) * 4 + 1 * 4);
|
||||
// TODO(benvanik): set name
|
||||
// TODO(benvanik): set type fn->type = FunctionSymbol::User;
|
||||
// TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagSaveFpr;
|
||||
symbol_info->set_status(SymbolInfo::STATUS_DECLARED);
|
||||
address += 4;
|
||||
}
|
||||
address = fpr_start + (18 * 4) + (1 * 4);
|
||||
for (int n = 14; n <= 31; n++) {
|
||||
xesnprintfa(name, XECOUNT(name), "__restfpr_%d", n);
|
||||
FunctionInfo* symbol_info;
|
||||
DeclareFunction(address, &symbol_info);
|
||||
symbol_info->set_end_address(address + (31 - n) * 4 + 1 * 4);
|
||||
// TODO(benvanik): set name
|
||||
// TODO(benvanik): set type fn->type = FunctionSymbol::User;
|
||||
// TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagRestFpr;
|
||||
symbol_info->set_status(SymbolInfo::STATUS_DECLARED);
|
||||
address += 4;
|
||||
}
|
||||
}
|
||||
if (vmx_start) {
|
||||
// vmx is:
|
||||
// 14-31 save
|
||||
// 64-127 save
|
||||
// 14-31 rest
|
||||
// 64-127 rest
|
||||
uint64_t address = vmx_start;
|
||||
for (int n = 14; n <= 31; n++) {
|
||||
xesnprintfa(name, XECOUNT(name), "__savevmx_%d", n);
|
||||
FunctionInfo* symbol_info;
|
||||
DeclareFunction(address, &symbol_info);
|
||||
// TODO(benvanik): set name
|
||||
// TODO(benvanik): set type fn->type = FunctionSymbol::User;
|
||||
// TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagSaveVmx;
|
||||
symbol_info->set_status(SymbolInfo::STATUS_DECLARED);
|
||||
address += 2 * 4;
|
||||
}
|
||||
address += 4;
|
||||
for (int n = 64; n <= 127; n++) {
|
||||
xesnprintfa(name, XECOUNT(name), "__savevmx_%d", n);
|
||||
FunctionInfo* symbol_info;
|
||||
DeclareFunction(address, &symbol_info);
|
||||
// TODO(benvanik): set name
|
||||
// TODO(benvanik): set type fn->type = FunctionSymbol::User;
|
||||
// TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagSaveVmx;
|
||||
symbol_info->set_status(SymbolInfo::STATUS_DECLARED);
|
||||
address += 2 * 4;
|
||||
}
|
||||
address = vmx_start + (18 * 2 * 4) + (1 * 4) + (64 * 2 * 4) + (1 * 4);
|
||||
for (int n = 14; n <= 31; n++) {
|
||||
xesnprintfa(name, XECOUNT(name), "__restvmx_%d", n);
|
||||
FunctionInfo* symbol_info;
|
||||
DeclareFunction(address, &symbol_info);
|
||||
// TODO(benvanik): set name
|
||||
// TODO(benvanik): set type fn->type = FunctionSymbol::User;
|
||||
// TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagRestVmx;
|
||||
symbol_info->set_status(SymbolInfo::STATUS_DECLARED);
|
||||
address += 2 * 4;
|
||||
}
|
||||
address += 4;
|
||||
for (int n = 64; n <= 127; n++) {
|
||||
xesnprintfa(name, XECOUNT(name), "__restvmx_%d", n);
|
||||
FunctionInfo* symbol_info;
|
||||
DeclareFunction(address, &symbol_info);
|
||||
// TODO(benvanik): set name
|
||||
// TODO(benvanik): set type fn->type = FunctionSymbol::User;
|
||||
// TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagRestVmx;
|
||||
symbol_info->set_status(SymbolInfo::STATUS_DECLARED);
|
||||
address += 2 * 4;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
56
src/xenia/cpu/xex_module.h
Normal file
56
src/xenia/cpu/xex_module.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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_XEX_MODULE_H_
|
||||
#define XENIA_CPU_XEX_MODULE_H_
|
||||
|
||||
#include <alloy/runtime/module.h>
|
||||
#include <xenia/core.h>
|
||||
#include <xenia/kernel/xex2.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
|
||||
class XenonRuntime;
|
||||
|
||||
|
||||
class XexModule : public alloy::runtime::Module {
|
||||
public:
|
||||
XexModule(XenonRuntime* runtime);
|
||||
virtual ~XexModule();
|
||||
|
||||
int Load(const char* name, const char* path, xe_xex2_ref xex);
|
||||
|
||||
virtual const char* name() const { return name_; }
|
||||
|
||||
virtual bool ContainsAddress(uint64_t address);
|
||||
|
||||
private:
|
||||
int SetupImports(xe_xex2_ref xex);
|
||||
int SetupLibraryImports(const xe_xex2_import_library_t* library);
|
||||
int FindSaveRest();
|
||||
|
||||
private:
|
||||
XenonRuntime* runtime_;
|
||||
char* name_;
|
||||
char* path_;
|
||||
xe_xex2_ref xex_;
|
||||
|
||||
uint64_t base_address_;
|
||||
uint64_t low_address_;
|
||||
uint64_t high_address_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_XEX_MODULE_H_
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include <xenia/apu/apu.h>
|
||||
#include <xenia/cpu/cpu.h>
|
||||
#include <xenia/cpu/xenon_memory.h>
|
||||
#include <xenia/dbg/debugger.h>
|
||||
#include <xenia/gpu/gpu.h>
|
||||
#include <xenia/hid/hid.h>
|
||||
@@ -34,7 +35,7 @@ using namespace xe::kernel::xboxkrnl::fs;
|
||||
Emulator::Emulator(const xechar_t* command_line) :
|
||||
memory_(0),
|
||||
debugger_(0),
|
||||
cpu_backend_(0), processor_(0),
|
||||
processor_(0),
|
||||
audio_system_(0), graphics_system_(0), input_system_(0),
|
||||
export_resolver_(0), file_system_(0),
|
||||
xboxkrnl_(0), xam_(0) {
|
||||
@@ -53,23 +54,20 @@ Emulator::~Emulator() {
|
||||
delete graphics_system_;
|
||||
delete audio_system_;
|
||||
delete processor_;
|
||||
delete cpu_backend_;
|
||||
|
||||
delete debugger_;
|
||||
|
||||
delete export_resolver_;
|
||||
|
||||
xe_memory_release(memory_);
|
||||
}
|
||||
|
||||
X_STATUS Emulator::Setup() {
|
||||
X_STATUS result = X_STATUS_UNSUCCESSFUL;
|
||||
|
||||
// Create memory system first, as it is required for other systems.
|
||||
xe_memory_options_t memory_options;
|
||||
xe_zero_struct(&memory_options, sizeof(memory_options));
|
||||
memory_ = xe_memory_create(memory_options);
|
||||
memory_ = new XenonMemory();
|
||||
XEEXPECTNOTNULL(memory_);
|
||||
result = memory_->Initialize();
|
||||
XEEXPECTZERO(result);
|
||||
|
||||
// Shared export resolver used to attach and query for HLE exports.
|
||||
export_resolver_ = new ExportResolver();
|
||||
@@ -80,9 +78,7 @@ X_STATUS Emulator::Setup() {
|
||||
XEEXPECTNOTNULL(debugger_);
|
||||
|
||||
// Initialize the CPU.
|
||||
cpu_backend_ = new xe::cpu::x64::X64Backend();
|
||||
XEEXPECTNOTNULL(cpu_backend_);
|
||||
processor_ = new Processor(this, cpu_backend_);
|
||||
processor_ = new Processor(this);
|
||||
XEEXPECTNOTNULL(processor_);
|
||||
|
||||
// Initialize the APU.
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
|
||||
XEDECLARECLASS1(xe, ExportResolver);
|
||||
XEDECLARECLASS2(xe, apu, AudioSystem);
|
||||
XEDECLARECLASS2(xe, cpu, Backend);
|
||||
XEDECLARECLASS2(xe, cpu, Processor);
|
||||
XEDECLARECLASS2(xe, dbg, Debugger);
|
||||
XEDECLARECLASS2(xe, gpu, GraphicsSystem);
|
||||
@@ -36,7 +35,7 @@ public:
|
||||
~Emulator();
|
||||
|
||||
const xechar_t* command_line() const { return command_line_; }
|
||||
xe_memory_ref memory() const { return memory_; }
|
||||
Memory* memory() const { return memory_; }
|
||||
|
||||
dbg::Debugger* debugger() const { return debugger_; }
|
||||
|
||||
@@ -55,12 +54,11 @@ public:
|
||||
X_STATUS LaunchDiscImage(const xechar_t* path);
|
||||
|
||||
private:
|
||||
xechar_t command_line_[XE_MAX_PATH];
|
||||
xe_memory_ref memory_;
|
||||
xechar_t command_line_[XE_MAX_PATH];
|
||||
Memory* memory_;
|
||||
|
||||
dbg::Debugger* debugger_;
|
||||
|
||||
cpu::Backend* cpu_backend_;
|
||||
cpu::Processor* processor_;
|
||||
apu::AudioSystem* audio_system_;
|
||||
gpu::GraphicsSystem* graphics_system_;
|
||||
|
||||
@@ -21,7 +21,7 @@ using namespace xe::gpu::xenos;
|
||||
|
||||
|
||||
D3D11GraphicsDriver::D3D11GraphicsDriver(
|
||||
xe_memory_ref memory, ID3D11Device* device) :
|
||||
Memory* memory, ID3D11Device* device) :
|
||||
GraphicsDriver(memory) {
|
||||
device_ = device;
|
||||
device_->AddRef();
|
||||
@@ -78,7 +78,7 @@ void D3D11GraphicsDriver::SetShader(
|
||||
uint32_t start,
|
||||
uint32_t length) {
|
||||
// Find or create shader in the cache.
|
||||
uint8_t* p = xe_memory_addr(memory_, address);
|
||||
uint8_t* p = memory_->Translate(address);
|
||||
Shader* shader = shader_cache_->FindOrCreate(
|
||||
type, p, length);
|
||||
|
||||
@@ -420,7 +420,7 @@ int D3D11GraphicsDriver::PrepareVertexFetcher(
|
||||
XESAFERELEASE(buffer);
|
||||
return 1;
|
||||
}
|
||||
uint32_t* src = (uint32_t*)xe_memory_addr(memory_, address);
|
||||
uint32_t* src = (uint32_t*)memory_->Translate(address);
|
||||
uint32_t* dest = (uint32_t*)res.pData;
|
||||
for (uint32_t n = 0; n < size_dwords; n++) {
|
||||
// union {
|
||||
@@ -481,7 +481,7 @@ int D3D11GraphicsDriver::PrepareIndexBuffer(
|
||||
D3D11_MAPPED_SUBRESOURCE res;
|
||||
context_->Map(buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &res);
|
||||
if (index_32bit) {
|
||||
uint32_t* src = (uint32_t*)xe_memory_addr(memory_, address);
|
||||
uint32_t* src = (uint32_t*)memory_->Translate(address);
|
||||
uint32_t* dest = (uint32_t*)res.pData;
|
||||
for (uint32_t n = 0; n < index_count; n++) {
|
||||
uint32_t d = { XESWAP32(src[n]) };
|
||||
@@ -489,7 +489,7 @@ int D3D11GraphicsDriver::PrepareIndexBuffer(
|
||||
dest[n] = d;
|
||||
}
|
||||
} else {
|
||||
uint16_t* src = (uint16_t*)xe_memory_addr(memory_, address);
|
||||
uint16_t* src = (uint16_t*)memory_->Translate(address);
|
||||
uint16_t* dest = (uint16_t*)res.pData;
|
||||
for (uint32_t n = 0; n < index_count; n++) {
|
||||
uint16_t d = XESWAP16(src[n]);
|
||||
|
||||
@@ -30,7 +30,7 @@ class D3D11VertexShader;
|
||||
|
||||
class D3D11GraphicsDriver : public GraphicsDriver {
|
||||
public:
|
||||
D3D11GraphicsDriver(xe_memory_ref memory, ID3D11Device* device);
|
||||
D3D11GraphicsDriver(Memory* memory, ID3D11Device* device);
|
||||
virtual ~D3D11GraphicsDriver();
|
||||
|
||||
virtual void Initialize();
|
||||
|
||||
@@ -14,13 +14,10 @@ using namespace xe;
|
||||
using namespace xe::gpu;
|
||||
|
||||
|
||||
GraphicsDriver::GraphicsDriver(xe_memory_ref memory) :
|
||||
address_translation_(0) {
|
||||
memory_ = xe_memory_retain(memory);
|
||||
|
||||
GraphicsDriver::GraphicsDriver(Memory* memory) :
|
||||
memory_(memory), address_translation_(0) {
|
||||
memset(®ister_file_, 0, sizeof(register_file_));
|
||||
}
|
||||
|
||||
GraphicsDriver::~GraphicsDriver() {
|
||||
xe_memory_release(memory_);
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ class GraphicsDriver {
|
||||
public:
|
||||
virtual ~GraphicsDriver();
|
||||
|
||||
xe_memory_ref memory() const { return memory_; }
|
||||
Memory* memory() const { return memory_; }
|
||||
xenos::RegisterFile* register_file() { return ®ister_file_; };
|
||||
void set_address_translation(uint32_t value) {
|
||||
address_translation_ = value;
|
||||
@@ -48,9 +48,9 @@ public:
|
||||
uint32_t index_count) = 0;
|
||||
|
||||
protected:
|
||||
GraphicsDriver(xe_memory_ref memory);
|
||||
GraphicsDriver(Memory* memory);
|
||||
|
||||
xe_memory_ref memory_;
|
||||
Memory* memory_;
|
||||
|
||||
xenos::RegisterFile register_file_;
|
||||
uint32_t address_translation_;
|
||||
|
||||
@@ -17,18 +17,16 @@
|
||||
|
||||
|
||||
using namespace xe;
|
||||
using namespace xe::cpu::ppc;
|
||||
using namespace xe::cpu;
|
||||
using namespace xe::gpu;
|
||||
using namespace xe::gpu::xenos;
|
||||
|
||||
|
||||
GraphicsSystem::GraphicsSystem(Emulator* emulator) :
|
||||
emulator_(emulator),
|
||||
emulator_(emulator), memory_(emulator->memory()),
|
||||
thread_(0), running_(false), driver_(0), worker_(0),
|
||||
interrupt_callback_(0), interrupt_callback_data_(0),
|
||||
last_interrupt_time_(0), swap_pending_(false) {
|
||||
memory_ = xe_memory_retain(emulator->memory());
|
||||
|
||||
// Create the run loop used for any windows/etc.
|
||||
// This must be done on the thread we create the driver.
|
||||
run_loop_ = xe_run_loop_create();
|
||||
@@ -43,7 +41,6 @@ GraphicsSystem::~GraphicsSystem() {
|
||||
delete worker_;
|
||||
|
||||
xe_run_loop_release(run_loop_);
|
||||
xe_memory_release(memory_);
|
||||
}
|
||||
|
||||
X_STATUS GraphicsSystem::Setup() {
|
||||
@@ -132,11 +129,11 @@ void GraphicsSystem::EnableReadPointerWriteBack(uint32_t ptr,
|
||||
worker_->EnableReadPointerWriteBack(ptr, block_size);
|
||||
}
|
||||
|
||||
bool GraphicsSystem::HandlesRegister(uint32_t addr) {
|
||||
bool GraphicsSystem::HandlesRegister(uint64_t addr) {
|
||||
return (addr & 0xFFFF0000) == 0x7FC80000;
|
||||
}
|
||||
|
||||
uint64_t GraphicsSystem::ReadRegister(uint32_t addr) {
|
||||
uint64_t GraphicsSystem::ReadRegister(uint64_t addr) {
|
||||
uint32_t r = addr & 0xFFFF;
|
||||
XELOGGPU("ReadRegister(%.4X)", r);
|
||||
|
||||
@@ -155,7 +152,7 @@ uint64_t GraphicsSystem::ReadRegister(uint32_t addr) {
|
||||
return regs->values[r].u32;
|
||||
}
|
||||
|
||||
void GraphicsSystem::WriteRegister(uint32_t addr, uint64_t value) {
|
||||
void GraphicsSystem::WriteRegister(uint64_t addr, uint64_t value) {
|
||||
uint32_t r = addr & 0xFFFF;
|
||||
XELOGGPU("WriteRegister(%.4X, %.8X)", r, value);
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ public:
|
||||
virtual ~GraphicsSystem();
|
||||
|
||||
Emulator* emulator() const { return emulator_; }
|
||||
xe_memory_ref memory() const { return memory_; }
|
||||
Memory* memory() const { return memory_; }
|
||||
cpu::Processor* processor() const { return processor_; }
|
||||
|
||||
virtual X_STATUS Setup();
|
||||
@@ -39,9 +39,9 @@ public:
|
||||
void InitializeRingBuffer(uint32_t ptr, uint32_t page_count);
|
||||
void EnableReadPointerWriteBack(uint32_t ptr, uint32_t block_size);
|
||||
|
||||
bool HandlesRegister(uint32_t addr);
|
||||
virtual uint64_t ReadRegister(uint32_t addr);
|
||||
virtual void WriteRegister(uint32_t addr, uint64_t value);
|
||||
bool HandlesRegister(uint64_t addr);
|
||||
virtual uint64_t ReadRegister(uint64_t addr);
|
||||
virtual void WriteRegister(uint64_t addr, uint64_t value);
|
||||
|
||||
void MarkVblank();
|
||||
void DispatchInterruptCallback(uint32_t source, uint32_t cpu = 0xFFFFFFFF);
|
||||
@@ -59,13 +59,13 @@ private:
|
||||
}
|
||||
void ThreadStart();
|
||||
|
||||
static bool HandlesRegisterThunk(GraphicsSystem* gs, uint32_t addr) {
|
||||
static bool HandlesRegisterThunk(GraphicsSystem* gs, uint64_t addr) {
|
||||
return gs->HandlesRegister(addr);
|
||||
}
|
||||
static uint64_t ReadRegisterThunk(GraphicsSystem* gs, uint32_t addr) {
|
||||
static uint64_t ReadRegisterThunk(GraphicsSystem* gs, uint64_t addr) {
|
||||
return gs->ReadRegister(addr);
|
||||
}
|
||||
static void WriteRegisterThunk(GraphicsSystem* gs, uint32_t addr,
|
||||
static void WriteRegisterThunk(GraphicsSystem* gs, uint64_t addr,
|
||||
uint64_t value) {
|
||||
gs->WriteRegister(addr, value);
|
||||
}
|
||||
@@ -74,7 +74,7 @@ protected:
|
||||
GraphicsSystem(Emulator* emulator);
|
||||
|
||||
Emulator* emulator_;
|
||||
xe_memory_ref memory_;
|
||||
Memory* memory_;
|
||||
cpu::Processor* processor_;
|
||||
|
||||
xe_run_loop_ref run_loop_;
|
||||
|
||||
@@ -19,7 +19,7 @@ using namespace xe::gpu::nop;
|
||||
using namespace xe::gpu::xenos;
|
||||
|
||||
|
||||
NopGraphicsDriver::NopGraphicsDriver(xe_memory_ref memory) :
|
||||
NopGraphicsDriver::NopGraphicsDriver(Memory* memory) :
|
||||
GraphicsDriver(memory) {
|
||||
shader_cache_ = new ShaderCache();
|
||||
}
|
||||
@@ -50,7 +50,7 @@ void NopGraphicsDriver::SetShader(
|
||||
uint32_t start,
|
||||
uint32_t length) {
|
||||
// Find or create shader in the cache.
|
||||
uint8_t* p = xe_memory_addr(memory_, address);
|
||||
uint8_t* p = memory_->Translate(address);
|
||||
Shader* shader = shader_cache_->FindOrCreate(
|
||||
type, p, length);
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace nop {
|
||||
|
||||
class NopGraphicsDriver : public GraphicsDriver {
|
||||
public:
|
||||
NopGraphicsDriver(xe_memory_ref memory);
|
||||
NopGraphicsDriver(Memory* memory);
|
||||
virtual ~NopGraphicsDriver();
|
||||
|
||||
virtual void Initialize();
|
||||
|
||||
@@ -25,7 +25,7 @@ using namespace xe::gpu::xenos;
|
||||
|
||||
|
||||
RingBufferWorker::RingBufferWorker(
|
||||
GraphicsSystem* graphics_system, xe_memory_ref memory) :
|
||||
GraphicsSystem* graphics_system, Memory* memory) :
|
||||
graphics_system_(graphics_system), memory_(memory), driver_(0) {
|
||||
write_ptr_index_event_ = CreateEvent(
|
||||
NULL, FALSE, FALSE, NULL);
|
||||
@@ -87,7 +87,7 @@ void RingBufferWorker::UpdateWritePointer(uint32_t value) {
|
||||
}
|
||||
|
||||
void RingBufferWorker::Pump() {
|
||||
uint8_t* p = xe_memory_addr(memory_);
|
||||
uint8_t* p = memory_->membase();
|
||||
|
||||
if (write_ptr_index_ == 0xBAADF00D ||
|
||||
read_ptr_index_ == write_ptr_index_) {
|
||||
@@ -189,7 +189,7 @@ void RingBufferWorker::AdvancePtr(PacketArgs& args, uint32_t n) {
|
||||
XEGETUINT32BE(p + args.ptr); ADVANCE_PTR(1);
|
||||
|
||||
uint32_t RingBufferWorker::ExecutePacket(PacketArgs& args) {
|
||||
uint8_t* p = xe_memory_addr(memory_);
|
||||
uint8_t* p = memory_->membase();
|
||||
RegisterFile* regs = driver_->register_file();
|
||||
|
||||
uint32_t packet_ptr = args.ptr;
|
||||
@@ -730,7 +730,7 @@ void RingBufferWorker::WriteRegister(
|
||||
uint32_t scratch_reg = index - XE_GPU_REG_SCRATCH_REG0;
|
||||
if ((1 << scratch_reg) & regs->values[XE_GPU_REG_SCRATCH_UMSK].u32) {
|
||||
// Enabled - write to address.
|
||||
uint8_t* p = xe_memory_addr(memory_);
|
||||
uint8_t* p = memory_->membase();
|
||||
uint32_t scratch_addr = regs->values[XE_GPU_REG_SCRATCH_ADDR].u32;
|
||||
uint32_t mem_addr = scratch_addr + (scratch_reg * 4);
|
||||
XESETUINT32BE(p + GpuToCpu(primary_buffer_ptr_, mem_addr), value);
|
||||
|
||||
@@ -23,10 +23,10 @@ class GraphicsSystem;
|
||||
|
||||
class RingBufferWorker {
|
||||
public:
|
||||
RingBufferWorker(GraphicsSystem* graphics_system, xe_memory_ref memory);
|
||||
RingBufferWorker(GraphicsSystem* graphics_system, Memory* memory);
|
||||
virtual ~RingBufferWorker();
|
||||
|
||||
xe_memory_ref memory();
|
||||
Memory* memory() const { return memory_; }
|
||||
|
||||
uint64_t QueryTime();
|
||||
uint32_t counter() const { return counter_; }
|
||||
@@ -54,7 +54,7 @@ private:
|
||||
void WriteRegister(uint32_t packet_ptr, uint32_t index, uint32_t value);
|
||||
|
||||
protected:
|
||||
xe_memory_ref memory_;
|
||||
Memory* memory_;
|
||||
GraphicsSystem* graphics_system_;
|
||||
GraphicsDriver* driver_;
|
||||
|
||||
|
||||
@@ -19,8 +19,7 @@ using namespace xe::hid;
|
||||
|
||||
|
||||
InputSystem::InputSystem(Emulator* emulator) :
|
||||
emulator_(emulator) {
|
||||
memory_ = xe_memory_retain(emulator->memory());
|
||||
emulator_(emulator), memory_(emulator->memory()) {
|
||||
}
|
||||
|
||||
InputSystem::~InputSystem() {
|
||||
@@ -29,7 +28,6 @@ InputSystem::~InputSystem() {
|
||||
InputDriver* driver = *it;
|
||||
delete driver;
|
||||
}
|
||||
xe_memory_release(memory_);
|
||||
}
|
||||
|
||||
X_STATUS InputSystem::Setup() {
|
||||
|
||||
@@ -30,7 +30,7 @@ public:
|
||||
~InputSystem();
|
||||
|
||||
Emulator* emulator() const { return emulator_; }
|
||||
xe_memory_ref memory() const { return memory_; }
|
||||
Memory* memory() const { return memory_; }
|
||||
cpu::Processor* processor() const { return processor_; }
|
||||
|
||||
X_STATUS Setup();
|
||||
@@ -44,7 +44,7 @@ public:
|
||||
|
||||
private:
|
||||
Emulator* emulator_;
|
||||
xe_memory_ref memory_;
|
||||
Memory* memory_;
|
||||
cpu::Processor* processor_;
|
||||
|
||||
std::vector<InputDriver*> drivers_;
|
||||
|
||||
@@ -18,11 +18,9 @@ using namespace xe::kernel;
|
||||
|
||||
|
||||
KernelModule::KernelModule(Emulator* emulator) :
|
||||
emulator_(emulator) {
|
||||
memory_ = xe_memory_retain(emulator_->memory());
|
||||
emulator_(emulator), memory_(emulator->memory()) {
|
||||
export_resolver_ = emulator->export_resolver();
|
||||
}
|
||||
|
||||
KernelModule::~KernelModule() {
|
||||
xe_memory_release(memory_);
|
||||
}
|
||||
|
||||
@@ -28,9 +28,9 @@ public:
|
||||
virtual ~KernelModule();
|
||||
|
||||
protected:
|
||||
Emulator* emulator_;
|
||||
xe_memory_ref memory_;
|
||||
ExportResolver* export_resolver_;
|
||||
Emulator* emulator_;
|
||||
Memory* memory_;
|
||||
ExportResolver* export_resolver_;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#include <xenia/common.h>
|
||||
#include <xenia/core.h>
|
||||
|
||||
#include <xenia/cpu/ppc.h>
|
||||
#include <alloy/frontend/ppc/ppc_context.h>
|
||||
#include <xenia/export_resolver.h>
|
||||
#include <xenia/kernel/kernel_module.h>
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
namespace xe {
|
||||
namespace kernel {
|
||||
|
||||
using PPCContext = alloy::frontend::ppc::PPCContext;
|
||||
|
||||
|
||||
#define SHIM_CALL void _cdecl
|
||||
#define SHIM_SET_MAPPING(library_name, export_name, shim_data) \
|
||||
@@ -52,7 +54,7 @@ namespace kernel {
|
||||
#define SHIM_SET_RETURN(v) SHIM_SET_GPR_64(3, v)
|
||||
|
||||
|
||||
#define IMPL_MEM_ADDR(a) (a ? xe_memory_addr(state->memory(), a) : NULL)
|
||||
#define IMPL_MEM_ADDR(a) (a ? state->memory()->Translate(a) : NULL)
|
||||
|
||||
#define IMPL_MEM_16(a) (uint16_t)XEGETUINT16BE(IMPL_MEM_ADDR(a))
|
||||
#define IMPL_MEM_32(a) (uint32_t)XEGETUINT32BE(IMPL_MEM_ADDR(a))
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace xam {
|
||||
|
||||
|
||||
SHIM_CALL XamContentGetLicenseMask_shim(
|
||||
xe_ppc_state_t* ppc_state, XamState* state) {
|
||||
PPCContext* ppc_state, XamState* state) {
|
||||
uint32_t unk0_ptr = SHIM_GET_ARG_32(0);
|
||||
uint32_t unk1_ptr = SHIM_GET_ARG_32(1);
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace xam {
|
||||
|
||||
|
||||
SHIM_CALL XGetAVPack_shim(
|
||||
xe_ppc_state_t* ppc_state, XamState* state) {
|
||||
PPCContext* ppc_state, XamState* state) {
|
||||
// DWORD
|
||||
// Not sure what the values are for this, but 6 is VGA.
|
||||
// Other likely values are 3/4/8 for HDMI or something.
|
||||
@@ -37,7 +37,7 @@ SHIM_CALL XGetAVPack_shim(
|
||||
|
||||
|
||||
SHIM_CALL XGetGameRegion_shim(
|
||||
xe_ppc_state_t* ppc_state, XamState* state) {
|
||||
PPCContext* ppc_state, XamState* state) {
|
||||
XELOGD("XGetGameRegion()");
|
||||
|
||||
SHIM_SET_RETURN(XEX_REGION_ALL);
|
||||
@@ -45,7 +45,7 @@ SHIM_CALL XGetGameRegion_shim(
|
||||
|
||||
|
||||
SHIM_CALL XGetLanguage_shim(
|
||||
xe_ppc_state_t* ppc_state, XamState* state) {
|
||||
PPCContext* ppc_state, XamState* state) {
|
||||
XELOGD("XGetLanguage()");
|
||||
|
||||
uint32_t desired_language = X_LANGUAGE_ENGLISH;
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace xam {
|
||||
|
||||
// http://msdn.microsoft.com/en-us/library/windows/desktop/microsoft.directx_sdk.reference.xinputgetcapabilities(v=vs.85).aspx
|
||||
SHIM_CALL XamInputGetCapabilities_shim(
|
||||
xe_ppc_state_t* ppc_state, XamState* state) {
|
||||
PPCContext* ppc_state, XamState* state) {
|
||||
uint32_t user_index = SHIM_GET_ARG_32(0);
|
||||
uint32_t flags = SHIM_GET_ARG_32(1);
|
||||
uint32_t caps_ptr = SHIM_GET_ARG_32(2);
|
||||
@@ -58,7 +58,7 @@ SHIM_CALL XamInputGetCapabilities_shim(
|
||||
|
||||
// http://msdn.microsoft.com/en-us/library/windows/desktop/microsoft.directx_sdk.reference.xinputgetstate(v=vs.85).aspx
|
||||
SHIM_CALL XamInputGetState_shim(
|
||||
xe_ppc_state_t* ppc_state, XamState* state) {
|
||||
PPCContext* ppc_state, XamState* state) {
|
||||
uint32_t user_index = SHIM_GET_ARG_32(0);
|
||||
uint32_t state_ptr = SHIM_GET_ARG_32(1);
|
||||
|
||||
@@ -85,7 +85,7 @@ SHIM_CALL XamInputGetState_shim(
|
||||
|
||||
// http://msdn.microsoft.com/en-us/library/windows/desktop/microsoft.directx_sdk.reference.xinputsetstate(v=vs.85).aspx
|
||||
SHIM_CALL XamInputSetState_shim(
|
||||
xe_ppc_state_t* ppc_state, XamState* state) {
|
||||
PPCContext* ppc_state, XamState* state) {
|
||||
uint32_t user_index = SHIM_GET_ARG_32(0);
|
||||
uint32_t vibration_ptr = SHIM_GET_ARG_32(1);
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace xam {
|
||||
|
||||
|
||||
SHIM_CALL NetDll_XNetStartup_shim(
|
||||
xe_ppc_state_t* ppc_state, XamState* state) {
|
||||
PPCContext* ppc_state, XamState* state) {
|
||||
uint32_t one = SHIM_GET_ARG_32(0);
|
||||
uint32_t params_ptr = SHIM_GET_ARG_32(1);
|
||||
|
||||
@@ -39,7 +39,7 @@ SHIM_CALL NetDll_XNetStartup_shim(
|
||||
|
||||
|
||||
SHIM_CALL NetDll_WSAStartup_shim(
|
||||
xe_ppc_state_t* ppc_state, XamState* state) {
|
||||
PPCContext* ppc_state, XamState* state) {
|
||||
uint32_t one = SHIM_GET_ARG_32(0);
|
||||
uint32_t version = SHIM_GET_ARG_16(1);
|
||||
uint32_t data_ptr = SHIM_GET_ARG_32(2);
|
||||
|
||||
@@ -23,11 +23,9 @@ namespace {
|
||||
|
||||
|
||||
XamState::XamState(Emulator* emulator) :
|
||||
emulator_(emulator) {
|
||||
memory_ = xe_memory_retain(emulator->memory());
|
||||
export_resolver_ = emulator->export_resolver();
|
||||
emulator_(emulator), memory_(emulator->memory()),
|
||||
export_resolver_(emulator->export_resolver()) {
|
||||
}
|
||||
|
||||
XamState::~XamState() {
|
||||
xe_memory_release(memory_);
|
||||
}
|
||||
|
||||
@@ -28,11 +28,11 @@ public:
|
||||
~XamState();
|
||||
|
||||
Emulator* emulator() const { return emulator_; }
|
||||
xe_memory_ref memory() const { return memory_; }
|
||||
Memory* memory() const { return memory_; }
|
||||
|
||||
private:
|
||||
Emulator* emulator_;
|
||||
xe_memory_ref memory_;
|
||||
Memory* memory_;
|
||||
ExportResolver* export_resolver_;
|
||||
};
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace xam {
|
||||
|
||||
|
||||
SHIM_CALL XamUserGetXUID_shim(
|
||||
xe_ppc_state_t* ppc_state, XamState* state) {
|
||||
PPCContext* ppc_state, XamState* state) {
|
||||
uint32_t user_index = SHIM_GET_ARG_32(0);
|
||||
uint32_t xuid_ptr = SHIM_GET_ARG_32(1);
|
||||
|
||||
@@ -43,7 +43,7 @@ SHIM_CALL XamUserGetXUID_shim(
|
||||
|
||||
|
||||
SHIM_CALL XamUserGetSigninState_shim(
|
||||
xe_ppc_state_t* ppc_state, XamState* state) {
|
||||
PPCContext* ppc_state, XamState* state) {
|
||||
uint32_t user_index = SHIM_GET_ARG_32(0);
|
||||
|
||||
XELOGD(
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace xam {
|
||||
|
||||
|
||||
SHIM_CALL XGetVideoMode_shim(
|
||||
xe_ppc_state_t* ppc_state, XamState* state) {
|
||||
PPCContext* ppc_state, XamState* state) {
|
||||
xe::kernel::xboxkrnl::X_VIDEO_MODE *video_mode = (xe::kernel::xboxkrnl::X_VIDEO_MODE*)SHIM_MEM_ADDR(SHIM_GET_ARG_32(0));
|
||||
xeVdQueryVideoMode(video_mode, true);
|
||||
}
|
||||
|
||||
@@ -22,9 +22,8 @@ using namespace xe::kernel::xboxkrnl;
|
||||
|
||||
|
||||
KernelState::KernelState(Emulator* emulator) :
|
||||
emulator_(emulator),
|
||||
emulator_(emulator), memory_(emulator->memory()),
|
||||
executable_module_(NULL) {
|
||||
memory_ = xe_memory_retain(emulator->memory());
|
||||
processor_ = emulator->processor();
|
||||
file_system_ = emulator->file_system();
|
||||
|
||||
@@ -38,8 +37,6 @@ KernelState::~KernelState() {
|
||||
// Delete all objects.
|
||||
xe_mutex_free(object_mutex_);
|
||||
delete object_table_;
|
||||
|
||||
xe_memory_release(memory_);
|
||||
}
|
||||
|
||||
KernelState* KernelState::shared() {
|
||||
|
||||
@@ -38,7 +38,7 @@ public:
|
||||
static KernelState* shared();
|
||||
|
||||
Emulator* emulator() const { return emulator_; }
|
||||
xe_memory_ref memory() const { return memory_; }
|
||||
Memory* memory() const { return memory_; }
|
||||
cpu::Processor* processor() const { return processor_; }
|
||||
fs::FileSystem* file_system() const { return file_system_; }
|
||||
|
||||
@@ -50,7 +50,7 @@ public:
|
||||
|
||||
private:
|
||||
Emulator* emulator_;
|
||||
xe_memory_ref memory_;
|
||||
Memory* memory_;
|
||||
cpu::Processor* processor_;
|
||||
fs::FileSystem* file_system_;
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
|
||||
using namespace xe;
|
||||
using namespace xe::cpu;
|
||||
using namespace xe::kernel;
|
||||
using namespace xe::kernel::xboxkrnl;
|
||||
|
||||
@@ -86,6 +87,10 @@ X_STATUS XModule::LoadFromFile(const char* path) {
|
||||
}
|
||||
|
||||
X_STATUS XModule::LoadFromMemory(const void* addr, const size_t length) {
|
||||
Processor* processor = kernel_state()->processor();
|
||||
XenonRuntime* runtime = processor->runtime();
|
||||
XexModule* xex_module = NULL;
|
||||
|
||||
// Load the XEX into memory and decrypt.
|
||||
xe_xex2_options_t xex_options;
|
||||
xe_zero_struct(&xex_options, sizeof(xex_options));
|
||||
@@ -93,11 +98,15 @@ X_STATUS XModule::LoadFromMemory(const void* addr, const size_t length) {
|
||||
XEEXPECTNOTNULL(xex_);
|
||||
|
||||
// Prepare the module for execution.
|
||||
XEEXPECTZERO(kernel_state()->processor()->LoadXexModule(name_, path_, xex_));
|
||||
// Runtime takes ownership.
|
||||
xex_module = new XexModule(runtime);
|
||||
XEEXPECTZERO(xex_module->Load(name_, path_, xex_));
|
||||
XEEXPECTZERO(runtime->AddModule(xex_module));
|
||||
|
||||
return X_STATUS_SUCCESS;
|
||||
|
||||
XECLEANUP:
|
||||
delete xex_module;
|
||||
return X_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,9 @@
|
||||
#include <xenia/kernel/xboxkrnl/objects/xmodule.h>
|
||||
|
||||
|
||||
using namespace alloy;
|
||||
using namespace xe;
|
||||
using namespace xe::cpu;
|
||||
using namespace xe::kernel;
|
||||
using namespace xe::kernel::xboxkrnl;
|
||||
|
||||
@@ -61,13 +63,13 @@ XThread::~XThread() {
|
||||
PlatformDestroy();
|
||||
|
||||
if (thread_state_) {
|
||||
kernel_state()->processor()->DeallocThread(thread_state_);
|
||||
delete thread_state_;
|
||||
}
|
||||
if (tls_address_) {
|
||||
xe_memory_heap_free(kernel_state()->memory(), tls_address_, 0);
|
||||
kernel_state()->memory()->HeapFree(tls_address_, 0);
|
||||
}
|
||||
if (thread_state_address_) {
|
||||
xe_memory_heap_free(kernel_state()->memory(), thread_state_address_, 0);
|
||||
kernel_state()->memory()->HeapFree(thread_state_address_, 0);
|
||||
}
|
||||
|
||||
if (thread_handle_) {
|
||||
@@ -111,12 +113,12 @@ uint32_t XThread::thread_id() {
|
||||
}
|
||||
|
||||
uint32_t XThread::last_error() {
|
||||
uint8_t *p = xe_memory_addr(memory(), thread_state_address_);
|
||||
uint8_t *p = memory()->Translate(thread_state_address_);
|
||||
return XEGETUINT32BE(p + 0x160);
|
||||
}
|
||||
|
||||
void XThread::set_last_error(uint32_t error_code) {
|
||||
uint8_t *p = xe_memory_addr(memory(), thread_state_address_);
|
||||
uint8_t *p = memory()->Translate(thread_state_address_);
|
||||
XESETUINT32BE(p + 0x160, error_code);
|
||||
}
|
||||
|
||||
@@ -133,8 +135,8 @@ X_STATUS XThread::Create() {
|
||||
// 0x160: last error
|
||||
// So, at offset 0x100 we have a 4b pointer to offset 200, then have the
|
||||
// structure.
|
||||
thread_state_address_ = xe_memory_heap_alloc(
|
||||
memory(), 0, 2048, XE_MEMORY_FLAG_ZERO);
|
||||
thread_state_address_ = (uint32_t)memory()->HeapAlloc(
|
||||
0, 2048, MEMORY_FLAG_ZERO);
|
||||
if (!thread_state_address_) {
|
||||
XELOGW("Unable to allocate thread state block");
|
||||
return X_STATUS_NO_MEMORY;
|
||||
@@ -148,8 +150,8 @@ X_STATUS XThread::Create() {
|
||||
// Allocate TLS block.
|
||||
const xe_xex2_header_t* header = module->xex_header();
|
||||
uint32_t tls_size = header->tls_info.slot_count * header->tls_info.data_size;
|
||||
tls_address_ = xe_memory_heap_alloc(
|
||||
memory(), 0, tls_size, XE_MEMORY_FLAG_ZERO);
|
||||
tls_address_ = (uint32_t)memory()->HeapAlloc(
|
||||
0, tls_size, MEMORY_FLAG_ZERO);
|
||||
if (!tls_address_) {
|
||||
XELOGW("Unable to allocate thread local storage block");
|
||||
module->Release();
|
||||
@@ -158,11 +160,11 @@ X_STATUS XThread::Create() {
|
||||
|
||||
// Copy in default TLS info.
|
||||
// TODO(benvanik): is this correct?
|
||||
xe_memory_copy(memory(),
|
||||
memory()->Copy(
|
||||
tls_address_, header->tls_info.raw_data_address, tls_size);
|
||||
|
||||
// Setup the thread state block (last error/etc).
|
||||
uint8_t *p = xe_memory_addr(memory(), thread_state_address_);
|
||||
uint8_t *p = memory()->Translate(thread_state_address_);
|
||||
XESETUINT32BE(p + 0x000, tls_address_);
|
||||
XESETUINT32BE(p + 0x100, thread_state_address_);
|
||||
XESETUINT32BE(p + 0x14C, thread_id_);
|
||||
@@ -171,13 +173,9 @@ X_STATUS XThread::Create() {
|
||||
|
||||
// Allocate processor thread state.
|
||||
// This is thread safe.
|
||||
thread_state_ = kernel_state()->processor()->AllocThread(
|
||||
creation_params_.stack_size, thread_state_address_, thread_id_);
|
||||
if (!thread_state_) {
|
||||
XELOGW("Unable to allocate processor thread state");
|
||||
module->Release();
|
||||
return X_STATUS_NO_MEMORY;
|
||||
}
|
||||
thread_state_ = new XenonThreadState(
|
||||
kernel_state()->processor()->runtime(),
|
||||
thread_id_, creation_params_.stack_size, thread_state_address_);
|
||||
|
||||
X_STATUS return_code = PlatformCreate();
|
||||
if (XFAILED(return_code)) {
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
class ThreadState;
|
||||
class XenonThreadState;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ private:
|
||||
void* thread_handle_;
|
||||
uint32_t tls_address_;
|
||||
uint32_t thread_state_address_;
|
||||
cpu::ThreadState* thread_state_;
|
||||
cpu::XenonThreadState* thread_state_;
|
||||
|
||||
uint32_t irql_;
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace xboxkrnl {
|
||||
|
||||
|
||||
SHIM_CALL XMACreateContext_shim(
|
||||
xe_ppc_state_t* ppc_state, KernelState* state) {
|
||||
PPCContext* ppc_state, KernelState* state) {
|
||||
uint32_t context_ptr = SHIM_GET_ARG_32(0);
|
||||
|
||||
XELOGD(
|
||||
@@ -41,7 +41,7 @@ SHIM_CALL XMACreateContext_shim(
|
||||
|
||||
|
||||
SHIM_CALL XMAReleaseContext_shim(
|
||||
xe_ppc_state_t* ppc_state, KernelState* state) {
|
||||
PPCContext* ppc_state, KernelState* state) {
|
||||
uint32_t context_ptr = SHIM_GET_ARG_32(0);
|
||||
|
||||
XELOGD(
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace xboxkrnl {
|
||||
|
||||
// TODO: clean me up!
|
||||
SHIM_CALL DbgPrint_shim(
|
||||
xe_ppc_state_t* ppc_state, KernelState* state) {
|
||||
PPCContext* ppc_state, KernelState* state) {
|
||||
|
||||
uint32_t format_ptr = SHIM_GET_ARG_32(0);
|
||||
if (format_ptr == 0) {
|
||||
@@ -237,7 +237,7 @@ void xeDbgBreakPoint() {
|
||||
|
||||
|
||||
SHIM_CALL DbgBreakPoint_shim(
|
||||
xe_ppc_state_t* ppc_state, KernelState* state) {
|
||||
PPCContext* ppc_state, KernelState* state) {
|
||||
XELOGD("DbgBreakPoint()");
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ void xeHalReturnToFirmware(uint32_t routine) {
|
||||
|
||||
|
||||
SHIM_CALL HalReturnToFirmware_shim(
|
||||
xe_ppc_state_t* ppc_state, KernelState* state) {
|
||||
PPCContext* ppc_state, KernelState* state) {
|
||||
uint32_t routine = SHIM_GET_ARG_32(0);
|
||||
|
||||
XELOGD(
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace xboxkrnl {
|
||||
|
||||
|
||||
SHIM_CALL NtCreateFile_shim(
|
||||
xe_ppc_state_t* ppc_state, KernelState* state) {
|
||||
PPCContext* ppc_state, KernelState* state) {
|
||||
uint32_t handle_ptr = SHIM_GET_ARG_32(0);
|
||||
uint32_t desired_access = SHIM_GET_ARG_32(1);
|
||||
uint32_t object_attributes_ptr = SHIM_GET_ARG_32(2);
|
||||
@@ -99,7 +99,7 @@ SHIM_CALL NtCreateFile_shim(
|
||||
}
|
||||
|
||||
SHIM_CALL NtOpenFile_shim(
|
||||
xe_ppc_state_t* ppc_state, KernelState* state) {
|
||||
PPCContext* ppc_state, KernelState* state) {
|
||||
SHIM_SET_RETURN(X_STATUS_NO_SUCH_FILE);
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ void xeNtReadFileCompleted(XAsyncRequest* request, xeNtReadFileState* state) {
|
||||
}
|
||||
|
||||
SHIM_CALL NtReadFile_shim(
|
||||
xe_ppc_state_t* ppc_state, KernelState* state) {
|
||||
PPCContext* ppc_state, KernelState* state) {
|
||||
uint32_t file_handle = SHIM_GET_ARG_32(0);
|
||||
uint32_t event_handle = SHIM_GET_ARG_32(1);
|
||||
uint32_t apc_routine_ptr = SHIM_GET_ARG_32(2);
|
||||
@@ -219,7 +219,7 @@ SHIM_CALL NtReadFile_shim(
|
||||
}
|
||||
|
||||
SHIM_CALL NtSetInformationFile_shim(
|
||||
xe_ppc_state_t* ppc_state, KernelState* state) {
|
||||
PPCContext* ppc_state, KernelState* state) {
|
||||
uint32_t file_handle = SHIM_GET_ARG_32(0);
|
||||
uint32_t io_status_block_ptr = SHIM_GET_ARG_32(1);
|
||||
uint32_t file_info_ptr = SHIM_GET_ARG_32(2);
|
||||
@@ -277,7 +277,7 @@ SHIM_CALL NtSetInformationFile_shim(
|
||||
}
|
||||
|
||||
SHIM_CALL NtQueryInformationFile_shim(
|
||||
xe_ppc_state_t* ppc_state, KernelState* state) {
|
||||
PPCContext* ppc_state, KernelState* state) {
|
||||
uint32_t file_handle = SHIM_GET_ARG_32(0);
|
||||
uint32_t io_status_block_ptr = SHIM_GET_ARG_32(1);
|
||||
uint32_t file_info_ptr = SHIM_GET_ARG_32(2);
|
||||
@@ -354,7 +354,7 @@ SHIM_CALL NtQueryInformationFile_shim(
|
||||
}
|
||||
|
||||
SHIM_CALL NtQueryFullAttributesFile_shim(
|
||||
xe_ppc_state_t* ppc_state, KernelState* state) {
|
||||
PPCContext* ppc_state, KernelState* state) {
|
||||
uint32_t object_attributes_ptr = SHIM_GET_ARG_32(0);
|
||||
uint32_t file_info_ptr = SHIM_GET_ARG_32(1);
|
||||
|
||||
@@ -384,7 +384,7 @@ SHIM_CALL NtQueryFullAttributesFile_shim(
|
||||
}
|
||||
|
||||
SHIM_CALL NtQueryVolumeInformationFile_shim(
|
||||
xe_ppc_state_t* ppc_state, KernelState* state) {
|
||||
PPCContext* ppc_state, KernelState* state) {
|
||||
SHIM_SET_RETURN(X_STATUS_NO_SUCH_FILE);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <xenia/kernel/xboxkrnl/xboxkrnl_private.h>
|
||||
|
||||
|
||||
using namespace alloy;
|
||||
using namespace xe;
|
||||
using namespace xe::kernel;
|
||||
using namespace xe::kernel::xboxkrnl;
|
||||
@@ -78,8 +79,8 @@ X_STATUS xeNtAllocateVirtualMemory(
|
||||
|
||||
// Allocate.
|
||||
uint32_t flags = (allocation_type & X_MEM_NOZERO);
|
||||
uint32_t addr = xe_memory_heap_alloc(
|
||||
state->memory(), *base_addr_ptr, adjusted_size, flags);
|
||||
uint32_t addr = (uint32_t)state->memory()->HeapAlloc(
|
||||
*base_addr_ptr, adjusted_size, flags);
|
||||
if (!addr) {
|
||||
// Failed - assume no memory available.
|
||||
return X_STATUS_NO_MEMORY;
|
||||
@@ -94,7 +95,7 @@ X_STATUS xeNtAllocateVirtualMemory(
|
||||
|
||||
|
||||
SHIM_CALL NtAllocateVirtualMemory_shim(
|
||||
xe_ppc_state_t* ppc_state, KernelState* state) {
|
||||
PPCContext* ppc_state, KernelState* state) {
|
||||
uint32_t base_addr_ptr = SHIM_GET_ARG_32(0);
|
||||
uint32_t base_addr_value = SHIM_MEM_32(base_addr_ptr);
|
||||
uint32_t region_size_ptr = SHIM_GET_ARG_32(1);
|
||||
@@ -147,8 +148,8 @@ X_STATUS xeNtFreeVirtualMemory(
|
||||
|
||||
// Free.
|
||||
uint32_t flags = 0;
|
||||
uint32_t freed_size = xe_memory_heap_free(state->memory(), *base_addr_ptr,
|
||||
flags);
|
||||
uint32_t freed_size = state->memory()->HeapFree(
|
||||
*base_addr_ptr, flags);
|
||||
if (!freed_size) {
|
||||
return X_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
@@ -160,7 +161,7 @@ X_STATUS xeNtFreeVirtualMemory(
|
||||
|
||||
|
||||
SHIM_CALL NtFreeVirtualMemory_shim(
|
||||
xe_ppc_state_t* ppc_state, KernelState* state) {
|
||||
PPCContext* ppc_state, KernelState* state) {
|
||||
uint32_t base_addr_ptr = SHIM_GET_ARG_32(0);
|
||||
uint32_t base_addr_value = SHIM_MEM_32(base_addr_ptr);
|
||||
uint32_t region_size_ptr = SHIM_GET_ARG_32(1);
|
||||
@@ -232,9 +233,9 @@ uint32_t xeMmAllocatePhysicalMemoryEx(
|
||||
XEASSERT(max_addr_range == 0xFFFFFFFF);
|
||||
|
||||
// Allocate.
|
||||
uint32_t flags = XE_MEMORY_FLAG_PHYSICAL;
|
||||
uint32_t base_address = xe_memory_heap_alloc(
|
||||
state->memory(), 0, adjusted_size, flags, adjusted_alignment);
|
||||
uint32_t flags = MEMORY_FLAG_PHYSICAL;
|
||||
uint32_t base_address = (uint32_t)state->memory()->HeapAlloc(
|
||||
0, adjusted_size, flags, adjusted_alignment);
|
||||
if (!base_address) {
|
||||
// Failed - assume no memory available.
|
||||
return 0;
|
||||
@@ -255,7 +256,7 @@ uint32_t xeMmAllocatePhysicalMemoryEx(
|
||||
|
||||
|
||||
SHIM_CALL MmAllocatePhysicalMemoryEx_shim(
|
||||
xe_ppc_state_t* ppc_state, KernelState* state) {
|
||||
PPCContext* ppc_state, KernelState* state) {
|
||||
uint32_t type = SHIM_GET_ARG_32(0);
|
||||
uint32_t region_size = SHIM_GET_ARG_32(1);
|
||||
uint32_t protect_bits = SHIM_GET_ARG_32(2);
|
||||
@@ -294,7 +295,7 @@ void xeMmFreePhysicalMemory(uint32_t type, uint32_t base_address) {
|
||||
|
||||
|
||||
SHIM_CALL MmFreePhysicalMemory_shim(
|
||||
xe_ppc_state_t* ppc_state, KernelState* state) {
|
||||
PPCContext* ppc_state, KernelState* state) {
|
||||
uint32_t type = SHIM_GET_ARG_32(0);
|
||||
uint32_t base_address = SHIM_GET_ARG_32(1);
|
||||
|
||||
@@ -310,15 +311,14 @@ uint32_t xeMmQueryAddressProtect(uint32_t base_address) {
|
||||
KernelState* state = shared_kernel_state_;
|
||||
XEASSERTNOTNULL(state);
|
||||
|
||||
uint32_t access = xe_memory_query_protect(
|
||||
state->memory(), base_address);
|
||||
uint32_t access = state->memory()->QueryProtect(base_address);
|
||||
|
||||
return access;
|
||||
}
|
||||
|
||||
|
||||
SHIM_CALL MmQueryAddressProtect_shim(
|
||||
xe_ppc_state_t* ppc_state, KernelState* state) {
|
||||
PPCContext* ppc_state, KernelState* state) {
|
||||
uint32_t base_address = SHIM_GET_ARG_32(0);
|
||||
|
||||
XELOGD(
|
||||
@@ -355,7 +355,7 @@ uint32_t xeMmGetPhysicalAddress(uint32_t base_address) {
|
||||
|
||||
|
||||
SHIM_CALL MmGetPhysicalAddress_shim(
|
||||
xe_ppc_state_t* ppc_state, KernelState* state) {
|
||||
PPCContext* ppc_state, KernelState* state) {
|
||||
uint32_t base_address = SHIM_GET_ARG_32(0);
|
||||
|
||||
XELOGD(
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user