Partial commit: Cell

This commit is contained in:
Nekotekina 2016-04-14 02:09:41 +03:00
parent 42e1d4d752
commit c4e99dbdb2
32 changed files with 10685 additions and 12527 deletions

View File

@ -8,16 +8,3 @@ enum FPSCR_RN
FPSCR_RN_PINF = 2,
FPSCR_RN_MINF = 3,
};
using ppu_inter_func_t = void(*)(class PPUThread& CPU, union ppu_opcode_t opcode);
struct ppu_decoder_cache_t
{
ppu_inter_func_t* const pointer;
ppu_decoder_cache_t();
~ppu_decoder_cache_t();
void initialize(u32 addr, u32 size);
};

View File

@ -2,10 +2,13 @@
#define ERROR_CODE(code) static_cast<s32>(code)
enum : s32
enum CellOk : s32
{
CELL_OK = 0,
};
enum CellError : s32
{
CELL_EAGAIN = ERROR_CODE(0x80010001), // The resource is temporarily unavailable
CELL_EINVAL = ERROR_CODE(0x80010002), // An invalid argument value is specified
CELL_ENOSYS = ERROR_CODE(0x80010003), // The feature is not yet implemented
@ -64,6 +67,146 @@ enum : s32
CELL_EOVERFLOW = ERROR_CODE(0x80010039),
CELL_ENOTMOUNTED = ERROR_CODE(0x8001003A),
CELL_ENOTSDATA = ERROR_CODE(0x8001003B),
CELL_UNKNOWN_ERROR = -1,
};
// Special return type signaling on errors
struct ppu_error_code
{
s32 value;
// Print error message, error code is returned
static s32 report(s32 error, const char* text);
// Must be specialized for specific tag type T
template<typename T>
static const char* print(T code)
{
return nullptr;
}
template<typename T>
s32 error_check(T code)
{
if (const auto text = print(code))
{
return report(code, text);
}
return code;
}
ppu_error_code() = default;
// General error check
template<typename T, typename = std::enable_if_t<std::is_enum<T>::value>>
ppu_error_code(T value)
: value(error_check(value))
{
}
// Force error reporting with a message specified
ppu_error_code(s32 value, const char* text)
: value(report(value, text))
{
}
// Silence any error
constexpr ppu_error_code(s32 value, const std::nothrow_t&)
: value(value)
{
}
// Conversion
constexpr operator s32() const
{
return value;
}
};
// Helper macro for silencing possible error checks on returning ppu_error_code values
#define NOT_AN_ERROR(value) { static_cast<s32>(value), std::nothrow }
template<typename T, typename>
struct ppu_gpr_cast_impl;
template<>
struct ppu_gpr_cast_impl<ppu_error_code, void>
{
static inline u64 to(const ppu_error_code& code)
{
return code;
}
static inline ppu_error_code from(const u64 reg)
{
return NOT_AN_ERROR(reg);
}
};
template<>
inline const char* ppu_error_code::print(CellError error)
{
switch (error)
{
STR_CASE(CELL_EAGAIN);
STR_CASE(CELL_EINVAL);
STR_CASE(CELL_ENOSYS);
STR_CASE(CELL_ENOMEM);
STR_CASE(CELL_ESRCH);
STR_CASE(CELL_ENOENT);
STR_CASE(CELL_ENOEXEC);
STR_CASE(CELL_EDEADLK);
STR_CASE(CELL_EPERM);
STR_CASE(CELL_EBUSY);
STR_CASE(CELL_ETIMEDOUT);
STR_CASE(CELL_EABORT);
STR_CASE(CELL_EFAULT);
STR_CASE(CELL_ESTAT);
STR_CASE(CELL_EALIGN);
STR_CASE(CELL_EKRESOURCE);
STR_CASE(CELL_EISDIR);
STR_CASE(CELL_ECANCELED);
STR_CASE(CELL_EEXIST);
STR_CASE(CELL_EISCONN);
STR_CASE(CELL_ENOTCONN);
STR_CASE(CELL_EAUTHFAIL);
STR_CASE(CELL_ENOTMSELF);
STR_CASE(CELL_ESYSVER);
STR_CASE(CELL_EAUTHFATAL);
STR_CASE(CELL_EDOM);
STR_CASE(CELL_ERANGE);
STR_CASE(CELL_EILSEQ);
STR_CASE(CELL_EFPOS);
STR_CASE(CELL_EINTR);
STR_CASE(CELL_EFBIG);
STR_CASE(CELL_EMLINK);
STR_CASE(CELL_ENFILE);
STR_CASE(CELL_ENOSPC);
STR_CASE(CELL_ENOTTY);
STR_CASE(CELL_EPIPE);
STR_CASE(CELL_EROFS);
STR_CASE(CELL_ESPIPE);
STR_CASE(CELL_E2BIG);
STR_CASE(CELL_EACCES);
STR_CASE(CELL_EBADF);
STR_CASE(CELL_EIO);
STR_CASE(CELL_EMFILE);
STR_CASE(CELL_ENODEV);
STR_CASE(CELL_ENOTDIR);
STR_CASE(CELL_ENXIO);
STR_CASE(CELL_EXDEV);
STR_CASE(CELL_EBADMSG);
STR_CASE(CELL_EINPROGRESS);
STR_CASE(CELL_EMSGSIZE);
STR_CASE(CELL_ENAMETOOLONG);
STR_CASE(CELL_ENOLCK);
STR_CASE(CELL_ENOTEMPTY);
STR_CASE(CELL_ENOTSUP);
STR_CASE(CELL_EFSSPECIFIC);
STR_CASE(CELL_EOVERFLOW);
STR_CASE(CELL_ENOTMOUNTED);
STR_CASE(CELL_ENOTSDATA);
}
return nullptr;
}

View File

@ -3,9 +3,8 @@
#include "Emu/System.h"
#include "Emu/IdManager.h"
#include "Emu/Cell/PPUThread.h"
#include "Emu/ARMv7/ARMv7Thread.h"
#include "Callback.h"
#include "PPUThread.h"
#include "PPUCallback.h"
void CallbackManager::Register(check_cb_t func)
{
@ -79,18 +78,15 @@ void CallbackManager::Init()
}
};
if (vm::get(vm::main)->addr == 0x10000)
{
auto thread = idm::make_ptr<PPUThread>("Callback Thread");
thread->prio = 1001;
thread->stack_size = 0x10000;
thread->custom_task = task;
thread->run();
thread->cpu_init();
m_cb_thread = thread;
}
}
void CallbackManager::Clear()
{

View File

@ -2,7 +2,7 @@
#include "Emu/Cell/PPUThread.h"
namespace cb_detail
namespace ppu_cb_detail
{
enum _func_arg_type
{
@ -10,7 +10,7 @@ namespace cb_detail
ARG_FLOAT,
ARG_VECTOR,
ARG_STACK,
ARG_CONTEXT, // for compatibility with SC_FUNC and CALL_FUNC
ARG_CONTEXT,
ARG_UNKNOWN,
};
@ -19,7 +19,7 @@ namespace cb_detail
// It's possible to calculate suitable stack frame size in template, but too complicated.
static const auto FIXED_STACK_FRAME_SIZE = 0x90;
template<typename T, _func_arg_type type, int g_count, int f_count, int v_count>
template<typename T, _func_arg_type type, u32 g_count, u32 f_count, u32 v_count>
struct _func_arg
{
static_assert(type == ARG_GENERAL, "Unknown callback argument type");
@ -27,50 +27,48 @@ namespace cb_detail
static_assert(!std::is_reference<T>::value, "Invalid callback argument type (reference)");
static_assert(sizeof(T) <= 8, "Invalid callback argument type for ARG_GENERAL");
force_inline static void set_value(PPUThread& CPU, const T& arg)
static inline void set_value(PPUThread& CPU, const T& arg)
{
CPU.GPR[g_count + 2] = cast_to_ppu_gpr<T>(arg);
CPU.GPR[g_count + 2] = ppu_gpr_cast(arg);
}
};
template<typename T, int g_count, int f_count, int v_count>
template<typename T, u32 g_count, u32 f_count, u32 v_count>
struct _func_arg<T, ARG_FLOAT, g_count, f_count, v_count>
{
static_assert(sizeof(T) <= 8, "Invalid callback argument type for ARG_FLOAT");
force_inline static void set_value(PPUThread& CPU, const T& arg)
static inline void set_value(PPUThread& CPU, const T& arg)
{
CPU.FPR[f_count] = static_cast<T>(arg);
}
};
template<typename T, int g_count, int f_count, int v_count>
template<typename T, u32 g_count, u32 f_count, u32 v_count>
struct _func_arg<T, ARG_VECTOR, g_count, f_count, v_count>
{
static_assert(std::is_same<std::remove_cv_t<T>, v128>::value, "Invalid callback argument type for ARG_VECTOR");
static_assert(std::is_same<CV T, CV v128>::value, "Invalid callback argument type for ARG_VECTOR");
force_inline static void set_value(PPUThread& CPU, const T& arg)
static inline void set_value(PPUThread& CPU, const T& arg)
{
CPU.VPR[v_count + 1] = arg;
CPU.VR[v_count + 1] = arg;
}
};
template<typename T, int g_count, int f_count, int v_count>
template<typename T, u32 g_count, u32 f_count, u32 v_count>
struct _func_arg<T, ARG_STACK, g_count, f_count, v_count>
{
static_assert(f_count <= 13, "TODO: Unsupported stack argument type (float)");
static_assert(v_count <= 12, "TODO: Unsupported stack argument type (vector)");
static_assert(sizeof(T) <= 8, "Invalid callback argument type for ARG_STACK");
static_assert(alignof(T) <= 16, "Unsupported callback argument type alignment for ARG_STACK");
force_inline static void set_value(PPUThread& CPU, const T& arg)
static inline void set_value(PPUThread& CPU, const T& arg)
{
const int stack_pos = (g_count - 9) * 8 - FIXED_STACK_FRAME_SIZE;
static_assert(stack_pos < 0, "TODO: Increase fixed stack frame size (arg count limit broken)");
vm::ps3::write64(CPU.GPR[1] + stack_pos, cast_to_ppu_gpr<T>(arg));
const s64 stack_pos = (g_count - 1) * 0x8 + 0x30 - FIXED_STACK_FRAME_SIZE;
static_assert(stack_pos < 0, "TODO: Increase FIXED_STACK_FRAME_SIZE (arg count limit broken)");
vm::ps3::write64(CPU.GPR[1] + stack_pos, ppu_gpr_cast(arg)); // TODO
}
};
template<typename T, int g_count, int f_count, int v_count>
template<typename T, u32 g_count, u32 f_count, u32 v_count>
struct _func_arg<T, ARG_CONTEXT, g_count, f_count, v_count>
{
static_assert(std::is_same<T, PPUThread&>::value, "Invalid callback argument type for ARG_CONTEXT");
@ -80,18 +78,18 @@ namespace cb_detail
}
};
template<int g_count, int f_count, int v_count>
template<u32 g_count, u32 f_count, u32 v_count>
force_inline static bool _bind_func_args(PPUThread& CPU)
{
// terminator
return false;
}
template<int g_count, int f_count, int v_count, typename T1, typename... T>
template<u32 g_count, u32 f_count, u32 v_count, typename T1, typename... T>
force_inline static bool _bind_func_args(PPUThread& CPU, T1 arg1, T... args)
{
const bool is_float = std::is_floating_point<T1>::value;
const bool is_vector = std::is_same<std::remove_cv_t<T1>, v128>::value;
const bool is_vector = std::is_same<CV T1, CV v128>::value;
const bool is_context = std::is_same<T1, PPUThread&>::value;
const bool is_general = !is_float && !is_vector && !is_context;
@ -102,9 +100,9 @@ namespace cb_detail
is_context ? ARG_CONTEXT :
ARG_UNKNOWN;
const int g = g_count + is_general;
const int f = f_count + is_float;
const int v = v_count + is_vector;
const u32 g = g_count + (is_general || is_float ? 1 : is_vector ? ::align(g_count, 2) + 2 : 0);
const u32 f = f_count + is_float;
const u32 v = v_count + is_vector;
_func_arg<T1, t, g, f, v>::set_value(CPU, arg1);
@ -120,7 +118,7 @@ namespace cb_detail
force_inline static T get_value(const PPUThread& CPU)
{
return cast_from_ppu_gpr<T>(CPU.GPR[3]);
return ppu_gpr_cast<T>(CPU.GPR[3]);
}
};
@ -138,11 +136,11 @@ namespace cb_detail
template<typename T>
struct _func_res<T, ARG_VECTOR>
{
static_assert(std::is_same<std::remove_cv_t<T>, v128>::value, "Invalid callback result type for ARG_VECTOR");
static_assert(std::is_same<CV T, CV v128>::value, "Invalid callback result type for ARG_VECTOR");
force_inline static T get_value(const PPUThread& CPU)
{
return CPU.VPR[2];
return CPU.VR[2];
}
};
@ -169,11 +167,9 @@ namespace cb_detail
force_inline static void call(PPUThread& CPU, u32 pc, u32 rtoc, T... args)
{
const bool stack = _bind_func_args<0, 0, 0, T...>(CPU, args...);
if (stack) CPU.GPR[1] -= FIXED_STACK_FRAME_SIZE;
CPU.GPR[1] -= 0x70; // create reserved area
CPU.GPR[1] -= stack ? FIXED_STACK_FRAME_SIZE : 0x30; // create reserved area
CPU.fast_call(pc, rtoc);
CPU.GPR[1] += 0x70;
if (stack) CPU.GPR[1] += FIXED_STACK_FRAME_SIZE;
CPU.GPR[1] += stack ? FIXED_STACK_FRAME_SIZE : 0x30;
}
};
}
@ -183,15 +179,44 @@ namespace vm
template<typename AT, typename RT, typename... T>
force_inline RT _ptr_base<RT(T...), AT>::operator()(PPUThread& CPU, T... args) const
{
const auto data = vm::ps3::_ptr<u32>(VM_CAST(m_addr));
const auto data = vm::ps3::_ptr<u32>(vm::cast(m_addr, HERE));
const u32 pc = data[0];
const u32 rtoc = data[1];
return cb_detail::_func_caller<RT, T...>::call(CPU, pc, rtoc, args...);
return ppu_cb_detail::_func_caller<RT, T...>::call(CPU, pc, rtoc, args...);
}
}
template<typename RT, typename... T> inline RT cb_call(PPUThread& CPU, u32 pc, u32 rtoc, T... args)
{
return cb_detail::_func_caller<RT, T...>::call(CPU, pc, rtoc, args...);
return ppu_cb_detail::_func_caller<RT, T...>::call(CPU, pc, rtoc, args...);
}
#include <queue>
class CallbackManager
{
using check_cb_t = std::function<s32(PPUThread&)>;
using async_cb_t = std::function<void(PPUThread&)>;
std::mutex m_mutex;
std::queue<check_cb_t> m_check_cb;
std::queue<async_cb_t> m_async_cb;
std::shared_ptr<PPUThread> m_cb_thread;
public:
// Register checked callback
void Register(check_cb_t func);
// Register async callback, called in callback thread
void Async(async_cb_t func);
// Get one registered callback
check_cb_t Check();
void Init();
void Clear();
};

