2013-06-30 10:46:29 +02:00
|
|
|
#pragma once
|
2015-07-06 01:21:15 +02:00
|
|
|
|
2014-07-11 13:59:13 +02:00
|
|
|
#include "Emu/Cell/PPUThread.h"
|
2014-07-10 16:16:19 +02:00
|
|
|
|
2015-03-15 14:26:01 +01:00
|
|
|
using ppu_func_caller = void(*)(PPUThread&);
|
2015-02-20 18:58:15 +01:00
|
|
|
|
2015-01-19 19:02:33 +01:00
|
|
|
namespace ppu_func_detail
|
2014-08-26 16:38:14 +02:00
|
|
|
{
|
2015-07-08 19:45:26 +02:00
|
|
|
// argument type classification
|
|
|
|
|
enum arg_class : u32
|
2014-08-26 16:38:14 +02:00
|
|
|
{
|
2015-07-08 19:45:26 +02:00
|
|
|
ARG_GENERAL, // argument is stored in GPR registers (from r3 to r10)
|
|
|
|
|
ARG_FLOAT, // argument is stored in FPR registers (from f1 to f12)
|
|
|
|
|
ARG_VECTOR, // argument is stored in VPR registers (from v2 to v13)
|
2014-08-26 16:38:14 +02:00
|
|
|
ARG_STACK,
|
|
|
|
|
};
|
|
|
|
|
|
2015-07-08 19:45:26 +02:00
|
|
|
template<typename T, arg_class type, u32 g_count, u32 f_count, u32 v_count> struct bind_arg;
|
2014-08-26 16:38:14 +02:00
|
|
|
|
2015-07-08 19:45:26 +02:00
|
|
|
template<typename T, u32 g_count, u32 f_count, u32 v_count>
|
2014-08-26 16:38:14 +02:00
|
|
|
struct bind_arg<T, ARG_GENERAL, g_count, f_count, v_count>
|
|
|
|
|
{
|
2014-09-08 02:54:17 +02:00
|
|
|
static_assert(sizeof(T) <= 8, "Invalid function argument type for ARG_GENERAL");
|
2014-08-26 23:09:50 +02:00
|
|
|
|
2015-05-28 17:14:22 +02:00
|
|
|
static force_inline T get_arg(PPUThread& CPU)
|
2014-08-26 23:09:50 +02:00
|
|
|
{
|
2015-01-19 15:16:31 +01:00
|
|
|
return cast_from_ppu_gpr<T>(CPU.GPR[g_count + 2]);
|
2014-08-26 23:09:50 +02:00
|
|
|
}
|
2014-08-26 16:38:14 +02:00
|
|
|
};
|
|
|
|
|
|
2015-07-08 19:45:26 +02:00
|
|
|
template<typename T, u32 g_count, u32 f_count, u32 v_count>
|
2014-08-26 16:38:14 +02:00
|
|
|
struct bind_arg<T, ARG_FLOAT, g_count, f_count, v_count>
|
|
|
|
|
{
|
2014-09-08 02:54:17 +02:00
|
|
|
static_assert(sizeof(T) <= 8, "Invalid function argument type for ARG_FLOAT");
|
2014-08-26 23:09:50 +02:00
|
|
|
|
2015-05-28 17:14:22 +02:00
|
|
|
static force_inline T get_arg(PPUThread& CPU)
|
2014-08-26 23:09:50 +02:00
|
|
|
{
|
2015-01-19 15:16:31 +01:00
|
|
|
return static_cast<T>(CPU.FPR[f_count]);
|
2014-08-26 23:09:50 +02:00
|
|
|
}
|
2014-08-26 16:38:14 +02:00
|
|
|
};
|
|
|
|
|
|
2015-07-08 19:45:26 +02:00
|
|
|
template<typename T, u32 g_count, u32 f_count, u32 v_count>
|
2014-08-26 16:38:14 +02:00
|
|
|
struct bind_arg<T, ARG_VECTOR, g_count, f_count, v_count>
|
|
|
|
|
{
|
2015-01-07 17:44:47 +01:00
|
|
|
static_assert(std::is_same<T, u128>::value, "Invalid function argument type for ARG_VECTOR");
|
2014-08-26 23:09:50 +02:00
|
|
|
|
2015-05-28 17:14:22 +02:00
|
|
|
static force_inline T get_arg(PPUThread& CPU)
|
2014-08-26 23:09:50 +02:00
|
|
|
{
|
2015-01-07 17:44:47 +01:00
|
|
|
return CPU.VPR[v_count + 1];
|
2014-08-26 23:09:50 +02:00
|
|
|
}
|
2014-08-26 16:38:14 +02:00
|
|
|
};
|
|
|
|
|
|
2015-07-08 19:45:26 +02:00
|
|
|
template<typename T, u32 g_count, u32 f_count, u32 v_count>
|
2014-08-26 16:38:14 +02:00
|
|
|
struct bind_arg<T, ARG_STACK, g_count, f_count, v_count>
|
|
|
|
|
{
|
2014-09-23 16:27:18 +02:00
|
|
|
static_assert(f_count <= 13, "TODO: Unsupported stack argument type (float)");
|
2014-09-08 02:54:17 +02:00
|
|
|
static_assert(v_count <= 12, "TODO: Unsupported stack argument type (vector)");
|
2014-09-12 15:08:24 +02:00
|
|
|
static_assert(sizeof(T) <= 8, "Invalid function argument type for ARG_STACK");
|
2014-08-26 23:09:50 +02:00
|
|
|
|
2015-05-28 17:14:22 +02:00
|
|
|
static force_inline T get_arg(PPUThread& CPU)
|
2014-08-26 23:09:50 +02:00
|
|
|
{
|
2014-09-08 02:54:17 +02:00
|
|
|
// TODO: check stack argument displacement
|
2015-07-08 19:45:26 +02:00
|
|
|
const u64 res = CPU.GetStackArg(8 + std::max<u32>(g_count - 8, 0) + std::max<u32>(f_count - 13, 0) + std::max<u32>(v_count - 12, 0));
|
2015-01-19 15:16:31 +01:00
|
|
|
return cast_from_ppu_gpr<T>(res);
|
2014-08-26 23:09:50 +02:00
|
|
|
}
|
2014-08-26 16:38:14 +02:00
|
|
|
};
|
|
|
|
|
|
2015-02-23 19:54:17 +01:00
|
|
|
template<typename T, arg_class type>
|
2014-08-26 16:38:14 +02:00
|
|
|
struct bind_result
|
|
|
|
|
{
|
2014-09-16 15:56:27 +02:00
|
|
|
static_assert(type == ARG_GENERAL, "Wrong use of bind_result template");
|
|
|
|
|
static_assert(sizeof(T) <= 8, "Invalid function result type for ARG_GENERAL");
|
2014-09-06 19:30:50 +02:00
|
|
|
|
2015-05-28 17:14:22 +02:00
|
|
|
static force_inline void put_result(PPUThread& CPU, const T& result)
|
2014-08-26 16:38:14 +02:00
|
|
|
{
|
2015-01-14 20:45:36 +01:00
|
|
|
CPU.GPR[3] = cast_to_ppu_gpr<T>(result);
|
2014-08-26 16:38:14 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2014-09-16 15:56:27 +02:00
|
|
|
template<typename T>
|
|
|
|
|
struct bind_result<T, ARG_FLOAT>
|
2014-09-06 19:30:50 +02:00
|
|
|
{
|
2014-09-16 15:56:27 +02:00
|
|
|
static_assert(sizeof(T) <= 8, "Invalid function result type for ARG_FLOAT");
|
|
|
|
|
|
2015-05-28 17:14:22 +02:00
|
|
|
static force_inline void put_result(PPUThread& CPU, const T& result)
|
2014-09-15 00:17:24 +02:00
|
|
|
{
|
2015-01-19 15:16:31 +01:00
|
|
|
CPU.FPR[1] = static_cast<T>(result);
|
2014-09-15 00:17:24 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2014-09-16 15:56:27 +02:00
|
|
|
template<typename T>
|
|
|
|
|
struct bind_result<T, ARG_VECTOR>
|
2014-09-15 00:17:24 +02:00
|
|
|
{
|
2015-01-07 17:44:47 +01:00
|
|
|
static_assert(std::is_same<T, u128>::value, "Invalid function result type for ARG_VECTOR");
|
2014-09-16 15:56:27 +02:00
|
|
|
|
2015-05-28 17:14:22 +02:00
|
|
|
static force_inline void put_result(PPUThread& CPU, const T& result)
|
2014-09-06 19:30:50 +02:00
|
|
|
{
|
2015-01-07 17:44:47 +01:00
|
|
|
CPU.VPR[2] = result;
|
2014-09-06 19:30:50 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2015-07-08 19:45:26 +02:00
|
|
|
// wrapper for variadic argument info list, each value contains packed argument type and counts of GENERAL, FLOAT and VECTOR arguments
|
|
|
|
|
template<u32... Values> struct arg_info_pack_t;
|
|
|
|
|
|
|
|
|
|
template<u32 First, u32... Values> struct arg_info_pack_t<First, Values...>
|
2015-02-23 19:54:17 +01:00
|
|
|
{
|
2015-07-08 19:45:26 +02:00
|
|
|
static const u32 last_value = arg_info_pack_t<Values...>::last_value;
|
2015-02-23 19:54:17 +01:00
|
|
|
};
|
|
|
|
|
|
2015-07-08 19:45:26 +02:00
|
|
|
template<u32 First> struct arg_info_pack_t<First>
|
2015-02-23 19:54:17 +01:00
|
|
|
{
|
2015-07-08 19:45:26 +02:00
|
|
|
static const u32 last_value = First;
|
2015-02-23 19:54:17 +01:00
|
|
|
};
|
|
|
|
|
|
2015-07-08 19:45:26 +02:00
|
|
|
template<> struct arg_info_pack_t<>
|
2014-08-26 16:38:14 +02:00
|
|
|
{
|
2015-07-08 19:45:26 +02:00
|
|
|
static const u32 last_value = 0;
|
2014-08-26 16:38:14 +02:00
|
|
|
};
|
|
|
|
|
|
2015-07-08 19:45:26 +02:00
|
|
|
// argument unpacker
|
|
|
|
|
template<typename T, u32 type_pack> struct bind_arg_packed
|
2014-08-26 16:38:14 +02:00
|
|
|
{
|
2015-07-08 19:45:26 +02:00
|
|
|
static force_inline T get_arg(PPUThread& CPU)
|
2014-08-26 16:38:14 +02:00
|
|
|
{
|
2015-07-08 19:45:26 +02:00
|
|
|
return bind_arg<T, static_cast<arg_class>(type_pack & 0xff), (type_pack >> 8) & 0xff, (type_pack >> 16) & 0xff, (type_pack >> 24)>::get_arg(CPU);
|
2014-08-26 16:38:14 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2015-07-08 19:45:26 +02:00
|
|
|
template<u32... Info, typename RT, typename... Args>
|
|
|
|
|
force_inline RT call(PPUThread& CPU, RT(*func)(Args...), arg_info_pack_t<Info...>)
|
2014-08-26 16:38:14 +02:00
|
|
|
{
|
2015-07-08 19:45:26 +02:00
|
|
|
return func(bind_arg_packed<Args, Info>::get_arg(CPU)...);
|
2014-08-26 16:38:14 +02:00
|
|
|
}
|
|
|
|
|
|
2015-07-08 19:45:26 +02:00
|
|
|
template<u32... Info, typename RT, typename... Args>
|
|
|
|
|
force_inline RT call(PPUThread& CPU, RT(*func)(PPUThread&, Args...), arg_info_pack_t<Info...>)
|
2014-08-26 16:38:14 +02:00
|
|
|
{
|
2015-07-08 19:45:26 +02:00
|
|
|
return func(CPU, bind_arg_packed<Args, Info>::get_arg(CPU)...);
|
2014-08-26 16:38:14 +02:00
|
|
|
}
|
|
|
|
|
|
2015-07-08 19:45:26 +02:00
|
|
|
template<typename T, typename... Types, u32... Info, typename RT, typename... Args>
|
|
|
|
|
force_inline RT call(PPUThread& CPU, RT(*func)(Args...), arg_info_pack_t<Info...> info)
|
2014-08-26 16:38:14 +02:00
|
|
|
{
|
2014-09-08 02:54:17 +02:00
|
|
|
static_assert(!std::is_pointer<T>::value, "Invalid function argument type (pointer)");
|
|
|
|
|
static_assert(!std::is_reference<T>::value, "Invalid function argument type (reference)");
|
2015-07-08 19:45:26 +02:00
|
|
|
|
|
|
|
|
// unpack previous type counts (0/0/0 for the first time)
|
|
|
|
|
const u32 g_count = (info.last_value >> 8) & 0xff;
|
|
|
|
|
const u32 f_count = (info.last_value >> 16) & 0xff;
|
|
|
|
|
const u32 v_count = (info.last_value >> 24);
|
|
|
|
|
|
2014-08-26 16:38:14 +02:00
|
|
|
// TODO: check calculations
|
2014-08-26 23:09:50 +02:00
|
|
|
const bool is_float = std::is_floating_point<T>::value;
|
2014-09-06 19:30:50 +02:00
|
|
|
const bool is_vector = std::is_same<T, u128>::value;
|
2015-02-23 19:54:17 +01:00
|
|
|
const arg_class t = is_float
|
2014-09-23 16:27:18 +02:00
|
|
|
? ((f_count >= 13) ? ARG_STACK : ARG_FLOAT)
|
2014-09-06 19:30:50 +02:00
|
|
|
: (is_vector ? ((v_count >= 12) ? ARG_STACK : ARG_VECTOR) : ((g_count >= 8) ? ARG_STACK : ARG_GENERAL));
|
2015-02-23 19:54:17 +01:00
|
|
|
const u32 g = g_count + (is_float || is_vector ? 0 : 1);
|
|
|
|
|
const u32 f = f_count + (is_float ? 1 : 0);
|
|
|
|
|
const u32 v = v_count + (is_vector ? 1 : 0);
|
2014-09-12 15:08:24 +02:00
|
|
|
|
2015-07-08 19:45:26 +02:00
|
|
|
return call<Types...>(CPU, func, arg_info_pack_t<Info..., t | (g << 8) | (f << 16) | (v << 24)>{});
|
2014-08-26 16:38:14 +02:00
|
|
|
}
|
|
|
|
|
|
2015-07-08 19:45:26 +02:00
|
|
|
template<typename RT> struct result_type
|
2015-01-19 19:02:33 +01:00
|
|
|
{
|
|
|
|
|
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<RT, u128>::value;
|
2015-02-23 19:54:17 +01:00
|
|
|
static const arg_class value = is_float ? ARG_FLOAT : (is_vector ? ARG_VECTOR : ARG_GENERAL);
|
2015-01-19 19:02:33 +01:00
|
|
|
};
|
|
|
|
|
|
2015-07-08 19:45:26 +02:00
|
|
|
template<typename RT, typename... T> struct func_binder;
|
2014-08-26 16:38:14 +02:00
|
|
|
|
2015-02-21 12:30:26 +01:00
|
|
|
template<typename... T>
|
|
|
|
|
struct func_binder<void, PPUThread&, T...>
|
2014-08-26 16:38:14 +02:00
|
|
|
{
|
2015-03-15 14:26:01 +01:00
|
|
|
using func_t = void(*)(PPUThread&, T...);
|
2014-08-26 16:38:14 +02:00
|
|
|
|
2015-07-08 19:45:26 +02:00
|
|
|
static force_inline void do_call(PPUThread& CPU, func_t func)
|
2015-02-20 18:58:15 +01:00
|
|
|
{
|
2015-07-08 19:45:26 +02:00
|
|
|
call<T...>(CPU, func, arg_info_pack_t<>{});
|
2015-02-20 18:58:15 +01:00
|
|
|
}
|
2014-08-26 16:38:14 +02:00
|
|
|
};
|
|
|
|
|
|
2015-02-21 12:30:26 +01:00
|
|
|
template<typename... T>
|
|
|
|
|
struct func_binder<void, T...>
|
2014-09-16 19:46:22 +02:00
|
|
|
{
|
2015-03-15 14:26:01 +01:00
|
|
|
using func_t = void(*)(T...);
|
2014-09-16 19:46:22 +02:00
|
|
|
|
2015-07-08 19:45:26 +02:00
|
|
|
static force_inline void do_call(PPUThread& CPU, func_t func)
|
|
|
|
|
{
|
|
|
|
|
call<T...>(CPU, func, arg_info_pack_t<>{});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template<>
|
|
|
|
|
struct func_binder<void> // redundant specialization to bypass internal compiler error in MSVC
|
|
|
|
|
{
|
|
|
|
|
using func_t = void(*)();
|
|
|
|
|
|
|
|
|
|
static force_inline void do_call(PPUThread& CPU, func_t func)
|
|
|
|
|
{
|
|
|
|
|
func();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template<typename RT>
|
|
|
|
|
struct func_binder<RT> // redundant specialization to bypass internal compiler error in MSVC
|
|
|
|
|
{
|
|
|
|
|
using func_t = RT(*)();
|
|
|
|
|
|
|
|
|
|
static force_inline void do_call(PPUThread& CPU, func_t func)
|
2015-02-20 18:58:15 +01:00
|
|
|
{
|
2015-07-08 19:45:26 +02:00
|
|
|
bind_result<RT, result_type<RT>::value>::put_result(CPU, func());
|
2015-02-20 18:58:15 +01:00
|
|
|
}
|
2014-09-16 19:46:22 +02:00
|
|
|
};
|
|
|
|
|
|
2015-02-21 12:30:26 +01:00
|
|
|
template<typename RT, typename... T>
|
|
|
|
|
struct func_binder<RT, PPUThread&, T...>
|
2014-08-26 16:38:14 +02:00
|
|
|
{
|
2015-03-15 14:26:01 +01:00
|
|
|
using func_t = RT(*)(PPUThread&, T...);
|
2014-08-26 16:38:14 +02:00
|
|
|
|
2015-07-08 19:45:26 +02:00
|
|
|
static force_inline void do_call(PPUThread& CPU, func_t func)
|
2015-02-20 18:58:15 +01:00
|
|
|
{
|
2015-07-08 19:45:26 +02:00
|
|
|
bind_result<RT, result_type<RT>::value>::put_result(CPU, call<T...>(CPU, func, arg_info_pack_t<>{}));
|
2015-02-20 18:58:15 +01:00
|
|
|
}
|
2014-08-26 16:38:14 +02:00
|
|
|
};
|
2014-09-16 19:46:22 +02:00
|
|
|
|
2015-02-21 12:30:26 +01:00
|
|
|
template<typename RT, typename... T>
|
2015-02-20 20:45:00 +01:00
|
|
|
struct func_binder
|
2014-09-16 19:46:22 +02:00
|
|
|
{
|
2015-03-15 14:26:01 +01:00
|
|
|
using func_t = RT(*)(T...);
|
2014-09-16 19:46:22 +02:00
|
|
|
|
2015-07-08 19:45:26 +02:00
|
|
|
static force_inline void do_call(PPUThread& CPU, func_t func)
|
2015-02-20 18:58:15 +01:00
|
|
|
{
|
2015-07-08 19:45:26 +02:00
|
|
|
bind_result<RT, result_type<RT>::value>::put_result(CPU, call<T...>(CPU, func, arg_info_pack_t<>{}));
|
2015-02-20 18:58:15 +01:00
|
|
|
}
|
2014-09-16 19:46:22 +02:00
|
|
|
};
|
2014-11-02 00:19:14 +01:00
|
|
|
}
|
2015-02-20 14:58:40 +01:00
|
|
|
|
2015-05-28 17:14:22 +02:00
|
|
|
template<typename RT, typename... T> force_inline void call_ppu_func(PPUThread& CPU, RT(*func)(T...))
|
2015-02-21 12:30:26 +01:00
|
|
|
{
|
|
|
|
|
ppu_func_detail::func_binder<RT, T...>::do_call(CPU, func);
|
|
|
|
|
}
|
2015-02-20 18:58:15 +01:00
|
|
|
|
2015-02-21 12:30:26 +01:00
|
|
|
#define bind_func(func) [](PPUThread& CPU){ call_ppu_func(CPU, func); }
|