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