2160
rpcs3/Emu/Cell/PPUDisAsm.cpp Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,14 +1,14 @@
#pragma once
#include "Emu/Cell/PPUThread.h"
#include "PPUThread.h"
using ppu_func_caller = void(*)(PPUThread&);
using ppu_function_t = void(*)(PPUThread&);
#define BIND_FUNC(func) [](PPUThread& ppu){ ppu.last_function = #func; ppu_func_detail::do_call(ppu, func); }
struct ppu_va_args_t
{
u32 g_count;
u32 f_count;
u32 v_count;
u32 count; // Number of 64-bit args passed
};
namespace ppu_func_detail
@ -16,12 +16,12 @@ namespace ppu_func_detail
// argument type classification
enum arg_class : u32
{
ARG_GENERAL, // argument is stored in GPR registers (from r3 to r10)
ARG_FLOAT, // argument is stored in FPR registers (from f1 to f13)
ARG_VECTOR, // argument is stored in VPR registers (from v2 to v13)
ARG_STACK, // argument is stored on the stack
ARG_GENERAL, // argument stored in GPR (from r3 to r10)
ARG_FLOAT, // argument stored in FPR (from f1 to f13)
ARG_VECTOR, // argument stored in VR (from v2 to v13)
ARG_STACK, // argument stored on the stack
ARG_CONTEXT, // PPUThread& passed, doesn't affect g/f/v_count
ARG_VARIADIC, // information about arg counts already passed, doesn't affect g/f/v_count
ARG_VARIADIC, // argument count at specific position, doesn't affect g/f/v_count
ARG_UNKNOWN,
};
@ -33,9 +33,9 @@ namespace ppu_func_detail
static_assert(!std::is_reference<T>::value, "Invalid function argument type (reference)");
static_assert(sizeof(T) <= 8, "Invalid function argument type for ARG_GENERAL");
static force_inline T get_arg(PPUThread& ppu)
static inline T get_arg(PPUThread& ppu)
{
return cast_from_ppu_gpr<T>(ppu.GPR[g_count + 2]);
return ppu_gpr_cast<T>(ppu.GPR[g_count + 2]);
}
};
@ -44,7 +44,7 @@ namespace ppu_func_detail
{
static_assert(sizeof(T) <= 8, "Invalid function argument type for ARG_FLOAT");
static force_inline T get_arg(PPUThread& ppu)
static inline T get_arg(PPUThread& ppu)
{
return static_cast<T>(ppu.FPR[f_count]);
}
@ -53,26 +53,22 @@ namespace ppu_func_detail
template<typename T, u32 g_count, u32 f_count, u32 v_count>
struct bind_arg<T, ARG_VECTOR, g_count, f_count, v_count>
{
static_assert(std::is_same<std::remove_cv_t<T>, v128>::value, "Invalid function argument type for ARG_VECTOR");
static_assert(std::is_same<CV T, CV v128>::value, "Invalid function argument type for ARG_VECTOR");
static force_inline T get_arg(PPUThread& ppu)
{
return ppu.VPR[v_count + 1];
return ppu.VR[v_count + 1];
}
};
template<typename T, u32 g_count, u32 f_count, u32 v_count>
struct bind_arg<T, ARG_STACK, g_count, f_count, v_count>
{
static_assert(f_count <= 13, "TODO: Unsupported stack argument type (float)");
static_assert(v_count <= 12, "TODO: Unsupported stack argument type (vector)");
static_assert(sizeof(T) <= 8, "Invalid function argument type for ARG_STACK");
static_assert(alignof(T) <= 16, "Unsupported type alignment for ARG_STACK");
static force_inline T get_arg(PPUThread& ppu)
{
// TODO: check stack argument displacement
const u64 res = ppu.get_stack_arg(8 + std::max<s32>(g_count - 8, 0) + std::max<s32>(f_count - 13, 0) + std::max<s32>(v_count - 12, 0));
return cast_from_ppu_gpr<T>(res);
return ppu_gpr_cast<T, u64>(*ppu.get_stack_arg(g_count, alignof(T))); // TODO
}
};
@ -94,7 +90,7 @@ namespace ppu_func_detail
static force_inline ppu_va_args_t get_arg(PPUThread& ppu)
{
return{ g_count, f_count, v_count };
return{ g_count };
}
};
@ -106,7 +102,7 @@ namespace ppu_func_detail
static force_inline void put_result(PPUThread& ppu, const T& result)
{
ppu.GPR[3] = cast_to_ppu_gpr<T>(result);
ppu.GPR[3] = ppu_gpr_cast(result);
}
};
@ -124,11 +120,11 @@ namespace ppu_func_detail
template<typename T>
struct bind_result<T, ARG_VECTOR>
{
static_assert(std::is_same<std::remove_cv_t<T>, v128>::value, "Invalid function result type for ARG_VECTOR");
static_assert(std::is_same<CV T, CV v128>::value, "Invalid function result type for ARG_VECTOR");
static force_inline void put_result(PPUThread& ppu, const T& result)
{
ppu.VPR[2] = result;
ppu.VR[2] = result;
}
};
@ -176,9 +172,9 @@ namespace ppu_func_detail
// TODO: check calculations
const bool is_float = std::is_floating_point<T>::value;
const bool is_vector = std::is_same<std::remove_cv_t<T>, v128>::value;
const bool is_vector = std::is_same<CV T, CV v128>::value;
const bool is_context = std::is_same<T, PPUThread&>::value;
const bool is_variadic = std::is_same<std::remove_cv_t<T>, ppu_va_args_t>::value;
const bool is_variadic = std::is_same<CV T, CV ppu_va_args_t>::value;
const bool is_general = !is_float && !is_vector && !is_context && !is_variadic;
const arg_class t =
@ -189,19 +185,20 @@ namespace ppu_func_detail
is_variadic ? ARG_VARIADIC :
ARG_UNKNOWN;
const u32 g = g_count + is_general;
const u32 g = g_count + (is_general || is_float ? 1 : is_vector ? ::align(g_count, 2) + 2 : 0);
const u32 f = f_count + is_float;
const u32 v = v_count + is_vector;
return call<Types...>(ppu, func, arg_info_pack_t<Info..., t | (g << 8) | (f << 16) | (v << 24)>{});
}
template<typename RT> struct result_type
template<typename RT>
struct result_type
{
static_assert(!std::is_pointer<RT>::value, "Invalid function result type (pointer)");
static_assert(!std::is_reference<RT>::value, "Invalid function result type (reference)");
static const bool is_float = std::is_floating_point<RT>::value;
static const bool is_vector = std::is_same<std::remove_cv_t<RT>, v128>::value;
static const bool is_vector = std::is_same<CV RT, CV v128>::value;
static const arg_class value = is_float ? ARG_FLOAT : (is_vector ? ARG_VECTOR : ARG_GENERAL);
};
@ -229,10 +226,66 @@ namespace ppu_func_detail
}
};
template<typename RT, typename... T> force_inline void do_call(PPUThread& ppu, RT(*func)(T...))
template<typename RT, typename... T>
force_inline void do_call(PPUThread& ppu, RT(*func)(T...))
{
func_binder<RT, T...>::do_call(ppu, func);
}
}
#define BIND_FUNC(func) [](PPUThread& ppu){ ppu_func_detail::do_call(ppu, func); }
class ppu_function_manager
{
// Global variable for each registered function
template<typename T, T Func>
struct registered
{
static u32 index;
};
// Access global function list
static never_inline auto& access()
{
static std::vector<ppu_function_t> list
{
nullptr,
[](PPUThread& ppu) { ppu.state += cpu_state::ret; },
};
return list;
}
static never_inline u32 add_function(ppu_function_t function)
{
auto& list = access();
list.push_back(function);
return ::size32(list) - 1;
}
public:
// Register function (shall only be called during global initialization)
template<typename T, T Func>
static inline u32 register_function(ppu_function_t func)
{
return registered<T, Func>::index = add_function(func);
}
// Get function index
template<typename T, T Func>
static inline u32 get_index()
{
return registered<T, Func>::index;
}
// Read all registered functions
static inline const auto& get()
{
return access();
}
};
template<typename T, T Func>
u32 ppu_function_manager::registered<T, Func>::index = 0;
#define FIND_FUNC(func) ppu_function_manager::get_index<decltype(&func), &func>()

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,870 +0,0 @@
#pragma once
#include "PPUOpcodes.h"
class PPUThread;
union ppu_opcode_t
{
u32 opcode;
bf_t<u32, 32 - 1 - 30, 1> shh; // 30
bf_t<u32, 32 - 1 - 26, 1> mbmeh; // 26
bf_t<u32, 32 - 5 - 21, 5> mbmel; // 21..25
bf_t<u32, 32 - 5 - 16, 5> shl; // 16..20
bf_t<u32, 32 - 5 - 11, 5> vuimm; // 11..15
bf_t<u32, 32 - 5 - 6, 5> vs; // 6..10
bf_t<u32, 32 - 4 - 22, 4> vsh; // 22..25
bf_t<u32, 32 - 1 - 21, 1> oe; // 21
bf_t<u32, 32 - 10 - 11, 10> spr; // 11..20
bf_t<u32, 32 - 5 - 21, 5> vc; // 21..25
bf_t<u32, 32 - 5 - 16, 5> vb; // 16..20
bf_t<u32, 32 - 5 - 11, 5> va; // 11..15
bf_t<u32, 32 - 5 - 6, 5> vd; // 6..10
bf_t<u32, 32 - 1 - 31, 1> lk; // 31
bf_t<u32, 32 - 1 - 30, 1> aa; // 30
bf_t<u32, 32 - 5 - 16, 5> rb; // 16..20
bf_t<u32, 32 - 5 - 11, 5> ra; // 11..15
bf_t<u32, 32 - 5 - 6, 5> rd; // 6..10
bf_t<u32, 32 - 16 - 16, 16> uimm16; // 16..31
bf_t<u32, 32 - 1 - 11, 1> l11; // 11
bf_t<u32, 32 - 5 - 6, 5> rs; // 6..10
bf_t<s32, 32 - 16 - 16, 16> simm16; // 16..31, signed
bf_t<s32, 32 - 5 - 11, 5> vsimm; // 11..15, signed
bf_t<s32, 32 - 26 - 6, 26> ll; // 6..31, signed
bf_t<u32, 32 - 7 - 20, 7> lev; // 20..26
bf_t<u32, 32 - 4 - 16, 4> i; // 16..19
bf_t<u32, 32 - 3 - 11, 3> crfs; // 11..13
bf_t<u32, 32 - 1 - 10, 1> l10; // 10
bf_t<u32, 32 - 3 - 6, 3> crfd; // 6..8
bf_t<u32, 32 - 5 - 16, 5> crbb; // 16..20
bf_t<u32, 32 - 5 - 11, 5> crba; // 11..15
bf_t<u32, 32 - 5 - 6, 5> crbd; // 6..10
bf_t<u32, 32 - 1 - 31, 1> rc; // 31
bf_t<u32, 32 - 5 - 26, 5> me; // 26..30
bf_t<u32, 32 - 5 - 21, 5> mb; // 21..25
bf_t<u32, 32 - 5 - 16, 5> sh; // 16..20
bf_t<u32, 32 - 5 - 11, 5> bi; // 11..15
bf_t<u32, 32 - 5 - 6, 5> bo; // 6..10
bf_t<u32, 32 - 5 - 21, 5> frc; // 21..25
bf_t<u32, 32 - 5 - 16, 5> frb; // 16..20
bf_t<u32, 32 - 5 - 11, 5> fra; // 11..15
bf_t<u32, 32 - 5 - 6, 5> frd; // 6..10
bf_t<u32, 32 - 8 - 12, 8> crm; // 12..19
bf_t<u32, 32 - 5 - 6, 5> frs; // 6..10
bf_t<u32, 32 - 8 - 7, 8> flm; // 7..14
};
namespace ppu_interpreter
{
void NULL_OP(PPUThread& CPU, ppu_opcode_t op);
void NOP(PPUThread& CPU, ppu_opcode_t op);
void TDI(PPUThread& CPU, ppu_opcode_t op);
void TWI(PPUThread& CPU, ppu_opcode_t op);
void MFVSCR(PPUThread& CPU, ppu_opcode_t op);
void MTVSCR(PPUThread& CPU, ppu_opcode_t op);
void VADDCUW(PPUThread& CPU, ppu_opcode_t op);
void VADDFP(PPUThread& CPU, ppu_opcode_t op);
void VADDSBS(PPUThread& CPU, ppu_opcode_t op);
void VADDSHS(PPUThread& CPU, ppu_opcode_t op);
void VADDSWS(PPUThread& CPU, ppu_opcode_t op);
void VADDUBM(PPUThread& CPU, ppu_opcode_t op);
void VADDUBS(PPUThread& CPU, ppu_opcode_t op);
void VADDUHM(PPUThread& CPU, ppu_opcode_t op);
void VADDUHS(PPUThread& CPU, ppu_opcode_t op);
void VADDUWM(PPUThread& CPU, ppu_opcode_t op);
void VADDUWS(PPUThread& CPU, ppu_opcode_t op);
void VAND(PPUThread& CPU, ppu_opcode_t op);
void VANDC(PPUThread& CPU, ppu_opcode_t op);
void VAVGSB(PPUThread& CPU, ppu_opcode_t op);
void VAVGSH(PPUThread& CPU, ppu_opcode_t op);
void VAVGSW(PPUThread& CPU, ppu_opcode_t op);
void VAVGUB(PPUThread& CPU, ppu_opcode_t op);
void VAVGUH(PPUThread& CPU, ppu_opcode_t op);
void VAVGUW(PPUThread& CPU, ppu_opcode_t op);
void VCFSX(PPUThread& CPU, ppu_opcode_t op);
void VCFUX(PPUThread& CPU, ppu_opcode_t op);
void VCMPBFP(PPUThread& CPU, ppu_opcode_t op);
void VCMPBFP_(PPUThread& CPU, ppu_opcode_t op);
void VCMPEQFP(PPUThread& CPU, ppu_opcode_t op);
void VCMPEQFP_(PPUThread& CPU, ppu_opcode_t op);
void VCMPEQUB(PPUThread& CPU, ppu_opcode_t op);
void VCMPEQUB_(PPUThread& CPU, ppu_opcode_t op);
void VCMPEQUH(PPUThread& CPU, ppu_opcode_t op);
void VCMPEQUH_(PPUThread& CPU, ppu_opcode_t op);
void VCMPEQUW(PPUThread& CPU, ppu_opcode_t op);
void VCMPEQUW_(PPUThread& CPU, ppu_opcode_t op);
void VCMPGEFP(PPUThread& CPU, ppu_opcode_t op);
void VCMPGEFP_(PPUThread& CPU, ppu_opcode_t op);
void VCMPGTFP(PPUThread& CPU, ppu_opcode_t op);
void VCMPGTFP_(PPUThread& CPU, ppu_opcode_t op);
void VCMPGTSB(PPUThread& CPU, ppu_opcode_t op);
void VCMPGTSB_(PPUThread& CPU, ppu_opcode_t op);
void VCMPGTSH(PPUThread& CPU, ppu_opcode_t op);
void VCMPGTSH_(PPUThread& CPU, ppu_opcode_t op);
void VCMPGTSW(PPUThread& CPU, ppu_opcode_t op);
void VCMPGTSW_(PPUThread& CPU, ppu_opcode_t op);
void VCMPGTUB(PPUThread& CPU, ppu_opcode_t op);
void VCMPGTUB_(PPUThread& CPU, ppu_opcode_t op);
void VCMPGTUH(PPUThread& CPU, ppu_opcode_t op);
void VCMPGTUH_(PPUThread& CPU, ppu_opcode_t op);
void VCMPGTUW(PPUThread& CPU, ppu_opcode_t op);
void VCMPGTUW_(PPUThread& CPU, ppu_opcode_t op);
void VCTSXS(PPUThread& CPU, ppu_opcode_t op);
void VCTUXS(PPUThread& CPU, ppu_opcode_t op);
void VEXPTEFP(PPUThread& CPU, ppu_opcode_t op);
void VLOGEFP(PPUThread& CPU, ppu_opcode_t op);
void VMADDFP(PPUThread& CPU, ppu_opcode_t op);
void VMAXFP(PPUThread& CPU, ppu_opcode_t op);
void VMAXSB(PPUThread& CPU, ppu_opcode_t op);
void VMAXSH(PPUThread& CPU, ppu_opcode_t op);
void VMAXSW(PPUThread& CPU, ppu_opcode_t op);
void VMAXUB(PPUThread& CPU, ppu_opcode_t op);
void VMAXUH(PPUThread& CPU, ppu_opcode_t op);
void VMAXUW(PPUThread& CPU, ppu_opcode_t op);
void VMHADDSHS(PPUThread& CPU, ppu_opcode_t op);
void VMHRADDSHS(PPUThread& CPU, ppu_opcode_t op);
void VMINFP(PPUThread& CPU, ppu_opcode_t op);
void VMINSB(PPUThread& CPU, ppu_opcode_t op);
void VMINSH(PPUThread& CPU, ppu_opcode_t op);
void VMINSW(PPUThread& CPU, ppu_opcode_t op);
void VMINUB(PPUThread& CPU, ppu_opcode_t op);
void VMINUH(PPUThread& CPU, ppu_opcode_t op);
void VMINUW(PPUThread& CPU, ppu_opcode_t op);
void VMLADDUHM(PPUThread& CPU, ppu_opcode_t op);
void VMRGHB(PPUThread& CPU, ppu_opcode_t op);
void VMRGHH(PPUThread& CPU, ppu_opcode_t op);
void VMRGHW(PPUThread& CPU, ppu_opcode_t op);
void VMRGLB(PPUThread& CPU, ppu_opcode_t op);
void VMRGLH(PPUThread& CPU, ppu_opcode_t op);
void VMRGLW(PPUThread& CPU, ppu_opcode_t op);
void VMSUMMBM(PPUThread& CPU, ppu_opcode_t op);
void VMSUMSHM(PPUThread& CPU, ppu_opcode_t op);
void VMSUMSHS(PPUThread& CPU, ppu_opcode_t op);
void VMSUMUBM(PPUThread& CPU, ppu_opcode_t op);
void VMSUMUHM(PPUThread& CPU, ppu_opcode_t op);
void VMSUMUHS(PPUThread& CPU, ppu_opcode_t op);
void VMULESB(PPUThread& CPU, ppu_opcode_t op);
void VMULESH(PPUThread& CPU, ppu_opcode_t op);
void VMULEUB(PPUThread& CPU, ppu_opcode_t op);
void VMULEUH(PPUThread& CPU, ppu_opcode_t op);
void VMULOSB(PPUThread& CPU, ppu_opcode_t op);
void VMULOSH(PPUThread& CPU, ppu_opcode_t op);
void VMULOUB(PPUThread& CPU, ppu_opcode_t op);
void VMULOUH(PPUThread& CPU, ppu_opcode_t op);
void VNMSUBFP(PPUThread& CPU, ppu_opcode_t op);
void VNOR(PPUThread& CPU, ppu_opcode_t op);
void VOR(PPUThread& CPU, ppu_opcode_t op);
void VPERM(PPUThread& CPU, ppu_opcode_t op);
void VPKPX(PPUThread& CPU, ppu_opcode_t op);
void VPKSHSS(PPUThread& CPU, ppu_opcode_t op);
void VPKSHUS(PPUThread& CPU, ppu_opcode_t op);
void VPKSWSS(PPUThread& CPU, ppu_opcode_t op);
void VPKSWUS(PPUThread& CPU, ppu_opcode_t op);
void VPKUHUM(PPUThread& CPU, ppu_opcode_t op);
void VPKUHUS(PPUThread& CPU, ppu_opcode_t op);
void VPKUWUM(PPUThread& CPU, ppu_opcode_t op);
void VPKUWUS(PPUThread& CPU, ppu_opcode_t op);
void VREFP(PPUThread& CPU, ppu_opcode_t op);
void VRFIM(PPUThread& CPU, ppu_opcode_t op);
void VRFIN(PPUThread& CPU, ppu_opcode_t op);
void VRFIP(PPUThread& CPU, ppu_opcode_t op);
void VRFIZ(PPUThread& CPU, ppu_opcode_t op);
void VRLB(PPUThread& CPU, ppu_opcode_t op);
void VRLH(PPUThread& CPU, ppu_opcode_t op);
void VRLW(PPUThread& CPU, ppu_opcode_t op);
void VRSQRTEFP(PPUThread& CPU, ppu_opcode_t op);
void VSEL(PPUThread& CPU, ppu_opcode_t op);
void VSL(PPUThread& CPU, ppu_opcode_t op);
void VSLB(PPUThread& CPU, ppu_opcode_t op);
void VSLDOI(PPUThread& CPU, ppu_opcode_t op);
void VSLH(PPUThread& CPU, ppu_opcode_t op);
void VSLO(PPUThread& CPU, ppu_opcode_t op);
void VSLW(PPUThread& CPU, ppu_opcode_t op);
void VSPLTB(PPUThread& CPU, ppu_opcode_t op);
void VSPLTH(PPUThread& CPU, ppu_opcode_t op);
void VSPLTISB(PPUThread& CPU, ppu_opcode_t op);
void VSPLTISH(PPUThread& CPU, ppu_opcode_t op);
void VSPLTISW(PPUThread& CPU, ppu_opcode_t op);
void VSPLTW(PPUThread& CPU, ppu_opcode_t op);
void VSR(PPUThread& CPU, ppu_opcode_t op);
void VSRAB(PPUThread& CPU, ppu_opcode_t op);
void VSRAH(PPUThread& CPU, ppu_opcode_t op);
void VSRAW(PPUThread& CPU, ppu_opcode_t op);
void VSRB(PPUThread& CPU, ppu_opcode_t op);
void VSRH(PPUThread& CPU, ppu_opcode_t op);
void VSRO(PPUThread& CPU, ppu_opcode_t op);
void VSRW(PPUThread& CPU, ppu_opcode_t op);
void VSUBCUW(PPUThread& CPU, ppu_opcode_t op);
void VSUBFP(PPUThread& CPU, ppu_opcode_t op);
void VSUBSBS(PPUThread& CPU, ppu_opcode_t op);
void VSUBSHS(PPUThread& CPU, ppu_opcode_t op);
void VSUBSWS(PPUThread& CPU, ppu_opcode_t op);
void VSUBUBM(PPUThread& CPU, ppu_opcode_t op);
void VSUBUBS(PPUThread& CPU, ppu_opcode_t op);
void VSUBUHM(PPUThread& CPU, ppu_opcode_t op);
void VSUBUHS(PPUThread& CPU, ppu_opcode_t op);
void VSUBUWM(PPUThread& CPU, ppu_opcode_t op);
void VSUBUWS(PPUThread& CPU, ppu_opcode_t op);
void VSUMSWS(PPUThread& CPU, ppu_opcode_t op);
void VSUM2SWS(PPUThread& CPU, ppu_opcode_t op);
void VSUM4SBS(PPUThread& CPU, ppu_opcode_t op);
void VSUM4SHS(PPUThread& CPU, ppu_opcode_t op);
void VSUM4UBS(PPUThread& CPU, ppu_opcode_t op);
void VUPKHPX(PPUThread& CPU, ppu_opcode_t op);
void VUPKHSB(PPUThread& CPU, ppu_opcode_t op);
void VUPKHSH(PPUThread& CPU, ppu_opcode_t op);
void VUPKLPX(PPUThread& CPU, ppu_opcode_t op);
void VUPKLSB(PPUThread& CPU, ppu_opcode_t op);
void VUPKLSH(PPUThread& CPU, ppu_opcode_t op);
void VXOR(PPUThread& CPU, ppu_opcode_t op);
void MULLI(PPUThread& CPU, ppu_opcode_t op);
void SUBFIC(PPUThread& CPU, ppu_opcode_t op);
void CMPLI(PPUThread& CPU, ppu_opcode_t op);
void CMPI(PPUThread& CPU, ppu_opcode_t op);
void ADDIC(PPUThread& CPU, ppu_opcode_t op);
void ADDIC_(PPUThread& CPU, ppu_opcode_t op);
void ADDI(PPUThread& CPU, ppu_opcode_t op);
void ADDIS(PPUThread& CPU, ppu_opcode_t op);
void BC(PPUThread& CPU, ppu_opcode_t op);
void HACK(PPUThread& CPU, ppu_opcode_t op);
void SC(PPUThread& CPU, ppu_opcode_t op);
void B(PPUThread& CPU, ppu_opcode_t op);
void MCRF(PPUThread& CPU, ppu_opcode_t op);
void BCLR(PPUThread& CPU, ppu_opcode_t op);
void CRNOR(PPUThread& CPU, ppu_opcode_t op);
void CRANDC(PPUThread& CPU, ppu_opcode_t op);
void ISYNC(PPUThread& CPU, ppu_opcode_t op);
void CRXOR(PPUThread& CPU, ppu_opcode_t op);
void CRNAND(PPUThread& CPU, ppu_opcode_t op);
void CRAND(PPUThread& CPU, ppu_opcode_t op);
void CREQV(PPUThread& CPU, ppu_opcode_t op);
void CRORC(PPUThread& CPU, ppu_opcode_t op);
void CROR(PPUThread& CPU, ppu_opcode_t op);
void BCCTR(PPUThread& CPU, ppu_opcode_t op);
void RLWIMI(PPUThread& CPU, ppu_opcode_t op);
void RLWINM(PPUThread& CPU, ppu_opcode_t op);
void RLWNM(PPUThread& CPU, ppu_opcode_t op);
void ORI(PPUThread& CPU, ppu_opcode_t op);
void ORIS(PPUThread& CPU, ppu_opcode_t op);
void XORI(PPUThread& CPU, ppu_opcode_t op);
void XORIS(PPUThread& CPU, ppu_opcode_t op);
void ANDI_(PPUThread& CPU, ppu_opcode_t op);
void ANDIS_(PPUThread& CPU, ppu_opcode_t op);
void RLDICL(PPUThread& CPU, ppu_opcode_t op);
void RLDICR(PPUThread& CPU, ppu_opcode_t op);
void RLDIC(PPUThread& CPU, ppu_opcode_t op);
void RLDIMI(PPUThread& CPU, ppu_opcode_t op);
void RLDC_LR(PPUThread& CPU, ppu_opcode_t op);
void CMP(PPUThread& CPU, ppu_opcode_t op);
void TW(PPUThread& CPU, ppu_opcode_t op);
void LVSL(PPUThread& CPU, ppu_opcode_t op);
void LVEBX(PPUThread& CPU, ppu_opcode_t op);
void SUBFC(PPUThread& CPU, ppu_opcode_t op);
void MULHDU(PPUThread& CPU, ppu_opcode_t op);
void ADDC(PPUThread& CPU, ppu_opcode_t op);
void MULHWU(PPUThread& CPU, ppu_opcode_t op);
void MFOCRF(PPUThread& CPU, ppu_opcode_t op);
void LWARX(PPUThread& CPU, ppu_opcode_t op);
void LDX(PPUThread& CPU, ppu_opcode_t op);
void LWZX(PPUThread& CPU, ppu_opcode_t op);
void SLW(PPUThread& CPU, ppu_opcode_t op);
void CNTLZW(PPUThread& CPU, ppu_opcode_t op);
void SLD(PPUThread& CPU, ppu_opcode_t op);
void AND(PPUThread& CPU, ppu_opcode_t op);
void CMPL(PPUThread& CPU, ppu_opcode_t op);
void LVSR(PPUThread& CPU, ppu_opcode_t op);
void LVEHX(PPUThread& CPU, ppu_opcode_t op);
void SUBF(PPUThread& CPU, ppu_opcode_t op);
void LDUX(PPUThread& CPU, ppu_opcode_t op);
void DCBST(PPUThread& CPU, ppu_opcode_t op);
void LWZUX(PPUThread& CPU, ppu_opcode_t op);
void CNTLZD(PPUThread& CPU, ppu_opcode_t op);
void ANDC(PPUThread& CPU, ppu_opcode_t op);
void TD(PPUThread& CPU, ppu_opcode_t op);
void LVEWX(PPUThread& CPU, ppu_opcode_t op);
void MULHD(PPUThread& CPU, ppu_opcode_t op);
void MULHW(PPUThread& CPU, ppu_opcode_t op);
void LDARX(PPUThread& CPU, ppu_opcode_t op);
void DCBF(PPUThread& CPU, ppu_opcode_t op);
void LBZX(PPUThread& CPU, ppu_opcode_t op);
void LVX(PPUThread& CPU, ppu_opcode_t op);
void NEG(PPUThread& CPU, ppu_opcode_t op);
void LBZUX(PPUThread& CPU, ppu_opcode_t op);
void NOR(PPUThread& CPU, ppu_opcode_t op);
void STVEBX(PPUThread& CPU, ppu_opcode_t op);
void SUBFE(PPUThread& CPU, ppu_opcode_t op);
void ADDE(PPUThread& CPU, ppu_opcode_t op);
void MTOCRF(PPUThread& CPU, ppu_opcode_t op);
void STDX(PPUThread& CPU, ppu_opcode_t op);
void STWCX_(PPUThread& CPU, ppu_opcode_t op);
void STWX(PPUThread& CPU, ppu_opcode_t op);
void STVEHX(PPUThread& CPU, ppu_opcode_t op);
void STDUX(PPUThread& CPU, ppu_opcode_t op);
void STWUX(PPUThread& CPU, ppu_opcode_t op);
void STVEWX(PPUThread& CPU, ppu_opcode_t op);
void SUBFZE(PPUThread& CPU, ppu_opcode_t op);
void ADDZE(PPUThread& CPU, ppu_opcode_t op);
void STDCX_(PPUThread& CPU, ppu_opcode_t op);
void STBX(PPUThread& CPU, ppu_opcode_t op);
void STVX(PPUThread& CPU, ppu_opcode_t op);
void MULLD(PPUThread& CPU, ppu_opcode_t op);
void SUBFME(PPUThread& CPU, ppu_opcode_t op);
void ADDME(PPUThread& CPU, ppu_opcode_t op);
void MULLW(PPUThread& CPU, ppu_opcode_t op);
void DCBTST(PPUThread& CPU, ppu_opcode_t op);
void STBUX(PPUThread& CPU, ppu_opcode_t op);
void ADD(PPUThread& CPU, ppu_opcode_t op);
void DCBT(PPUThread& CPU, ppu_opcode_t op);
void LHZX(PPUThread& CPU, ppu_opcode_t op);
void EQV(PPUThread& CPU, ppu_opcode_t op);
void ECIWX(PPUThread& CPU, ppu_opcode_t op);
void LHZUX(PPUThread& CPU, ppu_opcode_t op);
void XOR(PPUThread& CPU, ppu_opcode_t op);
void MFSPR(PPUThread& CPU, ppu_opcode_t op);
void LWAX(PPUThread& CPU, ppu_opcode_t op);
void DST(PPUThread& CPU, ppu_opcode_t op);
void LHAX(PPUThread& CPU, ppu_opcode_t op);
void LVXL(PPUThread& CPU, ppu_opcode_t op);
void MFTB(PPUThread& CPU, ppu_opcode_t op);
void LWAUX(PPUThread& CPU, ppu_opcode_t op);
void DSTST(PPUThread& CPU, ppu_opcode_t op);
void LHAUX(PPUThread& CPU, ppu_opcode_t op);
void STHX(PPUThread& CPU, ppu_opcode_t op);
void ORC(PPUThread& CPU, ppu_opcode_t op);
void ECOWX(PPUThread& CPU, ppu_opcode_t op);
void STHUX(PPUThread& CPU, ppu_opcode_t op);
void OR(PPUThread& CPU, ppu_opcode_t op);
void DIVDU(PPUThread& CPU, ppu_opcode_t op);
void DIVWU(PPUThread& CPU, ppu_opcode_t op);
void MTSPR(PPUThread& CPU, ppu_opcode_t op);
void DCBI(PPUThread& CPU, ppu_opcode_t op);
void NAND(PPUThread& CPU, ppu_opcode_t op);
void STVXL(PPUThread& CPU, ppu_opcode_t op);
void DIVD(PPUThread& CPU, ppu_opcode_t op);
void DIVW(PPUThread& CPU, ppu_opcode_t op);
void LVLX(PPUThread& CPU, ppu_opcode_t op);
void LDBRX(PPUThread& CPU, ppu_opcode_t op);
void LSWX(PPUThread& CPU, ppu_opcode_t op);
void LWBRX(PPUThread& CPU, ppu_opcode_t op);
void LFSX(PPUThread& CPU, ppu_opcode_t op);
void SRW(PPUThread& CPU, ppu_opcode_t op);
void SRD(PPUThread& CPU, ppu_opcode_t op);
void LVRX(PPUThread& CPU, ppu_opcode_t op);
void LSWI(PPUThread& CPU, ppu_opcode_t op);
void LFSUX(PPUThread& CPU, ppu_opcode_t op);
void SYNC(PPUThread& CPU, ppu_opcode_t op);
void LFDX(PPUThread& CPU, ppu_opcode_t op);
void LFDUX(PPUThread& CPU, ppu_opcode_t op);
void STVLX(PPUThread& CPU, ppu_opcode_t op);
void STDBRX(PPUThread& CPU, ppu_opcode_t op);
void STSWX(PPUThread& CPU, ppu_opcode_t op);
void STWBRX(PPUThread& CPU, ppu_opcode_t op);
void STFSX(PPUThread& CPU, ppu_opcode_t op);
void STVRX(PPUThread& CPU, ppu_opcode_t op);
void STFSUX(PPUThread& CPU, ppu_opcode_t op);
void STSWI(PPUThread& CPU, ppu_opcode_t op);
void STFDX(PPUThread& CPU, ppu_opcode_t op);
void STFDUX(PPUThread& CPU, ppu_opcode_t op);
void LVLXL(PPUThread& CPU, ppu_opcode_t op);
void LHBRX(PPUThread& CPU, ppu_opcode_t op);
void SRAW(PPUThread& CPU, ppu_opcode_t op);
void SRAD(PPUThread& CPU, ppu_opcode_t op);
void LVRXL(PPUThread& CPU, ppu_opcode_t op);
void DSS(PPUThread& CPU, ppu_opcode_t op);
void SRAWI(PPUThread& CPU, ppu_opcode_t op);
void SRADI(PPUThread& CPU, ppu_opcode_t op);
void EIEIO(PPUThread& CPU, ppu_opcode_t op);
void STVLXL(PPUThread& CPU, ppu_opcode_t op);
void STHBRX(PPUThread& CPU, ppu_opcode_t op);
void EXTSH(PPUThread& CPU, ppu_opcode_t op);
void STVRXL(PPUThread& CPU, ppu_opcode_t op);
void EXTSB(PPUThread& CPU, ppu_opcode_t op);
void STFIWX(PPUThread& CPU, ppu_opcode_t op);
void EXTSW(PPUThread& CPU, ppu_opcode_t op);
void ICBI(PPUThread& CPU, ppu_opcode_t op);
void DCBZ(PPUThread& CPU, ppu_opcode_t op);
void LWZ(PPUThread& CPU, ppu_opcode_t op);
void LWZU(PPUThread& CPU, ppu_opcode_t op);
void LBZ(PPUThread& CPU, ppu_opcode_t op);
void LBZU(PPUThread& CPU, ppu_opcode_t op);
void STW(PPUThread& CPU, ppu_opcode_t op);
void STWU(PPUThread& CPU, ppu_opcode_t op);
void STB(PPUThread& CPU, ppu_opcode_t op);
void STBU(PPUThread& CPU, ppu_opcode_t op);
void LHZ(PPUThread& CPU, ppu_opcode_t op);
void LHZU(PPUThread& CPU, ppu_opcode_t op);
void LHA(PPUThread& CPU, ppu_opcode_t op);
void LHAU(PPUThread& CPU, ppu_opcode_t op);
void STH(PPUThread& CPU, ppu_opcode_t op);
void STHU(PPUThread& CPU, ppu_opcode_t op);
void LMW(PPUThread& CPU, ppu_opcode_t op);
void STMW(PPUThread& CPU, ppu_opcode_t op);
void LFS(PPUThread& CPU, ppu_opcode_t op);
void LFSU(PPUThread& CPU, ppu_opcode_t op);
void LFD(PPUThread& CPU, ppu_opcode_t op);
void LFDU(PPUThread& CPU, ppu_opcode_t op);
void STFS(PPUThread& CPU, ppu_opcode_t op);
void STFSU(PPUThread& CPU, ppu_opcode_t op);
void STFD(PPUThread& CPU, ppu_opcode_t op);
void STFDU(PPUThread& CPU, ppu_opcode_t op);
void LD(PPUThread& CPU, ppu_opcode_t op);
void LDU(PPUThread& CPU, ppu_opcode_t op);
void LWA(PPUThread& CPU, ppu_opcode_t op);
void FDIVS(PPUThread& CPU, ppu_opcode_t op);
void FSUBS(PPUThread& CPU, ppu_opcode_t op);
void FADDS(PPUThread& CPU, ppu_opcode_t op);
void FSQRTS(PPUThread& CPU, ppu_opcode_t op);
void FRES(PPUThread& CPU, ppu_opcode_t op);
void FMULS(PPUThread& CPU, ppu_opcode_t op);
void FMADDS(PPUThread& CPU, ppu_opcode_t op);
void FMSUBS(PPUThread& CPU, ppu_opcode_t op);
void FNMSUBS(PPUThread& CPU, ppu_opcode_t op);
void FNMADDS(PPUThread& CPU, ppu_opcode_t op);
void STD(PPUThread& CPU, ppu_opcode_t op);
void STDU(PPUThread& CPU, ppu_opcode_t op);
void MTFSB1(PPUThread& CPU, ppu_opcode_t op);
void MCRFS(PPUThread& CPU, ppu_opcode_t op);
void MTFSB0(PPUThread& CPU, ppu_opcode_t op);
void MTFSFI(PPUThread& CPU, ppu_opcode_t op);
void MFFS(PPUThread& CPU, ppu_opcode_t op);
void MTFSF(PPUThread& CPU, ppu_opcode_t op);
void FCMPU(PPUThread& CPU, ppu_opcode_t op);
void FRSP(PPUThread& CPU, ppu_opcode_t op);
void FCTIW(PPUThread& CPU, ppu_opcode_t op);
void FCTIWZ(PPUThread& CPU, ppu_opcode_t op);
void FDIV(PPUThread& CPU, ppu_opcode_t op);
void FSUB(PPUThread& CPU, ppu_opcode_t op);
void FADD(PPUThread& CPU, ppu_opcode_t op);
void FSQRT(PPUThread& CPU, ppu_opcode_t op);
void FSEL(PPUThread& CPU, ppu_opcode_t op);
void FMUL(PPUThread& CPU, ppu_opcode_t op);
void FRSQRTE(PPUThread& CPU, ppu_opcode_t op);
void FMSUB(PPUThread& CPU, ppu_opcode_t op);
void FMADD(PPUThread& CPU, ppu_opcode_t op);
void FNMSUB(PPUThread& CPU, ppu_opcode_t op);
void FNMADD(PPUThread& CPU, ppu_opcode_t op);
void FCMPO(PPUThread& CPU, ppu_opcode_t op);
void FNEG(PPUThread& CPU, ppu_opcode_t op);
void FMR(PPUThread& CPU, ppu_opcode_t op);
void FNABS(PPUThread& CPU, ppu_opcode_t op);
void FABS(PPUThread& CPU, ppu_opcode_t op);
void FCTID(PPUThread& CPU, ppu_opcode_t op);
void FCTIDZ(PPUThread& CPU, ppu_opcode_t op);
void FCFID(PPUThread& CPU, ppu_opcode_t op);
void UNK(PPUThread& CPU, ppu_opcode_t op);
}
class PPUInterpreter2 : public PPUOpcodes
{
public:
virtual ~PPUInterpreter2() {}
ppu_inter_func_t func;
virtual void NULL_OP() { func = ppu_interpreter::NULL_OP; }
virtual void NOP() { func = ppu_interpreter::NOP; }
virtual void TDI(u32 to, u32 ra, s32 simm16) { func = ppu_interpreter::TDI; }
virtual void TWI(u32 to, u32 ra, s32 simm16) { func = ppu_interpreter::TWI; }
virtual void MFVSCR(u32 vd) { func = ppu_interpreter::MFVSCR; }
virtual void MTVSCR(u32 vb) { func = ppu_interpreter::MTVSCR; }
virtual void VADDCUW(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VADDCUW; }
virtual void VADDFP(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VADDFP; }
virtual void VADDSBS(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VADDSBS; }
virtual void VADDSHS(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VADDSHS; }
virtual void VADDSWS(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VADDSWS; }
virtual void VADDUBM(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VADDUBM; }
virtual void VADDUBS(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VADDUBS; }
virtual void VADDUHM(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VADDUHM; }
virtual void VADDUHS(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VADDUHS; }
virtual void VADDUWM(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VADDUWM; }
virtual void VADDUWS(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VADDUWS; }
virtual void VAND(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VAND; }
virtual void VANDC(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VANDC; }
virtual void VAVGSB(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VAVGSB; }
virtual void VAVGSH(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VAVGSH; }
virtual void VAVGSW(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VAVGSW; }
virtual void VAVGUB(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VAVGUB; }
virtual void VAVGUH(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VAVGUH; }
virtual void VAVGUW(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VAVGUW; }
virtual void VCFSX(u32 vd, u32 uimm5, u32 vb) { func = ppu_interpreter::VCFSX; }
virtual void VCFUX(u32 vd, u32 uimm5, u32 vb) { func = ppu_interpreter::VCFUX; }
virtual void VCMPBFP(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VCMPBFP; }
virtual void VCMPBFP_(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VCMPBFP_; }
virtual void VCMPEQFP(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VCMPEQFP; }
virtual void VCMPEQFP_(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VCMPEQFP_; }
virtual void VCMPEQUB(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VCMPEQUB; }
virtual void VCMPEQUB_(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VCMPEQUB_; }
virtual void VCMPEQUH(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VCMPEQUH; }
virtual void VCMPEQUH_(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VCMPEQUH_; }
virtual void VCMPEQUW(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VCMPEQUW; }
virtual void VCMPEQUW_(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VCMPEQUW_; }
virtual void VCMPGEFP(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VCMPGEFP; }
virtual void VCMPGEFP_(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VCMPGEFP_; }
virtual void VCMPGTFP(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VCMPGTFP; }
virtual void VCMPGTFP_(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VCMPGTFP_; }
virtual void VCMPGTSB(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VCMPGTSB; }
virtual void VCMPGTSB_(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VCMPGTSB_; }
virtual void VCMPGTSH(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VCMPGTSH; }
virtual void VCMPGTSH_(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VCMPGTSH_; }
virtual void VCMPGTSW(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VCMPGTSW; }
virtual void VCMPGTSW_(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VCMPGTSW_; }
virtual void VCMPGTUB(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VCMPGTUB; }
virtual void VCMPGTUB_(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VCMPGTUB_; }
virtual void VCMPGTUH(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VCMPGTUH; }
virtual void VCMPGTUH_(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VCMPGTUH_; }
virtual void VCMPGTUW(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VCMPGTUW; }
virtual void VCMPGTUW_(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VCMPGTUW_; }
virtual void VCTSXS(u32 vd, u32 uimm5, u32 vb) { func = ppu_interpreter::VCTSXS; }
virtual void VCTUXS(u32 vd, u32 uimm5, u32 vb) { func = ppu_interpreter::VCTUXS; }
virtual void VEXPTEFP(u32 vd, u32 vb) { func = ppu_interpreter::VEXPTEFP; }
virtual void VLOGEFP(u32 vd, u32 vb) { func = ppu_interpreter::VLOGEFP; }
virtual void VMADDFP(u32 vd, u32 va, u32 vc, u32 vb) { func = ppu_interpreter::VMADDFP; }
virtual void VMAXFP(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VMAXFP; }
virtual void VMAXSB(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VMAXSB; }
virtual void VMAXSH(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VMAXSH; }
virtual void VMAXSW(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VMAXSW; }
virtual void VMAXUB(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VMAXUB; }
virtual void VMAXUH(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VMAXUH; }
virtual void VMAXUW(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VMAXUW; }
virtual void VMHADDSHS(u32 vd, u32 va, u32 vb, u32 vc) { func = ppu_interpreter::VMHADDSHS; }
virtual void VMHRADDSHS(u32 vd, u32 va, u32 vb, u32 vc) { func = ppu_interpreter::VMHRADDSHS; }
virtual void VMINFP(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VMINFP; }
virtual void VMINSB(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VMINSB; }
virtual void VMINSH(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VMINSH; }
virtual void VMINSW(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VMINSW; }
virtual void VMINUB(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VMINUB; }
virtual void VMINUH(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VMINUH; }
virtual void VMINUW(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VMINUW; }
virtual void VMLADDUHM(u32 vd, u32 va, u32 vb, u32 vc) { func = ppu_interpreter::VMLADDUHM; }
virtual void VMRGHB(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VMRGHB; }
virtual void VMRGHH(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VMRGHH; }
virtual void VMRGHW(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VMRGHW; }
virtual void VMRGLB(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VMRGLB; }
virtual void VMRGLH(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VMRGLH; }
virtual void VMRGLW(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VMRGLW; }
virtual void VMSUMMBM(u32 vd, u32 va, u32 vb, u32 vc) { func = ppu_interpreter::VMSUMMBM; }
virtual void VMSUMSHM(u32 vd, u32 va, u32 vb, u32 vc) { func = ppu_interpreter::VMSUMSHM; }
virtual void VMSUMSHS(u32 vd, u32 va, u32 vb, u32 vc) { func = ppu_interpreter::VMSUMSHS; }
virtual void VMSUMUBM(u32 vd, u32 va, u32 vb, u32 vc) { func = ppu_interpreter::VMSUMUBM; }
virtual void VMSUMUHM(u32 vd, u32 va, u32 vb, u32 vc) { func = ppu_interpreter::VMSUMUHM; }
virtual void VMSUMUHS(u32 vd, u32 va, u32 vb, u32 vc) { func = ppu_interpreter::VMSUMUHS; }
virtual void VMULESB(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VMULESB; }
virtual void VMULESH(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VMULESH; }
virtual void VMULEUB(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VMULEUB; }
virtual void VMULEUH(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VMULEUH; }
virtual void VMULOSB(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VMULOSB; }
virtual void VMULOSH(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VMULOSH; }
virtual void VMULOUB(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VMULOUB; }
virtual void VMULOUH(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VMULOUH; }
virtual void VNMSUBFP(u32 vd, u32 va, u32 vc, u32 vb) { func = ppu_interpreter::VNMSUBFP; }
virtual void VNOR(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VNOR; }
virtual void VOR(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VOR; }
virtual void VPERM(u32 vd, u32 va, u32 vb, u32 vc) { func = ppu_interpreter::VPERM; }
virtual void VPKPX(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VPKPX; }
virtual void VPKSHSS(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VPKSHSS; }
virtual void VPKSHUS(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VPKSHUS; }
virtual void VPKSWSS(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VPKSWSS; }
virtual void VPKSWUS(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VPKSWUS; }
virtual void VPKUHUM(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VPKUHUM; }
virtual void VPKUHUS(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VPKUHUS; }
virtual void VPKUWUM(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VPKUWUM; }
virtual void VPKUWUS(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VPKUWUS; }
virtual void VREFP(u32 vd, u32 vb) { func = ppu_interpreter::VREFP; }
virtual void VRFIM(u32 vd, u32 vb) { func = ppu_interpreter::VRFIM; }
virtual void VRFIN(u32 vd, u32 vb) { func = ppu_interpreter::VRFIN; }
virtual void VRFIP(u32 vd, u32 vb) { func = ppu_interpreter::VRFIP; }
virtual void VRFIZ(u32 vd, u32 vb) { func = ppu_interpreter::VRFIZ; }
virtual void VRLB(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VRLB; }
virtual void VRLH(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VRLH; }
virtual void VRLW(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VRLW; }
virtual void VRSQRTEFP(u32 vd, u32 vb) { func = ppu_interpreter::VRSQRTEFP; }
virtual void VSEL(u32 vd, u32 va, u32 vb, u32 vc) { func = ppu_interpreter::VSEL; }
virtual void VSL(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VSL; }
virtual void VSLB(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VSLB; }
virtual void VSLDOI(u32 vd, u32 va, u32 vb, u32 sh) { func = ppu_interpreter::VSLDOI; }
virtual void VSLH(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VSLH; }
virtual void VSLO(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VSLO; }
virtual void VSLW(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VSLW; }
virtual void VSPLTB(u32 vd, u32 uimm5, u32 vb) { func = ppu_interpreter::VSPLTB; }
virtual void VSPLTH(u32 vd, u32 uimm5, u32 vb) { func = ppu_interpreter::VSPLTH; }
virtual void VSPLTISB(u32 vd, s32 simm5) { func = ppu_interpreter::VSPLTISB; }
virtual void VSPLTISH(u32 vd, s32 simm5) { func = ppu_interpreter::VSPLTISH; }
virtual void VSPLTISW(u32 vd, s32 simm5) { func = ppu_interpreter::VSPLTISW; }
virtual void VSPLTW(u32 vd, u32 uimm5, u32 vb) { func = ppu_interpreter::VSPLTW; }
virtual void VSR(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VSR; }
virtual void VSRAB(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VSRAB; }
virtual void VSRAH(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VSRAH; }
virtual void VSRAW(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VSRAW; }
virtual void VSRB(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VSRB; }
virtual void VSRH(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VSRH; }
virtual void VSRO(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VSRO; }
virtual void VSRW(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VSRW; }
virtual void VSUBCUW(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VSUBCUW; }
virtual void VSUBFP(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VSUBFP; }
virtual void VSUBSBS(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VSUBSBS; }
virtual void VSUBSHS(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VSUBSHS; }
virtual void VSUBSWS(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VSUBSWS; }
virtual void VSUBUBM(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VSUBUBM; }
virtual void VSUBUBS(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VSUBUBS; }
virtual void VSUBUHM(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VSUBUHM; }
virtual void VSUBUHS(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VSUBUHS; }
virtual void VSUBUWM(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VSUBUWM; }
virtual void VSUBUWS(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VSUBUWS; }
virtual void VSUMSWS(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VSUMSWS; }
virtual void VSUM2SWS(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VSUM2SWS; }
virtual void VSUM4SBS(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VSUM4SBS; }
virtual void VSUM4SHS(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VSUM4SHS; }
virtual void VSUM4UBS(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VSUM4UBS; }
virtual void VUPKHPX(u32 vd, u32 vb) { func = ppu_interpreter::VUPKHPX; }
virtual void VUPKHSB(u32 vd, u32 vb) { func = ppu_interpreter::VUPKHSB; }
virtual void VUPKHSH(u32 vd, u32 vb) { func = ppu_interpreter::VUPKHSH; }
virtual void VUPKLPX(u32 vd, u32 vb) { func = ppu_interpreter::VUPKLPX; }
virtual void VUPKLSB(u32 vd, u32 vb) { func = ppu_interpreter::VUPKLSB; }
virtual void VUPKLSH(u32 vd, u32 vb) { func = ppu_interpreter::VUPKLSH; }
virtual void VXOR(u32 vd, u32 va, u32 vb) { func = ppu_interpreter::VXOR; }
virtual void MULLI(u32 rd, u32 ra, s32 simm16) { func = ppu_interpreter::MULLI; }
virtual void SUBFIC(u32 rd, u32 ra, s32 simm16) { func = ppu_interpreter::SUBFIC; }
virtual void CMPLI(u32 bf, u32 l, u32 ra, u32 uimm16) { func = ppu_interpreter::CMPLI; }
virtual void CMPI(u32 bf, u32 l, u32 ra, s32 simm16) { func = ppu_interpreter::CMPI; }
virtual void ADDIC(u32 rd, u32 ra, s32 simm16) { func = ppu_interpreter::ADDIC; }
virtual void ADDIC_(u32 rd, u32 ra, s32 simm16) { func = ppu_interpreter::ADDIC_; }
virtual void ADDI(u32 rd, u32 ra, s32 simm16) { func = ppu_interpreter::ADDI; }
virtual void ADDIS(u32 rd, u32 ra, s32 simm16) { func = ppu_interpreter::ADDIS; }
virtual void BC(u32 bo, u32 bi, s32 bd, u32 aa, u32 lk) { func = ppu_interpreter::BC; }
virtual void HACK(u32 index) { func = ppu_interpreter::HACK; }
virtual void SC(u32 lev) { func = ppu_interpreter::SC; }
virtual void B(s32 ll, u32 aa, u32 lk) { func = ppu_interpreter::B; }
virtual void MCRF(u32 crfd, u32 crfs) { func = ppu_interpreter::MCRF; }
virtual void BCLR(u32 bo, u32 bi, u32 bh, u32 lk) { func = ppu_interpreter::BCLR; }
virtual void CRNOR(u32 bt, u32 ba, u32 bb) { func = ppu_interpreter::CRNOR; }
virtual void CRANDC(u32 bt, u32 ba, u32 bb) { func = ppu_interpreter::CRANDC; }
virtual void ISYNC() { func = ppu_interpreter::ISYNC; }
virtual void CRXOR(u32 bt, u32 ba, u32 bb) { func = ppu_interpreter::CRXOR; }
virtual void CRNAND(u32 bt, u32 ba, u32 bb) { func = ppu_interpreter::CRNAND; }
virtual void CRAND(u32 bt, u32 ba, u32 bb) { func = ppu_interpreter::CRAND; }
virtual void CREQV(u32 bt, u32 ba, u32 bb) { func = ppu_interpreter::CREQV; }
virtual void CRORC(u32 bt, u32 ba, u32 bb) { func = ppu_interpreter::CRORC; }
virtual void CROR(u32 bt, u32 ba, u32 bb) { func = ppu_interpreter::CROR; }
virtual void BCCTR(u32 bo, u32 bi, u32 bh, u32 lk) { func = ppu_interpreter::BCCTR; }
virtual void RLWIMI(u32 ra, u32 rs, u32 sh, u32 mb, u32 me, u32 rc) { func = ppu_interpreter::RLWIMI; }
virtual void RLWINM(u32 ra, u32 rs, u32 sh, u32 mb, u32 me, u32 rc) { func = ppu_interpreter::RLWINM; }
virtual void RLWNM(u32 ra, u32 rs, u32 rb, u32 MB, u32 ME, u32 rc) { func = ppu_interpreter::RLWNM; }
virtual void ORI(u32 rs, u32 ra, u32 uimm16) { func = ppu_interpreter::ORI; }
virtual void ORIS(u32 rs, u32 ra, u32 uimm16) { func = ppu_interpreter::ORIS; }
virtual void XORI(u32 ra, u32 rs, u32 uimm16) { func = ppu_interpreter::XORI; }
virtual void XORIS(u32 ra, u32 rs, u32 uimm16) { func = ppu_interpreter::XORIS; }
virtual void ANDI_(u32 ra, u32 rs, u32 uimm16) { func = ppu_interpreter::ANDI_; }
virtual void ANDIS_(u32 ra, u32 rs, u32 uimm16) { func = ppu_interpreter::ANDIS_; }
virtual void RLDICL(u32 ra, u32 rs, u32 sh, u32 mb, u32 rc) { func = ppu_interpreter::RLDICL; }
virtual void RLDICR(u32 ra, u32 rs, u32 sh, u32 me, u32 rc) { func = ppu_interpreter::RLDICR; }
virtual void RLDIC(u32 ra, u32 rs, u32 sh, u32 mb, u32 rc) { func = ppu_interpreter::RLDIC; }
virtual void RLDIMI(u32 ra, u32 rs, u32 sh, u32 mb, u32 rc) { func = ppu_interpreter::RLDIMI; }
virtual void RLDC_LR(u32 ra, u32 rs, u32 rb, u32 m_eb, u32 is_r, u32 rc) { func = ppu_interpreter::RLDC_LR; }
virtual void CMP(u32 crfd, u32 l, u32 ra, u32 rb) { func = ppu_interpreter::CMP; }
virtual void TW(u32 to, u32 ra, u32 rb) { func = ppu_interpreter::TW; }
virtual void LVSL(u32 vd, u32 ra, u32 rb) { func = ppu_interpreter::LVSL; }
virtual void LVEBX(u32 vd, u32 ra, u32 rb) { func = ppu_interpreter::LVEBX; }
virtual void SUBFC(u32 rd, u32 ra, u32 rb, u32 oe, u32 rc) { func = ppu_interpreter::SUBFC; }
virtual void MULHDU(u32 rd, u32 ra, u32 rb, u32 rc) { func = ppu_interpreter::MULHDU; }
virtual void ADDC(u32 rd, u32 ra, u32 rb, u32 oe, u32 rc) { func = ppu_interpreter::ADDC; }
virtual void MULHWU(u32 rd, u32 ra, u32 rb, u32 rc) { func = ppu_interpreter::MULHWU; }
virtual void MFOCRF(u32 a, u32 rd, u32 crm) { func = ppu_interpreter::MFOCRF; }
virtual void LWARX(u32 rd, u32 ra, u32 rb) { func = ppu_interpreter::LWARX; }
virtual void LDX(u32 ra, u32 rs, u32 rb) { func = ppu_interpreter::LDX; }
virtual void LWZX(u32 rd, u32 ra, u32 rb) { func = ppu_interpreter::LWZX; }
virtual void SLW(u32 ra, u32 rs, u32 rb, u32 rc) { func = ppu_interpreter::SLW; }
virtual void CNTLZW(u32 ra, u32 rs, u32 rc) { func = ppu_interpreter::CNTLZW; }
virtual void SLD(u32 ra, u32 rs, u32 rb, u32 rc) { func = ppu_interpreter::SLD; }
virtual void AND(u32 ra, u32 rs, u32 rb, u32 rc) { func = ppu_interpreter::AND; }
virtual void CMPL(u32 bf, u32 l, u32 ra, u32 rb) { func = ppu_interpreter::CMPL; }
virtual void LVSR(u32 vd, u32 ra, u32 rb) { func = ppu_interpreter::LVSR; }
virtual void LVEHX(u32 vd, u32 ra, u32 rb) { func = ppu_interpreter::LVEHX; }
virtual void SUBF(u32 rd, u32 ra, u32 rb, u32 oe, u32 rc) { func = ppu_interpreter::SUBF; }
virtual void LDUX(u32 rd, u32 ra, u32 rb) { func = ppu_interpreter::LDUX; }
virtual void DCBST(u32 ra, u32 rb) { func = ppu_interpreter::DCBST; }
virtual void LWZUX(u32 rd, u32 ra, u32 rb) { func = ppu_interpreter::LWZUX; }
virtual void CNTLZD(u32 ra, u32 rs, u32 rc) { func = ppu_interpreter::CNTLZD; }
virtual void ANDC(u32 ra, u32 rs, u32 rb, u32 rc) { func = ppu_interpreter::ANDC; }
virtual void TD(u32 to, u32 ra, u32 rb) { func = ppu_interpreter::TD; }
virtual void LVEWX(u32 vd, u32 ra, u32 rb) { func = ppu_interpreter::LVEWX; }
virtual void MULHD(u32 rd, u32 ra, u32 rb, u32 rc) { func = ppu_interpreter::MULHD; }
virtual void MULHW(u32 rd, u32 ra, u32 rb, u32 rc) { func = ppu_interpreter::MULHW; }
virtual void LDARX(u32 rd, u32 ra, u32 rb) { func = ppu_interpreter::LDARX; }
virtual void DCBF(u32 ra, u32 rb) { func = ppu_interpreter::DCBF; }
virtual void LBZX(u32 rd, u32 ra, u32 rb) { func = ppu_interpreter::LBZX; }
virtual void LVX(u32 vd, u32 ra, u32 rb) { func = ppu_interpreter::LVX; }
virtual void NEG(u32 rd, u32 ra, u32 oe, u32 rc) { func = ppu_interpreter::NEG; }
virtual void LBZUX(u32 rd, u32 ra, u32 rb) { func = ppu_interpreter::LBZUX; }
virtual void NOR(u32 ra, u32 rs, u32 rb, u32 rc) { func = ppu_interpreter::NOR; }
virtual void STVEBX(u32 vs, u32 ra, u32 rb) { func = ppu_interpreter::STVEBX; }
virtual void SUBFE(u32 rd, u32 ra, u32 rb, u32 oe, u32 rc) { func = ppu_interpreter::SUBFE; }
virtual void ADDE(u32 rd, u32 ra, u32 rb, u32 oe, u32 rc) { func = ppu_interpreter::ADDE; }
virtual void MTOCRF(u32 l, u32 crm, u32 rs) { func = ppu_interpreter::MTOCRF; }
virtual void STDX(u32 rs, u32 ra, u32 rb) { func = ppu_interpreter::STDX; }
virtual void STWCX_(u32 rs, u32 ra, u32 rb) { func = ppu_interpreter::STWCX_; }
virtual void STWX(u32 rs, u32 ra, u32 rb) { func = ppu_interpreter::STWX; }
virtual void STVEHX(u32 vs, u32 ra, u32 rb) { func = ppu_interpreter::STVEHX; }
virtual void STDUX(u32 rs, u32 ra, u32 rb) { func = ppu_interpreter::STDUX; }
virtual void STWUX(u32 rs, u32 ra, u32 rb) { func = ppu_interpreter::STWUX; }
virtual void STVEWX(u32 vs, u32 ra, u32 rb) { func = ppu_interpreter::STVEWX; }
virtual void SUBFZE(u32 rd, u32 ra, u32 oe, u32 rc) { func = ppu_interpreter::SUBFZE; }
virtual void ADDZE(u32 rd, u32 ra, u32 oe, u32 rc) { func = ppu_interpreter::ADDZE; }
virtual void STDCX_(u32 rs, u32 ra, u32 rb) { func = ppu_interpreter::STDCX_; }
virtual void STBX(u32 rs, u32 ra, u32 rb) { func = ppu_interpreter::STBX; }
virtual void STVX(u32 vs, u32 ra, u32 rb) { func = ppu_interpreter::STVX; }
virtual void MULLD(u32 rd, u32 ra, u32 rb, u32 oe, u32 rc) { func = ppu_interpreter::MULLD; }
virtual void SUBFME(u32 rd, u32 ra, u32 oe, u32 rc) { func = ppu_interpreter::SUBFME; }
virtual void ADDME(u32 rd, u32 ra, u32 oe, u32 rc) { func = ppu_interpreter::ADDME; }
virtual void MULLW(u32 rd, u32 ra, u32 rb, u32 oe, u32 rc) { func = ppu_interpreter::MULLW; }
virtual void DCBTST(u32 ra, u32 rb, u32 th) { func = ppu_interpreter::DCBTST; }
virtual void STBUX(u32 rs, u32 ra, u32 rb) { func = ppu_interpreter::STBUX; }
virtual void ADD(u32 rd, u32 ra, u32 rb, u32 oe, u32 rc) { func = ppu_interpreter::ADD; }
virtual void DCBT(u32 ra, u32 rb, u32 th) { func = ppu_interpreter::DCBT; }
virtual void LHZX(u32 rd, u32 ra, u32 rb) { func = ppu_interpreter::LHZX; }
virtual void EQV(u32 ra, u32 rs, u32 rb, u32 rc) { func = ppu_interpreter::EQV; }
virtual void ECIWX(u32 rd, u32 ra, u32 rb) { func = ppu_interpreter::ECIWX; }
virtual void LHZUX(u32 rd, u32 ra, u32 rb) { func = ppu_interpreter::LHZUX; }
virtual void XOR(u32 rs, u32 ra, u32 rb, u32 rc) { func = ppu_interpreter::XOR; }
virtual void MFSPR(u32 rd, u32 spr) { func = ppu_interpreter::MFSPR; }
virtual void LWAX(u32 rd, u32 ra, u32 rb) { func = ppu_interpreter::LWAX; }
virtual void DST(u32 ra, u32 rb, u32 strm, u32 t) { func = ppu_interpreter::DST; }
virtual void LHAX(u32 rd, u32 ra, u32 rb) { func = ppu_interpreter::LHAX; }
virtual void LVXL(u32 vd, u32 ra, u32 rb) { func = ppu_interpreter::LVXL; }
virtual void MFTB(u32 rd, u32 spr) { func = ppu_interpreter::MFTB; }
virtual void LWAUX(u32 rd, u32 ra, u32 rb) { func = ppu_interpreter::LWAUX; }
virtual void DSTST(u32 ra, u32 rb, u32 strm, u32 t) { func = ppu_interpreter::DSTST; }
virtual void LHAUX(u32 rd, u32 ra, u32 rb) { func = ppu_interpreter::LHAUX; }
virtual void STHX(u32 rs, u32 ra, u32 rb) { func = ppu_interpreter::STHX; }
virtual void ORC(u32 rs, u32 ra, u32 rb, u32 rc) { func = ppu_interpreter::ORC; }
virtual void ECOWX(u32 rs, u32 ra, u32 rb) { func = ppu_interpreter::ECOWX; }
virtual void STHUX(u32 rs, u32 ra, u32 rb) { func = ppu_interpreter::STHUX; }
virtual void OR(u32 ra, u32 rs, u32 rb, u32 rc) { func = ppu_interpreter::OR; }
virtual void DIVDU(u32 rd, u32 ra, u32 rb, u32 oe, u32 rc) { func = ppu_interpreter::DIVDU; }
virtual void DIVWU(u32 rd, u32 ra, u32 rb, u32 oe, u32 rc) { func = ppu_interpreter::DIVWU; }
virtual void MTSPR(u32 spr, u32 rs) { func = ppu_interpreter::MTSPR; }
virtual void DCBI(u32 ra, u32 rb) { func = ppu_interpreter::DCBI; }
virtual void NAND(u32 ra, u32 rs, u32 rb, u32 rc) { func = ppu_interpreter::NAND; }
virtual void STVXL(u32 vs, u32 ra, u32 rb) { func = ppu_interpreter::STVXL; }
virtual void DIVD(u32 rd, u32 ra, u32 rb, u32 oe, u32 rc) { func = ppu_interpreter::DIVD; }
virtual void DIVW(u32 rd, u32 ra, u32 rb, u32 oe, u32 rc) { func = ppu_interpreter::DIVW; }
virtual void LVLX(u32 vd, u32 ra, u32 rb) { func = ppu_interpreter::LVLX; }
virtual void LDBRX(u32 rd, u32 ra, u32 rb) { func = ppu_interpreter::LDBRX; }
virtual void LSWX(u32 rd, u32 ra, u32 rb) { func = ppu_interpreter::LSWX; }
virtual void LWBRX(u32 rd, u32 ra, u32 rb) { func = ppu_interpreter::LWBRX; }
virtual void LFSX(u32 frd, u32 ra, u32 rb) { func = ppu_interpreter::LFSX; }
virtual void SRW(u32 ra, u32 rs, u32 rb, u32 rc) { func = ppu_interpreter::SRW; }
virtual void SRD(u32 ra, u32 rs, u32 rb, u32 rc) { func = ppu_interpreter::SRD; }
virtual void LVRX(u32 vd, u32 ra, u32 rb) { func = ppu_interpreter::LVRX; }
virtual void LSWI(u32 rd, u32 ra, u32 nb) { func = ppu_interpreter::LSWI; }
virtual void LFSUX(u32 frd, u32 ra, u32 rb) { func = ppu_interpreter::LFSUX; }
virtual void SYNC(u32 l) { func = ppu_interpreter::SYNC; }
virtual void LFDX(u32 frd, u32 ra, u32 rb) { func = ppu_interpreter::LFDX; }
virtual void LFDUX(u32 frd, u32 ra, u32 rb) { func = ppu_interpreter::LFDUX; }
virtual void STVLX(u32 vs, u32 ra, u32 rb) { func = ppu_interpreter::STVLX; }
virtual void STDBRX(u32 rs, u32 ra, u32 rb) { func = ppu_interpreter::STDBRX; }
virtual void STSWX(u32 rs, u32 ra, u32 rb) { func = ppu_interpreter::STSWX; }
virtual void STWBRX(u32 rs, u32 ra, u32 rb) { func = ppu_interpreter::STWBRX; }
virtual void STFSX(u32 frs, u32 ra, u32 rb) { func = ppu_interpreter::STFSX; }
virtual void STVRX(u32 vs, u32 ra, u32 rb) { func = ppu_interpreter::STVRX; }
virtual void STFSUX(u32 frs, u32 ra, u32 rb) { func = ppu_interpreter::STFSUX; }
virtual void STSWI(u32 rd, u32 ra, u32 nb) { func = ppu_interpreter::STSWI; }
virtual void STFDX(u32 frs, u32 ra, u32 rb) { func = ppu_interpreter::STFDX; }
virtual void STFDUX(u32 frs, u32 ra, u32 rb) { func = ppu_interpreter::STFDUX; }
virtual void LVLXL(u32 vd, u32 ra, u32 rb) { func = ppu_interpreter::LVLXL; }
virtual void LHBRX(u32 rd, u32 ra, u32 rb) { func = ppu_interpreter::LHBRX; }
virtual void SRAW(u32 ra, u32 rs, u32 rb, u32 rc) { func = ppu_interpreter::SRAW; }
virtual void SRAD(u32 ra, u32 rs, u32 rb, u32 rc) { func = ppu_interpreter::SRAD; }
virtual void LVRXL(u32 vd, u32 ra, u32 rb) { func = ppu_interpreter::LVRXL; }
virtual void DSS(u32 strm, u32 a) { func = ppu_interpreter::DSS; }
virtual void SRAWI(u32 ra, u32 rs, u32 sh, u32 rc) { func = ppu_interpreter::SRAWI; }
virtual void SRADI1(u32 ra, u32 rs, u32 sh, u32 rc) { func = ppu_interpreter::SRADI; }
virtual void SRADI2(u32 ra, u32 rs, u32 sh, u32 rc) { func = ppu_interpreter::SRADI; }
virtual void EIEIO() { func = ppu_interpreter::EIEIO; }
virtual void STVLXL(u32 vs, u32 ra, u32 rb) { func = ppu_interpreter::STVLXL; }
virtual void STHBRX(u32 rs, u32 ra, u32 rb) { func = ppu_interpreter::STHBRX; }
virtual void EXTSH(u32 ra, u32 rs, u32 rc) { func = ppu_interpreter::EXTSH; }
virtual void STVRXL(u32 sd, u32 ra, u32 rb) { func = ppu_interpreter::STVRXL; }
virtual void EXTSB(u32 ra, u32 rs, u32 rc) { func = ppu_interpreter::EXTSB; }
virtual void STFIWX(u32 frs, u32 ra, u32 rb) { func = ppu_interpreter::STFIWX; }
virtual void EXTSW(u32 ra, u32 rs, u32 rc) { func = ppu_interpreter::EXTSW; }
virtual void ICBI(u32 ra, u32 rb) { func = ppu_interpreter::ICBI; }
virtual void DCBZ(u32 ra, u32 rb) { func = ppu_interpreter::DCBZ; }
virtual void LWZ(u32 rd, u32 ra, s32 d) { func = ppu_interpreter::LWZ; }
virtual void LWZU(u32 rd, u32 ra, s32 d) { func = ppu_interpreter::LWZU; }
virtual void LBZ(u32 rd, u32 ra, s32 d) { func = ppu_interpreter::LBZ; }
virtual void LBZU(u32 rd, u32 ra, s32 d) { func = ppu_interpreter::LBZU; }
virtual void STW(u32 rs, u32 ra, s32 d) { func = ppu_interpreter::STW; }
virtual void STWU(u32 rs, u32 ra, s32 d) { func = ppu_interpreter::STWU; }
virtual void STB(u32 rs, u32 ra, s32 d) { func = ppu_interpreter::STB; }
virtual void STBU(u32 rs, u32 ra, s32 d) { func = ppu_interpreter::STBU; }
virtual void LHZ(u32 rd, u32 ra, s32 d) { func = ppu_interpreter::LHZ; }
virtual void LHZU(u32 rd, u32 ra, s32 d) { func = ppu_interpreter::LHZU; }
virtual void LHA(u32 rs, u32 ra, s32 d) { func = ppu_interpreter::LHA; }
virtual void LHAU(u32 rs, u32 ra, s32 d) { func = ppu_interpreter::LHAU; }
virtual void STH(u32 rs, u32 ra, s32 d) { func = ppu_interpreter::STH; }
virtual void STHU(u32 rs, u32 ra, s32 d) { func = ppu_interpreter::STHU; }
virtual void LMW(u32 rd, u32 ra, s32 d) { func = ppu_interpreter::LMW; }
virtual void STMW(u32 rs, u32 ra, s32 d) { func = ppu_interpreter::STMW; }
virtual void LFS(u32 frd, u32 ra, s32 d) { func = ppu_interpreter::LFS; }
virtual void LFSU(u32 frd, u32 ra, s32 d) { func = ppu_interpreter::LFSU; }
virtual void LFD(u32 frd, u32 ra, s32 d) { func = ppu_interpreter::LFD; }
virtual void LFDU(u32 frd, u32 ra, s32 d) { func = ppu_interpreter::LFDU; }
virtual void STFS(u32 frs, u32 ra, s32 d) { func = ppu_interpreter::STFS; }
virtual void STFSU(u32 frs, u32 ra, s32 d) { func = ppu_interpreter::STFSU; }
virtual void STFD(u32 frs, u32 ra, s32 d) { func = ppu_interpreter::STFD; }
virtual void STFDU(u32 frs, u32 ra, s32 d) { func = ppu_interpreter::STFDU; }
virtual void LD(u32 rd, u32 ra, s32 ds) { func = ppu_interpreter::LD; }
virtual void LDU(u32 rd, u32 ra, s32 ds) { func = ppu_interpreter::LDU; }
virtual void LWA(u32 rd, u32 ra, s32 ds) { func = ppu_interpreter::LWA; }
virtual void FDIVS(u32 frd, u32 fra, u32 frb, u32 rc) { func = ppu_interpreter::FDIVS; }
virtual void FSUBS(u32 frd, u32 fra, u32 frb, u32 rc) { func = ppu_interpreter::FSUBS; }
virtual void FADDS(u32 frd, u32 fra, u32 frb, u32 rc) { func = ppu_interpreter::FADDS; }
virtual void FSQRTS(u32 frd, u32 frb, u32 rc) { func = ppu_interpreter::FSQRTS; }
virtual void FRES(u32 frd, u32 frb, u32 rc) { func = ppu_interpreter::FRES; }
virtual void FMULS(u32 frd, u32 fra, u32 frc, u32 rc) { func = ppu_interpreter::FMULS; }
virtual void FMADDS(u32 frd, u32 fra, u32 frc, u32 frb, u32 rc) { func = ppu_interpreter::FMADDS; }
virtual void FMSUBS(u32 frd, u32 fra, u32 frc, u32 frb, u32 rc) { func = ppu_interpreter::FMSUBS; }
virtual void FNMSUBS(u32 frd, u32 fra, u32 frc, u32 frb, u32 rc) { func = ppu_interpreter::FNMSUBS; }
virtual void FNMADDS(u32 frd, u32 fra, u32 frc, u32 frb, u32 rc) { func = ppu_interpreter::FNMADDS; }
virtual void STD(u32 rs, u32 ra, s32 ds) { func = ppu_interpreter::STD; }
virtual void STDU(u32 rs, u32 ra, s32 ds) { func = ppu_interpreter::STDU; }
virtual void MTFSB1(u32 bt, u32 rc) { func = ppu_interpreter::MTFSB1; }
virtual void MCRFS(u32 bf, u32 bfa) { func = ppu_interpreter::MCRFS; }
virtual void MTFSB0(u32 bt, u32 rc) { func = ppu_interpreter::MTFSB0; }
virtual void MTFSFI(u32 crfd, u32 i, u32 rc) { func = ppu_interpreter::MTFSFI; }
virtual void MFFS(u32 frd, u32 rc) { func = ppu_interpreter::MFFS; }
virtual void MTFSF(u32 flm, u32 frb, u32 rc) { func = ppu_interpreter::MTFSF; }
virtual void FCMPU(u32 bf, u32 fra, u32 frb) { func = ppu_interpreter::FCMPU; }
virtual void FRSP(u32 frd, u32 frb, u32 rc) { func = ppu_interpreter::FRSP; }
virtual void FCTIW(u32 frd, u32 frb, u32 rc) { func = ppu_interpreter::FCTIW; }
virtual void FCTIWZ(u32 frd, u32 frb, u32 rc) { func = ppu_interpreter::FCTIWZ; }
virtual void FDIV(u32 frd, u32 fra, u32 frb, u32 rc) { func = ppu_interpreter::FDIV; }
virtual void FSUB(u32 frd, u32 fra, u32 frb, u32 rc) { func = ppu_interpreter::FSUB; }
virtual void FADD(u32 frd, u32 fra, u32 frb, u32 rc) { func = ppu_interpreter::FADD; }
virtual void FSQRT(u32 frd, u32 frb, u32 rc) { func = ppu_interpreter::FSQRT; }
virtual void FSEL(u32 frd, u32 fra, u32 frc, u32 frb, u32 rc) { func = ppu_interpreter::FSEL; }
virtual void FMUL(u32 frd, u32 fra, u32 frc, u32 rc) { func = ppu_interpreter::FMUL; }
virtual void FRSQRTE(u32 frd, u32 frb, u32 rc) { func = ppu_interpreter::FRSQRTE; }
virtual void FMSUB(u32 frd, u32 fra, u32 frc, u32 frb, u32 rc) { func = ppu_interpreter::FMSUB; }
virtual void FMADD(u32 frd, u32 fra, u32 frc, u32 frb, u32 rc) { func = ppu_interpreter::FMADD; }
virtual void FNMSUB(u32 frd, u32 fra, u32 frc, u32 frb, u32 rc) { func = ppu_interpreter::FNMSUB; }
virtual void FNMADD(u32 frd, u32 fra, u32 frc, u32 frb, u32 rc) { func = ppu_interpreter::FNMADD; }
virtual void FCMPO(u32 crfd, u32 fra, u32 frb) { func = ppu_interpreter::FCMPO; }
virtual void FNEG(u32 frd, u32 frb, u32 rc) { func = ppu_interpreter::FNEG; }
virtual void FMR(u32 frd, u32 frb, u32 rc) { func = ppu_interpreter::FMR; }
virtual void FNABS(u32 frd, u32 frb, u32 rc) { func = ppu_interpreter::FNABS; }
virtual void FABS(u32 frd, u32 frb, u32 rc) { func = ppu_interpreter::FABS; }
virtual void FCTID(u32 frd, u32 frb, u32 rc) { func = ppu_interpreter::FCTID; }
virtual void FCTIDZ(u32 frd, u32 frb, u32 rc) { func = ppu_interpreter::FCTIDZ; }
virtual void FCFID(u32 frd, u32 frb, u32 rc) { func = ppu_interpreter::FCFID; }
virtual void UNK(const u32 code, const u32 opcode, const u32 gcode) { func = ppu_interpreter::UNK; }
};

