@@ -358,7 +358,8 @@ bool IsSelfLogging()

// ----------------------
// Code Functions
static bool Subtype_RamWriteAndFill(const ARAddr& addr, const u32 data)
static bool Subtype_RamWriteAndFill(const Core::CPUThreadGuard& guard, const ARAddr& addr,
const u32 data)
{
const u32 new_addr = addr.GCAddress();

@@ -374,7 +375,7 @@ static bool Subtype_RamWriteAndFill(const ARAddr& addr, const u32 data)
const u32 repeat = data >> 8;
for (u32 i = 0; i <= repeat; ++i)
{
PowerPC::HostWrite_U8(data & 0xFF, new_addr + i);
PowerPC::HostWrite_U8(guard, data & 0xFF, new_addr + i);
LogInfo("Wrote {:08x} to address {:08x}", data & 0xFF, new_addr + i);
}
LogInfo("--------");
@@ -388,7 +389,7 @@ static bool Subtype_RamWriteAndFill(const ARAddr& addr, const u32 data)
const u32 repeat = data >> 16;
for (u32 i = 0; i <= repeat; ++i)
{
PowerPC::HostWrite_U16(data & 0xFFFF, new_addr + i * 2);
PowerPC::HostWrite_U16(guard, data & 0xFFFF, new_addr + i * 2);
LogInfo("Wrote {:08x} to address {:08x}", data & 0xFFFF, new_addr + i * 2);
}
LogInfo("--------");
@@ -399,7 +400,7 @@ static bool Subtype_RamWriteAndFill(const ARAddr& addr, const u32 data)
case DATATYPE_32BIT: // Dword write
LogInfo("32-bit Write");
LogInfo("--------");
PowerPC::HostWrite_U32(data, new_addr);
PowerPC::HostWrite_U32(guard, data, new_addr);
LogInfo("Wrote {:08x} to address {:08x}", data, new_addr);
LogInfo("--------");
break;
@@ -415,10 +416,11 @@ static bool Subtype_RamWriteAndFill(const ARAddr& addr, const u32 data)
return true;
}

static bool Subtype_WriteToPointer(const ARAddr& addr, const u32 data)
static bool Subtype_WriteToPointer(const Core::CPUThreadGuard& guard, const ARAddr& addr,
const u32 data)
{
const u32 new_addr = addr.GCAddress();
const u32 ptr = PowerPC::HostRead_U32(new_addr);
const u32 ptr = PowerPC::HostRead_U32(guard, new_addr);

LogInfo("Hardware Address: {:08x}", new_addr);
LogInfo("Size: {:08x}", addr.size);
@@ -434,7 +436,7 @@ static bool Subtype_WriteToPointer(const ARAddr& addr, const u32 data)
LogInfo("Pointer: {:08x}", ptr);
LogInfo("Byte: {:08x}", thebyte);
LogInfo("Offset: {:08x}", offset);
PowerPC::HostWrite_U8(thebyte, ptr + offset);
PowerPC::HostWrite_U8(guard, thebyte, ptr + offset);
LogInfo("Wrote {:08x} to address {:08x}", thebyte, ptr + offset);
LogInfo("--------");
break;
@@ -449,7 +451,7 @@ static bool Subtype_WriteToPointer(const ARAddr& addr, const u32 data)
LogInfo("Pointer: {:08x}", ptr);
LogInfo("Byte: {:08x}", theshort);
LogInfo("Offset: {:08x}", offset);
PowerPC::HostWrite_U16(theshort, ptr + offset);
PowerPC::HostWrite_U16(guard, theshort, ptr + offset);
LogInfo("Wrote {:08x} to address {:08x}", theshort, ptr + offset);
LogInfo("--------");
break;
@@ -459,7 +461,7 @@ static bool Subtype_WriteToPointer(const ARAddr& addr, const u32 data)
case DATATYPE_32BIT:
LogInfo("Write 32-bit to pointer");
LogInfo("--------");
PowerPC::HostWrite_U32(data, ptr);
PowerPC::HostWrite_U32(guard, data, ptr);
LogInfo("Wrote {:08x} to address {:08x}", data, ptr);
LogInfo("--------");
break;
@@ -474,7 +476,7 @@ static bool Subtype_WriteToPointer(const ARAddr& addr, const u32 data)
return true;
}

