Moving alloy/ into xenia/cpu/ to start simplifying things.
This commit is contained in:
26
src/xenia/cpu/frontend/context_info.cc
Normal file
26
src/xenia/cpu/frontend/context_info.cc
Normal file
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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/frontend/context_info.h"
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace frontend {
|
||||
|
||||
ContextInfo::ContextInfo(size_t size, uintptr_t thread_state_offset,
|
||||
uintptr_t thread_id_offset)
|
||||
: size_(size),
|
||||
thread_state_offset_(thread_state_offset),
|
||||
thread_id_offset_(thread_id_offset) {}
|
||||
|
||||
ContextInfo::~ContextInfo() {}
|
||||
|
||||
} // namespace frontend
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
41
src/xenia/cpu/frontend/context_info.h
Normal file
41
src/xenia/cpu/frontend/context_info.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_FRONTEND_CONTEXT_INFO_H_
|
||||
#define XENIA_FRONTEND_CONTEXT_INFO_H_
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace frontend {
|
||||
|
||||
class ContextInfo {
|
||||
public:
|
||||
ContextInfo(size_t size, uintptr_t thread_state_offset,
|
||||
uintptr_t thread_id_offset);
|
||||
~ContextInfo();
|
||||
|
||||
size_t size() const { return size_; }
|
||||
|
||||
uintptr_t thread_state_offset() const { return thread_state_offset_; }
|
||||
uintptr_t thread_id_offset() const { return thread_id_offset_; }
|
||||
|
||||
private:
|
||||
size_t size_;
|
||||
uintptr_t thread_state_offset_;
|
||||
uintptr_t thread_id_offset_;
|
||||
};
|
||||
|
||||
} // namespace frontend
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_FRONTEND_CONTEXT_INFO_H_
|
||||
28
src/xenia/cpu/frontend/frontend.cc
Normal file
28
src/xenia/cpu/frontend/frontend.cc
Normal file
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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/frontend/frontend.h"
|
||||
|
||||
#include "xenia/cpu/runtime/runtime.h"
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace frontend {
|
||||
|
||||
Frontend::Frontend(runtime::Runtime* runtime) : runtime_(runtime) {}
|
||||
|
||||
Frontend::~Frontend() = default;
|
||||
|
||||
Memory* Frontend::memory() const { return runtime_->memory(); }
|
||||
|
||||
int Frontend::Initialize() { return 0; }
|
||||
|
||||
} // namespace frontend
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
57
src/xenia/cpu/frontend/frontend.h
Normal file
57
src/xenia/cpu/frontend/frontend.h
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. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_FRONTEND_FRONTEND_H_
|
||||
#define XENIA_FRONTEND_FRONTEND_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "xenia/cpu/frontend/context_info.h"
|
||||
#include "xenia/memory.h"
|
||||
#include "xenia/cpu/runtime/function.h"
|
||||
#include "xenia/cpu/runtime/symbol_info.h"
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace runtime {
|
||||
class Runtime;
|
||||
} // namespace runtime
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace frontend {
|
||||
|
||||
class Frontend {
|
||||
public:
|
||||
Frontend(runtime::Runtime* runtime);
|
||||
virtual ~Frontend();
|
||||
|
||||
runtime::Runtime* runtime() const { return runtime_; }
|
||||
Memory* memory() const;
|
||||
ContextInfo* context_info() const { return context_info_.get(); }
|
||||
|
||||
virtual int Initialize();
|
||||
|
||||
virtual int DeclareFunction(runtime::FunctionInfo* symbol_info) = 0;
|
||||
virtual int DefineFunction(runtime::FunctionInfo* symbol_info,
|
||||
uint32_t debug_info_flags, uint32_t trace_flags,
|
||||
runtime::Function** out_function) = 0;
|
||||
|
||||
protected:
|
||||
runtime::Runtime* runtime_;
|
||||
std::unique_ptr<ContextInfo> context_info_;
|
||||
};
|
||||
|
||||
} // namespace frontend
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_FRONTEND_FRONTEND_H_
|
||||
88
src/xenia/cpu/frontend/ppc/ppc_context.cc
Normal file
88
src/xenia/cpu/frontend/ppc/ppc_context.cc
Normal file
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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/frontend/ppc/ppc_context.h"
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace frontend {
|
||||
namespace ppc {
|
||||
|
||||
uint64_t ParseInt64(const char* value) {
|
||||
return std::strtoull(value, nullptr, 0);
|
||||
}
|
||||
|
||||
double ParseFloat64(const char* value) { return std::strtod(value, nullptr); }
|
||||
|
||||
vec128_t ParseVec128(const char* value) {
|
||||
vec128_t v;
|
||||
char* p = const_cast<char*>(value);
|
||||
if (*p == '[') ++p;
|
||||
v.i32[0] = std::strtoul(p, &p, 16);
|
||||
while (*p == ' ' || *p == ',') ++p;
|
||||
v.i32[1] = std::strtoul(p, &p, 16);
|
||||
while (*p == ' ' || *p == ',') ++p;
|
||||
v.i32[2] = std::strtoul(p, &p, 16);
|
||||
while (*p == ' ' || *p == ',') ++p;
|
||||
v.i32[3] = std::strtoul(p, &p, 16);
|
||||
return v;
|
||||
}
|
||||
|
||||
void PPCContext::SetRegFromString(const char* name, const char* value) {
|
||||
int n;
|
||||
if (sscanf(name, "r%d", &n) == 1) {
|
||||
this->r[n] = ParseInt64(value);
|
||||
} else if (sscanf(name, "f%d", &n) == 1) {
|
||||
this->f[n] = ParseFloat64(value);
|
||||
} else if (sscanf(name, "v%d", &n) == 1) {
|
||||
this->v[n] = ParseVec128(value);
|
||||
} else {
|
||||
printf("Unrecognized register name: %s\n", name);
|
||||
}
|
||||
}
|
||||
|
||||
bool PPCContext::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) {
|
||||
snprintf(out_value, out_value_size, "%016llX", this->r[n]);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else if (sscanf(name, "f%d", &n) == 1) {
|
||||
double expected = ParseFloat64(value);
|
||||
// TODO(benvanik): epsilon
|
||||
if (this->f[n] != expected) {
|
||||
snprintf(out_value, out_value_size, "%f", this->f[n]);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else if (sscanf(name, "v%d", &n) == 1) {
|
||||
vec128_t expected = ParseVec128(value);
|
||||
if (this->v[n] != expected) {
|
||||
snprintf(out_value, out_value_size, "[%.8X, %.8X, %.8X, %.8X]",
|
||||
this->v[n].i32[0], this->v[n].i32[1], this->v[n].i32[2],
|
||||
this->v[n].i32[3]);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
printf("Unrecognized register name: %s\n", name);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ppc
|
||||
} // namespace frontend
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
227
src/xenia/cpu/frontend/ppc/ppc_context.h
Normal file
227
src/xenia/cpu/frontend/ppc/ppc_context.h
Normal file
@@ -0,0 +1,227 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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_FRONTEND_PPC_PPC_CONTEXT_H_
|
||||
#define XENIA_FRONTEND_PPC_PPC_CONTEXT_H_
|
||||
|
||||
#include "poly/poly.h"
|
||||
#include "poly/vec128.h"
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace runtime {
|
||||
class Runtime;
|
||||
class ThreadState;
|
||||
} // namespace runtime
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace frontend {
|
||||
namespace ppc {
|
||||
|
||||
using vec128_t = poly::vec128_t;
|
||||
|
||||
// Map:
|
||||
// 0-31: GPR
|
||||
// 32-63: FPR
|
||||
// 64: LR
|
||||
// 65: CTR
|
||||
// 66: XER
|
||||
// 67: FPSCR
|
||||
// 68: VSCR
|
||||
// 69-76: CR0-7
|
||||
// 100: invalid
|
||||
// 128-256: VR
|
||||
|
||||
#pragma pack(push, 4)
|
||||
typedef struct alignas(64) PPCContext_s {
|
||||
// Must be stored at 0x0 for now.
|
||||
// TODO(benvanik): find a nice way to describe this to the JIT.
|
||||
runtime::ThreadState* thread_state;
|
||||
// TODO(benvanik): this is getting nasty. Must be here.
|
||||
uint8_t* membase;
|
||||
|
||||
// Most frequently used registers first.
|
||||
uint64_t r[32]; // General purpose registers
|
||||
uint64_t lr; // Link register
|
||||
uint64_t ctr; // Count register
|
||||
|
||||
// XER register
|
||||
// Split to make it easier to do individual updates.
|
||||
uint8_t xer_ca;
|
||||
uint8_t xer_ov;
|
||||
uint8_t xer_so;
|
||||
|
||||
// Condition registers
|
||||
// These are split to make it easier to do DCE on unused stores.
|
||||
union {
|
||||
uint32_t value;
|
||||
struct {
|
||||
uint8_t cr0_lt; // Negative (LT) - result is negative
|
||||
uint8_t cr0_gt; // Positive (GT) - result is positive (and not zero)
|
||||
uint8_t cr0_eq; // Zero (EQ) - result is zero or a stwcx/stdcx completed
|
||||
// successfully
|
||||
uint8_t cr0_so; // Summary Overflow (SO) - copy of XER[SO]
|
||||
};
|
||||
} cr0;
|
||||
union {
|
||||
uint32_t value;
|
||||
struct {
|
||||
uint8_t cr1_fx; // FP exception summary - copy of FPSCR[FX]
|
||||
uint8_t cr1_fex; // FP enabled exception summary - copy of FPSCR[FEX]
|
||||
uint8_t
|
||||
cr1_vx; // FP invalid operation exception summary - copy of FPSCR[VX]
|
||||
uint8_t cr1_ox; // FP overflow exception - copy of FPSCR[OX]
|
||||
};
|
||||
} cr1;
|
||||
union {
|
||||
uint32_t value;
|
||||
struct {
|
||||
uint8_t cr2_0;
|
||||
uint8_t cr2_1;
|
||||
uint8_t cr2_2;
|
||||
uint8_t cr2_3;
|
||||
};
|
||||
} cr2;
|
||||
union {
|
||||
uint32_t value;
|
||||
struct {
|
||||
uint8_t cr3_0;
|
||||
uint8_t cr3_1;
|
||||
uint8_t cr3_2;
|
||||
uint8_t cr3_3;
|
||||
};
|
||||
} cr3;
|
||||
union {
|
||||
uint32_t value;
|
||||
struct {
|
||||
uint8_t cr4_0;
|
||||
uint8_t cr4_1;
|
||||
uint8_t cr4_2;
|
||||
uint8_t cr4_3;
|
||||
};
|
||||
} cr4;
|
||||
union {
|
||||
uint32_t value;
|
||||
struct {
|
||||
uint8_t cr5_0;
|
||||
uint8_t cr5_1;
|
||||
uint8_t cr5_2;
|
||||
uint8_t cr5_3;
|
||||
};
|
||||
} cr5;
|
||||
union {
|
||||
uint32_t value;
|
||||
struct {
|
||||
uint8_t cr6_all_equal;
|
||||
uint8_t cr6_1;
|
||||
uint8_t cr6_none_equal;
|
||||
uint8_t cr6_3;
|
||||
};
|
||||
} cr6;
|
||||
union {
|
||||
uint32_t value;
|
||||
struct {
|
||||
uint8_t cr7_0;
|
||||
uint8_t cr7_1;
|
||||
uint8_t cr7_2;
|
||||
uint8_t cr7_3;
|
||||
};
|
||||
} cr7;
|
||||
|
||||
union {
|
||||
uint32_t value;
|
||||
struct {
|
||||
uint32_t rn : 2; // FP rounding control: 00 = nearest
|
||||
// 01 = toward zero
|
||||
// 10 = toward +infinity
|
||||
// 11 = toward -infinity
|
||||
uint32_t ni : 1; // Floating-point non-IEEE mode
|
||||
uint32_t xe : 1; // IEEE floating-point inexact exception enable
|
||||
uint32_t ze : 1; // IEEE floating-point zero divide exception enable
|
||||
uint32_t ue : 1; // IEEE floating-point underflow exception enable
|
||||
uint32_t oe : 1; // IEEE floating-point overflow exception enable
|
||||
uint32_t ve : 1; // FP invalid op exception enable
|
||||
uint32_t vxcvi : 1; // FP invalid op exception: invalid integer convert
|
||||
// -- sticky
|
||||
uint32_t vxsqrt : 1; // FP invalid op exception: invalid sqrt -- sticky
|
||||
uint32_t vxsoft : 1; // FP invalid op exception: software request
|
||||
// -- sticky
|
||||
uint32_t reserved : 1;
|
||||
uint32_t fprf_un : 1; // FP result unordered or NaN (FU or ?)
|
||||
uint32_t fprf_eq : 1; // FP result equal or zero (FE or =)
|
||||
uint32_t fprf_gt : 1; // FP result greater than or positive (FG or >)
|
||||
uint32_t fprf_lt : 1; // FP result less than or negative (FL or <)
|
||||
uint32_t fprf_c : 1; // FP result class
|
||||
uint32_t fi : 1; // FP fraction inexact
|
||||
uint32_t fr : 1; // FP fraction rounded
|
||||
uint32_t vxvc : 1; // FP invalid op exception: invalid compare --
|
||||
// sticky
|
||||
uint32_t vximz : 1; // FP invalid op exception: infinity * 0 -- sticky
|
||||
uint32_t vxzdz : 1; // FP invalid op exception: 0 / 0 -- sticky
|
||||
uint32_t vxidi : 1; // FP invalid op exception: infinity / infinity
|
||||
// -- sticky
|
||||
uint32_t vxisi : 1; // FP invalid op exception: infinity - infinity
|
||||
// -- sticky
|
||||
uint32_t vxsnan : 1; // FP invalid op exception: SNaN -- sticky
|
||||
uint32_t
|
||||
xx : 1; // FP inexact exception -- sticky
|
||||
uint32_t
|
||||
zx : 1; // FP zero divide exception -- sticky
|
||||
uint32_t
|
||||
ux : 1; // FP underflow exception -- sticky
|
||||
uint32_t
|
||||
ox : 1; // FP overflow exception -- sticky
|
||||
uint32_t vx : 1; // FP invalid operation exception summary
|
||||
uint32_t fex : 1; // FP enabled exception summary
|
||||
uint32_t
|
||||
fx : 1; // FP exception summary -- sticky
|
||||
} bits;
|
||||
} fpscr; // Floating-point status and control register
|
||||
|
||||
uint8_t vscr_sat;
|
||||
|
||||
double f[32]; // Floating-point registers
|
||||
vec128_t v[128]; // VMX128 vector registers
|
||||
|
||||
// uint32_t get_fprf() {
|
||||
// return fpscr.value & 0x000F8000;
|
||||
// }
|
||||
// void set_fprf(const uint32_t v) {
|
||||
// fpscr.value = (fpscr.value & ~0x000F8000) | v;
|
||||
// }
|
||||
|
||||
// Thread ID assigned to this context.
|
||||
uint32_t thread_id;
|
||||
|
||||
// Reserve address for load acquire/store release. Shared.
|
||||
uint64_t* reserve_address;
|
||||
uint64_t* reserve_value;
|
||||
|
||||
// Used to shuttle data into externs. Contents volatile.
|
||||
uint64_t scratch;
|
||||
|
||||
// Runtime-specific data pointer. Used on callbacks to get access to the
|
||||
// current runtime and its data.
|
||||
runtime::Runtime* runtime;
|
||||
|
||||
void SetRegFromString(const char* name, const char* value);
|
||||
bool CompareRegWithString(const char* name, const char* value,
|
||||
char* out_value, size_t out_value_size);
|
||||
} PPCContext;
|
||||
#pragma pack(pop)
|
||||
|
||||
} // namespace ppc
|
||||
} // namespace frontend
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_FRONTEND_PPC_PPC_CONTEXT_H_
|
||||
505
src/xenia/cpu/frontend/ppc/ppc_disasm.cc
Normal file
505
src/xenia/cpu/frontend/ppc/ppc_disasm.cc
Normal file
@@ -0,0 +1,505 @@
|
||||
/*
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2014 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "xenia/cpu/frontend/ppc/ppc_disasm.h"
|
||||
|
||||
#include "poly/poly.h"
|
||||
#include "poly/string_buffer.h"
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace frontend {
|
||||
namespace ppc {
|
||||
|
||||
void Disasm_0(InstrData& i, poly::StringBuffer* str) {
|
||||
str->Append("%-8s ???", i.type->name);
|
||||
}
|
||||
|
||||
void Disasm__(InstrData& i, poly::StringBuffer* str) {
|
||||
str->Append("%-8s", i.type->name);
|
||||
}
|
||||
|
||||
void Disasm_X_FRT_FRB(InstrData& i, poly::StringBuffer* str) {
|
||||
str->Append("%*s%s f%d, f%d", i.X.Rc ? -7 : -8, i.type->name,
|
||||
i.X.Rc ? "." : "", i.X.RT, i.X.RB);
|
||||
}
|
||||
void Disasm_A_FRT_FRB(InstrData& i, poly::StringBuffer* str) {
|
||||
str->Append("%*s%s f%d, f%d", i.A.Rc ? -7 : -8, i.type->name,
|
||||
i.A.Rc ? "." : "", i.A.FRT, i.A.FRB);
|
||||
}
|
||||
void Disasm_A_FRT_FRA_FRB(InstrData& i, poly::StringBuffer* str) {
|
||||
str->Append("%*s%s f%d, f%d, f%d", i.A.Rc ? -7 : -8, i.type->name,
|
||||
i.A.Rc ? "." : "", i.A.FRT, i.A.FRA, i.A.FRB);
|
||||
}
|
||||
void Disasm_A_FRT_FRA_FRB_FRC(InstrData& i, poly::StringBuffer* str) {
|
||||
str->Append("%*s%s f%d, f%d, f%d, f%d", i.A.Rc ? -7 : -8, i.type->name,
|
||||
i.A.Rc ? "." : "", i.A.FRT, i.A.FRA, i.A.FRB, i.A.FRC);
|
||||
}
|
||||
void Disasm_X_RT_RA_RB(InstrData& i, poly::StringBuffer* str) {
|
||||
str->Append("%-8s r%d, r%d, r%d", i.type->name, i.X.RT, i.X.RA, i.X.RB);
|
||||
}
|
||||
void Disasm_X_RT_RA0_RB(InstrData& i, poly::StringBuffer* str) {
|
||||
if (i.X.RA) {
|
||||
str->Append("%-8s r%d, r%d, r%d", i.type->name, i.X.RT, i.X.RA, i.X.RB);
|
||||
} else {
|
||||
str->Append("%-8s r%d, 0, r%d", i.type->name, i.X.RT, i.X.RB);
|
||||
}
|
||||
}
|
||||
void Disasm_X_FRT_RA_RB(InstrData& i, poly::StringBuffer* str) {
|
||||
str->Append("%-8s f%d, r%d, r%d", i.type->name, i.X.RT, i.X.RA, i.X.RB);
|
||||
}
|
||||
void Disasm_X_FRT_RA0_RB(InstrData& i, poly::StringBuffer* str) {
|
||||
if (i.X.RA) {
|
||||
str->Append("%-8s f%d, r%d, r%d", i.type->name, i.X.RT, i.X.RA, i.X.RB);
|
||||
} else {
|
||||
str->Append("%-8s f%d, 0, r%d", i.type->name, i.X.RT, i.X.RB);
|
||||
}
|
||||
}
|
||||
void Disasm_D_RT_RA_I(InstrData& i, poly::StringBuffer* str) {
|
||||
str->Append("%-8s r%d, r%d, %d", i.type->name, i.D.RT, i.D.RA,
|
||||
(int32_t)(int16_t) XEEXTS16(i.D.DS));
|
||||
}
|
||||
void Disasm_D_RT_RA0_I(InstrData& i, poly::StringBuffer* str) {
|
||||
if (i.D.RA) {
|
||||
str->Append("%-8s r%d, r%d, %d", i.type->name, i.D.RT, i.D.RA,
|
||||
(int32_t)(int16_t) XEEXTS16(i.D.DS));
|
||||
} else {
|
||||
str->Append("%-8s r%d, 0, %d", i.type->name, i.D.RT,
|
||||
(int32_t)(int16_t) XEEXTS16(i.D.DS));
|
||||
}
|
||||
}
|
||||
void Disasm_D_FRT_RA_I(InstrData& i, poly::StringBuffer* str) {
|
||||
str->Append("%-8s f%d, r%d, %d", i.type->name, i.D.RT, i.D.RA,
|
||||
(int32_t)(int16_t) XEEXTS16(i.D.DS));
|
||||
}
|
||||
void Disasm_D_FRT_RA0_I(InstrData& i, poly::StringBuffer* str) {
|
||||
if (i.D.RA) {
|
||||
str->Append("%-8s f%d, r%d, %d", i.type->name, i.D.RT, i.D.RA,
|
||||
(int32_t)(int16_t) XEEXTS16(i.D.DS));
|
||||
} else {
|
||||
str->Append("%-8s f%d, 0, %d", i.type->name, i.D.RT,
|
||||
(int32_t)(int16_t) XEEXTS16(i.D.DS));
|
||||
}
|
||||
}
|
||||
void Disasm_DS_RT_RA_I(InstrData& i, poly::StringBuffer* str) {
|
||||
str->Append("%-8s r%d, r%d, %d", i.type->name, i.DS.RT, i.DS.RA,
|
||||
(int32_t)(int16_t) XEEXTS16(i.DS.DS << 2));
|
||||
}
|
||||
void Disasm_DS_RT_RA0_I(InstrData& i, poly::StringBuffer* str) {
|
||||
if (i.DS.RA) {
|
||||
str->Append("%-8s r%d, r%d, %d", i.type->name, i.DS.RT, i.DS.RA,
|
||||
(int32_t)(int16_t) XEEXTS16(i.DS.DS << 2));
|
||||
} else {
|
||||
str->Append("%-8s r%d, 0, %d", i.type->name, i.DS.RT,
|
||||
(int32_t)(int16_t) XEEXTS16(i.DS.DS << 2));
|
||||
}
|
||||
}
|
||||
void Disasm_D_RA(InstrData& i, poly::StringBuffer* str) {
|
||||
str->Append("%-8s r%d", i.type->name, i.D.RA);
|
||||
}
|
||||
void Disasm_X_RA_RB(InstrData& i, poly::StringBuffer* str) {
|
||||
str->Append("%-8s r%d, r%d", i.type->name, i.X.RA, i.X.RB);
|
||||
}
|
||||
void Disasm_XO_RT_RA_RB(InstrData& i, poly::StringBuffer* str) {
|
||||
str->Append("%*s%s%s r%d, r%d, r%d", i.XO.Rc ? -7 : -8, i.type->name,
|
||||
i.XO.OE ? "o" : "", i.XO.Rc ? "." : "", i.XO.RT, i.XO.RA,
|
||||
i.XO.RB);
|
||||
}
|
||||
void Disasm_XO_RT_RA(InstrData& i, poly::StringBuffer* str) {
|
||||
str->Append("%*s%s%s r%d, r%d", i.XO.Rc ? -7 : -8, i.type->name,
|
||||
i.XO.OE ? "o" : "", i.XO.Rc ? "." : "", i.XO.RT, i.XO.RA);
|
||||
}
|
||||
void Disasm_X_RA_RT_RB(InstrData& i, poly::StringBuffer* str) {
|
||||
str->Append("%*s%s r%d, r%d, r%d", i.X.Rc ? -7 : -8, i.type->name,
|
||||
i.X.Rc ? "." : "", i.X.RA, i.X.RT, i.X.RB);
|
||||
}
|
||||
void Disasm_D_RA_RT_I(InstrData& i, poly::StringBuffer* str) {
|
||||
str->Append("%-7s. r%d, r%d, %.4Xh", i.type->name, i.D.RA, i.D.RT, i.D.DS);
|
||||
}
|
||||
void Disasm_X_RA_RT(InstrData& i, poly::StringBuffer* str) {
|
||||
str->Append("%*s%s r%d, r%d", i.X.Rc ? -7 : -8, i.type->name,
|
||||
i.X.Rc ? "." : "", i.X.RA, i.X.RT);
|
||||
}
|
||||
|
||||
#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_VD128 (i.VX128.VD128l | (i.VX128.VD128h << 5))
|
||||
#define VX128_VA128 \
|
||||
(i.VX128.VA128l | (i.VX128.VA128h << 5) | (i.VX128.VA128H << 6))
|
||||
#define VX128_VB128 (i.VX128.VB128l | (i.VX128.VB128h << 5))
|
||||
#define VX128_1_VD128 (i.VX128_1.VD128l | (i.VX128_1.VD128h << 5))
|
||||
#define VX128_2_VD128 (i.VX128_2.VD128l | (i.VX128_2.VD128h << 5))
|
||||
#define VX128_2_VA128 \
|
||||
(i.VX128_2.VA128l | (i.VX128_2.VA128h << 5) | (i.VX128_2.VA128H << 6))
|
||||
#define VX128_2_VB128 (i.VX128_2.VB128l | (i.VX128_2.VB128h << 5))
|
||||
#define VX128_2_VC (i.VX128_2.VC)
|
||||
#define VX128_3_VD128 (i.VX128_3.VD128l | (i.VX128_3.VD128h << 5))
|
||||
#define VX128_3_VB128 (i.VX128_3.VB128l | (i.VX128_3.VB128h << 5))
|
||||
#define VX128_3_IMM (i.VX128_3.IMM)
|
||||
#define VX128_4_VD128 (i.VX128_4.VD128l | (i.VX128_4.VD128h << 5))
|
||||
#define VX128_4_VB128 (i.VX128_4.VB128l | (i.VX128_4.VB128h << 5))
|
||||
#define VX128_5_VD128 (i.VX128_5.VD128l | (i.VX128_5.VD128h << 5))
|
||||
#define VX128_5_VA128 \
|
||||
(i.VX128_5.VA128l | (i.VX128_5.VA128h << 5)) | (i.VX128_5.VA128H << 6)
|
||||
#define VX128_5_VB128 (i.VX128_5.VB128l | (i.VX128_5.VB128h << 5))
|
||||
#define VX128_5_SH (i.VX128_5.SH)
|
||||
#define VX128_R_VD128 (i.VX128_R.VD128l | (i.VX128_R.VD128h << 5))
|
||||
#define VX128_R_VA128 \
|
||||
(i.VX128_R.VA128l | (i.VX128_R.VA128h << 5) | (i.VX128_R.VA128H << 6))
|
||||
#define VX128_R_VB128 (i.VX128_R.VB128l | (i.VX128_R.VB128h << 5))
|
||||
|
||||
void Disasm_X_VX_RA0_RB(InstrData& i, poly::StringBuffer* str) {
|
||||
if (i.X.RA) {
|
||||
str->Append("%-8s v%d, r%d, r%d", i.type->name, i.X.RT, i.X.RA, i.X.RB);
|
||||
} else {
|
||||
str->Append("%-8s v%d, 0, r%d", i.type->name, i.X.RT, i.X.RB);
|
||||
}
|
||||
}
|
||||
void Disasm_VX1281_VD_RA0_RB(InstrData& i, poly::StringBuffer* str) {
|
||||
const uint32_t vd = VX128_1_VD128;
|
||||
if (i.VX128_1.RA) {
|
||||
str->Append("%-8s v%d, r%d, r%d", i.type->name, vd, i.VX128_1.RA,
|
||||
i.VX128_1.RB);
|
||||
} else {
|
||||
str->Append("%-8s v%d, 0, r%d", i.type->name, vd, i.VX128_1.RB);
|
||||
}
|
||||
}
|
||||
void Disasm_VX1283_VD_VB(InstrData& i, poly::StringBuffer* str) {
|
||||
const uint32_t vd = VX128_3_VD128;
|
||||
const uint32_t vb = VX128_3_VB128;
|
||||
str->Append("%-8s v%d, v%d", i.type->name, vd, vb);
|
||||
}
|
||||
void Disasm_VX1283_VD_VB_I(InstrData& i, poly::StringBuffer* str) {
|
||||
const uint32_t vd = VX128_VD128;
|
||||
const uint32_t va = VX128_VA128;
|
||||
const uint32_t uimm = i.VX128_3.IMM;
|
||||
str->Append("%-8s v%d, v%d, %.2Xh", i.type->name, vd, va, uimm);
|
||||
}
|
||||
void Disasm_VX_VD_VA_VB(InstrData& i, poly::StringBuffer* str) {
|
||||
str->Append("%-8s v%d, v%d, v%d", i.type->name, i.VX.VD, i.VX.VA, i.VX.VB);
|
||||
}
|
||||
void Disasm_VX128_VD_VA_VB(InstrData& i, poly::StringBuffer* str) {
|
||||
const uint32_t vd = VX128_VD128;
|
||||
const uint32_t va = VX128_VA128;
|
||||
const uint32_t vb = VX128_VB128;
|
||||
str->Append("%-8s v%d, v%d, v%d", i.type->name, vd, va, vb);
|
||||
}
|
||||
void Disasm_VX128_VD_VA_VD_VB(InstrData& i, poly::StringBuffer* str) {
|
||||
const uint32_t vd = VX128_VD128;
|
||||
const uint32_t va = VX128_VA128;
|
||||
const uint32_t vb = VX128_VB128;
|
||||
str->Append("%-8s v%d, v%d, v%d, v%d", i.type->name, vd, va, vd, vb);
|
||||
}
|
||||
void Disasm_VX1282_VD_VA_VB_VC(InstrData& i, poly::StringBuffer* str) {
|
||||
const uint32_t vd = VX128_2_VD128;
|
||||
const uint32_t va = VX128_2_VA128;
|
||||
const uint32_t vb = VX128_2_VB128;
|
||||
const uint32_t vc = i.VX128_2.VC;
|
||||
str->Append("%-8s v%d, v%d, v%d, v%d", i.type->name, vd, va, vb, vc);
|
||||
}
|
||||
void Disasm_VXA_VD_VA_VB_VC(InstrData& i, poly::StringBuffer* str) {
|
||||
str->Append("%-8s v%d, v%d, v%d, v%d", i.type->name, i.VXA.VD, i.VXA.VA,
|
||||
i.VXA.VB, i.VXA.VC);
|
||||
}
|
||||
|
||||
void Disasm_sync(InstrData& i, poly::StringBuffer* str) {
|
||||
const char* name;
|
||||
int L = i.X.RT & 3;
|
||||
switch (L) {
|
||||
case 0:
|
||||
name = "hwsync";
|
||||
break;
|
||||
case 1:
|
||||
name = "lwsync";
|
||||
break;
|
||||
default:
|
||||
case 2:
|
||||
case 3:
|
||||
name = "sync";
|
||||
break;
|
||||
}
|
||||
str->Append("%-8s %.2X", name, L);
|
||||
}
|
||||
|
||||
void Disasm_dcbf(InstrData& i, poly::StringBuffer* str) {
|
||||
const char* name;
|
||||
switch (i.X.RT & 3) {
|
||||
case 0:
|
||||
name = "dcbf";
|
||||
break;
|
||||
case 1:
|
||||
name = "dcbfl";
|
||||
break;
|
||||
case 2:
|
||||
name = "dcbf.RESERVED";
|
||||
break;
|
||||
case 3:
|
||||
name = "dcbflp";
|
||||
break;
|
||||
default:
|
||||
name = "dcbf.??";
|
||||
break;
|
||||
}
|
||||
str->Append("%-8s r%d, r%d", name, i.X.RA, i.X.RB);
|
||||
}
|
||||
|
||||
void Disasm_dcbz(InstrData& i, poly::StringBuffer* str) {
|
||||
// or dcbz128 0x7C2007EC
|
||||
if (i.X.RA) {
|
||||
str->Append("%-8s r%d, r%d", i.type->name, i.X.RA, i.X.RB);
|
||||
} else {
|
||||
str->Append("%-8s 0, r%d", i.type->name, i.X.RB);
|
||||
}
|
||||
}
|
||||
|
||||
void Disasm_fcmp(InstrData& i, poly::StringBuffer* str) {
|
||||
str->Append("%-8s cr%d, f%d, f%d", i.type->name, i.X.RT >> 2, i.X.RA, i.X.RB);
|
||||
}
|
||||
|
||||
void Disasm_mffsx(InstrData& i, poly::StringBuffer* str) {
|
||||
str->Append("%*s%s f%d, FPSCR", i.X.Rc ? -7 : -8, i.type->name,
|
||||
i.X.Rc ? "." : "", i.X.RT);
|
||||
}
|
||||
|
||||
void Disasm_bx(InstrData& i, poly::StringBuffer* str) {
|
||||
const char* name = i.I.LK ? "bl" : "b";
|
||||
uint32_t nia;
|
||||
if (i.I.AA) {
|
||||
nia = (uint32_t)XEEXTS26(i.I.LI << 2);
|
||||
} else {
|
||||
nia = (uint32_t)(i.address + XEEXTS26(i.I.LI << 2));
|
||||
}
|
||||
str->Append("%-8s %.8X", name, nia);
|
||||
// TODO(benvanik): resolve target name?
|
||||
}
|
||||
void Disasm_bcx(InstrData& i, poly::StringBuffer* str) {
|
||||
const char* s0 = i.B.LK ? "lr, " : "";
|
||||
const char* s1;
|
||||
if (!select_bits(i.B.BO, 2, 2)) {
|
||||
s1 = "ctr, ";
|
||||
} else {
|
||||
s1 = "";
|
||||
}
|
||||
char s2[8] = {0};
|
||||
if (!select_bits(i.B.BO, 4, 4)) {
|
||||
snprintf(s2, poly::countof(s2), "cr%d, ", i.B.BI >> 2);
|
||||
}
|
||||
uint32_t nia;
|
||||
if (i.B.AA) {
|
||||
nia = (uint32_t)XEEXTS16(i.B.BD << 2);
|
||||
} else {
|
||||
nia = (uint32_t)(i.address + XEEXTS16(i.B.BD << 2));
|
||||
}
|
||||
str->Append("%-8s %s%s%s%.8X", i.type->name, s0, s1, s2, nia);
|
||||
// TODO(benvanik): resolve target name?
|
||||
}
|
||||
void Disasm_bcctrx(InstrData& i, poly::StringBuffer* str) {
|
||||
// TODO(benvanik): mnemonics
|
||||
const char* s0 = i.XL.LK ? "lr, " : "";
|
||||
char s2[8] = {0};
|
||||
if (!select_bits(i.XL.BO, 4, 4)) {
|
||||
snprintf(s2, poly::countof(s2), "cr%d, ", i.XL.BI >> 2);
|
||||
}
|
||||
str->Append("%-8s %s%sctr", i.type->name, s0, s2);
|
||||
// TODO(benvanik): resolve target name?
|
||||
}
|
||||
void Disasm_bclrx(InstrData& i, poly::StringBuffer* str) {
|
||||
const char* name = "bclr";
|
||||
if (i.code == 0x4E800020) {
|
||||
name = "blr";
|
||||
}
|
||||
const char* s1;
|
||||
if (!select_bits(i.XL.BO, 2, 2)) {
|
||||
s1 = "ctr, ";
|
||||
} else {
|
||||
s1 = "";
|
||||
}
|
||||
char s2[8] = {0};
|
||||
if (!select_bits(i.XL.BO, 4, 4)) {
|
||||
snprintf(s2, poly::countof(s2), "cr%d, ", i.XL.BI >> 2);
|
||||
}
|
||||
str->Append("%-8s %s%s", name, s1, s2);
|
||||
}
|
||||
|
||||
void Disasm_mfcr(InstrData& i, poly::StringBuffer* str) {
|
||||
str->Append("%-8s r%d, cr", i.type->name, i.X.RT);
|
||||
}
|
||||
const char* Disasm_spr_name(uint32_t n) {
|
||||
const char* reg = "???";
|
||||
switch (n) {
|
||||
case 1:
|
||||
reg = "xer";
|
||||
break;
|
||||
case 8:
|
||||
reg = "lr";
|
||||
break;
|
||||
case 9:
|
||||
reg = "ctr";
|
||||
break;
|
||||
}
|
||||
return reg;
|
||||
}
|
||||
void Disasm_mfspr(InstrData& i, poly::StringBuffer* str) {
|
||||
const uint32_t n = ((i.XFX.spr & 0x1F) << 5) | ((i.XFX.spr >> 5) & 0x1F);
|
||||
const char* reg = Disasm_spr_name(n);
|
||||
str->Append("%-8s r%d, %s", i.type->name, i.XFX.RT, reg);
|
||||
}
|
||||
void Disasm_mtspr(InstrData& i, poly::StringBuffer* str) {
|
||||
const uint32_t n = ((i.XFX.spr & 0x1F) << 5) | ((i.XFX.spr >> 5) & 0x1F);
|
||||
const char* reg = Disasm_spr_name(n);
|
||||
str->Append("%-8s %s, r%d", i.type->name, reg, i.XFX.RT);
|
||||
}
|
||||
void Disasm_mftb(InstrData& i, poly::StringBuffer* str) {
|
||||
str->Append("%-8s r%d, tb", i.type->name, i.XFX.RT);
|
||||
}
|
||||
void Disasm_mfmsr(InstrData& i, poly::StringBuffer* str) {
|
||||
str->Append("%-8s r%d", i.type->name, i.X.RT);
|
||||
}
|
||||
void Disasm_mtmsr(InstrData& i, poly::StringBuffer* str) {
|
||||
str->Append("%-8s r%d, %d", i.type->name, i.X.RT, (i.X.RA & 16) ? 1 : 0);
|
||||
}
|
||||
|
||||
void Disasm_cmp(InstrData& i, poly::StringBuffer* str) {
|
||||
str->Append("%-8s cr%d, %.2X, r%d, r%d", i.type->name, i.X.RT >> 2,
|
||||
i.X.RT & 1, i.X.RA, i.X.RB);
|
||||
}
|
||||
void Disasm_cmpi(InstrData& i, poly::StringBuffer* str) {
|
||||
str->Append("%-8s cr%d, %.2X, r%d, %d", i.type->name, i.D.RT >> 2, i.D.RT & 1,
|
||||
i.D.RA, XEEXTS16(i.D.DS));
|
||||
}
|
||||
void Disasm_cmpli(InstrData& i, poly::StringBuffer* str) {
|
||||
str->Append("%-8s cr%d, %.2X, r%d, %.2X", i.type->name, i.D.RT >> 2,
|
||||
i.D.RT & 1, i.D.RA, XEEXTS16(i.D.DS));
|
||||
}
|
||||
|
||||
void Disasm_rld(InstrData& i, poly::StringBuffer* str) {
|
||||
if (i.MD.idx == 0) {
|
||||
// XEDISASMR(rldiclx, 0x78000000, MD )
|
||||
str->Append("%*s%s r%d, r%d, %d, %d", i.MD.Rc ? -7 : -8, "rldicl",
|
||||
i.MD.Rc ? "." : "", i.MD.RA, i.MD.RT, (i.MD.SH5 << 5) | i.MD.SH,
|
||||
(i.MD.MB5 << 5) | i.MD.MB);
|
||||
} else if (i.MD.idx == 1) {
|
||||
// XEDISASMR(rldicrx, 0x78000004, MD )
|
||||
str->Append("%*s%s r%d, r%d, %d, %d", i.MD.Rc ? -7 : -8, "rldicr",
|
||||
i.MD.Rc ? "." : "", i.MD.RA, i.MD.RT, (i.MD.SH5 << 5) | i.MD.SH,
|
||||
(i.MD.MB5 << 5) | i.MD.MB);
|
||||
} else if (i.MD.idx == 2) {
|
||||
// XEDISASMR(rldicx, 0x78000008, MD )
|
||||
uint32_t sh = (i.MD.SH5 << 5) | i.MD.SH;
|
||||
uint32_t mb = (i.MD.MB5 << 5) | i.MD.MB;
|
||||
const char* name = (mb == 0x3E) ? "sldi" : "rldic";
|
||||
str->Append("%*s%s r%d, r%d, %d, %d", i.MD.Rc ? -7 : -8, name,
|
||||
i.MD.Rc ? "." : "", i.MD.RA, i.MD.RT, sh, mb);
|
||||
} else if (i.MDS.idx == 8) {
|
||||
// XEDISASMR(rldclx, 0x78000010, MDS)
|
||||
str->Append("%*s%s r%d, r%d, %d, %d", i.MDS.Rc ? -7 : -8, "rldcl",
|
||||
i.MDS.Rc ? "." : "", i.MDS.RA, i.MDS.RT, i.MDS.RB,
|
||||
(i.MDS.MB5 << 5) | i.MDS.MB);
|
||||
} else if (i.MDS.idx == 9) {
|
||||
// XEDISASMR(rldcrx, 0x78000012, MDS)
|
||||
str->Append("%*s%s r%d, r%d, %d, %d", i.MDS.Rc ? -7 : -8, "rldcr",
|
||||
i.MDS.Rc ? "." : "", i.MDS.RA, i.MDS.RT, i.MDS.RB,
|
||||
(i.MDS.MB5 << 5) | i.MDS.MB);
|
||||
} else if (i.MD.idx == 3) {
|
||||
// XEDISASMR(rldimix, 0x7800000C, MD )
|
||||
str->Append("%*s%s r%d, r%d, %d, %d", i.MD.Rc ? -7 : -8, "rldimi",
|
||||
i.MD.Rc ? "." : "", i.MD.RA, i.MD.RT, (i.MD.SH5 << 5) | i.MD.SH,
|
||||
(i.MD.MB5 << 5) | i.MD.MB);
|
||||
} else {
|
||||
assert_always();
|
||||
}
|
||||
}
|
||||
void Disasm_rlwim(InstrData& i, poly::StringBuffer* str) {
|
||||
str->Append("%*s%s r%d, r%d, %d, %d, %d", i.M.Rc ? -7 : -8, i.type->name,
|
||||
i.M.Rc ? "." : "", i.M.RA, i.M.RT, i.M.SH, i.M.MB, i.M.ME);
|
||||
}
|
||||
void Disasm_rlwnmx(InstrData& i, poly::StringBuffer* str) {
|
||||
str->Append("%*s%s r%d, r%d, r%d, %d, %d", i.M.Rc ? -7 : -8, i.type->name,
|
||||
i.M.Rc ? "." : "", i.M.RA, i.M.RT, i.M.SH, i.M.MB, i.M.ME);
|
||||
}
|
||||
void Disasm_srawix(InstrData& i, poly::StringBuffer* str) {
|
||||
str->Append("%*s%s r%d, r%d, %d", i.X.Rc ? -7 : -8, i.type->name,
|
||||
i.X.Rc ? "." : "", i.X.RA, i.X.RT, i.X.RB);
|
||||
}
|
||||
void Disasm_sradix(InstrData& i, poly::StringBuffer* str) {
|
||||
str->Append("%*s%s r%d, r%d, %d", i.XS.Rc ? -7 : -8, i.type->name,
|
||||
i.XS.Rc ? "." : "", i.XS.RA, i.XS.RT, (i.XS.SH5 << 5) | i.XS.SH);
|
||||
}
|
||||
|
||||
void Disasm_vpermwi128(InstrData& i, poly::StringBuffer* str) {
|
||||
const uint32_t vd = i.VX128_P.VD128l | (i.VX128_P.VD128h << 5);
|
||||
const uint32_t vb = i.VX128_P.VB128l | (i.VX128_P.VB128h << 5);
|
||||
str->Append("%-8s v%d, v%d, %.2X", i.type->name, vd, vb,
|
||||
i.VX128_P.PERMl | (i.VX128_P.PERMh << 5));
|
||||
}
|
||||
void Disasm_vrfin128(InstrData& i, poly::StringBuffer* str) {
|
||||
const uint32_t vd = VX128_3_VD128;
|
||||
const uint32_t vb = VX128_3_VB128;
|
||||
str->Append("%-8s v%d, v%d", i.type->name, vd, vb);
|
||||
}
|
||||
void Disasm_vrlimi128(InstrData& i, poly::StringBuffer* str) {
|
||||
const uint32_t vd = VX128_4_VD128;
|
||||
const uint32_t vb = VX128_4_VB128;
|
||||
str->Append("%-8s v%d, v%d, %.2X, %.2X", i.type->name, vd, vb, i.VX128_4.IMM,
|
||||
i.VX128_4.z);
|
||||
}
|
||||
void Disasm_vsldoi128(InstrData& i, poly::StringBuffer* str) {
|
||||
const uint32_t vd = VX128_5_VD128;
|
||||
const uint32_t va = VX128_5_VA128;
|
||||
const uint32_t vb = VX128_5_VB128;
|
||||
const uint32_t sh = i.VX128_5.SH;
|
||||
str->Append("%-8s v%d, v%d, v%d, %.2X", i.type->name, vd, va, vb, sh);
|
||||
}
|
||||
void Disasm_vspltb(InstrData& i, poly::StringBuffer* str) {
|
||||
str->Append("%-8s v%d, v%d, %.2X", i.type->name, i.VX.VD, i.VX.VB,
|
||||
i.VX.VA & 0xF);
|
||||
}
|
||||
void Disasm_vsplth(InstrData& i, poly::StringBuffer* str) {
|
||||
str->Append("%-8s v%d, v%d, %.2X", i.type->name, i.VX.VD, i.VX.VB,
|
||||
i.VX.VA & 0x7);
|
||||
}
|
||||
void Disasm_vspltw(InstrData& i, poly::StringBuffer* str) {
|
||||
str->Append("%-8s v%d, v%d, %.2X", i.type->name, i.VX.VD, i.VX.VB, i.VX.VA);
|
||||
}
|
||||
void Disasm_vspltisb(InstrData& i, poly::StringBuffer* str) {
|
||||
// 5bit -> 8bit sign extend
|
||||
int8_t simm = (i.VX.VA & 0x10) ? (i.VX.VA | 0xF0) : i.VX.VA;
|
||||
str->Append("%-8s v%d, %.2X", i.type->name, i.VX.VD, simm);
|
||||
}
|
||||
void Disasm_vspltish(InstrData& i, poly::StringBuffer* str) {
|
||||
// 5bit -> 16bit sign extend
|
||||
int16_t simm = (i.VX.VA & 0x10) ? (i.VX.VA | 0xFFF0) : i.VX.VA;
|
||||
str->Append("%-8s v%d, %.4X", i.type->name, i.VX.VD, simm);
|
||||
}
|
||||
void Disasm_vspltisw(InstrData& i, poly::StringBuffer* str) {
|
||||
// 5bit -> 32bit sign extend
|
||||
int32_t simm = (i.VX.VA & 0x10) ? (i.VX.VA | 0xFFFFFFF0) : i.VX.VA;
|
||||
str->Append("%-8s v%d, %.8X", i.type->name, i.VX.VD, simm);
|
||||
}
|
||||
|
||||
int DisasmPPC(InstrData& i, poly::StringBuffer* str) {
|
||||
if (!i.type) {
|
||||
str->Append("???");
|
||||
} else {
|
||||
i.type->disasm(i, str);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace ppc
|
||||
} // namespace frontend
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
28
src/xenia/cpu/frontend/ppc/ppc_disasm.h
Normal file
28
src/xenia/cpu/frontend/ppc/ppc_disasm.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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_FRONTEND_PPC_PPC_DISASM_H_
|
||||
#define XENIA_FRONTEND_PPC_PPC_DISASM_H_
|
||||
|
||||
#include "xenia/cpu/frontend/ppc/ppc_instr.h"
|
||||
#include "poly/string_buffer.h"
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace frontend {
|
||||
namespace ppc {
|
||||
|
||||
int DisasmPPC(InstrData& i, poly::StringBuffer* str);
|
||||
|
||||
} // namespace ppc
|
||||
} // namespace frontend
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_FRONTEND_PPC_PPC_DISASM_H_
|
||||
35
src/xenia/cpu/frontend/ppc/ppc_emit-private.h
Normal file
35
src/xenia/cpu/frontend/ppc/ppc_emit-private.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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_FRONTEND_PPC_PPC_EMIT_PRIVATE_H_
|
||||
#define XENIA_FRONTEND_PPC_PPC_EMIT_PRIVATE_H_
|
||||
|
||||
#include "xenia/cpu/frontend/ppc/ppc_emit.h"
|
||||
#include "xenia/cpu/frontend/ppc/ppc_instr.h"
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace frontend {
|
||||
namespace ppc {
|
||||
|
||||
#define XEEMITTER(name, opcode, format) int InstrEmit_##name
|
||||
|
||||
#define XEREGISTERINSTR(name, opcode) \
|
||||
RegisterInstrEmit(opcode, (InstrEmitFn)InstrEmit_##name);
|
||||
|
||||
//#define XEINSTRNOTIMPLEMENTED()
|
||||
#define XEINSTRNOTIMPLEMENTED() assert_always("Instruction not implemented");
|
||||
//#define XEINSTRNOTIMPLEMENTED() __debugbreak()
|
||||
|
||||
} // namespace ppc
|
||||
} // namespace frontend
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_FRONTEND_PPC_PPC_EMIT_PRIVATE_H_
|
||||
31
src/xenia/cpu/frontend/ppc/ppc_emit.h
Normal file
31
src/xenia/cpu/frontend/ppc/ppc_emit.h
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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_FRONTEND_PPC_PPC_EMIT_H_
|
||||
#define XENIA_FRONTEND_PPC_PPC_EMIT_H_
|
||||
|
||||
#include "xenia/cpu/frontend/ppc/ppc_instr.h"
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace frontend {
|
||||
namespace ppc {
|
||||
|
||||
void RegisterEmitCategoryAltivec();
|
||||
void RegisterEmitCategoryALU();
|
||||
void RegisterEmitCategoryControl();
|
||||
void RegisterEmitCategoryFPU();
|
||||
void RegisterEmitCategoryMemory();
|
||||
|
||||
} // namespace ppc
|
||||
} // namespace frontend
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_FRONTEND_PPC_PPC_EMIT_H_
|
||||
2414
src/xenia/cpu/frontend/ppc/ppc_emit_altivec.cc
Normal file
2414
src/xenia/cpu/frontend/ppc/ppc_emit_altivec.cc
Normal file
File diff suppressed because it is too large
Load Diff
1276
src/xenia/cpu/frontend/ppc/ppc_emit_alu.cc
Normal file
1276
src/xenia/cpu/frontend/ppc/ppc_emit_alu.cc
Normal file
File diff suppressed because it is too large
Load Diff
754
src/xenia/cpu/frontend/ppc/ppc_emit_control.cc
Normal file
754
src/xenia/cpu/frontend/ppc/ppc_emit_control.cc
Normal file
@@ -0,0 +1,754 @@
|
||||
/*
|
||||
******************************************************************************
|
||||
* 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/frontend/ppc/ppc_emit-private.h"
|
||||
|
||||
#include "xenia/cpu/frontend/ppc/ppc_context.h"
|
||||
#include "xenia/cpu/frontend/ppc/ppc_hir_builder.h"
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace frontend {
|
||||
namespace ppc {
|
||||
|
||||
// TODO(benvanik): remove when enums redefined.
|
||||
using namespace xe::cpu::hir;
|
||||
|
||||
using xe::cpu::hir::Label;
|
||||
using xe::cpu::hir::Value;
|
||||
|
||||
int InstrEmit_branch(PPCHIRBuilder& f, const char* src, uint64_t cia,
|
||||
Value* nia, bool lk, Value* cond = NULL,
|
||||
bool expect_true = true, bool nia_is_lr = false) {
|
||||
uint32_t call_flags = 0;
|
||||
|
||||
// TODO(benvanik): this may be wrong and overwrite LRs when not desired!
|
||||
// The docs say always, though...
|
||||
// Note that we do the update before we branch/call as we need it to
|
||||
// be correct for returns.
|
||||
if (lk) {
|
||||
Value* return_address = f.LoadConstant(cia + 4);
|
||||
f.SetReturnAddress(return_address);
|
||||
f.StoreLR(return_address);
|
||||
}
|
||||
|
||||
if (!lk) {
|
||||
// If LR is not set this call will never return here.
|
||||
call_flags |= CALL_TAIL;
|
||||
}
|
||||
|
||||
// TODO(benvanik): set CALL_TAIL if !lk and the last block in the fn.
|
||||
// This is almost always a jump to restore gpr.
|
||||
|
||||
if (nia->IsConstant()) {
|
||||
// Direct branch to address.
|
||||
// If it's a block inside of ourself, setup a fast jump.
|
||||
// Unless it's to ourselves directly, in which case it's
|
||||
// recursion.
|
||||
uint64_t nia_value = nia->AsUint64() & 0xFFFFFFFF;
|
||||
bool is_recursion = false;
|
||||
if (nia_value == f.symbol_info()->address() && lk) {
|
||||
is_recursion = true;
|
||||
}
|
||||
Label* label = is_recursion ? NULL : f.LookupLabel(nia_value);
|
||||
if (label) {
|
||||
// Branch to label.
|
||||
uint32_t branch_flags = 0;
|
||||
if (cond) {
|
||||
if (expect_true) {
|
||||
f.BranchTrue(cond, label, branch_flags);
|
||||
} else {
|
||||
f.BranchFalse(cond, label, branch_flags);
|
||||
}
|
||||
} else {
|
||||
f.Branch(label, branch_flags);
|
||||
}
|
||||
} else {
|
||||
// Call function.
|
||||
auto symbol_info = f.LookupFunction(nia_value);
|
||||
if (cond) {
|
||||
if (!expect_true) {
|
||||
cond = f.IsFalse(cond);
|
||||
}
|
||||
f.CallTrue(cond, symbol_info, call_flags);
|
||||
} else {
|
||||
f.Call(symbol_info, call_flags);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Indirect branch to pointer.
|
||||
|
||||
// TODO(benvanik): runtime recursion detection?
|
||||
|
||||
// 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!
|
||||
|
||||
//// 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);
|
||||
//}
|
||||
#if 0
|
||||
// This breaks longjump, as that uses blr with a non-return lr.
|
||||
// It'd be nice to move SET_RETURN_ADDRESS semantics up into context
|
||||
// so that we can just use this.
|
||||
if (!lk && nia_is_lr) {
|
||||
// Return (most likely).
|
||||
// TODO(benvanik): test? ReturnCheck()?
|
||||
if (cond) {
|
||||
if (!expect_true) {
|
||||
cond = f.IsFalse(cond);
|
||||
}
|
||||
f.ReturnTrue(cond);
|
||||
} else {
|
||||
f.Return();
|
||||
}
|
||||
} else {
|
||||
#else
|
||||
{
|
||||
#endif
|
||||
// Jump to pointer.
|
||||
bool likely_return = !lk && nia_is_lr;
|
||||
if (likely_return) {
|
||||
call_flags |= CALL_POSSIBLE_RETURN;
|
||||
}
|
||||
if (cond) {
|
||||
if (!expect_true) {
|
||||
cond = f.IsFalse(cond);
|
||||
}
|
||||
f.CallIndirectTrue(cond, nia, call_flags);
|
||||
} else {
|
||||
f.CallIndirect(nia, call_flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(bx, 0x48000000, I)(PPCHIRBuilder& f, 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 = (uint32_t)XEEXTS26(i.I.LI << 2);
|
||||
} else {
|
||||
nia = (uint32_t)(i.address + XEEXTS26(i.I.LI << 2));
|
||||
}
|
||||
|
||||
return InstrEmit_branch(f, "bx", i.address, f.LoadConstant(nia), i.I.LK);
|
||||
}
|
||||
|
||||
XEEMITTER(bcx, 0x40000000, B)(PPCHIRBuilder& f, 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)
|
||||
|
||||
Value* ctr_ok = NULL;
|
||||
if (select_bits(i.B.BO, 2, 2)) {
|
||||
// Ignore ctr.
|
||||
} else {
|
||||
// Decrement counter.
|
||||
Value* ctr = f.LoadCTR();
|
||||
ctr = f.Sub(ctr, f.LoadConstant((int64_t)1));
|
||||
f.StoreCTR(ctr);
|
||||
// Ctr check.
|
||||
ctr = f.Truncate(ctr, INT32_TYPE);
|
||||
// TODO(benvanik): could do something similar to cond and avoid the
|
||||
// is_true/branch_true pairing.
|
||||
if (select_bits(i.B.BO, 1, 1)) {
|
||||
ctr_ok = f.IsFalse(ctr);
|
||||
} else {
|
||||
ctr_ok = f.IsTrue(ctr);
|
||||
}
|
||||
}
|
||||
|
||||
Value* cond_ok = NULL;
|
||||
bool not_cond_ok = false;
|
||||
if (select_bits(i.B.BO, 4, 4)) {
|
||||
// Ignore cond.
|
||||
} else {
|
||||
Value* cr = f.LoadCRField(i.B.BI >> 2, i.B.BI & 3);
|
||||
cond_ok = cr;
|
||||
if (select_bits(i.B.BO, 3, 3)) {
|
||||
// Expect true.
|
||||
not_cond_ok = false;
|
||||
} else {
|
||||
// Expect false.
|
||||
not_cond_ok = true;
|
||||
}
|
||||
}
|
||||
|
||||
// We do a bit of optimization here to make the llvm assembly easier to read.
|
||||
Value* ok = NULL;
|
||||
bool expect_true = true;
|
||||
if (ctr_ok && cond_ok) {
|
||||
if (not_cond_ok) {
|
||||
cond_ok = f.IsFalse(cond_ok);
|
||||
}
|
||||
ok = f.And(ctr_ok, cond_ok);
|
||||
} else if (ctr_ok) {
|
||||
ok = ctr_ok;
|
||||
} else if (cond_ok) {
|
||||
ok = cond_ok;
|
||||
expect_true = !not_cond_ok;
|
||||
}
|
||||
|
||||
uint32_t nia;
|
||||
if (i.B.AA) {
|
||||
nia = (uint32_t)XEEXTS16(i.B.BD << 2);
|
||||
} else {
|
||||
nia = (uint32_t)(i.address + XEEXTS16(i.B.BD << 2));
|
||||
}
|
||||
return InstrEmit_branch(f, "bcx", i.address, f.LoadConstant(nia), i.B.LK, ok,
|
||||
expect_true);
|
||||
}
|
||||
|
||||
XEEMITTER(bcctrx, 0x4C000420, XL)(PPCHIRBuilder& f, 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)
|
||||
|
||||
Value* cond_ok = NULL;
|
||||
bool not_cond_ok = false;
|
||||
if (select_bits(i.XL.BO, 4, 4)) {
|
||||
// Ignore cond.
|
||||
} else {
|
||||
Value* cr = f.LoadCRField(i.XL.BI >> 2, i.XL.BI & 3);
|
||||
cond_ok = cr;
|
||||
if (select_bits(i.XL.BO, 3, 3)) {
|
||||
// Expect true.
|
||||
not_cond_ok = false;
|
||||
} else {
|
||||
// Expect false.
|
||||
not_cond_ok = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool expect_true = !not_cond_ok;
|
||||
return InstrEmit_branch(f, "bcctrx", i.address, f.LoadCTR(), i.XL.LK, cond_ok,
|
||||
expect_true);
|
||||
}
|
||||
|
||||
XEEMITTER(bclrx, 0x4C000020, XL)(PPCHIRBuilder& f, 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)
|
||||
|
||||
Value* ctr_ok = NULL;
|
||||
if (select_bits(i.XL.BO, 2, 2)) {
|
||||
// Ignore ctr.
|
||||
} else {
|
||||
// Decrement counter.
|
||||
Value* ctr = f.LoadCTR();
|
||||
ctr = f.Sub(ctr, f.LoadConstant((int64_t)1));
|
||||
f.StoreCTR(ctr);
|
||||
// Ctr check.
|
||||
ctr = f.Truncate(ctr, INT32_TYPE);
|
||||
// TODO(benvanik): could do something similar to cond and avoid the
|
||||
// is_true/branch_true pairing.
|
||||
if (select_bits(i.XL.BO, 1, 1)) {
|
||||
ctr_ok = f.IsFalse(ctr);
|
||||
} else {
|
||||
ctr_ok = f.IsTrue(ctr);
|
||||
}
|
||||
}
|
||||
|
||||
Value* cond_ok = NULL;
|
||||
bool not_cond_ok = false;
|
||||
if (select_bits(i.XL.BO, 4, 4)) {
|
||||
// Ignore cond.
|
||||
} else {
|
||||
Value* cr = f.LoadCRField(i.XL.BI >> 2, i.XL.BI & 3);
|
||||
cond_ok = cr;
|
||||
if (select_bits(i.XL.BO, 3, 3)) {
|
||||
// Expect true.
|
||||
not_cond_ok = false;
|
||||
} else {
|
||||
// Expect false.
|
||||
not_cond_ok = true;
|
||||
}
|
||||
}
|
||||
|
||||
// We do a bit of optimization here to make the llvm assembly easier to read.
|
||||
Value* ok = NULL;
|
||||
bool expect_true = true;
|
||||
if (ctr_ok && cond_ok) {
|
||||
if (not_cond_ok) {
|
||||
cond_ok = f.IsFalse(cond_ok);
|
||||
}
|
||||
ok = f.And(ctr_ok, cond_ok);
|
||||
} else if (ctr_ok) {
|
||||
ok = ctr_ok;
|
||||
} else if (cond_ok) {
|
||||
ok = cond_ok;
|
||||
expect_true = !not_cond_ok;
|
||||
}
|
||||
|
||||
return InstrEmit_branch(f, "bclrx", i.address, f.LoadLR(), i.XL.LK, ok,
|
||||
expect_true, true);
|
||||
}
|
||||
|
||||
// Condition register logical (A-23)
|
||||
|
||||
XEEMITTER(crand, 0x4C000202, XL)(PPCHIRBuilder& f, InstrData& i) {
|
||||
// CR[bt] <- CR[ba] & CR[bb] bt=bo, ba=bi, bb=bb
|
||||
Value* ba = f.LoadCRField(i.XL.BI >> 2, i.XL.BI & 3);
|
||||
Value* bb = f.LoadCRField(i.XL.BB >> 2, i.XL.BB & 3);
|
||||
Value* bt = f.And(ba, bb);
|
||||
f.StoreCRField(i.XL.BO >> 2, i.XL.BO & 3, bt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(crandc, 0x4C000102, XL)(PPCHIRBuilder& f, InstrData& i) {
|
||||
// CR[bt] <- CR[ba] & ¬CR[bb] bt=bo, ba=bi, bb=bb
|
||||
Value* ba = f.LoadCRField(i.XL.BI >> 2, i.XL.BI & 3);
|
||||
Value* bb = f.LoadCRField(i.XL.BB >> 2, i.XL.BB & 3);
|
||||
Value* bt = f.And(ba, f.Not(bb));
|
||||
f.StoreCRField(i.XL.BO >> 2, i.XL.BO & 3, bt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(creqv, 0x4C000242, XL)(PPCHIRBuilder& f, InstrData& i) {
|
||||
// CR[bt] <- CR[ba] == CR[bb] bt=bo, ba=bi, bb=bb
|
||||
Value* ba = f.LoadCRField(i.XL.BI >> 2, i.XL.BI & 3);
|
||||
Value* bb = f.LoadCRField(i.XL.BB >> 2, i.XL.BB & 3);
|
||||
Value* bt = f.CompareEQ(ba, bb);
|
||||
f.StoreCRField(i.XL.BO >> 2, i.XL.BO & 3, bt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(crnand, 0x4C0001C2, XL)(PPCHIRBuilder& f, InstrData& i) {
|
||||
// CR[bt] <- ¬(CR[ba] & CR[bb]) bt=bo, ba=bi, bb=bb
|
||||
Value* ba = f.LoadCRField(i.XL.BI >> 2, i.XL.BI & 3);
|
||||
Value* bb = f.LoadCRField(i.XL.BB >> 2, i.XL.BB & 3);
|
||||
Value* bt = f.Not(f.And(ba, bb));
|
||||
f.StoreCRField(i.XL.BO >> 2, i.XL.BO & 3, bt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(crnor, 0x4C000042, XL)(PPCHIRBuilder& f, InstrData& i) {
|
||||
// CR[bt] <- ¬(CR[ba] | CR[bb]) bt=bo, ba=bi, bb=bb
|
||||
Value* ba = f.LoadCRField(i.XL.BI >> 2, i.XL.BI & 3);
|
||||
Value* bb = f.LoadCRField(i.XL.BB >> 2, i.XL.BB & 3);
|
||||
Value* bt = f.Not(f.Or(ba, bb));
|
||||
f.StoreCRField(i.XL.BO >> 2, i.XL.BO & 3, bt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(cror, 0x4C000382, XL)(PPCHIRBuilder& f, InstrData& i) {
|
||||
// CR[bt] <- CR[ba] | CR[bb] bt=bo, ba=bi, bb=bb
|
||||
Value* ba = f.LoadCRField(i.XL.BI >> 2, i.XL.BI & 3);
|
||||
Value* bb = f.LoadCRField(i.XL.BB >> 2, i.XL.BB & 3);
|
||||
Value* bt = f.Or(ba, bb);
|
||||
f.StoreCRField(i.XL.BO >> 2, i.XL.BO & 3, bt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(crorc, 0x4C000342, XL)(PPCHIRBuilder& f, InstrData& i) {
|
||||
// CR[bt] <- CR[ba] | ¬CR[bb] bt=bo, ba=bi, bb=bb
|
||||
Value* ba = f.LoadCRField(i.XL.BI >> 2, i.XL.BI & 3);
|
||||
Value* bb = f.LoadCRField(i.XL.BB >> 2, i.XL.BB & 3);
|
||||
Value* bt = f.Or(ba, f.Not(bb));
|
||||
f.StoreCRField(i.XL.BO >> 2, i.XL.BO & 3, bt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(crxor, 0x4C000182, XL)(PPCHIRBuilder& f, InstrData& i) {
|
||||
// CR[bt] <- CR[ba] xor CR[bb] bt=bo, ba=bi, bb=bb
|
||||
Value* ba = f.LoadCRField(i.XL.BI >> 2, i.XL.BI & 3);
|
||||
Value* bb = f.LoadCRField(i.XL.BB >> 2, i.XL.BB & 3);
|
||||
Value* bt = f.Xor(ba, bb);
|
||||
f.StoreCRField(i.XL.BO >> 2, i.XL.BO & 3, bt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(mcrf, 0x4C000000, XL)(PPCHIRBuilder& f, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// System linkage (A-24)
|
||||
|
||||
XEEMITTER(sc, 0x44000002, SC)(PPCHIRBuilder& f, InstrData& i) {
|
||||
f.CallExtern(f.symbol_info());
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Trap (A-25)
|
||||
|
||||
int InstrEmit_trap(PPCHIRBuilder& f, InstrData& i, Value* va, Value* vb,
|
||||
uint32_t TO) {
|
||||
// if (a < b) & TO[0] then TRAP
|
||||
// if (a > b) & TO[1] then TRAP
|
||||
// if (a = b) & TO[2] then TRAP
|
||||
// if (a <u b) & TO[3] then TRAP
|
||||
// if (a >u b) & TO[4] then TRAP
|
||||
// Bits swapped:
|
||||
// 01234
|
||||
// 43210
|
||||
if (!TO) {
|
||||
return 0;
|
||||
}
|
||||
Value* v = nullptr;
|
||||
if (TO & (1 << 4)) {
|
||||
// a < b
|
||||
auto cmp = f.CompareSLT(va, vb);
|
||||
v = v ? f.Or(v, cmp) : cmp;
|
||||
}
|
||||
if (TO & (1 << 3)) {
|
||||
// a > b
|
||||
auto cmp = f.CompareSGT(va, vb);
|
||||
v = v ? f.Or(v, cmp) : cmp;
|
||||
}
|
||||
if (TO & (1 << 2)) {
|
||||
// a = b
|
||||
auto cmp = f.CompareEQ(va, vb);
|
||||
v = v ? f.Or(v, cmp) : cmp;
|
||||
}
|
||||
if (TO & (1 << 1)) {
|
||||
// a <u b
|
||||
auto cmp = f.CompareULT(va, vb);
|
||||
v = v ? f.Or(v, cmp) : cmp;
|
||||
}
|
||||
if (TO & (1 << 0)) {
|
||||
// a >u b
|
||||
auto cmp = f.CompareUGT(va, vb);
|
||||
v = v ? f.Or(v, cmp) : cmp;
|
||||
}
|
||||
if (v) {
|
||||
f.TrapTrue(v);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(td, 0x7C000088, X)(PPCHIRBuilder& f, 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
|
||||
Value* ra = f.LoadGPR(i.X.RA);
|
||||
Value* rb = f.LoadGPR(i.X.RB);
|
||||
return InstrEmit_trap(f, i, ra, rb, i.X.RT);
|
||||
}
|
||||
|
||||
XEEMITTER(tdi, 0x08000000, D)(PPCHIRBuilder& f, 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
|
||||
Value* ra = f.LoadGPR(i.D.RA);
|
||||
Value* rb = f.LoadConstant(XEEXTS16(i.D.DS));
|
||||
return InstrEmit_trap(f, i, ra, rb, i.D.RT);
|
||||
}
|
||||
|
||||
XEEMITTER(tw, 0x7C000008, X)(PPCHIRBuilder& f, 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
|
||||
Value* ra =
|
||||
f.SignExtend(f.Truncate(f.LoadGPR(i.X.RA), INT32_TYPE), INT64_TYPE);
|
||||
Value* rb =
|
||||
f.SignExtend(f.Truncate(f.LoadGPR(i.X.RB), INT32_TYPE), INT64_TYPE);
|
||||
return InstrEmit_trap(f, i, ra, rb, i.X.RT);
|
||||
}
|
||||
|
||||
XEEMITTER(twi, 0x0C000000, D)(PPCHIRBuilder& f, 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
|
||||
if (i.D.RA == 0 && i.D.RT == 0x1F) {
|
||||
// This is a special trap. Probably.
|
||||
uint16_t type = (uint16_t)XEEXTS16(i.D.DS);
|
||||
f.Trap(type);
|
||||
return 0;
|
||||
}
|
||||
Value* ra =
|
||||
f.SignExtend(f.Truncate(f.LoadGPR(i.D.RA), INT32_TYPE), INT64_TYPE);
|
||||
Value* rb = f.LoadConstant(XEEXTS16(i.D.DS));
|
||||
return InstrEmit_trap(f, i, ra, rb, i.D.RT);
|
||||
}
|
||||
|
||||
// Processor control (A-26)
|
||||
|
||||
XEEMITTER(mfcr, 0x7C000026, XFX)(PPCHIRBuilder& f, InstrData& i) {
|
||||
// mfocrf RT,FXM
|
||||
// RT <- undefined
|
||||
// count <- 0
|
||||
// do i = 0 to 7
|
||||
// if FXMi = 1 then
|
||||
// n <- i
|
||||
// count <- count + 1
|
||||
// if count = 1 then
|
||||
// RT4un + 32:4un + 35 <- CR4un + 32 : 4un + 35
|
||||
|
||||
// TODO(benvanik): optimize mfcr sequences.
|
||||
// Often look something like this:
|
||||
// mfocrf r11, cr6
|
||||
// not r10, r11
|
||||
// extrwi r3, r10, 1, 26
|
||||
// Could recognize this and only load the appropriate CR bit.
|
||||
|
||||
Value* v;
|
||||
if (i.XFX.spr & (1 << 9)) {
|
||||
uint32_t bits = (i.XFX.spr & 0x1FF) >> 1;
|
||||
int count = 0;
|
||||
int cri = 0;
|
||||
for (int b = 0; b <= 7; ++b) {
|
||||
if (bits & (1 << b)) {
|
||||
cri = 7 - b;
|
||||
++count;
|
||||
}
|
||||
}
|
||||
if (count == 1) {
|
||||
v = f.LoadCR(cri);
|
||||
} else {
|
||||
v = f.LoadZero(INT64_TYPE);
|
||||
}
|
||||
} else {
|
||||
v = f.LoadCR();
|
||||
}
|
||||
f.StoreGPR(i.XFX.RT, v);
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(mfspr, 0x7C0002A6, XFX)(PPCHIRBuilder& f, InstrData& i) {
|
||||
// n <- spr[5:9] || spr[0:4]
|
||||
// if length(SPR(n)) = 64 then
|
||||
// RT <- SPR(n)
|
||||
// else
|
||||
// RT <- i32.0 || SPR(n)
|
||||
Value* v;
|
||||
const uint32_t n = ((i.XFX.spr & 0x1F) << 5) | ((i.XFX.spr >> 5) & 0x1F);
|
||||
switch (n) {
|
||||
case 1:
|
||||
// XER
|
||||
v = f.LoadXER();
|
||||
break;
|
||||
case 8:
|
||||
// LR
|
||||
v = f.LoadLR();
|
||||
break;
|
||||
case 9:
|
||||
// CTR
|
||||
v = f.LoadCTR();
|
||||
break;
|
||||
case 268:
|
||||
// TB
|
||||
v = f.LoadClock();
|
||||
break;
|
||||
case 269:
|
||||
// TBU
|
||||
v = f.Shr(f.LoadClock(), 32);
|
||||
break;
|
||||
default:
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
f.StoreGPR(i.XFX.RT, v);
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(mftb, 0x7C0002E6, XFX)(PPCHIRBuilder& f, InstrData& i) {
|
||||
Value* time = f.LoadClock();
|
||||
const uint32_t n = ((i.XFX.spr & 0x1F) << 5) | ((i.XFX.spr >> 5) & 0x1F);
|
||||
if (n == 268) {
|
||||
// TB - full bits.
|
||||
} else {
|
||||
// TBU - upper bits only.
|
||||
time = f.Shr(time, 32);
|
||||
}
|
||||
f.StoreGPR(i.XFX.RT, time);
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(mtcrf, 0x7C000120, XFX)(PPCHIRBuilder& f, InstrData& i) {
|
||||
// mtocrf FXM,RS
|
||||
// count <- 0
|
||||
// do i = 0 to 7
|
||||
// if FXMi = 1 then
|
||||
// n <- i
|
||||
// count <- count + 1
|
||||
// if count = 1 then
|
||||
// CR4un + 32 : 4un + 35 <- RS4un + 32:4un + 35
|
||||
|
||||
Value* v = f.LoadGPR(i.XFX.RT);
|
||||
if (i.XFX.spr & (1 << 9)) {
|
||||
uint32_t bits = (i.XFX.spr & 0x1FF) >> 1;
|
||||
int count = 0;
|
||||
int cri = 0;
|
||||
for (int b = 0; b <= 7; ++b) {
|
||||
if (bits & (1 << b)) {
|
||||
cri = 7 - b;
|
||||
++count;
|
||||
}
|
||||
}
|
||||
if (count == 1) {
|
||||
f.StoreCR(cri, v);
|
||||
} else {
|
||||
// Invalid; store zero to CR.
|
||||
f.StoreCR(f.LoadZero(INT64_TYPE));
|
||||
}
|
||||
} else {
|
||||
f.StoreCR(v);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(mtspr, 0x7C0003A6, XFX)(PPCHIRBuilder& f, InstrData& i) {
|
||||
// n <- spr[5:9] || spr[0:4]
|
||||
// if length(SPR(n)) = 64 then
|
||||
// SPR(n) <- (RS)
|
||||
// else
|
||||
// SPR(n) <- (RS)[32:63]
|
||||
|
||||
Value* rt = f.LoadGPR(i.XFX.RT);
|
||||
|
||||
const uint32_t n = ((i.XFX.spr & 0x1F) << 5) | ((i.XFX.spr >> 5) & 0x1F);
|
||||
switch (n) {
|
||||
case 1:
|
||||
// XER
|
||||
f.StoreXER(rt);
|
||||
break;
|
||||
case 8:
|
||||
// LR
|
||||
f.StoreLR(rt);
|
||||
break;
|
||||
case 9:
|
||||
// CTR
|
||||
f.StoreCTR(rt);
|
||||
break;
|
||||
default:
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// MSR is used for toggling interrupts (among other things).
|
||||
// We track it here for taking a global processor lock, as lots of lockfree
|
||||
// code requires it. Sequences of mtmsr/lwar/stcw/mtmsr come up a lot, and
|
||||
// without the lock here threads can livelock.
|
||||
|
||||
XEEMITTER(mfmsr, 0x7C0000A6, X)(PPCHIRBuilder& f, InstrData& i) {
|
||||
f.StoreGPR(i.X.RT, f.LoadMSR());
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(mtmsr, 0x7C000124, X)(PPCHIRBuilder& f, InstrData& i) {
|
||||
if (i.X.RA & 0x01) {
|
||||
// L = 1
|
||||
f.StoreMSR(f.ZeroExtend(f.LoadGPR(i.X.RT), INT64_TYPE));
|
||||
return 0;
|
||||
} else {
|
||||
// L = 0
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
XEEMITTER(mtmsrd, 0x7C000164, X)(PPCHIRBuilder& f, InstrData& i) {
|
||||
if (i.X.RA & 0x01) {
|
||||
// L = 1
|
||||
f.StoreMSR(f.LoadGPR(i.X.RT));
|
||||
return 0;
|
||||
} else {
|
||||
// L = 0
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterEmitCategoryControl() {
|
||||
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 frontend
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
559
src/xenia/cpu/frontend/ppc/ppc_emit_fpu.cc
Normal file
559
src/xenia/cpu/frontend/ppc/ppc_emit_fpu.cc
Normal file
@@ -0,0 +1,559 @@
|
||||
/*
|
||||
******************************************************************************
|
||||
* 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/frontend/ppc/ppc_emit-private.h"
|
||||
|
||||
#include "xenia/cpu/frontend/ppc/ppc_context.h"
|
||||
#include "xenia/cpu/frontend/ppc/ppc_hir_builder.h"
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace frontend {
|
||||
namespace ppc {
|
||||
|
||||
// TODO(benvanik): remove when enums redefined.
|
||||
using namespace xe::cpu::hir;
|
||||
|
||||
using xe::cpu::hir::RoundMode;
|
||||
using xe::cpu::hir::Value;
|
||||
|
||||
// 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
|
||||
|
||||
// Floating-point arithmetic (A-8)
|
||||
|
||||
XEEMITTER(faddx, 0xFC00002A, A)(PPCHIRBuilder& f, InstrData& i) {
|
||||
// frD <- (frA) + (frB)
|
||||
Value* v = f.Add(f.LoadFPR(i.A.FRA), f.LoadFPR(i.A.FRB));
|
||||
f.StoreFPR(i.A.FRT, v);
|
||||
// f.UpdateFPRF(v);
|
||||
if (i.A.Rc) {
|
||||
// e.update_cr_with_cond(1, v);
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(faddsx, 0xEC00002A, A)(PPCHIRBuilder& f, InstrData& i) {
|
||||
// frD <- (frA) + (frB)
|
||||
Value* v = f.Add(f.LoadFPR(i.A.FRA), f.LoadFPR(i.A.FRB));
|
||||
v = f.Convert(f.Convert(v, FLOAT32_TYPE), FLOAT64_TYPE);
|
||||
f.StoreFPR(i.A.FRT, v);
|
||||
// f.UpdateFPRF(v);
|
||||
if (i.A.Rc) {
|
||||
// e.update_cr_with_cond(1, v);
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(fdivx, 0xFC000024, A)(PPCHIRBuilder& f, InstrData& i) {
|
||||
// frD <- frA / frB
|
||||
Value* v = f.Div(f.LoadFPR(i.A.FRA), f.LoadFPR(i.A.FRB));
|
||||
f.StoreFPR(i.A.FRT, v);
|
||||
// f.UpdateFPRF(v);
|
||||
if (i.A.Rc) {
|
||||
// e.update_cr_with_cond(1, v);
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(fdivsx, 0xEC000024, A)(PPCHIRBuilder& f, InstrData& i) {
|
||||
// frD <- frA / frB
|
||||
Value* v = f.Div(f.LoadFPR(i.A.FRA), f.LoadFPR(i.A.FRB));
|
||||
v = f.Convert(f.Convert(v, FLOAT32_TYPE), FLOAT64_TYPE);
|
||||
f.StoreFPR(i.A.FRT, v);
|
||||
// f.UpdateFPRF(v);
|
||||
if (i.A.Rc) {
|
||||
// e.update_cr_with_cond(1, v);
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(fmulx, 0xFC000032, A)(PPCHIRBuilder& f, InstrData& i) {
|
||||
// frD <- (frA) x (frC)
|
||||
Value* v = f.Mul(f.LoadFPR(i.A.FRA), f.LoadFPR(i.A.FRC));
|
||||
f.StoreFPR(i.A.FRT, v);
|
||||
// f.UpdateFPRF(v);
|
||||
if (i.A.Rc) {
|
||||
// e.update_cr_with_cond(1, v);
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(fmulsx, 0xEC000032, A)(PPCHIRBuilder& f, InstrData& i) {
|
||||
// frD <- (frA) x (frC)
|
||||
Value* v = f.Mul(f.LoadFPR(i.A.FRA), f.LoadFPR(i.A.FRC));
|
||||
v = f.Convert(f.Convert(v, FLOAT32_TYPE), FLOAT64_TYPE);
|
||||
f.StoreFPR(i.A.FRT, v);
|
||||
// f.UpdateFPRF(v);
|
||||
if (i.A.Rc) {
|
||||
// e.update_cr_with_cond(1, v);
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(fresx, 0xEC000030, A)(PPCHIRBuilder& f, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(frsqrtex, 0xFC000034, A)(PPCHIRBuilder& f, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fsubx, 0xFC000028, A)(PPCHIRBuilder& f, InstrData& i) {
|
||||
// frD <- (frA) - (frB)
|
||||
Value* v = f.Sub(f.LoadFPR(i.A.FRA), f.LoadFPR(i.A.FRB));
|
||||
f.StoreFPR(i.A.FRT, v);
|
||||
// f.UpdateFPRF(v);
|
||||
if (i.A.Rc) {
|
||||
// e.update_cr_with_cond(1, v);
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(fsubsx, 0xEC000028, A)(PPCHIRBuilder& f, InstrData& i) {
|
||||
// frD <- (frA) - (frB)
|
||||
Value* v = f.Sub(f.LoadFPR(i.A.FRA), f.LoadFPR(i.A.FRB));
|
||||
v = f.Convert(f.Convert(v, FLOAT32_TYPE), FLOAT64_TYPE);
|
||||
f.StoreFPR(i.A.FRT, v);
|
||||
// f.UpdateFPRF(v);
|
||||
if (i.A.Rc) {
|
||||
// e.update_cr_with_cond(1, v);
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(fselx, 0xFC00002E, A)(PPCHIRBuilder& f, InstrData& i) {
|
||||
// if (frA) >= 0.0
|
||||
// then frD <- (frC)
|
||||
// else frD <- (frB)
|
||||
Value* ge = f.CompareSGE(f.LoadFPR(i.A.FRA), f.LoadConstant(0.0));
|
||||
Value* v = f.Select(ge, f.LoadFPR(i.A.FRC), f.LoadFPR(i.A.FRB));
|
||||
f.StoreFPR(i.A.FRT, v);
|
||||
if (i.A.Rc) {
|
||||
// e.update_cr_with_cond(1, v);
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(fsqrtx, 0xFC00002C, A)(PPCHIRBuilder& f, InstrData& i) {
|
||||
// Double precision:
|
||||
// frD <- sqrt(frB)
|
||||
Value* v = f.Sqrt(f.LoadFPR(i.A.FRA));
|
||||
f.StoreFPR(i.A.FRT, v);
|
||||
// f.UpdateFPRF(v);
|
||||
if (i.A.Rc) {
|
||||
// e.update_cr_with_cond(1, v);
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(fsqrtsx, 0xEC00002C, A)(PPCHIRBuilder& f, InstrData& i) {
|
||||
// Single precision:
|
||||
// frD <- sqrt(frB)
|
||||
Value* v = f.Sqrt(f.LoadFPR(i.A.FRA));
|
||||
v = f.Convert(f.Convert(v, FLOAT32_TYPE), FLOAT64_TYPE);
|
||||
f.StoreFPR(i.A.FRT, v);
|
||||
// f.UpdateFPRF(v);
|
||||
if (i.A.Rc) {
|
||||
// e.update_cr_with_cond(1, v);
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Floating-point multiply-add (A-9)
|
||||
|
||||
XEEMITTER(fmaddx, 0xFC00003A, A)(PPCHIRBuilder& f, InstrData& i) {
|
||||
// frD <- (frA x frC) + frB
|
||||
Value* v =
|
||||
f.MulAdd(f.LoadFPR(i.A.FRA), f.LoadFPR(i.A.FRC), f.LoadFPR(i.A.FRB));
|
||||
f.StoreFPR(i.A.FRT, v);
|
||||
// f.UpdateFPRF(v);
|
||||
if (i.A.Rc) {
|
||||
// e.update_cr_with_cond(1, v);
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(fmaddsx, 0xEC00003A, A)(PPCHIRBuilder& f, InstrData& i) {
|
||||
// frD <- (frA x frC) + frB
|
||||
Value* v =
|
||||
f.MulAdd(f.LoadFPR(i.A.FRA), f.LoadFPR(i.A.FRC), f.LoadFPR(i.A.FRB));
|
||||
v = f.Convert(f.Convert(v, FLOAT32_TYPE), FLOAT64_TYPE);
|
||||
f.StoreFPR(i.A.FRT, v);
|
||||
// f.UpdateFPRF(v);
|
||||
if (i.A.Rc) {
|
||||
// e.update_cr_with_cond(1, v);
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(fmsubx, 0xFC000038, A)(PPCHIRBuilder& f, InstrData& i) {
|
||||
// frD <- (frA x frC) - frB
|
||||
Value* v =
|
||||
f.MulSub(f.LoadFPR(i.A.FRA), f.LoadFPR(i.A.FRC), f.LoadFPR(i.A.FRB));
|
||||
f.StoreFPR(i.A.FRT, v);
|
||||
// f.UpdateFPRF(v);
|
||||
if (i.A.Rc) {
|
||||
// e.update_cr_with_cond(1, v);
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(fmsubsx, 0xEC000038, A)(PPCHIRBuilder& f, InstrData& i) {
|
||||
// frD <- (frA x frC) - frB
|
||||
Value* v =
|
||||
f.MulSub(f.LoadFPR(i.A.FRA), f.LoadFPR(i.A.FRC), f.LoadFPR(i.A.FRB));
|
||||
v = f.Convert(f.Convert(v, FLOAT32_TYPE), FLOAT64_TYPE);
|
||||
f.StoreFPR(i.A.FRT, v);
|
||||
// f.UpdateFPRF(v);
|
||||
if (i.A.Rc) {
|
||||
// e.update_cr_with_cond(1, v);
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(fnmaddx, 0xFC00003E, A)(PPCHIRBuilder& f, InstrData& i) {
|
||||
// frD <- -([frA x frC] + frB)
|
||||
Value* v = f.Neg(
|
||||
f.MulAdd(f.LoadFPR(i.A.FRA), f.LoadFPR(i.A.FRC), f.LoadFPR(i.A.FRB)));
|
||||
f.StoreFPR(i.A.FRT, v);
|
||||
// f.UpdateFPRF(v);
|
||||
if (i.A.Rc) {
|
||||
// e.update_cr_with_cond(1, v);
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(fnmaddsx, 0xEC00003E, A)(PPCHIRBuilder& f, InstrData& i) {
|
||||
// frD <- -([frA x frC] + frB)
|
||||
Value* v = f.Neg(
|
||||
f.MulAdd(f.LoadFPR(i.A.FRA), f.LoadFPR(i.A.FRC), f.LoadFPR(i.A.FRB)));
|
||||
v = f.Convert(f.Convert(v, FLOAT32_TYPE), FLOAT64_TYPE);
|
||||
f.StoreFPR(i.A.FRT, v);
|
||||
// f.UpdateFPRF(v);
|
||||
if (i.A.Rc) {
|
||||
// e.update_cr_with_cond(1, v);
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(fnmsubx, 0xFC00003C, A)(PPCHIRBuilder& f, InstrData& i) {
|
||||
// frD <- -([frA x frC] - frB)
|
||||
Value* v = f.Neg(
|
||||
f.MulSub(f.LoadFPR(i.A.FRA), f.LoadFPR(i.A.FRC), f.LoadFPR(i.A.FRB)));
|
||||
f.StoreFPR(i.A.FRT, v);
|
||||
// f.UpdateFPRF(v);
|
||||
if (i.A.Rc) {
|
||||
// e.update_cr_with_cond(1, v);
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(fnmsubsx, 0xEC00003C, A)(PPCHIRBuilder& f, InstrData& i) {
|
||||
// frD <- -([frA x frC] - frB)
|
||||
Value* v = f.Neg(
|
||||
f.MulSub(f.LoadFPR(i.A.FRA), f.LoadFPR(i.A.FRC), f.LoadFPR(i.A.FRB)));
|
||||
v = f.Convert(f.Convert(v, FLOAT32_TYPE), FLOAT64_TYPE);
|
||||
f.StoreFPR(i.A.FRT, v);
|
||||
// f.UpdateFPRF(v);
|
||||
if (i.A.Rc) {
|
||||
// e.update_cr_with_cond(1, v);
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Floating-point rounding and conversion (A-10)
|
||||
|
||||
XEEMITTER(fcfidx, 0xFC00069C, X)(PPCHIRBuilder& f, InstrData& i) {
|
||||
// frD <- signed_int64_to_double( frB )
|
||||
Value* v = f.Convert(f.Cast(f.LoadFPR(i.X.RB), INT64_TYPE), FLOAT64_TYPE);
|
||||
f.StoreFPR(i.X.RT, v);
|
||||
// f.UpdateFPRF(v);
|
||||
if (i.A.Rc) {
|
||||
// e.update_cr_with_cond(1, v);
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(fctidx, 0xFC00065C, X)(PPCHIRBuilder& f, InstrData& i) {
|
||||
// frD <- double_to_signed_int64( frB )
|
||||
// TODO(benvanik): pull from FPSCR[RN]
|
||||
RoundMode round_mode = ROUND_TO_ZERO;
|
||||
Value* v = f.Convert(f.LoadFPR(i.X.RB), INT64_TYPE, round_mode);
|
||||
v = f.Cast(v, FLOAT64_TYPE);
|
||||
f.StoreFPR(i.X.RT, v);
|
||||
// f.UpdateFPRF(v);
|
||||
if (i.X.Rc) {
|
||||
// e.update_cr_with_cond(1, v);
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(fctidzx, 0xFC00065E, X)(PPCHIRBuilder& f, InstrData& i) {
|
||||
// TODO(benvanik): assuming round to zero is always set, is that ok?
|
||||
return InstrEmit_fctidx(f, i);
|
||||
}
|
||||
|
||||
XEEMITTER(fctiwx, 0xFC00001C, X)(PPCHIRBuilder& f, InstrData& i) {
|
||||
// frD <- double_to_signed_int32( frB )
|
||||
// TODO(benvanik): pull from FPSCR[RN]
|
||||
RoundMode round_mode = ROUND_TO_ZERO;
|
||||
Value* v = f.Convert(f.LoadFPR(i.X.RB), INT32_TYPE, round_mode);
|
||||
v = f.Cast(f.ZeroExtend(v, INT64_TYPE), FLOAT64_TYPE);
|
||||
f.StoreFPR(i.X.RT, v);
|
||||
// f.UpdateFPRF(v);
|
||||
if (i.X.Rc) {
|
||||
// e.update_cr_with_cond(1, v);
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(fctiwzx, 0xFC00001E, X)(PPCHIRBuilder& f, InstrData& i) {
|
||||
// TODO(benvanik): assuming round to zero is always set, is that ok?
|
||||
return InstrEmit_fctiwx(f, i);
|
||||
}
|
||||
|
||||
XEEMITTER(frspx, 0xFC000018, X)(PPCHIRBuilder& f, InstrData& i) {
|
||||
// frD <- Round_single(frB)
|
||||
// TODO(benvanik): pull from FPSCR[RN]
|
||||
RoundMode round_mode = ROUND_TO_ZERO;
|
||||
Value* v = f.Convert(f.LoadFPR(i.X.RB), FLOAT32_TYPE, round_mode);
|
||||
v = f.Convert(v, FLOAT64_TYPE);
|
||||
f.StoreFPR(i.X.RT, v);
|
||||
// f.UpdateFPRF(v);
|
||||
if (i.X.Rc) {
|
||||
// e.update_cr_with_cond(1, v);
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Floating-point compare (A-11)
|
||||
|
||||
int InstrEmit_fcmpx_(PPCHIRBuilder& f, 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
|
||||
|
||||
// TODO(benvanik): update FPCC for mffsx/etc
|
||||
// TODO(benvanik): update VXSNAN
|
||||
const uint32_t crf = i.X.RT >> 2;
|
||||
// f.UpdateFPRF(v);
|
||||
f.UpdateCR(crf, f.LoadFPR(i.X.RA), f.LoadFPR(i.X.RB), false);
|
||||
return 0;
|
||||
}
|
||||
XEEMITTER(fcmpo, 0xFC000040, X)(PPCHIRBuilder& f, InstrData& i) {
|
||||
return InstrEmit_fcmpx_(f, i, true);
|
||||
}
|
||||
XEEMITTER(fcmpu, 0xFC000000, X)(PPCHIRBuilder& f, InstrData& i) {
|
||||
return InstrEmit_fcmpx_(f, i, false);
|
||||
}
|
||||
|
||||
// Floating-point status and control register (A
|
||||
|
||||
XEEMITTER(mcrfs, 0xFC000080, X)(PPCHIRBuilder& f, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(mffsx, 0xFC00048E, X)(PPCHIRBuilder& f, InstrData& i) {
|
||||
if (i.X.Rc) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
Value* v = f.Cast(f.LoadFPSCR(), FLOAT64_TYPE);
|
||||
f.StoreFPR(i.X.RT, v);
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(mtfsb0x, 0xFC00008C, X)(PPCHIRBuilder& f, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(mtfsb1x, 0xFC00004C, X)(PPCHIRBuilder& f, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(mtfsfx, 0xFC00058E, XFL)(PPCHIRBuilder& f, InstrData& i) {
|
||||
if (i.XFL.Rc) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
if (i.XFL.L) {
|
||||
// Move/shift.
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
} else {
|
||||
// Directly store.
|
||||
// TODO(benvanik): use w/field mask to select bits.
|
||||
i.XFL.W;
|
||||
i.XFL.FM;
|
||||
f.StoreFPSCR(f.Cast(f.LoadFPR(i.XFL.RB), INT64_TYPE));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(mtfsfix, 0xFC00010C, X)(PPCHIRBuilder& f, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Floating-point move (A-21)
|
||||
|
||||
XEEMITTER(fabsx, 0xFC000210, X)(PPCHIRBuilder& f, InstrData& i) {
|
||||
// frD <- abs(frB)
|
||||
Value* v = f.Abs(f.LoadFPR(i.X.RB));
|
||||
f.StoreFPR(i.X.RT, v);
|
||||
if (i.X.Rc) {
|
||||
// e.update_cr_with_cond(1, v);
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(fmrx, 0xFC000090, X)(PPCHIRBuilder& f, InstrData& i) {
|
||||
// frD <- (frB)
|
||||
Value* v = f.LoadFPR(i.X.RB);
|
||||
f.StoreFPR(i.X.RT, v);
|
||||
if (i.X.Rc) {
|
||||
// e.update_cr_with_cond(1, v);
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(fnabsx, 0xFC000110, X)(PPCHIRBuilder& f, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fnegx, 0xFC000050, X)(PPCHIRBuilder& f, InstrData& i) {
|
||||
// frD <- ¬ frB[0] || frB[1-63]
|
||||
Value* v = f.Neg(f.LoadFPR(i.X.RB));
|
||||
f.StoreFPR(i.X.RT, v);
|
||||
if (i.X.Rc) {
|
||||
// e.update_cr_with_cond(1, v);
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void RegisterEmitCategoryFPU() {
|
||||
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 frontend
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
1088
src/xenia/cpu/frontend/ppc/ppc_emit_memory.cc
Normal file
1088
src/xenia/cpu/frontend/ppc/ppc_emit_memory.cc
Normal file
File diff suppressed because it is too large
Load Diff
119
src/xenia/cpu/frontend/ppc/ppc_frontend.cc
Normal file
119
src/xenia/cpu/frontend/ppc/ppc_frontend.cc
Normal file
@@ -0,0 +1,119 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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/frontend/ppc/ppc_frontend.h"
|
||||
|
||||
#include "xenia/cpu/frontend/ppc/ppc_context.h"
|
||||
#include "xenia/cpu/frontend/ppc/ppc_disasm.h"
|
||||
#include "xenia/cpu/frontend/ppc/ppc_emit.h"
|
||||
#include "xenia/cpu/frontend/ppc/ppc_translator.h"
|
||||
#include "xenia/cpu/runtime/runtime.h"
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace frontend {
|
||||
namespace ppc {
|
||||
|
||||
using xe::cpu::runtime::Function;
|
||||
using xe::cpu::runtime::FunctionInfo;
|
||||
using xe::cpu::runtime::Runtime;
|
||||
|
||||
void InitializeIfNeeded();
|
||||
void CleanupOnShutdown();
|
||||
|
||||
void InitializeIfNeeded() {
|
||||
static bool has_initialized = false;
|
||||
if (has_initialized) {
|
||||
return;
|
||||
}
|
||||
has_initialized = true;
|
||||
|
||||
RegisterEmitCategoryAltivec();
|
||||
RegisterEmitCategoryALU();
|
||||
RegisterEmitCategoryControl();
|
||||
RegisterEmitCategoryFPU();
|
||||
RegisterEmitCategoryMemory();
|
||||
|
||||
atexit(CleanupOnShutdown);
|
||||
}
|
||||
|
||||
void CleanupOnShutdown() {}
|
||||
|
||||
PPCFrontend::PPCFrontend(Runtime* runtime) : Frontend(runtime) {
|
||||
InitializeIfNeeded();
|
||||
|
||||
std::unique_ptr<ContextInfo> context_info(
|
||||
new ContextInfo(sizeof(PPCContext), offsetof(PPCContext, thread_state),
|
||||
offsetof(PPCContext, thread_id)));
|
||||
// Add fields/etc.
|
||||
context_info_ = std::move(context_info);
|
||||
}
|
||||
|
||||
PPCFrontend::~PPCFrontend() {
|
||||
// Force cleanup now before we deinit.
|
||||
translator_pool_.Reset();
|
||||
}
|
||||
|
||||
void CheckGlobalLock(PPCContext* ppc_state, void* arg0, void* arg1) {
|
||||
ppc_state->scratch = 0x8000;
|
||||
}
|
||||
void HandleGlobalLock(PPCContext* ppc_state, void* arg0, void* arg1) {
|
||||
std::mutex* global_lock = reinterpret_cast<std::mutex*>(arg0);
|
||||
volatile bool* global_lock_taken = reinterpret_cast<bool*>(arg1);
|
||||
uint64_t value = ppc_state->scratch;
|
||||
if (value == 0x8000) {
|
||||
global_lock->unlock();
|
||||
*global_lock_taken = false;
|
||||
} else if (value == ppc_state->r[13]) {
|
||||
global_lock->lock();
|
||||
*global_lock_taken = true;
|
||||
}
|
||||
}
|
||||
|
||||
int PPCFrontend::Initialize() {
|
||||
int result = Frontend::Initialize();
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
void* arg0 = reinterpret_cast<void*>(&builtins_.global_lock);
|
||||
void* arg1 = reinterpret_cast<void*>(&builtins_.global_lock_taken);
|
||||
builtins_.check_global_lock = runtime_->DefineBuiltin(
|
||||
"CheckGlobalLock", (FunctionInfo::ExternHandler)CheckGlobalLock, arg0,
|
||||
arg1);
|
||||
builtins_.handle_global_lock = runtime_->DefineBuiltin(
|
||||
"HandleGlobalLock", (FunctionInfo::ExternHandler)HandleGlobalLock, arg0,
|
||||
arg1);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int PPCFrontend::DeclareFunction(FunctionInfo* symbol_info) {
|
||||
// Could scan or something here.
|
||||
// Could also check to see if it's a well-known function type and classify
|
||||
// for later.
|
||||
// Could also kick off a precompiler, since we know it's likely the function
|
||||
// will be demanded soon.
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PPCFrontend::DefineFunction(FunctionInfo* symbol_info,
|
||||
uint32_t debug_info_flags, uint32_t trace_flags,
|
||||
Function** out_function) {
|
||||
PPCTranslator* translator = translator_pool_.Allocate(this);
|
||||
int result = translator->Translate(symbol_info, debug_info_flags, trace_flags,
|
||||
out_function);
|
||||
translator_pool_.Release(translator);
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace ppc
|
||||
} // namespace frontend
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
56
src/xenia/cpu/frontend/ppc/ppc_frontend.h
Normal file
56
src/xenia/cpu/frontend/ppc/ppc_frontend.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_FRONTEND_PPC_PPC_FRONTEND_H_
|
||||
#define XENIA_FRONTEND_PPC_PPC_FRONTEND_H_
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include "xenia/cpu/frontend/frontend.h"
|
||||
#include "poly/type_pool.h"
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace frontend {
|
||||
namespace ppc {
|
||||
|
||||
class PPCTranslator;
|
||||
|
||||
struct PPCBuiltins {
|
||||
std::mutex global_lock;
|
||||
bool global_lock_taken;
|
||||
runtime::FunctionInfo* check_global_lock;
|
||||
runtime::FunctionInfo* handle_global_lock;
|
||||
};
|
||||
|
||||
class PPCFrontend : public Frontend {
|
||||
public:
|
||||
PPCFrontend(runtime::Runtime* runtime);
|
||||
~PPCFrontend() override;
|
||||
|
||||
int Initialize() override;
|
||||
|
||||
PPCBuiltins* builtins() { return &builtins_; }
|
||||
|
||||
int DeclareFunction(runtime::FunctionInfo* symbol_info) override;
|
||||
int DefineFunction(runtime::FunctionInfo* symbol_info,
|
||||
uint32_t debug_info_flags, uint32_t trace_flags,
|
||||
runtime::Function** out_function) override;
|
||||
|
||||
private:
|
||||
poly::TypePool<PPCTranslator, PPCFrontend*> translator_pool_;
|
||||
PPCBuiltins builtins_;
|
||||
};
|
||||
|
||||
} // namespace ppc
|
||||
} // namespace frontend
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_FRONTEND_PPC_PPC_FRONTEND_H_
|
||||
498
src/xenia/cpu/frontend/ppc/ppc_hir_builder.cc
Normal file
498
src/xenia/cpu/frontend/ppc/ppc_hir_builder.cc
Normal file
@@ -0,0 +1,498 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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/frontend/ppc/ppc_hir_builder.h"
|
||||
|
||||
#include "xenia/cpu/cpu-private.h"
|
||||
#include "xenia/cpu/frontend/ppc/ppc_context.h"
|
||||
#include "xenia/cpu/frontend/ppc/ppc_disasm.h"
|
||||
#include "xenia/cpu/frontend/ppc/ppc_frontend.h"
|
||||
#include "xenia/cpu/frontend/ppc/ppc_instr.h"
|
||||
#include "xenia/cpu/hir/label.h"
|
||||
#include "xenia/cpu/runtime/runtime.h"
|
||||
#include "xenia/profiling.h"
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace frontend {
|
||||
namespace ppc {
|
||||
|
||||
// TODO(benvanik): remove when enums redefined.
|
||||
using namespace xe::cpu::hir;
|
||||
|
||||
using xe::cpu::hir::Label;
|
||||
using xe::cpu::hir::TypeName;
|
||||
using xe::cpu::hir::Value;
|
||||
using xe::cpu::runtime::Runtime;
|
||||
using xe::cpu::runtime::FunctionInfo;
|
||||
|
||||
PPCHIRBuilder::PPCHIRBuilder(PPCFrontend* frontend)
|
||||
: HIRBuilder(), frontend_(frontend), comment_buffer_(4096) {}
|
||||
|
||||
PPCHIRBuilder::~PPCHIRBuilder() = default;
|
||||
|
||||
void PPCHIRBuilder::Reset() {
|
||||
start_address_ = 0;
|
||||
instr_offset_list_ = NULL;
|
||||
label_list_ = NULL;
|
||||
with_debug_info_ = false;
|
||||
HIRBuilder::Reset();
|
||||
}
|
||||
|
||||
int PPCHIRBuilder::Emit(FunctionInfo* symbol_info, uint32_t flags) {
|
||||
SCOPE_profile_cpu_f("cpu");
|
||||
|
||||
Memory* memory = frontend_->memory();
|
||||
const uint8_t* p = memory->membase();
|
||||
|
||||
symbol_info_ = symbol_info;
|
||||
start_address_ = symbol_info->address();
|
||||
instr_count_ = (symbol_info->end_address() - symbol_info->address()) / 4 + 1;
|
||||
|
||||
with_debug_info_ = (flags & EMIT_DEBUG_COMMENTS) == EMIT_DEBUG_COMMENTS;
|
||||
if (with_debug_info_) {
|
||||
Comment("%s fn %.8X-%.8X %s", symbol_info->module()->name().c_str(),
|
||||
symbol_info->address(), symbol_info->end_address(),
|
||||
symbol_info->name().c_str());
|
||||
}
|
||||
|
||||
// Allocate offset list.
|
||||
// This is used to quickly map labels to instructions.
|
||||
// The list is built as the instructions are traversed, with the values
|
||||
// being the previous HIR Instr before the given instruction. An
|
||||
// instruction may have a label assigned to it if it hasn't been hit
|
||||
// yet.
|
||||
size_t list_size = instr_count_ * sizeof(void*);
|
||||
instr_offset_list_ = (Instr**)arena_->Alloc(list_size);
|
||||
label_list_ = (Label**)arena_->Alloc(list_size);
|
||||
memset(instr_offset_list_, 0, list_size);
|
||||
memset(label_list_, 0, list_size);
|
||||
|
||||
// Always mark entry with label.
|
||||
label_list_[0] = NewLabel();
|
||||
|
||||
uint64_t start_address = symbol_info->address();
|
||||
uint64_t end_address = symbol_info->end_address();
|
||||
InstrData i;
|
||||
for (uint64_t address = start_address, offset = 0; address <= end_address;
|
||||
address += 4, offset++) {
|
||||
i.address = address;
|
||||
i.code = poly::load_and_swap<uint32_t>(p + address);
|
||||
// TODO(benvanik): find a way to avoid using the opcode tables.
|
||||
i.type = GetInstrType(i.code);
|
||||
trace_info_.dest_count = 0;
|
||||
|
||||
// Mark label, if we were assigned one earlier on in the walk.
|
||||
// We may still get a label, but it'll be inserted by LookupLabel
|
||||
// as needed.
|
||||
Label* label = label_list_[offset];
|
||||
if (label) {
|
||||
MarkLabel(label);
|
||||
}
|
||||
|
||||
Instr* first_instr = 0;
|
||||
if (with_debug_info_) {
|
||||
if (label) {
|
||||
AnnotateLabel(address, label);
|
||||
}
|
||||
comment_buffer_.Reset();
|
||||
DisasmPPC(i, &comment_buffer_);
|
||||
Comment("%.8X %.8X %s", address, i.code, comment_buffer_.GetString());
|
||||
first_instr = last_instr();
|
||||
}
|
||||
|
||||
// Mark source offset for debugging.
|
||||
// We could omit this if we never wanted to debug.
|
||||
SourceOffset(i.address);
|
||||
if (!first_instr) {
|
||||
first_instr = last_instr();
|
||||
}
|
||||
|
||||
// Stash instruction offset. It's either the SOURCE_OFFSET or the COMMENT.
|
||||
instr_offset_list_[offset] = first_instr;
|
||||
|
||||
if (!i.type) {
|
||||
PLOGE("Invalid instruction %.8llX %.8X", i.address, i.code);
|
||||
Comment("INVALID!");
|
||||
// TraceInvalidInstruction(i);
|
||||
continue;
|
||||
}
|
||||
++i.type->translation_count;
|
||||
|
||||
typedef int (*InstrEmitter)(PPCHIRBuilder& f, InstrData& i);
|
||||
InstrEmitter emit = (InstrEmitter)i.type->emit;
|
||||
|
||||
if (i.address == FLAGS_break_on_instruction) {
|
||||
Comment("--break-on-instruction target");
|
||||
DebugBreak();
|
||||
}
|
||||
|
||||
if (!i.type->emit || emit(*this, i)) {
|
||||
PLOGE("Unimplemented instr %.8llX %.8X %s", i.address, i.code,
|
||||
i.type->name);
|
||||
Comment("UNIMPLEMENTED!");
|
||||
// DebugBreak();
|
||||
// TraceInvalidInstruction(i);
|
||||
}
|
||||
|
||||
if (flags & EMIT_TRACE_SOURCE) {
|
||||
if (flags & EMIT_TRACE_SOURCE_VALUES) {
|
||||
switch (trace_info_.dest_count) {
|
||||
case 0:
|
||||
TraceSource(i.address);
|
||||
break;
|
||||
case 1:
|
||||
TraceSource(i.address, trace_info_.dests[0].reg,
|
||||
trace_info_.dests[0].value);
|
||||
break;
|
||||
case 2:
|
||||
TraceSource(i.address, trace_info_.dests[0].reg,
|
||||
trace_info_.dests[0].value, trace_info_.dests[1].reg,
|
||||
trace_info_.dests[1].value);
|
||||
break;
|
||||
default:
|
||||
assert_unhandled_case(trace_info_.dest_count);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
TraceSource(i.address);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Finalize();
|
||||
}
|
||||
|
||||
void PPCHIRBuilder::AnnotateLabel(uint64_t address, Label* label) {
|
||||
char name_buffer[13];
|
||||
snprintf(name_buffer, poly::countof(name_buffer), "loc_%.8X",
|
||||
(uint32_t)address);
|
||||
label->name = (char*)arena_->Alloc(sizeof(name_buffer));
|
||||
memcpy(label->name, name_buffer, sizeof(name_buffer));
|
||||
}
|
||||
|
||||
FunctionInfo* PPCHIRBuilder::LookupFunction(uint64_t address) {
|
||||
Runtime* runtime = frontend_->runtime();
|
||||
FunctionInfo* symbol_info;
|
||||
if (runtime->LookupFunctionInfo(address, &symbol_info)) {
|
||||
return NULL;
|
||||
}
|
||||
return symbol_info;
|
||||
}
|
||||
|
||||
Label* PPCHIRBuilder::LookupLabel(uint64_t address) {
|
||||
if (address < start_address_) {
|
||||
return NULL;
|
||||
}
|
||||
size_t offset = (address - start_address_) / 4;
|
||||
if (offset >= instr_count_) {
|
||||
return NULL;
|
||||
}
|
||||
Label* label = label_list_[offset];
|
||||
if (label) {
|
||||
return label;
|
||||
}
|
||||
// No label. If we haven't yet hit the instruction in the walk
|
||||
// then create a label. Otherwise, we must go back and insert
|
||||
// the label.
|
||||
label = NewLabel();
|
||||
label_list_[offset] = label;
|
||||
Instr* instr = instr_offset_list_[offset];
|
||||
if (instr) {
|
||||
if (instr->prev) {
|
||||
// Insert label, breaking up existing instructions.
|
||||
InsertLabel(label, instr->prev);
|
||||
} else {
|
||||
// Instruction is at the head of a block, so just add the label.
|
||||
MarkLabel(label, instr->block);
|
||||
}
|
||||
|
||||
// Annotate the label, as we won't do it later.
|
||||
if (with_debug_info_) {
|
||||
AnnotateLabel(address, label);
|
||||
}
|
||||
}
|
||||
return label;
|
||||
}
|
||||
|
||||
// Value* PPCHIRBuilder::LoadXER() {
|
||||
//}
|
||||
//
|
||||
// void PPCHIRBuilder::StoreXER(Value* value) {
|
||||
//}
|
||||
|
||||
Value* PPCHIRBuilder::LoadLR() {
|
||||
return LoadContext(offsetof(PPCContext, lr), INT64_TYPE);
|
||||
}
|
||||
|
||||
void PPCHIRBuilder::StoreLR(Value* value) {
|
||||
assert_true(value->type == INT64_TYPE);
|
||||
StoreContext(offsetof(PPCContext, lr), value);
|
||||
|
||||
auto& trace_reg = trace_info_.dests[trace_info_.dest_count++];
|
||||
trace_reg.reg = 64;
|
||||
trace_reg.value = value;
|
||||
}
|
||||
|
||||
Value* PPCHIRBuilder::LoadCTR() {
|
||||
return LoadContext(offsetof(PPCContext, ctr), INT64_TYPE);
|
||||
}
|
||||
|
||||
void PPCHIRBuilder::StoreCTR(Value* value) {
|
||||
assert_true(value->type == INT64_TYPE);
|
||||
StoreContext(offsetof(PPCContext, ctr), value);
|
||||
|
||||
auto& trace_reg = trace_info_.dests[trace_info_.dest_count++];
|
||||
trace_reg.reg = 65;
|
||||
trace_reg.value = value;
|
||||
}
|
||||
|
||||
Value* PPCHIRBuilder::LoadCR() {
|
||||
// All bits. This is expensive, but seems to be less used than the
|
||||
// field-specific LoadCR.
|
||||
Value* v = LoadCR(0);
|
||||
for (int i = 1; i <= 7; ++i) {
|
||||
v = Or(v, LoadCR(i));
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
Value* PPCHIRBuilder::LoadCR(uint32_t n) {
|
||||
// Construct the entire word of just the bits we care about.
|
||||
// This makes it easier for the optimizer to exclude things, though
|
||||
// we could be even more clever and watch sequences.
|
||||
Value* v = Shl(ZeroExtend(LoadContext(offsetof(PPCContext, cr0) + (4 * n) + 0,
|
||||
INT8_TYPE),
|
||||
INT64_TYPE),
|
||||
4 * (7 - n) + 3);
|
||||
v = Or(v, Shl(ZeroExtend(LoadContext(offsetof(PPCContext, cr0) + (4 * n) + 1,
|
||||
INT8_TYPE),
|
||||
INT64_TYPE),
|
||||
4 * (7 - n) + 2));
|
||||
v = Or(v, Shl(ZeroExtend(LoadContext(offsetof(PPCContext, cr0) + (4 * n) + 2,
|
||||
INT8_TYPE),
|
||||
INT64_TYPE),
|
||||
4 * (7 - n) + 1));
|
||||
v = Or(v, Shl(ZeroExtend(LoadContext(offsetof(PPCContext, cr0) + (4 * n) + 3,
|
||||
INT8_TYPE),
|
||||
INT64_TYPE),
|
||||
4 * (7 - n) + 0));
|
||||
return v;
|
||||
}
|
||||
|
||||
Value* PPCHIRBuilder::LoadCRField(uint32_t n, uint32_t bit) {
|
||||
return LoadContext(offsetof(PPCContext, cr0) + (4 * n) + bit, INT8_TYPE);
|
||||
}
|
||||
|
||||
void PPCHIRBuilder::StoreCR(Value* value) {
|
||||
// All bits. This is expensive, but seems to be less used than the
|
||||
// field-specific StoreCR.
|
||||
for (int i = 0; i <= 7; ++i) {
|
||||
StoreCR(i, value);
|
||||
}
|
||||
}
|
||||
|
||||
void PPCHIRBuilder::StoreCR(uint32_t n, Value* value) {
|
||||
// Pull out the bits we are interested in.
|
||||
// Optimization passes will kill any unneeded stores (mostly).
|
||||
StoreContext(offsetof(PPCContext, cr0) + (4 * n) + 0,
|
||||
And(Truncate(Shr(value, 4 * (7 - n) + 3), INT8_TYPE),
|
||||
LoadConstant(uint8_t(1))));
|
||||
StoreContext(offsetof(PPCContext, cr0) + (4 * n) + 1,
|
||||
And(Truncate(Shr(value, 4 * (7 - n) + 2), INT8_TYPE),
|
||||
LoadConstant(uint8_t(1))));
|
||||
StoreContext(offsetof(PPCContext, cr0) + (4 * n) + 2,
|
||||
And(Truncate(Shr(value, 4 * (7 - n) + 1), INT8_TYPE),
|
||||
LoadConstant(uint8_t(1))));
|
||||
StoreContext(offsetof(PPCContext, cr0) + (4 * n) + 3,
|
||||
And(Truncate(Shr(value, 4 * (7 - n) + 0), INT8_TYPE),
|
||||
LoadConstant(uint8_t(1))));
|
||||
}
|
||||
|
||||
void PPCHIRBuilder::StoreCRField(uint32_t n, uint32_t bit, Value* value) {
|
||||
StoreContext(offsetof(PPCContext, cr0) + (4 * n) + bit, value);
|
||||
|
||||
// TODO(benvanik): trace CR.
|
||||
}
|
||||
|
||||
void PPCHIRBuilder::UpdateCR(uint32_t n, Value* lhs, bool is_signed) {
|
||||
UpdateCR(n, Truncate(lhs, INT32_TYPE), LoadZero(INT32_TYPE), is_signed);
|
||||
}
|
||||
|
||||
void PPCHIRBuilder::UpdateCR(uint32_t n, Value* lhs, Value* rhs,
|
||||
bool is_signed) {
|
||||
if (is_signed) {
|
||||
Value* lt = CompareSLT(lhs, rhs);
|
||||
StoreContext(offsetof(PPCContext, cr0) + (4 * n) + 0, lt);
|
||||
Value* gt = CompareSGT(lhs, rhs);
|
||||
StoreContext(offsetof(PPCContext, cr0) + (4 * n) + 1, gt);
|
||||
} else {
|
||||
Value* lt = CompareULT(lhs, rhs);
|
||||
StoreContext(offsetof(PPCContext, cr0) + (4 * n) + 0, lt);
|
||||
Value* gt = CompareUGT(lhs, rhs);
|
||||
StoreContext(offsetof(PPCContext, cr0) + (4 * n) + 1, gt);
|
||||
}
|
||||
Value* eq = CompareEQ(lhs, rhs);
|
||||
StoreContext(offsetof(PPCContext, cr0) + (4 * n) + 2, eq);
|
||||
|
||||
// Value* so = AllocValue(UINT8_TYPE);
|
||||
// StoreContext(offsetof(PPCContext, cr) + (4 * n) + 3, so);
|
||||
|
||||
// TOOD(benvanik): trace CR.
|
||||
}
|
||||
|
||||
void PPCHIRBuilder::UpdateCR6(Value* src_value) {
|
||||
// Testing for all 1's and all 0's.
|
||||
// if (Rc) CR6 = all_equal | 0 | none_equal | 0
|
||||
// TODO(benvanik): efficient instruction?
|
||||
StoreContext(offsetof(PPCContext, cr6.cr6_1), LoadZero(INT8_TYPE));
|
||||
StoreContext(offsetof(PPCContext, cr6.cr6_3), LoadZero(INT8_TYPE));
|
||||
StoreContext(offsetof(PPCContext, cr6.cr6_all_equal),
|
||||
IsFalse(Not(src_value)));
|
||||
StoreContext(offsetof(PPCContext, cr6.cr6_none_equal), IsFalse(src_value));
|
||||
|
||||
// TOOD(benvanik): trace CR.
|
||||
}
|
||||
|
||||
Value* PPCHIRBuilder::LoadMSR() {
|
||||
// bit 48 = EE; interrupt enabled
|
||||
// bit 62 = RI; recoverable interrupt
|
||||
// return 8000h if unlocked, else 0
|
||||
CallExtern(frontend_->builtins()->check_global_lock);
|
||||
return LoadContext(offsetof(PPCContext, scratch), INT64_TYPE);
|
||||
}
|
||||
|
||||
void PPCHIRBuilder::StoreMSR(Value* value) {
|
||||
// if & 0x8000 == 0, lock, else unlock
|
||||
StoreContext(offsetof(PPCContext, scratch), ZeroExtend(value, INT64_TYPE));
|
||||
CallExtern(frontend_->builtins()->handle_global_lock);
|
||||
}
|
||||
|
||||
Value* PPCHIRBuilder::LoadFPSCR() {
|
||||
return LoadContext(offsetof(PPCContext, fpscr), INT64_TYPE);
|
||||
}
|
||||
|
||||
void PPCHIRBuilder::StoreFPSCR(Value* value) {
|
||||
assert_true(value->type == INT64_TYPE);
|
||||
StoreContext(offsetof(PPCContext, fpscr), value);
|
||||
|
||||
auto& trace_reg = trace_info_.dests[trace_info_.dest_count++];
|
||||
trace_reg.reg = 67;
|
||||
trace_reg.value = value;
|
||||
}
|
||||
|
||||
Value* PPCHIRBuilder::LoadXER() {
|
||||
assert_always();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void PPCHIRBuilder::StoreXER(Value* value) { assert_always(); }
|
||||
|
||||
Value* PPCHIRBuilder::LoadCA() {
|
||||
return LoadContext(offsetof(PPCContext, xer_ca), INT8_TYPE);
|
||||
}
|
||||
|
||||
void PPCHIRBuilder::StoreCA(Value* value) {
|
||||
assert_true(value->type == INT8_TYPE);
|
||||
StoreContext(offsetof(PPCContext, xer_ca), value);
|
||||
|
||||
auto& trace_reg = trace_info_.dests[trace_info_.dest_count++];
|
||||
trace_reg.reg = 66;
|
||||
trace_reg.value = value;
|
||||
}
|
||||
|
||||
Value* PPCHIRBuilder::LoadSAT() {
|
||||
return LoadContext(offsetof(PPCContext, vscr_sat), INT8_TYPE);
|
||||
}
|
||||
|
||||
void PPCHIRBuilder::StoreSAT(Value* value) {
|
||||
value = Truncate(value, INT8_TYPE);
|
||||
StoreContext(offsetof(PPCContext, vscr_sat), value);
|
||||
|
||||
auto& trace_reg = trace_info_.dests[trace_info_.dest_count++];
|
||||
trace_reg.reg = 44;
|
||||
trace_reg.value = value;
|
||||
}
|
||||
|
||||
Value* PPCHIRBuilder::LoadGPR(uint32_t reg) {
|
||||
return LoadContext(offsetof(PPCContext, r) + reg * 8, INT64_TYPE);
|
||||
}
|
||||
|
||||
void PPCHIRBuilder::StoreGPR(uint32_t reg, Value* value) {
|
||||
assert_true(value->type == INT64_TYPE);
|
||||
StoreContext(offsetof(PPCContext, r) + reg * 8, value);
|
||||
|
||||
auto& trace_reg = trace_info_.dests[trace_info_.dest_count++];
|
||||
trace_reg.reg = reg;
|
||||
trace_reg.value = value;
|
||||
}
|
||||
|
||||
Value* PPCHIRBuilder::LoadFPR(uint32_t reg) {
|
||||
return LoadContext(offsetof(PPCContext, f) + reg * 8, FLOAT64_TYPE);
|
||||
}
|
||||
|
||||
void PPCHIRBuilder::StoreFPR(uint32_t reg, Value* value) {
|
||||
assert_true(value->type == FLOAT64_TYPE);
|
||||
StoreContext(offsetof(PPCContext, f) + reg * 8, value);
|
||||
|
||||
auto& trace_reg = trace_info_.dests[trace_info_.dest_count++];
|
||||
trace_reg.reg = reg + 32;
|
||||
trace_reg.value = value;
|
||||
}
|
||||
|
||||
Value* PPCHIRBuilder::LoadVR(uint32_t reg) {
|
||||
return LoadContext(offsetof(PPCContext, v) + reg * 16, VEC128_TYPE);
|
||||
}
|
||||
|
||||
void PPCHIRBuilder::StoreVR(uint32_t reg, Value* value) {
|
||||
assert_true(value->type == VEC128_TYPE);
|
||||
StoreContext(offsetof(PPCContext, v) + reg * 16, value);
|
||||
|
||||
auto& trace_reg = trace_info_.dests[trace_info_.dest_count++];
|
||||
trace_reg.reg = 128 + reg;
|
||||
trace_reg.value = value;
|
||||
}
|
||||
|
||||
Value* PPCHIRBuilder::LoadAcquire(Value* address, TypeName type,
|
||||
uint32_t load_flags) {
|
||||
AtomicExchange(LoadContext(offsetof(PPCContext, reserve_address), INT64_TYPE),
|
||||
Truncate(address, INT32_TYPE));
|
||||
Value* value = Load(address, type, load_flags);
|
||||
// Save the value so that we can compare it later in StoreRelease.
|
||||
AtomicExchange(LoadContext(offsetof(PPCContext, reserve_value), INT64_TYPE),
|
||||
value);
|
||||
return value;
|
||||
}
|
||||
|
||||
Value* PPCHIRBuilder::StoreRelease(Value* address, Value* value,
|
||||
uint32_t store_flags) {
|
||||
Value* old_address = AtomicExchange(
|
||||
LoadContext(offsetof(PPCContext, reserve_address), INT64_TYPE),
|
||||
LoadZero(INT32_TYPE));
|
||||
// HACK: ensure the reservation addresses match AND the value hasn't changed.
|
||||
Value* old_value = AtomicExchange(
|
||||
LoadContext(offsetof(PPCContext, reserve_value), INT64_TYPE),
|
||||
LoadZero(value->type));
|
||||
Value* current_value = Load(address, value->type);
|
||||
Value* eq = And(CompareEQ(Truncate(address, INT32_TYPE), old_address),
|
||||
CompareEQ(current_value, old_value));
|
||||
StoreContext(offsetof(PPCContext, cr0.cr0_eq), eq);
|
||||
StoreContext(offsetof(PPCContext, cr0.cr0_lt), LoadZero(INT8_TYPE));
|
||||
StoreContext(offsetof(PPCContext, cr0.cr0_gt), LoadZero(INT8_TYPE));
|
||||
auto skip_label = NewLabel();
|
||||
BranchFalse(eq, skip_label, BRANCH_UNLIKELY);
|
||||
Store(address, value, store_flags);
|
||||
MarkLabel(skip_label);
|
||||
return eq;
|
||||
}
|
||||
|
||||
} // namespace ppc
|
||||
} // namespace frontend
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
120
src/xenia/cpu/frontend/ppc/ppc_hir_builder.h
Normal file
120
src/xenia/cpu/frontend/ppc/ppc_hir_builder.h
Normal file
@@ -0,0 +1,120 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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_FRONTEND_PPC_PPC_HIR_BUILDER_H_
|
||||
#define XENIA_FRONTEND_PPC_PPC_HIR_BUILDER_H_
|
||||
|
||||
#include "xenia/cpu/hir/hir_builder.h"
|
||||
#include "xenia/cpu/runtime/function.h"
|
||||
#include "xenia/cpu/runtime/symbol_info.h"
|
||||
#include "poly/string_buffer.h"
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace frontend {
|
||||
namespace ppc {
|
||||
|
||||
class PPCFrontend;
|
||||
|
||||
class PPCHIRBuilder : public hir::HIRBuilder {
|
||||
using Instr = xe::cpu::hir::Instr;
|
||||
using Label = xe::cpu::hir::Label;
|
||||
using Value = xe::cpu::hir::Value;
|
||||
|
||||
public:
|
||||
PPCHIRBuilder(PPCFrontend* frontend);
|
||||
virtual ~PPCHIRBuilder();
|
||||
|
||||
virtual void Reset();
|
||||
|
||||
enum EmitFlags {
|
||||
// Emit comment nodes.
|
||||
EMIT_DEBUG_COMMENTS = 1 << 0,
|
||||
// Emit TraceSource nodes.
|
||||
EMIT_TRACE_SOURCE = 1 << 1,
|
||||
// Emit TraceSource nodes with the resulting values of the operations.
|
||||
EMIT_TRACE_SOURCE_VALUES = EMIT_TRACE_SOURCE | (1 << 2),
|
||||
};
|
||||
int Emit(runtime::FunctionInfo* symbol_info, uint32_t flags);
|
||||
|
||||
runtime::FunctionInfo* symbol_info() const { return symbol_info_; }
|
||||
runtime::FunctionInfo* LookupFunction(uint64_t address);
|
||||
Label* LookupLabel(uint64_t address);
|
||||
|
||||
Value* LoadLR();
|
||||
void StoreLR(Value* value);
|
||||
Value* LoadCTR();
|
||||
void StoreCTR(Value* value);
|
||||
Value* LoadCR();
|
||||
Value* LoadCR(uint32_t n);
|
||||
Value* LoadCRField(uint32_t n, uint32_t bit);
|
||||
void StoreCR(Value* value);
|
||||
void StoreCR(uint32_t n, Value* value);
|
||||
void StoreCRField(uint32_t n, uint32_t bit, Value* value);
|
||||
void UpdateCR(uint32_t n, Value* lhs, bool is_signed = true);
|
||||
void UpdateCR(uint32_t n, Value* lhs, Value* rhs, bool is_signed = true);
|
||||
void UpdateCR6(Value* src_value);
|
||||
Value* LoadMSR();
|
||||
void StoreMSR(Value* value);
|
||||
Value* LoadFPSCR();
|
||||
void StoreFPSCR(Value* value);
|
||||
Value* LoadXER();
|
||||
void StoreXER(Value* value);
|
||||
// void UpdateXERWithOverflow();
|
||||
// void UpdateXERWithOverflowAndCarry();
|
||||
// void StoreOV(Value* value);
|
||||
Value* LoadCA();
|
||||
void StoreCA(Value* value);
|
||||
Value* LoadSAT();
|
||||
void StoreSAT(Value* value);
|
||||
|
||||
Value* LoadGPR(uint32_t reg);
|
||||
void StoreGPR(uint32_t reg, Value* value);
|
||||
Value* LoadFPR(uint32_t reg);
|
||||
void StoreFPR(uint32_t reg, Value* value);
|
||||
Value* LoadVR(uint32_t reg);
|
||||
void StoreVR(uint32_t reg, Value* value);
|
||||
|
||||
Value* LoadAcquire(Value* address, hir::TypeName type,
|
||||
uint32_t load_flags = 0);
|
||||
Value* StoreRelease(Value* address, Value* value, uint32_t store_flags = 0);
|
||||
|
||||
private:
|
||||
void AnnotateLabel(uint64_t address, Label* label);
|
||||
|
||||
private:
|
||||
PPCFrontend* frontend_;
|
||||
|
||||
// Reset whenever needed:
|
||||
poly::StringBuffer comment_buffer_;
|
||||
|
||||
// Reset each Emit:
|
||||
bool with_debug_info_;
|
||||
runtime::FunctionInfo* symbol_info_;
|
||||
uint64_t start_address_;
|
||||
uint64_t instr_count_;
|
||||
Instr** instr_offset_list_;
|
||||
Label** label_list_;
|
||||
|
||||
// Reset each instruction.
|
||||
struct {
|
||||
uint32_t dest_count;
|
||||
struct {
|
||||
uint8_t reg;
|
||||
Value* value;
|
||||
} dests[4];
|
||||
} trace_info_;
|
||||
};
|
||||
|
||||
} // namespace ppc
|
||||
} // namespace frontend
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_FRONTEND_PPC_PPC_HIR_BUILDER_H_
|
||||
408
src/xenia/cpu/frontend/ppc/ppc_instr.cc
Normal file
408
src/xenia/cpu/frontend/ppc/ppc_instr.cc
Normal file
@@ -0,0 +1,408 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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/frontend/ppc/ppc_instr.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include "xenia/cpu/frontend/ppc/ppc_instr_tables.h"
|
||||
#include "poly/poly.h"
|
||||
#include "poly/string_buffer.h"
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace frontend {
|
||||
namespace ppc {
|
||||
|
||||
std::vector<InstrType*> all_instrs_;
|
||||
|
||||
void DumpAllInstrCounts() {
|
||||
poly::StringBuffer sb;
|
||||
sb.Append("Instruction translation counts:\n");
|
||||
for (auto instr_type : all_instrs_) {
|
||||
if (instr_type->translation_count) {
|
||||
sb.Append("%8d : %s\n", instr_type->translation_count, instr_type->name);
|
||||
}
|
||||
}
|
||||
fprintf(stdout, sb.GetString());
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
void InstrOperand::Dump(std::string& out_str) {
|
||||
if (display) {
|
||||
out_str += display;
|
||||
return;
|
||||
}
|
||||
|
||||
char buffer[32];
|
||||
const size_t max_count = poly::countof(buffer);
|
||||
switch (type) {
|
||||
case InstrOperand::kRegister:
|
||||
switch (reg.set) {
|
||||
case InstrRegister::kXER:
|
||||
snprintf(buffer, max_count, "XER");
|
||||
break;
|
||||
case InstrRegister::kLR:
|
||||
snprintf(buffer, max_count, "LR");
|
||||
break;
|
||||
case InstrRegister::kCTR:
|
||||
snprintf(buffer, max_count, "CTR");
|
||||
break;
|
||||
case InstrRegister::kCR:
|
||||
snprintf(buffer, max_count, "CR%d", reg.ordinal);
|
||||
break;
|
||||
case InstrRegister::kFPSCR:
|
||||
snprintf(buffer, max_count, "FPSCR");
|
||||
break;
|
||||
case InstrRegister::kGPR:
|
||||
snprintf(buffer, max_count, "r%d", reg.ordinal);
|
||||
break;
|
||||
case InstrRegister::kFPR:
|
||||
snprintf(buffer, max_count, "f%d", reg.ordinal);
|
||||
break;
|
||||
case InstrRegister::kVMX:
|
||||
snprintf(buffer, max_count, "vr%d", reg.ordinal);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case InstrOperand::kImmediate:
|
||||
switch (imm.width) {
|
||||
case 1:
|
||||
if (imm.is_signed) {
|
||||
snprintf(buffer, max_count, "%d", (int32_t)(int8_t) imm.value);
|
||||
} else {
|
||||
snprintf(buffer, max_count, "0x%.2X", (uint8_t)imm.value);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (imm.is_signed) {
|
||||
snprintf(buffer, max_count, "%d", (int32_t)(int16_t) imm.value);
|
||||
} else {
|
||||
snprintf(buffer, max_count, "0x%.4X", (uint16_t)imm.value);
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
if (imm.is_signed) {
|
||||
snprintf(buffer, max_count, "%d", (int32_t)imm.value);
|
||||
} else {
|
||||
snprintf(buffer, max_count, "0x%.8X", (uint32_t)imm.value);
|
||||
}
|
||||
break;
|
||||
case 8:
|
||||
if (imm.is_signed) {
|
||||
snprintf(buffer, max_count, "%lld", (int64_t)imm.value);
|
||||
} else {
|
||||
snprintf(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:
|
||||
assert_unhandled_case(reg.set);
|
||||
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) {
|
||||
this->name = name;
|
||||
this->info = info;
|
||||
this->flags = flags;
|
||||
}
|
||||
|
||||
void InstrDisasm::AddLR(InstrRegister::Access access) {}
|
||||
|
||||
void InstrDisasm::AddCTR(InstrRegister::Access access) {}
|
||||
|
||||
void InstrDisasm::AddCR(uint32_t bf, InstrRegister::Access access) {}
|
||||
|
||||
void InstrDisasm::AddFPSCR(InstrRegister::Access access) {}
|
||||
|
||||
void InstrDisasm::AddRegOperand(InstrRegister::RegisterSet set,
|
||||
uint32_t ordinal, InstrRegister::Access access,
|
||||
const char* display) {}
|
||||
|
||||
void InstrDisasm::AddSImmOperand(uint64_t value, size_t width,
|
||||
const char* display) {}
|
||||
|
||||
void InstrDisasm::AddUImmOperand(uint64_t value, size_t width,
|
||||
const char* display) {}
|
||||
|
||||
int InstrDisasm::Finish() { 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";
|
||||
}
|
||||
}
|
||||
|
||||
InstrType* GetInstrType(uint32_t code) {
|
||||
// Fast lookup via tables.
|
||||
InstrType* slot = NULL;
|
||||
switch (code >> 26) {
|
||||
case 4:
|
||||
// Opcode = 4, index = bits 10-0 (10)
|
||||
slot = tables::instr_table_4[select_bits(code, 0, 10)];
|
||||
break;
|
||||
case 19:
|
||||
// Opcode = 19, index = bits 10-1 (10)
|
||||
slot = tables::instr_table_19[select_bits(code, 1, 10)];
|
||||
break;
|
||||
case 30:
|
||||
// Opcode = 30, index = bits 4-1 (4)
|
||||
// Special cased to an uber instruction.
|
||||
slot = tables::instr_table_30[select_bits(code, 0, 0)];
|
||||
break;
|
||||
case 31:
|
||||
// Opcode = 31, index = bits 10-1 (10)
|
||||
slot = tables::instr_table_31[select_bits(code, 1, 10)];
|
||||
break;
|
||||
case 58:
|
||||
// Opcode = 58, index = bits 1-0 (2)
|
||||
slot = tables::instr_table_58[select_bits(code, 0, 1)];
|
||||
break;
|
||||
case 59:
|
||||
// Opcode = 59, index = bits 5-1 (5)
|
||||
slot = tables::instr_table_59[select_bits(code, 1, 5)];
|
||||
break;
|
||||
case 62:
|
||||
// Opcode = 62, index = bits 1-0 (2)
|
||||
slot = tables::instr_table_62[select_bits(code, 0, 1)];
|
||||
break;
|
||||
case 63:
|
||||
// Opcode = 63, index = bits 10-1 (10)
|
||||
slot = tables::instr_table_63[select_bits(code, 1, 10)];
|
||||
break;
|
||||
default:
|
||||
slot = tables::instr_table[select_bits(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 < poly::countof(tables::instr_table_scan); n++) {
|
||||
slot = &(tables::instr_table_scan[n]);
|
||||
if (slot->opcode == (code & slot->opcode_mask)) {
|
||||
return slot;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int RegisterInstrEmit(uint32_t code, InstrEmitFn emit) {
|
||||
InstrType* instr_type = GetInstrType(code);
|
||||
assert_not_null(instr_type);
|
||||
if (!instr_type) {
|
||||
return 1;
|
||||
}
|
||||
all_instrs_.push_back(instr_type);
|
||||
assert_null(instr_type->emit);
|
||||
instr_type->emit = emit;
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace ppc
|
||||
} // namespace frontend
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
577
src/xenia/cpu/frontend/ppc/ppc_instr.h
Normal file
577
src/xenia/cpu/frontend/ppc/ppc_instr.h
Normal file
@@ -0,0 +1,577 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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_FRONTEND_PPC_PPC_INSTR_H_
|
||||
#define XENIA_FRONTEND_PPC_PPC_INSTR_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "poly/string_buffer.h"
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace frontend {
|
||||
namespace ppc {
|
||||
|
||||
inline uint32_t make_bitmask(uint32_t a, uint32_t b) {
|
||||
return (static_cast<uint32_t>(-1) >> (31 - b)) & ~((1u << a) - 1);
|
||||
}
|
||||
|
||||
inline uint32_t select_bits(uint32_t value, uint32_t a, uint32_t b) {
|
||||
return (value & make_bitmask(a, b)) >> a;
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
enum xe_ppc_instr_mask_e : uint32_t {
|
||||
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,
|
||||
};
|
||||
|
||||
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 int64_t XEEXTS16(uint32_t v) { return (int64_t)((int16_t)v); }
|
||||
static inline int64_t XEEXTS26(uint32_t v) {
|
||||
return (int64_t)(v & 0x02000000 ? (int32_t)v | 0xFC000000 : (int32_t)(v));
|
||||
}
|
||||
static inline uint64_t XEEXTZ16(uint32_t v) { return (uint64_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;
|
||||
uint64_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 W : 1;
|
||||
uint32_t FM : 8;
|
||||
uint32_t L : 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;
|
||||
|
||||
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 void (*InstrDisasmFn)(InstrData& i, poly::StringBuffer* str);
|
||||
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
|
||||
InstrDisasmFn disasm;
|
||||
char name[16];
|
||||
uint32_t translation_count;
|
||||
|
||||
InstrEmitFn emit;
|
||||
};
|
||||
|
||||
void DumpAllInstrCounts();
|
||||
InstrType* GetInstrType(uint32_t code);
|
||||
int RegisterInstrEmit(uint32_t code, InstrEmitFn emit);
|
||||
|
||||
} // namespace ppc
|
||||
} // namespace frontend
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_FRONTEND_PPC_PPC_INSTR_H_
|
||||
1102
src/xenia/cpu/frontend/ppc/ppc_instr_tables.h
Normal file
1102
src/xenia/cpu/frontend/ppc/ppc_instr_tables.h
Normal file
File diff suppressed because it is too large
Load Diff
365
src/xenia/cpu/frontend/ppc/ppc_scanner.cc
Normal file
365
src/xenia/cpu/frontend/ppc/ppc_scanner.cc
Normal file
@@ -0,0 +1,365 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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/frontend/ppc/ppc_scanner.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
|
||||
#include "xenia/cpu/frontend/ppc/ppc_frontend.h"
|
||||
#include "xenia/cpu/frontend/ppc/ppc_instr.h"
|
||||
#include "xenia/cpu/runtime/runtime.h"
|
||||
#include "poly/logging.h"
|
||||
#include "poly/memory.h"
|
||||
#include "xenia/profiling.h"
|
||||
|
||||
#if 0
|
||||
#define LOGPPC(fmt, ...) PLOGCORE('p', fmt, ##__VA_ARGS__)
|
||||
#else
|
||||
#define LOGPPC(fmt, ...) POLY_EMPTY_MACRO
|
||||
#endif
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace frontend {
|
||||
namespace ppc {
|
||||
|
||||
using xe::cpu::runtime::FunctionInfo;
|
||||
|
||||
PPCScanner::PPCScanner(PPCFrontend* frontend) : frontend_(frontend) {}
|
||||
|
||||
PPCScanner::~PPCScanner() {}
|
||||
|
||||
bool PPCScanner::IsRestGprLr(uint64_t address) {
|
||||
FunctionInfo* symbol_info;
|
||||
if (frontend_->runtime()->LookupFunctionInfo(address, &symbol_info)) {
|
||||
return false;
|
||||
}
|
||||
return symbol_info->behavior() == FunctionInfo::BEHAVIOR_EPILOG_RETURN;
|
||||
}
|
||||
|
||||
int PPCScanner::FindExtents(FunctionInfo* symbol_info) {
|
||||
// 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. 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.
|
||||
|
||||
Memory* memory = frontend_->memory();
|
||||
const uint8_t* p = memory->membase();
|
||||
|
||||
LOGPPC("Analyzing function %.8X...", symbol_info->address());
|
||||
|
||||
uint32_t start_address = static_cast<uint32_t>(symbol_info->address());
|
||||
uint32_t end_address = static_cast<uint32_t>(symbol_info->end_address());
|
||||
uint32_t address = start_address;
|
||||
uint32_t furthest_target = start_address;
|
||||
size_t blocks_found = 0;
|
||||
bool in_block = false;
|
||||
bool starts_with_mfspr_lr = false;
|
||||
InstrData i;
|
||||
while (true) {
|
||||
i.address = address;
|
||||
i.code = poly::load_and_swap<uint32_t>(p + address);
|
||||
|
||||
// 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) {
|
||||
LOGPPC("function end %.8X (0x00000000 read)", address);
|
||||
// Don't include the 0's.
|
||||
address -= 4;
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO(benvanik): find a way to avoid using the opcode tables.
|
||||
// This lookup is *expensive* and should be avoided when scanning.
|
||||
i.type = GetInstrType(i.code);
|
||||
|
||||
// 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 (address == start_address && i.type && i.type->opcode == 0x7C0002A6 &&
|
||||
(((i.XFX.spr & 0x1F) << 5) | ((i.XFX.spr >> 5) & 0x1F)) == 8) {
|
||||
starts_with_mfspr_lr = true;
|
||||
}
|
||||
|
||||
if (!in_block) {
|
||||
in_block = true;
|
||||
blocks_found++;
|
||||
}
|
||||
|
||||
bool ends_fn = false;
|
||||
bool ends_block = false;
|
||||
if (!i.type) {
|
||||
// Invalid instruction.
|
||||
// We can just ignore it because there's (very little)/no chance it'll
|
||||
// affect flow control.
|
||||
LOGPPC("Invalid instruction at %.8X: %.8X", address, i.code);
|
||||
} else if (i.code == 0x4E800020) {
|
||||
// blr -- unconditional branch to LR.
|
||||
// This is generally a return.
|
||||
if (furthest_target > address) {
|
||||
// Remaining targets within function, not end.
|
||||
LOGPPC("ignoring blr %.8X (branch to %.8X)", address, furthest_target);
|
||||
} else {
|
||||
// Function end point.
|
||||
LOGPPC("function end %.8X", address);
|
||||
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).
|
||||
// This is almost always a jump table.
|
||||
// TODO(benvanik): decode jump tables.
|
||||
if (furthest_target > address) {
|
||||
// Remaining targets within function, not end.
|
||||
LOGPPC("ignoring bctr %.8X (branch to %.8X)", address, furthest_target);
|
||||
} else {
|
||||
// Function end point.
|
||||
LOGPPC("function end %.8X", address);
|
||||
ends_fn = true;
|
||||
}
|
||||
ends_block = true;
|
||||
} else if (i.type->opcode == 0x48000000) {
|
||||
// b/ba/bl/bla
|
||||
uint32_t target =
|
||||
(uint32_t)XEEXTS26(i.I.LI << 2) + (i.I.AA ? 0 : (int32_t)address);
|
||||
|
||||
if (i.I.LK) {
|
||||
LOGPPC("bl %.8X -> %.8X", address, target);
|
||||
// Queue call target if needed.
|
||||
// GetOrInsertFunction(target);
|
||||
} else {
|
||||
LOGPPC("b %.8X -> %.8X", address, target);
|
||||
|
||||
// If the target is back into the function and there's no further target
|
||||
// we are at the end of a function.
|
||||
// (Indirect branches may still go beyond, but no way of knowing).
|
||||
if (target >= start_address && target < address &&
|
||||
furthest_target <= address) {
|
||||
LOGPPC("function end %.8X (back b)", address);
|
||||
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 < start_address && furthest_target <= address) {
|
||||
LOGPPC("function end %.8X (back b before addr)", address);
|
||||
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 <= address && IsRestGprLr(target)) {
|
||||
LOGPPC("function end %.8X (__restgprlr_*)", address);
|
||||
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 && blocks_found == 1) {
|
||||
LOGPPC("HEURISTIC: ending at simple leaf thunk %.8X", address);
|
||||
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) {
|
||||
LOGPPC("HEURISTIC: ending at tail call branch %.8X", addr);
|
||||
ends_fn = true;
|
||||
}
|
||||
*/
|
||||
|
||||
if (!ends_fn && !IsRestGprLr(target)) {
|
||||
furthest_target = std::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 =
|
||||
(uint32_t)XEEXTS16(i.B.BD << 2) + (i.B.AA ? 0 : (int32_t)address);
|
||||
if (i.B.LK) {
|
||||
LOGPPC("bcl %.8X -> %.8X", address, target);
|
||||
|
||||
// Queue call target if needed.
|
||||
// TODO(benvanik): see if this is correct - not sure anyone makes
|
||||
// function calls with bcl.
|
||||
// GetOrInsertFunction(target);
|
||||
} else {
|
||||
LOGPPC("bc %.8X -> %.8X", address, target);
|
||||
|
||||
// TODO(benvanik): GetOrInsertFunction? it's likely a BB
|
||||
|
||||
if (!IsRestGprLr(target)) {
|
||||
furthest_target = std::max(furthest_target, target);
|
||||
}
|
||||
}
|
||||
ends_block = true;
|
||||
} else if (i.type->opcode == 0x4C000020) {
|
||||
// bclr/bclrl
|
||||
if (i.XL.LK) {
|
||||
LOGPPC("bclrl %.8X", address);
|
||||
} else {
|
||||
LOGPPC("bclr %.8X", address);
|
||||
}
|
||||
ends_block = true;
|
||||
} else if (i.type->opcode == 0x4C000420) {
|
||||
// bcctr/bcctrl
|
||||
if (i.XL.LK) {
|
||||
LOGPPC("bcctrl %.8X", address);
|
||||
} else {
|
||||
LOGPPC("bcctr %.8X", address);
|
||||
}
|
||||
ends_block = true;
|
||||
}
|
||||
|
||||
if (ends_block) {
|
||||
in_block = false;
|
||||
}
|
||||
if (ends_fn) {
|
||||
break;
|
||||
}
|
||||
|
||||
address += 4;
|
||||
if (end_address && address > end_address) {
|
||||
// Hmm....
|
||||
LOGPPC("Ran over function bounds! %.8X-%.8X", start_address, end_address);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (end_address && address + 4 < 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.
|
||||
LOGPPC("Function ran under: %.8X-%.8X ended at %.8X", start_address,
|
||||
end_address, address + 4);
|
||||
}
|
||||
symbol_info->set_end_address(address);
|
||||
|
||||
// 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
|
||||
|
||||
LOGPPC("Finished analyzing %.8X", start_address);
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::vector<BlockInfo> PPCScanner::FindBlocks(FunctionInfo* symbol_info) {
|
||||
Memory* memory = frontend_->memory();
|
||||
const uint8_t* p = memory->membase();
|
||||
|
||||
std::map<uint64_t, BlockInfo> block_map;
|
||||
|
||||
uint64_t start_address = symbol_info->address();
|
||||
uint64_t end_address = symbol_info->end_address();
|
||||
bool in_block = false;
|
||||
uint64_t block_start = 0;
|
||||
InstrData i;
|
||||
for (uint64_t address = start_address; address <= end_address; address += 4) {
|
||||
i.address = address;
|
||||
i.code = poly::load_and_swap<uint32_t>(p + address);
|
||||
if (!i.code) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO(benvanik): find a way to avoid using the opcode tables.
|
||||
// This lookup is *expensive* and should be avoided when scanning.
|
||||
i.type = GetInstrType(i.code);
|
||||
|
||||
if (!in_block) {
|
||||
in_block = true;
|
||||
block_start = address;
|
||||
}
|
||||
|
||||
bool ends_block = false;
|
||||
if (!i.type) {
|
||||
// Invalid instruction.
|
||||
} else if (i.code == 0x4E800020) {
|
||||
// blr -- unconditional branch to LR.
|
||||
ends_block = true;
|
||||
} else if (i.code == 0x4E800420) {
|
||||
// bctr -- unconditional branch to CTR.
|
||||
// This is almost always a jump table.
|
||||
// TODO(benvanik): decode jump tables.
|
||||
ends_block = true;
|
||||
} else if (i.type->opcode == 0x48000000) {
|
||||
// b/ba/bl/bla
|
||||
// uint32_t target =
|
||||
// (uint32_t)XEEXTS26(i.I.LI << 2) + (i.I.AA ? 0 : (int32_t)address);
|
||||
ends_block = true;
|
||||
} else if (i.type->opcode == 0x40000000) {
|
||||
// bc/bca/bcl/bcla
|
||||
// uint32_t target =
|
||||
// (uint32_t)XEEXTS16(i.B.BD << 2) + (i.B.AA ? 0 : (int32_t)address);
|
||||
ends_block = true;
|
||||
} else if (i.type->opcode == 0x4C000020) {
|
||||
// bclr/bclrl
|
||||
ends_block = true;
|
||||
} else if (i.type->opcode == 0x4C000420) {
|
||||
// bcctr/bcctrl
|
||||
ends_block = true;
|
||||
}
|
||||
|
||||
if (ends_block) {
|
||||
in_block = false;
|
||||
block_map[block_start] = {
|
||||
block_start, address,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (in_block) {
|
||||
block_map[block_start] = {
|
||||
block_start, end_address,
|
||||
};
|
||||
}
|
||||
|
||||
std::vector<BlockInfo> blocks;
|
||||
for (auto it = block_map.begin(); it != block_map.end(); ++it) {
|
||||
blocks.push_back(it->second);
|
||||
}
|
||||
return blocks;
|
||||
}
|
||||
|
||||
} // namespace ppc
|
||||
} // namespace frontend
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
50
src/xenia/cpu/frontend/ppc/ppc_scanner.h
Normal file
50
src/xenia/cpu/frontend/ppc/ppc_scanner.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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_FRONTEND_PPC_PPC_SCANNER_H_
|
||||
#define XENIA_FRONTEND_PPC_PPC_SCANNER_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "xenia/cpu/runtime/symbol_info.h"
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace frontend {
|
||||
namespace ppc {
|
||||
|
||||
class PPCFrontend;
|
||||
|
||||
typedef struct BlockInfo_t {
|
||||
uint64_t start_address;
|
||||
uint64_t end_address;
|
||||
} BlockInfo;
|
||||
|
||||
class PPCScanner {
|
||||
public:
|
||||
PPCScanner(PPCFrontend* frontend);
|
||||
~PPCScanner();
|
||||
|
||||
int FindExtents(runtime::FunctionInfo* symbol_info);
|
||||
|
||||
std::vector<BlockInfo> FindBlocks(runtime::FunctionInfo* symbol_info);
|
||||
|
||||
private:
|
||||
bool IsRestGprLr(uint64_t address);
|
||||
|
||||
private:
|
||||
PPCFrontend* frontend_;
|
||||
};
|
||||
|
||||
} // namespace ppc
|
||||
} // namespace frontend
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_FRONTEND_PPC_PPC_SCANNER_H_
|
||||
216
src/xenia/cpu/frontend/ppc/ppc_translator.cc
Normal file
216
src/xenia/cpu/frontend/ppc/ppc_translator.cc
Normal file
@@ -0,0 +1,216 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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/frontend/ppc/ppc_translator.h"
|
||||
|
||||
#include "xenia/cpu/compiler/compiler_passes.h"
|
||||
#include "xenia/cpu/cpu-private.h"
|
||||
#include "xenia/cpu/frontend/ppc/ppc_disasm.h"
|
||||
#include "xenia/cpu/frontend/ppc/ppc_frontend.h"
|
||||
#include "xenia/cpu/frontend/ppc/ppc_hir_builder.h"
|
||||
#include "xenia/cpu/frontend/ppc/ppc_instr.h"
|
||||
#include "xenia/cpu/frontend/ppc/ppc_scanner.h"
|
||||
#include "xenia/cpu/runtime/runtime.h"
|
||||
#include "poly/reset_scope.h"
|
||||
#include "xenia/profiling.h"
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace frontend {
|
||||
namespace ppc {
|
||||
|
||||
// TODO(benvanik): remove when enums redefined.
|
||||
using namespace xe::cpu::runtime;
|
||||
|
||||
using xe::cpu::backend::Backend;
|
||||
using xe::cpu::compiler::Compiler;
|
||||
using xe::cpu::runtime::Function;
|
||||
using xe::cpu::runtime::FunctionInfo;
|
||||
namespace passes = xe::cpu::compiler::passes;
|
||||
|
||||
PPCTranslator::PPCTranslator(PPCFrontend* frontend) : frontend_(frontend) {
|
||||
Backend* backend = frontend->runtime()->backend();
|
||||
|
||||
scanner_.reset(new PPCScanner(frontend));
|
||||
builder_.reset(new PPCHIRBuilder(frontend));
|
||||
compiler_.reset(new Compiler(frontend->runtime()));
|
||||
assembler_ = std::move(backend->CreateAssembler());
|
||||
assembler_->Initialize();
|
||||
|
||||
bool validate = FLAGS_validate_hir;
|
||||
|
||||
// Merge blocks early. This will let us use more context in other passes.
|
||||
// The CFG is required for simplification and dirtied by it.
|
||||
compiler_->AddPass(std::make_unique<passes::ControlFlowAnalysisPass>());
|
||||
compiler_->AddPass(std::make_unique<passes::ControlFlowSimplificationPass>());
|
||||
compiler_->AddPass(std::make_unique<passes::ControlFlowAnalysisPass>());
|
||||
|
||||
// Passes are executed in the order they are added. Multiple of the same
|
||||
// pass type may be used.
|
||||
if (validate) compiler_->AddPass(std::make_unique<passes::ValidationPass>());
|
||||
compiler_->AddPass(std::make_unique<passes::ContextPromotionPass>());
|
||||
if (validate) compiler_->AddPass(std::make_unique<passes::ValidationPass>());
|
||||
compiler_->AddPass(std::make_unique<passes::SimplificationPass>());
|
||||
if (validate) compiler_->AddPass(std::make_unique<passes::ValidationPass>());
|
||||
compiler_->AddPass(std::make_unique<passes::ConstantPropagationPass>());
|
||||
if (validate) compiler_->AddPass(std::make_unique<passes::ValidationPass>());
|
||||
compiler_->AddPass(std::make_unique<passes::SimplificationPass>());
|
||||
if (validate) compiler_->AddPass(std::make_unique<passes::ValidationPass>());
|
||||
// compiler_->AddPass(std::make_unique<passes::DeadStoreEliminationPass>());
|
||||
// if (validate)
|
||||
// compiler_->AddPass(std::make_unique<passes::ValidationPass>());
|
||||
compiler_->AddPass(std::make_unique<passes::DeadCodeEliminationPass>());
|
||||
if (validate) compiler_->AddPass(std::make_unique<passes::ValidationPass>());
|
||||
|
||||
//// Removes all unneeded variables. Try not to add new ones after this.
|
||||
// compiler_->AddPass(new passes::ValueReductionPass());
|
||||
// if (validate) compiler_->AddPass(new passes::ValidationPass());
|
||||
|
||||
// Register allocation for the target backend.
|
||||
// Will modify the HIR to add loads/stores.
|
||||
// This should be the last pass before finalization, as after this all
|
||||
// registers are assigned and ready to be emitted.
|
||||
compiler_->AddPass(std::make_unique<passes::RegisterAllocationPass>(
|
||||
backend->machine_info()));
|
||||
if (validate) compiler_->AddPass(std::make_unique<passes::ValidationPass>());
|
||||
|
||||
// Must come last. The HIR is not really HIR after this.
|
||||
compiler_->AddPass(std::make_unique<passes::FinalizationPass>());
|
||||
}
|
||||
|
||||
PPCTranslator::~PPCTranslator() = default;
|
||||
|
||||
int PPCTranslator::Translate(FunctionInfo* symbol_info,
|
||||
uint32_t debug_info_flags, uint32_t trace_flags,
|
||||
Function** out_function) {
|
||||
SCOPE_profile_cpu_f("cpu");
|
||||
|
||||
// Reset() all caching when we leave.
|
||||
poly::make_reset_scope(builder_);
|
||||
poly::make_reset_scope(compiler_);
|
||||
poly::make_reset_scope(assembler_);
|
||||
poly::make_reset_scope(&string_buffer_);
|
||||
|
||||
// Scan the function to find its extents. We only need to do this if we
|
||||
// haven't already been provided with them from some other source.
|
||||
if (!symbol_info->has_end_address()) {
|
||||
// TODO(benvanik): find a way to remove the need for the scan. A fixup
|
||||
// scheme acting on branches could go back and modify calls to branches
|
||||
// if they are within the extents.
|
||||
int result = scanner_->FindExtents(symbol_info);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: we only want to do this when required, as it's expensive to build.
|
||||
if (FLAGS_always_disasm) {
|
||||
debug_info_flags |= DEBUG_INFO_ALL_DISASM;
|
||||
}
|
||||
std::unique_ptr<DebugInfo> debug_info;
|
||||
if (debug_info_flags) {
|
||||
debug_info.reset(new DebugInfo());
|
||||
}
|
||||
|
||||
// Stash source.
|
||||
if (debug_info_flags & DEBUG_INFO_SOURCE_DISASM) {
|
||||
DumpSource(symbol_info, &string_buffer_);
|
||||
debug_info->set_source_disasm(string_buffer_.ToString());
|
||||
string_buffer_.Reset();
|
||||
}
|
||||
|
||||
if (false) {
|
||||
xe::cpu::frontend::ppc::DumpAllInstrCounts();
|
||||
}
|
||||
|
||||
// Emit function.
|
||||
uint32_t emit_flags = 0;
|
||||
if (debug_info) {
|
||||
emit_flags |= PPCHIRBuilder::EMIT_DEBUG_COMMENTS;
|
||||
}
|
||||
if (trace_flags & TRACE_SOURCE_VALUES) {
|
||||
emit_flags |= PPCHIRBuilder::EMIT_TRACE_SOURCE_VALUES;
|
||||
} else if (trace_flags & TRACE_SOURCE) {
|
||||
emit_flags |= PPCHIRBuilder::EMIT_TRACE_SOURCE;
|
||||
}
|
||||
int result = builder_->Emit(symbol_info, emit_flags);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Stash raw HIR.
|
||||
if (debug_info_flags & DEBUG_INFO_RAW_HIR_DISASM) {
|
||||
builder_->Dump(&string_buffer_);
|
||||
debug_info->set_raw_hir_disasm(string_buffer_.ToString());
|
||||
string_buffer_.Reset();
|
||||
}
|
||||
|
||||
// Compile/optimize/etc.
|
||||
result = compiler_->Compile(builder_.get());
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Stash optimized HIR.
|
||||
if (debug_info_flags & DEBUG_INFO_HIR_DISASM) {
|
||||
builder_->Dump(&string_buffer_);
|
||||
debug_info->set_hir_disasm(string_buffer_.ToString());
|
||||
string_buffer_.Reset();
|
||||
}
|
||||
|
||||
// Assemble to backend machine code.
|
||||
result =
|
||||
assembler_->Assemble(symbol_info, builder_.get(), debug_info_flags,
|
||||
std::move(debug_info), trace_flags, out_function);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
void PPCTranslator::DumpSource(runtime::FunctionInfo* symbol_info,
|
||||
poly::StringBuffer* string_buffer) {
|
||||
Memory* memory = frontend_->memory();
|
||||
const uint8_t* p = memory->membase();
|
||||
|
||||
string_buffer->Append("%s fn %.8X-%.8X %s\n",
|
||||
symbol_info->module()->name().c_str(),
|
||||
symbol_info->address(), symbol_info->end_address(),
|
||||
symbol_info->name().c_str());
|
||||
|
||||
auto blocks = scanner_->FindBlocks(symbol_info);
|
||||
|
||||
uint64_t start_address = symbol_info->address();
|
||||
uint64_t end_address = symbol_info->end_address();
|
||||
InstrData i;
|
||||
auto block_it = blocks.begin();
|
||||
for (uint64_t address = start_address, offset = 0; address <= end_address;
|
||||
address += 4, offset++) {
|
||||
i.address = address;
|
||||
i.code = poly::load_and_swap<uint32_t>(p + address);
|
||||
// TODO(benvanik): find a way to avoid using the opcode tables.
|
||||
i.type = GetInstrType(i.code);
|
||||
|
||||
// Check labels.
|
||||
if (block_it != blocks.end() && block_it->start_address == address) {
|
||||
string_buffer->Append("%.8X loc_%.8X:\n", address, address);
|
||||
++block_it;
|
||||
}
|
||||
|
||||
string_buffer->Append("%.8X %.8X ", address, i.code);
|
||||
DisasmPPC(i, string_buffer);
|
||||
string_buffer->Append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ppc
|
||||
} // namespace frontend
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
56
src/xenia/cpu/frontend/ppc/ppc_translator.h
Normal file
56
src/xenia/cpu/frontend/ppc/ppc_translator.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_FRONTEND_PPC_PPC_TRANSLATOR_H_
|
||||
#define XENIA_FRONTEND_PPC_PPC_TRANSLATOR_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "xenia/cpu/backend/assembler.h"
|
||||
#include "xenia/cpu/compiler/compiler.h"
|
||||
#include "xenia/cpu/runtime/symbol_info.h"
|
||||
#include "poly/string_buffer.h"
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace frontend {
|
||||
namespace ppc {
|
||||
|
||||
class PPCFrontend;
|
||||
class PPCHIRBuilder;
|
||||
class PPCScanner;
|
||||
|
||||
class PPCTranslator {
|
||||
public:
|
||||
PPCTranslator(PPCFrontend* frontend);
|
||||
~PPCTranslator();
|
||||
|
||||
int Translate(runtime::FunctionInfo* symbol_info, uint32_t debug_info_flags,
|
||||
uint32_t trace_flags, runtime::Function** out_function);
|
||||
|
||||
private:
|
||||
void DumpSource(runtime::FunctionInfo* symbol_info,
|
||||
poly::StringBuffer* string_buffer);
|
||||
|
||||
private:
|
||||
PPCFrontend* frontend_;
|
||||
std::unique_ptr<PPCScanner> scanner_;
|
||||
std::unique_ptr<PPCHIRBuilder> builder_;
|
||||
std::unique_ptr<compiler::Compiler> compiler_;
|
||||
std::unique_ptr<backend::Assembler> assembler_;
|
||||
|
||||
poly::StringBuffer string_buffer_;
|
||||
};
|
||||
|
||||
} // namespace ppc
|
||||
} // namespace frontend
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_FRONTEND_PPC_PPC_TRANSLATOR_H_
|
||||
30
src/xenia/cpu/frontend/ppc/sources.gypi
Normal file
30
src/xenia/cpu/frontend/ppc/sources.gypi
Normal file
@@ -0,0 +1,30 @@
|
||||
# Copyright 2013 Ben Vanik. All Rights Reserved.
|
||||
{
|
||||
'sources': [
|
||||
'ppc_context.cc',
|
||||
'ppc_context.h',
|
||||
'ppc_disasm.cc',
|
||||
'ppc_disasm.h',
|
||||
'ppc_emit-private.h',
|
||||
'ppc_emit.h',
|
||||
'ppc_emit_altivec.cc',
|
||||
'ppc_emit_alu.cc',
|
||||
'ppc_emit_control.cc',
|
||||
'ppc_emit_fpu.cc',
|
||||
'ppc_emit_memory.cc',
|
||||
'ppc_frontend.cc',
|
||||
'ppc_frontend.h',
|
||||
'ppc_hir_builder.cc',
|
||||
'ppc_hir_builder.h',
|
||||
'ppc_instr.cc',
|
||||
'ppc_instr.h',
|
||||
'ppc_instr_tables.h',
|
||||
'ppc_scanner.cc',
|
||||
'ppc_scanner.h',
|
||||
'ppc_translator.cc',
|
||||
'ppc_translator.h',
|
||||
],
|
||||
|
||||
'includes': [
|
||||
],
|
||||
}
|
||||
61
src/xenia/cpu/frontend/ppc/test/README.md
Normal file
61
src/xenia/cpu/frontend/ppc/test/README.md
Normal file
@@ -0,0 +1,61 @@
|
||||
# Codegen Tests
|
||||
|
||||
This directory contains the test assets used by the automated codegen test
|
||||
runner.
|
||||
|
||||
Each test is structured as a source `[name].s` PPC assembly file and the
|
||||
generated outputs. The outputs are made using the custom build of binutils
|
||||
setup when `xenia-build setup` is called and are checked in to make it easier
|
||||
to run the tests on Windows.
|
||||
|
||||
Tests are run using the `xenia-test` app or via `xenia-build test`.
|
||||
|
||||
## Execution
|
||||
|
||||
The test binary is placed into memory at `0x82010000` and all other memory is
|
||||
zeroed.
|
||||
|
||||
All registers are reset to zero. In order to provide useful inputs tests can
|
||||
specify `# REGISTER_IN` values.
|
||||
|
||||
The code is jumped into at the starting address and executed until the last
|
||||
instruction in the input file is reached.
|
||||
|
||||
After all instructions complete any `# REGISTER_OUT` values are checked and if
|
||||
they do not match the test is failed.
|
||||
|
||||
## Annotations
|
||||
|
||||
Annotations can appear at any line in a file. If a number is required it can
|
||||
be in either hex or decimal form, or IEEE if floating-point.
|
||||
|
||||
### REGISTER_IN
|
||||
|
||||
```
|
||||
# REGISTER_IN [register name] [register value]
|
||||
```
|
||||
|
||||
Sets the value of a register prior to executing the instructions.
|
||||
|
||||
Examples:
|
||||
```
|
||||
# REGISTER_IN r4 0x1234
|
||||
# REGISTER_IN r4 5678
|
||||
```
|
||||
|
||||
### REGISTER_OUT
|
||||
|
||||
```
|
||||
# REGISTER_OUT [register name] [register value]
|
||||
```
|
||||
|
||||
Defines the expected register value when the instructions have executed.
|
||||
If after all instructions have completed the register value does not match
|
||||
the value given here the test will fail.
|
||||
|
||||
Examples:
|
||||
```
|
||||
# REGISTER_OUT r3 123
|
||||
```
|
||||
|
||||
TODO: memory setup/assertions
|
||||
BIN
src/xenia/cpu/frontend/ppc/test/bin/instr_add.bin
Normal file
BIN
src/xenia/cpu/frontend/ppc/test/bin/instr_add.bin
Normal file
Binary file not shown.
13
src/xenia/cpu/frontend/ppc/test/bin/instr_add.dis
Normal file
13
src/xenia/cpu/frontend/ppc/test/bin/instr_add.dis
Normal file
@@ -0,0 +1,13 @@
|
||||
|
||||
/vagrant/src/xenia/cpu/frontend/ppc/test/bin//instr_add.o: file format elf64-powerpc
|
||||
|
||||
|
||||
Disassembly of section .text:
|
||||
|
||||
0000000000100000 <test_add_1>:
|
||||
100000: 7d 65 ca 14 add r11,r5,r25
|
||||
100004: 4e 80 00 20 blr
|
||||
|
||||
0000000000100008 <test_add_2>:
|
||||
100008: 7d 60 ca 14 add r11,r0,r25
|
||||
10000c: 4e 80 00 20 blr
|
||||
2
src/xenia/cpu/frontend/ppc/test/bin/instr_add.map
Normal file
2
src/xenia/cpu/frontend/ppc/test/bin/instr_add.map
Normal file
@@ -0,0 +1,2 @@
|
||||
0000000000000000 t test_add_1
|
||||
0000000000000008 t test_add_2
|
||||
BIN
src/xenia/cpu/frontend/ppc/test/bin/instr_addc.bin
Normal file
BIN
src/xenia/cpu/frontend/ppc/test/bin/instr_addc.bin
Normal file
Binary file not shown.
30
src/xenia/cpu/frontend/ppc/test/bin/instr_addc.dis
Normal file
30
src/xenia/cpu/frontend/ppc/test/bin/instr_addc.dis
Normal file
@@ -0,0 +1,30 @@
|
||||
|
||||
/vagrant/src/xenia/cpu/frontend/ppc/test/bin//instr_addc.o: file format elf64-powerpc
|
||||
|
||||
|
||||
Disassembly of section .text:
|
||||
|
||||
0000000000100000 <test_addc_1>:
|
||||
100000: 7c 64 28 14 addc r3,r4,r5
|
||||
100004: 7c c0 01 14 adde r6,r0,r0
|
||||
100008: 4e 80 00 20 blr
|
||||
|
||||
000000000010000c <test_addc_2>:
|
||||
10000c: 7c 64 28 14 addc r3,r4,r5
|
||||
100010: 7c c0 01 14 adde r6,r0,r0
|
||||
100014: 4e 80 00 20 blr
|
||||
|
||||
0000000000100018 <test_addc_3>:
|
||||
100018: 7c 64 28 14 addc r3,r4,r5
|
||||
10001c: 7c c0 01 14 adde r6,r0,r0
|
||||
100020: 4e 80 00 20 blr
|
||||
|
||||
0000000000100024 <test_addc_4>:
|
||||
100024: 7c 64 28 14 addc r3,r4,r5
|
||||
100028: 7c c0 01 14 adde r6,r0,r0
|
||||
10002c: 4e 80 00 20 blr
|
||||
|
||||
0000000000100030 <test_addc_5>:
|
||||
100030: 7c 64 28 14 addc r3,r4,r5
|
||||
100034: 7c c0 01 14 adde r6,r0,r0
|
||||
100038: 4e 80 00 20 blr
|
||||
5
src/xenia/cpu/frontend/ppc/test/bin/instr_addc.map
Normal file
5
src/xenia/cpu/frontend/ppc/test/bin/instr_addc.map
Normal file
@@ -0,0 +1,5 @@
|
||||
0000000000000000 t test_addc_1
|
||||
000000000000000c t test_addc_2
|
||||
0000000000000018 t test_addc_3
|
||||
0000000000000024 t test_addc_4
|
||||
0000000000000030 t test_addc_5
|
||||
BIN
src/xenia/cpu/frontend/ppc/test/bin/instr_adde.bin
Normal file
BIN
src/xenia/cpu/frontend/ppc/test/bin/instr_adde.bin
Normal file
Binary file not shown.
70
src/xenia/cpu/frontend/ppc/test/bin/instr_adde.dis
Normal file
70
src/xenia/cpu/frontend/ppc/test/bin/instr_adde.dis
Normal file
@@ -0,0 +1,70 @@
|
||||
|
||||
/vagrant/src/xenia/cpu/frontend/ppc/test/bin//instr_adde.o: file format elf64-powerpc
|
||||
|
||||
|
||||
Disassembly of section .text:
|
||||
|
||||
0000000000100000 <test_adde_1>:
|
||||
100000: 7c 64 29 14 adde r3,r4,r5
|
||||
100004: 7c c0 01 14 adde r6,r0,r0
|
||||
100008: 4e 80 00 20 blr
|
||||
|
||||
000000000010000c <test_adde_2>:
|
||||
10000c: 7c 63 1a 78 xor r3,r3,r3
|
||||
100010: 7c 63 18 f8 not r3,r3
|
||||
100014: 30 63 00 01 addic r3,r3,1
|
||||
100018: 7c 64 29 14 adde r3,r4,r5
|
||||
10001c: 7c c0 01 14 adde r6,r0,r0
|
||||
100020: 4e 80 00 20 blr
|
||||
|
||||
0000000000100024 <test_adde_3>:
|
||||
100024: 7c 64 29 14 adde r3,r4,r5
|
||||
100028: 7c c0 01 14 adde r6,r0,r0
|
||||
10002c: 4e 80 00 20 blr
|
||||
|
||||
0000000000100030 <test_adde_4>:
|
||||
100030: 7c 63 1a 78 xor r3,r3,r3
|
||||
100034: 7c 63 18 f8 not r3,r3
|
||||
100038: 30 63 00 01 addic r3,r3,1
|
||||
10003c: 7c 64 29 14 adde r3,r4,r5
|
||||
100040: 7c c0 01 14 adde r6,r0,r0
|
||||
100044: 4e 80 00 20 blr
|
||||
|
||||
0000000000100048 <test_adde_5>:
|
||||
100048: 7c 64 29 14 adde r3,r4,r5
|
||||
10004c: 7c c0 01 14 adde r6,r0,r0
|
||||
100050: 4e 80 00 20 blr
|
||||
|
||||
0000000000100054 <test_adde_6>:
|
||||
100054: 7c 63 1a 78 xor r3,r3,r3
|
||||
100058: 7c 63 18 f8 not r3,r3
|
||||
10005c: 30 63 00 01 addic r3,r3,1
|
||||
100060: 7c 64 29 14 adde r3,r4,r5
|
||||
100064: 7c c0 01 14 adde r6,r0,r0
|
||||
100068: 4e 80 00 20 blr
|
||||
|
||||
000000000010006c <test_adde_7>:
|
||||
10006c: 7c 64 29 14 adde r3,r4,r5
|
||||
100070: 7c c0 01 14 adde r6,r0,r0
|
||||
100074: 4e 80 00 20 blr
|
||||
|
||||
0000000000100078 <test_adde_8>:
|
||||
100078: 7c 63 1a 78 xor r3,r3,r3
|
||||
10007c: 7c 63 18 f8 not r3,r3
|
||||
100080: 30 63 00 01 addic r3,r3,1
|
||||
100084: 7c 64 29 14 adde r3,r4,r5
|
||||
100088: 7c c0 01 14 adde r6,r0,r0
|
||||
10008c: 4e 80 00 20 blr
|
||||
|
||||
0000000000100090 <test_adde_9>:
|
||||
100090: 7c 64 29 14 adde r3,r4,r5
|
||||
100094: 7c c0 01 14 adde r6,r0,r0
|
||||
100098: 4e 80 00 20 blr
|
||||
|
||||
000000000010009c <test_adde_10>:
|
||||
10009c: 7c 63 1a 78 xor r3,r3,r3
|
||||
1000a0: 7c 63 18 f8 not r3,r3
|
||||
1000a4: 30 63 00 01 addic r3,r3,1
|
||||
1000a8: 7c 64 29 14 adde r3,r4,r5
|
||||
1000ac: 7c c0 01 14 adde r6,r0,r0
|
||||
1000b0: 4e 80 00 20 blr
|
||||
10
src/xenia/cpu/frontend/ppc/test/bin/instr_adde.map
Normal file
10
src/xenia/cpu/frontend/ppc/test/bin/instr_adde.map
Normal file
@@ -0,0 +1,10 @@
|
||||
0000000000000000 t test_adde_1
|
||||
000000000000000c t test_adde_2
|
||||
0000000000000024 t test_adde_3
|
||||
0000000000000030 t test_adde_4
|
||||
0000000000000048 t test_adde_5
|
||||
0000000000000054 t test_adde_6
|
||||
000000000000006c t test_adde_7
|
||||
0000000000000078 t test_adde_8
|
||||
0000000000000090 t test_adde_9
|
||||
000000000000009c t test_adde_10
|
||||
BIN
src/xenia/cpu/frontend/ppc/test/bin/instr_addic.bin
Normal file
BIN
src/xenia/cpu/frontend/ppc/test/bin/instr_addic.bin
Normal file
Binary file not shown.
15
src/xenia/cpu/frontend/ppc/test/bin/instr_addic.dis
Normal file
15
src/xenia/cpu/frontend/ppc/test/bin/instr_addic.dis
Normal file
@@ -0,0 +1,15 @@
|
||||
|
||||
/vagrant/src/xenia/cpu/frontend/ppc/test/bin//instr_addic.o: file format elf64-powerpc
|
||||
|
||||
|
||||
Disassembly of section .text:
|
||||
|
||||
0000000000100000 <test_addic_1>:
|
||||
100000: 30 84 00 01 addic r4,r4,1
|
||||
100004: 7c c0 01 14 adde r6,r0,r0
|
||||
100008: 4e 80 00 20 blr
|
||||
|
||||
000000000010000c <test_addic_2>:
|
||||
10000c: 30 84 00 01 addic r4,r4,1
|
||||
100010: 7c c0 01 14 adde r6,r0,r0
|
||||
100014: 4e 80 00 20 blr
|
||||
2
src/xenia/cpu/frontend/ppc/test/bin/instr_addic.map
Normal file
2
src/xenia/cpu/frontend/ppc/test/bin/instr_addic.map
Normal file
@@ -0,0 +1,2 @@
|
||||
0000000000000000 t test_addic_1
|
||||
000000000000000c t test_addic_2
|
||||
BIN
src/xenia/cpu/frontend/ppc/test/bin/instr_addme.bin
Normal file
BIN
src/xenia/cpu/frontend/ppc/test/bin/instr_addme.bin
Normal file
Binary file not shown.
57
src/xenia/cpu/frontend/ppc/test/bin/instr_addme.dis
Normal file
57
src/xenia/cpu/frontend/ppc/test/bin/instr_addme.dis
Normal file
@@ -0,0 +1,57 @@
|
||||
|
||||
/vagrant/src/xenia/cpu/frontend/ppc/test/bin//instr_addme.o: file format elf64-powerpc
|
||||
|
||||
|
||||
Disassembly of section .text:
|
||||
|
||||
0000000000100000 <test_addme_1>:
|
||||
100000: 7c 64 01 d4 addme r3,r4
|
||||
100004: 7c c0 01 14 adde r6,r0,r0
|
||||
100008: 4e 80 00 20 blr
|
||||
|
||||
000000000010000c <test_addme_2>:
|
||||
10000c: 7c 63 1a 78 xor r3,r3,r3
|
||||
100010: 7c 63 18 f8 not r3,r3
|
||||
100014: 30 63 00 01 addic r3,r3,1
|
||||
100018: 7c 64 01 d4 addme r3,r4
|
||||
10001c: 7c c0 01 14 adde r6,r0,r0
|
||||
100020: 4e 80 00 20 blr
|
||||
|
||||
0000000000100024 <test_addme_3>:
|
||||
100024: 7c 64 01 d4 addme r3,r4
|
||||
100028: 7c c0 01 14 adde r6,r0,r0
|
||||
10002c: 4e 80 00 20 blr
|
||||
|
||||
0000000000100030 <test_addme_4>:
|
||||
100030: 7c 63 1a 78 xor r3,r3,r3
|
||||
100034: 7c 63 18 f8 not r3,r3
|
||||
100038: 30 63 00 01 addic r3,r3,1
|
||||
10003c: 7c 64 01 d4 addme r3,r4
|
||||
100040: 7c c0 01 14 adde r6,r0,r0
|
||||
100044: 4e 80 00 20 blr
|
||||
|
||||
0000000000100048 <test_addme_5>:
|
||||
100048: 7c 64 01 d4 addme r3,r4
|
||||
10004c: 7c c0 01 14 adde r6,r0,r0
|
||||
100050: 4e 80 00 20 blr
|
||||
|
||||
0000000000100054 <test_addme_6>:
|
||||
100054: 7c 63 1a 78 xor r3,r3,r3
|
||||
100058: 7c 63 18 f8 not r3,r3
|
||||
10005c: 30 63 00 01 addic r3,r3,1
|
||||
100060: 7c 64 01 d4 addme r3,r4
|
||||
100064: 7c c0 01 14 adde r6,r0,r0
|
||||
100068: 4e 80 00 20 blr
|
||||
|
||||
000000000010006c <test_addme_7>:
|
||||
10006c: 7c 64 01 d4 addme r3,r4
|
||||
100070: 7c c0 01 14 adde r6,r0,r0
|
||||
100074: 4e 80 00 20 blr
|
||||
|
||||
0000000000100078 <test_addme_8>:
|
||||
100078: 7c 63 1a 78 xor r3,r3,r3
|
||||
10007c: 7c 63 18 f8 not r3,r3
|
||||
100080: 30 63 00 01 addic r3,r3,1
|
||||
100084: 7c 64 01 d4 addme r3,r4
|
||||
100088: 7c c0 01 14 adde r6,r0,r0
|
||||
10008c: 4e 80 00 20 blr
|
||||
8
src/xenia/cpu/frontend/ppc/test/bin/instr_addme.map
Normal file
8
src/xenia/cpu/frontend/ppc/test/bin/instr_addme.map
Normal file
@@ -0,0 +1,8 @@
|
||||
0000000000000000 t test_addme_1
|
||||
000000000000000c t test_addme_2
|
||||
0000000000000024 t test_addme_3
|
||||
0000000000000030 t test_addme_4
|
||||
0000000000000048 t test_addme_5
|
||||
0000000000000054 t test_addme_6
|
||||
000000000000006c t test_addme_7
|
||||
0000000000000078 t test_addme_8
|
||||
BIN
src/xenia/cpu/frontend/ppc/test/bin/instr_addze.bin
Normal file
BIN
src/xenia/cpu/frontend/ppc/test/bin/instr_addze.bin
Normal file
Binary file not shown.
57
src/xenia/cpu/frontend/ppc/test/bin/instr_addze.dis
Normal file
57
src/xenia/cpu/frontend/ppc/test/bin/instr_addze.dis
Normal file
@@ -0,0 +1,57 @@
|
||||
|
||||
/vagrant/src/xenia/cpu/frontend/ppc/test/bin//instr_addze.o: file format elf64-powerpc
|
||||
|
||||
|
||||
Disassembly of section .text:
|
||||
|
||||
0000000000100000 <test_addze_1>:
|
||||
100000: 7c 64 01 94 addze r3,r4
|
||||
100004: 7c c0 01 14 adde r6,r0,r0
|
||||
100008: 4e 80 00 20 blr
|
||||
|
||||
000000000010000c <test_addze_2>:
|
||||
10000c: 7c 63 1a 78 xor r3,r3,r3
|
||||
100010: 7c 63 18 f8 not r3,r3
|
||||
100014: 30 63 00 01 addic r3,r3,1
|
||||
100018: 7c 64 01 94 addze r3,r4
|
||||
10001c: 7c c0 01 14 adde r6,r0,r0
|
||||
100020: 4e 80 00 20 blr
|
||||
|
||||
0000000000100024 <test_addze_3>:
|
||||
100024: 7c 64 01 94 addze r3,r4
|
||||
100028: 7c c0 01 14 adde r6,r0,r0
|
||||
10002c: 4e 80 00 20 blr
|
||||
|
||||
0000000000100030 <test_addze_4>:
|
||||
100030: 7c 63 1a 78 xor r3,r3,r3
|
||||
100034: 7c 63 18 f8 not r3,r3
|
||||
100038: 30 63 00 01 addic r3,r3,1
|
||||
10003c: 7c 64 01 94 addze r3,r4
|
||||
100040: 7c c0 01 14 adde r6,r0,r0
|
||||
100044: 4e 80 00 20 blr
|
||||
|
||||
0000000000100048 <test_addze_5>:
|
||||
100048: 7c 64 01 94 addze r3,r4
|
||||
10004c: 7c c0 01 14 adde r6,r0,r0
|
||||
100050: 4e 80 00 20 blr
|
||||
|
||||
0000000000100054 <test_addze_6>:
|
||||
100054: 7c 63 1a 78 xor r3,r3,r3
|
||||
100058: 7c 63 18 f8 not r3,r3
|
||||
10005c: 30 63 00 01 addic r3,r3,1
|
||||
100060: 7c 64 01 94 addze r3,r4
|
||||
100064: 7c c0 01 14 adde r6,r0,r0
|
||||
100068: 4e 80 00 20 blr
|
||||
|
||||
000000000010006c <test_addze_7>:
|
||||
10006c: 7c 64 01 94 addze r3,r4
|
||||
100070: 7c c0 01 14 adde r6,r0,r0
|
||||
100074: 4e 80 00 20 blr
|
||||
|
||||
0000000000100078 <test_addze_8>:
|
||||
100078: 7c 63 1a 78 xor r3,r3,r3
|
||||
10007c: 7c 63 18 f8 not r3,r3
|
||||
100080: 30 63 00 01 addic r3,r3,1
|
||||
100084: 7c 64 01 94 addze r3,r4
|
||||
100088: 7c c0 01 14 adde r6,r0,r0
|
||||
10008c: 4e 80 00 20 blr
|
||||
8
src/xenia/cpu/frontend/ppc/test/bin/instr_addze.map
Normal file
8
src/xenia/cpu/frontend/ppc/test/bin/instr_addze.map
Normal file
@@ -0,0 +1,8 @@
|
||||
0000000000000000 t test_addze_1
|
||||
000000000000000c t test_addze_2
|
||||
0000000000000024 t test_addze_3
|
||||
0000000000000030 t test_addze_4
|
||||
0000000000000048 t test_addze_5
|
||||
0000000000000054 t test_addze_6
|
||||
000000000000006c t test_addze_7
|
||||
0000000000000078 t test_addze_8
|
||||
BIN
src/xenia/cpu/frontend/ppc/test/bin/instr_cntlzd.bin
Normal file
BIN
src/xenia/cpu/frontend/ppc/test/bin/instr_cntlzd.bin
Normal file
Binary file not shown.
21
src/xenia/cpu/frontend/ppc/test/bin/instr_cntlzd.dis
Normal file
21
src/xenia/cpu/frontend/ppc/test/bin/instr_cntlzd.dis
Normal file
@@ -0,0 +1,21 @@
|
||||
|
||||
/vagrant/src/xenia/cpu/frontend/ppc/test/bin//instr_cntlzd.o: file format elf64-powerpc
|
||||
|
||||
|
||||
Disassembly of section .text:
|
||||
|
||||
0000000000100000 <test_cntlzd_1>:
|
||||
100000: 7c a6 00 74 cntlzd r6,r5
|
||||
100004: 4e 80 00 20 blr
|
||||
|
||||
0000000000100008 <test_cntlzd_2>:
|
||||
100008: 7c a6 00 74 cntlzd r6,r5
|
||||
10000c: 4e 80 00 20 blr
|
||||
|
||||
0000000000100010 <test_cntlzd_3>:
|
||||
100010: 7c a6 00 74 cntlzd r6,r5
|
||||
100014: 4e 80 00 20 blr
|
||||
|
||||
0000000000100018 <test_cntlzd_4>:
|
||||
100018: 7c a6 00 74 cntlzd r6,r5
|
||||
10001c: 4e 80 00 20 blr
|
||||
4
src/xenia/cpu/frontend/ppc/test/bin/instr_cntlzd.map
Normal file
4
src/xenia/cpu/frontend/ppc/test/bin/instr_cntlzd.map
Normal file
@@ -0,0 +1,4 @@
|
||||
0000000000000000 t test_cntlzd_1
|
||||
0000000000000008 t test_cntlzd_2
|
||||
0000000000000010 t test_cntlzd_3
|
||||
0000000000000018 t test_cntlzd_4
|
||||
BIN
src/xenia/cpu/frontend/ppc/test/bin/instr_cntlzw.bin
Normal file
BIN
src/xenia/cpu/frontend/ppc/test/bin/instr_cntlzw.bin
Normal file
Binary file not shown.
21
src/xenia/cpu/frontend/ppc/test/bin/instr_cntlzw.dis
Normal file
21
src/xenia/cpu/frontend/ppc/test/bin/instr_cntlzw.dis
Normal file
@@ -0,0 +1,21 @@
|
||||
|
||||
/vagrant/src/xenia/cpu/frontend/ppc/test/bin//instr_cntlzw.o: file format elf64-powerpc
|
||||
|
||||
|
||||
Disassembly of section .text:
|
||||
|
||||
0000000000100000 <test_cntlzw_1>:
|
||||
100000: 7c a6 00 34 cntlzw r6,r5
|
||||
100004: 4e 80 00 20 blr
|
||||
|
||||
0000000000100008 <test_cntlzw_2>:
|
||||
100008: 7c a6 00 34 cntlzw r6,r5
|
||||
10000c: 4e 80 00 20 blr
|
||||
|
||||
0000000000100010 <test_cntlzw_3>:
|
||||
100010: 7c a6 00 34 cntlzw r6,r5
|
||||
100014: 4e 80 00 20 blr
|
||||
|
||||
0000000000100018 <test_cntlzw_4>:
|
||||
100018: 7c a6 00 34 cntlzw r6,r5
|
||||
10001c: 4e 80 00 20 blr
|
||||
4
src/xenia/cpu/frontend/ppc/test/bin/instr_cntlzw.map
Normal file
4
src/xenia/cpu/frontend/ppc/test/bin/instr_cntlzw.map
Normal file
@@ -0,0 +1,4 @@
|
||||
0000000000000000 t test_cntlzw_1
|
||||
0000000000000008 t test_cntlzw_2
|
||||
0000000000000010 t test_cntlzw_3
|
||||
0000000000000018 t test_cntlzw_4
|
||||
BIN
src/xenia/cpu/frontend/ppc/test/bin/instr_divd.bin
Normal file
BIN
src/xenia/cpu/frontend/ppc/test/bin/instr_divd.bin
Normal file
Binary file not shown.
33
src/xenia/cpu/frontend/ppc/test/bin/instr_divd.dis
Normal file
33
src/xenia/cpu/frontend/ppc/test/bin/instr_divd.dis
Normal file
@@ -0,0 +1,33 @@
|
||||
|
||||
/vagrant/src/xenia/cpu/frontend/ppc/test/bin//instr_divd.o: file format elf64-powerpc
|
||||
|
||||
|
||||
Disassembly of section .text:
|
||||
|
||||
0000000000100000 <test_divd_1>:
|
||||
100000: 7c 64 2b d2 divd r3,r4,r5
|
||||
100004: 4e 80 00 20 blr
|
||||
|
||||
0000000000100008 <test_divd_3>:
|
||||
100008: 7c 64 2b d2 divd r3,r4,r5
|
||||
10000c: 4e 80 00 20 blr
|
||||
|
||||
0000000000100010 <test_divd_4>:
|
||||
100010: 7c 64 2b d2 divd r3,r4,r5
|
||||
100014: 4e 80 00 20 blr
|
||||
|
||||
0000000000100018 <test_divd_5>:
|
||||
100018: 7c 64 2b d2 divd r3,r4,r5
|
||||
10001c: 4e 80 00 20 blr
|
||||
|
||||
0000000000100020 <test_divd_6>:
|
||||
100020: 7c 64 2b d2 divd r3,r4,r5
|
||||
100024: 4e 80 00 20 blr
|
||||
|
||||
0000000000100028 <test_divd_7>:
|
||||
100028: 7c 64 2b d2 divd r3,r4,r5
|
||||
10002c: 4e 80 00 20 blr
|
||||
|
||||
0000000000100030 <test_divd_8>:
|
||||
100030: 7c 64 2b d2 divd r3,r4,r5
|
||||
100034: 4e 80 00 20 blr
|
||||
7
src/xenia/cpu/frontend/ppc/test/bin/instr_divd.map
Normal file
7
src/xenia/cpu/frontend/ppc/test/bin/instr_divd.map
Normal file
@@ -0,0 +1,7 @@
|
||||
0000000000000000 t test_divd_1
|
||||
0000000000000008 t test_divd_3
|
||||
0000000000000010 t test_divd_4
|
||||
0000000000000018 t test_divd_5
|
||||
0000000000000020 t test_divd_6
|
||||
0000000000000028 t test_divd_7
|
||||
0000000000000030 t test_divd_8
|
||||
BIN
src/xenia/cpu/frontend/ppc/test/bin/instr_divdu.bin
Normal file
BIN
src/xenia/cpu/frontend/ppc/test/bin/instr_divdu.bin
Normal file
Binary file not shown.
37
src/xenia/cpu/frontend/ppc/test/bin/instr_divdu.dis
Normal file
37
src/xenia/cpu/frontend/ppc/test/bin/instr_divdu.dis
Normal file
@@ -0,0 +1,37 @@
|
||||
|
||||
/vagrant/src/xenia/cpu/frontend/ppc/test/bin//instr_divdu.o: file format elf64-powerpc
|
||||
|
||||
|
||||
Disassembly of section .text:
|
||||
|
||||
0000000000100000 <test_divdu_1>:
|
||||
100000: 7c 64 2b 92 divdu r3,r4,r5
|
||||
100004: 4e 80 00 20 blr
|
||||
|
||||
0000000000100008 <test_divdu_3>:
|
||||
100008: 7c 64 2b 92 divdu r3,r4,r5
|
||||
10000c: 4e 80 00 20 blr
|
||||
|
||||
0000000000100010 <test_divdu_4>:
|
||||
100010: 7c 64 2b 92 divdu r3,r4,r5
|
||||
100014: 4e 80 00 20 blr
|
||||
|
||||
0000000000100018 <test_divdu_5>:
|
||||
100018: 7c 64 2b 92 divdu r3,r4,r5
|
||||
10001c: 4e 80 00 20 blr
|
||||
|
||||
0000000000100020 <test_divdu_6>:
|
||||
100020: 7c 64 2b 92 divdu r3,r4,r5
|
||||
100024: 4e 80 00 20 blr
|
||||
|
||||
0000000000100028 <test_divdu_7>:
|
||||
100028: 7c 64 2b 92 divdu r3,r4,r5
|
||||
10002c: 4e 80 00 20 blr
|
||||
|
||||
0000000000100030 <test_divdu_8>:
|
||||
100030: 7c 64 2b 92 divdu r3,r4,r5
|
||||
100034: 4e 80 00 20 blr
|
||||
|
||||
0000000000100038 <test_divdu_9>:
|
||||
100038: 7c 64 2b 92 divdu r3,r4,r5
|
||||
10003c: 4e 80 00 20 blr
|
||||
8
src/xenia/cpu/frontend/ppc/test/bin/instr_divdu.map
Normal file
8
src/xenia/cpu/frontend/ppc/test/bin/instr_divdu.map
Normal file
@@ -0,0 +1,8 @@
|
||||
0000000000000000 t test_divdu_1
|
||||
0000000000000008 t test_divdu_3
|
||||
0000000000000010 t test_divdu_4
|
||||
0000000000000018 t test_divdu_5
|
||||
0000000000000020 t test_divdu_6
|
||||
0000000000000028 t test_divdu_7
|
||||
0000000000000030 t test_divdu_8
|
||||
0000000000000038 t test_divdu_9
|
||||
BIN
src/xenia/cpu/frontend/ppc/test/bin/instr_divw.bin
Normal file
BIN
src/xenia/cpu/frontend/ppc/test/bin/instr_divw.bin
Normal file
Binary file not shown.
45
src/xenia/cpu/frontend/ppc/test/bin/instr_divw.dis
Normal file
45
src/xenia/cpu/frontend/ppc/test/bin/instr_divw.dis
Normal file
@@ -0,0 +1,45 @@
|
||||
|
||||
/vagrant/src/xenia/cpu/frontend/ppc/test/bin//instr_divw.o: file format elf64-powerpc
|
||||
|
||||
|
||||
Disassembly of section .text:
|
||||
|
||||
0000000000100000 <test_divw_1>:
|
||||
100000: 7c 64 2b d6 divw r3,r4,r5
|
||||
100004: 4e 80 00 20 blr
|
||||
|
||||
0000000000100008 <test_divw_3>:
|
||||
100008: 7c 64 2b d6 divw r3,r4,r5
|
||||
10000c: 4e 80 00 20 blr
|
||||
|
||||
0000000000100010 <test_divw_4>:
|
||||
100010: 7c 64 2b d6 divw r3,r4,r5
|
||||
100014: 4e 80 00 20 blr
|
||||
|
||||
0000000000100018 <test_divw_5>:
|
||||
100018: 7c 64 2b d6 divw r3,r4,r5
|
||||
10001c: 4e 80 00 20 blr
|
||||
|
||||
0000000000100020 <test_divw_6>:
|
||||
100020: 7c 64 2b d6 divw r3,r4,r5
|
||||
100024: 4e 80 00 20 blr
|
||||
|
||||
0000000000100028 <test_divw_7>:
|
||||
100028: 7c 64 2b d6 divw r3,r4,r5
|
||||
10002c: 4e 80 00 20 blr
|
||||
|
||||
0000000000100030 <test_divw_8>:
|
||||
100030: 7c 64 2b d6 divw r3,r4,r5
|
||||
100034: 4e 80 00 20 blr
|
||||
|
||||
0000000000100038 <test_divw_9>:
|
||||
100038: 7c 64 2b d6 divw r3,r4,r5
|
||||
10003c: 4e 80 00 20 blr
|
||||
|
||||
0000000000100040 <test_divw_10>:
|
||||
100040: 7c 64 2b d6 divw r3,r4,r5
|
||||
100044: 4e 80 00 20 blr
|
||||
|
||||
0000000000100048 <test_divw_11>:
|
||||
100048: 7c 64 2b d6 divw r3,r4,r5
|
||||
10004c: 4e 80 00 20 blr
|
||||
10
src/xenia/cpu/frontend/ppc/test/bin/instr_divw.map
Normal file
10
src/xenia/cpu/frontend/ppc/test/bin/instr_divw.map
Normal file
@@ -0,0 +1,10 @@
|
||||
0000000000000000 t test_divw_1
|
||||
0000000000000008 t test_divw_3
|
||||
0000000000000010 t test_divw_4
|
||||
0000000000000018 t test_divw_5
|
||||
0000000000000020 t test_divw_6
|
||||
0000000000000028 t test_divw_7
|
||||
0000000000000030 t test_divw_8
|
||||
0000000000000038 t test_divw_9
|
||||
0000000000000040 t test_divw_10
|
||||
0000000000000048 t test_divw_11
|
||||
BIN
src/xenia/cpu/frontend/ppc/test/bin/instr_divwu.bin
Normal file
BIN
src/xenia/cpu/frontend/ppc/test/bin/instr_divwu.bin
Normal file
Binary file not shown.
49
src/xenia/cpu/frontend/ppc/test/bin/instr_divwu.dis
Normal file
49
src/xenia/cpu/frontend/ppc/test/bin/instr_divwu.dis
Normal file
@@ -0,0 +1,49 @@
|
||||
|
||||
/vagrant/src/xenia/cpu/frontend/ppc/test/bin//instr_divwu.o: file format elf64-powerpc
|
||||
|
||||
|
||||
Disassembly of section .text:
|
||||
|
||||
0000000000100000 <test_divwu_1>:
|
||||
100000: 7c 64 2b 96 divwu r3,r4,r5
|
||||
100004: 4e 80 00 20 blr
|
||||
|
||||
0000000000100008 <test_divwu_3>:
|
||||
100008: 7c 64 2b 96 divwu r3,r4,r5
|
||||
10000c: 4e 80 00 20 blr
|
||||
|
||||
0000000000100010 <test_divwu_4>:
|
||||
100010: 7c 64 2b 96 divwu r3,r4,r5
|
||||
100014: 4e 80 00 20 blr
|
||||
|
||||
0000000000100018 <test_divwu_5>:
|
||||
100018: 7c 64 2b 96 divwu r3,r4,r5
|
||||
10001c: 4e 80 00 20 blr
|
||||
|
||||
0000000000100020 <test_divwu_6>:
|
||||
100020: 7c 64 2b 96 divwu r3,r4,r5
|
||||
100024: 4e 80 00 20 blr
|
||||
|
||||
0000000000100028 <test_divwu_7>:
|
||||
100028: 7c 64 2b 96 divwu r3,r4,r5
|
||||
10002c: 4e 80 00 20 blr
|
||||
|
||||
0000000000100030 <test_divwu_8>:
|
||||
100030: 7c 64 2b 96 divwu r3,r4,r5
|
||||
100034: 4e 80 00 20 blr
|
||||
|
||||
0000000000100038 <test_divwu_9>:
|
||||
100038: 7c 64 2b 96 divwu r3,r4,r5
|
||||
10003c: 4e 80 00 20 blr
|
||||
|
||||
0000000000100040 <test_divwu_10>:
|
||||
100040: 7c 64 2b 96 divwu r3,r4,r5
|
||||
100044: 4e 80 00 20 blr
|
||||
|
||||
0000000000100048 <test_divwu_11>:
|
||||
100048: 7c 64 2b 96 divwu r3,r4,r5
|
||||
10004c: 4e 80 00 20 blr
|
||||
|
||||
0000000000100050 <test_divwu_12>:
|
||||
100050: 7c 64 2b 96 divwu r3,r4,r5
|
||||
100054: 4e 80 00 20 blr
|
||||
11
src/xenia/cpu/frontend/ppc/test/bin/instr_divwu.map
Normal file
11
src/xenia/cpu/frontend/ppc/test/bin/instr_divwu.map
Normal file
@@ -0,0 +1,11 @@
|
||||
0000000000000000 t test_divwu_1
|
||||
0000000000000008 t test_divwu_3
|
||||
0000000000000010 t test_divwu_4
|
||||
0000000000000018 t test_divwu_5
|
||||
0000000000000020 t test_divwu_6
|
||||
0000000000000028 t test_divwu_7
|
||||
0000000000000030 t test_divwu_8
|
||||
0000000000000038 t test_divwu_9
|
||||
0000000000000040 t test_divwu_10
|
||||
0000000000000048 t test_divwu_11
|
||||
0000000000000050 t test_divwu_12
|
||||
BIN
src/xenia/cpu/frontend/ppc/test/bin/instr_eqv.bin
Normal file
BIN
src/xenia/cpu/frontend/ppc/test/bin/instr_eqv.bin
Normal file
Binary file not shown.
29
src/xenia/cpu/frontend/ppc/test/bin/instr_eqv.dis
Normal file
29
src/xenia/cpu/frontend/ppc/test/bin/instr_eqv.dis
Normal file
@@ -0,0 +1,29 @@
|
||||
|
||||
/vagrant/src/xenia/cpu/frontend/ppc/test/bin//instr_eqv.o: file format elf64-powerpc
|
||||
|
||||
|
||||
Disassembly of section .text:
|
||||
|
||||
0000000000100000 <test_eqv_1>:
|
||||
100000: 7c 83 2a 38 eqv r3,r4,r5
|
||||
100004: 4e 80 00 20 blr
|
||||
|
||||
0000000000100008 <test_eqv_2>:
|
||||
100008: 7c 83 2a 38 eqv r3,r4,r5
|
||||
10000c: 4e 80 00 20 blr
|
||||
|
||||
0000000000100010 <test_eqv_3>:
|
||||
100010: 7c 83 2a 38 eqv r3,r4,r5
|
||||
100014: 4e 80 00 20 blr
|
||||
|
||||
0000000000100018 <test_eqv_4>:
|
||||
100018: 7c 83 2a 38 eqv r3,r4,r5
|
||||
10001c: 4e 80 00 20 blr
|
||||
|
||||
0000000000100020 <test_eqv_5>:
|
||||
100020: 7c 83 2a 38 eqv r3,r4,r5
|
||||
100024: 4e 80 00 20 blr
|
||||
|
||||
0000000000100028 <test_eqv_6>:
|
||||
100028: 7c 83 2a 38 eqv r3,r4,r5
|
||||
10002c: 4e 80 00 20 blr
|
||||
6
src/xenia/cpu/frontend/ppc/test/bin/instr_eqv.map
Normal file
6
src/xenia/cpu/frontend/ppc/test/bin/instr_eqv.map
Normal file
@@ -0,0 +1,6 @@
|
||||
0000000000000000 t test_eqv_1
|
||||
0000000000000008 t test_eqv_2
|
||||
0000000000000010 t test_eqv_3
|
||||
0000000000000018 t test_eqv_4
|
||||
0000000000000020 t test_eqv_5
|
||||
0000000000000028 t test_eqv_6
|
||||
BIN
src/xenia/cpu/frontend/ppc/test/bin/instr_fabs.bin
Normal file
BIN
src/xenia/cpu/frontend/ppc/test/bin/instr_fabs.bin
Normal file
Binary file not shown.
17
src/xenia/cpu/frontend/ppc/test/bin/instr_fabs.dis
Normal file
17
src/xenia/cpu/frontend/ppc/test/bin/instr_fabs.dis
Normal file
@@ -0,0 +1,17 @@
|
||||
|
||||
/vagrant/src/xenia/cpu/frontend/ppc/test/bin//instr_fabs.o: file format elf64-powerpc
|
||||
|
||||
|
||||
Disassembly of section .text:
|
||||
|
||||
0000000000100000 <test_fabs_1>:
|
||||
100000: fc 20 0a 10 fabs f1,f1
|
||||
100004: 4e 80 00 20 blr
|
||||
|
||||
0000000000100008 <test_fabs_2>:
|
||||
100008: fc 20 0a 10 fabs f1,f1
|
||||
10000c: 4e 80 00 20 blr
|
||||
|
||||
0000000000100010 <test_fabs_3>:
|
||||
100010: fc 20 0a 10 fabs f1,f1
|
||||
100014: 4e 80 00 20 blr
|
||||
3
src/xenia/cpu/frontend/ppc/test/bin/instr_fabs.map
Normal file
3
src/xenia/cpu/frontend/ppc/test/bin/instr_fabs.map
Normal file
@@ -0,0 +1,3 @@
|
||||
0000000000000000 t test_fabs_1
|
||||
0000000000000008 t test_fabs_2
|
||||
0000000000000010 t test_fabs_3
|
||||
BIN
src/xenia/cpu/frontend/ppc/test/bin/instr_fsel.bin
Normal file
BIN
src/xenia/cpu/frontend/ppc/test/bin/instr_fsel.bin
Normal file
Binary file not shown.
17
src/xenia/cpu/frontend/ppc/test/bin/instr_fsel.dis
Normal file
17
src/xenia/cpu/frontend/ppc/test/bin/instr_fsel.dis
Normal file
@@ -0,0 +1,17 @@
|
||||
|
||||
/vagrant/src/xenia/cpu/frontend/ppc/test/bin//instr_fsel.o: file format elf64-powerpc
|
||||
|
||||
|
||||
Disassembly of section .text:
|
||||
|
||||
0000000000100000 <test_fsel_1>:
|
||||
100000: fc 22 20 ee fsel f1,f2,f3,f4
|
||||
100004: 4e 80 00 20 blr
|
||||
|
||||
0000000000100008 <test_fsel_2>:
|
||||
100008: fc 22 20 ee fsel f1,f2,f3,f4
|
||||
10000c: 4e 80 00 20 blr
|
||||
|
||||
0000000000100010 <test_fsel_3>:
|
||||
100010: fc 22 20 ee fsel f1,f2,f3,f4
|
||||
100014: 4e 80 00 20 blr
|
||||
3
src/xenia/cpu/frontend/ppc/test/bin/instr_fsel.map
Normal file
3
src/xenia/cpu/frontend/ppc/test/bin/instr_fsel.map
Normal file
@@ -0,0 +1,3 @@
|
||||
0000000000000000 t test_fsel_1
|
||||
0000000000000008 t test_fsel_2
|
||||
0000000000000010 t test_fsel_3
|
||||
BIN
src/xenia/cpu/frontend/ppc/test/bin/instr_lvexx.bin
Normal file
BIN
src/xenia/cpu/frontend/ppc/test/bin/instr_lvexx.bin
Normal file
Binary file not shown.
29
src/xenia/cpu/frontend/ppc/test/bin/instr_lvexx.dis
Normal file
29
src/xenia/cpu/frontend/ppc/test/bin/instr_lvexx.dis
Normal file
@@ -0,0 +1,29 @@
|
||||
|
||||
/vagrant/src/xenia/cpu/frontend/ppc/test/bin//instr_lvexx.o: file format elf64-powerpc
|
||||
|
||||
|
||||
Disassembly of section .text:
|
||||
|
||||
0000000000100000 <test_lvebx_1>:
|
||||
100000: 7c 60 20 0e lvebx v3,0,r4
|
||||
100004: 4e 80 00 20 blr
|
||||
|
||||
0000000000100008 <test_lvebx_2>:
|
||||
100008: 7c 60 20 0e lvebx v3,0,r4
|
||||
10000c: 4e 80 00 20 blr
|
||||
|
||||
0000000000100010 <test_lvehx_1>:
|
||||
100010: 7c 60 20 4e lvehx v3,0,r4
|
||||
100014: 4e 80 00 20 blr
|
||||
|
||||
0000000000100018 <test_lvehx_2>:
|
||||
100018: 7c 60 20 4e lvehx v3,0,r4
|
||||
10001c: 4e 80 00 20 blr
|
||||
|
||||
0000000000100020 <test_lvewx_1>:
|
||||
100020: 7c 60 20 8e lvewx v3,0,r4
|
||||
100024: 4e 80 00 20 blr
|
||||
|
||||
0000000000100028 <test_lvewx_2>:
|
||||
100028: 7c 60 20 8e lvewx v3,0,r4
|
||||
10002c: 4e 80 00 20 blr
|
||||
6
src/xenia/cpu/frontend/ppc/test/bin/instr_lvexx.map
Normal file
6
src/xenia/cpu/frontend/ppc/test/bin/instr_lvexx.map
Normal file
@@ -0,0 +1,6 @@
|
||||
0000000000000000 t test_lvebx_1
|
||||
0000000000000008 t test_lvebx_2
|
||||
0000000000000010 t test_lvehx_1
|
||||
0000000000000018 t test_lvehx_2
|
||||
0000000000000020 t test_lvewx_1
|
||||
0000000000000028 t test_lvewx_2
|
||||
BIN
src/xenia/cpu/frontend/ppc/test/bin/instr_lvl.bin
Normal file
BIN
src/xenia/cpu/frontend/ppc/test/bin/instr_lvl.bin
Normal file
Binary file not shown.
9
src/xenia/cpu/frontend/ppc/test/bin/instr_lvl.dis
Normal file
9
src/xenia/cpu/frontend/ppc/test/bin/instr_lvl.dis
Normal file
@@ -0,0 +1,9 @@
|
||||
|
||||
/vagrant/src/xenia/cpu/frontend/ppc/test/bin//instr_lvl.o: file format elf64-powerpc
|
||||
|
||||
|
||||
Disassembly of section .text:
|
||||
|
||||
0000000000100000 <test_lvl_1>:
|
||||
100000: 7c 64 04 0e lvlx v3,r4,r0
|
||||
100004: 4e 80 00 20 blr
|
||||
1
src/xenia/cpu/frontend/ppc/test/bin/instr_lvl.map
Normal file
1
src/xenia/cpu/frontend/ppc/test/bin/instr_lvl.map
Normal file
@@ -0,0 +1 @@
|
||||
0000000000000000 t test_lvl_1
|
||||
BIN
src/xenia/cpu/frontend/ppc/test/bin/instr_lvr.bin
Normal file
BIN
src/xenia/cpu/frontend/ppc/test/bin/instr_lvr.bin
Normal file
Binary file not shown.
9
src/xenia/cpu/frontend/ppc/test/bin/instr_lvr.dis
Normal file
9
src/xenia/cpu/frontend/ppc/test/bin/instr_lvr.dis
Normal file
@@ -0,0 +1,9 @@
|
||||
|
||||
/vagrant/src/xenia/cpu/frontend/ppc/test/bin//instr_lvr.o: file format elf64-powerpc
|
||||
|
||||
|
||||
Disassembly of section .text:
|
||||
|
||||
0000000000100000 <test_lvr_1>:
|
||||
100000: 7c 64 2c 4e lvrx v3,r4,r5
|
||||
100004: 4e 80 00 20 blr
|
||||
1
src/xenia/cpu/frontend/ppc/test/bin/instr_lvr.map
Normal file
1
src/xenia/cpu/frontend/ppc/test/bin/instr_lvr.map
Normal file
@@ -0,0 +1 @@
|
||||
0000000000000000 t test_lvr_1
|
||||
BIN
src/xenia/cpu/frontend/ppc/test/bin/instr_lvsl.bin
Normal file
BIN
src/xenia/cpu/frontend/ppc/test/bin/instr_lvsl.bin
Normal file
Binary file not shown.
17
src/xenia/cpu/frontend/ppc/test/bin/instr_lvsl.dis
Normal file
17
src/xenia/cpu/frontend/ppc/test/bin/instr_lvsl.dis
Normal file
@@ -0,0 +1,17 @@
|
||||
|
||||
/vagrant/src/xenia/cpu/frontend/ppc/test/bin//instr_lvsl.o: file format elf64-powerpc
|
||||
|
||||
|
||||
Disassembly of section .text:
|
||||
|
||||
0000000000100000 <test_lvsl_1>:
|
||||
100000: 7c 64 00 0c lvsl v3,r4,r0
|
||||
100004: 4e 80 00 20 blr
|
||||
|
||||
0000000000100008 <test_lvsl_2>:
|
||||
100008: 7c 64 00 0c lvsl v3,r4,r0
|
||||
10000c: 4e 80 00 20 blr
|
||||
|
||||
0000000000100010 <test_lvsl_3>:
|
||||
100010: 7c 64 00 0c lvsl v3,r4,r0
|
||||
100014: 4e 80 00 20 blr
|
||||
3
src/xenia/cpu/frontend/ppc/test/bin/instr_lvsl.map
Normal file
3
src/xenia/cpu/frontend/ppc/test/bin/instr_lvsl.map
Normal file
@@ -0,0 +1,3 @@
|
||||
0000000000000000 t test_lvsl_1
|
||||
0000000000000008 t test_lvsl_2
|
||||
0000000000000010 t test_lvsl_3
|
||||
BIN
src/xenia/cpu/frontend/ppc/test/bin/instr_lvsr.bin
Normal file
BIN
src/xenia/cpu/frontend/ppc/test/bin/instr_lvsr.bin
Normal file
Binary file not shown.
17
src/xenia/cpu/frontend/ppc/test/bin/instr_lvsr.dis
Normal file
17
src/xenia/cpu/frontend/ppc/test/bin/instr_lvsr.dis
Normal file
@@ -0,0 +1,17 @@
|
||||
|
||||
/vagrant/src/xenia/cpu/frontend/ppc/test/bin//instr_lvsr.o: file format elf64-powerpc
|
||||
|
||||
|
||||
Disassembly of section .text:
|
||||
|
||||
0000000000100000 <test_lvsr_1>:
|
||||
100000: 7c 64 00 4c lvsr v3,r4,r0
|
||||
100004: 4e 80 00 20 blr
|
||||
|
||||
0000000000100008 <test_lvsr_2>:
|
||||
100008: 7c 64 00 4c lvsr v3,r4,r0
|
||||
10000c: 4e 80 00 20 blr
|
||||
|
||||
0000000000100010 <test_lvsr_3>:
|
||||
100010: 7c 64 00 4c lvsr v3,r4,r0
|
||||
100014: 4e 80 00 20 blr
|
||||
3
src/xenia/cpu/frontend/ppc/test/bin/instr_lvsr.map
Normal file
3
src/xenia/cpu/frontend/ppc/test/bin/instr_lvsr.map
Normal file
@@ -0,0 +1,3 @@
|
||||
0000000000000000 t test_lvsr_1
|
||||
0000000000000008 t test_lvsr_2
|
||||
0000000000000010 t test_lvsr_3
|
||||
BIN
src/xenia/cpu/frontend/ppc/test/bin/instr_mulhd.bin
Normal file
BIN
src/xenia/cpu/frontend/ppc/test/bin/instr_mulhd.bin
Normal file
Binary file not shown.
25
src/xenia/cpu/frontend/ppc/test/bin/instr_mulhd.dis
Normal file
25
src/xenia/cpu/frontend/ppc/test/bin/instr_mulhd.dis
Normal file
@@ -0,0 +1,25 @@
|
||||
|
||||
/vagrant/src/xenia/cpu/frontend/ppc/test/bin//instr_mulhd.o: file format elf64-powerpc
|
||||
|
||||
|
||||
Disassembly of section .text:
|
||||
|
||||
0000000000100000 <test_mulhd_1>:
|
||||
100000: 7c 64 28 92 mulhd r3,r4,r5
|
||||
100004: 4e 80 00 20 blr
|
||||
|
||||
0000000000100008 <test_mulhd_2>:
|
||||
100008: 7c 64 28 92 mulhd r3,r4,r5
|
||||
10000c: 4e 80 00 20 blr
|
||||
|
||||
0000000000100010 <test_mulhd_3>:
|
||||
100010: 7c 64 28 92 mulhd r3,r4,r5
|
||||
100014: 4e 80 00 20 blr
|
||||
|
||||
0000000000100018 <test_mulhd_4>:
|
||||
100018: 7c 64 28 92 mulhd r3,r4,r5
|
||||
10001c: 4e 80 00 20 blr
|
||||
|
||||
0000000000100020 <test_mulhd_5>:
|
||||
100020: 7c 64 28 92 mulhd r3,r4,r5
|
||||
100024: 4e 80 00 20 blr
|
||||
5
src/xenia/cpu/frontend/ppc/test/bin/instr_mulhd.map
Normal file
5
src/xenia/cpu/frontend/ppc/test/bin/instr_mulhd.map
Normal file
@@ -0,0 +1,5 @@
|
||||
0000000000000000 t test_mulhd_1
|
||||
0000000000000008 t test_mulhd_2
|
||||
0000000000000010 t test_mulhd_3
|
||||
0000000000000018 t test_mulhd_4
|
||||
0000000000000020 t test_mulhd_5
|
||||
BIN
src/xenia/cpu/frontend/ppc/test/bin/instr_mulhdu.bin
Normal file
BIN
src/xenia/cpu/frontend/ppc/test/bin/instr_mulhdu.bin
Normal file
Binary file not shown.
25
src/xenia/cpu/frontend/ppc/test/bin/instr_mulhdu.dis
Normal file
25
src/xenia/cpu/frontend/ppc/test/bin/instr_mulhdu.dis
Normal file
@@ -0,0 +1,25 @@
|
||||
|
||||
/vagrant/src/xenia/cpu/frontend/ppc/test/bin//instr_mulhdu.o: file format elf64-powerpc
|
||||
|
||||
|
||||
Disassembly of section .text:
|
||||
|
||||
0000000000100000 <test_mulhdu_1>:
|
||||
100000: 7c 64 28 12 mulhdu r3,r4,r5
|
||||
100004: 4e 80 00 20 blr
|
||||
|
||||
0000000000100008 <test_mulhdu_2>:
|
||||
100008: 7c 64 28 12 mulhdu r3,r4,r5
|
||||
10000c: 4e 80 00 20 blr
|
||||
|
||||
0000000000100010 <test_mulhdu_3>:
|
||||
100010: 7c 64 28 12 mulhdu r3,r4,r5
|
||||
100014: 4e 80 00 20 blr
|
||||
|
||||
0000000000100018 <test_mulhdu_4>:
|
||||
100018: 7c 64 28 12 mulhdu r3,r4,r5
|
||||
10001c: 4e 80 00 20 blr
|
||||
|
||||
0000000000100020 <test_mulhdu_5>:
|
||||
100020: 7c 64 28 12 mulhdu r3,r4,r5
|
||||
100024: 4e 80 00 20 blr
|
||||
5
src/xenia/cpu/frontend/ppc/test/bin/instr_mulhdu.map
Normal file
5
src/xenia/cpu/frontend/ppc/test/bin/instr_mulhdu.map
Normal file
@@ -0,0 +1,5 @@
|
||||
0000000000000000 t test_mulhdu_1
|
||||
0000000000000008 t test_mulhdu_2
|
||||
0000000000000010 t test_mulhdu_3
|
||||
0000000000000018 t test_mulhdu_4
|
||||
0000000000000020 t test_mulhdu_5
|
||||
BIN
src/xenia/cpu/frontend/ppc/test/bin/instr_mulhw.bin
Normal file
BIN
src/xenia/cpu/frontend/ppc/test/bin/instr_mulhw.bin
Normal file
Binary file not shown.
29
src/xenia/cpu/frontend/ppc/test/bin/instr_mulhw.dis
Normal file
29
src/xenia/cpu/frontend/ppc/test/bin/instr_mulhw.dis
Normal file
@@ -0,0 +1,29 @@
|
||||
|
||||
/vagrant/src/xenia/cpu/frontend/ppc/test/bin//instr_mulhw.o: file format elf64-powerpc
|
||||
|
||||
|
||||
Disassembly of section .text:
|
||||
|
||||
0000000000100000 <test_mulhw_1>:
|
||||
100000: 7c 64 28 96 mulhw r3,r4,r5
|
||||
100004: 4e 80 00 20 blr
|
||||
|
||||
0000000000100008 <test_mulhw_2>:
|
||||
100008: 7c 64 28 96 mulhw r3,r4,r5
|
||||
10000c: 4e 80 00 20 blr
|
||||
|
||||
0000000000100010 <test_mulhw_3>:
|
||||
100010: 7c 64 28 96 mulhw r3,r4,r5
|
||||
100014: 4e 80 00 20 blr
|
||||
|
||||
0000000000100018 <test_mulhw_4>:
|
||||
100018: 7c 64 28 96 mulhw r3,r4,r5
|
||||
10001c: 4e 80 00 20 blr
|
||||
|
||||
0000000000100020 <test_mulhw_5>:
|
||||
100020: 7c 64 28 96 mulhw r3,r4,r5
|
||||
100024: 4e 80 00 20 blr
|
||||
|
||||
0000000000100028 <test_mulhw_6>:
|
||||
100028: 7c 64 28 96 mulhw r3,r4,r5
|
||||
10002c: 4e 80 00 20 blr
|
||||
6
src/xenia/cpu/frontend/ppc/test/bin/instr_mulhw.map
Normal file
6
src/xenia/cpu/frontend/ppc/test/bin/instr_mulhw.map
Normal file
@@ -0,0 +1,6 @@
|
||||
0000000000000000 t test_mulhw_1
|
||||
0000000000000008 t test_mulhw_2
|
||||
0000000000000010 t test_mulhw_3
|
||||
0000000000000018 t test_mulhw_4
|
||||
0000000000000020 t test_mulhw_5
|
||||
0000000000000028 t test_mulhw_6
|
||||
BIN
src/xenia/cpu/frontend/ppc/test/bin/instr_mulhwu.bin
Normal file
BIN
src/xenia/cpu/frontend/ppc/test/bin/instr_mulhwu.bin
Normal file
Binary file not shown.
29
src/xenia/cpu/frontend/ppc/test/bin/instr_mulhwu.dis
Normal file
29
src/xenia/cpu/frontend/ppc/test/bin/instr_mulhwu.dis
Normal file
@@ -0,0 +1,29 @@
|
||||
|
||||
/vagrant/src/xenia/cpu/frontend/ppc/test/bin//instr_mulhwu.o: file format elf64-powerpc
|
||||
|
||||
|
||||
Disassembly of section .text:
|
||||
|
||||
0000000000100000 <test_mulhwu_1>:
|
||||
100000: 7c 64 28 16 mulhwu r3,r4,r5
|
||||
100004: 4e 80 00 20 blr
|
||||
|
||||
0000000000100008 <test_mulhwu_2>:
|
||||
100008: 7c 64 28 16 mulhwu r3,r4,r5
|
||||
10000c: 4e 80 00 20 blr
|
||||
|
||||
0000000000100010 <test_mulhwu_3>:
|
||||
100010: 7c 64 28 16 mulhwu r3,r4,r5
|
||||
100014: 4e 80 00 20 blr
|
||||
|
||||
0000000000100018 <test_mulhwu_4>:
|
||||
100018: 7c 64 28 16 mulhwu r3,r4,r5
|
||||
10001c: 4e 80 00 20 blr
|
||||
|
||||
0000000000100020 <test_mulhwu_5>:
|
||||
100020: 7c 64 28 16 mulhwu r3,r4,r5
|
||||
100024: 4e 80 00 20 blr
|
||||
|
||||
0000000000100028 <test_mulhwu_6>:
|
||||
100028: 7c 64 28 16 mulhwu r3,r4,r5
|
||||
10002c: 4e 80 00 20 blr
|
||||
6
src/xenia/cpu/frontend/ppc/test/bin/instr_mulhwu.map
Normal file
6
src/xenia/cpu/frontend/ppc/test/bin/instr_mulhwu.map
Normal file
@@ -0,0 +1,6 @@
|
||||
0000000000000000 t test_mulhwu_1
|
||||
0000000000000008 t test_mulhwu_2
|
||||
0000000000000010 t test_mulhwu_3
|
||||
0000000000000018 t test_mulhwu_4
|
||||
0000000000000020 t test_mulhwu_5
|
||||
0000000000000028 t test_mulhwu_6
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user