1152
rpcs3/Emu/Cell/PPUModule.cpp Normal file

File diff suppressed because it is too large Load Diff

255
rpcs3/Emu/Cell/PPUModule.h Normal file
View File

@ -0,0 +1,255 @@
#pragma once
#include "Utilities/Config.h"
#include "PPUFunction.h"
#include "PPUCallback.h"
#include "ErrorCodes.h"
namespace vm { using namespace ps3; }
// Generate FNID or VNID for given name
extern u32 ppu_generate_id(const char* name);
// Flags set with REG_FUNC
enum ppu_static_function_flags : u32
{
MFF_FORCED_HLE = (1 << 0), // Always call HLE function (TODO: deactivated)
MFF_PERFECT = MFF_FORCED_HLE, // Indicates that function is completely implemented and can replace LLE implementation
};
// HLE function information
struct ppu_static_function
{
const char* name;
u32 index; // Index for ppu_function_manager
u32 flags;
};
// HLE variable information
struct ppu_static_variable
{
const char* name;
vm::gvar<void>* var; // Pointer to variable address storage
void(*init)(); // Variable initialization function
u32 size;
u32 align;
};
// HLE module information
class ppu_static_module final
{
public:
const std::string name;
task_stack on_load;
task_stack on_unload;
std::unordered_map<u32, ppu_static_function> functions;
std::unordered_map<u32, ppu_static_variable> variables;
public:
ppu_static_module(const char* name);
ppu_static_module(const char* name, void(*init)())
: ppu_static_module(name)
{
init();
}
ppu_static_module(const char* name, void(*init)(ppu_static_module* _this))
: ppu_static_module(name)
{
init(this);
}
};
class ppu_module_manager final
{
friend class ppu_static_module;
static never_inline auto& access()
{
static std::unordered_map<std::string, ppu_static_module*> map;
return map;
}
static never_inline void register_module(ppu_static_module* module)
{
access().emplace(module->name, module);
}
static never_inline auto& access_static_function(const char* module, u32 fnid)
{
return access().at(module)->functions[fnid];
}
static never_inline auto& access_static_variable(const char* module, u32 vnid)
{
return access().at(module)->variables[vnid];
}
public:
static never_inline const ppu_static_module* get_module(const std::string& name)
{
const auto& map = access();
const auto found = map.find(name);
return found != map.end() ? found->second : nullptr;
}
template<typename T, T Func>
static void register_static_function(const char* module, const char* name, ppu_function_t func, u32 fnid, u32 flags)
{
auto& info = access_static_function(module, fnid);
info.name = name;
info.index = ppu_function_manager::register_function<T, Func>(func);
info.flags = flags;
}
template<typename T, T* Var>
static void register_static_variable(const char* module, const char* name, u32 vnid, void(*init)())
{
static_assert(std::is_same<CV u32, CV typename T::addr_type>::value, "Static variable registration: vm::gvar<T> expected");
auto& info = access_static_variable(module, vnid);
info.name = name;
info.var = reinterpret_cast<vm::gvar<void>*>(Var);
info.init = init ? init : [] {};
info.size = SIZE_32(typename T::type);
info.align = ALIGN_32(typename T::type);
}
static const ppu_static_module cellAdec;
static const ppu_static_module cellAtrac;
static const ppu_static_module cellAtracMulti;
static const ppu_static_module cellAudio;
static const ppu_static_module cellAvconfExt;
static const ppu_static_module cellBGDL;
static const ppu_static_module cellCamera;
static const ppu_static_module cellCelp8Enc;
static const ppu_static_module cellCelpEnc;
static const ppu_static_module cellDaisy;
static const ppu_static_module cellDmux;
static const ppu_static_module cellFiber;
static const ppu_static_module cellFont;
static const ppu_static_module cellFontFT;
static const ppu_static_module cellFs;
static const ppu_static_module cellGame;
static const ppu_static_module cellGameExec;
static const ppu_static_module cellGcmSys;
static const ppu_static_module cellGem;
static const ppu_static_module cellGifDec;
static const ppu_static_module cellHttp;
static const ppu_static_module cellHttps;
static const ppu_static_module cellHttpUtil;
static const ppu_static_module cellImeJp;
static const ppu_static_module cellJpgDec;
static const ppu_static_module cellJpgEnc;
static const ppu_static_module cellKey2char;
static const ppu_static_module cellL10n;
static const ppu_static_module cellMic;
static const ppu_static_module cellMusic;
static const ppu_static_module cellMusicDecode;
static const ppu_static_module cellMusicExport;
static const ppu_static_module cellNetCtl;
static const ppu_static_module cellOskDialog;
static const ppu_static_module cellOvis;
static const ppu_static_module cellPamf;
static const ppu_static_module cellPhotoDecode;
static const ppu_static_module cellPhotoExport;
static const ppu_static_module cellPhotoImportUtil;
static const ppu_static_module cellPngDec;
static const ppu_static_module cellPngEnc;
static const ppu_static_module cellPrint;
static const ppu_static_module cellRec;
static const ppu_static_module cellRemotePlay;
static const ppu_static_module cellResc;
static const ppu_static_module cellRtc;
static const ppu_static_module cellRudp;
static const ppu_static_module cellSail;
static const ppu_static_module cellSailRec;
static const ppu_static_module cellSaveData;
static const ppu_static_module cellMinisSaveData;
static const ppu_static_module cellScreenShot;
static const ppu_static_module cellSearch;
static const ppu_static_module cellSheap;
static const ppu_static_module cellSpudll;
static const ppu_static_module cellSpurs;
static const ppu_static_module cellSpursJq;
static const ppu_static_module cellSsl;
static const ppu_static_module cellSubdisplay;
static const ppu_static_module cellSync;
static const ppu_static_module cellSync2;
static const ppu_static_module cellSysconf;
static const ppu_static_module cellSysmodule;
static const ppu_static_module cellSysutil;
static const ppu_static_module cellSysutilAp;
static const ppu_static_module cellSysutilAvc;
static const ppu_static_module cellSysutilAvc2;
static const ppu_static_module cellSysutilMisc;
static const ppu_static_module cellUsbd;
static const ppu_static_module cellUsbPspcm;
static const ppu_static_module cellUserInfo;
static const ppu_static_module cellVdec;
static const ppu_static_module cellVideoExport;
static const ppu_static_module cellVideoUpload;
static const ppu_static_module cellVoice;
static const ppu_static_module cellVpost;
static const ppu_static_module libmixer;
static const ppu_static_module libsnd3;
static const ppu_static_module libsynth2;
static const ppu_static_module sceNp;
static const ppu_static_module sceNp2;
static const ppu_static_module sceNpClans;
static const ppu_static_module sceNpCommerce2;
static const ppu_static_module sceNpSns;
static const ppu_static_module sceNpTrophy;
static const ppu_static_module sceNpTus;
static const ppu_static_module sceNpUtil;
static const ppu_static_module sys_io;
static const ppu_static_module libnet;
static const ppu_static_module sysPrxForUser;
static const ppu_static_module sys_libc;
static const ppu_static_module sys_lv2dbg;
};
// Call specified function directly if LLE is not available, call LLE equivalent in callback style otherwise
template<typename T, T Func, typename... Args, typename RT = std::result_of_t<T(Args...)>>
inline RT ppu_execute_function_or_callback(const char* name, PPUThread& ppu, Args&&... args)
{
const auto previous_function = ppu.last_function; // TODO
try
{
return Func(std::forward<Args>(args)...);
}
catch (const std::exception&)
{
LOG_ERROR(PPU, "Function '%s' aborted", ppu.last_function);
ppu.last_function = previous_function;
throw;
}
catch (...)
{
LOG_WARNING(PPU, "Function '%s' aborted", ppu.last_function);
ppu.last_function = previous_function;
throw;
}
ppu.last_function = previous_function;
}
#define CALL_FUNC(ppu, func, ...) ppu_execute_function_or_callback<decltype(&func), &func>(#func, ppu, __VA_ARGS__)
#define REG_FNID(module, nid, func, ...) ppu_module_manager::register_static_function<decltype(&func), &func>(#module, #func, BIND_FUNC(func), nid, {__VA_ARGS__})
#define REG_FUNC(module, func, ...) REG_FNID(module, ppu_generate_id(#func), func, __VA_ARGS__)
#define REG_VNID(module, nid, var, ...) ppu_module_manager::register_static_variable<decltype(var), &var>(#module, #var, nid, {__VA_ARGS__})
#define REG_VAR(module, var, ...) REG_VNID(module, ppu_generate_id(#var), var, __VA_ARGS__)
#define UNIMPLEMENTED_FUNC(module) module.todo("%s", __func__)