static bool Subtype_AddCode(const ARAddr& addr, const u32 data)
static bool Subtype_AddCode(const Core::CPUThreadGuard& guard, const ARAddr& addr, const u32 data)
{
// Used to increment/decrement a value in memory
const u32 new_addr = addr.GCAddress();
@@ -487,24 +489,24 @@ static bool Subtype_AddCode(const ARAddr& addr, const u32 data)
case DATATYPE_8BIT:
LogInfo("8-bit Add");
LogInfo("--------");
PowerPC::HostWrite_U8(PowerPC::HostRead_U8(new_addr) + data, new_addr);
LogInfo("Wrote {:02x} to address {:08x}", PowerPC::HostRead_U8(new_addr), new_addr);
PowerPC::HostWrite_U8(guard, PowerPC::HostRead_U8(guard, new_addr) + data, new_addr);
LogInfo("Wrote {:02x} to address {:08x}", PowerPC::HostRead_U8(guard, new_addr), new_addr);
LogInfo("--------");
break;

case DATATYPE_16BIT:
LogInfo("16-bit Add");
LogInfo("--------");
PowerPC::HostWrite_U16(PowerPC::HostRead_U16(new_addr) + data, new_addr);
LogInfo("Wrote {:04x} to address {:08x}", PowerPC::HostRead_U16(new_addr), new_addr);
PowerPC::HostWrite_U16(guard, PowerPC::HostRead_U16(guard, new_addr) + data, new_addr);
LogInfo("Wrote {:04x} to address {:08x}", PowerPC::HostRead_U16(guard, new_addr), new_addr);
LogInfo("--------");
break;

case DATATYPE_32BIT:
LogInfo("32-bit Add");
LogInfo("--------");
PowerPC::HostWrite_U32(PowerPC::HostRead_U32(new_addr) + data, new_addr);
LogInfo("Wrote {:08x} to address {:08x}", PowerPC::HostRead_U32(new_addr), new_addr);
PowerPC::HostWrite_U32(guard, PowerPC::HostRead_U32(guard, new_addr) + data, new_addr);
LogInfo("Wrote {:08x} to address {:08x}", PowerPC::HostRead_U32(guard, new_addr), new_addr);
LogInfo("--------");
break;

@@ -513,12 +515,12 @@ static bool Subtype_AddCode(const ARAddr& addr, const u32 data)
LogInfo("32-bit floating Add");
LogInfo("--------");

const u32 read = PowerPC::HostRead_U32(new_addr);
const u32 read = PowerPC::HostRead_U32(guard, new_addr);
const float read_float = Common::BitCast<float>(read);
// data contains an (unsigned?) integer value
const float fread = read_float + static_cast<float>(data);
const u32 newval = Common::BitCast<u32>(fread);
PowerPC::HostWrite_U32(newval, new_addr);
PowerPC::HostWrite_U32(guard, newval, new_addr);
LogInfo("Old Value {:08x}", read);
LogInfo("Increment {:08x}", data);
LogInfo("New value {:08x}", newval);
@@ -550,7 +552,8 @@ static bool Subtype_MasterCodeAndWriteToCCXXXXXX(const ARAddr& addr, const u32 d
}

// This needs more testing
static bool ZeroCode_FillAndSlide(const u32 val_last, const ARAddr& addr, const u32 data)
static bool ZeroCode_FillAndSlide(const Core::CPUThreadGuard& guard, const u32 val_last,
const ARAddr& addr, const u32 data)
{
const u32 new_addr = ARAddr(val_last).GCAddress();
const u8 size = ARAddr(val_last).size;
@@ -575,7 +578,7 @@ static bool ZeroCode_FillAndSlide(const u32 val_last, const ARAddr& addr, const
LogInfo("--------");
for (int i = 0; i < write_num; ++i)
{
PowerPC::HostWrite_U8(val & 0xFF, curr_addr);
PowerPC::HostWrite_U8(guard, val & 0xFF, curr_addr);
curr_addr += addr_incr;
val += val_incr;
LogInfo("Write {:08x} to address {:08x}", val & 0xFF, curr_addr);
@@ -591,7 +594,7 @@ static bool ZeroCode_FillAndSlide(const u32 val_last, const ARAddr& addr, const
LogInfo("--------");
for (int i = 0; i < write_num; ++i)
{
PowerPC::HostWrite_U16(val & 0xFFFF, curr_addr);
PowerPC::HostWrite_U16(guard, val & 0xFFFF, curr_addr);
LogInfo("Write {:08x} to address {:08x}", val & 0xFFFF, curr_addr);
curr_addr += addr_incr * 2;
val += val_incr;
@@ -606,7 +609,7 @@ static bool ZeroCode_FillAndSlide(const u32 val_last, const ARAddr& addr, const
LogInfo("--------");
for (int i = 0; i < write_num; ++i)
{
PowerPC::HostWrite_U32(val, curr_addr);
PowerPC::HostWrite_U32(guard, val, curr_addr);
LogInfo("Write {:08x} to address {:08x}", val, curr_addr);
curr_addr += addr_incr * 4;
val += val_incr;
@@ -629,7 +632,8 @@ static bool ZeroCode_FillAndSlide(const u32 val_last, const ARAddr& addr, const
// kenobi's "memory copy" Z-code. Requires an additional master code
// on a real AR device. Documented here:
// https://github.com/dolphin-emu/dolphin/wiki/GameCube-Action-Replay-Code-Types#type-z4-size-3--memory-copy
static bool ZeroCode_MemoryCopy(const u32 val_last, const ARAddr& addr, const u32 data)
static bool ZeroCode_MemoryCopy(const Core::CPUThreadGuard& guard, const u32 val_last,
const ARAddr& addr, const u32 data)
{
const u32 addr_dest = val_last & ~0x06000000;
const u32 addr_src = addr.GCAddress();
@@ -646,14 +650,15 @@ static bool ZeroCode_MemoryCopy(const u32 val_last, const ARAddr& addr, const u3
{ // Memory Copy With Pointers Support
LogInfo("Memory Copy With Pointers Support");
LogInfo("--------");
const u32 ptr_dest = PowerPC::HostRead_U32(addr_dest);
const u32 ptr_dest = PowerPC::HostRead_U32(guard, addr_dest);
LogInfo("Resolved Dest Address to: {:08x}", ptr_dest);
const u32 ptr_src = PowerPC::HostRead_U32(addr_src);
const u32 ptr_src = PowerPC::HostRead_U32(guard, addr_src);
LogInfo("Resolved Src Address to: {:08x}", ptr_src);
for (int i = 0; i < num_bytes; ++i)
{
PowerPC::HostWrite_U8(PowerPC::HostRead_U8(ptr_src + i), ptr_dest + i);
LogInfo("Wrote {:08x} to address {:08x}", PowerPC::HostRead_U8(ptr_src + i), ptr_dest + i);
PowerPC::HostWrite_U8(guard, PowerPC::HostRead_U8(guard, ptr_src + i), ptr_dest + i);
LogInfo("Wrote {:08x} to address {:08x}", PowerPC::HostRead_U8(guard, ptr_src + i),
ptr_dest + i);
}
LogInfo("--------");
}
@@ -663,8 +668,8 @@ static bool ZeroCode_MemoryCopy(const u32 val_last, const ARAddr& addr, const u3
LogInfo("--------");
for (int i = 0; i < num_bytes; ++i)
{
PowerPC::HostWrite_U8(PowerPC::HostRead_U8(addr_src + i), addr_dest + i);
LogInfo("Wrote {:08x} to address {:08x}", PowerPC::HostRead_U8(addr_src + i),
PowerPC::HostWrite_U8(guard, PowerPC::HostRead_U8(guard, addr_src + i), addr_dest + i);
LogInfo("Wrote {:08x} to address {:08x}", PowerPC::HostRead_U8(guard, addr_src + i),
addr_dest + i);
}
LogInfo("--------");
@@ -681,25 +686,25 @@ static bool ZeroCode_MemoryCopy(const u32 val_last, const ARAddr& addr, const u3
return true;
}

static bool NormalCode(const ARAddr& addr, const u32 data)
static bool NormalCode(const Core::CPUThreadGuard& guard, const ARAddr& addr, const u32 data)
{
switch (addr.subtype)
{
case SUB_RAM_WRITE: // Ram write (and fill)
LogInfo("Doing Ram Write And Fill");
if (!Subtype_RamWriteAndFill(addr, data))
if (!Subtype_RamWriteAndFill(guard, addr, data))
return false;
break;

case SUB_WRITE_POINTER: // Write to pointer
LogInfo("Doing Write To Pointer");
if (!Subtype_WriteToPointer(addr, data))
if (!Subtype_WriteToPointer(guard, addr, data))
return false;
break;

case SUB_ADD_CODE: // Increment Value
LogInfo("Doing Add Code");
if (!Subtype_AddCode(addr, data))
if (!Subtype_AddCode(guard, addr, data))
return false;
break;

@@ -759,7 +764,8 @@ static bool CompareValues(const u32 val1, const u32 val2, const int type)
}
}

static bool ConditionalCode(const ARAddr& addr, const u32 data, int* const pSkipCount)
static bool ConditionalCode(const Core::CPUThreadGuard& guard, const ARAddr& addr, const u32 data,
int* const pSkipCount)
{
const u32 new_addr = addr.GCAddress();

@@ -771,16 +777,16 @@ static bool ConditionalCode(const ARAddr& addr, const u32 data, int* const pSkip
switch (addr.size)
{
case DATATYPE_8BIT:
result = CompareValues(PowerPC::HostRead_U8(new_addr), (data & 0xFF), addr.type);
result = CompareValues(PowerPC::HostRead_U8(guard, new_addr), (data & 0xFF), addr.type);
break;

case DATATYPE_16BIT:
result = CompareValues(PowerPC::HostRead_U16(new_addr), (data & 0xFFFF), addr.type);
result = CompareValues(PowerPC::HostRead_U16(guard, new_addr), (data & 0xFFFF), addr.type);
break;

case DATATYPE_32BIT_FLOAT:
case DATATYPE_32BIT:
result = CompareValues(PowerPC::HostRead_U32(new_addr), data, addr.type);
result = CompareValues(PowerPC::HostRead_U32(guard, new_addr), data, addr.type);
break;

default:
@@ -819,7 +825,7 @@ static bool ConditionalCode(const ARAddr& addr, const u32 data, int* const pSkip
}

// NOTE: Lock needed to give mutual exclusion to s_current_code and LogInfo
static bool RunCodeLocked(const ARCode& arcode)
static bool RunCodeLocked(const Core::CPUThreadGuard& guard, const ARCode& arcode)
{
// The mechanism is different than what the real AR uses, so there may be compatibility problems.

@@ -873,7 +879,7 @@ static bool RunCodeLocked(const ARCode& arcode)
{
do_fill_and_slide = false;
LogInfo("Doing Fill And Slide");
if (false == ZeroCode_FillAndSlide(val_last, addr, data))
if (false == ZeroCode_FillAndSlide(guard, val_last, addr, data))
return false;
continue;
}
@@ -883,7 +889,7 @@ static bool RunCodeLocked(const ARCode& arcode)
{
do_memory_copy = false;
LogInfo("Doing Memory Copy");
if (false == ZeroCode_MemoryCopy(val_last, addr, data))
if (false == ZeroCode_MemoryCopy(guard, val_last, addr, data))
return false;
continue;
}
@@ -962,13 +968,13 @@ static bool RunCodeLocked(const ARCode& arcode)
switch (addr.type)
{
case 0x00:
if (false == NormalCode(addr, data))
if (false == NormalCode(guard, addr, data))
return false;
break;

default:
LogInfo("This Normal Code is a Conditional Code");
if (false == ConditionalCode(addr, data, &skip_count))
if (false == ConditionalCode(guard, addr, data, &skip_count))
return false;
break;
}
@@ -977,7 +983,7 @@ static bool RunCodeLocked(const ARCode& arcode)
return true;
}

void RunAllActive()
void RunAllActive(const Core::CPUThreadGuard& cpu_guard)
{
if (!Config::Get(Config::MAIN_ENABLE_CHEATS))
return;
@@ -987,8 +993,8 @@ void RunAllActive()
// be contested.
std::lock_guard guard(s_lock);
s_active_codes.erase(std::remove_if(s_active_codes.begin(), s_active_codes.end(),
[](const ARCode& code) {
bool success = RunCodeLocked(code);
[&cpu_guard](const ARCode& code) {
bool success = RunCodeLocked(cpu_guard, code);
LogInfo("\n");
return !success;
}),
@@ -12,6 +12,11 @@

class IniFile;

namespace Core
{
class CPUThreadGuard;
};

namespace ActionReplay
{
struct AREntry
@@ -35,7 +40,7 @@ struct ARCode
bool user_defined = false;
};

void RunAllActive();
void RunAllActive(const Core::CPUThreadGuard& cpu_guard);

void ApplyCodes(std::span<const ARCode> codes);
void SetSyncedCodesAsActive();
@@ -375,11 +375,11 @@ bool CBoot::FindMapFile(std::string* existing_map_file, std::string* writable_ma
return false;
}

bool CBoot::LoadMapFromFilename()
bool CBoot::LoadMapFromFilename(const Core::CPUThreadGuard& guard)
{
std::string strMapFilename;
bool found = FindMapFile(&strMapFilename, nullptr);
if (found && g_symbolDB.LoadMap(strMapFilename))
if (found && g_symbolDB.LoadMap(guard, strMapFilename))
{
UpdateDebugger_MapLoaded();
return true;
@@ -486,7 +486,8 @@ static void CopyDefaultExceptionHandlers(Core::System& system)
}

// Third boot step after BootManager and Core. See Call schedule in BootManager.cpp
bool CBoot::BootUp(Core::System& system, std::unique_ptr<BootParameters> boot)
bool CBoot::BootUp(Core::System& system, const Core::CPUThreadGuard& guard,
std::unique_ptr<BootParameters> boot)
{
SConfig& config = SConfig::GetInstance();

@@ -502,8 +503,10 @@ bool CBoot::BootUp(Core::System& system, std::unique_ptr<BootParameters> boot)

struct BootTitle
{
BootTitle(Core::System& system_, const std::vector<DiscIO::Riivolution::Patch>& patches)
: system(system_), config(SConfig::GetInstance()), riivolution_patches(patches)
BootTitle(Core::System& system_, const Core::CPUThreadGuard& guard_,
const std::vector<DiscIO::Riivolution::Patch>& patches)
: system(system_), guard(guard_), config(SConfig::GetInstance()),
riivolution_patches(patches)
{
}
bool operator()(BootParameters::Disc& disc) const
@@ -515,10 +518,10 @@ bool CBoot::BootUp(Core::System& system, std::unique_ptr<BootParameters> boot)
if (!volume)
return false;

if (!EmulatedBS2(system, config.bWii, *volume, riivolution_patches))
if (!EmulatedBS2(system, guard, config.bWii, *volume, riivolution_patches))
return false;

SConfig::OnNewTitleLoad();
SConfig::OnNewTitleLoad(guard);
return true;
}

@@ -551,7 +554,7 @@ bool CBoot::BootUp(Core::System& system, std::unique_ptr<BootParameters> boot)
}
else
{
SetupGCMemory(system);
SetupGCMemory(system, guard);
}

if (!executable.reader->LoadIntoMemory())
@@ -560,11 +563,11 @@ bool CBoot::BootUp(Core::System& system, std::unique_ptr<BootParameters> boot)
return false;
}

SConfig::OnNewTitleLoad();
SConfig::OnNewTitleLoad(guard);

ppc_state.pc = executable.reader->GetEntryPoint();

if (executable.reader->LoadSymbols())
if (executable.reader->LoadSymbols(guard))
{
UpdateDebugger_MapLoaded();
HLE::PatchFunctions(system);
@@ -578,7 +581,7 @@ bool CBoot::BootUp(Core::System& system, std::unique_ptr<BootParameters> boot)
if (!Boot_WiiWAD(system, wad))
return false;

SConfig::OnNewTitleLoad();
SConfig::OnNewTitleLoad(guard);
return true;
}

@@ -588,7 +591,7 @@ bool CBoot::BootUp(Core::System& system, std::unique_ptr<BootParameters> boot)
if (!BootNANDTitle(system, nand_title.id))
return false;

SConfig::OnNewTitleLoad();
SConfig::OnNewTitleLoad(guard);
return true;
}

@@ -613,7 +616,7 @@ bool CBoot::BootUp(Core::System& system, std::unique_ptr<BootParameters> boot)
SetDisc(DiscIO::CreateDisc(ipl.disc->path), ipl.disc->auto_disc_change_paths);
}

SConfig::OnNewTitleLoad();
SConfig::OnNewTitleLoad(guard);
return true;
}

@@ -625,14 +628,15 @@ bool CBoot::BootUp(Core::System& system, std::unique_ptr<BootParameters> boot)

private:
Core::System& system;
const Core::CPUThreadGuard& guard;
const SConfig& config;
const std::vector<DiscIO::Riivolution::Patch>& riivolution_patches;
};

if (!std::visit(BootTitle(system, boot->riivolution_patches), boot->parameters))
if (!std::visit(BootTitle(system, guard, boot->riivolution_patches), boot->parameters))
return false;

DiscIO::Riivolution::ApplyGeneralMemoryPatches(boot->riivolution_patches);
DiscIO::Riivolution::ApplyGeneralMemoryPatches(guard, boot->riivolution_patches);

return true;
}
@@ -21,8 +21,9 @@

namespace Core
{
class CPUThreadGuard;
class System;
}
} // namespace Core

namespace File
{
@@ -153,7 +154,8 @@ struct BootParameters
class CBoot
{
public:
static bool BootUp(Core::System& system, std::unique_ptr<BootParameters> boot);
static bool BootUp(Core::System& system, const Core::CPUThreadGuard& guard,
std::unique_ptr<BootParameters> boot);

// Tries to find a map file for the current game by looking first in the
// local user directory, then in the shared user directory.
@@ -166,7 +168,7 @@ class CBoot
//
// Returns true if a map file exists, false if none could be found.
static bool FindMapFile(std::string* existing_map_file, std::string* writable_map_file);
static bool LoadMapFromFilename();
static bool LoadMapFromFilename(const Core::CPUThreadGuard& guard);

private:
static bool DVDRead(const DiscIO::VolumeDisc& disc, u64 dvd_offset, u32 output_address,
@@ -182,17 +184,21 @@ class CBoot
static void SetupMSR(PowerPC::PowerPCState& ppc_state);
static void SetupHID(PowerPC::PowerPCState& ppc_state, bool is_wii);
static void SetupBAT(Core::System& system, bool is_wii);
static bool RunApploader(Core::System& system, bool is_wii, const DiscIO::VolumeDisc& volume,
static bool RunApploader(Core::System& system, const Core::CPUThreadGuard& guard, bool is_wii,
const DiscIO::VolumeDisc& volume,
const std::vector<DiscIO::Riivolution::Patch>& riivolution_patches);
static bool EmulatedBS2_GC(Core::System& system, const DiscIO::VolumeDisc& volume,
static bool EmulatedBS2_GC(Core::System& system, const Core::CPUThreadGuard& guard,
const DiscIO::VolumeDisc& volume,
const std::vector<DiscIO::Riivolution::Patch>& riivolution_patches);
static bool EmulatedBS2_Wii(Core::System& system, const DiscIO::VolumeDisc& volume,
static bool EmulatedBS2_Wii(Core::System& system, const Core::CPUThreadGuard& guard,
const DiscIO::VolumeDisc& volume,
const std::vector<DiscIO::Riivolution::Patch>& riivolution_patches);
static bool EmulatedBS2(Core::System& system, bool is_wii, const DiscIO::VolumeDisc& volume,
static bool EmulatedBS2(Core::System& system, const Core::CPUThreadGuard& guard, bool is_wii,
const DiscIO::VolumeDisc& volume,
const std::vector<DiscIO::Riivolution::Patch>& riivolution_patches);
static bool Load_BS2(Core::System& system, const std::string& boot_rom_filename);

static void SetupGCMemory(Core::System& system);
static void SetupGCMemory(Core::System& system, const Core::CPUThreadGuard& guard);
static bool SetupWiiMemory(Core::System& system, IOS::HLE::IOSC::ConsoleType console_type);
};

@@ -208,7 +214,7 @@ class BootExecutableReader
virtual bool IsValid() const = 0;
virtual bool IsWii() const = 0;
virtual bool LoadIntoMemory(bool only_in_mem1 = false) const = 0;
virtual bool LoadSymbols() const = 0;
virtual bool LoadSymbols(const Core::CPUThreadGuard& guard) const = 0;

protected:
std::vector<u8> m_bytes;
@@ -44,14 +44,14 @@

namespace
{
void PresetTimeBaseTicks()
void PresetTimeBaseTicks(const Core::CPUThreadGuard& guard)
{
const u64 emulated_time =
ExpansionInterface::CEXIIPL::GetEmulatedTime(ExpansionInterface::CEXIIPL::GC_EPOCH);

const u64 time_base_ticks = emulated_time * 40500000ULL;

PowerPC::HostWrite_U64(time_base_ticks, 0x800030D8);
PowerPC::HostWrite_U64(guard, time_base_ticks, 0x800030D8);
}
} // Anonymous namespace

@@ -131,7 +131,8 @@ void CBoot::SetupBAT(Core::System& system, bool is_wii)
PowerPC::IBATUpdated();
}

bool CBoot::RunApploader(Core::System& system, bool is_wii, const DiscIO::VolumeDisc& volume,
bool CBoot::RunApploader(Core::System& system, const Core::CPUThreadGuard& guard, bool is_wii,
const DiscIO::VolumeDisc& volume,
const std::vector<DiscIO::Riivolution::Patch>& riivolution_patches)
{
const DiscIO::Partition partition = volume.GetGamePartition();
@@ -166,8 +167,8 @@ bool CBoot::RunApploader(Core::System& system, bool is_wii, const DiscIO::Volume

// iAppLoaderInit
DEBUG_LOG_FMT(BOOT, "Call iAppLoaderInit");
PowerPC::HostWrite_U32(0x4E800020, 0x81300000); // Write BLR
HLE::Patch(system, 0x81300000, "AppLoaderReport"); // HLE OSReport for Apploader
PowerPC::HostWrite_U32(guard, 0x4E800020, 0x81300000); // Write BLR
HLE::Patch(system, 0x81300000, "AppLoaderReport"); // HLE OSReport for Apploader
ppc_state.gpr[3] = 0x81300000;
RunFunction(system, iAppLoaderInit);

@@ -196,7 +197,8 @@ bool CBoot::RunApploader(Core::System& system, bool is_wii, const DiscIO::Volume
ram_address, length);
DVDRead(volume, dvd_offset, ram_address, length, partition);

DiscIO::Riivolution::ApplyApploaderMemoryPatches(riivolution_patches, ram_address, length);
DiscIO::Riivolution::ApplyApploaderMemoryPatches(guard, riivolution_patches, ram_address,
length);

ppc_state.gpr[3] = 0x81300004;
ppc_state.gpr[4] = 0x81300008;
@@ -216,36 +218,37 @@ bool CBoot::RunApploader(Core::System& system, bool is_wii, const DiscIO::Volume
return true;
}

void CBoot::SetupGCMemory(Core::System& system)
void CBoot::SetupGCMemory(Core::System& system, const Core::CPUThreadGuard& guard)
{
auto& memory = system.GetMemory();

// Booted from bootrom. 0xE5207C22 = booted from jtag
PowerPC::HostWrite_U32(0x0D15EA5E, 0x80000020);
PowerPC::HostWrite_U32(guard, 0x0D15EA5E, 0x80000020);

// Physical Memory Size (24MB on retail)
PowerPC::HostWrite_U32(memory.GetRamSizeReal(), 0x80000028);
PowerPC::HostWrite_U32(guard, memory.GetRamSizeReal(), 0x80000028);

// Console type - DevKit (retail ID == 0x00000003) see YAGCD 4.2.1.1.2
// TODO: determine why some games fail when using a retail ID.
// (Seem to take different EXI paths, see Ikaruga for example)
const u32 console_type = static_cast<u32>(Core::ConsoleType::LatestDevkit);
PowerPC::HostWrite_U32(console_type, 0x8000002C);
PowerPC::HostWrite_U32(guard, console_type, 0x8000002C);

// Fake the VI Init of the IPL (YAGCD 4.2.1.4)
PowerPC::HostWrite_U32(DiscIO::IsNTSC(SConfig::GetInstance().m_region) ? 0 : 1, 0x800000CC);
PowerPC::HostWrite_U32(guard, DiscIO::IsNTSC(SConfig::GetInstance().m_region) ? 0 : 1,
0x800000CC);

PowerPC::HostWrite_U32(0x01000000, 0x800000d0); // ARAM Size. 16MB main + 4/16/32MB external
// (retail consoles have no external ARAM)
// ARAM Size. 16MB main + 4/16/32MB external. (retail consoles have no external ARAM)
PowerPC::HostWrite_U32(guard, 0x01000000, 0x800000d0);

PowerPC::HostWrite_U32(0x09a7ec80, 0x800000F8); // Bus Clock Speed
PowerPC::HostWrite_U32(0x1cf7c580, 0x800000FC); // CPU Clock Speed
PowerPC::HostWrite_U32(guard, 0x09a7ec80, 0x800000F8); // Bus Clock Speed
PowerPC::HostWrite_U32(guard, 0x1cf7c580, 0x800000FC); // CPU Clock Speed

PowerPC::HostWrite_U32(0x4c000064, 0x80000300); // Write default DSI Handler: rfi
PowerPC::HostWrite_U32(0x4c000064, 0x80000800); // Write default FPU Handler: rfi
PowerPC::HostWrite_U32(0x4c000064, 0x80000C00); // Write default Syscall Handler: rfi
PowerPC::HostWrite_U32(guard, 0x4c000064, 0x80000300); // Write default DSI Handler: rfi
PowerPC::HostWrite_U32(guard, 0x4c000064, 0x80000800); // Write default FPU Handler: rfi
PowerPC::HostWrite_U32(guard, 0x4c000064, 0x80000C00); // Write default Syscall Handler: rfi

PresetTimeBaseTicks();
PresetTimeBaseTicks(guard);

// HIO checks this
// PowerPC::HostWrite_U16(0x8200, 0x000030e6); // Console type
@@ -255,7 +258,8 @@ void CBoot::SetupGCMemory(Core::System& system)
// GameCube Bootstrap 2 HLE:
// copy the apploader to 0x81200000
// execute the apploader, function by function, using the above utility.
bool CBoot::EmulatedBS2_GC(Core::System& system, const DiscIO::VolumeDisc& volume,
bool CBoot::EmulatedBS2_GC(Core::System& system, const Core::CPUThreadGuard& guard,
const DiscIO::VolumeDisc& volume,
const std::vector<DiscIO::Riivolution::Patch>& riivolution_patches)
{
INFO_LOG_FMT(BOOT, "Faking GC BS2...");
@@ -266,7 +270,7 @@ bool CBoot::EmulatedBS2_GC(Core::System& system, const DiscIO::VolumeDisc& volum
SetupHID(ppc_state, /*is_wii*/ false);
SetupBAT(system, /*is_wii*/ false);

SetupGCMemory(system);
SetupGCMemory(system, guard);

// Datel titles don't initialize the postMatrices, but they have dual-texture coordinate
// transformation enabled. We initialize all of xfmem to 0, which results in everything using
@@ -309,7 +313,7 @@ bool CBoot::EmulatedBS2_GC(Core::System& system, const DiscIO::VolumeDisc& volum
// Global pointer to Small Data Area Base (Luigi's Mansion's apploader uses it)
ppc_state.gpr[13] = ntsc ? 0x81465320 : 0x814b4fc0;

return RunApploader(system, /*is_wii*/ false, volume, riivolution_patches);
return RunApploader(system, guard, /*is_wii*/ false, volume, riivolution_patches);
}

static DiscIO::Region CodeRegion(char c)
@@ -507,7 +511,8 @@ static void WriteEmptyPlayRecord()
// Wii Bootstrap 2 HLE:
// copy the apploader to 0x81200000
// execute the apploader
bool CBoot::EmulatedBS2_Wii(Core::System& system, const DiscIO::VolumeDisc& volume,
bool CBoot::EmulatedBS2_Wii(Core::System& system, const Core::CPUThreadGuard& guard,
const DiscIO::VolumeDisc& volume,
const std::vector<DiscIO::Riivolution::Patch>& riivolution_patches)
{
INFO_LOG_FMT(BOOT, "Faking Wii BS2...");
@@ -578,7 +583,7 @@ bool CBoot::EmulatedBS2_Wii(Core::System& system, const DiscIO::VolumeDisc& volu

ppc_state.gpr[1] = 0x816ffff0; // StackPointer

if (!RunApploader(system, /*is_wii*/ true, volume, riivolution_patches))
if (!RunApploader(system, guard, /*is_wii*/ true, volume, riivolution_patches))
return false;

// The Apploader probably just overwrote values needed for RAM Override. Run this again!
@@ -593,9 +598,10 @@ bool CBoot::EmulatedBS2_Wii(Core::System& system, const DiscIO::VolumeDisc& volu

// Returns true if apploader has run successfully. If is_wii is true, the disc
// that volume refers to must currently be inserted into the emulated disc drive.
bool CBoot::EmulatedBS2(Core::System& system, bool is_wii, const DiscIO::VolumeDisc& volume,
bool CBoot::EmulatedBS2(Core::System& system, const Core::CPUThreadGuard& guard, bool is_wii,
const DiscIO::VolumeDisc& volume,
const std::vector<DiscIO::Riivolution::Patch>& riivolution_patches)
{
return is_wii ? EmulatedBS2_Wii(system, volume, riivolution_patches) :
EmulatedBS2_GC(system, volume, riivolution_patches);
return is_wii ? EmulatedBS2_Wii(system, guard, volume, riivolution_patches) :
EmulatedBS2_GC(system, guard, volume, riivolution_patches);
}
@@ -27,7 +27,7 @@ class DolReader final : public BootExecutableReader
bool IsAncast() const { return m_is_ancast; };
u32 GetEntryPoint() const override { return m_dolheader.entryPoint; }
bool LoadIntoMemory(bool only_in_mem1 = false) const override;
bool LoadSymbols() const override { return false; }
bool LoadSymbols(const Core::CPUThreadGuard& guard) const override { return false; }

private:
enum
@@ -181,7 +181,7 @@ SectionID ElfReader::GetSectionByName(const char* name, int firstSection) const
return -1;
}

bool ElfReader::LoadSymbols() const
bool ElfReader::LoadSymbols(const Core::CPUThreadGuard& guard) const
{
bool hasSymbols = false;
SectionID sec = GetSectionByName(".symtab");
@@ -219,7 +219,7 @@ bool ElfReader::LoadSymbols() const
default:
continue;
}
g_symbolDB.AddKnownSymbol(value, size, name, symtype);
g_symbolDB.AddKnownSymbol(guard, value, size, name, symtype);
hasSymbols = true;
}
}
@@ -36,7 +36,7 @@ class ElfReader final : public BootExecutableReader
u32 GetEntryPoint() const override { return entryPoint; }
u32 GetFlags() const { return (u32)(header->e_flags); }
bool LoadIntoMemory(bool only_in_mem1 = false) const override;
bool LoadSymbols() const override;
bool LoadSymbols(const Core::CPUThreadGuard& guard) const override;
// TODO: actually check for validity.
bool IsValid() const override { return true; }
bool IsWii() const override;
@@ -103,94 +103,106 @@ namespace
{
template <typename T>
static std::optional<PowerPC::ReadResult<T>>
TryReadValueFromEmulatedMemory(u32 addr, PowerPC::RequestedAddressSpace space);
TryReadValueFromEmulatedMemory(const Core::CPUThreadGuard& guard, u32 addr,
PowerPC::RequestedAddressSpace space);

template <>
std::optional<PowerPC::ReadResult<u8>>
TryReadValueFromEmulatedMemory(u32 addr, PowerPC::RequestedAddressSpace space)
TryReadValueFromEmulatedMemory(const Core::CPUThreadGuard& guard, u32 addr,
PowerPC::RequestedAddressSpace space)
{
return PowerPC::HostTryReadU8(addr, space);
return PowerPC::HostTryReadU8(guard, addr, space);
}

template <>
std::optional<PowerPC::ReadResult<u16>>
TryReadValueFromEmulatedMemory(u32 addr, PowerPC::RequestedAddressSpace space)
TryReadValueFromEmulatedMemory(const Core::CPUThreadGuard& guard, u32 addr,
PowerPC::RequestedAddressSpace space)
{
return PowerPC::HostTryReadU16(addr, space);
return PowerPC::HostTryReadU16(guard, addr, space);
}

template <>
std::optional<PowerPC::ReadResult<u32>>
TryReadValueFromEmulatedMemory(u32 addr, PowerPC::RequestedAddressSpace space)
TryReadValueFromEmulatedMemory(const Core::CPUThreadGuard& guard, u32 addr,
PowerPC::RequestedAddressSpace space)
{
return PowerPC::HostTryReadU32(addr, space);
return PowerPC::HostTryReadU32(guard, addr, space);
}

template <>
std::optional<PowerPC::ReadResult<u64>>
TryReadValueFromEmulatedMemory(u32 addr, PowerPC::RequestedAddressSpace space)
TryReadValueFromEmulatedMemory(const Core::CPUThreadGuard& guard, u32 addr,
PowerPC::RequestedAddressSpace space)
{
return PowerPC::HostTryReadU64(addr, space);
return PowerPC::HostTryReadU64(guard, addr, space);
}

template <>
std::optional<PowerPC::ReadResult<s8>>
TryReadValueFromEmulatedMemory(u32 addr, PowerPC::RequestedAddressSpace space)
TryReadValueFromEmulatedMemory(const Core::CPUThreadGuard& guard, u32 addr,
PowerPC::RequestedAddressSpace space)
{
auto tmp = PowerPC::HostTryReadU8(addr, space);
auto tmp = PowerPC::HostTryReadU8(guard, addr, space);
if (!tmp)
return std::nullopt;
return PowerPC::ReadResult<s8>(tmp->translated, Common::BitCast<s8>(tmp->value));
}

template <>
std::optional<PowerPC::ReadResult<s16>>
TryReadValueFromEmulatedMemory(u32 addr, PowerPC::RequestedAddressSpace space)
TryReadValueFromEmulatedMemory(const Core::CPUThreadGuard& guard, u32 addr,
PowerPC::RequestedAddressSpace space)
{
auto tmp = PowerPC::HostTryReadU16(addr, space);
auto tmp = PowerPC::HostTryReadU16(guard, addr, space);
if (!tmp)
return std::nullopt;
return PowerPC::ReadResult<s16>(tmp->translated, Common::BitCast<s16>(tmp->value));
}

template <>
std::optional<PowerPC::ReadResult<s32>>
TryReadValueFromEmulatedMemory(u32 addr, PowerPC::RequestedAddressSpace space)
TryReadValueFromEmulatedMemory(const Core::CPUThreadGuard& guard, u32 addr,
PowerPC::RequestedAddressSpace space)
{
auto tmp = PowerPC::HostTryReadU32(addr, space);
auto tmp = PowerPC::HostTryReadU32(guard, addr, space);
if (!tmp)
return std::nullopt;
return PowerPC::ReadResult<s32>(tmp->translated, Common::BitCast<s32>(tmp->value));
}

template <>
std::optional<PowerPC::ReadResult<s64>>
TryReadValueFromEmulatedMemory(u32 addr, PowerPC::RequestedAddressSpace space)
TryReadValueFromEmulatedMemory(const Core::CPUThreadGuard& guard, u32 addr,
PowerPC::RequestedAddressSpace space)
{
auto tmp = PowerPC::HostTryReadU64(addr, space);
auto tmp = PowerPC::HostTryReadU64(guard, addr, space);
if (!tmp)
return std::nullopt;
return PowerPC::ReadResult<s64>(tmp->translated, Common::BitCast<s64>(tmp->value));
}

template <>
std::optional<PowerPC::ReadResult<float>>
TryReadValueFromEmulatedMemory(u32 addr, PowerPC::RequestedAddressSpace space)
TryReadValueFromEmulatedMemory(const Core::CPUThreadGuard& guard, u32 addr,
PowerPC::RequestedAddressSpace space)
{
return PowerPC::HostTryReadF32(addr, space);
return PowerPC::HostTryReadF32(guard, addr, space);
}

template <>
std::optional<PowerPC::ReadResult<double>>
TryReadValueFromEmulatedMemory(u32 addr, PowerPC::RequestedAddressSpace space)
TryReadValueFromEmulatedMemory(const Core::CPUThreadGuard& guard, u32 addr,
PowerPC::RequestedAddressSpace space)
{
return PowerPC::HostTryReadF64(addr, space);
return PowerPC::HostTryReadF64(guard, addr, space);
}
} // namespace

template <typename T>
Common::Result<Cheats::SearchErrorCode, std::vector<Cheats::SearchResult<T>>>
Cheats::NewSearch(const std::vector<Cheats::MemoryRange>& memory_ranges,
Cheats::NewSearch(const Core::CPUThreadGuard& guard,
const std::vector<Cheats::MemoryRange>& memory_ranges,
PowerPC::RequestedAddressSpace address_space, bool aligned,
const std::function<bool(const T& value)>& validator)
{
@@ -229,7 +241,7 @@ Cheats::NewSearch(const std::vector<Cheats::MemoryRange>& memory_ranges,
for (u64 i = 0; i < length; i += increment_per_loop)
{
const u32 addr = start_address + i;
const auto current_value = TryReadValueFromEmulatedMemory<T>(addr, address_space);
const auto current_value = TryReadValueFromEmulatedMemory<T>(guard, addr, address_space);
if (!current_value)
continue;

@@ -252,7 +264,8 @@ Cheats::NewSearch(const std::vector<Cheats::MemoryRange>& memory_ranges,

template <typename T>
Common::Result<Cheats::SearchErrorCode, std::vector<Cheats::SearchResult<T>>>
Cheats::NextSearch(const std::vector<Cheats::SearchResult<T>>& previous_results,
Cheats::NextSearch(const Core::CPUThreadGuard& guard,
const std::vector<Cheats::SearchResult<T>>& previous_results,
PowerPC::RequestedAddressSpace address_space,
const std::function<bool(const T& new_value, const T& old_value)>& validator)
{
@@ -277,7 +290,7 @@ Cheats::NextSearch(const std::vector<Cheats::SearchResult<T>>& previous_results,
for (const auto& previous_result : previous_results)
{
const u32 addr = previous_result.m_address;
const auto current_value = TryReadValueFromEmulatedMemory<T>(addr, address_space);
const auto current_value = TryReadValueFromEmulatedMemory<T>(guard, addr, address_space);
if (!current_value)
{
auto& r = results.emplace_back();
@@ -429,7 +442,7 @@ MakeCompareFunctionForLastValue(Cheats::CompareType op)
}

template <typename T>
Cheats::SearchErrorCode Cheats::CheatSearchSession<T>::RunSearch()
Cheats::SearchErrorCode Cheats::CheatSearchSession<T>::RunSearch(const Core::CPUThreadGuard& guard)
{
Common::Result<SearchErrorCode, std::vector<SearchResult<T>>> result =
Cheats::SearchErrorCode::InvalidParameters;
@@ -442,32 +455,32 @@ Cheats::SearchErrorCode Cheats::CheatSearchSession<T>::RunSearch()
if (m_first_search_done)
{
result = Cheats::NextSearch<T>(
m_search_results, m_address_space,
guard, m_search_results, m_address_space,
[&func](const T& new_value, const T& old_value) { return func(new_value); });
}
else
{
result = Cheats::NewSearch<T>(m_memory_ranges, m_address_space, m_aligned, func);
result = Cheats::NewSearch<T>(guard, m_memory_ranges, m_address_space, m_aligned, func);
}
}
else if (m_filter_type == FilterType::CompareAgainstLastValue)
{
if (!m_first_search_done)
return Cheats::SearchErrorCode::InvalidParameters;

result = Cheats::NextSearch<T>(m_search_results, m_address_space,
result = Cheats::NextSearch<T>(guard, m_search_results, m_address_space,
MakeCompareFunctionForLastValue<T>(m_compare_type));
}
else if (m_filter_type == FilterType::DoNotFilter)
{
if (m_first_search_done)
{
result = Cheats::NextSearch<T>(m_search_results, m_address_space,
result = Cheats::NextSearch<T>(guard, m_search_results, m_address_space,
[](const T& v1, const T& v2) { return true; });
}
else
{
result = Cheats::NewSearch<T>(m_memory_ranges, m_address_space, m_aligned,
result = Cheats::NewSearch<T>(guard, m_memory_ranges, m_address_space, m_aligned,
[](const T& v) { return true; });
}
}
@@ -14,6 +14,11 @@
#include "Common/Result.h"
#include "Core/PowerPC/MMU.h"

namespace Core
{
class CPUThreadGuard;
};

namespace Cheats
{
enum class CompareType
@@ -108,15 +113,15 @@ std::vector<u8> GetValueAsByteVector(const SearchValue& value);
// for which the given validator returns true.
template <typename T>
Common::Result<SearchErrorCode, std::vector<SearchResult<T>>>
NewSearch(const std::vector<MemoryRange>& memory_ranges,
NewSearch(const Core::CPUThreadGuard& guard, const std::vector<MemoryRange>& memory_ranges,
PowerPC::RequestedAddressSpace address_space, bool aligned,
const std::function<bool(const T& value)>& validator);

// Refresh the values for the given results in the given address space, only keeping values for
// which the given validator returns true.
template <typename T>
Common::Result<SearchErrorCode, std::vector<SearchResult<T>>>
NextSearch(const std::vector<SearchResult<T>>& previous_results,
NextSearch(const Core::CPUThreadGuard& guard, const std::vector<SearchResult<T>>& previous_results,
PowerPC::RequestedAddressSpace address_space,
const std::function<bool(const T& new_value, const T& old_value)>& validator);

@@ -138,7 +143,7 @@ class CheatSearchSessionBase
virtual void ResetResults() = 0;

// Run either a new search or a next search based on the current state of this session.
virtual SearchErrorCode RunSearch() = 0;
virtual SearchErrorCode RunSearch(const Core::CPUThreadGuard& guard) = 0;

virtual size_t GetMemoryRangeCount() const = 0;
virtual MemoryRange GetMemoryRange(size_t index) const = 0;
@@ -184,7 +189,7 @@ class CheatSearchSession final : public CheatSearchSessionBase
bool SetValueFromString(const std::string& value_as_string, bool force_parse_as_hex) override;

void ResetResults() override;
SearchErrorCode RunSearch() override;
SearchErrorCode RunSearch(const Core::CPUThreadGuard& guard) override;

size_t GetMemoryRangeCount() const override;
MemoryRange GetMemoryRange(size_t index) const override;
@@ -191,7 +191,7 @@ void SConfig::SetRunningGameMetadata(const std::string& game_id, const std::stri
DolphinAnalytics::Instance().ReportGameStart();
}

void SConfig::OnNewTitleLoad()
void SConfig::OnNewTitleLoad(const Core::CPUThreadGuard& guard)
{
if (!Core::IsRunning())
return;
@@ -201,7 +201,7 @@ void SConfig::OnNewTitleLoad()
g_symbolDB.Clear();
Host_NotifyMapLoaded();
}
CBoot::LoadMapFromFilename();
CBoot::LoadMapFromFilename(guard);
auto& system = Core::System::GetInstance();
HLE::Reload(system);
PatchEngine::Reload();
@@ -16,6 +16,11 @@

class IniFile;

namespace Core
{
class CPUThreadGuard;
}

namespace DiscIO
{
enum class Language;
@@ -68,7 +73,7 @@ struct SConfig
void SetRunningGameMetadata(const std::string& game_id);
// Reloads title-specific map files, patches, custom textures, etc.
// This should only be called after the new title has been loaded into memory.
static void OnNewTitleLoad();
static void OnNewTitleLoad(const Core::CPUThreadGuard& guard);

void LoadDefaults();
static std::string MakeGameID(std::string_view file_name);
@@ -166,7 +166,12 @@ void OnFrameEnd()
{
#ifdef USE_MEMORYWATCHER
if (s_memory_watcher)
s_memory_watcher->Step();
{
ASSERT(IsCPUThread());
CPUThreadGuard guard;

s_memory_watcher->Step(guard);
}
#endif
}

@@ -537,7 +542,9 @@ static void EmuThread(std::unique_ptr<BootParameters> boot, WindowSystemInfo wsi

PatchEngine::Shutdown();
HLE::Clear();
PowerPC::debug_interface.Clear();

CPUThreadGuard guard;
PowerPC::debug_interface.Clear(guard);
}};

VideoBackendBase::PopulateBackendInfo();
@@ -587,8 +594,12 @@ static void EmuThread(std::unique_ptr<BootParameters> boot, WindowSystemInfo wsi
if (SConfig::GetInstance().bWii)
savegame_redirect = DiscIO::Riivolution::ExtractSavegameRedirect(boot->riivolution_patches);

if (!CBoot::BootUp(system, std::move(boot)))
return;
{
ASSERT(IsCPUThread());
CPUThreadGuard guard;
if (!CBoot::BootUp(system, guard, std::move(boot)))
return;
}

// Initialise Wii filesystem contents.
// This is done here after Boot and not in BootManager to ensure that we operate
@@ -1036,4 +1047,16 @@ void UpdateInputGate(bool require_focus, bool require_full_focus)
ControlReference::SetInputGate(focus_passes && full_focus_passes);
}

CPUThreadGuard::CPUThreadGuard() : m_was_cpu_thread(IsCPUThread())
{
if (!m_was_cpu_thread)
m_was_unpaused = PauseAndLock(true, true);
}

CPUThreadGuard::~CPUThreadGuard()
{
if (!m_was_cpu_thread)
PauseAndLock(false, m_was_unpaused);
}

} // namespace Core
@@ -92,6 +92,33 @@ enum class ConsoleType : u32
ReservedTDEVSystem = 0x20000007,
};

// Run a function as the CPU thread. This is an RAII alternative to the RunAsCPUThread function.
//
// If constructed from the Host thread, the CPU thread is paused and the current thread temporarily
// becomes the CPU thread.
// If constructed from the CPU thread, nothing special happens.
//
// This should only be constructed from the CPU thread or the host thread.
//
// Some functions use a parameter of this type to indicate that the function should only be called
// from the CPU thread. If the parameter is a pointer, the function has a fallback for being called
// from the wrong thread (with the argument being set to nullptr).
class CPUThreadGuard final
{
public:
CPUThreadGuard();
~CPUThreadGuard();

CPUThreadGuard(const CPUThreadGuard&) = delete;
CPUThreadGuard(CPUThreadGuard&&) = delete;
CPUThreadGuard& operator=(const CPUThreadGuard&) = delete;
CPUThreadGuard& operator=(CPUThreadGuard&&) = delete;

private:
const bool m_was_cpu_thread;
bool m_was_unpaused = false;
};

bool Init(std::unique_ptr<BootParameters> boot, const WindowSystemInfo& wsi);
void Stop();
void Shutdown();
@@ -39,41 +39,43 @@ void AddAutoBreakpoints()
}

// Returns true if the address is not a valid RAM address or NULL.
static bool IsStackBottom(u32 addr)
static bool IsStackBottom(const Core::CPUThreadGuard& guard, u32 addr)
{
return !addr || !PowerPC::HostIsRAMAddress(addr);
return !addr || !PowerPC::HostIsRAMAddress(guard, addr);
}

static void WalkTheStack(Core::System& system, const std::function<void(u32)>& stack_step)
static void WalkTheStack(Core::System& system, const Core::CPUThreadGuard& guard,
const std::function<void(u32)>& stack_step)
{
auto& ppc_state = system.GetPPCState();

if (!IsStackBottom(ppc_state.gpr[1]))
if (!IsStackBottom(guard, ppc_state.gpr[1]))
{
u32 addr = PowerPC::HostRead_U32(ppc_state.gpr[1]); // SP
u32 addr = PowerPC::HostRead_U32(guard, ppc_state.gpr[1]); // SP

// Walk the stack chain
for (int count = 0; !IsStackBottom(addr + 4) && (count++ < 20); ++count)
for (int count = 0; !IsStackBottom(guard, addr + 4) && (count++ < 20); ++count)
{
u32 func_addr = PowerPC::HostRead_U32(addr + 4);
u32 func_addr = PowerPC::HostRead_U32(guard, addr + 4);
stack_step(func_addr);

if (IsStackBottom(addr))
if (IsStackBottom(guard, addr))
break;

addr = PowerPC::HostRead_U32(addr);
addr = PowerPC::HostRead_U32(guard, addr);
}
}
}

// Returns callstack "formatted for debugging" - meaning that it
// includes LR as the last item, and all items are the last step,
// instead of "pointing ahead"
bool GetCallstack(Core::System& system, std::vector<CallstackEntry>& output)
bool GetCallstack(Core::System& system, const Core::CPUThreadGuard& guard,
std::vector<CallstackEntry>& output)
{
auto& ppc_state = system.GetPPCState();

if (!Core::IsRunning() || !PowerPC::HostIsRAMAddress(ppc_state.gpr[1]))
if (!Core::IsRunning() || !PowerPC::HostIsRAMAddress(guard, ppc_state.gpr[1]))
return false;

if (LR(ppc_state) == 0)
@@ -91,7 +93,7 @@ bool GetCallstack(Core::System& system, std::vector<CallstackEntry>& output)
entry.vAddress = LR(ppc_state) - 4;
output.push_back(entry);

WalkTheStack(system, [&entry, &output](u32 func_addr) {
WalkTheStack(system, guard, [&entry, &output](u32 func_addr) {
std::string func_desc = g_symbolDB.GetDescription(func_addr);
if (func_desc.empty() || func_desc == "Invalid")
func_desc = "(unknown)";
@@ -103,7 +105,8 @@ bool GetCallstack(Core::System& system, std::vector<CallstackEntry>& output)
return true;
}

void PrintCallstack(Core::System& system, Common::Log::LogType type, Common::Log::LogLevel level)
void PrintCallstack(Core::System& system, const Core::CPUThreadGuard& guard,
Common::Log::LogType type, Common::Log::LogLevel level)
{
auto& ppc_state = system.GetPPCState();

@@ -120,7 +123,7 @@ void PrintCallstack(Core::System& system, Common::Log::LogType type, Common::Log
LR(ppc_state));
}

WalkTheStack(system, [type, level](u32 func_addr) {
WalkTheStack(system, guard, [type, level](u32 func_addr) {
std::string func_desc = g_symbolDB.GetDescription(func_addr);
if (func_desc.empty() || func_desc == "Invalid")
func_desc = "(unknown)";
@@ -12,8 +12,9 @@

namespace Core
{
class CPUThreadGuard;
class System;
}
} // namespace Core

namespace Dolphin_Debugger
{
@@ -23,8 +24,10 @@ struct CallstackEntry
u32 vAddress = 0;
};

bool GetCallstack(Core::System& system, std::vector<CallstackEntry>& output);
void PrintCallstack(Core::System& system, Common::Log::LogType type, Common::Log::LogLevel level);
bool GetCallstack(Core::System& system, const Core::CPUThreadGuard& guard,
std::vector<CallstackEntry>& output);
void PrintCallstack(Core::System& system, const Core::CPUThreadGuard& guard,
Common::Log::LogType type, Common::Log::LogLevel level);
void PrintDataBuffer(Common::Log::LogType type, const u8* data, size_t size,
std::string_view title);
void AddAutoBreakpoints();
@@ -16,40 +16,40 @@ namespace Core::Debug
// - OSDumpContext
// - OSClearContext
// - OSExceptionVector
void OSContext::Read(u32 addr)
void OSContext::Read(const Core::CPUThreadGuard& guard, u32 addr)
{
for (std::size_t i = 0; i < gpr.size(); i++)
gpr[i] = PowerPC::HostRead_U32(addr + u32(i * sizeof(int)));
cr = PowerPC::HostRead_U32(addr + 0x80);
lr = PowerPC::HostRead_U32(addr + 0x84);
ctr = PowerPC::HostRead_U32(addr + 0x88);
xer = PowerPC::HostRead_U32(addr + 0x8C);
gpr[i] = PowerPC::HostRead_U32(guard, addr + u32(i * sizeof(int)));
cr = PowerPC::HostRead_U32(guard, addr + 0x80);
lr = PowerPC::HostRead_U32(guard, addr + 0x84);
ctr = PowerPC::HostRead_U32(guard, addr + 0x88);
xer = PowerPC::HostRead_U32(guard, addr + 0x8C);
for (std::size_t i = 0; i < fpr.size(); i++)
fpr[i] = PowerPC::HostRead_F64(addr + 0x90 + u32(i * sizeof(double)));
fpscr = PowerPC::HostRead_U64(addr + 0x190);
srr0 = PowerPC::HostRead_U32(addr + 0x198);
srr1 = PowerPC::HostRead_U32(addr + 0x19c);
dummy = PowerPC::HostRead_U16(addr + 0x1a0);
state = static_cast<OSContext::State>(PowerPC::HostRead_U16(addr + 0x1a2));
fpr[i] = PowerPC::HostRead_F64(guard, addr + 0x90 + u32(i * sizeof(double)));
fpscr = PowerPC::HostRead_U64(guard, addr + 0x190);
srr0 = PowerPC::HostRead_U32(guard, addr + 0x198);
srr1 = PowerPC::HostRead_U32(guard, addr + 0x19c);
dummy = PowerPC::HostRead_U16(guard, addr + 0x1a0);
state = static_cast<OSContext::State>(PowerPC::HostRead_U16(guard, addr + 0x1a2));
for (std::size_t i = 0; i < gqr.size(); i++)
gqr[i] = PowerPC::HostRead_U32(addr + 0x1a4 + u32(i * sizeof(int)));
gqr[i] = PowerPC::HostRead_U32(guard, addr + 0x1a4 + u32(i * sizeof(int)));
psf_padding = 0;
for (std::size_t i = 0; i < psf.size(); i++)
psf[i] = PowerPC::HostRead_F64(addr + 0x1c8 + u32(i * sizeof(double)));
psf[i] = PowerPC::HostRead_F64(guard, addr + 0x1c8 + u32(i * sizeof(double)));
}

// Mutex offsets based on the following functions:
// - OSInitMutex
// - OSLockMutex
// - __OSUnlockAllMutex
void OSMutex::Read(u32 addr)
void OSMutex::Read(const Core::CPUThreadGuard& guard, u32 addr)
{
thread_queue.head = PowerPC::HostRead_U32(addr);
thread_queue.tail = PowerPC::HostRead_U32(addr + 0x4);
owner_addr = PowerPC::HostRead_U32(addr + 0x8);
lock_count = PowerPC::HostRead_U32(addr + 0xc);
link.next = PowerPC::HostRead_U32(addr + 0x10);
link.prev = PowerPC::HostRead_U32(addr + 0x14);
thread_queue.head = PowerPC::HostRead_U32(guard, addr);
thread_queue.tail = PowerPC::HostRead_U32(guard, addr + 0x4);
owner_addr = PowerPC::HostRead_U32(guard, addr + 0x8);
lock_count = PowerPC::HostRead_U32(guard, addr + 0xc);
link.next = PowerPC::HostRead_U32(guard, addr + 0x10);
link.prev = PowerPC::HostRead_U32(guard, addr + 0x14);
}

// Thread offsets based on the following functions:
@@ -64,58 +64,59 @@ void OSMutex::Read(u32 addr)
// - __OSThreadInit
// - OSSetThreadSpecific
// - SOInit (for errno)
void OSThread::Read(u32 addr)
void OSThread::Read(const Core::CPUThreadGuard& guard, u32 addr)
{
context.Read(addr);
state = PowerPC::HostRead_U16(addr + 0x2c8);
is_detached = PowerPC::HostRead_U16(addr + 0x2ca);
suspend = PowerPC::HostRead_U32(addr + 0x2cc);
effective_priority = PowerPC::HostRead_U32(addr + 0x2d0);
base_priority = PowerPC::HostRead_U32(addr + 0x2d4);
exit_code_addr = PowerPC::HostRead_U32(addr + 0x2d8);
context.Read(guard, addr);
state = PowerPC::HostRead_U16(guard, addr + 0x2c8);
is_detached = PowerPC::HostRead_U16(guard, addr + 0x2ca);
suspend = PowerPC::HostRead_U32(guard, addr + 0x2cc);
effective_priority = PowerPC::HostRead_U32(guard, addr + 0x2d0);
base_priority = PowerPC::HostRead_U32(guard, addr + 0x2d4);
exit_code_addr = PowerPC::HostRead_U32(guard, addr + 0x2d8);

queue_addr = PowerPC::HostRead_U32(addr + 0x2dc);
queue_link.next = PowerPC::HostRead_U32(addr + 0x2e0);
queue_link.prev = PowerPC::HostRead_U32(addr + 0x2e4);
queue_addr = PowerPC::HostRead_U32(guard, addr + 0x2dc);
queue_link.next = PowerPC::HostRead_U32(guard, addr + 0x2e0);
queue_link.prev = PowerPC::HostRead_U32(guard, addr + 0x2e4);

join_queue.head = PowerPC::HostRead_U32(addr + 0x2e8);
join_queue.tail = PowerPC::HostRead_U32(addr + 0x2ec);
join_queue.head = PowerPC::HostRead_U32(guard, addr + 0x2e8);
join_queue.tail = PowerPC::HostRead_U32(guard, addr + 0x2ec);

mutex_addr = PowerPC::HostRead_U32(addr + 0x2f0);
mutex_queue.head = PowerPC::HostRead_U32(addr + 0x2f4);
mutex_queue.tail = PowerPC::HostRead_U32(addr + 0x2f8);
mutex_addr = PowerPC::HostRead_U32(guard, addr + 0x2f0);
mutex_queue.head = PowerPC::HostRead_U32(guard, addr + 0x2f4);
mutex_queue.tail = PowerPC::HostRead_U32(guard, addr + 0x2f8);

thread_link.next = PowerPC::HostRead_U32(addr + 0x2fc);
thread_link.prev = PowerPC::HostRead_U32(addr + 0x300);
thread_link.next = PowerPC::HostRead_U32(guard, addr + 0x2fc);
thread_link.prev = PowerPC::HostRead_U32(guard, addr + 0x300);

stack_addr = PowerPC::HostRead_U32(addr + 0x304);
stack_end = PowerPC::HostRead_U32(addr + 0x308);
error = PowerPC::HostRead_U32(addr + 0x30c);
specific[0] = PowerPC::HostRead_U32(addr + 0x310);
specific[1] = PowerPC::HostRead_U32(addr + 0x314);
stack_addr = PowerPC::HostRead_U32(guard, addr + 0x304);
stack_end = PowerPC::HostRead_U32(guard, addr + 0x308);
error = PowerPC::HostRead_U32(guard, addr + 0x30c);
specific[0] = PowerPC::HostRead_U32(guard, addr + 0x310);
specific[1] = PowerPC::HostRead_U32(guard, addr + 0x314);
}

bool OSThread::IsValid() const
bool OSThread::IsValid(const Core::CPUThreadGuard& guard) const
{
return PowerPC::HostIsRAMAddress(stack_end) && PowerPC::HostRead_U32(stack_end) == STACK_MAGIC;
return PowerPC::HostIsRAMAddress(guard, stack_end) &&
PowerPC::HostRead_U32(guard, stack_end) == STACK_MAGIC;
}

OSThreadView::OSThreadView(u32 addr)
OSThreadView::OSThreadView(const Core::CPUThreadGuard& guard, u32 addr)
{
m_address = addr;
m_thread.Read(addr);
m_thread.Read(guard, addr);
}

const OSThread& OSThreadView::Data() const
{
return m_thread;
}

Common::Debug::PartialContext OSThreadView::GetContext() const
Common::Debug::PartialContext OSThreadView::GetContext(const Core::CPUThreadGuard& guard) const
{
Common::Debug::PartialContext context;

if (!IsValid())
if (!IsValid(guard))
return context;

context.gpr = m_thread.context.gpr;
@@ -185,23 +186,23 @@ s32 OSThreadView::GetErrno() const
return m_thread.error;
}

std::string OSThreadView::GetSpecific() const
std::string OSThreadView::GetSpecific(const Core::CPUThreadGuard& guard) const
{
std::string specific;

for (u32 addr : m_thread.specific)
{
if (!PowerPC::HostIsRAMAddress(addr))
if (!PowerPC::HostIsRAMAddress(guard, addr))
break;
specific += fmt::format("{:08x} \"{}\"\n", addr, PowerPC::HostGetString(addr));
specific += fmt::format("{:08x} \"{}\"\n", addr, PowerPC::HostGetString(guard, addr));
}

return specific;
}

bool OSThreadView::IsValid() const
bool OSThreadView::IsValid(const Core::CPUThreadGuard& guard) const
{
return m_thread.IsValid();
return m_thread.IsValid(guard);
}

} // namespace Core::Debug
@@ -10,6 +10,11 @@
#include "Common/CommonTypes.h"
#include "Common/Debug/Threads.h"

namespace Core
{
class CPUThreadGuard;
};

namespace Core::Debug
{
template <class C>
@@ -56,7 +61,7 @@ struct OSContext
u32 psf_padding;
std::array<double, 32> psf;

void Read(u32 addr);
void Read(const Core::CPUThreadGuard& guard, u32 addr);
};

static_assert(std::is_trivially_copyable_v<OSContext>);
@@ -96,8 +101,8 @@ struct OSThread
std::array<u32, 2> specific; // Pointers to data (can be used to store thread names)

static constexpr u32 STACK_MAGIC = 0xDEADBABE;
void Read(u32 addr);
bool IsValid() const;
void Read(const Core::CPUThreadGuard& guard, u32 addr);
bool IsValid(const Core::CPUThreadGuard& guard) const;
};

static_assert(std::is_trivially_copyable_v<OSThread>);
@@ -115,7 +120,7 @@ struct OSMutex
OSMutexLink link; // Used to traverse the thread's mutex queue
// OSLockMutex uses it to insert the acquired mutex at the end of the queue

void Read(u32 addr);
void Read(const Core::CPUThreadGuard& guard, u32 addr);
};

static_assert(std::is_trivially_copyable_v<OSMutex>);
@@ -126,12 +131,12 @@ static_assert(offsetof(OSMutex, link) == 0x10);
class OSThreadView : public Common::Debug::ThreadView
{
public:
explicit OSThreadView(u32 addr);
explicit OSThreadView(const Core::CPUThreadGuard& guard, u32 addr);
~OSThreadView() = default;

const OSThread& Data() const;

Common::Debug::PartialContext GetContext() const override;
Common::Debug::PartialContext GetContext(const Core::CPUThreadGuard& guard) const override;
u32 GetAddress() const override;
u16 GetState() const override;
bool IsSuspended() const override;
@@ -142,8 +147,8 @@ class OSThreadView : public Common::Debug::ThreadView
u32 GetStackEnd() const override;
std::size_t GetStackSize() const override;
s32 GetErrno() const override;
std::string GetSpecific() const override;
bool IsValid() const override;
std::string GetSpecific(const Core::CPUThreadGuard& guard) const override;
bool IsValid(const Core::CPUThreadGuard& guard) const override;

private:
u32 m_address = 0;
@@ -25,27 +25,28 @@
#include "Core/PowerPC/PowerPC.h"
#include "Core/System.h"

void ApplyMemoryPatch(Common::Debug::MemoryPatch& patch, bool store_existing_value)
void ApplyMemoryPatch(const Core::CPUThreadGuard& guard, Common::Debug::MemoryPatch& patch,
bool store_existing_value)
{
if (patch.value.empty())
return;

const u32 address = patch.address;
const std::size_t size = patch.value.size();
if (!PowerPC::HostIsRAMAddress(address))
if (!PowerPC::HostIsRAMAddress(guard, address))
return;

for (u32 offset = 0; offset < size; ++offset)
{
if (store_existing_value)
{
const u8 value = PowerPC::HostRead_U8(address + offset);
PowerPC::HostWrite_U8(patch.value[offset], address + offset);
const u8 value = PowerPC::HostRead_U8(guard, address + offset);
PowerPC::HostWrite_U8(guard, patch.value[offset], address + offset);
patch.value[offset] = value;
}
else
{
PowerPC::HostWrite_U8(patch.value[offset], address + offset);
PowerPC::HostWrite_U8(guard, patch.value[offset], address + offset);
}

if (((address + offset) % 4) == 3)
@@ -58,17 +59,17 @@ void ApplyMemoryPatch(Common::Debug::MemoryPatch& patch, bool store_existing_val
}
}

void PPCPatches::ApplyExistingPatch(std::size_t index)
void PPCPatches::ApplyExistingPatch(const Core::CPUThreadGuard& guard, std::size_t index)
{
auto& patch = m_patches[index];
ApplyMemoryPatch(patch, false);
ApplyMemoryPatch(guard, patch, false);
}

void PPCPatches::Patch(std::size_t index)
void PPCPatches::Patch(const Core::CPUThreadGuard& guard, std::size_t index)
{
auto& patch = m_patches[index];
if (patch.type == Common::Debug::MemoryPatch::ApplyType::Once)
ApplyMemoryPatch(patch);
ApplyMemoryPatch(guard, patch);
else
PatchEngine::AddMemoryPatch(index);
}
@@ -163,90 +164,92 @@ void PPCDebugInterface::ClearWatches()
m_watches.Clear();
}

void PPCDebugInterface::SetPatch(u32 address, u32 value)
void PPCDebugInterface::SetPatch(const Core::CPUThreadGuard& guard, u32 address, u32 value)
{
m_patches.SetPatch(address, value);
m_patches.SetPatch(guard, address, value);
}

void PPCDebugInterface::SetPatch(u32 address, std::vector<u8> value)
void PPCDebugInterface::SetPatch(const Core::CPUThreadGuard& guard, u32 address,
std::vector<u8> value)
{
m_patches.SetPatch(address, std::move(value));
m_patches.SetPatch(guard, address, std::move(value));
}

void PPCDebugInterface::SetFramePatch(u32 address, u32 value)
void PPCDebugInterface::SetFramePatch(const Core::CPUThreadGuard& guard, u32 address, u32 value)
{
m_patches.SetFramePatch(address, value);
m_patches.SetFramePatch(guard, address, value);
}

void PPCDebugInterface::SetFramePatch(u32 address, std::vector<u8> value)
void PPCDebugInterface::SetFramePatch(const Core::CPUThreadGuard& guard, u32 address,
std::vector<u8> value)
{
m_patches.SetFramePatch(address, std::move(value));
m_patches.SetFramePatch(guard, address, std::move(value));
}

const std::vector<Common::Debug::MemoryPatch>& PPCDebugInterface::GetPatches() const
{
return m_patches.GetPatches();
}

void PPCDebugInterface::UnsetPatch(u32 address)
void PPCDebugInterface::UnsetPatch(const Core::CPUThreadGuard& guard, u32 address)
{
m_patches.UnsetPatch(address);
m_patches.UnsetPatch(guard, address);
}

void PPCDebugInterface::EnablePatch(std::size_t index)
void PPCDebugInterface::EnablePatch(const Core::CPUThreadGuard& guard, std::size_t index)
{
m_patches.EnablePatch(index);
m_patches.EnablePatch(guard, index);
}

void PPCDebugInterface::DisablePatch(std::size_t index)
void PPCDebugInterface::DisablePatch(const Core::CPUThreadGuard& guard, std::size_t index)
{
m_patches.DisablePatch(index);
m_patches.DisablePatch(guard, index);
}

bool PPCDebugInterface::HasEnabledPatch(u32 address) const
{
return m_patches.HasEnabledPatch(address);
}

void PPCDebugInterface::RemovePatch(std::size_t index)
void PPCDebugInterface::RemovePatch(const Core::CPUThreadGuard& guard, std::size_t index)
{
m_patches.RemovePatch(index);
m_patches.RemovePatch(guard, index);
}

void PPCDebugInterface::ClearPatches()
void PPCDebugInterface::ClearPatches(const Core::CPUThreadGuard& guard)
{
m_patches.ClearPatches();
m_patches.ClearPatches(guard);
}

void PPCDebugInterface::ApplyExistingPatch(std::size_t index)
void PPCDebugInterface::ApplyExistingPatch(const Core::CPUThreadGuard& guard, std::size_t index)
{
m_patches.ApplyExistingPatch(index);
m_patches.ApplyExistingPatch(guard, index);
}

Common::Debug::Threads PPCDebugInterface::GetThreads() const
Common::Debug::Threads PPCDebugInterface::GetThreads(const Core::CPUThreadGuard& guard) const
{
Common::Debug::Threads threads;

constexpr u32 ACTIVE_QUEUE_HEAD_ADDR = 0x800000dc;
if (!PowerPC::HostIsRAMAddress(ACTIVE_QUEUE_HEAD_ADDR))
if (!PowerPC::HostIsRAMAddress(guard, ACTIVE_QUEUE_HEAD_ADDR))
return threads;
const u32 active_queue_head = PowerPC::HostRead_U32(ACTIVE_QUEUE_HEAD_ADDR);
if (!PowerPC::HostIsRAMAddress(active_queue_head))
const u32 active_queue_head = PowerPC::HostRead_U32(guard, ACTIVE_QUEUE_HEAD_ADDR);
if (!PowerPC::HostIsRAMAddress(guard, active_queue_head))
return threads;

auto active_thread = std::make_unique<Core::Debug::OSThreadView>(active_queue_head);
if (!active_thread->IsValid())
auto active_thread = std::make_unique<Core::Debug::OSThreadView>(guard, active_queue_head);
if (!active_thread->IsValid(guard))
return threads;

std::vector<u32> visited_addrs{active_thread->GetAddress()};
const auto insert_threads = [&threads, &visited_addrs](u32 addr, auto get_next_addr) {
while (addr != 0 && PowerPC::HostIsRAMAddress(addr))
const auto insert_threads = [&guard, &threads, &visited_addrs](u32 addr, auto get_next_addr) {
while (addr != 0 && PowerPC::HostIsRAMAddress(guard, addr))
{
if (std::find(visited_addrs.begin(), visited_addrs.end(), addr) != visited_addrs.end())
break;
visited_addrs.push_back(addr);
auto thread = std::make_unique<Core::Debug::OSThreadView>(addr);
if (!thread->IsValid())
auto thread = std::make_unique<Core::Debug::OSThreadView>(guard, addr);
if (!thread->IsValid(guard))
break;
addr = get_next_addr(*thread);
threads.emplace_back(std::move(thread));
@@ -264,20 +267,16 @@ Common::Debug::Threads PPCDebugInterface::GetThreads() const
return threads;
}

std::string PPCDebugInterface::Disassemble(u32 address) const
std::string PPCDebugInterface::Disassemble(const Core::CPUThreadGuard* guard, u32 address) const
{
// PowerPC::HostRead_U32 seemed to crash on shutdown
if (!IsAlive())
return "";

if (Core::GetState() == Core::State::Paused)
if (guard)
{
if (!PowerPC::HostIsRAMAddress(address))
if (!PowerPC::HostIsRAMAddress(*guard, address))
{
return "(No RAM here)";
}

const u32 op = PowerPC::HostRead_Instruction(address);
const u32 op = PowerPC::HostRead_Instruction(*guard, address);
std::string disasm = Common::GekkoDisassembler::Disassemble(op, address);
const UGeckoInstruction inst{op};

@@ -294,32 +293,37 @@ std::string PPCDebugInterface::Disassemble(u32 address) const
}
}

std::string PPCDebugInterface::GetRawMemoryString(int memory, u32 address) const
std::string PPCDebugInterface::GetRawMemoryString(const Core::CPUThreadGuard& guard, int memory,
u32 address) const
{
if (IsAlive())
{
const bool is_aram = memory != 0;

if (is_aram || PowerPC::HostIsRAMAddress(address))
return fmt::format("{:08X}{}", ReadExtraMemory(memory, address), is_aram ? " (ARAM)" : "");
if (is_aram || PowerPC::HostIsRAMAddress(guard, address))
{
return fmt::format("{:08X}{}", ReadExtraMemory(guard, memory, address),
is_aram ? " (ARAM)" : "");
}

return is_aram ? "--ARAM--" : "--------";
}

return "<unknwn>"; // bad spelling - 8 chars
}

u32 PPCDebugInterface::ReadMemory(u32 address) const
u32 PPCDebugInterface::ReadMemory(const Core::CPUThreadGuard& guard, u32 address) const
{
return PowerPC::HostRead_U32(address);
return PowerPC::HostRead_U32(guard, address);
}

u32 PPCDebugInterface::ReadExtraMemory(int memory, u32 address) const
u32 PPCDebugInterface::ReadExtraMemory(const Core::CPUThreadGuard& guard, int memory,
u32 address) const
{
switch (memory)
{
case 0:
return PowerPC::HostRead_U32(address);
return PowerPC::HostRead_U32(guard, address);
case 1:
return (DSP::ReadARAM(address) << 24) | (DSP::ReadARAM(address + 1) << 16) |
(DSP::ReadARAM(address + 2) << 8) | (DSP::ReadARAM(address + 3));
@@ -328,9 +332,9 @@ u32 PPCDebugInterface::ReadExtraMemory(int memory, u32 address) const
}
}

u32 PPCDebugInterface::ReadInstruction(u32 address) const
u32 PPCDebugInterface::ReadInstruction(const Core::CPUThreadGuard& guard, u32 address) const
{
return PowerPC::HostRead_Instruction(address);
return PowerPC::HostRead_Instruction(guard, address);
}

bool PPCDebugInterface::IsAlive() const
@@ -401,11 +405,11 @@ void PPCDebugInterface::ToggleMemCheck(u32 address, bool read, bool write, bool
// =======================================================
// Separate the blocks with colors.
// -------------
u32 PPCDebugInterface::GetColor(u32 address) const
u32 PPCDebugInterface::GetColor(const Core::CPUThreadGuard* guard, u32 address) const
{
if (!IsAlive())
if (!guard || !IsAlive())
return 0xFFFFFF;
if (!PowerPC::HostIsRAMAddress(address))
if (!PowerPC::HostIsRAMAddress(*guard, address))
return 0xeeeeee;

Common::Symbol* symbol = g_symbolDB.GetSymbolFromAddr(address);
@@ -525,11 +529,11 @@ std::shared_ptr<Core::NetworkCaptureLogger> PPCDebugInterface::NetworkLogger()
return m_network_logger;
}

void PPCDebugInterface::Clear()
void PPCDebugInterface::Clear(const Core::CPUThreadGuard& guard)
{
ClearAllBreakpoints();
ClearAllMemChecks();
ClearPatches();
ClearPatches(guard);
ClearWatches();
m_network_logger.reset();
}
@@ -14,18 +14,20 @@

namespace Core
{
class CPUThreadGuard;
class System;
}
} // namespace Core

void ApplyMemoryPatch(Common::Debug::MemoryPatch& patch, bool store_existing_value = true);
void ApplyMemoryPatch(const Core::CPUThreadGuard&, Common::Debug::MemoryPatch& patch,
bool store_existing_value = true);

class PPCPatches final : public Common::Debug::MemoryPatches
{
public:
void ApplyExistingPatch(std::size_t index) override;
void ApplyExistingPatch(const Core::CPUThreadGuard& guard, std::size_t index) override;

private:
void Patch(std::size_t index) override;
void Patch(const Core::CPUThreadGuard& guard, std::size_t index) override;
void UnPatch(std::size_t index) override;
};

@@ -55,24 +57,26 @@ class PPCDebugInterface final : public Common::DebugInterface
void ClearWatches() override;

// Memory Patches
void SetPatch(u32 address, u32 value) override;
void SetPatch(u32 address, std::vector<u8> value) override;
void SetFramePatch(u32 address, u32 value) override;
void SetFramePatch(u32 address, std::vector<u8> value) override;
void SetPatch(const Core::CPUThreadGuard& guard, u32 address, u32 value) override;
void SetPatch(const Core::CPUThreadGuard& guard, u32 address, std::vector<u8> value) override;
void SetFramePatch(const Core::CPUThreadGuard& guard, u32 address, u32 value) override;
void SetFramePatch(const Core::CPUThreadGuard& guard, u32 address,
std::vector<u8> value) override;
const std::vector<Common::Debug::MemoryPatch>& GetPatches() const override;
void UnsetPatch(u32 address) override;
void EnablePatch(std::size_t index) override;
void DisablePatch(std::size_t index) override;
void UnsetPatch(const Core::CPUThreadGuard& guard, u32 address) override;
void EnablePatch(const Core::CPUThreadGuard& guard, std::size_t index) override;
void DisablePatch(const Core::CPUThreadGuard& guard, std::size_t index) override;
bool HasEnabledPatch(u32 address) const override;
void RemovePatch(std::size_t index) override;
void ClearPatches() override;
void ApplyExistingPatch(std::size_t index) override;
void RemovePatch(const Core::CPUThreadGuard& guard, std::size_t index) override;
void ClearPatches(const Core::CPUThreadGuard& guard) override;
void ApplyExistingPatch(const Core::CPUThreadGuard& guard, std::size_t index) override;

// Threads
Common::Debug::Threads GetThreads() const override;
Common::Debug::Threads GetThreads(const Core::CPUThreadGuard& guard) const override;

std::string Disassemble(u32 address) const override;
std::string GetRawMemoryString(int memory, u32 address) const override;
std::string Disassemble(const Core::CPUThreadGuard* guard, u32 address) const override;
std::string GetRawMemoryString(const Core::CPUThreadGuard& guard, int memory,
u32 address) const override;
bool IsAlive() const override;
bool IsBreakpoint(u32 address) const override;
void SetBreakpoint(u32 address) override;
@@ -82,26 +86,26 @@ class PPCDebugInterface final : public Common::DebugInterface
void ClearAllMemChecks() override;
bool IsMemCheck(u32 address, size_t size = 1) const override;
void ToggleMemCheck(u32 address, bool read = true, bool write = true, bool log = true) override;
u32 ReadMemory(u32 address) const override;
u32 ReadMemory(const Core::CPUThreadGuard& guard, u32 address) const override;

enum
{
EXTRAMEM_ARAM = 1,
};

u32 ReadExtraMemory(int memory, u32 address) const override;
u32 ReadInstruction(u32 address) const override;
u32 ReadExtraMemory(const Core::CPUThreadGuard& guard, int memory, u32 address) const override;
u32 ReadInstruction(const Core::CPUThreadGuard& guard, u32 address) const override;
std::optional<u32> GetMemoryAddressFromInstruction(const std::string& instruction) const override;
u32 GetPC() const override;
void SetPC(u32 address) override;
void Step() override {}
void RunToBreakpoint() override;
u32 GetColor(u32 address) const override;
u32 GetColor(const Core::CPUThreadGuard* guard, u32 address) const override;
std::string GetDescription(u32 address) const override;

std::shared_ptr<Core::NetworkCaptureLogger> NetworkLogger();

void Clear() override;
void Clear(const Core::CPUThreadGuard& guard) override;

private:
Common::Debug::Watches m_watches;

Large diffs are not rendered by default.

@@ -13,6 +13,11 @@

class PPCSymbolDB;

namespace Core
{
class CPUThreadGuard;
};

struct RSOEntry
{
u32 next_entry;
@@ -103,7 +108,7 @@ using RSOExternalsEntry = RSORelocationTableEntry<RSORelocationTableType::Extern
class RSOHeaderView
{
public:
void Load(u32 address);
void Load(const Core::CPUThreadGuard& guard, u32 address);

u32 GetNextEntry() const;
u32 GetPrevEntry() const;
@@ -139,7 +144,7 @@ class RSOHeaderView
class RSOSectionsView
{
public:
void Load(u32 address, std::size_t count = 1);
void Load(const Core::CPUThreadGuard& guard, u32 address, std::size_t count = 1);
std::size_t Count() const;

const RSOSection& GetSection(std::size_t index) const;
@@ -153,7 +158,7 @@ class RSOSectionsView
class RSOImportsView
{
public:
void Load(u32 address, std::size_t count = 1);
void Load(const Core::CPUThreadGuard& guard, u32 address, std::size_t count = 1);
std::size_t Count() const;

const RSOImport& GetImport(std::size_t index) const;
@@ -167,7 +172,7 @@ class RSOImportsView
class RSOExportsView
{
public:
void Load(u32 address, std::size_t count = 1);
void Load(const Core::CPUThreadGuard& guard, u32 address, std::size_t count = 1);
std::size_t Count() const;

const RSOExport& GetExport(std::size_t index) const;
@@ -181,7 +186,7 @@ class RSOExportsView
class RSOInternalsView
{
public:
void Load(u32 address, std::size_t count = 1);
void Load(const Core::CPUThreadGuard& guard, u32 address, std::size_t count = 1);
std::size_t Count() const;

const RSOInternalsEntry& GetEntry(std::size_t index) const;
@@ -195,7 +200,7 @@ class RSOInternalsView
class RSOExternalsView
{
public:
void Load(u32 address, std::size_t count = 1);
void Load(const Core::CPUThreadGuard& guard, u32 address, std::size_t count = 1);
std::size_t Count() const;

const RSOExternalsEntry& GetEntry(std::size_t index) const;
@@ -209,15 +214,15 @@ class RSOExternalsView
class RSOView
{
public:
void LoadHeader(u32 address);
void LoadSections();
void LoadImports();
void LoadExports();
void LoadInternals();
void LoadExternals();
void LoadAll(u32 address);
void LoadHeader(const Core::CPUThreadGuard& guard, u32 address);
void LoadSections(const Core::CPUThreadGuard& guard);
void LoadImports(const Core::CPUThreadGuard& guard);
void LoadExports(const Core::CPUThreadGuard& guard);
void LoadInternals(const Core::CPUThreadGuard& guard);
void LoadExternals(const Core::CPUThreadGuard& guard);
void LoadAll(const Core::CPUThreadGuard& guard, u32 address);

void Apply(PPCSymbolDB* symbol_db) const;
void Apply(const Core::CPUThreadGuard& guard, PPCSymbolDB* symbol_db) const;

u32 GetNextEntry() const;
u32 GetPrevEntry() const;
@@ -230,12 +235,12 @@ class RSOView

std::size_t GetImportsCount() const;
const RSOImport& GetImport(std::size_t index) const;
std::string GetImportName(const RSOImport& rso_import) const;
std::string GetImportName(const Core::CPUThreadGuard& guard, const RSOImport& rso_import) const;
const std::vector<RSOImport>& GetImports() const;

std::size_t GetExportsCount() const;
const RSOExport& GetExport(std::size_t index) const;
std::string GetExportName(const RSOExport& rso_export) const;
std::string GetExportName(const Core::CPUThreadGuard& guard, const RSOExport& rso_export) const;
u32 GetExportAddress(const RSOExport& rso_export) const;
const std::vector<RSOExport>& GetExports() const;

@@ -248,8 +253,8 @@ class RSOView
const std::vector<RSOExternalsEntry>& GetExternals() const;

const std::string& GetName() const;
std::string GetName(const RSOImport& rso_import) const;
std::string GetName(const RSOExport& rso_export) const;
std::string GetName(const Core::CPUThreadGuard& guard, const RSOImport& rso_import) const;
std::string GetName(const Core::CPUThreadGuard& guard, const RSOExport& rso_export) const;

u32 GetProlog() const;
u32 GetEpilog() const;
@@ -268,14 +273,14 @@ class RSOView
class RSOChainView
{
public:
bool Load(u32 address);
void Apply(PPCSymbolDB* symbol_db) const;
bool Load(const Core::CPUThreadGuard& guard, u32 address);
void Apply(const Core::CPUThreadGuard& guard, PPCSymbolDB* symbol_db) const;
void Clear();
const std::list<RSOView>& GetChain() const;

private:
bool LoadNextChain(const RSOView& view);
bool LoadPrevChain(const RSOView& view);
bool LoadNextChain(const Core::CPUThreadGuard& guard, const RSOView& view);
bool LoadPrevChain(const Core::CPUThreadGuard& guard, const RSOView& view);

std::list<RSOView> m_chain;
};
@@ -118,7 +118,7 @@ std::vector<GeckoCode> SetAndReturnActiveCodes(std::span<const GeckoCode> gcodes

// Requires s_active_codes_lock
// NOTE: Refer to "codehandleronly.s" from Gecko OS.
static Installation InstallCodeHandlerLocked()
static Installation InstallCodeHandlerLocked(const Core::CPUThreadGuard& guard)
{
std::string data;
if (!File::ReadFileToString(File::GetSysDirectory() + GECKO_CODE_HANDLER, data))
@@ -142,16 +142,17 @@ static Installation InstallCodeHandlerLocked()

// Install code handler
for (u32 i = 0; i < data.size(); ++i)
PowerPC::HostWrite_U8(data[i], INSTALLER_BASE_ADDRESS + i);
PowerPC::HostWrite_U8(guard, data[i], INSTALLER_BASE_ADDRESS + i);

// Patch the code handler to the current system type (Gamecube/Wii)
for (unsigned int h = 0; h < data.length(); h += 4)
{
// Patch MMIO address
if (PowerPC::HostRead_U32(INSTALLER_BASE_ADDRESS + h) == (0x3f000000u | ((mmio_addr ^ 1) << 8)))
if (PowerPC::HostRead_U32(guard, INSTALLER_BASE_ADDRESS + h) ==
(0x3f000000u | ((mmio_addr ^ 1) << 8)))
{
NOTICE_LOG_FMT(ACTIONREPLAY, "Patching MMIO access at {:08x}", INSTALLER_BASE_ADDRESS + h);
PowerPC::HostWrite_U32(0x3f000000u | mmio_addr << 8, INSTALLER_BASE_ADDRESS + h);
PowerPC::HostWrite_U32(guard, 0x3f000000u | mmio_addr << 8, INSTALLER_BASE_ADDRESS + h);
}
}

@@ -161,11 +162,11 @@ static Installation InstallCodeHandlerLocked()

// Write a magic value to 'gameid' (codehandleronly does not actually read this).
// This value will be read back and modified over time by HLE_Misc::GeckoCodeHandlerICacheFlush.
PowerPC::HostWrite_U32(MAGIC_GAMEID, INSTALLER_BASE_ADDRESS);
PowerPC::HostWrite_U32(guard, MAGIC_GAMEID, INSTALLER_BASE_ADDRESS);

// Create GCT in memory
PowerPC::HostWrite_U32(0x00d0c0de, codelist_base_address);
PowerPC::HostWrite_U32(0x00d0c0de, codelist_base_address + 4);
PowerPC::HostWrite_U32(guard, 0x00d0c0de, codelist_base_address);
PowerPC::HostWrite_U32(guard, 0x00d0c0de, codelist_base_address + 4);

// Each code is 8 bytes (2 words) wide. There is a starter code and an end code.
const u32 start_address = codelist_base_address + CODE_SIZE;
@@ -188,8 +189,8 @@ static Installation InstallCodeHandlerLocked()

for (const GeckoCode::Code& code : active_code.codes)
{
PowerPC::HostWrite_U32(code.address, next_address);
PowerPC::HostWrite_U32(code.data, next_address + 4);
PowerPC::HostWrite_U32(guard, code.address, next_address);
PowerPC::HostWrite_U32(guard, code.data, next_address + 4);
next_address += CODE_SIZE;
}
}
@@ -198,12 +199,12 @@ static Installation InstallCodeHandlerLocked()
end_address - start_address);

// Stop code. Tells the handler that this is the end of the list.
PowerPC::HostWrite_U32(0xF0000000, next_address);
PowerPC::HostWrite_U32(0x00000000, next_address + 4);
PowerPC::HostWrite_U32(0, HLE_TRAMPOLINE_ADDRESS);
PowerPC::HostWrite_U32(guard, 0xF0000000, next_address);
PowerPC::HostWrite_U32(guard, 0x00000000, next_address + 4);
PowerPC::HostWrite_U32(guard, 0, HLE_TRAMPOLINE_ADDRESS);

// Turn on codes
PowerPC::HostWrite_U8(1, INSTALLER_BASE_ADDRESS + 7);
PowerPC::HostWrite_U8(guard, 1, INSTALLER_BASE_ADDRESS + 7);

auto& system = Core::System::GetInstance();
auto& ppc_state = system.GetPPCState();
@@ -235,7 +236,7 @@ void Shutdown()
s_code_handler_installed = Installation::Uninstalled;
}

void RunCodeHandler()
void RunCodeHandler(const Core::CPUThreadGuard& guard)
{
if (!Config::Get(Config::MAIN_ENABLE_CHEATS))
return;
@@ -249,7 +250,7 @@ void RunCodeHandler()
// fixed within 1 frame of the last error.
if (s_active_codes.empty() || s_code_handler_installed == Installation::Failed)
return;
s_code_handler_installed = InstallCodeHandlerLocked();
s_code_handler_installed = InstallCodeHandlerLocked(guard);

// A warning was already issued for the install failing
if (s_code_handler_installed != Installation::Installed)
@@ -273,17 +274,17 @@ void RunCodeHandler()
ppc_state.gpr[1] -= 8; // Fake stack frame for codehandler
ppc_state.gpr[1] &= 0xFFFFFFF0; // Align stack to 16bytes
u32 SP = ppc_state.gpr[1]; // Stack Pointer
PowerPC::HostWrite_U32(SP + 8, SP);
PowerPC::HostWrite_U32(guard, SP + 8, SP);
// SP + 4 is reserved for the codehandler to save LR to the stack.
PowerPC::HostWrite_U32(SFP, SP + 8); // Real stack frame
PowerPC::HostWrite_U32(ppc_state.pc, SP + 12);
PowerPC::HostWrite_U32(LR(ppc_state), SP + 16);
PowerPC::HostWrite_U32(ppc_state.cr.Get(), SP + 20);
PowerPC::HostWrite_U32(guard, SFP, SP + 8); // Real stack frame
PowerPC::HostWrite_U32(guard, ppc_state.pc, SP + 12);
PowerPC::HostWrite_U32(guard, LR(ppc_state), SP + 16);
PowerPC::HostWrite_U32(guard, ppc_state.cr.Get(), SP + 20);
// Registers FPR0->13 are volatile
for (int i = 0; i < 14; ++i)
{
PowerPC::HostWrite_U64(ppc_state.ps[i].PS0AsU64(), SP + 24 + 2 * i * sizeof(u64));
PowerPC::HostWrite_U64(ppc_state.ps[i].PS1AsU64(), SP + 24 + (2 * i + 1) * sizeof(u64));
PowerPC::HostWrite_U64(guard, ppc_state.ps[i].PS0AsU64(), SP + 24 + 2 * i * sizeof(u64));
PowerPC::HostWrite_U64(guard, ppc_state.ps[i].PS1AsU64(), SP + 24 + (2 * i + 1) * sizeof(u64));
}
DEBUG_LOG_FMT(ACTIONREPLAY,
"GeckoCodes: Initiating phantom branch-and-link. "
@@ -11,6 +11,11 @@

class PointerWrap;

namespace Core
{
class CPUThreadGuard;
};

namespace Gecko
{
class GeckoCode
@@ -63,7 +68,7 @@ void SetActiveCodes(std::span<const GeckoCode> gcodes);
void SetSyncedCodesAsActive();
void UpdateSyncedCodes(std::span<const GeckoCode> gcodes);
std::vector<GeckoCode> SetAndReturnActiveCodes(std::span<const GeckoCode> gcodes);
void RunCodeHandler();
void RunCodeHandler(const Core::CPUThreadGuard& guard);
void Shutdown();
void DoState(PointerWrap&);

@@ -152,12 +152,12 @@ void Reload(Core::System& system)
PatchFunctions(system);
}

void Execute(u32 current_pc, u32 hook_index)
void Execute(const Core::CPUThreadGuard& guard, u32 current_pc, u32 hook_index)
{
hook_index &= 0xFFFFF;
if (hook_index > 0 && hook_index < os_patches.size())
{
os_patches[hook_index].function();
os_patches[hook_index].function(guard);
}
else
{
@@ -9,12 +9,13 @@

namespace Core
{
class CPUThreadGuard;
class System;
}
} // namespace Core

namespace HLE
{
using HookFunction = void (*)();
using HookFunction = void (*)(const Core::CPUThreadGuard&);

enum class HookType
{
@@ -46,7 +47,7 @@ void Reload(Core::System& system);
void Patch(Core::System& system, u32 pc, std::string_view func_name);
u32 UnPatch(Core::System& system, std::string_view patch_name);
u32 UnpatchRange(Core::System& system, u32 start_addr, u32 end_addr);
void Execute(u32 current_pc, u32 hook_index);
void Execute(const Core::CPUThreadGuard& guard, u32 current_pc, u32 hook_index);

// Returns the HLE hook index of the address
u32 GetHookByAddress(u32 address);
@@ -16,21 +16,21 @@ namespace HLE_Misc
{
// If you just want to kill a function, one of the three following are usually appropriate.
// According to the PPC ABI, the return value is always in r3.
void UnimplementedFunction()
void UnimplementedFunction(const Core::CPUThreadGuard&)
{
auto& system = Core::System::GetInstance();
auto& ppc_state = system.GetPPCState();
ppc_state.npc = LR(ppc_state);
}

void HBReload()
void HBReload(const Core::CPUThreadGuard&)
{
// There isn't much we can do. Just stop cleanly.
CPU::Break();
Host_Message(HostMessageID::WMUserStop);
}

void GeckoCodeHandlerICacheFlush()
void GeckoCodeHandlerICacheFlush(const Core::CPUThreadGuard& guard)
{
auto& system = Core::System::GetInstance();
auto& ppc_state = system.GetPPCState();
@@ -41,7 +41,7 @@ void GeckoCodeHandlerICacheFlush()
// been read into memory, or such, so we do the first 5 frames. More
// robust alternative would be to actually detect memory writes, but that
// would be even uglier.)
u32 gch_gameid = PowerPC::HostRead_U32(Gecko::INSTALLER_BASE_ADDRESS);
u32 gch_gameid = PowerPC::HostRead_U32(guard, Gecko::INSTALLER_BASE_ADDRESS);
if (gch_gameid - Gecko::MAGIC_GAMEID == 5)
{
return;
@@ -50,7 +50,7 @@ void GeckoCodeHandlerICacheFlush()
{
gch_gameid = Gecko::MAGIC_GAMEID;
}
PowerPC::HostWrite_U32(gch_gameid + 1, Gecko::INSTALLER_BASE_ADDRESS);
PowerPC::HostWrite_U32(guard, gch_gameid + 1, Gecko::INSTALLER_BASE_ADDRESS);

ppc_state.iCache.Reset();
}
@@ -59,21 +59,21 @@ void GeckoCodeHandlerICacheFlush()
// need a way to branch into the GCH from an arbitrary PC address. Branching is easy, returning
// back is the hard part. This HLE function acts as a trampoline that restores the original LR, SP,
// and PC before the magic, invisible BL instruction happened.
void GeckoReturnTrampoline()
void GeckoReturnTrampoline(const Core::CPUThreadGuard& guard)
{
auto& system = Core::System::GetInstance();
auto& ppc_state = system.GetPPCState();

// Stack frame is built in GeckoCode.cpp, Gecko::RunCodeHandler.
u32 SP = ppc_state.gpr[1];
ppc_state.gpr[1] = PowerPC::HostRead_U32(SP + 8);
ppc_state.npc = PowerPC::HostRead_U32(SP + 12);
LR(ppc_state) = PowerPC::HostRead_U32(SP + 16);
ppc_state.cr.Set(PowerPC::HostRead_U32(SP + 20));
ppc_state.gpr[1] = PowerPC::HostRead_U32(guard, SP + 8);
ppc_state.npc = PowerPC::HostRead_U32(guard, SP + 12);
LR(ppc_state) = PowerPC::HostRead_U32(guard, SP + 16);
ppc_state.cr.Set(PowerPC::HostRead_U32(guard, SP + 20));
for (int i = 0; i < 14; ++i)
{
ppc_state.ps[i].SetBoth(PowerPC::HostRead_U64(SP + 24 + 2 * i * sizeof(u64)),
PowerPC::HostRead_U64(SP + 24 + (2 * i + 1) * sizeof(u64)));
ppc_state.ps[i].SetBoth(PowerPC::HostRead_U64(guard, SP + 24 + 2 * i * sizeof(u64)),
PowerPC::HostRead_U64(guard, SP + 24 + (2 * i + 1) * sizeof(u64)));
}
}
} // namespace HLE_Misc
@@ -3,10 +3,15 @@

#pragma once

namespace Core
{
class CPUThreadGuard;
};

namespace HLE_Misc
{
void UnimplementedFunction();
void HBReload();
void GeckoCodeHandlerICacheFlush();
void GeckoReturnTrampoline();
void UnimplementedFunction(const Core::CPUThreadGuard& guard);
void HBReload(const Core::CPUThreadGuard& guard);
void GeckoCodeHandlerICacheFlush(const Core::CPUThreadGuard& guard);
void GeckoReturnTrampoline(const Core::CPUThreadGuard& guard);
} // namespace HLE_Misc
@@ -23,19 +23,19 @@ enum class ParameterType : bool
VariableArgumentList = true
};

std::string GetStringVA(Core::System& system, u32 str_reg = 3,
std::string GetStringVA(Core::System& system, const Core::CPUThreadGuard& guard, u32 str_reg = 3,
ParameterType parameter_type = ParameterType::ParameterList);
void HLE_GeneralDebugPrint(ParameterType parameter_type);
void HLE_LogDPrint(ParameterType parameter_type);
void HLE_LogFPrint(ParameterType parameter_type);
void HLE_GeneralDebugPrint(const Core::CPUThreadGuard& guard, ParameterType parameter_type);
void HLE_LogDPrint(const Core::CPUThreadGuard& guard, ParameterType parameter_type);
void HLE_LogFPrint(const Core::CPUThreadGuard& guard, ParameterType parameter_type);

void HLE_OSPanic()
void HLE_OSPanic(const Core::CPUThreadGuard& guard)
{
auto& system = Core::System::GetInstance();
auto& ppc_state = system.GetPPCState();

std::string error = GetStringVA(system);
std::string msg = GetStringVA(system, 5);
std::string error = GetStringVA(system, guard);
std::string msg = GetStringVA(system, guard, 5);

StringPopBackIf(&error, '\n');
StringPopBackIf(&msg, '\n');
@@ -48,40 +48,40 @@ void HLE_OSPanic()
}

// Generalized function for printing formatted string.
void HLE_GeneralDebugPrint(ParameterType parameter_type)
void HLE_GeneralDebugPrint(const Core::CPUThreadGuard& guard, ParameterType parameter_type)
{
auto& system = Core::System::GetInstance();
auto& ppc_state = system.GetPPCState();

std::string report_message;

// Is gpr3 pointing to a pointer (including nullptr) rather than an ASCII string
if (PowerPC::HostIsRAMAddress(ppc_state.gpr[3]) &&
(PowerPC::HostIsRAMAddress(PowerPC::HostRead_U32(ppc_state.gpr[3])) ||
PowerPC::HostRead_U32(ppc_state.gpr[3]) == 0))
if (PowerPC::HostIsRAMAddress(guard, ppc_state.gpr[3]) &&
(PowerPC::HostIsRAMAddress(guard, PowerPC::HostRead_U32(guard, ppc_state.gpr[3])) ||
PowerPC::HostRead_U32(guard, ppc_state.gpr[3]) == 0))
{
if (PowerPC::HostIsRAMAddress(ppc_state.gpr[4]))
if (PowerPC::HostIsRAMAddress(guard, ppc_state.gpr[4]))
{
// ___blank(void* this, const char* fmt, ...);
report_message = GetStringVA(system, 4, parameter_type);
report_message = GetStringVA(system, guard, 4, parameter_type);
}
else
{
// ___blank(void* this, int log_type, const char* fmt, ...);
report_message = GetStringVA(system, 5, parameter_type);
report_message = GetStringVA(system, guard, 5, parameter_type);
}
}
else
{
if (PowerPC::HostIsRAMAddress(ppc_state.gpr[3]))
if (PowerPC::HostIsRAMAddress(guard, ppc_state.gpr[3]))
{
// ___blank(const char* fmt, ...);
report_message = GetStringVA(system, 3, parameter_type);
report_message = GetStringVA(system, guard, 3, parameter_type);
}
else
{
// ___blank(int log_type, const char* fmt, ...);
report_message = GetStringVA(system, 4, parameter_type);
report_message = GetStringVA(system, guard, 4, parameter_type);
}
}

@@ -92,25 +92,25 @@ void HLE_GeneralDebugPrint(ParameterType parameter_type)
}

// Generalized function for printing formatted string using parameter list.
void HLE_GeneralDebugPrint()
void HLE_GeneralDebugPrint(const Core::CPUThreadGuard& guard)
{
HLE_GeneralDebugPrint(ParameterType::ParameterList);
HLE_GeneralDebugPrint(guard, ParameterType::ParameterList);
}

// Generalized function for printing formatted string using va_list.
void HLE_GeneralDebugVPrint()
void HLE_GeneralDebugVPrint(const Core::CPUThreadGuard& guard)
{
HLE_GeneralDebugPrint(ParameterType::VariableArgumentList);
HLE_GeneralDebugPrint(guard, ParameterType::VariableArgumentList);
}

// __write_console(int fd, const void* buffer, const u32* size)
void HLE_write_console()
void HLE_write_console(const Core::CPUThreadGuard& guard)
{
auto& system = Core::System::GetInstance();
auto& ppc_state = system.GetPPCState();

std::string report_message = GetStringVA(system, 4);
if (PowerPC::HostIsRAMAddress(ppc_state.gpr[5]))
std::string report_message = GetStringVA(system, guard, 4);
if (PowerPC::HostIsRAMAddress(guard, ppc_state.gpr[5]))
{
const u32 size = PowerPC::Read_U32(ppc_state.gpr[5]);
if (size > report_message.size())
@@ -132,87 +132,89 @@ void HLE_write_console()
}

// Log (v)dprintf message if fd is 1 (stdout) or 2 (stderr)
void HLE_LogDPrint(ParameterType parameter_type)
void HLE_LogDPrint(const Core::CPUThreadGuard& guard, ParameterType parameter_type)
{
auto& system = Core::System::GetInstance();
auto& ppc_state = system.GetPPCState();

if (ppc_state.gpr[3] != 1 && ppc_state.gpr[3] != 2)
return;

std::string report_message = GetStringVA(system, 4, parameter_type);
std::string report_message = GetStringVA(system, guard, 4, parameter_type);
StringPopBackIf(&report_message, '\n');
NOTICE_LOG_FMT(OSREPORT_HLE, "{:08x}->{:08x}| {}", LR(ppc_state), ppc_state.pc,
SHIFTJISToUTF8(report_message));
}

// Log dprintf message
// -> int dprintf(int fd, const char* format, ...);
void HLE_LogDPrint()
void HLE_LogDPrint(const Core::CPUThreadGuard& guard)
{
HLE_LogDPrint(ParameterType::ParameterList);
HLE_LogDPrint(guard, ParameterType::ParameterList);
}

// Log vdprintf message
// -> int vdprintf(int fd, const char* format, va_list ap);
void HLE_LogVDPrint()
void HLE_LogVDPrint(const Core::CPUThreadGuard& guard)
{
HLE_LogDPrint(ParameterType::VariableArgumentList);
HLE_LogDPrint(guard, ParameterType::VariableArgumentList);
}

// Log (v)fprintf message if FILE is stdout or stderr
void HLE_LogFPrint(ParameterType parameter_type)
void HLE_LogFPrint(const Core::CPUThreadGuard& guard, ParameterType parameter_type)
{
auto& system = Core::System::GetInstance();
auto& ppc_state = system.GetPPCState();

// 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(ppc_state.gpr[3]) &&
PowerPC::HostIsRAMAddress(ppc_state.gpr[3] + 0xF))
if (PowerPC::HostIsRAMAddress(guard, ppc_state.gpr[3]) &&
PowerPC::HostIsRAMAddress(guard, ppc_state.gpr[3] + 0xF))
{
// The fd is stored as a short at FILE+0xE.
fd = static_cast<short>(PowerPC::HostRead_U16(ppc_state.gpr[3] + 0xE));
fd = static_cast<short>(PowerPC::HostRead_U16(guard, ppc_state.gpr[3] + 0xE));
}
if (fd != 1 && fd != 2)
{
// On RVL SDK it seems stored at FILE+0x2.
fd = static_cast<short>(PowerPC::HostRead_U16(ppc_state.gpr[3] + 0x2));
fd = static_cast<short>(PowerPC::HostRead_U16(guard, ppc_state.gpr[3] + 0x2));
}
if (fd != 1 && fd != 2)
return;

std::string report_message = GetStringVA(system, 4, parameter_type);
std::string report_message = GetStringVA(system, guard, 4, parameter_type);
StringPopBackIf(&report_message, '\n');
NOTICE_LOG_FMT(OSREPORT_HLE, "{:08x}->{:08x}| {}", LR(ppc_state), ppc_state.pc,
SHIFTJISToUTF8(report_message));
}

// Log fprintf message
// -> int fprintf(FILE* stream, const char* format, ...);
void HLE_LogFPrint()
void HLE_LogFPrint(const Core::CPUThreadGuard& guard)
{
HLE_LogFPrint(ParameterType::ParameterList);
HLE_LogFPrint(guard, ParameterType::ParameterList);
}

// Log vfprintf message
// -> int vfprintf(FILE* stream, const char* format, va_list ap);
void HLE_LogVFPrint()
void HLE_LogVFPrint(const Core::CPUThreadGuard& guard)
{
HLE_LogFPrint(ParameterType::VariableArgumentList);
HLE_LogFPrint(guard, ParameterType::VariableArgumentList);
}

std::string GetStringVA(Core::System& system, u32 str_reg, ParameterType parameter_type)
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::HostGetString(ppc_state.gpr[str_reg]);
std::string string = PowerPC::HostGetString(guard, ppc_state.gpr[str_reg]);
auto ap =
parameter_type == ParameterType::VariableArgumentList ?
std::make_unique<HLE::SystemVABI::VAListStruct>(system, ppc_state.gpr[str_reg + 1]) :
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);

for (size_t i = 0; i < string.size(); i++)
@@ -241,7 +243,7 @@ std::string GetStringVA(Core::System& system, u32 str_reg, ParameterType paramet
{
case 's':
result += StringFromFormat(ArgumentBuffer.c_str(),
PowerPC::HostGetString(ap->GetArgT<u32>()).c_str());
PowerPC::HostGetString(guard, ap->GetArgT<u32>(guard)).c_str());
break;

case 'a':
@@ -252,12 +254,12 @@ std::string GetStringVA(Core::System& system, u32 str_reg, ParameterType paramet
case 'F':
case 'g':
case 'G':
result += StringFromFormat(ArgumentBuffer.c_str(), ap->GetArgT<double>());
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>());
result += StringFromFormat("%x", ap->GetArgT<u32>(guard));
break;

case 'n':
@@ -267,9 +269,9 @@ std::string GetStringVA(Core::System& system, u32 str_reg, ParameterType paramet

default:
if (string[i - 1] == 'l' && string[i - 2] == 'l')
result += StringFromFormat(ArgumentBuffer.c_str(), ap->GetArgT<u64>());
result += StringFromFormat(ArgumentBuffer.c_str(), ap->GetArgT<u64>(guard));
else
result += StringFromFormat(ArgumentBuffer.c_str(), ap->GetArgT<u32>());
result += StringFromFormat(ArgumentBuffer.c_str(), ap->GetArgT<u32>(guard));
break;
}
}
@@ -3,14 +3,19 @@

#pragma once

namespace Core
{
class CPUThreadGuard;
};

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();
void HLE_GeneralDebugPrint(const Core::CPUThreadGuard& guard);
void HLE_GeneralDebugVPrint(const Core::CPUThreadGuard& guard);
void HLE_write_console(const Core::CPUThreadGuard& guard);
void HLE_OSPanic(const Core::CPUThreadGuard& guard);
void HLE_LogDPrint(const Core::CPUThreadGuard& guard);
void HLE_LogVDPrint(const Core::CPUThreadGuard& guard);
void HLE_LogFPrint(const Core::CPUThreadGuard& guard);
void HLE_LogVFPrint(const Core::CPUThreadGuard& guard);
} // namespace HLE_OS