diff --git a/Source/Core/Core/HLE/HLE_VarArgs.cpp b/Source/Core/Core/HLE/HLE_VarArgs.cpp index 27ac6f7bf2..1b9b14e760 100644 --- a/Source/Core/Core/HLE/HLE_VarArgs.cpp +++ b/Source/Core/Core/HLE/HLE_VarArgs.cpp @@ -4,6 +4,8 @@ #include "Core/HLE/HLE_VarArgs.h" +#include "Common/Logging/Log.h" + HLE::SystemVABI::VAList::~VAList() = default; u32 HLE::SystemVABI::VAList::GetGPR(u32 gpr) const @@ -15,3 +17,51 @@ double HLE::SystemVABI::VAList::GetFPR(u32 fpr) const { return rPS0(fpr); } + +HLE::SystemVABI::VAListStruct::VAListStruct(u32 address) + : VAList(0), m_va_list{PowerPC::HostRead_U8(address), PowerPC::HostRead_U8(address + 1), + PowerPC::HostRead_U32(address + 4), PowerPC::HostRead_U32(address + 8)}, + m_address(address), m_has_fpr_area(GetCRBit(6) == 1) +{ + m_stack = m_va_list.overflow_arg_area; + m_gpr += m_va_list.gpr; + m_fpr += m_va_list.fpr; +} + +u32 HLE::SystemVABI::VAListStruct::GetGPRArea() const +{ + return m_va_list.reg_save_area; +} + +u32 HLE::SystemVABI::VAListStruct::GetFPRArea() const +{ + return GetGPRArea() + 4 * 8; +} + +u32 HLE::SystemVABI::VAListStruct::GetGPR(u32 gpr) const +{ + if (gpr < 3 || gpr > 10) + { + ERROR_LOG(OSHLE, "VAListStruct at %08x doesn't have GPR%d!", m_address, gpr); + return 0; + } + const u32 gpr_address = Common::AlignUp(GetGPRArea() + 4 * (gpr - 3), 4); + return PowerPC::HostRead_U32(gpr_address); +} + +double HLE::SystemVABI::VAListStruct::GetFPR(u32 fpr) const +{ + double value = 0.0; + + if (!m_has_fpr_area || fpr < 1 || fpr > 8) + { + ERROR_LOG(OSHLE, "VAListStruct at %08x doesn't have FPR%d!", m_address, fpr); + } + else + { + const u32 fpr_address = Common::AlignUp(GetFPRArea() + 8 * (fpr - 1), 8); + const u64 integral = PowerPC::HostRead_U64(fpr_address); + std::memcpy(&value, &integral, sizeof(double)); + } + return value; +} diff --git a/Source/Core/Core/HLE/HLE_VarArgs.h b/Source/Core/Core/HLE/HLE_VarArgs.h index a76ae76fe3..3c72c1f05e 100644 --- a/Source/Core/Core/HLE/HLE_VarArgs.h +++ b/Source/Core/Core/HLE/HLE_VarArgs.h @@ -130,15 +130,48 @@ public: return static_cast(GetArg()); } -private: +protected: u32 m_gpr = 3; u32 m_fpr = 1; const u32 m_gpr_max = 10; const u32 m_fpr_max = 8; u32 m_stack; +private: virtual u32 GetGPR(u32 gpr) const; virtual double GetFPR(u32 fpr) const; }; + +// See System V ABI (SVR4) for more details +// -> 6-6 Required Routines +// -> 3-21 Variable Argument Lists +// +// Source: +// http://refspecs.linux-foundation.org/elf/elfspec_ppc.pdf +class VAListStruct : public VAList +{ +public: + explicit VAListStruct(u32 address); + ~VAListStruct() = default; + +private: + struct svr4_va_list + { + u8 gpr; + u8 fpr; + u32 overflow_arg_area; + u32 reg_save_area; + }; + const svr4_va_list m_va_list; + const u32 m_address; + const bool m_has_fpr_area; + + u32 GetGPRArea() const; + u32 GetFPRArea() const; + + u32 GetGPR(u32 gpr) const override; + double GetFPR(u32 fpr) const override; +}; + } // namespace SystemVABI } // namespace HLE