File diff suppressed because it is too large Load Diff

View File

@ -1,108 +1,62 @@
#include "stdafx.h"
#include "Emu/Memory/Memory.h"
#include "Emu/System.h"
#include "Emu/state.h"
#include "Emu/IdManager.h"
#include "Emu/Cell/PPUThread.h"
#include "Emu/Cell/PPUDecoder.h"
#include "Emu/Cell/PPUInterpreter.h"
#include "Emu/Cell/PPUInterpreter2.h"
#include "Emu/Cell/PPULLVMRecompiler.h"
//#include "Emu/Cell/PPURecompiler.h"
#include "Utilities/VirtualMemory.h"
#include "PPUThread.h"
#include "PPUInterpreter.h"
#include "PPUModule.h"
#ifdef _WIN32
#include <Windows.h>
#else
#include <sys/mman.h>
#include <sys/stat.h>
#endif
u64 rotate_mask[64][64];
extern u32 ppu_get_tls(u32 thread);
extern void ppu_free_tls(u32 thread);
//thread_local const std::weak_ptr<ppu_decoder_cache_t> g_tls_ppu_decoder_cache = fxm::get<ppu_decoder_cache_t>();
thread_local const ppu_decoder_cache_t* g_tls_ppu_decoder_cache = nullptr; // temporarily, because thread_local is not fully available
ppu_decoder_cache_t::ppu_decoder_cache_t()
: pointer(static_cast<decltype(pointer)>(memory_helper::reserve_memory(0x200000000)))
enum class ppu_decoder_type
{
}
precise,
fast,
llvm,
};
ppu_decoder_cache_t::~ppu_decoder_cache_t()
cfg::map_entry<ppu_decoder_type> g_cfg_ppu_decoder(cfg::root.core, "PPU Decoder", 1,
{
memory_helper::free_reserved_memory(pointer, 0x200000000);
}
{ "Interpreter (precise)", ppu_decoder_type::precise },
{ "Interpreter (fast)", ppu_decoder_type::fast },
{ "Recompiler (LLVM)", ppu_decoder_type::llvm },
});
void ppu_decoder_cache_t::initialize(u32 addr, u32 size)
{
memory_helper::commit_page_memory(pointer + addr / 4, size * 2);
PPUInterpreter2* inter;
PPUDecoder dec(inter = new PPUInterpreter2);
for (u32 pos = addr; pos < addr + size; pos += 4)
{
inter->func = ppu_interpreter::NULL_OP;
// decode PPU opcode
dec.Decode(vm::ps3::read32(pos));
// store function address
pointer[pos / 4] = inter->func;
}
}
PPUThread::PPUThread(const std::string& name)
: CPUThread(CPU_THREAD_PPU, name)
{
InitRotateMask();
}
PPUThread::~PPUThread()
{
close_stack();
ppu_free_tls(m_id);
}
const ppu_decoder<ppu_interpreter_precise> s_ppu_interpreter_precise;
const ppu_decoder<ppu_interpreter_fast> s_ppu_interpreter_fast;
std::string PPUThread::get_name() const
{
return fmt::format("PPU Thread[0x%x] (%s)[0x%08x]", m_id, CPUThread::get_name(), PC);
return fmt::format("PPU[0x%x] Thread (%s)", id, name);
}
void PPUThread::dump_info() const
std::string PPUThread::dump() const
{
extern std::string get_ps3_function_name(u64 fid);
std::string ret = "Registers:\n=========\n";
if (~hle_code < 1024)
{
LOG_SUCCESS(HLE, "Last syscall: %lld (%s)", ~hle_code, get_ps3_function_name(hle_code));
}
else if (hle_code)
{
LOG_SUCCESS(HLE, "Last function: %s (0x%llx)", get_ps3_function_name(hle_code), hle_code);
for (uint i = 0; i<32; ++i) ret += fmt::format("GPR[%d] = 0x%llx\n", i, GPR[i]);
for (uint i = 0; i<32; ++i) ret += fmt::format("FPR[%d] = %.6G\n", i, FPR[i]);
for (uint i = 0; i<32; ++i) ret += fmt::format("VR[%d] = 0x%s [%s]\n", i, VR[i].to_hex().c_str(), VR[i].to_xyzw().c_str());
ret += fmt::format("CR = 0x%08x\n", GetCR());
ret += fmt::format("LR = 0x%llx\n", LR);
ret += fmt::format("CTR = 0x%llx\n", CTR);
ret += fmt::format("XER = [CA=%u | OV=%u | SO=%u | CNT=%u]\n", u32{ CA }, u32{ OV }, u32{ SO }, u32{ XCNT });
//ret += fmt::format("FPSCR = 0x%x "
// "[RN=%d | NI=%d | XE=%d | ZE=%d | UE=%d | OE=%d | VE=%d | "
// "VXCVI=%d | VXSQRT=%d | VXSOFT=%d | FPRF=%d | "
// "FI=%d | FR=%d | VXVC=%d | VXIMZ=%d | "
// "VXZDZ=%d | VXIDI=%d | VXISI=%d | VXSNAN=%d | "
// "XX=%d | ZX=%d | UX=%d | OX=%d | VX=%d | FEX=%d | FX=%d]\n",
// FPSCR.FPSCR,
// u32{ FPSCR.RN },
// u32{ FPSCR.NI }, u32{ FPSCR.XE }, u32{ FPSCR.ZE }, u32{ FPSCR.UE }, u32{ FPSCR.OE }, u32{ FPSCR.VE },
// u32{ FPSCR.VXCVI }, u32{ FPSCR.VXSQRT }, u32{ FPSCR.VXSOFT }, u32{ FPSCR.FPRF },
// u32{ FPSCR.FI }, u32{ FPSCR.FR }, u32{ FPSCR.VXVC }, u32{ FPSCR.VXIMZ },
// u32{ FPSCR.VXZDZ }, u32{ FPSCR.VXIDI }, u32{ FPSCR.VXISI }, u32{ FPSCR.VXSNAN },
// u32{ FPSCR.XX }, u32{ FPSCR.ZX }, u32{ FPSCR.UX }, u32{ FPSCR.OX }, u32{ FPSCR.VX }, u32{ FPSCR.FEX }, u32{ FPSCR.FX });
return ret;
}
CPUThread::dump_info();
}
void PPUThread::init_regs()
{
GPR[1] = align(stack_addr + stack_size, 0x200) - 0x200;
GPR[13] = ppu_get_tls(m_id) + 0x7000; // 0x7000 is subtracted from r13 to access first TLS element
LR = 0;
CTR = PC;
CR.CR = 0x22000082;
VSCR.NJ = 1;
TB = 0;
//m_state |= CPU_STATE_INTR;
}
void PPUThread::init_stack()
void PPUThread::cpu_init()
{
if (!stack_addr)
{
@ -118,14 +72,70 @@ void PPUThread::init_stack()
throw EXCEPTION("Out of stack memory");
}
}
GPR[1] = align(stack_addr + stack_size, 0x200) - 0x200;
}
void PPUThread::close_stack()
void PPUThread::cpu_task()
{
if (stack_addr)
//SetHostRoundingMode(FPSCR_RN_NEAR);
if (custom_task)
{
vm::dealloc_verbose_nothrow(stack_addr, vm::stack);
stack_addr = 0;
if (check_status()) return;
return custom_task(*this);
}
_log::g_tls_make_prefix = [](const auto&, auto, const auto&)
{
const auto cpu = static_cast<PPUThread*>(get_current_cpu_thread());
return fmt::format("%s [0x%08x]", cpu->get_name(), cpu->PC);
};
const auto base = vm::_ptr<const u8>(0);
// Select opcode table
const auto& table = *(
g_cfg_ppu_decoder.get() == ppu_decoder_type::precise ? &s_ppu_interpreter_precise.get_table() :
g_cfg_ppu_decoder.get() == ppu_decoder_type::fast ? &s_ppu_interpreter_fast.get_table() :
throw std::logic_error("Invalid PPU decoder"));
u32 _pc{};
u32 op0, op1, op2;
ppu_inter_func_t func0, func1, func2;
while (true)
{
if (_pc == PC && !state.load())
{
func0(*this, { op0 });
if ((_pc += 4) == (PC += 4) && !state.load())
{
func1(*this, { op1 });
if ((_pc += 4) == (PC += 4))
{
op0 = op2;
func0 = func2;
const auto ops = reinterpret_cast<const be_t<u32>*>(base + _pc);
func1 = table[ppu_decode(op1 = ops[1])];
func2 = table[ppu_decode(op2 = ops[2])];
continue;
}
}
}
// Reinitialize
_pc = PC;
const auto ops = reinterpret_cast<const be_t<u32>*>(base + _pc);
func0 = table[ppu_decode(op0 = ops[0])];
func1 = table[ppu_decode(op1 = ops[1])];
func2 = table[ppu_decode(op2 = ops[2])];
if (check_status()) return;
}
}
@ -134,99 +144,28 @@ bool PPUThread::handle_interrupt()
return false;
}
void PPUThread::do_run()
PPUThread::~PPUThread()
{
m_dec.reset();
switch (auto mode = rpcs3::state.config.core.ppu_decoder.value())
if (stack_addr)
{
case ppu_decoder_type::interpreter: // original interpreter
{
m_dec.reset(new PPUDecoder(new PPUInterpreter(*this)));
break;
}
case ppu_decoder_type::interpreter2: // alternative interpreter
{
break;
}
case ppu_decoder_type::recompiler_llvm:
{
#ifdef PPU_LLVM_RECOMPILER
m_dec.reset(new ppu_recompiler_llvm::CPUHybridDecoderRecompiler(*this));
#else
LOG_ERROR(PPU, "This image does not include PPU JIT (LLVM)");
Emu.Pause();
#endif
break;
}
//case 3: m_dec.reset(new PPURecompiler(*this)); break;
default:
{
LOG_ERROR(PPU, "Invalid CPU decoder mode: %d", mode);
Emu.Pause();
}
vm::dealloc_verbose_nothrow(stack_addr, vm::stack);
}
}
bool FPRdouble::IsINF(PPCdouble d)
be_t<u64>* PPUThread::get_stack_arg(s32 i, u64 align)
{
return ((u64&)d & 0x7FFFFFFFFFFFFFFFULL) == 0x7FF0000000000000ULL;
}
bool FPRdouble::IsNaN(PPCdouble d)
{
return std::isnan((double)d) ? 1 : 0;
}
bool FPRdouble::IsQNaN(PPCdouble d)
{
return
((u64&)d & 0x7FF0000000000000ULL) == 0x7FF0000000000000ULL &&
((u64&)d & 0x0007FFFFFFFFFFFULL) == 0ULL &&
((u64&)d & 0x000800000000000ULL) != 0ULL;
}
bool FPRdouble::IsSNaN(PPCdouble d)
{
return
((u64&)d & 0x7FF0000000000000ULL) == 0x7FF0000000000000ULL &&
((u64&)d & 0x000FFFFFFFFFFFFFULL) != 0ULL &&
((u64&)d & 0x0008000000000000ULL) == 0ULL;
}
int FPRdouble::Cmp(PPCdouble a, PPCdouble b)
{
if(a < b) return CR_LT;
if(a > b) return CR_GT;
if(a == b) return CR_EQ;
return CR_SO;
}
u64 PPUThread::get_stack_arg(s32 i)
{
return vm::ps3::read64(VM_CAST(GPR[1] + 0x70 + 0x8 * (i - 9)));
if (align != 1 && align != 2 && align != 4 && align != 8 && align != 16) throw fmt::exception("Unsupported alignment: 0x%llx" HERE, align);
return vm::_ptr<u64>(vm::cast((GPR[1] + 0x30 + 0x8 * (i - 1)) & (0 - align), HERE));
}
void PPUThread::fast_call(u32 addr, u32 rtoc)
{
if (!is_current())
{
throw EXCEPTION("Called from the wrong thread");
}
auto old_PC = PC;
auto old_stack = GPR[1];
auto old_rtoc = GPR[2];
auto old_LR = LR;
auto old_task = std::move(custom_task);
assert(!old_task || !custom_task);
PC = addr;
GPR[2] = rtoc;
LR = Emu.GetCPUThreadStop();
@ -236,11 +175,13 @@ void PPUThread::fast_call(u32 addr, u32 rtoc)
{
cpu_task();
}
catch (CPUThreadReturn)
catch (cpu_state _s)
{
state += _s;
if (_s != cpu_state::ret) throw;
}
m_state &= ~CPU_STATE_RETURN;
state -= cpu_state::ret;
PC = old_PC;
@ -253,135 +194,3 @@ void PPUThread::fast_call(u32 addr, u32 rtoc)
LR = old_LR;
custom_task = std::move(old_task);
}
void PPUThread::fast_stop()
{
m_state |= CPU_STATE_RETURN;
}
void PPUThread::cpu_task()
{
SetHostRoundingMode(FPSCR_RN_NEAR);
if (custom_task)
{
if (check_status()) return;
return custom_task(*this);
}
if (!g_tls_ppu_decoder_cache)
{
const auto decoder_cache = fxm::get<ppu_decoder_cache_t>();
if (!decoder_cache)
{
throw EXCEPTION("PPU Decoder Cache not initialized");
}
g_tls_ppu_decoder_cache = decoder_cache.get(); // unsafe (TODO)
}
const auto exec_map = g_tls_ppu_decoder_cache->pointer;
if (m_dec)
{
while (true)
{
if (m_state && check_status()) break;
// decode instruction using specified decoder
m_dec->DecodeMemory(PC);
// next instruction
PC += 4;
}
}
else
{
while (true)
{
// get cached interpreter function address
const auto func = exec_map[PC / 4];
// check status
if (!m_state)
{
// call interpreter function
func(*this, { vm::ps3::read32(PC) });
// next instruction
PC += 4;
continue;
}
if (check_status())
{
break;
}
}
}
}
ppu_thread::ppu_thread(u32 entry, const std::string& name, u32 stack_size, s32 prio)
{
auto ppu = idm::make_ptr<PPUThread>(name);
if (entry)
{
ppu->PC = vm::ps3::read32(entry);
ppu->GPR[2] = vm::ps3::read32(entry + 4); // rtoc
}
ppu->stack_size = stack_size ? stack_size : Emu.GetPrimaryStackSize();
ppu->prio = prio ? prio : Emu.GetPrimaryPrio();
thread = std::move(ppu);
argc = 0;
}
cpu_thread& ppu_thread::args(std::initializer_list<std::string> values)
{
if (!values.size())
return *this;
assert(argc == 0);
envp.set(vm::alloc(align(SIZE_32(*envp), stack_align), vm::main));
*envp = 0;
argv.set(vm::alloc(SIZE_32(*argv) * (u32)values.size(), vm::main));
for (auto &arg : values)
{
const u32 arg_size = align(u32(arg.size() + 1), stack_align);
const u32 arg_addr = vm::alloc(arg_size, vm::main);
std::memcpy(vm::base(arg_addr), arg.c_str(), arg.size() + 1);
argv[argc++] = arg_addr;
}
return *this;
}
cpu_thread& ppu_thread::run()
{
thread->run();
gpr(3, argc);
gpr(4, argv.addr());
gpr(5, envp.addr());
return *this;
}
ppu_thread& ppu_thread::gpr(uint index, u64 value)
{
assert(index < 32);
static_cast<PPUThread&>(*thread).GPR[index] = value;
return *this;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,17 +1,27 @@
#include "stdafx.h"
#include "Emu/Memory/Memory.h"
#include "Emu/System.h"
#include "Emu/SysCalls/Callback.h"
#include "Emu/IdManager.h"
#include "Loader/ELF.h"
#include "Emu/Cell/RawSPUThread.h"
// Originally, SPU MFC registers are accessed externally in a concurrent manner (don't mix with channels, SPU MFC channels are isolated)
thread_local spu_mfc_arg_t raw_spu_mfc[8] = {};
RawSPUThread::RawSPUThread(const std::string& name, u32 index)
: SPUThread(CPU_THREAD_RAW_SPU, name, index, RAW_SPU_BASE_ADDR + RAW_SPU_OFFSET * index)
void RawSPUThread::cpu_task()
{
CHECK_ASSERTION(vm::falloc(offset, 0x40000) == offset);
// get next PC and SPU Interrupt status
pc = npc.exchange(0);
set_interrupt_status((pc & 1) != 0);
pc &= 0x3fffc;
SPUThread::cpu_task();
// save next PC and current SPU Interrupt status
npc = pc | ((ch_event_stat & SPU_EVENT_INTR_ENABLED) != 0);
}
bool RawSPUThread::read_reg(const u32 addr, u32& value)
@ -81,7 +91,8 @@ bool RawSPUThread::write_reg(const u32 addr, const u32 value)
}
}))
{
exec();
state -= cpu_state::stop;
safe_notify();
}
};
@ -182,7 +193,7 @@ bool RawSPUThread::write_reg(const u32 addr, const u32 value)
else if (value == SPU_RUNCNTL_STOP_REQUEST)
{
status &= ~SPU_STATUS_RUNNING;
stop();
state += cpu_state::stop;
}
else
{
@ -221,17 +232,19 @@ bool RawSPUThread::write_reg(const u32 addr, const u32 value)
return false;
}
void RawSPUThread::cpu_task()
template<>
void spu_exec_loader::load() const
{
// get next PC and SPU Interrupt status
pc = npc.exchange(0);
auto spu = idm::make_ptr<RawSPUThread>("TEST_SPU");
set_interrupt_status((pc & 1) != 0);
pc &= 0x3fffc;
SPUThread::cpu_task();
// save next PC and current SPU Interrupt status
npc = pc | ((ch_event_stat & SPU_EVENT_INTR_ENABLED) != 0);
for (const auto& prog : progs)
{
if (prog.p_type == 0x1 /* LOAD */ && prog.p_memsz)
{
std::memcpy(vm::base(spu->offset + prog.p_vaddr), prog.bin.data(), prog.p_filesz);
}
}
spu->cpu_init();
spu->npc = header.e_entry;
}

View File

@ -2,27 +2,36 @@
#include "SPUThread.h"
enum : u32
{
RAW_SPU_OFFSET = 0x00100000,
RAW_SPU_BASE_ADDR = 0xE0000000,
RAW_SPU_LS_OFFSET = 0x00000000,
RAW_SPU_PROB_OFFSET = 0x00040000,
};
force_inline static u32 GetRawSPURegAddrByNum(int num, int offset)
{
return RAW_SPU_OFFSET * num + RAW_SPU_BASE_ADDR + RAW_SPU_PROB_OFFSET + offset;
}
class RawSPUThread final : public SPUThread
{
void cpu_task() override;
public:
RawSPUThread(const std::string& name, u32 index);
/* IdManager setups */
using id_base = RawSPUThread;
static constexpr u32 id_min = 0;
static constexpr u32 id_max = 4;
void on_init() override
{
if (!offset)
{
// Install correct SPU index and LS address
const_cast<u32&>(index) = id;
const_cast<u32&>(offset) = vm::falloc(RAW_SPU_BASE_ADDR + RAW_SPU_OFFSET * index, 0x40000);
ASSERT(offset);
SPUThread::on_init();
}
}
RawSPUThread(const std::string& name)
: SPUThread(name)
{
}
bool read_reg(const u32 addr, u32& value);
bool write_reg(const u32 addr, const u32 value);
private:
virtual void cpu_task() override;
};

View File

@ -1,4 +1,5 @@
#include "stdafx.h"
#include "Emu/Memory/Memory.h"
#include "Emu/System.h"
#include "SPUDisAsm.h"
@ -9,10 +10,6 @@
#define ASMJIT_STATIC
#define ASMJIT_DEBUG
#ifdef _MSC_VER
#pragma comment(lib, "asmjit.lib")
#endif
#include "asmjit.h"
#define SPU_OFF_128(x) asmjit::host::oword_ptr(*cpu, OFFSET_32(SPUThread, x))
@ -21,6 +18,9 @@
#define SPU_OFF_16(x) asmjit::host::word_ptr(*cpu, OFFSET_32(SPUThread, x))
#define SPU_OFF_8(x) asmjit::host::byte_ptr(*cpu, OFFSET_32(SPUThread, x))
const spu_decoder<spu_interpreter_fast> s_spu_interpreter; // TODO: remove
const spu_decoder<spu_recompiler> s_spu_decoder;
spu_recompiler::spu_recompiler()
: m_jit(std::make_shared<asmjit::JitRuntime>())
{
@ -29,7 +29,7 @@ spu_recompiler::spu_recompiler()
LOG_SUCCESS(SPU, "SPU Recompiler (ASMJIT) created...");
fs::file(fs::get_config_dir() + "SPUJIT.log", fom::rewrite).write(fmt::format("SPU JIT initialization...\n\nTitle: %s\nTitle ID: %s\n\n", Emu.GetTitle().c_str(), Emu.GetTitleID().c_str()));
fs::file(fs::get_config_dir() + "SPUJIT.log", fs::rewrite).write(fmt::format("SPU JIT initialization...\n\nTitle: %s\nTitle ID: %s\n\n", Emu.GetTitle().c_str(), Emu.GetTitleID().c_str()));
}
void spu_recompiler::compile(spu_function_t& f)
@ -145,13 +145,13 @@ void spu_recompiler::compile(spu_function_t& f)
// Disasm
dis_asm.dump_pc = m_pos;
dis_asm.do_disasm(op);
dis_asm.disasm(m_pos);
compiler.addComment(dis_asm.last_opcode.c_str());
log += dis_asm.last_opcode.c_str();
log += '\n';
// Recompiler function
(this->*spu_recompiler::opcodes[op])({ op });
(this->*s_spu_decoder.decode(op))({ op });
// Collect allocated xmm vars
for (u32 i = 0; i < vec_vars.size(); i++)
@ -214,7 +214,7 @@ void spu_recompiler::compile(spu_function_t& f)
log += "\n\n\n";
// Append log file
fs::file(fs::get_config_dir() + "SPUJIT.log", fom::write | fom::append).write(log);
fs::file(fs::get_config_dir() + "SPUJIT.log", fs::write + fs::append).write(log);
}
spu_recompiler::XmmLink spu_recompiler::XmmAlloc() // get empty xmm register
@ -267,7 +267,7 @@ void spu_recompiler::InterpreterCall(spu_opcode_t op)
const u32 old_pc = _spu->pc;
if (_spu->m_state && _spu->check_status())
if (_spu->state.load() && _spu->check_status())
{
return 0x2000000 | _spu->pc;
}
@ -294,7 +294,7 @@ void spu_recompiler::InterpreterCall(spu_opcode_t op)
asmjit::X86CallNode* call = c->call(asmjit::imm_ptr(asmjit_cast<void*, u32(SPUThread*, u32, spu_inter_func_t)>(gate)), asmjit::kFuncConvHost, asmjit::FuncBuilder3<u32, void*, u32, void*>());
call->setArg(0, *cpu);
call->setArg(1, asmjit::imm_u(op.opcode));
call->setArg(2, asmjit::imm_ptr(asmjit_cast<void*>(spu_interpreter::fast::g_spu_opcode_table[op.opcode])));
call->setArg(2, asmjit::imm_ptr(asmjit_cast<void*>(s_spu_interpreter.decode(op.opcode))));
call->setRet(0, *addr);
// return immediately if an error occured
@ -338,21 +338,20 @@ void spu_recompiler::FunctionCall()
LOG_ERROR(SPU, "Branch-to-self");
}
while (!_spu->m_state || !_spu->check_status())
while (!_spu->state.load() || !_spu->check_status())
{
// Call override function directly since the type is known
static_cast<SPURecompilerDecoder&>(*_spu->m_dec).DecodeMemory(_spu->offset + _spu->pc);
// Proceed recursively
spu_recompiler_base::enter(*_spu);
if (_spu->m_state & CPU_STATE_RETURN)
if (_spu->state & cpu_state::ret)
{
break;
}
if (_spu->pc == link)
{
// returned successfully
_spu->recursion_level--;
return 0;
return 0; // Successfully returned
}
}
@ -2185,7 +2184,7 @@ void spu_recompiler::BR(spu_opcode_t op)
c->mov(*addr, target | 0x2000000);
//c->cmp(asmjit::host::dword_ptr(*ls, m_pos), 0x32); // compare instruction opcode with BR-to-self
//c->je(labels[target / 4]);
c->lock().or_(SPU_OFF_64(m_state), CPU_STATE_RETURN | CPU_STATE_STOPPED);
c->lock().or_(SPU_OFF_32(state), (to_mset(cpu_state::stop) + cpu_state::ret)._value());
c->jmp(*end);
c->unuse(*addr);
return;
@ -2614,7 +2613,6 @@ void spu_recompiler::FMS(spu_opcode_t op)
void spu_recompiler::UNK(spu_opcode_t op)
{
throw EXCEPTION("Unknown/Illegal opcode (0x%08x)", op.opcode);
LOG_ERROR(SPU, "0x%05x: Unknown/Illegal opcode (0x%08x)", m_pos, op.opcode);
c->int3();
}
const spu_opcode_table_t<void(spu_recompiler::*)(spu_opcode_t)> spu_recompiler::opcodes{ DEFINE_SPU_OPCODES(&spu_recompiler::), &spu_recompiler::UNK };

