Moving alloy/ into xenia/cpu/ to start simplifying things.

This commit is contained in:
Ben Vanik
2015-03-24 07:46:18 -07:00
parent 59395318f3
commit 29912f44c0
519 changed files with 2246 additions and 2296 deletions

View 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

View 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_

View 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

View 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_

View 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

View 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_

View 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

View 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_

View 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_

View 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_

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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

View 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

File diff suppressed because it is too large Load Diff

View 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

View 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_

View 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

View 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_

View 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

View 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_

File diff suppressed because it is too large Load Diff

View 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

View 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_

View 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

View 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_

View 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': [
],
}

View 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

Binary file not shown.

View 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

View File

@@ -0,0 +1,2 @@
0000000000000000 t test_add_1
0000000000000008 t test_add_2

Binary file not shown.

View 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

View 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

Binary file not shown.

View 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

View 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

Binary file not shown.

View 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

View File

@@ -0,0 +1,2 @@
0000000000000000 t test_addic_1
000000000000000c t test_addic_2

Binary file not shown.

View 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

View 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

Binary file not shown.

View 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

View 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

Binary file not shown.

View 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

View 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

Binary file not shown.

View 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

View 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

Binary file not shown.

View 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

View 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

Binary file not shown.

View 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

View 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

Binary file not shown.

View 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

View 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

Binary file not shown.

View 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

View 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

Binary file not shown.

View 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

View 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

Binary file not shown.

View 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

View File

@@ -0,0 +1,3 @@
0000000000000000 t test_fabs_1
0000000000000008 t test_fabs_2
0000000000000010 t test_fabs_3

Binary file not shown.

View 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

View File

@@ -0,0 +1,3 @@
0000000000000000 t test_fsel_1
0000000000000008 t test_fsel_2
0000000000000010 t test_fsel_3

Binary file not shown.

View 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

View 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

Binary file not shown.

View 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

View File

@@ -0,0 +1 @@
0000000000000000 t test_lvl_1

Binary file not shown.

View 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

View File

@@ -0,0 +1 @@
0000000000000000 t test_lvr_1

Binary file not shown.

View 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

View File

@@ -0,0 +1,3 @@
0000000000000000 t test_lvsl_1
0000000000000008 t test_lvsl_2
0000000000000010 t test_lvsl_3

Binary file not shown.

View 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

View File

@@ -0,0 +1,3 @@
0000000000000000 t test_lvsr_1
0000000000000008 t test_lvsr_2
0000000000000010 t test_lvsr_3

Binary file not shown.

View 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

View 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

Binary file not shown.

View 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

View 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

Binary file not shown.

View 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

View 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

Binary file not shown.

View 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

View 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