From e9dd0072de608d9ec7dc0b27a4c6a7c2522602ae Mon Sep 17 00:00:00 2001 From: Sepalani Date: Thu, 1 Dec 2016 12:27:13 +0000 Subject: [PATCH] HLE: Variable Argument Lists support --- Source/Core/Core/Core.vcxproj | 1 + Source/Core/Core/Core.vcxproj.filters | 3 + Source/Core/Core/HLE/HLE_OS.cpp | 51 +++------- Source/Core/Core/HLE/HLE_VarArgs.h | 138 ++++++++++++++++++++++++++ 4 files changed, 154 insertions(+), 39 deletions(-) create mode 100644 Source/Core/Core/HLE/HLE_VarArgs.h diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj index a13c4ea09c..55e2d9c328 100644 --- a/Source/Core/Core/Core.vcxproj +++ b/Source/Core/Core/Core.vcxproj @@ -343,6 +343,7 @@ + diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters index 8dee1673d1..1411269cb1 100644 --- a/Source/Core/Core/Core.vcxproj.filters +++ b/Source/Core/Core/Core.vcxproj.filters @@ -955,6 +955,9 @@ HLE + + HLE + PowerPC\Cached Interpreter diff --git a/Source/Core/Core/HLE/HLE_OS.cpp b/Source/Core/Core/HLE/HLE_OS.cpp index 74adaa148c..547fc7e608 100644 --- a/Source/Core/Core/HLE/HLE_OS.cpp +++ b/Source/Core/Core/HLE/HLE_OS.cpp @@ -9,6 +9,7 @@ #include "Common/MsgHandler.h" #include "Common/StringUtil.h" #include "Core/HLE/HLE_OS.h" +#include "Core/HLE/HLE_VarArgs.h" #include "Core/HW/Memmap.h" #include "Core/PowerPC/PowerPC.h" @@ -89,14 +90,12 @@ void HLE_write_console() NOTICE_LOG(OSREPORT, "%08x->%08x| %s", LR, PC, SHIFTJISToUTF8(report_message).c_str()); } -std::string GetStringVA(u32 strReg) +std::string GetStringVA(u32 str_reg) { std::string ArgumentBuffer; - u32 ParameterCounter = strReg + 1; - u32 FloatingParameterCounter = 1; - std::string result; - std::string string = PowerPC::HostGetString(GPR(strReg)); + std::string string = PowerPC::HostGetString(GPR(str_reg)); + HLE::VAList ap(GPR(1) + 0x8, str_reg + 1); for (size_t i = 0; i < string.size(); i++) { @@ -120,38 +119,13 @@ std::string GetStringVA(u32 strReg) ArgumentBuffer += string[i]; - u64 Parameter; - if (ParameterCounter > 10) - { - Parameter = PowerPC::HostRead_U32(GPR(1) + 0x8 + ((ParameterCounter - 11) * 4)); - } - else - { - if (string[i - 1] == 'l' && - string[i - 2] == 'l') // hax, just seen this on sysmenu osreport - { - Parameter = GPR(++ParameterCounter); - Parameter = (Parameter << 32) | GPR(++ParameterCounter); - } - else // normal, 32bit - Parameter = GPR(ParameterCounter); - } - ParameterCounter++; - switch (string[i]) { case 's': result += StringFromFormat(ArgumentBuffer.c_str(), - PowerPC::HostGetString((u32)Parameter).c_str()); + PowerPC::HostGetString(ap.GetArgT()).c_str()); break; - case 'd': - case 'i': - { - result += StringFromFormat(ArgumentBuffer.c_str(), Parameter); - break; - } - case 'a': case 'A': case 'e': @@ -160,25 +134,24 @@ std::string GetStringVA(u32 strReg) case 'F': case 'g': case 'G': - { - result += StringFromFormat(ArgumentBuffer.c_str(), rPS0(FloatingParameterCounter)); - FloatingParameterCounter++; - ParameterCounter--; + result += StringFromFormat(ArgumentBuffer.c_str(), ap.GetArgT()); break; - } case 'p': // Override, so 64bit Dolphin prints 32bit pointers, since the ppc is 32bit :) - result += StringFromFormat("%x", (u32)Parameter); + result += StringFromFormat("%x", ap.GetArgT()); break; case 'n': - PowerPC::HostWrite_U32(static_cast(result.size()), static_cast(Parameter)); + PowerPC::HostWrite_U32(static_cast(result.size()), ap.GetArgT()); // %n doesn't output anything, so the result variable is untouched break; default: - result += StringFromFormat(ArgumentBuffer.c_str(), Parameter); + if (string[i - 1] == 'l' && string[i - 2] == 'l') + result += StringFromFormat(ArgumentBuffer.c_str(), ap.GetArgT()); + else + result += StringFromFormat(ArgumentBuffer.c_str(), ap.GetArgT()); break; } } diff --git a/Source/Core/Core/HLE/HLE_VarArgs.h b/Source/Core/Core/HLE/HLE_VarArgs.h new file mode 100644 index 0000000000..8c31549d70 --- /dev/null +++ b/Source/Core/Core/HLE/HLE_VarArgs.h @@ -0,0 +1,138 @@ +// Copyright 2016 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include "Common/Align.h" +#include "Common/CommonTypes.h" + +#include "Core/HW/Memmap.h" +#include "Core/PowerPC/PowerPC.h" + +#include + +namespace HLE +{ +// See System V ABI (SVR4) for more details +// -> 3-18 Parameter Passing +// -> 3-21 Variable Argument Lists +// +// Source: +// http://refspecs.linux-foundation.org/elf/elfspec_ppc.pdf +class VAList +{ +public: + explicit VAList(u32 stack, u32 gpr = 3, u32 fpr = 1, u32 gpr_max = 10, u32 fpr_max = 8) + : m_gpr(gpr), m_fpr(fpr), m_gpr_max(gpr_max), m_fpr_max(fpr_max), m_stack(stack) + { + } + ~VAList() = default; + + // SFINAE + template + constexpr bool IS_ARG_POINTER = std::is_union() || std::is_class(); + template + constexpr bool IS_WORD = std::is_pointer() || (std::is_integral() && sizeof(T) <= 4); + template + constexpr bool IS_DOUBLE_WORD = std::is_integral() && sizeof(T) == 8; + template + constexpr bool IS_ARG_REAL = std::is_floating_point(); + + // 0 - arg_ARGPOINTER + template >* = nullptr> + T GetArg() + { + T obj; + u32 addr = GetArg(); + + for (size_t i = 0; i < sizeof(T); i += 1, addr += 1) + { + reinterpret_cast(&obj)[i] = PowerPC::HostRead_U8(addr); + } + + return obj; + } + + // 1 - arg_WORD + template >* = nullptr> + T GetArg() + { + static_assert(!std::is_pointer(), "VAList doesn't support pointers"); + u64 value; + + if (m_gpr <= m_gpr_max) + { + value = GPR(m_gpr); + m_gpr += 1; + } + else + { + m_stack = Common::AlignUp(m_stack, 4); + value = PowerPC::HostRead_U32(m_stack); + m_stack += 4; + } + + return static_cast(value); + } + + // 2 - arg_DOUBLEWORD + template >* = nullptr> + T GetArg() + { + u64 value; + + if (m_gpr % 2 == 0) + m_gpr += 1; + if (m_gpr < m_gpr_max) + { + value = static_cast(GPR(m_gpr)) << 32 | GPR(m_gpr + 1); + m_gpr += 2; + } + else + { + m_stack = Common::AlignUp(m_stack, 8); + value = PowerPC::HostRead_U64(m_stack); + m_stack += 8; + } + + return static_cast(value); + } + + // 3 - arg_ARGREAL + template >* = nullptr> + T GetArg() + { + double value; + + if (m_fpr <= m_fpr_max) + { + value = rPS0(m_fpr); + m_fpr += 1; + } + else + { + m_stack = Common::AlignUp(m_stack, 8); + const u64 integral = PowerPC::HostRead_U64(m_stack); + std::memcpy(&value, &integral, sizeof(double)); + m_stack += 8; + } + + return static_cast(value); + } + + // Helper + template + T GetArgT() + { + return static_cast(GetArg()); + } + +private: + u32 m_gpr = 3; + u32 m_fpr = 1; + const u32 m_gpr_max = 10; + const u32 m_fpr_max = 8; + u32 m_stack; +}; +}