View File

@ -13,7 +13,7 @@ namespace asmjit
}
// SPU ASMJIT Recompiler
class spu_recompiler : public SPURecompilerBase
class spu_recompiler : public spu_recompiler_base
{
const std::shared_ptr<asmjit::JitRuntime> m_jit;
@ -75,7 +75,7 @@ private:
asmjit::X86Mem XmmConst(__m128 data);
asmjit::X86Mem XmmConst(__m128i data);
private:
public:
void InterpreterCall(spu_opcode_t op);
void FunctionCall();
@ -280,6 +280,4 @@ private:
void FMS(spu_opcode_t op);
void UNK(spu_opcode_t op);
static const spu_opcode_table_t<void(spu_recompiler::*)(spu_opcode_t)> opcodes;
};

View File

@ -4,7 +4,7 @@
#include "SPURecompiler.h"
#include "SPUAnalyser.h"
const spu_opcode_table_t<spu_itype_t> g_spu_itype{ DEFINE_SPU_OPCODES(spu_itype::), spu_itype::UNK };
const spu_decoder<spu_itype::type> s_spu_itype;
std::shared_ptr<spu_function_t> SPUDatabase::find(const be_t<u32>* data, u64 key, u32 max_size)
{
@ -83,7 +83,7 @@ std::shared_ptr<spu_function_t> SPUDatabase::analyse(const be_t<u32>* ls, u32 en
{
const spu_opcode_t op{ ls[pos / 4] };
const spu_itype_t type = g_spu_itype[op.opcode];
const auto type = s_spu_itype.decode(op.opcode);
using namespace spu_itype;
@ -172,15 +172,15 @@ std::shared_ptr<spu_function_t> SPUDatabase::analyse(const be_t<u32>* ls, u32 en
break;
}
if (type == BI || type == IRET) // Branch Indirect
if (type == &type::BI || type == &type::IRET) // Branch Indirect
{
if (type == IRET) LOG_ERROR(SPU, "[0x%05x] Interrupt Return", pos);
if (type == &type::IRET) LOG_ERROR(SPU, "[0x%05x] Interrupt Return", pos);
blocks.emplace(start); start = pos + 4;
}
else if (type == BR || type == BRA) // Branch Relative/Absolute
else if (type == &type::BR || type == &type::BRA) // Branch Relative/Absolute
{
const u32 target = spu_branch_target(type == BR ? pos : 0, op.i16);
const u32 target = spu_branch_target(type == &type::BR ? pos : 0, op.i16);
// Add adjacent function because it always could be
adjacent.emplace(target);
@ -192,9 +192,9 @@ std::shared_ptr<spu_function_t> SPUDatabase::analyse(const be_t<u32>* ls, u32 en
blocks.emplace(start); start = pos + 4;
}
else if (type == BRSL || type == BRASL) // Branch Relative/Absolute and Set Link
else if (type == &type::BRSL || type == &type::BRASL) // Branch Relative/Absolute and Set Link
{
const u32 target = spu_branch_target(type == BRSL ? pos : 0, op.i16);
const u32 target = spu_branch_target(type == &type::BRSL ? pos : 0, op.i16);
if (target == pos + 4)
{
@ -215,11 +215,11 @@ std::shared_ptr<spu_function_t> SPUDatabase::analyse(const be_t<u32>* ls, u32 en
if (op.rt != 0) LOG_ERROR(SPU, "[0x%05x] Function call without $LR", pos);
}
}
else if (type == BISL || type == BISLED) // Branch Indirect and Set Link
else if (type == &type::BISL || type == &type::BISLED) // Branch Indirect and Set Link
{
if (op.rt != 0) LOG_ERROR(SPU, "[0x%05x] Indirect function call without $LR", pos);
}
else if (type == BRNZ || type == BRZ || type == BRHNZ || type == BRHZ) // Branch Relative if (Not) Zero (Half)word
else if (type == &type::BRNZ || type == &type::BRZ || type == &type::BRHNZ || type == &type::BRHZ) // Branch Relative if (Not) Zero (Half)word
{
const u32 target = spu_branch_target(pos, op.i16);
@ -231,24 +231,24 @@ std::shared_ptr<spu_function_t> SPUDatabase::analyse(const be_t<u32>* ls, u32 en
blocks.emplace(target);
}
}
else if (type == BINZ || type == BIZ || type == BIHNZ || type == BIHZ) // Branch Indirect if (Not) Zero (Half)word
else if (type == &type::BINZ || type == &type::BIZ || type == &type::BIHNZ || type == &type::BIHZ) // Branch Indirect if (Not) Zero (Half)word
{
}
else if (type == HBR || type == HBRA || type == HBRR) // Hint for Branch
else if (type == &type::HBR || type == &type::HBRA || type == &type::HBRR) // Hint for Branch
{
}
else if (type == STQA || type == STQD || type == STQR || type == STQX || type == FSCRWR || type == MTSPR || type == WRCH) // Store
else if (type == &type::STQA || type == &type::STQD || type == &type::STQR || type == &type::STQX || type == &type::FSCRWR || type == &type::MTSPR || type == &type::WRCH) // Store
{
}
else if (type == HEQ || type == HEQI || type == HGT || type == HGTI || type == HLGT || type == HLGTI) // Halt
else if (type == &type::HEQ || type == &type::HEQI || type == &type::HGT || type == &type::HGTI || type == &type::HLGT || type == &type::HLGTI) // Halt
{
}
else if (type == STOP || type == STOPD || type == NOP || type == LNOP || type == SYNC || type == DSYNC) // Miscellaneous
else if (type == &type::STOP || type == &type::STOPD || type == &type::NOP || type == &type::LNOP || type == &type::SYNC || type == &type::DSYNC) // Miscellaneous
{
}
else // Other instructions (writing rt reg)
{
const u32 rt = type == SELB || type == SHUFB || type == MPYA || type == FNMS || type == FMA || type == FMS ? +op.rc : +op.rt;
const u32 rt = type == &type::SELB || type == &type::SHUFB || type == &type::MPYA || type == &type::FNMS || type == &type::FMA || type == &type::FMS ? +op.rc : +op.rt;
// Analyse link register access
if (rt == 0)
@ -258,7 +258,7 @@ std::shared_ptr<spu_function_t> SPUDatabase::analyse(const be_t<u32>* ls, u32 en
// Analyse stack pointer access
if (rt == 1)
{
if (type == ILA && pos < ila_sp_pos)
if (type == &type::ILA && pos < ila_sp_pos)
{
// set minimal ila $SP,* instruction position
ila_sp_pos = pos;
@ -272,7 +272,7 @@ std::shared_ptr<spu_function_t> SPUDatabase::analyse(const be_t<u32>* ls, u32 en
{
const spu_opcode_t op{ ls[pos / 4] };
const spu_itype_t type = g_spu_itype[op.opcode];
const auto type = s_spu_itype.decode(op.opcode);
using namespace spu_itype;
@ -280,9 +280,9 @@ std::shared_ptr<spu_function_t> SPUDatabase::analyse(const be_t<u32>* ls, u32 en
{
break;
}
else if (type == BRSL || type == BRASL) // Branch Relative/Absolute and Set Link
else if (type == &type::BRSL || type == &type::BRASL) // Branch Relative/Absolute and Set Link
{
const u32 target = spu_branch_target(type == BRSL ? pos : 0, op.i16);
const u32 target = spu_branch_target(type == &type::BRSL ? pos : 0, op.i16);
if (target != pos + 4 && target > entry && limit > target)
{

View File

@ -3,250 +3,246 @@
#include "Emu/Cell/SPUOpcodes.h"
#include "Utilities/SharedMutex.h"
#include <set>
class SPUThread;
// Type of the runtime functions generated by SPU recompiler
using spu_jit_func_t = u32(*)(SPUThread* _spu, be_t<u32>* _ls);
// SPU instruction classification namespace
// SPU Instruction Classifier
namespace spu_itype
{
enum spu_itype_t : u32
struct type
{
UNK = 0,
STOP,
LNOP,
SYNC,
DSYNC,
MFSPR,
RDCH,
RCHCNT,
SF,
OR,
BG,
SFH,
NOR,
ABSDB,
ROT,
ROTM,
ROTMA,
SHL,
ROTH,
ROTHM,
ROTMAH,
SHLH,
ROTI,
ROTMI,
ROTMAI,
SHLI,
ROTHI,
ROTHMI,
ROTMAHI,
SHLHI,
A,
AND,
CG,
AH,
NAND,
AVGB,
MTSPR,
WRCH,
BIZ,
BINZ,
BIHZ,
BIHNZ,
STOPD,
STQX,
BI,
BISL,
IRET,
BISLED,
HBR,
GB,
GBH,
GBB,
FSM,
FSMH,
FSMB,
FREST,
FRSQEST,
LQX,
ROTQBYBI,
ROTQMBYBI,
SHLQBYBI,
CBX,
CHX,
CWX,
CDX,
ROTQBI,
ROTQMBI,
SHLQBI,
ROTQBY,
ROTQMBY,
SHLQBY,
ORX,
CBD,
CHD,
CWD,
CDD,
ROTQBII,
ROTQMBII,
SHLQBII,
ROTQBYI,
ROTQMBYI,
SHLQBYI,
NOP,
CGT,
XOR,
CGTH,
EQV,
CGTB,
SUMB,
HGT,
CLZ,
XSWD,
XSHW,
CNTB,
XSBH,
CLGT,
ANDC,
FCGT,
DFCGT,
FA,
FS,
FM,
CLGTH,
ORC,
FCMGT,
DFCMGT,
DFA,
DFS,
DFM,
CLGTB,
HLGT,
DFMA,
DFMS,
DFNMS,
DFNMA,
CEQ,
MPYHHU,
ADDX,
SFX,
CGX,
BGX,
MPYHHA,
MPYHHAU,
FSCRRD,
FESD,
FRDS,
FSCRWR,
DFTSV,
FCEQ,
DFCEQ,
MPY,
MPYH,
MPYHH,
MPYS,
CEQH,
FCMEQ,
DFCMEQ,
MPYU,
CEQB,
FI,
HEQ,
CFLTS,
CFLTU,
CSFLT,
CUFLT,
BRZ,
STQA,
BRNZ,
BRHZ,
BRHNZ,
STQR,
BRA,
LQA,
BRASL,
BR,
FSMBI,
BRSL,
LQR,
IL,
ILHU,
ILH,
IOHL,
ORI,
ORHI,
ORBI,
SFI,
SFHI,
ANDI,
ANDHI,
ANDBI,
AI,
AHI,
STQD,
LQD,
XORI,
XORHI,
XORBI,
CGTI,
CGTHI,
CGTBI,
HGTI,
CLGTI,
CLGTHI,
CLGTBI,
HLGTI,
MPYI,
MPYUI,
CEQI,
CEQHI,
CEQBI,
HEQI,
HBRA,
HBRR,
ILA,
SELB,
SHUFB,
MPYA,
FNMS,
FMA,
FMS,
u32 UNK;
u32 STOP;
u32 LNOP;
u32 SYNC;
u32 DSYNC;
u32 MFSPR;
u32 RDCH;
u32 RCHCNT;
u32 SF;
u32 OR;
u32 BG;
u32 SFH;
u32 NOR;
u32 ABSDB;
u32 ROT;
u32 ROTM;
u32 ROTMA;
u32 SHL;
u32 ROTH;
u32 ROTHM;
u32 ROTMAH;
u32 SHLH;
u32 ROTI;
u32 ROTMI;
u32 ROTMAI;
u32 SHLI;
u32 ROTHI;
u32 ROTHMI;
u32 ROTMAHI;
u32 SHLHI;
u32 A;
u32 AND;
u32 CG;
u32 AH;
u32 NAND;
u32 AVGB;
u32 MTSPR;
u32 WRCH;
u32 BIZ;
u32 BINZ;
u32 BIHZ;
u32 BIHNZ;
u32 STOPD;
u32 STQX;
u32 BI;
u32 BISL;
u32 IRET;
u32 BISLED;
u32 HBR;
u32 GB;
u32 GBH;
u32 GBB;
u32 FSM;
u32 FSMH;
u32 FSMB;
u32 FREST;
u32 FRSQEST;
u32 LQX;
u32 ROTQBYBI;
u32 ROTQMBYBI;
u32 SHLQBYBI;
u32 CBX;
u32 CHX;
u32 CWX;
u32 CDX;
u32 ROTQBI;
u32 ROTQMBI;
u32 SHLQBI;
u32 ROTQBY;
u32 ROTQMBY;
u32 SHLQBY;
u32 ORX;
u32 CBD;
u32 CHD;
u32 CWD;
u32 CDD;
u32 ROTQBII;
u32 ROTQMBII;
u32 SHLQBII;
u32 ROTQBYI;
u32 ROTQMBYI;
u32 SHLQBYI;
u32 NOP;
u32 CGT;
u32 XOR;
u32 CGTH;
u32 EQV;
u32 CGTB;
u32 SUMB;
u32 HGT;
u32 CLZ;
u32 XSWD;
u32 XSHW;
u32 CNTB;
u32 XSBH;
u32 CLGT;
u32 ANDC;
u32 FCGT;
u32 DFCGT;
u32 FA;
u32 FS;
u32 FM;
u32 CLGTH;
u32 ORC;
u32 FCMGT;
u32 DFCMGT;
u32 DFA;
u32 DFS;
u32 DFM;
u32 CLGTB;
u32 HLGT;
u32 DFMA;
u32 DFMS;
u32 DFNMS;
u32 DFNMA;
u32 CEQ;
u32 MPYHHU;
u32 ADDX;
u32 SFX;
u32 CGX;
u32 BGX;
u32 MPYHHA;
u32 MPYHHAU;
u32 FSCRRD;
u32 FESD;
u32 FRDS;
u32 FSCRWR;
u32 DFTSV;
u32 FCEQ;
u32 DFCEQ;
u32 MPY;
u32 MPYH;
u32 MPYHH;
u32 MPYS;
u32 CEQH;
u32 FCMEQ;
u32 DFCMEQ;
u32 MPYU;
u32 CEQB;
u32 FI;
u32 HEQ;
u32 CFLTS;
u32 CFLTU;
u32 CSFLT;
u32 CUFLT;
u32 BRZ;
u32 STQA;
u32 BRNZ;
u32 BRHZ;
u32 BRHNZ;
u32 STQR;
u32 BRA;
u32 LQA;
u32 BRASL;
u32 BR;
u32 FSMBI;
u32 BRSL;
u32 LQR;
u32 IL;
u32 ILHU;
u32 ILH;
u32 IOHL;
u32 ORI;
u32 ORHI;
u32 ORBI;
u32 SFI;
u32 SFHI;
u32 ANDI;
u32 ANDHI;
u32 ANDBI;
u32 AI;
u32 AHI;
u32 STQD;
u32 LQD;
u32 XORI;
u32 XORHI;
u32 XORBI;
u32 CGTI;
u32 CGTHI;
u32 CGTBI;
u32 HGTI;
u32 CLGTI;
u32 CLGTHI;
u32 CLGTBI;
u32 HLGTI;
u32 MPYI;
u32 MPYUI;
u32 CEQI;
u32 CEQHI;
u32 CEQBI;
u32 HEQI;
u32 HBRA;
u32 HBRR;
u32 ILA;
u32 SELB;
u32 SHUFB;
u32 MPYA;
u32 FNMS;
u32 FMA;
u32 FMS;
};
};
}
using spu_itype::spu_itype_t;
// SPU Instruction Classification table
extern const spu_opcode_table_t<spu_itype_t> g_spu_itype;
// SPU basic function information structure
struct spu_function_t
{
// entry point (LS address)
// Entry point (LS address)
const u32 addr;
// function size (in bytes)
// Function size (in bytes)
const u32 size;
// function contents (binary copy)
// Function contents (binary copy)
std::vector<be_t<u32>> data;
// basic blocks (start addresses)
// Basic blocks (start addresses)
std::set<u32> blocks;
// functions possibly called by this function (may not be available)
// Functions possibly called by this function (may not be available)
std::set<u32> adjacent;
// jump table values (start addresses)
// Jump table values (start addresses)
std::set<u32> jtable;
// whether ila $SP,* instruction found
// Whether ila $SP,* instruction found
bool does_reset_stack;
// pointer to the compiled function
// Pointer to the compiled function
spu_jit_func_t compiled = nullptr;
spu_function_t(u32 addr, u32 size)

View File

@ -1,5 +0,0 @@
#pragma once
struct spu_context_t
{
};

View File

@ -0,0 +1,11 @@
#include "stdafx.h"
#include "SPUDisAsm.h"
const spu_decoder<SPUDisAsm> s_spu_disasm;
u32 SPUDisAsm::disasm(u32 pc)
{
const u32 op = *(be_t<u32>*)(offset + pc);
(this->*(s_spu_disasm.decode(op)))({ op });
return 4;
}

View File

@ -66,7 +66,7 @@ static const char* spu_ch_name[128] =
"$ch121", "$ch122", "$ch123", "$ch124", "$ch125", "$ch126", "$ch127",
};
class SPUDisAsm : public PPCDisAsm
class SPUDisAsm final : public PPCDisAsm
{
public:
SPUDisAsm(CPUDisAsmMode mode) : PPCDisAsm(mode)
@ -133,6 +133,10 @@ private:
{
Write(fmt::format("%s %s,%s,%s,%s", FixOp(op).c_str(), a1, a2, a3, a4));
}
public:
u32 disasm(u32 pc) override;
//0 - 10
void STOP(spu_opcode_t op)
{
@ -945,12 +949,4 @@ private:
{
Write(fmt::format("Unknown/Illegal opcode! (0x%08x)", op.opcode));
}
static const spu_opcode_table_t<void(SPUDisAsm::*)(spu_opcode_t)> opcodes;
public:
void do_disasm(u32 opcode)
{
(this->*opcodes[opcode])({ opcode });
}
};

View File

@ -7,20 +7,7 @@
#include <fenv.h>
namespace spu_interpreter
{
namespace fast
{
const spu_opcode_table_t<spu_inter_func_t> g_spu_opcode_table{ DEFINE_SPU_OPCODES(+), default_function };
}
namespace precise
{
const spu_opcode_table_t<spu_inter_func_t> g_spu_opcode_table{ DEFINE_SPU_OPCODES(+), default_function };
}
}
void spu_interpreter::default_function(SPUThread& spu, spu_opcode_t op)
void spu_interpreter::UNK(SPUThread& spu, spu_opcode_t op)
{
throw EXCEPTION("Unknown/Illegal instruction (0x%08x)", op.opcode);
}
@ -396,12 +383,12 @@ void spu_interpreter::FSMB(SPUThread& spu, spu_opcode_t op)
spu.gpr[op.rt] = g_spu_imm.fsmb[spu.gpr[op.ra]._u32[3] & 0xffff];
}
void spu_interpreter::fast::FREST(SPUThread& spu, spu_opcode_t op)
void spu_interpreter_fast::FREST(SPUThread& spu, spu_opcode_t op)
{
spu.gpr[op.rt].vf = _mm_rcp_ps(spu.gpr[op.ra].vf);
}
void spu_interpreter::fast::FRSQEST(SPUThread& spu, spu_opcode_t op)
void spu_interpreter_fast::FRSQEST(SPUThread& spu, spu_opcode_t op)
{
const auto mask = _mm_castsi128_ps(_mm_set1_epi32(0x7fffffff));
spu.gpr[op.rt].vf = _mm_rsqrt_ps(_mm_and_ps(spu.gpr[op.ra].vf, mask));
@ -687,7 +674,7 @@ void spu_interpreter::ANDC(SPUThread& spu, spu_opcode_t op)
spu.gpr[op.rt] = v128::andnot(spu.gpr[op.rb], spu.gpr[op.ra]);
}
void spu_interpreter::fast::FCGT(SPUThread& spu, spu_opcode_t op)
void spu_interpreter_fast::FCGT(SPUThread& spu, spu_opcode_t op)
{
spu.gpr[op.rt].vf = _mm_cmplt_ps(spu.gpr[op.rb].vf, spu.gpr[op.ra].vf);
}
@ -697,17 +684,17 @@ void spu_interpreter::DFCGT(SPUThread& spu, spu_opcode_t op)
throw EXCEPTION("Unexpected instruction");
}
void spu_interpreter::fast::FA(SPUThread& spu, spu_opcode_t op)
void spu_interpreter_fast::FA(SPUThread& spu, spu_opcode_t op)
{
spu.gpr[op.rt] = v128::addfs(spu.gpr[op.ra], spu.gpr[op.rb]);
}
void spu_interpreter::fast::FS(SPUThread& spu, spu_opcode_t op)
void spu_interpreter_fast::FS(SPUThread& spu, spu_opcode_t op)
{
spu.gpr[op.rt] = v128::subfs(spu.gpr[op.ra], spu.gpr[op.rb]);
}
void spu_interpreter::fast::FM(SPUThread& spu, spu_opcode_t op)
void spu_interpreter_fast::FM(SPUThread& spu, spu_opcode_t op)
{
spu.gpr[op.rt].vf = _mm_mul_ps(spu.gpr[op.ra].vf, spu.gpr[op.rb].vf);
}
@ -722,7 +709,7 @@ void spu_interpreter::ORC(SPUThread& spu, spu_opcode_t op)
spu.gpr[op.rt] = spu.gpr[op.ra] | ~spu.gpr[op.rb];
}
void spu_interpreter::fast::FCMGT(SPUThread& spu, spu_opcode_t op)
void spu_interpreter_fast::FCMGT(SPUThread& spu, spu_opcode_t op)
{
const auto mask = _mm_castsi128_ps(_mm_set1_epi32(0x7fffffff));
spu.gpr[op.rt].vf = _mm_cmplt_ps(_mm_and_ps(spu.gpr[op.rb].vf, mask), _mm_and_ps(spu.gpr[op.ra].vf, mask));
@ -733,17 +720,17 @@ void spu_interpreter::DFCMGT(SPUThread& spu, spu_opcode_t op)
throw EXCEPTION("Unexpected instruction");
}
void spu_interpreter::fast::DFA(SPUThread& spu, spu_opcode_t op)
void spu_interpreter_fast::DFA(SPUThread& spu, spu_opcode_t op)
{
spu.gpr[op.rt] = v128::addfd(spu.gpr[op.ra], spu.gpr[op.rb]);
}
void spu_interpreter::fast::DFS(SPUThread& spu, spu_opcode_t op)
void spu_interpreter_fast::DFS(SPUThread& spu, spu_opcode_t op)
{
spu.gpr[op.rt] = v128::subfd(spu.gpr[op.ra], spu.gpr[op.rb]);
}
void spu_interpreter::fast::DFM(SPUThread& spu, spu_opcode_t op)
void spu_interpreter_fast::DFM(SPUThread& spu, spu_opcode_t op)
{
spu.gpr[op.rt].vd = _mm_mul_pd(spu.gpr[op.ra].vd, spu.gpr[op.rb].vd);
}
@ -761,22 +748,22 @@ void spu_interpreter::HLGT(SPUThread& spu, spu_opcode_t op)
}
}
void spu_interpreter::fast::DFMA(SPUThread& spu, spu_opcode_t op)
void spu_interpreter_fast::DFMA(SPUThread& spu, spu_opcode_t op)
{
spu.gpr[op.rt].vd = _mm_add_pd(_mm_mul_pd(spu.gpr[op.ra].vd, spu.gpr[op.rb].vd), spu.gpr[op.rt].vd);
}
void spu_interpreter::fast::DFMS(SPUThread& spu, spu_opcode_t op)
void spu_interpreter_fast::DFMS(SPUThread& spu, spu_opcode_t op)
{
spu.gpr[op.rt].vd = _mm_sub_pd(_mm_mul_pd(spu.gpr[op.ra].vd, spu.gpr[op.rb].vd), spu.gpr[op.rt].vd);
}
void spu_interpreter::fast::DFNMS(SPUThread& spu, spu_opcode_t op)
void spu_interpreter_fast::DFNMS(SPUThread& spu, spu_opcode_t op)
{
spu.gpr[op.rt].vd = _mm_sub_pd(spu.gpr[op.rt].vd, _mm_mul_pd(spu.gpr[op.ra].vd, spu.gpr[op.rb].vd));
}
void spu_interpreter::fast::DFNMA(SPUThread& spu, spu_opcode_t op)
void spu_interpreter_fast::DFNMA(SPUThread& spu, spu_opcode_t op)
{
spu.gpr[op.rt].vd = _mm_sub_pd(_mm_set1_pd(0.0), _mm_add_pd(_mm_mul_pd(spu.gpr[op.ra].vd, spu.gpr[op.rb].vd), spu.gpr[op.rt].vd));
}
@ -833,24 +820,24 @@ void spu_interpreter::MPYHHAU(SPUThread& spu, spu_opcode_t op)
spu.gpr[op.rt].vi = _mm_add_epi32(spu.gpr[op.rt].vi, _mm_or_si128(_mm_srli_epi32(_mm_mullo_epi16(a, b), 16), _mm_and_si128(_mm_mulhi_epu16(a, b), _mm_set1_epi32(0xffff0000))));
}
void spu_interpreter::fast::FSCRRD(SPUThread& spu, spu_opcode_t op)
void spu_interpreter_fast::FSCRRD(SPUThread& spu, spu_opcode_t op)
{
spu.gpr[op.rt].clear();
}
void spu_interpreter::fast::FESD(SPUThread& spu, spu_opcode_t op)
void spu_interpreter_fast::FESD(SPUThread& spu, spu_opcode_t op)
{
const auto a = spu.gpr[op.ra].vf;
spu.gpr[op.rt].vd = _mm_cvtps_pd(_mm_shuffle_ps(a, a, 0x8d));
}
void spu_interpreter::fast::FRDS(SPUThread& spu, spu_opcode_t op)
void spu_interpreter_fast::FRDS(SPUThread& spu, spu_opcode_t op)
{
const auto t = _mm_cvtpd_ps(spu.gpr[op.ra].vd);
spu.gpr[op.rt].vf = _mm_shuffle_ps(t, t, 0x72);
}
void spu_interpreter::fast::FSCRWR(SPUThread& spu, spu_opcode_t op)
void spu_interpreter_fast::FSCRWR(SPUThread& spu, spu_opcode_t op)
{
}
@ -859,7 +846,7 @@ void spu_interpreter::DFTSV(SPUThread& spu, spu_opcode_t op)
throw EXCEPTION("Unexpected instruction");
}
void spu_interpreter::fast::FCEQ(SPUThread& spu, spu_opcode_t op)
void spu_interpreter_fast::FCEQ(SPUThread& spu, spu_opcode_t op)
{
spu.gpr[op.rt].vf = _mm_cmpeq_ps(spu.gpr[op.rb].vf, spu.gpr[op.ra].vf);
}
@ -895,7 +882,7 @@ void spu_interpreter::CEQH(SPUThread& spu, spu_opcode_t op)
spu.gpr[op.rt].vi = _mm_cmpeq_epi16(spu.gpr[op.ra].vi, spu.gpr[op.rb].vi);
}
void spu_interpreter::fast::FCMEQ(SPUThread& spu, spu_opcode_t op)
void spu_interpreter_fast::FCMEQ(SPUThread& spu, spu_opcode_t op)
{
const auto mask = _mm_castsi128_ps(_mm_set1_epi32(0x7fffffff));
spu.gpr[op.rt].vf = _mm_cmpeq_ps(_mm_and_ps(spu.gpr[op.rb].vf, mask), _mm_and_ps(spu.gpr[op.ra].vf, mask));
@ -918,7 +905,7 @@ void spu_interpreter::CEQB(SPUThread& spu, spu_opcode_t op)
spu.gpr[op.rt].vi = _mm_cmpeq_epi8(spu.gpr[op.ra].vi, spu.gpr[op.rb].vi);
}
void spu_interpreter::fast::FI(SPUThread& spu, spu_opcode_t op)
void spu_interpreter_fast::FI(SPUThread& spu, spu_opcode_t op)
{
// TODO
const auto mask_se = _mm_castsi128_ps(_mm_set1_epi32(0xff800000)); // sign and exponent mask
@ -940,25 +927,25 @@ void spu_interpreter::HEQ(SPUThread& spu, spu_opcode_t op)
}
void spu_interpreter::fast::CFLTS(SPUThread& spu, spu_opcode_t op)
void spu_interpreter_fast::CFLTS(SPUThread& spu, spu_opcode_t op)
{
const auto scaled = _mm_mul_ps(spu.gpr[op.ra].vf, g_spu_imm.scale[173 - op.i8]);
spu.gpr[op.rt].vi = _mm_xor_si128(_mm_cvttps_epi32(scaled), _mm_castps_si128(_mm_cmpge_ps(scaled, _mm_set1_ps(0x80000000))));
}
void spu_interpreter::fast::CFLTU(SPUThread& spu, spu_opcode_t op)
void spu_interpreter_fast::CFLTU(SPUThread& spu, spu_opcode_t op)
{
const auto scaled1 = _mm_max_ps(_mm_mul_ps(spu.gpr[op.ra].vf, g_spu_imm.scale[173 - op.i8]), _mm_set1_ps(0.0f));
const auto scaled2 = _mm_and_ps(_mm_sub_ps(scaled1, _mm_set1_ps(0x80000000)), _mm_cmpge_ps(scaled1, _mm_set1_ps(0x80000000)));
spu.gpr[op.rt].vi = _mm_or_si128(_mm_or_si128(_mm_cvttps_epi32(scaled1), _mm_cvttps_epi32(scaled2)), _mm_castps_si128(_mm_cmpge_ps(scaled1, _mm_set1_ps(0x100000000))));
}
void spu_interpreter::fast::CSFLT(SPUThread& spu, spu_opcode_t op)
void spu_interpreter_fast::CSFLT(SPUThread& spu, spu_opcode_t op)
{
spu.gpr[op.rt].vf = _mm_mul_ps(_mm_cvtepi32_ps(spu.gpr[op.ra].vi), g_spu_imm.scale[op.i8 - 155]);
}
void spu_interpreter::fast::CUFLT(SPUThread& spu, spu_opcode_t op)
void spu_interpreter_fast::CUFLT(SPUThread& spu, spu_opcode_t op)
{
const auto a = spu.gpr[op.ra].vi;
const auto fix = _mm_and_ps(_mm_castsi128_ps(_mm_srai_epi32(a, 31)), _mm_set1_ps(0x80000000));
@ -1265,17 +1252,17 @@ void spu_interpreter::MPYA(SPUThread& spu, spu_opcode_t op)
spu.gpr[op.rt4].vi = _mm_add_epi32(spu.gpr[op.rc].vi, _mm_madd_epi16(_mm_and_si128(spu.gpr[op.ra].vi, mask), _mm_and_si128(spu.gpr[op.rb].vi, mask)));
}
void spu_interpreter::fast::FNMS(SPUThread& spu, spu_opcode_t op)
void spu_interpreter_fast::FNMS(SPUThread& spu, spu_opcode_t op)
{
spu.gpr[op.rt4].vf = _mm_sub_ps(spu.gpr[op.rc].vf, _mm_mul_ps(spu.gpr[op.ra].vf, spu.gpr[op.rb].vf));
}
void spu_interpreter::fast::FMA(SPUThread& spu, spu_opcode_t op)
void spu_interpreter_fast::FMA(SPUThread& spu, spu_opcode_t op)
{
spu.gpr[op.rt4].vf = _mm_add_ps(_mm_mul_ps(spu.gpr[op.ra].vf, spu.gpr[op.rb].vf), spu.gpr[op.rc].vf);
}
void spu_interpreter::fast::FMS(SPUThread& spu, spu_opcode_t op)
void spu_interpreter_fast::FMS(SPUThread& spu, spu_opcode_t op)
{
spu.gpr[op.rt4].vf = _mm_sub_ps(_mm_mul_ps(spu.gpr[op.ra].vf, spu.gpr[op.rb].vf), spu.gpr[op.rc].vf);
}
@ -1360,7 +1347,7 @@ inline bool isdenormal(double x)
#endif
}
void spu_interpreter::precise::FREST(SPUThread& spu, spu_opcode_t op)
void spu_interpreter_precise::FREST(SPUThread& spu, spu_opcode_t op)
{
SetHostRoundingMode(FPSCR_RN_ZERO);
for (int i = 0; i < 4; i++)
@ -1380,7 +1367,7 @@ void spu_interpreter::precise::FREST(SPUThread& spu, spu_opcode_t op)
}
}
void spu_interpreter::precise::FRSQEST(SPUThread& spu, spu_opcode_t op)
void spu_interpreter_precise::FRSQEST(SPUThread& spu, spu_opcode_t op)
{
SetHostRoundingMode(FPSCR_RN_ZERO);
for (int i = 0; i < 4; i++)
@ -1400,7 +1387,7 @@ void spu_interpreter::precise::FRSQEST(SPUThread& spu, spu_opcode_t op)
}
}
void spu_interpreter::precise::FCGT(SPUThread& spu, spu_opcode_t op)
void spu_interpreter_precise::FCGT(SPUThread& spu, spu_opcode_t op)
{
for (int i = 0; i < 4; i++)
{
@ -1508,11 +1495,11 @@ static void FA_FS(SPUThread& spu, spu_opcode_t op, bool sub)
}
}
void spu_interpreter::precise::FA(SPUThread& spu, spu_opcode_t op) { FA_FS(spu, op, false); }
void spu_interpreter_precise::FA(SPUThread& spu, spu_opcode_t op) { FA_FS(spu, op, false); }
void spu_interpreter::precise::FS(SPUThread& spu, spu_opcode_t op) { FA_FS(spu, op, true); }
void spu_interpreter_precise::FS(SPUThread& spu, spu_opcode_t op) { FA_FS(spu, op, true); }
void spu_interpreter::precise::FM(SPUThread& spu, spu_opcode_t op)
void spu_interpreter_precise::FM(SPUThread& spu, spu_opcode_t op)
{
SetHostRoundingMode(FPSCR_RN_ZERO);
for (int w = 0; w < 4; w++)
@ -1585,7 +1572,7 @@ void spu_interpreter::precise::FM(SPUThread& spu, spu_opcode_t op)
}
}
void spu_interpreter::precise::FCMGT(SPUThread& spu, spu_opcode_t op)
void spu_interpreter_precise::FCMGT(SPUThread& spu, spu_opcode_t op)
{
for (int i = 0; i < 4; i++)
{
@ -1666,11 +1653,11 @@ static void DFASM(SPUThread& spu, spu_opcode_t op, DoubleOp operation)
}
}
void spu_interpreter::precise::DFA(SPUThread& spu, spu_opcode_t op) { DFASM(spu, op, DFASM_A); }
void spu_interpreter_precise::DFA(SPUThread& spu, spu_opcode_t op) { DFASM(spu, op, DFASM_A); }
void spu_interpreter::precise::DFS(SPUThread& spu, spu_opcode_t op) { DFASM(spu, op, DFASM_S); }
void spu_interpreter_precise::DFS(SPUThread& spu, spu_opcode_t op) { DFASM(spu, op, DFASM_S); }
void spu_interpreter::precise::DFM(SPUThread& spu, spu_opcode_t op) { DFASM(spu, op, DFASM_M); }
void spu_interpreter_precise::DFM(SPUThread& spu, spu_opcode_t op) { DFASM(spu, op, DFASM_M); }
static void DFMA(SPUThread& spu, spu_opcode_t op, bool neg, bool sub)
{
@ -1727,20 +1714,20 @@ static void DFMA(SPUThread& spu, spu_opcode_t op, bool neg, bool sub)
}
}
void spu_interpreter::precise::DFMA(SPUThread& spu, spu_opcode_t op) { DFMA(spu, op, false, false); }
void spu_interpreter_precise::DFMA(SPUThread& spu, spu_opcode_t op) { ::DFMA(spu, op, false, false); }
void spu_interpreter::precise::DFMS(SPUThread& spu, spu_opcode_t op) { DFMA(spu, op, false, true); }
void spu_interpreter_precise::DFMS(SPUThread& spu, spu_opcode_t op) { ::DFMA(spu, op, false, true); }
void spu_interpreter::precise::DFNMS(SPUThread& spu, spu_opcode_t op) { DFMA(spu, op, true, true); }
void spu_interpreter_precise::DFNMS(SPUThread& spu, spu_opcode_t op) { ::DFMA(spu, op, true, true); }
void spu_interpreter::precise::DFNMA(SPUThread& spu, spu_opcode_t op) { DFMA(spu, op, true, false); }
void spu_interpreter_precise::DFNMA(SPUThread& spu, spu_opcode_t op) { ::DFMA(spu, op, true, false); }
void spu_interpreter::precise::FSCRRD(SPUThread& spu, spu_opcode_t op)
void spu_interpreter_precise::FSCRRD(SPUThread& spu, spu_opcode_t op)
{
spu.fpscr.Read(spu.gpr[op.rt]);
}
void spu_interpreter::precise::FESD(SPUThread& spu, spu_opcode_t op)
void spu_interpreter_precise::FESD(SPUThread& spu, spu_opcode_t op)
{
for (int i = 0; i < 2; i++)
{
@ -1764,7 +1751,7 @@ void spu_interpreter::precise::FESD(SPUThread& spu, spu_opcode_t op)
}
}
void spu_interpreter::precise::FRDS(SPUThread& spu, spu_opcode_t op)
void spu_interpreter_precise::FRDS(SPUThread& spu, spu_opcode_t op)
{
for (int i = 0; i < 2; i++)
{
@ -1792,12 +1779,12 @@ void spu_interpreter::precise::FRDS(SPUThread& spu, spu_opcode_t op)
}
}
void spu_interpreter::precise::FSCRWR(SPUThread& spu, spu_opcode_t op)
void spu_interpreter_precise::FSCRWR(SPUThread& spu, spu_opcode_t op)
{
spu.fpscr.Write(spu.gpr[op.ra]);
}
void spu_interpreter::precise::FCEQ(SPUThread& spu, spu_opcode_t op)
void spu_interpreter_precise::FCEQ(SPUThread& spu, spu_opcode_t op)
{
for (int i = 0; i < 4; i++)
{
@ -1812,7 +1799,7 @@ void spu_interpreter::precise::FCEQ(SPUThread& spu, spu_opcode_t op)
}
}
void spu_interpreter::precise::FCMEQ(SPUThread& spu, spu_opcode_t op)
void spu_interpreter_precise::FCMEQ(SPUThread& spu, spu_opcode_t op)
{
for (int i = 0; i < 4; i++)
{
@ -1827,13 +1814,13 @@ void spu_interpreter::precise::FCMEQ(SPUThread& spu, spu_opcode_t op)
}
}
void spu_interpreter::precise::FI(SPUThread& spu, spu_opcode_t op)
void spu_interpreter_precise::FI(SPUThread& spu, spu_opcode_t op)
{
// TODO
spu.gpr[op.rt] = spu.gpr[op.rb];
}
void spu_interpreter::precise::CFLTS(SPUThread& spu, spu_opcode_t op)
void spu_interpreter_precise::CFLTS(SPUThread& spu, spu_opcode_t op)
{
const int scale = 173 - (op.i8 & 0xff); //unsigned immediate
for (int i = 0; i < 4; i++)
@ -1855,7 +1842,7 @@ void spu_interpreter::precise::CFLTS(SPUThread& spu, spu_opcode_t op)
}
}
void spu_interpreter::precise::CFLTU(SPUThread& spu, spu_opcode_t op)
void spu_interpreter_precise::CFLTU(SPUThread& spu, spu_opcode_t op)
{
const int scale = 173 - (op.i8 & 0xff); //unsigned immediate
for (int i = 0; i < 4; i++)
@ -1877,7 +1864,7 @@ void spu_interpreter::precise::CFLTU(SPUThread& spu, spu_opcode_t op)
}
}
void spu_interpreter::precise::CSFLT(SPUThread& spu, spu_opcode_t op)
void spu_interpreter_precise::CSFLT(SPUThread& spu, spu_opcode_t op)
{
SetHostRoundingMode(FPSCR_RN_ZERO);
const int scale = 155 - (op.i8 & 0xff); //unsigned immediate
@ -1900,7 +1887,7 @@ void spu_interpreter::precise::CSFLT(SPUThread& spu, spu_opcode_t op)
}
}
void spu_interpreter::precise::CUFLT(SPUThread& spu, spu_opcode_t op)
void spu_interpreter_precise::CUFLT(SPUThread& spu, spu_opcode_t op)
{
SetHostRoundingMode(FPSCR_RN_ZERO);
const int scale = 155 - (op.i8 & 0xff); //unsigned immediate
@ -2068,8 +2055,8 @@ static void FMA(SPUThread& spu, spu_opcode_t op, bool neg, bool sub)
}
}
void spu_interpreter::precise::FNMS(SPUThread& spu, spu_opcode_t op) { FMA(spu, op, true, true); }
void spu_interpreter_precise::FNMS(SPUThread& spu, spu_opcode_t op) { ::FMA(spu, op, true, true); }
void spu_interpreter::precise::FMA(SPUThread& spu, spu_opcode_t op) { FMA(spu, op, false, false); }
void spu_interpreter_precise::FMA(SPUThread& spu, spu_opcode_t op) { ::FMA(spu, op, false, false); }
void spu_interpreter::precise::FMS(SPUThread& spu, spu_opcode_t op) { FMA(spu, op, false, true); }
void spu_interpreter_precise::FMS(SPUThread& spu, spu_opcode_t op) { ::FMA(spu, op, false, true); }

