#pragma once #include "ARMv7Thread.h" using arm_function_t = void(*)(ARMv7Thread&); #define BIND_FUNC(func) static_cast([](ARMv7Thread& cpu){\ const auto old_f = cpu.last_function;\ cpu.last_function = #func;\ arm_func_detail::do_call(cpu, func);\ cpu.last_function = old_f;\ }) struct arm_va_args_t { u32 count; // Number of 32-bit args passed }; namespace arm_func_detail { enum arg_class : u32 { ARG_GENERAL, ARG_FLOAT, ARG_VECTOR, ARG_STACK, ARG_CONTEXT, ARG_VARIADIC, ARG_UNKNOWN, }; static const auto FIXED_STACK_FRAME_SIZE = 0x80; template struct bind_arg { static_assert(type == ARG_GENERAL, "Unknown function argument type"); static_assert(!std::is_pointer::value, "Invalid function argument type (pointer)"); static_assert(!std::is_reference::value, "Invalid function argument type (reference)"); static_assert(sizeof(T) <= 4, "Invalid function argument type for ARG_GENERAL"); FORCE_INLINE static T get_arg(ARMv7Thread& cpu) { return arm_gpr_cast(cpu.GPR[g_count - 1]); } FORCE_INLINE static void put_arg(ARMv7Thread& cpu, const T& arg) { cpu.GPR[g_count - 1] = arm_gpr_cast(arg); } }; template struct bind_arg { // first u64 argument is passed in r0-r1, second one is passed in r2-r3 (if g_count = 3) static_assert(g_count == 2 || g_count == 4, "Wrong u64 argument position"); FORCE_INLINE static u64 get_arg(ARMv7Thread& cpu) { return cpu.GPR_D[(g_count - 1) >> 1]; } FORCE_INLINE static void put_arg(ARMv7Thread& cpu, u64 arg) { cpu.GPR_D[(g_count - 1) >> 1] = arg; } }; template struct bind_arg { static_assert(g_count == 2 || g_count == 4, "Wrong s64 argument position"); FORCE_INLINE static s64 get_arg(ARMv7Thread& cpu) { return cpu.GPR_D[(g_count - 1) >> 1]; } FORCE_INLINE static void put_arg(ARMv7Thread& cpu, s64 arg) { cpu.GPR_D[(g_count - 1) >> 1] = arg; } }; template struct bind_arg { static_assert(f_count <= 0, "TODO: Unsupported argument type (float)"); static_assert(sizeof(T) <= 8, "Invalid function argument type for ARG_FLOAT"); FORCE_INLINE static T get_arg(ARMv7Thread& cpu) { } FORCE_INLINE static void put_arg(ARMv7Thread& cpu, const T& arg) { } }; template struct bind_arg { static_assert(v_count <= 0, "TODO: Unsupported argument type (vector)"); static_assert(std::is_same, v128>::value, "Invalid function argument type for ARG_VECTOR"); FORCE_INLINE static T get_arg(ARMv7Thread& cpu) { } FORCE_INLINE static void put_arg(ARMv7Thread& cpu, const T& arg) { } }; template struct bind_arg { static_assert(f_count <= 0, "TODO: Unsupported stack argument type (float)"); static_assert(v_count <= 0, "TODO: Unsupported stack argument type (vector)"); static_assert(sizeof(T) <= 4, "Invalid function argument type for ARG_STACK"); FORCE_INLINE static T get_arg(ARMv7Thread& cpu) { // TODO: check return arm_gpr_cast(vm::psv::read32(cpu.SP + sizeof(u32) * (g_count - 5))); } FORCE_INLINE static void put_arg(ARMv7Thread& cpu, const T& arg) { // TODO: check const int stack_pos = (g_count - 5) * 4 - FIXED_STACK_FRAME_SIZE; static_assert(stack_pos < 0, "TODO: Increase fixed stack frame size (arg count limit broken)"); vm::psv::write32(cpu.SP + stack_pos, arm_gpr_cast(arg)); } }; template struct bind_arg { FORCE_INLINE static u64 get_arg(ARMv7Thread& cpu) { // TODO: check return vm::psv::read64(cpu.SP + sizeof(u32) * (g_count - 6)); } FORCE_INLINE static void put_arg(ARMv7Thread& cpu, u64 arg) { // TODO: check const int stack_pos = (g_count - 6) * 4 - FIXED_STACK_FRAME_SIZE; static_assert(stack_pos < -4, "TODO: Increase fixed stack frame size (arg count limit broken)"); vm::psv::write64(cpu.SP + stack_pos, arg); } }; template struct bind_arg { FORCE_INLINE static s64 get_arg(ARMv7Thread& cpu) { // TODO: check return vm::psv::read64(cpu.SP + sizeof(u32) * (g_count - 6)); } FORCE_INLINE static void put_arg(ARMv7Thread& cpu, s64 arg) { // TODO: check const int stack_pos = (g_count - 6) * 4 - FIXED_STACK_FRAME_SIZE; static_assert(stack_pos < -4, "TODO: Increase fixed stack frame size (arg count limit broken)"); vm::psv::write64(cpu.SP + stack_pos, arg); } }; template struct bind_arg { static_assert(std::is_same, ARMv7Thread>::value, "Invalid function argument type for ARG_CONTEXT"); FORCE_INLINE static ARMv7Thread& get_arg(ARMv7Thread& cpu) { return cpu; } FORCE_INLINE static void put_arg(ARMv7Thread& cpu, ARMv7Thread& arg) { } }; template struct bind_arg { static_assert(std::is_same, arm_va_args_t>::value, "Invalid function argument type for ARG_VARIADIC"); FORCE_INLINE static arm_va_args_t get_arg(ARMv7Thread& cpu) { return{ g_count }; } }; template struct bind_result { static_assert(type != ARG_FLOAT, "TODO: Unsupported funcion result type (float)"); static_assert(type != ARG_VECTOR, "TODO: Unsupported funcion result type (vector)"); static_assert(type == ARG_GENERAL, "Wrong use of bind_result template"); static_assert(sizeof(T) <= 4, "Invalid function result type for ARG_GENERAL"); FORCE_INLINE static T get_result(ARMv7Thread& cpu) { return arm_gpr_cast(cpu.GPR[0]); } FORCE_INLINE static void put_result(ARMv7Thread& cpu, const T& result) { cpu.GPR[0] = arm_gpr_cast(result); } }; template<> struct bind_result { FORCE_INLINE static u64 get_result(ARMv7Thread& cpu) { return cpu.GPR_D[0]; } FORCE_INLINE static void put_result(ARMv7Thread& cpu, u64 result) { cpu.GPR_D[0] = result; } }; template<> struct bind_result { FORCE_INLINE static s64 get_result(ARMv7Thread& cpu) { return cpu.GPR_D[0]; } FORCE_INLINE static void put_result(ARMv7Thread& cpu, s64 result) { cpu.GPR_D[0] = result; } }; //template //struct bind_result //{ // static_assert(sizeof(T) <= 8, "Invalid function result type for ARG_FLOAT"); // static FORCE_INLINE void put_result(ARMv7Thread& cpu, const T& result) // { // } //}; //template //struct bind_result //{ // static_assert(std::is_same, v128>::value, "Invalid function result type for ARG_VECTOR"); // static FORCE_INLINE void put_result(ARMv7Thread& cpu, const T& result) // { // } //}; template struct result_type { static_assert(!std::is_pointer::value, "Invalid function result type (pointer)"); static_assert(!std::is_reference::value, "Invalid function result type (reference)"); static const bool is_float = std::is_floating_point::value; static const bool is_vector = std::is_same, v128>::value; static const arg_class value = is_float ? ARG_FLOAT : (is_vector ? ARG_VECTOR : ARG_GENERAL); }; template struct arg_type { // TODO: check calculations static const bool is_float = std::is_floating_point::value; static const bool is_vector = std::is_same, v128>::value; static const bool is_context = std::is_same, ARMv7Thread>::value; static const bool is_variadic = std::is_same, arm_va_args_t>::value; static const bool is_general = !is_float && !is_vector && !is_context && !is_variadic; static const u32 g_align = ALIGN_32(T) > 4 ? ALIGN_32(T) >> 2 : 1; static const u32 g_value = is_general ? ((g_count + (g_align - 1)) & ~(g_align - 1)) + (g_align) : g_count; static const u32 f_value = f_count + is_float; static const u32 v_value = v_count + is_vector; static const arg_class value = is_general ? (g_value > 4 ? ARG_STACK : ARG_GENERAL) : is_float ? (f_value > 9000 ? ARG_STACK : ARG_FLOAT) : is_vector ? (v_value > 9000 ? ARG_STACK : ARG_VECTOR) : is_context ? ARG_CONTEXT : is_variadic ? ARG_VARIADIC : ARG_UNKNOWN; }; // wrapper for variadic argument info list, each value contains packed argument type and counts of GENERAL, FLOAT and VECTOR arguments template struct arg_info_pack_t; template struct arg_info_pack_t { static const u32 last_value = arg_info_pack_t::last_value; }; template struct arg_info_pack_t { static const u32 last_value = First; }; template<> struct arg_info_pack_t<> { static const u32 last_value = 0; }; // argument type + g/f/v_count unpacker template struct bind_arg_packed { FORCE_INLINE static T get_arg(ARMv7Thread& cpu) { return bind_arg(type_pack & 0xff), (type_pack >> 8) & 0xff, (type_pack >> 16) & 0xff, (type_pack >> 24)>::get_arg(cpu); } }; template FORCE_INLINE RT call(ARMv7Thread& cpu, RT(*func)(Args...), arg_info_pack_t info) { // do the actual function call when all arguments are prepared (simultaneous unpacking of Args... and Info...) return func(bind_arg_packed::get_arg(cpu)...); } template FORCE_INLINE RT call(ARMv7Thread& cpu, RT(*func)(Args...), arg_info_pack_t info) { // 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); using type = arg_type; const arg_class t = type::value; const u32 g = type::g_value; const u32 f = type::f_value; const u32 v = type::v_value; return call(cpu, func, arg_info_pack_t{}); } template FORCE_INLINE static bool put_func_args(ARMv7Thread& cpu) { // terminator return false; } template FORCE_INLINE static bool put_func_args(ARMv7Thread& cpu, T1 arg, T... args) { using type = arg_type; const arg_class t = type::value; const u32 g = type::g_value; const u32 f = type::f_value; const u32 v = type::v_value; bind_arg::put_arg(cpu, arg); // return true if stack was used return put_func_args(cpu, args...) || (t == ARG_STACK); } template struct func_binder; template struct func_binder { using func_t = void(*)(T...); static void do_call(ARMv7Thread& cpu, func_t func) { call(cpu, func, arg_info_pack_t<>{}); } }; template struct func_binder { using func_t = RT(*)(T...); static void do_call(ARMv7Thread& cpu, func_t func) { bind_result::value>::put_result(cpu, call(cpu, func, arg_info_pack_t<>{})); } }; template struct func_caller { FORCE_INLINE static RT call(ARMv7Thread& cpu, u32 addr, T... args) { func_caller::call(cpu, addr, args...); return bind_result::value>::get_result(cpu); } }; template struct func_caller { FORCE_INLINE static void call(ARMv7Thread& cpu, u32 addr, T... args) { if (put_func_args<0, 0, 0, T...>(cpu, args...)) { cpu.SP -= FIXED_STACK_FRAME_SIZE; cpu.fast_call(addr); cpu.SP += FIXED_STACK_FRAME_SIZE; } else { cpu.fast_call(addr); } } }; template FORCE_INLINE void do_call(ARMv7Thread& cpu, RT(*func)(T...)) { func_binder::do_call(cpu, func); } } class arm_function_manager { // Global variable for each registered function template struct registered { static u32 index; }; // Access global function list static std::vector& access(); static u32 add_function(arm_function_t function); public: // Register function (shall only be called during global initialization) template static inline u32 register_function(arm_function_t func) { return registered::index = add_function(func); } // Get function index template static inline u32 get_index() { return registered::index; } // Read all registered functions static inline const auto& get() { return access(); } // Allocation address static u32 addr; }; template u32 arm_function_manager::registered::index = 0; #define FIND_FUNC(func) arm_function_manager::get_index()