HLE_OS: Manually handle printfs from emulated software to prevent emulated software from crashing Dolphin with an invalid printf formatting string.
This commit is contained in:
parent
7f29f0398c
commit
9f817911fd
|
@ -3,9 +3,15 @@
|
|||
|
||||
#include "Core/HLE/HLE_OS.h"
|
||||
|
||||
#include <cinttypes>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <fmt/printf.h>
|
||||
|
||||
#include "Common/BitUtils.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/MsgHandler.h"
|
||||
|
@ -204,82 +210,365 @@ void HLE_LogVFPrint(const Core::CPUThreadGuard& guard)
|
|||
HLE_LogFPrint(guard, ParameterType::VariableArgumentList);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
class HLEPrintArgsVAList final : public HLEPrintArgs
|
||||
{
|
||||
public:
|
||||
HLEPrintArgsVAList(const Core::CPUThreadGuard& guard, HLE::SystemVABI::VAList* va_list)
|
||||
: m_guard(guard), m_va_list(va_list)
|
||||
{
|
||||
}
|
||||
|
||||
u32 GetU32() override { return m_va_list->GetArgT<u32>(); }
|
||||
u64 GetU64() override { return m_va_list->GetArgT<u64>(); }
|
||||
double GetF64() override { return m_va_list->GetArgT<double>(); }
|
||||
std::string GetString(std::optional<u32> max_length) override
|
||||
{
|
||||
return max_length == 0u ? std::string() :
|
||||
PowerPC::MMU::HostGetString(m_guard, m_va_list->GetArgT<u32>(),
|
||||
max_length.value_or(0u));
|
||||
}
|
||||
std::u16string GetU16String(std::optional<u32> max_length) override
|
||||
{
|
||||
return max_length == 0u ? std::u16string() :
|
||||
PowerPC::MMU::HostGetU16String(m_guard, m_va_list->GetArgT<u32>(),
|
||||
max_length.value_or(0u));
|
||||
}
|
||||
|
||||
private:
|
||||
const Core::CPUThreadGuard& m_guard;
|
||||
HLE::SystemVABI::VAList* m_va_list;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
std::string GetStringVA(Core::System& system, const Core::CPUThreadGuard& guard, u32 str_reg,
|
||||
ParameterType parameter_type)
|
||||
{
|
||||
auto& ppc_state = system.GetPPCState();
|
||||
|
||||
std::string ArgumentBuffer;
|
||||
std::string result;
|
||||
std::string string = PowerPC::MMU::HostGetString(guard, ppc_state.gpr[str_reg]);
|
||||
auto ap =
|
||||
std::unique_ptr<HLE::SystemVABI::VAList> ap =
|
||||
parameter_type == ParameterType::VariableArgumentList ?
|
||||
std::make_unique<HLE::SystemVABI::VAListStruct>(system, guard,
|
||||
ppc_state.gpr[str_reg + 1]) :
|
||||
std::make_unique<HLE::SystemVABI::VAList>(system, ppc_state.gpr[1] + 0x8, str_reg + 1);
|
||||
std::make_unique<HLE::SystemVABI::VAListStruct>(guard, ppc_state.gpr[str_reg + 1]) :
|
||||
std::make_unique<HLE::SystemVABI::VAList>(guard, ppc_state.gpr[1] + 0x8, str_reg + 1);
|
||||
|
||||
for (size_t i = 0; i < string.size(); i++)
|
||||
HLEPrintArgsVAList args(guard, ap.get());
|
||||
return GetStringVA(&args, string);
|
||||
}
|
||||
|
||||
std::string GetStringVA(HLEPrintArgs* args, std::string_view string)
|
||||
{
|
||||
std::string result;
|
||||
for (size_t i = 0; i < string.size(); ++i)
|
||||
{
|
||||
if (string[i] == '%')
|
||||
{
|
||||
ArgumentBuffer = '%';
|
||||
i++;
|
||||
if (string[i] == '%')
|
||||
{
|
||||
result += '%';
|
||||
continue;
|
||||
}
|
||||
|
||||
while (i < string.size() &&
|
||||
(string[i] < 'A' || string[i] > 'z' || string[i] == 'l' || string[i] == '-'))
|
||||
{
|
||||
ArgumentBuffer += string[i++];
|
||||
}
|
||||
if (i >= string.size())
|
||||
break;
|
||||
|
||||
ArgumentBuffer += string[i];
|
||||
|
||||
switch (string[i])
|
||||
{
|
||||
case 's':
|
||||
result +=
|
||||
StringFromFormat(ArgumentBuffer.c_str(),
|
||||
PowerPC::MMU::HostGetString(guard, ap->GetArgT<u32>(guard)).c_str());
|
||||
break;
|
||||
|
||||
case 'a':
|
||||
case 'A':
|
||||
case 'e':
|
||||
case 'E':
|
||||
case 'f':
|
||||
case 'F':
|
||||
case 'g':
|
||||
case 'G':
|
||||
result += StringFromFormat(ArgumentBuffer.c_str(), ap->GetArgT<double>(guard));
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
// Override, so 64bit Dolphin prints 32bit pointers, since the ppc is 32bit :)
|
||||
result += StringFromFormat("%x", ap->GetArgT<u32>(guard));
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
// %n doesn't output anything, so the result variable is untouched
|
||||
// the actual PPC function will take care of the memory write
|
||||
break;
|
||||
|
||||
default:
|
||||
if (string[i - 1] == 'l' && string[i - 2] == 'l')
|
||||
result += StringFromFormat(ArgumentBuffer.c_str(), ap->GetArgT<u64>(guard));
|
||||
else
|
||||
result += StringFromFormat(ArgumentBuffer.c_str(), ap->GetArgT<u32>(guard));
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
if (string[i] != '%')
|
||||
{
|
||||
result += string[i];
|
||||
continue;
|
||||
}
|
||||
|
||||
const size_t formatting_start_position = i;
|
||||
++i;
|
||||
if (i < string.size() && string[i] == '%')
|
||||
{
|
||||
result += '%';
|
||||
continue;
|
||||
}
|
||||
|
||||
bool left_justified_flag = false;
|
||||
bool sign_prepended_flag = false;
|
||||
bool space_prepended_flag = false;
|
||||
bool alternative_form_flag = false;
|
||||
bool padding_zeroes_flag = false;
|
||||
while (i < string.size())
|
||||
{
|
||||
if (string[i] == '-')
|
||||
left_justified_flag = true;
|
||||
else if (string[i] == '+')
|
||||
sign_prepended_flag = true;
|
||||
else if (string[i] == ' ')
|
||||
space_prepended_flag = true;
|
||||
else if (string[i] == '#')
|
||||
alternative_form_flag = true;
|
||||
else if (string[i] == '0')
|
||||
padding_zeroes_flag = true;
|
||||
else
|
||||
break;
|
||||
++i;
|
||||
}
|
||||
|
||||
const auto take_field_or_precision = [&](bool* left_justified_flag_ptr) -> std::optional<u32> {
|
||||
if (i >= string.size())
|
||||
return std::nullopt;
|
||||
|
||||
if (string[i] == '*')
|
||||
{
|
||||
++i;
|
||||
const s32 result = Common::BitCast<s32>(args->GetU32());
|
||||
if (result >= 0)
|
||||
return static_cast<u32>(result);
|
||||
|
||||
if (left_justified_flag_ptr)
|
||||
{
|
||||
// field width; this results in positive field width and left_justified flag set
|
||||
*left_justified_flag_ptr = true;
|
||||
return static_cast<u32>(-static_cast<s64>(result));
|
||||
}
|
||||
|
||||
// precision; this is ignored
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
size_t start = i;
|
||||
while (i < string.size() && string[i] >= '0' && string[i] <= '9')
|
||||
++i;
|
||||
if (start != i)
|
||||
{
|
||||
while (start < i && string[start] == '0')
|
||||
++start;
|
||||
if (start == i)
|
||||
return 0;
|
||||
u32 result = 0;
|
||||
const std::string tmp(string, start, i - start);
|
||||
if (TryParse(tmp, &result, 10))
|
||||
return result;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
};
|
||||
|
||||
const std::optional<u32> field_width = take_field_or_precision(&left_justified_flag);
|
||||
std::optional<u32> precision = std::nullopt;
|
||||
if (i < string.size() && string[i] == '.')
|
||||
{
|
||||
++i;
|
||||
precision = take_field_or_precision(nullptr).value_or(0);
|
||||
}
|
||||
|
||||
enum class LengthModifier
|
||||
{
|
||||
None,
|
||||
hh,
|
||||
h,
|
||||
l,
|
||||
ll,
|
||||
L,
|
||||
};
|
||||
auto length_modifier = LengthModifier::None;
|
||||
|
||||
if (i < string.size() && (string[i] == 'h' || string[i] == 'l' || string[i] == 'L'))
|
||||
{
|
||||
++i;
|
||||
if (i < string.size() && string[i - 1] == 'h' && string[i] == 'h')
|
||||
{
|
||||
++i;
|
||||
length_modifier = LengthModifier::hh;
|
||||
}
|
||||
else if (i < string.size() && string[i - 1] == 'l' && string[i] == 'l')
|
||||
{
|
||||
++i;
|
||||
length_modifier = LengthModifier::ll;
|
||||
}
|
||||
else if (string[i - 1] == 'h')
|
||||
{
|
||||
length_modifier = LengthModifier::h;
|
||||
}
|
||||
else if (string[i - 1] == 'l')
|
||||
{
|
||||
length_modifier = LengthModifier::l;
|
||||
}
|
||||
else if (string[i - 1] == 'L')
|
||||
{
|
||||
length_modifier = LengthModifier::L;
|
||||
}
|
||||
}
|
||||
|
||||
if (i >= string.size())
|
||||
{
|
||||
// not a valid formatting string, print the formatting string as-is
|
||||
result += string.substr(formatting_start_position);
|
||||
break;
|
||||
}
|
||||
|
||||
const char format_specifier = string[i];
|
||||
switch (format_specifier)
|
||||
{
|
||||
case 's':
|
||||
{
|
||||
if (length_modifier == LengthModifier::l)
|
||||
{
|
||||
// This is a bit of a mess... wchar_t could be 16 bits or 32 bits per character depending
|
||||
// on the software. Retail software seems usually to use 16 bits and homebrew 32 bits, but
|
||||
// that's really just a guess. Ideally we can figure out a way to autodetect this, but if
|
||||
// not we should probably just expose a setting for it in the debugger somewhere. For now
|
||||
// we just assume 16 bits.
|
||||
fmt::format_to(
|
||||
std::back_inserter(result), fmt::runtime(left_justified_flag ? "{0:<{1}}" : "{0:>{1}}"),
|
||||
UTF8ToSHIFTJIS(UTF16ToUTF8(args->GetU16String(precision))), field_width.value_or(0));
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt::format_to(std::back_inserter(result),
|
||||
fmt::runtime(left_justified_flag ? "{0:<{1}}" : "{0:>{1}}"),
|
||||
args->GetString(precision), field_width.value_or(0));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'c':
|
||||
{
|
||||
const s32 value = Common::BitCast<s32>(args->GetU32());
|
||||
if (length_modifier == LengthModifier::l)
|
||||
{
|
||||
// Same problem as with wide strings here.
|
||||
const char16_t wide_char = static_cast<char16_t>(value);
|
||||
fmt::format_to(std::back_inserter(result),
|
||||
fmt::runtime(left_justified_flag ? "{0:<{1}}" : "{0:>{1}}"),
|
||||
UTF8ToSHIFTJIS(UTF16ToUTF8(std::u16string_view(&wide_char, 1))),
|
||||
field_width.value_or(0));
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt::format_to(std::back_inserter(result),
|
||||
fmt::runtime(left_justified_flag ? "{0:<{1}}" : "{0:>{1}}"),
|
||||
static_cast<char>(value), field_width.value_or(0));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'd':
|
||||
case 'i':
|
||||
{
|
||||
const auto options = fmt::format(
|
||||
"{}{}{}{}{}{}", left_justified_flag ? "-" : "", sign_prepended_flag ? "+" : "",
|
||||
space_prepended_flag ? " " : "", padding_zeroes_flag ? "0" : "",
|
||||
field_width ? fmt::format("{}", *field_width) : "",
|
||||
precision ? fmt::format(".{}", *precision) : "");
|
||||
if (length_modifier == LengthModifier::ll)
|
||||
{
|
||||
const s64 value = Common::BitCast<s64>(args->GetU64());
|
||||
result += fmt::sprintf(fmt::format("%{}" PRId64, options).c_str(), value);
|
||||
}
|
||||
else
|
||||
{
|
||||
s32 value = Common::BitCast<s32>(args->GetU32());
|
||||
if (length_modifier == LengthModifier::h)
|
||||
value = static_cast<s16>(value);
|
||||
else if (length_modifier == LengthModifier::hh)
|
||||
value = static_cast<s8>(value);
|
||||
result += fmt::sprintf(fmt::format("%{}" PRId32, options).c_str(), value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'o':
|
||||
{
|
||||
const auto options = fmt::format(
|
||||
"{}{}{}{}{}{}{}", left_justified_flag ? "-" : "", sign_prepended_flag ? "+" : "",
|
||||
space_prepended_flag ? " " : "", alternative_form_flag ? "#" : "",
|
||||
padding_zeroes_flag ? "0" : "", field_width ? fmt::format("{}", *field_width) : "",
|
||||
precision ? fmt::format(".{}", *precision) : "");
|
||||
if (length_modifier == LengthModifier::ll)
|
||||
{
|
||||
const u64 value = args->GetU64();
|
||||
result += fmt::sprintf(fmt::format("%{}" PRIo64, options).c_str(), value);
|
||||
}
|
||||
else
|
||||
{
|
||||
u32 value = args->GetU32();
|
||||
if (length_modifier == LengthModifier::h)
|
||||
value = static_cast<u16>(value);
|
||||
else if (length_modifier == LengthModifier::hh)
|
||||
value = static_cast<u8>(value);
|
||||
result += fmt::sprintf(fmt::format("%{}" PRIo32, options).c_str(), value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'x':
|
||||
case 'X':
|
||||
{
|
||||
const auto options = fmt::format(
|
||||
"{}{}{}{}{}{}{}", left_justified_flag ? "-" : "", sign_prepended_flag ? "+" : "",
|
||||
space_prepended_flag ? " " : "", alternative_form_flag ? "#" : "",
|
||||
padding_zeroes_flag ? "0" : "", field_width ? fmt::format("{}", *field_width) : "",
|
||||
precision ? fmt::format(".{}", *precision) : "");
|
||||
if (length_modifier == LengthModifier::ll)
|
||||
{
|
||||
const u64 value = args->GetU64();
|
||||
result += fmt::sprintf(
|
||||
fmt::format("%{}{}", options, format_specifier == 'x' ? PRIx64 : PRIX64).c_str(),
|
||||
value);
|
||||
}
|
||||
else
|
||||
{
|
||||
u32 value = args->GetU32();
|
||||
if (length_modifier == LengthModifier::h)
|
||||
value = static_cast<u16>(value);
|
||||
else if (length_modifier == LengthModifier::hh)
|
||||
value = static_cast<u8>(value);
|
||||
result += fmt::sprintf(
|
||||
fmt::format("%{}{}", options, format_specifier == 'x' ? PRIx32 : PRIX32).c_str(),
|
||||
value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'u':
|
||||
{
|
||||
const auto options = fmt::format(
|
||||
"{}{}{}{}{}{}", left_justified_flag ? "-" : "", sign_prepended_flag ? "+" : "",
|
||||
space_prepended_flag ? " " : "", padding_zeroes_flag ? "0" : "",
|
||||
field_width ? fmt::format("{}", *field_width) : "",
|
||||
precision ? fmt::format(".{}", *precision) : "");
|
||||
if (length_modifier == LengthModifier::ll)
|
||||
{
|
||||
const u64 value = args->GetU64();
|
||||
result += fmt::sprintf(fmt::format("%{}" PRIu64, options).c_str(), value);
|
||||
}
|
||||
else
|
||||
{
|
||||
u32 value = args->GetU32();
|
||||
if (length_modifier == LengthModifier::h)
|
||||
value = static_cast<u16>(value);
|
||||
else if (length_modifier == LengthModifier::hh)
|
||||
value = static_cast<u8>(value);
|
||||
result += fmt::sprintf(fmt::format("%{}" PRIu32, options).c_str(), value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'f':
|
||||
case 'F':
|
||||
case 'e':
|
||||
case 'E':
|
||||
case 'a':
|
||||
case 'A':
|
||||
case 'g':
|
||||
case 'G':
|
||||
{
|
||||
const auto options = fmt::format(
|
||||
"{}{}{}{}{}{}{}", left_justified_flag ? "-" : "", sign_prepended_flag ? "+" : "",
|
||||
space_prepended_flag ? " " : "", alternative_form_flag ? "#" : "",
|
||||
padding_zeroes_flag ? "0" : "", field_width ? fmt::format("{}", *field_width) : "",
|
||||
precision ? fmt::format(".{}", *precision) : "");
|
||||
double value = args->GetF64();
|
||||
result += fmt::sprintf(fmt::format("%{}{}", options, format_specifier).c_str(), value);
|
||||
break;
|
||||
}
|
||||
case 'n':
|
||||
// %n doesn't output anything, so the result variable is untouched
|
||||
// the actual PPC function will take care of the memory write
|
||||
break;
|
||||
case 'p':
|
||||
{
|
||||
const auto options =
|
||||
fmt::format("{}{}{}{}{}", left_justified_flag ? "-" : "", sign_prepended_flag ? "+" : "",
|
||||
space_prepended_flag ? " " : "", padding_zeroes_flag ? "0" : "",
|
||||
field_width ? fmt::format("{}", *field_width) : "");
|
||||
const u32 value = args->GetU32();
|
||||
result += fmt::sprintf(fmt::format("%{}" PRIx32, options).c_str(), value);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// invalid conversion specifier, print the formatting string as-is
|
||||
result += string.substr(formatting_start_position, formatting_start_position - i + 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,11 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
namespace Core
|
||||
{
|
||||
class CPUThreadGuard;
|
||||
|
@ -10,6 +15,18 @@ class CPUThreadGuard;
|
|||
|
||||
namespace HLE_OS
|
||||
{
|
||||
class HLEPrintArgs
|
||||
{
|
||||
public:
|
||||
virtual u32 GetU32() = 0;
|
||||
virtual u64 GetU64() = 0;
|
||||
virtual double GetF64() = 0;
|
||||
virtual std::string GetString(std::optional<u32> max_length) = 0;
|
||||
virtual std::u16string GetU16String(std::optional<u32> max_length) = 0;
|
||||
};
|
||||
|
||||
std::string GetStringVA(HLEPrintArgs* args, std::string_view string);
|
||||
|
||||
void HLE_GeneralDebugPrint(const Core::CPUThreadGuard& guard);
|
||||
void HLE_GeneralDebugVPrint(const Core::CPUThreadGuard& guard);
|
||||
void HLE_write_console(const Core::CPUThreadGuard& guard);
|
||||
|
|
|
@ -2,29 +2,29 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "Core/HLE/HLE_VarArgs.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/System.h"
|
||||
|
||||
#include "Common/Logging/Log.h"
|
||||
|
||||
HLE::SystemVABI::VAList::~VAList() = default;
|
||||
|
||||
u32 HLE::SystemVABI::VAList::GetGPR(const Core::CPUThreadGuard&, u32 gpr) const
|
||||
u32 HLE::SystemVABI::VAList::GetGPR(u32 gpr) const
|
||||
{
|
||||
return m_system.GetPPCState().gpr[gpr];
|
||||
return m_guard.GetSystem().GetPPCState().gpr[gpr];
|
||||
}
|
||||
|
||||
double HLE::SystemVABI::VAList::GetFPR(const Core::CPUThreadGuard&, u32 fpr) const
|
||||
double HLE::SystemVABI::VAList::GetFPR(u32 fpr) const
|
||||
{
|
||||
return m_system.GetPPCState().ps[fpr].PS0AsDouble();
|
||||
return m_guard.GetSystem().GetPPCState().ps[fpr].PS0AsDouble();
|
||||
}
|
||||
|
||||
HLE::SystemVABI::VAListStruct::VAListStruct(Core::System& system, const Core::CPUThreadGuard& guard,
|
||||
u32 address)
|
||||
: VAList(system, 0), m_va_list{PowerPC::MMU::HostRead_U8(guard, address),
|
||||
PowerPC::MMU::HostRead_U8(guard, address + 1),
|
||||
PowerPC::MMU::HostRead_U32(guard, address + 4),
|
||||
PowerPC::MMU::HostRead_U32(guard, address + 8)},
|
||||
m_address(address), m_has_fpr_area(system.GetPPCState().cr.GetBit(6) == 1)
|
||||
HLE::SystemVABI::VAListStruct::VAListStruct(const Core::CPUThreadGuard& guard, u32 address)
|
||||
: VAList(guard, 0), m_va_list{PowerPC::MMU::HostRead_U8(guard, address),
|
||||
PowerPC::MMU::HostRead_U8(guard, address + 1),
|
||||
PowerPC::MMU::HostRead_U32(guard, address + 4),
|
||||
PowerPC::MMU::HostRead_U32(guard, address + 8)},
|
||||
m_address(address), m_has_fpr_area(guard.GetSystem().GetPPCState().cr.GetBit(6) == 1)
|
||||
{
|
||||
m_stack = m_va_list.overflow_arg_area;
|
||||
m_gpr += m_va_list.gpr;
|
||||
|
@ -41,7 +41,7 @@ u32 HLE::SystemVABI::VAListStruct::GetFPRArea() const
|
|||
return GetGPRArea() + 4 * 8;
|
||||
}
|
||||
|
||||
u32 HLE::SystemVABI::VAListStruct::GetGPR(const Core::CPUThreadGuard& guard, u32 gpr) const
|
||||
u32 HLE::SystemVABI::VAListStruct::GetGPR(u32 gpr) const
|
||||
{
|
||||
if (gpr < 3 || gpr > 10)
|
||||
{
|
||||
|
@ -49,10 +49,10 @@ u32 HLE::SystemVABI::VAListStruct::GetGPR(const Core::CPUThreadGuard& guard, u32
|
|||
return 0;
|
||||
}
|
||||
const u32 gpr_address = Common::AlignUp(GetGPRArea() + 4 * (gpr - 3), 4);
|
||||
return PowerPC::MMU::HostRead_U32(guard, gpr_address);
|
||||
return PowerPC::MMU::HostRead_U32(m_guard, gpr_address);
|
||||
}
|
||||
|
||||
double HLE::SystemVABI::VAListStruct::GetFPR(const Core::CPUThreadGuard& guard, u32 fpr) const
|
||||
double HLE::SystemVABI::VAListStruct::GetFPR(u32 fpr) const
|
||||
{
|
||||
if (!m_has_fpr_area || fpr < 1 || fpr > 8)
|
||||
{
|
||||
|
@ -60,5 +60,5 @@ double HLE::SystemVABI::VAListStruct::GetFPR(const Core::CPUThreadGuard& guard,
|
|||
return 0.0;
|
||||
}
|
||||
const u32 fpr_address = Common::AlignUp(GetFPRArea() + 8 * (fpr - 1), 8);
|
||||
return PowerPC::MMU::HostRead_F64(guard, fpr_address);
|
||||
return PowerPC::MMU::HostRead_F64(m_guard, fpr_address);
|
||||
}
|
||||
|
|
|
@ -3,14 +3,14 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "Common/Align.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
#include "Core/PowerPC/MMU.h"
|
||||
#include "Core/PowerPC/PowerPC.h"
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace Core
|
||||
{
|
||||
class CPUThreadGuard;
|
||||
|
@ -38,9 +38,9 @@ constexpr bool IS_ARG_REAL = std::is_floating_point<T>();
|
|||
class VAList
|
||||
{
|
||||
public:
|
||||
explicit VAList(Core::System& system, u32 stack, u32 gpr = 3, u32 fpr = 1, u32 gpr_max = 10,
|
||||
u32 fpr_max = 8)
|
||||
: m_system(system), m_gpr(gpr), m_fpr(fpr), m_gpr_max(gpr_max), m_fpr_max(fpr_max),
|
||||
explicit VAList(const Core::CPUThreadGuard& guard, u32 stack, u32 gpr = 3, u32 fpr = 1,
|
||||
u32 gpr_max = 10, u32 fpr_max = 8)
|
||||
: m_guard(guard), m_gpr(gpr), m_fpr(fpr), m_gpr_max(gpr_max), m_fpr_max(fpr_max),
|
||||
m_stack(stack)
|
||||
{
|
||||
}
|
||||
|
@ -48,14 +48,14 @@ public:
|
|||
|
||||
// 0 - arg_ARGPOINTER
|
||||
template <typename T, typename std::enable_if_t<IS_ARG_POINTER<T>>* = nullptr>
|
||||
T GetArg(const Core::CPUThreadGuard& guard)
|
||||
T GetArg()
|
||||
{
|
||||
T obj;
|
||||
u32 addr = GetArg<u32>(guard);
|
||||
u32 addr = GetArg<u32>();
|
||||
|
||||
for (size_t i = 0; i < sizeof(T); i += 1, addr += 1)
|
||||
{
|
||||
reinterpret_cast<u8*>(&obj)[i] = PowerPC::MMU::HostRead_U8(guard, addr);
|
||||
reinterpret_cast<u8*>(&obj)[i] = PowerPC::MMU::HostRead_U8(m_guard, addr);
|
||||
}
|
||||
|
||||
return obj;
|
||||
|
@ -63,20 +63,20 @@ public:
|
|||
|
||||
// 1 - arg_WORD
|
||||
template <typename T, typename std::enable_if_t<IS_WORD<T>>* = nullptr>
|
||||
T GetArg(const Core::CPUThreadGuard& guard)
|
||||
T GetArg()
|
||||
{
|
||||
static_assert(!std::is_pointer<T>(), "VAList doesn't support pointers");
|
||||
u64 value;
|
||||
|
||||
if (m_gpr <= m_gpr_max)
|
||||
{
|
||||
value = GetGPR(guard, m_gpr);
|
||||
value = GetGPR(m_gpr);
|
||||
m_gpr += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_stack = Common::AlignUp(m_stack, 4);
|
||||
value = PowerPC::MMU::HostRead_U32(guard, m_stack);
|
||||
value = PowerPC::MMU::HostRead_U32(m_guard, m_stack);
|
||||
m_stack += 4;
|
||||
}
|
||||
|
||||
|
@ -85,7 +85,7 @@ public:
|
|||
|
||||
// 2 - arg_DOUBLEWORD
|
||||
template <typename T, typename std::enable_if_t<IS_DOUBLE_WORD<T>>* = nullptr>
|
||||
T GetArg(const Core::CPUThreadGuard& guard)
|
||||
T GetArg()
|
||||
{
|
||||
u64 value;
|
||||
|
||||
|
@ -93,13 +93,13 @@ public:
|
|||
m_gpr += 1;
|
||||
if (m_gpr < m_gpr_max)
|
||||
{
|
||||
value = static_cast<u64>(GetGPR(guard, m_gpr)) << 32 | GetGPR(guard, m_gpr + 1);
|
||||
value = static_cast<u64>(GetGPR(m_gpr)) << 32 | GetGPR(m_gpr + 1);
|
||||
m_gpr += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_stack = Common::AlignUp(m_stack, 8);
|
||||
value = PowerPC::MMU::HostRead_U64(guard, m_stack);
|
||||
value = PowerPC::MMU::HostRead_U64(m_guard, m_stack);
|
||||
m_stack += 8;
|
||||
}
|
||||
|
||||
|
@ -108,19 +108,19 @@ public:
|
|||
|
||||
// 3 - arg_ARGREAL
|
||||
template <typename T, typename std::enable_if_t<IS_ARG_REAL<T>>* = nullptr>
|
||||
T GetArg(const Core::CPUThreadGuard& guard)
|
||||
T GetArg()
|
||||
{
|
||||
double value;
|
||||
|
||||
if (m_fpr <= m_fpr_max)
|
||||
{
|
||||
value = GetFPR(guard, m_fpr);
|
||||
value = GetFPR(m_fpr);
|
||||
m_fpr += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_stack = Common::AlignUp(m_stack, 8);
|
||||
value = PowerPC::MMU::HostRead_F64(guard, m_stack);
|
||||
value = PowerPC::MMU::HostRead_F64(m_guard, m_stack);
|
||||
m_stack += 8;
|
||||
}
|
||||
|
||||
|
@ -129,13 +129,13 @@ public:
|
|||
|
||||
// Helper
|
||||
template <typename T>
|
||||
T GetArgT(const Core::CPUThreadGuard& guard)
|
||||
T GetArgT()
|
||||
{
|
||||
return static_cast<T>(GetArg<T>(guard));
|
||||
return static_cast<T>(GetArg<T>());
|
||||
}
|
||||
|
||||
protected:
|
||||
Core::System& m_system;
|
||||
const Core::CPUThreadGuard& m_guard;
|
||||
u32 m_gpr = 3;
|
||||
u32 m_fpr = 1;
|
||||
const u32 m_gpr_max = 10;
|
||||
|
@ -143,8 +143,8 @@ protected:
|
|||
u32 m_stack;
|
||||
|
||||
private:
|
||||
virtual u32 GetGPR(const Core::CPUThreadGuard& guard, u32 gpr) const;
|
||||
virtual double GetFPR(const Core::CPUThreadGuard& guard, u32 fpr) const;
|
||||
virtual u32 GetGPR(u32 gpr) const;
|
||||
virtual double GetFPR(u32 fpr) const;
|
||||
};
|
||||
|
||||
// See System V ABI (SVR4) for more details
|
||||
|
@ -156,7 +156,7 @@ private:
|
|||
class VAListStruct : public VAList
|
||||
{
|
||||
public:
|
||||
explicit VAListStruct(Core::System& system, const Core::CPUThreadGuard& guard, u32 address);
|
||||
explicit VAListStruct(const Core::CPUThreadGuard& guard, u32 address);
|
||||
~VAListStruct() = default;
|
||||
|
||||
private:
|
||||
|
@ -174,8 +174,8 @@ private:
|
|||
u32 GetGPRArea() const;
|
||||
u32 GetFPRArea() const;
|
||||
|
||||
u32 GetGPR(const Core::CPUThreadGuard& guard, u32 gpr) const override;
|
||||
double GetFPR(const Core::CPUThreadGuard& guard, u32 fpr) const override;
|
||||
u32 GetGPR(u32 gpr) const override;
|
||||
double GetFPR(u32 fpr) const override;
|
||||
};
|
||||
|
||||
} // namespace HLE::SystemVABI
|
||||
|
|
Loading…
Reference in New Issue