View File

@ -4,256 +4,246 @@
class SPUThread;
using spu_inter_func_t = void(*)(SPUThread& spu, spu_opcode_t opcode);
using spu_inter_func_t = void(*)(SPUThread& spu, spu_opcode_t op);
namespace spu_interpreter
struct spu_interpreter
{
namespace fast
static void UNK(SPUThread&, spu_opcode_t);
static void set_interrupt_status(SPUThread&, spu_opcode_t);
static void STOP(SPUThread&, spu_opcode_t);
static void LNOP(SPUThread&, spu_opcode_t);
static void SYNC(SPUThread&, spu_opcode_t);
static void DSYNC(SPUThread&, spu_opcode_t);
static void MFSPR(SPUThread&, spu_opcode_t);
static void RDCH(SPUThread&, spu_opcode_t);
static void RCHCNT(SPUThread&, spu_opcode_t);
static void SF(SPUThread&, spu_opcode_t);
static void OR(SPUThread&, spu_opcode_t);
static void BG(SPUThread&, spu_opcode_t);
static void SFH(SPUThread&, spu_opcode_t);
static void NOR(SPUThread&, spu_opcode_t);
static void ABSDB(SPUThread&, spu_opcode_t);
static void ROT(SPUThread&, spu_opcode_t);
static void ROTM(SPUThread&, spu_opcode_t);
static void ROTMA(SPUThread&, spu_opcode_t);
static void SHL(SPUThread&, spu_opcode_t);
static void ROTH(SPUThread&, spu_opcode_t);
static void ROTHM(SPUThread&, spu_opcode_t);
static void ROTMAH(SPUThread&, spu_opcode_t);
static void SHLH(SPUThread&, spu_opcode_t);
static void ROTI(SPUThread&, spu_opcode_t);
static void ROTMI(SPUThread&, spu_opcode_t);
static void ROTMAI(SPUThread&, spu_opcode_t);
static void SHLI(SPUThread&, spu_opcode_t);
static void ROTHI(SPUThread&, spu_opcode_t);
static void ROTHMI(SPUThread&, spu_opcode_t);
static void ROTMAHI(SPUThread&, spu_opcode_t);
static void SHLHI(SPUThread&, spu_opcode_t);
static void A(SPUThread&, spu_opcode_t);
static void AND(SPUThread&, spu_opcode_t);
static void CG(SPUThread&, spu_opcode_t);
static void AH(SPUThread&, spu_opcode_t);
static void NAND(SPUThread&, spu_opcode_t);
static void AVGB(SPUThread&, spu_opcode_t);
static void MTSPR(SPUThread&, spu_opcode_t);
static void WRCH(SPUThread&, spu_opcode_t);
static void BIZ(SPUThread&, spu_opcode_t);
static void BINZ(SPUThread&, spu_opcode_t);
static void BIHZ(SPUThread&, spu_opcode_t);
static void BIHNZ(SPUThread&, spu_opcode_t);
static void STOPD(SPUThread&, spu_opcode_t);
static void STQX(SPUThread&, spu_opcode_t);
static void BI(SPUThread&, spu_opcode_t);
static void BISL(SPUThread&, spu_opcode_t);
static void IRET(SPUThread&, spu_opcode_t);
static void BISLED(SPUThread&, spu_opcode_t);
static void HBR(SPUThread&, spu_opcode_t);
static void GB(SPUThread&, spu_opcode_t);
static void GBH(SPUThread&, spu_opcode_t);
static void GBB(SPUThread&, spu_opcode_t);
static void FSM(SPUThread&, spu_opcode_t);
static void FSMH(SPUThread&, spu_opcode_t);
static void FSMB(SPUThread&, spu_opcode_t);
static void LQX(SPUThread&, spu_opcode_t);
static void ROTQBYBI(SPUThread&, spu_opcode_t);
static void ROTQMBYBI(SPUThread&, spu_opcode_t);
static void SHLQBYBI(SPUThread&, spu_opcode_t);
static void CBX(SPUThread&, spu_opcode_t);
static void CHX(SPUThread&, spu_opcode_t);
static void CWX(SPUThread&, spu_opcode_t);
static void CDX(SPUThread&, spu_opcode_t);
static void ROTQBI(SPUThread&, spu_opcode_t);
static void ROTQMBI(SPUThread&, spu_opcode_t);
static void SHLQBI(SPUThread&, spu_opcode_t);
static void ROTQBY(SPUThread&, spu_opcode_t);
static void ROTQMBY(SPUThread&, spu_opcode_t);
static void SHLQBY(SPUThread&, spu_opcode_t);
static void ORX(SPUThread&, spu_opcode_t);
static void CBD(SPUThread&, spu_opcode_t);
static void CHD(SPUThread&, spu_opcode_t);
static void CWD(SPUThread&, spu_opcode_t);
static void CDD(SPUThread&, spu_opcode_t);
static void ROTQBII(SPUThread&, spu_opcode_t);
static void ROTQMBII(SPUThread&, spu_opcode_t);
static void SHLQBII(SPUThread&, spu_opcode_t);
static void ROTQBYI(SPUThread&, spu_opcode_t);
static void ROTQMBYI(SPUThread&, spu_opcode_t);
static void SHLQBYI(SPUThread&, spu_opcode_t);
static void NOP(SPUThread&, spu_opcode_t);
static void CGT(SPUThread&, spu_opcode_t);
static void XOR(SPUThread&, spu_opcode_t);
static void CGTH(SPUThread&, spu_opcode_t);
static void EQV(SPUThread&, spu_opcode_t);
static void CGTB(SPUThread&, spu_opcode_t);
static void SUMB(SPUThread&, spu_opcode_t);
static void HGT(SPUThread&, spu_opcode_t);
static void CLZ(SPUThread&, spu_opcode_t);
static void XSWD(SPUThread&, spu_opcode_t);
static void XSHW(SPUThread&, spu_opcode_t);
static void CNTB(SPUThread&, spu_opcode_t);
static void XSBH(SPUThread&, spu_opcode_t);
static void CLGT(SPUThread&, spu_opcode_t);
static void ANDC(SPUThread&, spu_opcode_t);
static void CLGTH(SPUThread&, spu_opcode_t);
static void ORC(SPUThread&, spu_opcode_t);
static void CLGTB(SPUThread&, spu_opcode_t);
static void HLGT(SPUThread&, spu_opcode_t);
static void CEQ(SPUThread&, spu_opcode_t);
static void MPYHHU(SPUThread&, spu_opcode_t);
static void ADDX(SPUThread&, spu_opcode_t);
static void SFX(SPUThread&, spu_opcode_t);
static void CGX(SPUThread&, spu_opcode_t);
static void BGX(SPUThread&, spu_opcode_t);
static void MPYHHA(SPUThread&, spu_opcode_t);
static void MPYHHAU(SPUThread&, spu_opcode_t);
static void MPY(SPUThread&, spu_opcode_t);
static void MPYH(SPUThread&, spu_opcode_t);
static void MPYHH(SPUThread&, spu_opcode_t);
static void MPYS(SPUThread&, spu_opcode_t);
static void CEQH(SPUThread&, spu_opcode_t);
static void MPYU(SPUThread&, spu_opcode_t);
static void CEQB(SPUThread&, spu_opcode_t);
static void HEQ(SPUThread&, spu_opcode_t);
static void BRZ(SPUThread&, spu_opcode_t);
static void STQA(SPUThread&, spu_opcode_t);
static void BRNZ(SPUThread&, spu_opcode_t);
static void BRHZ(SPUThread&, spu_opcode_t);
static void BRHNZ(SPUThread&, spu_opcode_t);
static void STQR(SPUThread&, spu_opcode_t);
static void BRA(SPUThread&, spu_opcode_t);
static void LQA(SPUThread&, spu_opcode_t);
static void BRASL(SPUThread&, spu_opcode_t);
static void BR(SPUThread&, spu_opcode_t);
static void FSMBI(SPUThread&, spu_opcode_t);
static void BRSL(SPUThread&, spu_opcode_t);
static void LQR(SPUThread&, spu_opcode_t);
static void IL(SPUThread&, spu_opcode_t);
static void ILHU(SPUThread&, spu_opcode_t);
static void ILH(SPUThread&, spu_opcode_t);
static void IOHL(SPUThread&, spu_opcode_t);
static void ORI(SPUThread&, spu_opcode_t);
static void ORHI(SPUThread&, spu_opcode_t);
static void ORBI(SPUThread&, spu_opcode_t);
static void SFI(SPUThread&, spu_opcode_t);
static void SFHI(SPUThread&, spu_opcode_t);
static void ANDI(SPUThread&, spu_opcode_t);
static void ANDHI(SPUThread&, spu_opcode_t);
static void ANDBI(SPUThread&, spu_opcode_t);
static void AI(SPUThread&, spu_opcode_t);
static void AHI(SPUThread&, spu_opcode_t);
static void STQD(SPUThread&, spu_opcode_t);
static void LQD(SPUThread&, spu_opcode_t);
static void XORI(SPUThread&, spu_opcode_t);
static void XORHI(SPUThread&, spu_opcode_t);
static void XORBI(SPUThread&, spu_opcode_t);
static void CGTI(SPUThread&, spu_opcode_t);
static void CGTHI(SPUThread&, spu_opcode_t);
static void CGTBI(SPUThread&, spu_opcode_t);
static void HGTI(SPUThread&, spu_opcode_t);
static void CLGTI(SPUThread&, spu_opcode_t);
static void CLGTHI(SPUThread&, spu_opcode_t);
static void CLGTBI(SPUThread&, spu_opcode_t);
static void HLGTI(SPUThread&, spu_opcode_t);
static void MPYI(SPUThread&, spu_opcode_t);
static void MPYUI(SPUThread&, spu_opcode_t);
static void CEQI(SPUThread&, spu_opcode_t);
static void CEQHI(SPUThread&, spu_opcode_t);
static void CEQBI(SPUThread&, spu_opcode_t);
static void HEQI(SPUThread&, spu_opcode_t);
static void HBRA(SPUThread&, spu_opcode_t);
static void HBRR(SPUThread&, spu_opcode_t);
static void ILA(SPUThread&, spu_opcode_t);
static void SELB(SPUThread&, spu_opcode_t);
static void SHUFB(SPUThread&, spu_opcode_t);
static void MPYA(SPUThread&, spu_opcode_t);
static void DFCGT(SPUThread&, spu_opcode_t);
static void DFCMGT(SPUThread&, spu_opcode_t);
static void DFTSV(SPUThread&, spu_opcode_t);
static void DFCEQ(SPUThread&, spu_opcode_t);
static void DFCMEQ(SPUThread&, spu_opcode_t);
};
struct spu_interpreter_fast final : spu_interpreter
{
extern const spu_opcode_table_t<spu_inter_func_t> g_spu_opcode_table;
}
static void FREST(SPUThread&, spu_opcode_t);
static void FRSQEST(SPUThread&, spu_opcode_t);
static void FCGT(SPUThread&, spu_opcode_t);
static void FA(SPUThread&, spu_opcode_t);
static void FS(SPUThread&, spu_opcode_t);
static void FM(SPUThread&, spu_opcode_t);
static void FCMGT(SPUThread&, spu_opcode_t);
static void DFA(SPUThread&, spu_opcode_t);
static void DFS(SPUThread&, spu_opcode_t);
static void DFM(SPUThread&, spu_opcode_t);
static void DFMA(SPUThread&, spu_opcode_t);
static void DFMS(SPUThread&, spu_opcode_t);
static void DFNMS(SPUThread&, spu_opcode_t);
static void DFNMA(SPUThread&, spu_opcode_t);
static void FSCRRD(SPUThread&, spu_opcode_t);
static void FESD(SPUThread&, spu_opcode_t);
static void FRDS(SPUThread&, spu_opcode_t);
static void FSCRWR(SPUThread&, spu_opcode_t);
static void FCEQ(SPUThread&, spu_opcode_t);
static void FCMEQ(SPUThread&, spu_opcode_t);
static void FI(SPUThread&, spu_opcode_t);
static void CFLTS(SPUThread&, spu_opcode_t);
static void CFLTU(SPUThread&, spu_opcode_t);
static void CSFLT(SPUThread&, spu_opcode_t);
static void CUFLT(SPUThread&, spu_opcode_t);
static void FNMS(SPUThread&, spu_opcode_t);
static void FMA(SPUThread&, spu_opcode_t);
static void FMS(SPUThread&, spu_opcode_t);
};
namespace precise
struct spu_interpreter_precise final : spu_interpreter
{
extern const spu_opcode_table_t<spu_inter_func_t> g_spu_opcode_table;
}
void default_function(SPUThread& spu, spu_opcode_t op);
void set_interrupt_status(SPUThread& spu, spu_opcode_t op);
void STOP(SPUThread& spu, spu_opcode_t op);
void LNOP(SPUThread& spu, spu_opcode_t op);
void SYNC(SPUThread& spu, spu_opcode_t op);
void DSYNC(SPUThread& spu, spu_opcode_t op);
void MFSPR(SPUThread& spu, spu_opcode_t op);
void RDCH(SPUThread& spu, spu_opcode_t op);
void RCHCNT(SPUThread& spu, spu_opcode_t op);
void SF(SPUThread& spu, spu_opcode_t op);
void OR(SPUThread& spu, spu_opcode_t op);
void BG(SPUThread& spu, spu_opcode_t op);
void SFH(SPUThread& spu, spu_opcode_t op);
void NOR(SPUThread& spu, spu_opcode_t op);
void ABSDB(SPUThread& spu, spu_opcode_t op);
void ROT(SPUThread& spu, spu_opcode_t op);
void ROTM(SPUThread& spu, spu_opcode_t op);
void ROTMA(SPUThread& spu, spu_opcode_t op);
void SHL(SPUThread& spu, spu_opcode_t op);
void ROTH(SPUThread& spu, spu_opcode_t op);
void ROTHM(SPUThread& spu, spu_opcode_t op);
void ROTMAH(SPUThread& spu, spu_opcode_t op);
void SHLH(SPUThread& spu, spu_opcode_t op);
void ROTI(SPUThread& spu, spu_opcode_t op);
void ROTMI(SPUThread& spu, spu_opcode_t op);
void ROTMAI(SPUThread& spu, spu_opcode_t op);
void SHLI(SPUThread& spu, spu_opcode_t op);
void ROTHI(SPUThread& spu, spu_opcode_t op);
void ROTHMI(SPUThread& spu, spu_opcode_t op);
void ROTMAHI(SPUThread& spu, spu_opcode_t op);
void SHLHI(SPUThread& spu, spu_opcode_t op);
void A(SPUThread& spu, spu_opcode_t op);
void AND(SPUThread& spu, spu_opcode_t op);
void CG(SPUThread& spu, spu_opcode_t op);
void AH(SPUThread& spu, spu_opcode_t op);
void NAND(SPUThread& spu, spu_opcode_t op);
void AVGB(SPUThread& spu, spu_opcode_t op);
void MTSPR(SPUThread& spu, spu_opcode_t op);
void WRCH(SPUThread& spu, spu_opcode_t op);
void BIZ(SPUThread& spu, spu_opcode_t op);
void BINZ(SPUThread& spu, spu_opcode_t op);
void BIHZ(SPUThread& spu, spu_opcode_t op);
void BIHNZ(SPUThread& spu, spu_opcode_t op);
void STOPD(SPUThread& spu, spu_opcode_t op);
void STQX(SPUThread& spu, spu_opcode_t op);
void BI(SPUThread& spu, spu_opcode_t op);
void BISL(SPUThread& spu, spu_opcode_t op);
void IRET(SPUThread& spu, spu_opcode_t op);
void BISLED(SPUThread& spu, spu_opcode_t op);
void HBR(SPUThread& spu, spu_opcode_t op);
void GB(SPUThread& spu, spu_opcode_t op);
void GBH(SPUThread& spu, spu_opcode_t op);
void GBB(SPUThread& spu, spu_opcode_t op);
void FSM(SPUThread& spu, spu_opcode_t op);
void FSMH(SPUThread& spu, spu_opcode_t op);
void FSMB(SPUThread& spu, spu_opcode_t op);
void LQX(SPUThread& spu, spu_opcode_t op);
void ROTQBYBI(SPUThread& spu, spu_opcode_t op);
void ROTQMBYBI(SPUThread& spu, spu_opcode_t op);
void SHLQBYBI(SPUThread& spu, spu_opcode_t op);
void CBX(SPUThread& spu, spu_opcode_t op);
void CHX(SPUThread& spu, spu_opcode_t op);
void CWX(SPUThread& spu, spu_opcode_t op);
void CDX(SPUThread& spu, spu_opcode_t op);
void ROTQBI(SPUThread& spu, spu_opcode_t op);
void ROTQMBI(SPUThread& spu, spu_opcode_t op);
void SHLQBI(SPUThread& spu, spu_opcode_t op);
void ROTQBY(SPUThread& spu, spu_opcode_t op);
void ROTQMBY(SPUThread& spu, spu_opcode_t op);
void SHLQBY(SPUThread& spu, spu_opcode_t op);
void ORX(SPUThread& spu, spu_opcode_t op);
void CBD(SPUThread& spu, spu_opcode_t op);
void CHD(SPUThread& spu, spu_opcode_t op);
void CWD(SPUThread& spu, spu_opcode_t op);
void CDD(SPUThread& spu, spu_opcode_t op);
void ROTQBII(SPUThread& spu, spu_opcode_t op);
void ROTQMBII(SPUThread& spu, spu_opcode_t op);
void SHLQBII(SPUThread& spu, spu_opcode_t op);
void ROTQBYI(SPUThread& spu, spu_opcode_t op);
void ROTQMBYI(SPUThread& spu, spu_opcode_t op);
void SHLQBYI(SPUThread& spu, spu_opcode_t op);
void NOP(SPUThread& spu, spu_opcode_t op);
void CGT(SPUThread& spu, spu_opcode_t op);
void XOR(SPUThread& spu, spu_opcode_t op);
void CGTH(SPUThread& spu, spu_opcode_t op);
void EQV(SPUThread& spu, spu_opcode_t op);
void CGTB(SPUThread& spu, spu_opcode_t op);
void SUMB(SPUThread& spu, spu_opcode_t op);
void HGT(SPUThread& spu, spu_opcode_t op);
void CLZ(SPUThread& spu, spu_opcode_t op);
void XSWD(SPUThread& spu, spu_opcode_t op);
void XSHW(SPUThread& spu, spu_opcode_t op);
void CNTB(SPUThread& spu, spu_opcode_t op);
void XSBH(SPUThread& spu, spu_opcode_t op);
void CLGT(SPUThread& spu, spu_opcode_t op);
void ANDC(SPUThread& spu, spu_opcode_t op);
void CLGTH(SPUThread& spu, spu_opcode_t op);
void ORC(SPUThread& spu, spu_opcode_t op);
void CLGTB(SPUThread& spu, spu_opcode_t op);
void HLGT(SPUThread& spu, spu_opcode_t op);
void CEQ(SPUThread& spu, spu_opcode_t op);
void MPYHHU(SPUThread& spu, spu_opcode_t op);
void ADDX(SPUThread& spu, spu_opcode_t op);
void SFX(SPUThread& spu, spu_opcode_t op);
void CGX(SPUThread& spu, spu_opcode_t op);
void BGX(SPUThread& spu, spu_opcode_t op);
void MPYHHA(SPUThread& spu, spu_opcode_t op);
void MPYHHAU(SPUThread& spu, spu_opcode_t op);
void MPY(SPUThread& spu, spu_opcode_t op);
void MPYH(SPUThread& spu, spu_opcode_t op);
void MPYHH(SPUThread& spu, spu_opcode_t op);
void MPYS(SPUThread& spu, spu_opcode_t op);
void CEQH(SPUThread& spu, spu_opcode_t op);
void MPYU(SPUThread& spu, spu_opcode_t op);
void CEQB(SPUThread& spu, spu_opcode_t op);
void HEQ(SPUThread& spu, spu_opcode_t op);
void BRZ(SPUThread& spu, spu_opcode_t op);
void STQA(SPUThread& spu, spu_opcode_t op);
void BRNZ(SPUThread& spu, spu_opcode_t op);
void BRHZ(SPUThread& spu, spu_opcode_t op);
void BRHNZ(SPUThread& spu, spu_opcode_t op);
void STQR(SPUThread& spu, spu_opcode_t op);
void BRA(SPUThread& spu, spu_opcode_t op);
void LQA(SPUThread& spu, spu_opcode_t op);
void BRASL(SPUThread& spu, spu_opcode_t op);
void BR(SPUThread& spu, spu_opcode_t op);
void FSMBI(SPUThread& spu, spu_opcode_t op);
void BRSL(SPUThread& spu, spu_opcode_t op);
void LQR(SPUThread& spu, spu_opcode_t op);
void IL(SPUThread& spu, spu_opcode_t op);
void ILHU(SPUThread& spu, spu_opcode_t op);
void ILH(SPUThread& spu, spu_opcode_t op);
void IOHL(SPUThread& spu, spu_opcode_t op);
void ORI(SPUThread& spu, spu_opcode_t op);
void ORHI(SPUThread& spu, spu_opcode_t op);
void ORBI(SPUThread& spu, spu_opcode_t op);
void SFI(SPUThread& spu, spu_opcode_t op);
void SFHI(SPUThread& spu, spu_opcode_t op);
void ANDI(SPUThread& spu, spu_opcode_t op);
void ANDHI(SPUThread& spu, spu_opcode_t op);
void ANDBI(SPUThread& spu, spu_opcode_t op);
void AI(SPUThread& spu, spu_opcode_t op);
void AHI(SPUThread& spu, spu_opcode_t op);
void STQD(SPUThread& spu, spu_opcode_t op);
void LQD(SPUThread& spu, spu_opcode_t op);
void XORI(SPUThread& spu, spu_opcode_t op);
void XORHI(SPUThread& spu, spu_opcode_t op);
void XORBI(SPUThread& spu, spu_opcode_t op);
void CGTI(SPUThread& spu, spu_opcode_t op);
void CGTHI(SPUThread& spu, spu_opcode_t op);
void CGTBI(SPUThread& spu, spu_opcode_t op);
void HGTI(SPUThread& spu, spu_opcode_t op);
void CLGTI(SPUThread& spu, spu_opcode_t op);
void CLGTHI(SPUThread& spu, spu_opcode_t op);
void CLGTBI(SPUThread& spu, spu_opcode_t op);
void HLGTI(SPUThread& spu, spu_opcode_t op);
void MPYI(SPUThread& spu, spu_opcode_t op);
void MPYUI(SPUThread& spu, spu_opcode_t op);
void CEQI(SPUThread& spu, spu_opcode_t op);
void CEQHI(SPUThread& spu, spu_opcode_t op);
void CEQBI(SPUThread& spu, spu_opcode_t op);
void HEQI(SPUThread& spu, spu_opcode_t op);
void HBRA(SPUThread& spu, spu_opcode_t op);
void HBRR(SPUThread& spu, spu_opcode_t op);
void ILA(SPUThread& spu, spu_opcode_t op);
void SELB(SPUThread& spu, spu_opcode_t op);
void SHUFB(SPUThread& spu, spu_opcode_t op);
void MPYA(SPUThread& spu, spu_opcode_t op);
void DFCGT(SPUThread& spu, spu_opcode_t op);
void DFCMGT(SPUThread& spu, spu_opcode_t op);
void DFTSV(SPUThread& spu, spu_opcode_t op);
void DFCEQ(SPUThread& spu, spu_opcode_t op);
void DFCMEQ(SPUThread& spu, spu_opcode_t op);
namespace fast
{
void FREST(SPUThread& spu, spu_opcode_t op);
void FRSQEST(SPUThread& spu, spu_opcode_t op);
void FCGT(SPUThread& spu, spu_opcode_t op);
void FA(SPUThread& spu, spu_opcode_t op);
void FS(SPUThread& spu, spu_opcode_t op);
void FM(SPUThread& spu, spu_opcode_t op);
void FCMGT(SPUThread& spu, spu_opcode_t op);
void DFA(SPUThread& spu, spu_opcode_t op);
void DFS(SPUThread& spu, spu_opcode_t op);
void DFM(SPUThread& spu, spu_opcode_t op);
void DFMA(SPUThread& spu, spu_opcode_t op);
void DFMS(SPUThread& spu, spu_opcode_t op);
void DFNMS(SPUThread& spu, spu_opcode_t op);
void DFNMA(SPUThread& spu, spu_opcode_t op);
void FSCRRD(SPUThread& spu, spu_opcode_t op);
void FESD(SPUThread& spu, spu_opcode_t op);
void FRDS(SPUThread& spu, spu_opcode_t op);
void FSCRWR(SPUThread& spu, spu_opcode_t op);
void FCEQ(SPUThread& spu, spu_opcode_t op);
void FCMEQ(SPUThread& spu, spu_opcode_t op);
void FI(SPUThread& spu, spu_opcode_t op);
void CFLTS(SPUThread& spu, spu_opcode_t op);
void CFLTU(SPUThread& spu, spu_opcode_t op);
void CSFLT(SPUThread& spu, spu_opcode_t op);
void CUFLT(SPUThread& spu, spu_opcode_t op);
void FNMS(SPUThread& spu, spu_opcode_t op);
void FMA(SPUThread& spu, spu_opcode_t op);
void FMS(SPUThread& spu, spu_opcode_t op);
}
namespace precise
{
void FREST(SPUThread& spu, spu_opcode_t op);
void FRSQEST(SPUThread& spu, spu_opcode_t op);
void FCGT(SPUThread& spu, spu_opcode_t op);
void FA(SPUThread& spu, spu_opcode_t op);
void FS(SPUThread& spu, spu_opcode_t op);
void FM(SPUThread& spu, spu_opcode_t op);
void FCMGT(SPUThread& spu, spu_opcode_t op);
void DFA(SPUThread& spu, spu_opcode_t op);
void DFS(SPUThread& spu, spu_opcode_t op);
void DFM(SPUThread& spu, spu_opcode_t op);
void DFMA(SPUThread& spu, spu_opcode_t op);
void DFMS(SPUThread& spu, spu_opcode_t op);
void DFNMS(SPUThread& spu, spu_opcode_t op);
void DFNMA(SPUThread& spu, spu_opcode_t op);
void FSCRRD(SPUThread& spu, spu_opcode_t op);
void FESD(SPUThread& spu, spu_opcode_t op);
void FRDS(SPUThread& spu, spu_opcode_t op);
void FSCRWR(SPUThread& spu, spu_opcode_t op);
void FCEQ(SPUThread& spu, spu_opcode_t op);
void FCMEQ(SPUThread& spu, spu_opcode_t op);
void FI(SPUThread& spu, spu_opcode_t op);
void CFLTS(SPUThread& spu, spu_opcode_t op);
void CFLTU(SPUThread& spu, spu_opcode_t op);
void CSFLT(SPUThread& spu, spu_opcode_t op);
void CUFLT(SPUThread& spu, spu_opcode_t op);
void FNMS(SPUThread& spu, spu_opcode_t op);
void FMA(SPUThread& spu, spu_opcode_t op);
void FMS(SPUThread& spu, spu_opcode_t op);
}
}
static void FREST(SPUThread&, spu_opcode_t);
static void FRSQEST(SPUThread&, spu_opcode_t);
static void FCGT(SPUThread&, spu_opcode_t);
static void FA(SPUThread&, spu_opcode_t);
static void FS(SPUThread&, spu_opcode_t);
static void FM(SPUThread&, spu_opcode_t);
static void FCMGT(SPUThread&, spu_opcode_t);
static void DFA(SPUThread&, spu_opcode_t);
static void DFS(SPUThread&, spu_opcode_t);
static void DFM(SPUThread&, spu_opcode_t);
static void DFMA(SPUThread&, spu_opcode_t);
static void DFMS(SPUThread&, spu_opcode_t);
static void DFNMS(SPUThread&, spu_opcode_t);
static void DFNMA(SPUThread&, spu_opcode_t);
static void FSCRRD(SPUThread&, spu_opcode_t);
static void FESD(SPUThread&, spu_opcode_t);
static void FRDS(SPUThread&, spu_opcode_t);
static void FSCRWR(SPUThread&, spu_opcode_t);
static void FCEQ(SPUThread&, spu_opcode_t);
static void FCMEQ(SPUThread&, spu_opcode_t);
static void FI(SPUThread&, spu_opcode_t);
static void CFLTS(SPUThread&, spu_opcode_t);
static void CFLTU(SPUThread&, spu_opcode_t);
static void CSFLT(SPUThread&, spu_opcode_t);
static void CUFLT(SPUThread&, spu_opcode_t);
static void FNMS(SPUThread&, spu_opcode_t);
static void FMA(SPUThread&, spu_opcode_t);
static void FMS(SPUThread&, spu_opcode_t);
};

