diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index 2712a9f951..1f7103009b 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -72,6 +72,7 @@ set(SRCS HLE/HLE.cpp HLE/HLE_Misc.cpp HLE/HLE_OS.cpp + HLE/HLE_VarArgs.cpp HW/AudioInterface.cpp HW/CPU.cpp HW/DSP.cpp diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj index a13c4ea09c..a6334c1bb9 100644 --- a/Source/Core/Core/Core.vcxproj +++ b/Source/Core/Core/Core.vcxproj @@ -100,6 +100,7 @@ + @@ -343,6 +344,7 @@ + diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters index 5f10c6fbd5..2cdf6affa7 100644 --- a/Source/Core/Core/Core.vcxproj.filters +++ b/Source/Core/Core/Core.vcxproj.filters @@ -293,6 +293,9 @@ HLE + + HLE + PowerPC @@ -955,6 +958,9 @@ HLE + + HLE + PowerPC\Cached Interpreter diff --git a/Source/Core/Core/HLE/HLE.cpp b/Source/Core/Core/HLE/HLE.cpp index 4671ce1fd8..cc166768da 100644 --- a/Source/Core/Core/HLE/HLE.cpp +++ b/Source/Core/Core/HLE/HLE.cpp @@ -60,8 +60,12 @@ static const SPatch OSPatches[] = { {"OSReport", HLE_OS::HLE_GeneralDebugPrint, HLE_HOOK_START, HLE_TYPE_DEBUG}, {"DEBUGPrint", HLE_OS::HLE_GeneralDebugPrint, HLE_HOOK_START, HLE_TYPE_DEBUG}, {"WUD_DEBUGPrint", HLE_OS::HLE_GeneralDebugPrint, HLE_HOOK_START, HLE_TYPE_DEBUG}, - {"vprintf", HLE_OS::HLE_GeneralDebugPrint, HLE_HOOK_START, HLE_TYPE_DEBUG}, + {"vprintf", HLE_OS::HLE_GeneralDebugVPrint, HLE_HOOK_START, HLE_TYPE_DEBUG}, {"printf", HLE_OS::HLE_GeneralDebugPrint, HLE_HOOK_START, HLE_TYPE_DEBUG}, + {"vdprintf", HLE_OS::HLE_LogVDPrint, HLE_HOOK_START, HLE_TYPE_DEBUG}, + {"dprintf", HLE_OS::HLE_LogDPrint, HLE_HOOK_START, HLE_TYPE_DEBUG}, + {"vfprintf", HLE_OS::HLE_LogVFPrint, HLE_HOOK_START, HLE_TYPE_DEBUG}, + {"fprintf", HLE_OS::HLE_LogFPrint, HLE_HOOK_START, HLE_TYPE_DEBUG}, {"nlPrintf", HLE_OS::HLE_GeneralDebugPrint, HLE_HOOK_START, HLE_TYPE_DEBUG}, {"puts", HLE_OS::HLE_GeneralDebugPrint, HLE_HOOK_START, HLE_TYPE_DEBUG}, // gcc-optimized printf? {"___blank", HLE_OS::HLE_GeneralDebugPrint, HLE_HOOK_START, HLE_TYPE_DEBUG}, // used for early init things (normally) diff --git a/Source/Core/Core/HLE/HLE_OS.cpp b/Source/Core/Core/HLE/HLE_OS.cpp index 0135de898e..56c81377a2 100644 --- a/Source/Core/Core/HLE/HLE_OS.cpp +++ b/Source/Core/Core/HLE/HLE_OS.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. +#include #include #include "Common/CommonTypes.h" @@ -9,12 +10,20 @@ #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" namespace HLE_OS { -std::string GetStringVA(u32 strReg = 3); +enum class ParameterType : bool +{ + ParameterList = false, + VariableArgumentList = true +}; + +std::string GetStringVA(u32 str_reg = 3, + ParameterType parameter_type = ParameterType::ParameterList); void HLE_OSPanic() { @@ -27,8 +36,8 @@ void HLE_OSPanic() NPC = LR; } -// Generalized func for just printing string pointed to by r3. -void HLE_GeneralDebugPrint() +// Generalized function for printing formatted string. +void HLE_GeneralDebugPrint(ParameterType parameter_type) { std::string report_message; @@ -38,12 +47,12 @@ void HLE_GeneralDebugPrint() if (PowerPC::HostIsRAMAddress(GPR(4))) { // ___blank(void* this, const char* fmt, ...); - report_message = GetStringVA(4); + report_message = GetStringVA(4, parameter_type); } else { // ___blank(void* this, int log_type, const char* fmt, ...); - report_message = GetStringVA(5); + report_message = GetStringVA(5, parameter_type); } } else @@ -51,12 +60,12 @@ void HLE_GeneralDebugPrint() if (PowerPC::HostIsRAMAddress(GPR(3))) { // ___blank(const char* fmt, ...); - report_message = GetStringVA(); + report_message = GetStringVA(3, parameter_type); } else { // ___blank(int log_type, const char* fmt, ...); - report_message = GetStringVA(4); + report_message = GetStringVA(4, parameter_type); } } @@ -65,6 +74,18 @@ void HLE_GeneralDebugPrint() NOTICE_LOG(OSREPORT, "%08x->%08x| %s", LR, PC, SHIFTJISToUTF8(report_message).c_str()); } +// Generalized function for printing formatted string using parameter list. +void HLE_GeneralDebugPrint() +{ + HLE_GeneralDebugPrint(ParameterType::ParameterList); +} + +// Generalized function for printing formatted string using va_list. +void HLE_GeneralDebugVPrint() +{ + HLE_GeneralDebugPrint(ParameterType::VariableArgumentList); +} + // __write_console(int fd, const void* buffer, const u32* size) void HLE_write_console() { @@ -89,14 +110,79 @@ void HLE_write_console() NOTICE_LOG(OSREPORT, "%08x->%08x| %s", LR, PC, SHIFTJISToUTF8(report_message).c_str()); } -std::string GetStringVA(u32 strReg) +// Log (v)dprintf message if fd is 1 (stdout) or 2 (stderr) +void HLE_LogDPrint(ParameterType parameter_type) +{ + NPC = LR; + + if (GPR(3) != 1 && GPR(3) != 2) + return; + + std::string report_message = GetStringVA(4, parameter_type); + NOTICE_LOG(OSREPORT, "%08x->%08x| %s", LR, PC, SHIFTJISToUTF8(report_message).c_str()); +} + +// Log dprintf message +// -> int dprintf(int fd, const char* format, ...); +void HLE_LogDPrint() +{ + HLE_LogDPrint(ParameterType::ParameterList); +} + +// Log vdprintf message +// -> int vdprintf(int fd, const char* format, va_list ap); +void HLE_LogVDPrint() +{ + HLE_LogDPrint(ParameterType::VariableArgumentList); +} + +// Log (v)fprintf message if FILE is stdout or stderr +void HLE_LogFPrint(ParameterType parameter_type) +{ + NPC = LR; + + // The structure FILE is implementation defined. + // Both libogc and Dolphin SDK seem to store the fd at the same address. + int fd = -1; + if (PowerPC::HostIsRAMAddress(GPR(3)) && PowerPC::HostIsRAMAddress(GPR(3) + 0xF)) + { + // The fd is stored as a short at FILE+0xE. + fd = static_cast(PowerPC::HostRead_U16(GPR(3) + 0xE)); + } + if (fd != 1 && fd != 2) + { + // On RVL SDK it seems stored at FILE+0x2. + fd = static_cast(PowerPC::HostRead_U16(GPR(3) + 0x2)); + } + if (fd != 1 && fd != 2) + return; + + std::string report_message = GetStringVA(4, parameter_type); + NOTICE_LOG(OSREPORT, "%08x->%08x| %s", LR, PC, SHIFTJISToUTF8(report_message).c_str()); +} + +// Log fprintf message +// -> int fprintf(FILE* stream, const char* format, ...); +void HLE_LogFPrint() +{ + HLE_LogFPrint(ParameterType::ParameterList); +} + +// Log vfprintf message +// -> int vfprintf(FILE* stream, const char* format, va_list ap); +void HLE_LogVFPrint() +{ + HLE_LogFPrint(ParameterType::VariableArgumentList); +} + +std::string GetStringVA(u32 str_reg, ParameterType parameter_type) { 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)); + auto ap = parameter_type == ParameterType::VariableArgumentList ? + std::make_unique(GPR(str_reg + 1)) : + std::make_unique(GPR(1) + 0x8, str_reg + 1); for (size_t i = 0; i < string.size(); i++) { @@ -120,38 +206,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 +221,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)); // %n doesn't output anything, so the result variable is untouched + PowerPC::HostWrite_U32(static_cast(result.size()), ap->GetArgT()); 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_OS.h b/Source/Core/Core/HLE/HLE_OS.h index d729a8842f..af3abb3139 100644 --- a/Source/Core/Core/HLE/HLE_OS.h +++ b/Source/Core/Core/HLE/HLE_OS.h @@ -7,6 +7,11 @@ namespace HLE_OS { void HLE_GeneralDebugPrint(); +void HLE_GeneralDebugVPrint(); void HLE_write_console(); void HLE_OSPanic(); +void HLE_LogDPrint(); +void HLE_LogVDPrint(); +void HLE_LogFPrint(); +void HLE_LogVFPrint(); } diff --git a/Source/Core/Core/HLE/HLE_VarArgs.cpp b/Source/Core/Core/HLE/HLE_VarArgs.cpp new file mode 100644 index 0000000000..1b9b14e760 --- /dev/null +++ b/Source/Core/Core/HLE/HLE_VarArgs.cpp @@ -0,0 +1,67 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "Core/HLE/HLE_VarArgs.h" + +#include "Common/Logging/Log.h" + +HLE::SystemVABI::VAList::~VAList() = default; + +u32 HLE::SystemVABI::VAList::GetGPR(u32 gpr) const +{ + return GPR(gpr); +} + +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 new file mode 100644 index 0000000000..3c72c1f05e --- /dev/null +++ b/Source/Core/Core/HLE/HLE_VarArgs.h @@ -0,0 +1,177 @@ +// Copyright 2017 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 +{ +namespace SystemVABI +{ +// 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(); + +// 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) + { + } + virtual ~VAList(); + + // 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 = GetGPR(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(GetGPR(m_gpr)) << 32 | GetGPR(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 = GetFPR(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()); + } + +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