#pragma once #include "Emu/Cell/PPUThread.h" namespace cb_detail { enum _func_arg_type { ARG_GENERAL, ARG_FLOAT, ARG_VECTOR, ARG_STACK, }; // Current implementation can handle only fixed amount of stack arguments. // This constant can be increased if necessary. // It's possible to calculate suitable stack frame size in template, but too complicated. static const auto FIXED_STACK_FRAME_SIZE = 0x100; template struct _func_arg; template struct _func_arg { static_assert(sizeof(T) <= 8, "Invalid callback argument type for ARG_GENERAL"); __forceinline static void set_value(PPUThread& CPU, const T& arg) { CPU.GPR[g_count + 2] = cast_to_ppu_gpr(arg); } }; template struct _func_arg { static_assert(sizeof(T) <= 8, "Invalid callback argument type for ARG_FLOAT"); __forceinline static void set_value(PPUThread& CPU, const T& arg) { CPU.FPR[f_count] = arg; } }; template struct _func_arg { static_assert(std::is_same::value, "Invalid callback argument type for ARG_VECTOR"); __forceinline static void set_value(PPUThread& CPU, const T& arg) { CPU.VPR[v_count + 1] = arg; } }; template struct _func_arg { 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"); __forceinline static void set_value(PPUThread& CPU, const T& arg) { const int stack_pos = 0x70 + (g_count - 9) * 8 - FIXED_STACK_FRAME_SIZE; static_assert(stack_pos < 0, "TODO: Increase fixed stack frame size (arg count limit broken)"); vm::write64(CPU.GPR[1] + stack_pos, cast_to_ppu_gpr(arg)); } }; template __forceinline static bool _bind_func_args(PPUThread& CPU) { // terminator return false; } template __forceinline static bool _bind_func_args(PPUThread& CPU, T1 arg1, T... args) { static_assert(!std::is_pointer::value, "Invalid callback argument type (pointer)"); static_assert(!std::is_reference::value, "Invalid callback argument type (reference)"); const bool is_float = std::is_floating_point::value; const bool is_vector = std::is_same::value; const _func_arg_type t = is_float ? ((f_count >= 13) ? ARG_STACK : ARG_FLOAT) : (is_vector ? ((v_count >= 12) ? ARG_STACK : ARG_VECTOR) : ((g_count >= 8) ? ARG_STACK : ARG_GENERAL)); const int g = g_count + (is_float || is_vector ? 0 : 1); const int f = f_count + (is_float ? 1 : 0); const int v = v_count + (is_vector ? 1 : 0); _func_arg::set_value(CPU, arg1); // return true if stack was used return _bind_func_args(CPU, args...) || (t == ARG_STACK); } template struct _func_res { static_assert(type == ARG_GENERAL, "Wrong use of _func_res template"); static_assert(sizeof(T) <= 8, "Invalid callback result type for ARG_GENERAL"); __forceinline static T get_value(const PPUThread& CPU) { return (T&)CPU.GPR[3]; } }; template struct _func_res { static_assert(sizeof(T) <= 8, "Invalid callback result type for ARG_FLOAT"); __forceinline static T get_value(const PPUThread& CPU) { return (T)CPU.FPR[1]; } }; template struct _func_res { static_assert(std::is_same::value, "Invalid callback result type for ARG_VECTOR"); __forceinline static T get_value(const PPUThread& CPU) { return CPU.VPR[2]; } }; template struct _func_caller { __forceinline static RT 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.FastCall2(pc, rtoc); if (stack) CPU.GPR[1] += FIXED_STACK_FRAME_SIZE; static_assert(!std::is_pointer::value, "Invalid callback result type (pointer)"); static_assert(!std::is_reference::value, "Invalid callback result type (reference)"); const bool is_float = std::is_floating_point::value; const bool is_vector = std::is_same::value; const _func_arg_type t = is_float ? ARG_FLOAT : (is_vector ? ARG_VECTOR : ARG_GENERAL); return _func_res::get_value(CPU); } }; template struct _func_caller { __forceinline 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.FastCall2(pc, rtoc); if (stack) CPU.GPR[1] += FIXED_STACK_FRAME_SIZE; } }; } namespace vm { template __forceinline RT _ptr_base::operator()(CPUThread& CPU, T... args) const { auto data = vm::get_ptr>(vm::cast(m_addr)); const u32 pc = data[0]; const u32 rtoc = data[1]; assert(CPU.GetType() == CPU_THREAD_PPU); return cb_detail::_func_caller::call(static_cast(CPU), pc, rtoc, args...); } template __forceinline RT _ptr_base::operator()(T... args) const { return operator()(GetCurrentPPUThread(), args...); } } template RT cb_call(PPUThread& CPU, u32 pc, u32 rtoc, T... args) { return cb_detail::_func_caller::call(CPU, pc, rtoc, args...); } template void cb_call(PPUThread& CPU, u32 pc, u32 rtoc, T... args) { cb_detail::_func_caller::call(CPU, pc, rtoc, args...); }