View File

@ -1,5 +1,7 @@
#pragma once
#include "../../../Utilities/BitField.h"
union spu_opcode_t
{
u32 opcode;
@ -23,246 +25,6 @@ union spu_opcode_t
bf_t<u32, 7, 18> i18; // 7..24
};
#define DEFINE_SPU_OPCODES(ns) { \
{ 10, 0x0, ns STOP }, \
{ 10, 0x1, ns LNOP }, \
{ 10, 0x2, ns SYNC }, \
{ 10, 0x3, ns DSYNC }, \
{ 10, 0xc, ns MFSPR }, \
{ 10, 0xd, ns RDCH }, \
{ 10, 0xf, ns RCHCNT }, \
{ 10, 0x40, ns SF }, \
{ 10, 0x41, ns OR }, \
{ 10, 0x42, ns BG }, \
{ 10, 0x48, ns SFH }, \
{ 10, 0x49, ns NOR }, \
{ 10, 0x53, ns ABSDB }, \
{ 10, 0x58, ns ROT }, \
{ 10, 0x59, ns ROTM }, \
{ 10, 0x5a, ns ROTMA }, \
{ 10, 0x5b, ns SHL }, \
{ 10, 0x5c, ns ROTH }, \
{ 10, 0x5d, ns ROTHM }, \
{ 10, 0x5e, ns ROTMAH }, \
{ 10, 0x5f, ns SHLH }, \
{ 10, 0x78, ns ROTI }, \
{ 10, 0x79, ns ROTMI }, \
{ 10, 0x7a, ns ROTMAI }, \
{ 10, 0x7b, ns SHLI }, \
{ 10, 0x7c, ns ROTHI }, \
{ 10, 0x7d, ns ROTHMI }, \
{ 10, 0x7e, ns ROTMAHI }, \
{ 10, 0x7f, ns SHLHI }, \
{ 10, 0xc0, ns A }, \
{ 10, 0xc1, ns AND }, \
{ 10, 0xc2, ns CG }, \
{ 10, 0xc8, ns AH }, \
{ 10, 0xc9, ns NAND }, \
{ 10, 0xd3, ns AVGB }, \
{ 10, 0x10c, ns MTSPR }, \
{ 10, 0x10d, ns WRCH }, \
{ 10, 0x128, ns BIZ }, \
{ 10, 0x129, ns BINZ }, \
{ 10, 0x12a, ns BIHZ }, \
{ 10, 0x12b, ns BIHNZ }, \
{ 10, 0x140, ns STOPD }, \
{ 10, 0x144, ns STQX }, \
{ 10, 0x1a8, ns BI }, \
{ 10, 0x1a9, ns BISL }, \
{ 10, 0x1aa, ns IRET }, \
{ 10, 0x1ab, ns BISLED }, \
{ 10, 0x1ac, ns HBR }, \
{ 10, 0x1b0, ns GB }, \
{ 10, 0x1b1, ns GBH }, \
{ 10, 0x1b2, ns GBB }, \
{ 10, 0x1b4, ns FSM }, \
{ 10, 0x1b5, ns FSMH }, \
{ 10, 0x1b6, ns FSMB }, \
{ 10, 0x1b8, ns FREST }, \
{ 10, 0x1b9, ns FRSQEST }, \
{ 10, 0x1c4, ns LQX }, \
{ 10, 0x1cc, ns ROTQBYBI }, \
{ 10, 0x1cd, ns ROTQMBYBI }, \
{ 10, 0x1cf, ns SHLQBYBI }, \
{ 10, 0x1d4, ns CBX }, \
{ 10, 0x1d5, ns CHX }, \
{ 10, 0x1d6, ns CWX }, \
{ 10, 0x1d7, ns CDX }, \
{ 10, 0x1d8, ns ROTQBI }, \
{ 10, 0x1d9, ns ROTQMBI }, \
{ 10, 0x1db, ns SHLQBI }, \
{ 10, 0x1dc, ns ROTQBY }, \
{ 10, 0x1dd, ns ROTQMBY }, \
{ 10, 0x1df, ns SHLQBY }, \
{ 10, 0x1f0, ns ORX }, \
{ 10, 0x1f4, ns CBD }, \
{ 10, 0x1f5, ns CHD }, \
{ 10, 0x1f6, ns CWD }, \
{ 10, 0x1f7, ns CDD }, \
{ 10, 0x1f8, ns ROTQBII }, \
{ 10, 0x1f9, ns ROTQMBII }, \
{ 10, 0x1fb, ns SHLQBII }, \
{ 10, 0x1fc, ns ROTQBYI }, \
{ 10, 0x1fd, ns ROTQMBYI }, \
{ 10, 0x1ff, ns SHLQBYI }, \
{ 10, 0x201, ns NOP }, \
{ 10, 0x240, ns CGT }, \
{ 10, 0x241, ns XOR }, \
{ 10, 0x248, ns CGTH }, \
{ 10, 0x249, ns EQV }, \
{ 10, 0x250, ns CGTB }, \
{ 10, 0x253, ns SUMB }, \
{ 10, 0x258, ns HGT }, \
{ 10, 0x2a5, ns CLZ }, \
{ 10, 0x2a6, ns XSWD }, \
{ 10, 0x2ae, ns XSHW }, \
{ 10, 0x2b4, ns CNTB }, \
{ 10, 0x2b6, ns XSBH }, \
{ 10, 0x2c0, ns CLGT }, \
{ 10, 0x2c1, ns ANDC }, \
{ 10, 0x2c2, ns FCGT }, \
{ 10, 0x2c3, ns DFCGT }, \
{ 10, 0x2c4, ns FA }, \
{ 10, 0x2c5, ns FS }, \
{ 10, 0x2c6, ns FM }, \
{ 10, 0x2c8, ns CLGTH }, \
{ 10, 0x2c9, ns ORC }, \
{ 10, 0x2ca, ns FCMGT }, \
{ 10, 0x2cb, ns DFCMGT }, \
{ 10, 0x2cc, ns DFA }, \
{ 10, 0x2cd, ns DFS }, \
{ 10, 0x2ce, ns DFM }, \
{ 10, 0x2d0, ns CLGTB }, \
{ 10, 0x2d8, ns HLGT }, \
{ 10, 0x35c, ns DFMA }, \
{ 10, 0x35d, ns DFMS }, \
{ 10, 0x35e, ns DFNMS }, \
{ 10, 0x35f, ns DFNMA }, \
{ 10, 0x3c0, ns CEQ }, \
{ 10, 0x3ce, ns MPYHHU }, \
{ 10, 0x340, ns ADDX }, \
{ 10, 0x341, ns SFX }, \
{ 10, 0x342, ns CGX }, \
{ 10, 0x343, ns BGX }, \
{ 10, 0x346, ns MPYHHA }, \
{ 10, 0x34e, ns MPYHHAU }, \
{ 10, 0x398, ns FSCRRD }, \
{ 10, 0x3b8, ns FESD }, \
{ 10, 0x3b9, ns FRDS }, \
{ 10, 0x3ba, ns FSCRWR }, \
{ 10, 0x3bf, ns DFTSV }, \
{ 10, 0x3c2, ns FCEQ }, \
{ 10, 0x3c3, ns DFCEQ }, \
{ 10, 0x3c4, ns MPY }, \
{ 10, 0x3c5, ns MPYH }, \
{ 10, 0x3c6, ns MPYHH }, \
{ 10, 0x3c7, ns MPYS }, \
{ 10, 0x3c8, ns CEQH }, \
{ 10, 0x3ca, ns FCMEQ }, \
{ 10, 0x3cb, ns DFCMEQ }, \
{ 10, 0x3cc, ns MPYU }, \
{ 10, 0x3d0, ns CEQB }, \
{ 10, 0x3d4, ns FI }, \
{ 10, 0x3d8, ns HEQ }, \
{ 9, 0x1d8, ns CFLTS }, \
{ 9, 0x1d9, ns CFLTU }, \
{ 9, 0x1da, ns CSFLT }, \
{ 9, 0x1db, ns CUFLT }, \
{ 8, 0x40, ns BRZ }, \
{ 8, 0x41, ns STQA }, \
{ 8, 0x42, ns BRNZ }, \
{ 8, 0x44, ns BRHZ }, \
{ 8, 0x46, ns BRHNZ }, \
{ 8, 0x47, ns STQR }, \
{ 8, 0x60, ns BRA }, \
{ 8, 0x61, ns LQA }, \
{ 8, 0x62, ns BRASL }, \
{ 8, 0x64, ns BR }, \
{ 8, 0x65, ns FSMBI }, \
{ 8, 0x66, ns BRSL }, \
{ 8, 0x67, ns LQR }, \
{ 8, 0x81, ns IL }, \
{ 8, 0x82, ns ILHU }, \
{ 8, 0x83, ns ILH }, \
{ 8, 0xc1, ns IOHL }, \
{ 7, 0x4, ns ORI }, \
{ 7, 0x5, ns ORHI }, \
{ 7, 0x6, ns ORBI }, \
{ 7, 0xc, ns SFI }, \
{ 7, 0xd, ns SFHI }, \
{ 7, 0x14, ns ANDI }, \
{ 7, 0x15, ns ANDHI }, \
{ 7, 0x16, ns ANDBI }, \
{ 7, 0x1c, ns AI }, \
{ 7, 0x1d, ns AHI }, \
{ 7, 0x24, ns STQD }, \
{ 7, 0x34, ns LQD }, \
{ 7, 0x44, ns XORI }, \
{ 7, 0x45, ns XORHI }, \
{ 7, 0x46, ns XORBI }, \
{ 7, 0x4c, ns CGTI }, \
{ 7, 0x4d, ns CGTHI }, \
{ 7, 0x4e, ns CGTBI }, \
{ 7, 0x4f, ns HGTI }, \
{ 7, 0x5c, ns CLGTI }, \
{ 7, 0x5d, ns CLGTHI }, \
{ 7, 0x5e, ns CLGTBI }, \
{ 7, 0x5f, ns HLGTI }, \
{ 7, 0x74, ns MPYI }, \
{ 7, 0x75, ns MPYUI }, \
{ 7, 0x7c, ns CEQI }, \
{ 7, 0x7d, ns CEQHI }, \
{ 7, 0x7e, ns CEQBI }, \
{ 7, 0x7f, ns HEQI }, \
{ 6, 0x8, ns HBRA }, \
{ 6, 0x9, ns HBRR }, \
{ 6, 0x21, ns ILA }, \
{ 3, 0x8, ns SELB }, \
{ 3, 0xb, ns SHUFB }, \
{ 3, 0xc, ns MPYA }, \
{ 3, 0xd, ns FNMS }, \
{ 3, 0xe, ns FMA }, \
{ 3, 0xf, ns FMS }, \
}
template<typename T> class spu_opcode_table_t
{
std::array<T, 2048> m_data;
struct opcode_entry_t
{
u32 group;
u32 value;
T pointer;
};
public:
// opcode table initialization (TODO: optimize it a bit)
spu_opcode_table_t(std::initializer_list<opcode_entry_t> opcodes, T default_value = {})
{
for (u32 i = 0; i < 2048; i++)
{
m_data[i] = default_value;
for (auto& op : opcodes)
{
if (((i << 21) & (INT_MIN >> op.group)) == (op.value << (31 - op.group)))
{
m_data[i] = op.pointer;
break;
}
}
}
}
// access opcode table
T operator [](u32 opcode_data) const
{
// the whole decoding process is shifting opcode data
return m_data[opcode_data >> 21];
}
};
inline u32 spu_branch_target(u32 pc, u32 imm = 0)
{
return (pc + (imm << 2)) & 0x3fffc;
@ -272,3 +34,250 @@ inline u32 spu_ls_target(u32 pc, u32 imm = 0)
{
return (pc + (imm << 2)) & 0x3fff0;
}
static u32 spu_decode(u32 inst)
{
return inst >> 21;
}
// SPU decoder object. D provides functions. T is function pointer type returned.
template<typename D, typename T = decltype(&D::UNK)>
class spu_decoder
{
// Fast lookup table
std::array<T, 2048> m_table;
struct instruction_info
{
u32 magn; // Count = 2 ^ magn
u32 value;
T pointer;
};
public:
spu_decoder()
{
const std::initializer_list<instruction_info> instructions
{
{ 0, 0x0, &D::STOP },
{ 0, 0x1, &D::LNOP },
{ 0, 0x2, &D::SYNC },
{ 0, 0x3, &D::DSYNC },
{ 0, 0xc, &D::MFSPR },
{ 0, 0xd, &D::RDCH },
{ 0, 0xf, &D::RCHCNT },
{ 0, 0x40, &D::SF },
{ 0, 0x41, &D::OR },
{ 0, 0x42, &D::BG },
{ 0, 0x48, &D::SFH },
{ 0, 0x49, &D::NOR },
{ 0, 0x53, &D::ABSDB },
{ 0, 0x58, &D::ROT },
{ 0, 0x59, &D::ROTM },
{ 0, 0x5a, &D::ROTMA },
{ 0, 0x5b, &D::SHL },
{ 0, 0x5c, &D::ROTH },
{ 0, 0x5d, &D::ROTHM },
{ 0, 0x5e, &D::ROTMAH },
{ 0, 0x5f, &D::SHLH },
{ 0, 0x78, &D::ROTI },
{ 0, 0x79, &D::ROTMI },
{ 0, 0x7a, &D::ROTMAI },
{ 0, 0x7b, &D::SHLI },
{ 0, 0x7c, &D::ROTHI },
{ 0, 0x7d, &D::ROTHMI },
{ 0, 0x7e, &D::ROTMAHI },
{ 0, 0x7f, &D::SHLHI },
{ 0, 0xc0, &D::A },
{ 0, 0xc1, &D::AND },
{ 0, 0xc2, &D::CG },
{ 0, 0xc8, &D::AH },
{ 0, 0xc9, &D::NAND },
{ 0, 0xd3, &D::AVGB },
{ 0, 0x10c, &D::MTSPR },
{ 0, 0x10d, &D::WRCH },
{ 0, 0x128, &D::BIZ },
{ 0, 0x129, &D::BINZ },
{ 0, 0x12a, &D::BIHZ },
{ 0, 0x12b, &D::BIHNZ },
{ 0, 0x140, &D::STOPD },
{ 0, 0x144, &D::STQX },
{ 0, 0x1a8, &D::BI },
{ 0, 0x1a9, &D::BISL },
{ 0, 0x1aa, &D::IRET },
{ 0, 0x1ab, &D::BISLED },
{ 0, 0x1ac, &D::HBR },
{ 0, 0x1b0, &D::GB },
{ 0, 0x1b1, &D::GBH },
{ 0, 0x1b2, &D::GBB },
{ 0, 0x1b4, &D::FSM },
{ 0, 0x1b5, &D::FSMH },
{ 0, 0x1b6, &D::FSMB },
{ 0, 0x1b8, &D::FREST },
{ 0, 0x1b9, &D::FRSQEST },
{ 0, 0x1c4, &D::LQX },
{ 0, 0x1cc, &D::ROTQBYBI },
{ 0, 0x1cd, &D::ROTQMBYBI },
{ 0, 0x1cf, &D::SHLQBYBI },
{ 0, 0x1d4, &D::CBX },
{ 0, 0x1d5, &D::CHX },
{ 0, 0x1d6, &D::CWX },
{ 0, 0x1d7, &D::CDX },
{ 0, 0x1d8, &D::ROTQBI },
{ 0, 0x1d9, &D::ROTQMBI },
{ 0, 0x1db, &D::SHLQBI },
{ 0, 0x1dc, &D::ROTQBY },
{ 0, 0x1dd, &D::ROTQMBY },
{ 0, 0x1df, &D::SHLQBY },
{ 0, 0x1f0, &D::ORX },
{ 0, 0x1f4, &D::CBD },
{ 0, 0x1f5, &D::CHD },
{ 0, 0x1f6, &D::CWD },
{ 0, 0x1f7, &D::CDD },
{ 0, 0x1f8, &D::ROTQBII },
{ 0, 0x1f9, &D::ROTQMBII },
{ 0, 0x1fb, &D::SHLQBII },
{ 0, 0x1fc, &D::ROTQBYI },
{ 0, 0x1fd, &D::ROTQMBYI },
{ 0, 0x1ff, &D::SHLQBYI },
{ 0, 0x201, &D::NOP },
{ 0, 0x240, &D::CGT },
{ 0, 0x241, &D::XOR },
{ 0, 0x248, &D::CGTH },
{ 0, 0x249, &D::EQV },
{ 0, 0x250, &D::CGTB },
{ 0, 0x253, &D::SUMB },
{ 0, 0x258, &D::HGT },
{ 0, 0x2a5, &D::CLZ },
{ 0, 0x2a6, &D::XSWD },
{ 0, 0x2ae, &D::XSHW },
{ 0, 0x2b4, &D::CNTB },
{ 0, 0x2b6, &D::XSBH },
{ 0, 0x2c0, &D::CLGT },
{ 0, 0x2c1, &D::ANDC },
{ 0, 0x2c2, &D::FCGT },
{ 0, 0x2c3, &D::DFCGT },
{ 0, 0x2c4, &D::FA },
{ 0, 0x2c5, &D::FS },
{ 0, 0x2c6, &D::FM },
{ 0, 0x2c8, &D::CLGTH },
{ 0, 0x2c9, &D::ORC },
{ 0, 0x2ca, &D::FCMGT },
{ 0, 0x2cb, &D::DFCMGT },
{ 0, 0x2cc, &D::DFA },
{ 0, 0x2cd, &D::DFS },
{ 0, 0x2ce, &D::DFM },
{ 0, 0x2d0, &D::CLGTB },
{ 0, 0x2d8, &D::HLGT },
{ 0, 0x35c, &D::DFMA },
{ 0, 0x35d, &D::DFMS },
{ 0, 0x35e, &D::DFNMS },
{ 0, 0x35f, &D::DFNMA },
{ 0, 0x3c0, &D::CEQ },
{ 0, 0x3ce, &D::MPYHHU },
{ 0, 0x340, &D::ADDX },
{ 0, 0x341, &D::SFX },
{ 0, 0x342, &D::CGX },
{ 0, 0x343, &D::BGX },
{ 0, 0x346, &D::MPYHHA },
{ 0, 0x34e, &D::MPYHHAU },
{ 0, 0x398, &D::FSCRRD },
{ 0, 0x3b8, &D::FESD },
{ 0, 0x3b9, &D::FRDS },
{ 0, 0x3ba, &D::FSCRWR },
{ 0, 0x3bf, &D::DFTSV },
{ 0, 0x3c2, &D::FCEQ },
{ 0, 0x3c3, &D::DFCEQ },
{ 0, 0x3c4, &D::MPY },
{ 0, 0x3c5, &D::MPYH },
{ 0, 0x3c6, &D::MPYHH },
{ 0, 0x3c7, &D::MPYS },
{ 0, 0x3c8, &D::CEQH },
{ 0, 0x3ca, &D::FCMEQ },
{ 0, 0x3cb, &D::DFCMEQ },
{ 0, 0x3cc, &D::MPYU },
{ 0, 0x3d0, &D::CEQB },
{ 0, 0x3d4, &D::FI },
{ 0, 0x3d8, &D::HEQ },
{ 1, 0x1d8, &D::CFLTS },
{ 1, 0x1d9, &D::CFLTU },
{ 1, 0x1da, &D::CSFLT },
{ 1, 0x1db, &D::CUFLT },
{ 2, 0x40, &D::BRZ },
{ 2, 0x41, &D::STQA },
{ 2, 0x42, &D::BRNZ },
{ 2, 0x44, &D::BRHZ },
{ 2, 0x46, &D::BRHNZ },
{ 2, 0x47, &D::STQR },
{ 2, 0x60, &D::BRA },
{ 2, 0x61, &D::LQA },
{ 2, 0x62, &D::BRASL },
{ 2, 0x64, &D::BR },
{ 2, 0x65, &D::FSMBI },
{ 2, 0x66, &D::BRSL },
{ 2, 0x67, &D::LQR },
{ 2, 0x81, &D::IL },
{ 2, 0x82, &D::ILHU },
{ 2, 0x83, &D::ILH },
{ 2, 0xc1, &D::IOHL },
{ 3, 0x4, &D::ORI },
{ 3, 0x5, &D::ORHI },
{ 3, 0x6, &D::ORBI },
{ 3, 0xc, &D::SFI },
{ 3, 0xd, &D::SFHI },
{ 3, 0x14, &D::ANDI },
{ 3, 0x15, &D::ANDHI },
{ 3, 0x16, &D::ANDBI },
{ 3, 0x1c, &D::AI },
{ 3, 0x1d, &D::AHI },
{ 3, 0x24, &D::STQD },
{ 3, 0x34, &D::LQD },
{ 3, 0x44, &D::XORI },
{ 3, 0x45, &D::XORHI },
{ 3, 0x46, &D::XORBI },
{ 3, 0x4c, &D::CGTI },
{ 3, 0x4d, &D::CGTHI },
{ 3, 0x4e, &D::CGTBI },
{ 3, 0x4f, &D::HGTI },
{ 3, 0x5c, &D::CLGTI },
{ 3, 0x5d, &D::CLGTHI },
{ 3, 0x5e, &D::CLGTBI },
{ 3, 0x5f, &D::HLGTI },
{ 3, 0x74, &D::MPYI },
{ 3, 0x75, &D::MPYUI },
{ 3, 0x7c, &D::CEQI },
{ 3, 0x7d, &D::CEQHI },
{ 3, 0x7e, &D::CEQBI },
{ 3, 0x7f, &D::HEQI },
{ 4, 0x8, &D::HBRA },
{ 4, 0x9, &D::HBRR },
{ 4, 0x21, &D::ILA },
{ 7, 0x8, &D::SELB },
{ 7, 0xb, &D::SHUFB },
{ 7, 0xc, &D::MPYA },
{ 7, 0xd, &D::FNMS },
{ 7, 0xe, &D::FMA },
{ 7, 0xf, &D::FMS },
};
m_table.fill(&D::UNK);
for (auto& entry : instructions)
{
for (u32 i = 0; i < 1u << entry.magn; i++)
{
m_table[entry.value << entry.magn | i] = entry.pointer;
}
}
}
const std::array<T, 2048>& get_table() const
{
return m_table;
}
T decode(u32 inst) const
{
return m_table[spu_decode(inst)];
}
};

View File

@ -8,39 +8,36 @@
extern u64 get_system_time();
SPURecompilerDecoder::SPURecompilerDecoder(SPUThread& spu)
: db(fxm::get_always<SPUDatabase>())
, rec(fxm::get_always<spu_recompiler>())
, spu(spu)
void spu_recompiler_base::enter(SPUThread& spu)
{
if (spu.pc >= 0x40000 || spu.pc % 4)
{
throw fmt::exception("Invalid PC: 0x%05x", spu.pc);
}
u32 SPURecompilerDecoder::DecodeMemory(const u32 address)
{
if (spu.offset != address - spu.pc || spu.pc >= 0x40000 || spu.pc % 4)
{
throw EXCEPTION("Invalid address or PC (address=0x%x, PC=0x%05x)", address, spu.pc);
}
// get SPU LS pointer
// Get SPU LS pointer
const auto _ls = vm::ps3::_ptr<u32>(spu.offset);
// always validate (TODO)
const auto func = db->analyse(_ls, spu.pc);
// Always validate (TODO)
const auto func = spu.spu_db->analyse(_ls, spu.pc);
// reset callstack if necessary
// Reset callstack if necessary
if (func->does_reset_stack && spu.recursion_level)
{
spu.m_state |= CPU_STATE_RETURN;
return 0;
spu.state += cpu_state::ret;
return;
}
if (!func->compiled)
{
rec->compile(*func);
if (!spu.spu_rec)
{
spu.spu_rec = fxm::get_always<spu_recompiler>();
}
if (!func->compiled) throw EXCEPTION("Compilation failed");
spu.spu_rec->compile(*func);
if (!func->compiled) throw std::runtime_error("Compilation failed" HERE);
}
const u32 res = func->compiled(&spu, _ls);
@ -64,7 +61,7 @@ u32 SPURecompilerDecoder::DecodeMemory(const u32 address)
{
if (res & 0x8000000)
{
throw EXCEPTION("Undefined behaviour");
throw std::logic_error("Invalid interrupt status set" HERE);
}
spu.set_interrupt_status(true);
@ -75,6 +72,4 @@ u32 SPURecompilerDecoder::DecodeMemory(const u32 address)
}
spu.pc = res & 0x3fffc;
return 0;
}

View File

@ -1,12 +1,9 @@
#pragma once
#include "Emu/CPU/CPUDecoder.h"
#include "SPUAnalyser.h"
class SPUThread;
// SPU Recompiler instance base (must be global or PS3 process-local)
class SPURecompilerBase
class spu_recompiler_base
{
protected:
std::mutex m_mutex; // must be locked in compile()
@ -16,21 +13,11 @@ protected:
u32 m_pos; // current position
public:
virtual void compile(spu_function_t& f) = 0; // compile specified function
virtual ~SPURecompilerBase() {};
};
// SPU Decoder instance (created per SPU thread)
class SPURecompilerDecoder final : public CPUDecoder
{
public:
const std::shared_ptr<SPUDatabase> db; // associated SPU Analyser instance
const std::shared_ptr<SPURecompilerBase> rec; // assiciated SPU Recompiler instance
SPUThread& spu; // associated SPU Thread
SPURecompilerDecoder(SPUThread& spu);
u32 DecodeMemory(const u32 address) override; // non-virtual override (to avoid virtual call whenever possible)
virtual ~spu_recompiler_base() = default;
// Compile specified function
virtual void compile(spu_function_t& f) = 0;
// Run
static void enter(class SPUThread&);
};

View File

@ -1,15 +1,15 @@
#include "stdafx.h"
#include "Utilities/Config.h"
#include "Emu/Memory/Memory.h"
#include "Emu/System.h"
#include "Emu/state.h"
#include "Emu/IdManager.h"
#include "Emu/Cell/PPUThread.h"
#include "Emu/SysCalls/ErrorCodes.h"
#include "Emu/SysCalls/lv2/sys_spu.h"
#include "Emu/SysCalls/lv2/sys_event_flag.h"
#include "Emu/SysCalls/lv2/sys_event.h"
#include "Emu/SysCalls/lv2/sys_interrupt.h"
#include "Emu/Cell/ErrorCodes.h"
#include "Emu/Cell/lv2/sys_spu.h"
#include "Emu/Cell/lv2/sys_event_flag.h"
#include "Emu/Cell/lv2/sys_event.h"
#include "Emu/Cell/lv2/sys_interrupt.h"
#include "Emu/Cell/SPUDisAsm.h"
#include "Emu/Cell/SPUThread.h"
@ -20,8 +20,24 @@
extern u64 get_timebased_time();
// defined here since SPUDisAsm.cpp doesn't exist
const spu_opcode_table_t<void(SPUDisAsm::*)(spu_opcode_t)> SPUDisAsm::opcodes{ DEFINE_SPU_OPCODES(&SPUDisAsm::), &SPUDisAsm::UNK };
enum class spu_decoder_type
{
precise,
fast,
asmjit,
llvm,
};
cfg::map_entry<spu_decoder_type> g_cfg_spu_decoder(cfg::root.core, "SPU Decoder", 2,
{
{ "Interpreter (precise)", spu_decoder_type::precise },
{ "Interpreter (fast)", spu_decoder_type::fast },
{ "Recompiler (ASMJIT)", spu_decoder_type::asmjit },
{ "Recompiler (LLVM)", spu_decoder_type::llvm },
});
const spu_decoder<spu_interpreter_precise> s_spu_interpreter_precise;
const spu_decoder<spu_interpreter_fast> s_spu_interpreter_fast;
thread_local bool spu_channel_t::notification_required;
@ -31,7 +47,7 @@ void spu_int_ctrl_t::set(u64 ints)
ints &= mask;
// notify if at least 1 bit was set
if (ints && ~stat._or(ints) & ints && tag)
if (ints && ~stat.fetch_or(ints) & ints && tag)
{
LV2_LOCK;
@ -44,112 +60,23 @@ void spu_int_ctrl_t::set(u64 ints)
}
}
void spu_int_ctrl_t::clear(u64 ints)
{
stat &= ~ints;
}
const spu_imm_table_t g_spu_imm;
SPUThread::SPUThread(CPUThreadType type, const std::string& name, u32 index, u32 offset)
: CPUThread(type, name)
, index(index)
, offset(offset)
{
}
SPUThread::SPUThread(const std::string& name, u32 index)
: CPUThread(CPU_THREAD_SPU, name)
, index(index)
, offset(vm::alloc(0x40000, vm::main))
{
CHECK_ASSERTION(offset);
}
SPUThread::~SPUThread()
{
// Deallocate Local Storage
vm::dealloc_verbose_nothrow(offset);
}
bool SPUThread::is_paused() const
{
if (CPUThread::is_paused())
{
return true;
}
if (const auto group = tg.lock())
{
if (group->state >= SPU_THREAD_GROUP_STATUS_WAITING && group->state <= SPU_THREAD_GROUP_STATUS_SUSPENDED)
{
return true;
}
}
return false;
}
std::string SPUThread::get_name() const
{
return fmt::format("%s[0x%x] Thread (%s)[0x%05x]", CPUThread::GetTypeString(), m_id, CPUThread::get_name(), pc);
return fmt::format("%sSPU[0x%x] Thread (%s)", offset > RAW_SPU_BASE_ADDR ? "Raw" : "", id, name);
}
void SPUThread::dump_info() const
std::string SPUThread::dump() const
{
CPUThread::dump_info();
std::string ret = "Registers:\n=========\n";
for (uint i = 0; i<128; ++i) ret += fmt::format("GPR[%d] = 0x%s\n", i, gpr[i].to_hex().c_str());
return ret;
}
void SPUThread::cpu_task()
{
std::fesetround(FE_TOWARDZERO);
if (!custom_task && !m_dec)
{
// Select opcode table (TODO)
const auto& table = rpcs3::state.config.core.spu_decoder.value() == spu_decoder_type::interpreter_precise ? spu_interpreter::precise::g_spu_opcode_table : spu_interpreter::fast::g_spu_opcode_table;
// LS base address
const auto base = vm::_ptr<const u32>(offset);
while (true)
{
if (!m_state)
{
// read opcode
const u32 opcode = base[pc / 4];
// call interpreter function
table[opcode](*this, { opcode });
// next instruction
pc += 4;
continue;
}
if (check_status())
{
return;
}
}
}
if (custom_task)
{
if (check_status()) return;
return custom_task(*this);
}
while (!m_state || !check_status())
{
// decode instruction using specified decoder
pc += m_dec->DecodeMemory(pc + offset);
}
}
void SPUThread::init_regs()
void SPUThread::cpu_init()
{
gpr = {};
fpscr.Reset();
@ -190,75 +117,96 @@ void SPUThread::init_regs()
gpr[1]._u32[3] = 0x3FFF0; // initial stack frame pointer
}
void SPUThread::init_stack()
void SPUThread::cpu_task()
{
// nothing to do
std::fesetround(FE_TOWARDZERO);
if (custom_task)
{
if (check_status()) return;
return custom_task(*this);
}
void SPUThread::close_stack()
_log::g_tls_make_prefix = [](const auto&, auto, const auto&)
{
// nothing to do here
const auto cpu = static_cast<SPUThread*>(get_current_cpu_thread());
return fmt::format("%s [0x%05x]", cpu->get_name(), cpu->pc);
};
if (g_cfg_spu_decoder.get() == spu_decoder_type::asmjit)
{
if (!spu_db) spu_db = fxm::get_always<SPUDatabase>();
return spu_recompiler_base::enter(*this);
}
void SPUThread::do_run()
{
m_dec.reset();
// Select opcode table
const auto& table = *(
g_cfg_spu_decoder.get() == spu_decoder_type::precise ? &s_spu_interpreter_precise.get_table() :
g_cfg_spu_decoder.get() == spu_decoder_type::fast ? &s_spu_interpreter_fast.get_table() :
throw std::logic_error("Invalid SPU decoder"));
switch (auto mode = rpcs3::state.config.core.spu_decoder.value())
// LS base address
const auto base = vm::_ptr<const u32>(offset);
while (true)
{
case spu_decoder_type::interpreter_precise: // Interpreter 1 (Precise)
case spu_decoder_type::interpreter_fast: // Interpreter 2 (Fast)
if (!state.load())
{
break;
// Read opcode
const u32 op = base[pc / 4];
// Call interpreter function
table[spu_decode(op)](*this, { op });
// Next instruction
pc += 4;
continue;
}
case spu_decoder_type::recompiler_asmjit:
if (check_status()) return;
}
}
SPUThread::SPUThread(const std::string & name, u32 index)
: cpu_thread(cpu_type::spu, name)
, index(index)
, offset(vm::alloc(0x40000, vm::main))
{
m_dec.reset(new SPURecompilerDecoder(*this));
break;
Ensures(offset);
}
default:
SPUThread::~SPUThread()
{
LOG_ERROR(SPU, "Invalid SPU decoder mode: %d", (u8)mode);
Emu.Pause();
}
}
// Deallocate Local Storage
vm::dealloc_verbose_nothrow(offset);
}
void SPUThread::fast_call(u32 ls_addr)
void SPUThread::push_snr(u32 number, u32 value)
{
if (!is_current())
// get channel
const auto channel =
number == 0 ? &ch_snr1 :
number == 1 ? &ch_snr2 : throw EXCEPTION("Unexpected");
// check corresponding SNR register settings
if ((snr_config >> number) & 1)
{
throw EXCEPTION("Called from the wrong thread");
channel->push_or(value);
}
else
{
channel->push(value);
}
// LS:0x0: this is originally the entry point of the interrupt handler, but interrupts are not implemented
_ref<u32>(0) = 0x00000002; // STOP 2
auto old_pc = pc;
auto old_lr = gpr[0]._u32[3];
auto old_stack = gpr[1]._u32[3]; // only saved and restored (may be wrong)
auto old_task = std::move(custom_task);
pc = ls_addr;
gpr[0]._u32[3] = 0x0;
custom_task = nullptr;
try
if (channel->notification_required)
{
cpu_task();
}
catch (CPUThreadReturn)
{
}
// lock for reliable notification
std::lock_guard<std::mutex> lock(mutex);
m_state &= ~CPU_STATE_RETURN;
pc = old_pc;
gpr[0]._u32[3] = old_lr;
gpr[1]._u32[3] = old_stack;
custom_task = std::move(old_task);
cv.notify_one();
}
}
void SPUThread::do_dma_transfer(u32 cmd, spu_mfc_arg_t args)
@ -268,9 +216,9 @@ void SPUThread::do_dma_transfer(u32 cmd, spu_mfc_arg_t args)
_mm_mfence();
}
u32 eal = VM_CAST(args.ea);
u32 eal = vm::cast(args.ea, HERE);
if (eal >= SYS_SPU_THREAD_BASE_LOW && m_type == CPU_THREAD_SPU) // SPU Thread Group MMIO (LS and SNR)
if (eal >= SYS_SPU_THREAD_BASE_LOW && offset >= RAW_SPU_BASE_ADDR) // SPU Thread Group MMIO (LS and SNR)
{
const u32 index = (eal - SYS_SPU_THREAD_BASE_LOW) / SYS_SPU_THREAD_OFFSET; // thread number in group
const u32 offset = (eal - SYS_SPU_THREAD_BASE_LOW) % SYS_SPU_THREAD_OFFSET; // LS offset or MMIO register
@ -413,7 +361,7 @@ void SPUThread::process_mfc_cmd(u32 cmd)
break;
}
const u32 raddr = VM_CAST(ch_mfc_args.ea);
const u32 raddr = vm::cast(ch_mfc_args.ea, HERE);
vm::reservation_acquire(vm::base(offset + ch_mfc_args.lsa), raddr, 128);
@ -434,7 +382,7 @@ void SPUThread::process_mfc_cmd(u32 cmd)
break;
}
if (vm::reservation_update(VM_CAST(ch_mfc_args.ea), vm::base(offset + ch_mfc_args.lsa), 128))
if (vm::reservation_update(vm::cast(ch_mfc_args.ea, HERE), vm::base(offset + ch_mfc_args.lsa), 128))
{
if (last_raddr == 0)
{
@ -466,9 +414,9 @@ void SPUThread::process_mfc_cmd(u32 cmd)
break;
}
vm::reservation_op(VM_CAST(ch_mfc_args.ea), 128, [this]()
vm::reservation_op(vm::cast(ch_mfc_args.ea, HERE), 128, [this]()
{
std::memcpy(vm::base_priv(VM_CAST(ch_mfc_args.ea)), vm::base(offset + ch_mfc_args.lsa), 128);
std::memcpy(vm::base_priv(vm::cast(ch_mfc_args.ea, HERE)), vm::base(offset + ch_mfc_args.lsa), 128);
});
if (last_raddr != 0 && vm::g_tls_did_break_reservation)
@ -539,7 +487,7 @@ void SPUThread::set_events(u32 mask)
}
// set new events, get old event mask
const u32 old_stat = ch_event_stat._or(mask);
const u32 old_stat = ch_event_stat.fetch_or(mask);
// notify if some events were set
if (~old_stat & mask && old_stat & SPU_EVENT_WAITING)
@ -617,7 +565,10 @@ u32 SPUThread::get_ch_value(u32 ch)
CHECK_EMU_STATUS;
if (is_stopped()) throw CPUThreadStop{};
if (state & cpu_state::stop)
{
throw cpu_state::stop;
}
if (!lock)
{
@ -658,7 +609,10 @@ u32 SPUThread::get_ch_value(u32 ch)
CHECK_EMU_STATUS;
if (is_stopped()) throw CPUThreadStop{};
if (state & cpu_state::stop)
{
throw cpu_state::stop;
}
if (!lock)
{
@ -723,14 +677,14 @@ u32 SPUThread::get_ch_value(u32 ch)
if (ch_event_mask & SPU_EVENT_LR)
{
// register waiter if polling reservation status is required
vm::wait_op(*this, last_raddr, 128, WRAP_EXPR(get_events(true) || is_stopped()));
vm::wait_op(*this, last_raddr, 128, WRAP_EXPR(get_events(true) || state & cpu_state::stop));
}
else
{
lock.lock();
// simple waiting loop otherwise
while (!get_events(true) && !is_stopped())
while (!get_events(true) && !(state & cpu_state::stop))
{
CHECK_EMU_STATUS;
@ -740,7 +694,10 @@ u32 SPUThread::get_ch_value(u32 ch)
ch_event_stat &= ~SPU_EVENT_WAITING;
if (is_stopped()) throw CPUThreadStop{};
if (state & cpu_state::stop)
{
throw cpu_state::stop;
}
return get_events();
}
@ -767,7 +724,7 @@ void SPUThread::set_ch_value(u32 ch, u32 value)
// break;
case SPU_WrOutIntrMbox:
{
if (m_type == CPU_THREAD_RAW_SPU)
if (offset >= RAW_SPU_BASE_ADDR)
{
std::unique_lock<std::mutex> lock(mutex, std::defer_lock);
@ -775,7 +732,10 @@ void SPUThread::set_ch_value(u32 ch, u32 value)
{
CHECK_EMU_STATUS;
if (is_stopped()) throw CPUThreadStop{};
if (state & cpu_state::stop)
{
throw cpu_state::stop;
}
if (!lock)
{
@ -824,12 +784,12 @@ void SPUThread::set_ch_value(u32 ch, u32 value)
return ch_in_mbox.set_values(1, CELL_ENOTCONN); // TODO: check error passing
}
if (queue->events.size() >= queue->size)
if (queue->events() >= queue->size)
{
return ch_in_mbox.set_values(1, CELL_EBUSY);
}
queue->push(lv2_lock, SYS_SPU_THREAD_EVENT_USER_KEY, m_id, ((u64)spup << 32) | (value & 0x00ffffff), data);
queue->push(lv2_lock, SYS_SPU_THREAD_EVENT_USER_KEY, id, ((u64)spup << 32) | (value & 0x00ffffff), data);
return ch_in_mbox.set_values(1, CELL_OK);
}
@ -861,13 +821,13 @@ void SPUThread::set_ch_value(u32 ch, u32 value)
}
// TODO: check passing spup value
if (queue->events.size() >= queue->size)
if (queue->events() >= queue->size)
{
LOG_WARNING(SPU, "sys_spu_thread_throw_event(spup=%d, data0=0x%x, data1=0x%x) failed (queue is full)", spup, (value & 0x00ffffff), data);
return;
}
queue->push(lv2_lock, SYS_SPU_THREAD_EVENT_USER_KEY, m_id, ((u64)spup << 32) | (value & 0x00ffffff), data);
queue->push(lv2_lock, SYS_SPU_THREAD_EVENT_USER_KEY, id, ((u64)spup << 32) | (value & 0x00ffffff), data);
return;
}
else if (code == 128)
@ -979,7 +939,10 @@ void SPUThread::set_ch_value(u32 ch, u32 value)
{
CHECK_EMU_STATUS;
if (is_stopped()) throw CPUThreadStop{};
if (state & cpu_state::stop)
{
throw cpu_state::stop;
}
if (!lock)
{
@ -1136,7 +1099,7 @@ void SPUThread::stop_and_signal(u32 code)
{
LOG_TRACE(SPU, "stop_and_signal(code=0x%x)", code);
if (m_type == CPU_THREAD_RAW_SPU)
if (offset >= RAW_SPU_BASE_ADDR)
{
status.atomic_op([code](u32& status)
{
@ -1146,8 +1109,7 @@ void SPUThread::stop_and_signal(u32 code)
});
int_ctrl[2].set(SPU_INT2_STAT_SPU_STOP_AND_SIGNAL_INT);
return stop();
throw cpu_state::stop;
}
switch (code)
@ -1160,7 +1122,7 @@ void SPUThread::stop_and_signal(u32 code)
case 0x002:
{
m_state |= CPU_STATE_RETURN;
state += cpu_state::ret;
return;
}
@ -1241,7 +1203,10 @@ void SPUThread::stop_and_signal(u32 code)
{
CHECK_EMU_STATUS;
if (is_stopped()) throw CPUThreadStop{};
if (state & cpu_state::stop)
{
throw cpu_state::stop;
}
group->cv.wait_for(lv2_lock, std::chrono::milliseconds(1));
}
@ -1253,7 +1218,10 @@ void SPUThread::stop_and_signal(u32 code)
for (auto& thread : group->threads)
{
if (thread) thread->sleep(); // trigger status check
if (thread)
{
thread->state += cpu_state::suspend;
}
}
}
else
@ -1261,24 +1229,25 @@ void SPUThread::stop_and_signal(u32 code)
throw EXCEPTION("Unexpected SPU Thread Group state (%d)", group->state);
}
if (queue->events.size())
if (queue->events())
{
auto& event = queue->events.front();
const auto event = queue->pop(lv2_lock);
ch_in_mbox.set_values(4, CELL_OK, static_cast<u32>(std::get<1>(event)), static_cast<u32>(std::get<2>(event)), static_cast<u32>(std::get<3>(event)));
queue->events.pop_front();
}
else
{
// add waiter; protocol is ignored in current implementation
sleep_queue_entry_t waiter(*this, queue->sq);
sleep_entry<cpu_thread> waiter(queue->thread_queue(lv2_lock), *this);
// wait on the event queue
while (!unsignal())
while (!state.test_and_reset(cpu_state::signal))
{
CHECK_EMU_STATUS;
if (is_stopped()) throw CPUThreadStop{};
if (state & cpu_state::stop)
{
throw cpu_state::stop;
}
cv.wait(lv2_lock);
}
@ -1302,9 +1271,14 @@ void SPUThread::stop_and_signal(u32 code)
for (auto& thread : group->threads)
{
if (thread) thread->awake(); // untrigger status check
if (thread && thread.get() != this)
{
thread->state -= cpu_state::suspend;
thread->safe_notify();
}
}
state -= cpu_state::suspend;
group->cv.notify_all();
return;
@ -1338,7 +1312,8 @@ void SPUThread::stop_and_signal(u32 code)
{
if (thread && thread.get() != this)
{
thread->stop();
thread->state += cpu_state::stop;
thread->safe_notify();
}
}
@ -1347,7 +1322,7 @@ void SPUThread::stop_and_signal(u32 code)
group->join_state |= SPU_TGJSF_GROUP_EXIT;
group->cv.notify_one();
return stop();
throw cpu_state::stop;
}
case 0x102:
@ -1373,7 +1348,7 @@ void SPUThread::stop_and_signal(u32 code)
status |= SPU_STATUS_STOPPED_BY_STOP;
group->cv.notify_one();
return stop();
throw cpu_state::stop;
}
}
@ -1391,7 +1366,7 @@ void SPUThread::halt()
{
LOG_TRACE(SPU, "halt()");
if (m_type == CPU_THREAD_RAW_SPU)
if (offset >= RAW_SPU_BASE_ADDR)
{
status.atomic_op([](u32& status)
{
@ -1401,18 +1376,41 @@ void SPUThread::halt()
int_ctrl[2].set(SPU_INT2_STAT_SPU_HALT_OR_STEP_INT);
return stop();
throw cpu_state::stop;
}
status |= SPU_STATUS_STOPPED_BY_HALT;
throw EXCEPTION("Halt");
}
spu_thread::spu_thread(u32 entry, const std::string& name, u32 stack_size, u32 prio)
void SPUThread::fast_call(u32 ls_addr)
{
auto spu = idm::make_ptr<SPUThread>(name, 0x13370666);
// LS:0x0: this is originally the entry point of the interrupt handler, but interrupts are not implemented
_ref<u32>(0) = 0x00000002; // STOP 2
spu->pc = entry;
auto old_pc = pc;
auto old_lr = gpr[0]._u32[3];
auto old_stack = gpr[1]._u32[3]; // only saved and restored (may be wrong)
auto old_task = std::move(custom_task);
thread = std::move(spu);
pc = ls_addr;
gpr[0]._u32[3] = 0x0;
custom_task = nullptr;
try
{
cpu_task();
}
catch (cpu_state _s)
{
state += _s;
if (_s != cpu_state::ret) throw;
}
state -= cpu_state::ret;
pc = old_pc;
gpr[0]._u32[3] = old_lr;
gpr[1]._u32[3] = old_stack;
custom_task = std::move(old_task);
}

View File

@ -2,10 +2,10 @@
#include "Emu/Cell/Common.h"
#include "Emu/CPU/CPUThread.h"
#include "Emu/Cell/SPUContext.h"
#include "Emu/Cell/SPUInterpreter.h"
#include "MFC.h"
struct lv2_event_queue_t;
class lv2_event_queue_t;
struct lv2_spu_group_t;
struct lv2_int_tag_t;
@ -135,12 +135,20 @@ enum
SPU_RdSigNotify2_offs = 0x1C00C,
};
enum : u32
{
RAW_SPU_BASE_ADDR = 0xE0000000,
RAW_SPU_OFFSET = 0x00100000,
RAW_SPU_LS_OFFSET = 0x00000000,
RAW_SPU_PROB_OFFSET = 0x00040000,
};
struct spu_channel_t
{
// set to true if SPU thread must be notified after SPU channel operation
thread_local static bool notification_required;
struct sync_var_t
struct alignas(8) sync_var_t
{
bool count; // value available
bool wait; // notification required
@ -153,7 +161,7 @@ public:
// returns true on success
bool try_push(u32 value)
{
const auto old = data.atomic_op([=](sync_var_t& data)
const auto old = data.fetch_op([=](sync_var_t& data)
{
if ((data.wait = data.count) == false)
{
@ -168,7 +176,7 @@ public:
// push performing bitwise OR with previous value, may require notification
void push_or(u32 value)
{
const auto old = data.atomic_op([=](sync_var_t& data)
const auto old = data.fetch_op([=](sync_var_t& data)
{
data.count = true;
data.wait = false;
@ -181,7 +189,7 @@ public:
// push unconditionally (overwriting previous value), may require notification
void push(u32 value)
{
const auto old = data.atomic_op([=](sync_var_t& data)
const auto old = data.fetch_op([=](sync_var_t& data)
{
data.count = true;
data.wait = false;
@ -194,7 +202,7 @@ public:
// returns true on success and loaded value
std::tuple<bool, u32> try_pop()
{
const auto old = data.atomic_op([](sync_var_t& data)
const auto old = data.fetch_op([](sync_var_t& data)
{
data.wait = !data.count;
data.count = false;
@ -207,7 +215,7 @@ public:
// pop unconditionally (loading last value), may require notification
u32 pop()
{
const auto old = data.atomic_op([](sync_var_t& data)
const auto old = data.fetch_op([](sync_var_t& data)
{
data.wait = false;
data.count = false;
@ -237,7 +245,7 @@ public:
struct spu_channel_4_t
{
struct sync_var_t
struct alignas(16) sync_var_t
{
struct
{
@ -256,7 +264,7 @@ struct spu_channel_4_t
public:
void clear()
{
values = sync_var_t{};
values.store({});
value3 = 0;
}
@ -333,7 +341,10 @@ struct spu_int_ctrl_t
void set(u64 ints);
void clear(u64 ints);
void clear(u64 ints)
{
stat &= ~ints;
}
void clear()
{
@ -526,12 +537,27 @@ public:
}
};
class SPUThread : public CPUThread
class SPUThread : public cpu_thread
{
friend class SPURecompilerDecoder;
friend class spu_recompiler;
public:
virtual std::string get_name() const override;
virtual std::string dump() const override;
virtual void cpu_init() override;
virtual void cpu_task() override;
protected:
SPUThread(const std::string& name)
: cpu_thread(cpu_type::spu, name)
, index(0)
, offset(0)
{
}
public:
SPUThread(const std::string& name, u32 index);
virtual ~SPUThread() override;
std::array<v128, 128> gpr; // General-Purpose Registers
SPU_FPSCR fpscr;
@ -578,32 +604,14 @@ public:
const u32 index; // SPU index
const u32 offset; // SPU LS offset
void push_snr(u32 number, u32 value)
{
// get channel
const auto channel =
number == 0 ? &ch_snr1 :
number == 1 ? &ch_snr2 : throw EXCEPTION("Unexpected");
std::function<void(SPUThread&)> custom_task;
std::exception_ptr pending_exception;
// check corresponding SNR register settings
if ((snr_config >> number) & 1)
{
channel->push_or(value);
}
else
{
channel->push(value);
}
if (channel->notification_required)
{
// lock for reliable notification
std::lock_guard<std::mutex> lock(mutex);
cv.notify_one();
}
}
std::shared_ptr<class SPUDatabase> spu_db;
std::shared_ptr<class spu_recompiler_base> spu_rec;
u32 recursion_level = 0;
void push_snr(u32 number, u32 value);
void do_dma_transfer(u32 cmd, spu_mfc_arg_t args);
void do_dma_list_cmd(u32 cmd, spu_mfc_arg_t args);
void process_mfc_cmd(u32 cmd);
@ -618,14 +626,18 @@ public:
void stop_and_signal(u32 code);
void halt();
void fast_call(u32 ls_addr);
// Convert specified SPU LS address to a pointer of specified (possibly converted to BE) type
template<typename T> inline to_be_t<T>* _ptr(u32 lsa)
template<typename T>
inline to_be_t<T>* _ptr(u32 lsa)
{
return static_cast<to_be_t<T>*>(vm::base(offset + lsa));
}
// Convert specified SPU LS address to a reference of specified (possibly converted to BE) type
template<typename T> inline to_be_t<T>& _ref(u32 lsa)
template<typename T>
inline to_be_t<T>& _ref(u32 lsa)
{
return *_ptr<T>(lsa);
}
@ -655,100 +667,4 @@ public:
}
}
}
std::function<void(SPUThread&)> custom_task;
std::exception_ptr pending_exception;
u32 recursion_level = 0;
protected:
SPUThread(CPUThreadType type, const std::string& name, u32 index, u32 offset);
public:
SPUThread(const std::string& name, u32 index);
virtual ~SPUThread() override;
virtual bool is_paused() const override;
virtual std::string get_name() const override;
virtual void dump_info() const override;
virtual u32 get_pc() const override { return pc; }
virtual u32 get_offset() const override { return offset; }
virtual void do_run() override;
virtual void cpu_task() override;
virtual void init_regs() override;
virtual void init_stack() override;
virtual void close_stack() override;
void fast_call(u32 ls_addr);
virtual std::string RegsToString() const override
{
std::string ret = "Registers:\n=========\n";
for(uint i=0; i<128; ++i) ret += fmt::format("GPR[%d] = 0x%s\n", i, gpr[i].to_hex().c_str());
return ret;
}
virtual std::string ReadRegString(const std::string& reg) const override
{
std::string::size_type first_brk = reg.find('[');
if (first_brk != std::string::npos)
{
long reg_index;
reg_index = atol(reg.substr(first_brk + 1, reg.length()-2).c_str());
if (reg.find("GPR")==0) return fmt::format("%016llx%016llx", gpr[reg_index]._u64[1], gpr[reg_index]._u64[0]);
}
return "";
}
bool WriteRegString(const std::string& reg, std::string value) override
{
while (value.length() < 32) value = "0"+value;
std::string::size_type first_brk = reg.find('[');
if (first_brk != std::string::npos)
{
long reg_index;
reg_index = atol(reg.substr(first_brk + 1, reg.length() - 2).c_str());
if (reg.find("GPR")==0)
{
unsigned long long reg_value0;
unsigned long long reg_value1;
try
{
reg_value0 = std::stoull(value.substr(16, 31), 0, 16);
reg_value1 = std::stoull(value.substr(0, 15), 0, 16);
}
catch (std::invalid_argument& /*e*/)
{
return false;
}
gpr[reg_index]._u64[0] = (u64)reg_value0;
gpr[reg_index]._u64[1] = (u64)reg_value1;
return true;
}
}
return false;
}
};
class spu_thread : cpu_thread
{
public:
spu_thread(u32 entry, const std::string& name = "", u32 stack_size = 0, u32 prio = 0);
cpu_thread& args(std::initializer_list<std::string> values) override
{
return *this;
}
cpu_thread& run() override
{
auto& spu = static_cast<SPUThread&>(*thread);
spu.run();
return *this;
}
};