DolphinQt: Properly lock CPU before accessing emulated memory

This fixes a problem I was having where using frame advance with the
debugger open would frequently cause panic alerts about invalid addresses
due to the CPU thread changing MSR.DR while the host thread was trying
to access memory.

To aid in tracking down all the places where we weren't properly locking
the CPU, I've created a new type (in Core.h) that you have to pass as a
reference or pointer to functions that require running as the CPU thread.
This commit is contained in:
JosJuice 2023-02-12 11:07:11 +01:00
parent efed037c4a
commit 7cecb28bdf
79 changed files with 1796 additions and 1184 deletions

View File

@ -122,14 +122,14 @@ InstructionAttributes CodeTrace::GetInstructionAttributes(const TraceOutput& ins
return tmp_attributes;
}
TraceOutput CodeTrace::SaveCurrentInstruction() const
TraceOutput CodeTrace::SaveCurrentInstruction(const Core::CPUThreadGuard* guard) const
{
auto& system = Core::System::GetInstance();
auto& ppc_state = system.GetPPCState();
// Quickly save instruction and memory target for fast logging.
TraceOutput output;
const std::string instr = PowerPC::debug_interface.Disassemble(ppc_state.pc);
const std::string instr = PowerPC::debug_interface.Disassemble(guard, ppc_state.pc);
output.instruction = instr;
output.address = ppc_state.pc;
@ -139,14 +139,15 @@ TraceOutput CodeTrace::SaveCurrentInstruction() const
return output;
}
AutoStepResults CodeTrace::AutoStepping(bool continue_previous, AutoStop stop_on)
AutoStepResults CodeTrace::AutoStepping(const Core::CPUThreadGuard& guard, bool continue_previous,
AutoStop stop_on)
{
AutoStepResults results;
if (!CPU::IsStepping() || m_recording)
if (m_recording)
return results;
TraceOutput pc_instr = SaveCurrentInstruction();
TraceOutput pc_instr = SaveCurrentInstruction(&guard);
const InstructionAttributes instr = GetInstructionAttributes(pc_instr);
// Not an instruction we should start autostepping from (ie branches).
@ -187,7 +188,6 @@ AutoStepResults CodeTrace::AutoStepping(bool continue_previous, AutoStop stop_on
else if (stop_on == AutoStop::Changed)
stop_condition = HitType::ACTIVE;
CPU::PauseAndLock(true, false);
PowerPC::breakpoints.ClearAllTemporary();
using clock = std::chrono::steady_clock;
clock::time_point timeout = clock::now() + std::chrono::seconds(4);
@ -199,7 +199,7 @@ AutoStepResults CodeTrace::AutoStepping(bool continue_previous, AutoStop stop_on
{
PowerPC::SingleStep();
pc_instr = SaveCurrentInstruction();
pc_instr = SaveCurrentInstruction(&guard);
hit = TraceLogic(pc_instr);
results.count += 1;
} while (clock::now() < timeout && hit < stop_condition &&
@ -210,7 +210,6 @@ AutoStepResults CodeTrace::AutoStepping(bool continue_previous, AutoStop stop_on
results.timed_out = true;
PowerPC::SetMode(old_mode);
CPU::PauseAndLock(false, false);
m_recording = false;
results.reg_tracked = m_reg_autotrack;

View File

@ -10,6 +10,11 @@
#include "Common/CommonTypes.h"
namespace Core
{
class CPUThreadGuard;
}
struct InstructionAttributes
{
u32 address = 0;
@ -63,11 +68,12 @@ public:
};
void SetRegTracked(const std::string& reg);
AutoStepResults AutoStepping(bool continue_previous = false, AutoStop stop_on = AutoStop::Always);
AutoStepResults AutoStepping(const Core::CPUThreadGuard& guard, bool continue_previous = false,
AutoStop stop_on = AutoStop::Always);
private:
InstructionAttributes GetInstructionAttributes(const TraceOutput& line) const;
TraceOutput SaveCurrentInstruction() const;
TraceOutput SaveCurrentInstruction(const Core::CPUThreadGuard* guard) const;
HitType TraceLogic(const TraceOutput& current_instr, bool first_hit = false);
bool m_recording = false;

View File

@ -23,36 +23,37 @@ MemoryPatch::MemoryPatch(u32 address_, u32 value_)
MemoryPatches::MemoryPatches() = default;
MemoryPatches::~MemoryPatches() = default;
void MemoryPatches::SetPatch(u32 address, u32 value)
void MemoryPatches::SetPatch(const Core::CPUThreadGuard& guard, u32 address, u32 value)
{
const std::size_t index = m_patches.size();
m_patches.emplace_back(address, value);
Patch(index);
Patch(guard, index);
}
void MemoryPatches::SetPatch(u32 address, std::vector<u8> value)
void MemoryPatches::SetPatch(const Core::CPUThreadGuard& guard, u32 address, std::vector<u8> value)
{
UnsetPatch(address);
UnsetPatch(guard, address);
const std::size_t index = m_patches.size();
m_patches.emplace_back(address, std::move(value));
Patch(index);
Patch(guard, index);
}
void MemoryPatches::SetFramePatch(u32 address, u32 value)
void MemoryPatches::SetFramePatch(const Core::CPUThreadGuard& guard, u32 address, u32 value)
{
const std::size_t index = m_patches.size();
m_patches.emplace_back(address, value);
m_patches.back().type = MemoryPatch::ApplyType::EachFrame;
Patch(index);
Patch(guard, index);
}
void MemoryPatches::SetFramePatch(u32 address, std::vector<u8> value)
void MemoryPatches::SetFramePatch(const Core::CPUThreadGuard& guard, u32 address,
std::vector<u8> value)
{
UnsetPatch(address);
UnsetPatch(guard, address);
const std::size_t index = m_patches.size();
m_patches.emplace_back(address, std::move(value));
m_patches.back().type = MemoryPatch::ApplyType::EachFrame;
Patch(index);
Patch(guard, index);
}
const std::vector<MemoryPatch>& MemoryPatches::GetPatches() const
@ -60,7 +61,7 @@ const std::vector<MemoryPatch>& MemoryPatches::GetPatches() const
return m_patches;
}
void MemoryPatches::UnsetPatch(u32 address)
void MemoryPatches::UnsetPatch(const Core::CPUThreadGuard& guard, u32 address)
{
const auto it = std::find_if(m_patches.begin(), m_patches.end(),
[address](const auto& patch) { return patch.address == address; });
@ -69,23 +70,23 @@ void MemoryPatches::UnsetPatch(u32 address)
return;
const std::size_t index = std::distance(m_patches.begin(), it);
RemovePatch(index);
RemovePatch(guard, index);
}
void MemoryPatches::EnablePatch(std::size_t index)
void MemoryPatches::EnablePatch(const Core::CPUThreadGuard& guard, std::size_t index)
{
if (m_patches[index].is_enabled == MemoryPatch::State::Enabled)
return;
m_patches[index].is_enabled = MemoryPatch::State::Enabled;
Patch(index);
Patch(guard, index);
}
void MemoryPatches::DisablePatch(std::size_t index)
void MemoryPatches::DisablePatch(const Core::CPUThreadGuard& guard, std::size_t index)
{
if (m_patches[index].is_enabled == MemoryPatch::State::Disabled)
return;
m_patches[index].is_enabled = MemoryPatch::State::Disabled;
Patch(index);
Patch(guard, index);
}
bool MemoryPatches::HasEnabledPatch(u32 address) const
@ -95,19 +96,19 @@ bool MemoryPatches::HasEnabledPatch(u32 address) const
});
}
void MemoryPatches::RemovePatch(std::size_t index)
void MemoryPatches::RemovePatch(const Core::CPUThreadGuard& guard, std::size_t index)
{
DisablePatch(index);
DisablePatch(guard, index);
UnPatch(index);
m_patches.erase(m_patches.begin() + index);
}
void MemoryPatches::ClearPatches()
void MemoryPatches::ClearPatches(const Core::CPUThreadGuard& guard)
{
const std::size_t size = m_patches.size();
for (std::size_t index = 0; index < size; ++index)
{
DisablePatch(index);
DisablePatch(guard, index);
UnPatch(index);
}
m_patches.clear();

View File

@ -9,6 +9,11 @@
#include "Common/CommonTypes.h"
namespace Core
{
class CPUThreadGuard;
}
namespace Common::Debug
{
struct MemoryPatch
@ -40,21 +45,21 @@ public:
MemoryPatches();
virtual ~MemoryPatches();
void SetPatch(u32 address, u32 value);
void SetPatch(u32 address, std::vector<u8> value);
void SetFramePatch(u32 address, u32 value);
void SetFramePatch(u32 address, std::vector<u8> value);
void SetPatch(const Core::CPUThreadGuard& guard, u32 address, u32 value);
void SetPatch(const Core::CPUThreadGuard& guard, u32 address, std::vector<u8> value);
void SetFramePatch(const Core::CPUThreadGuard& guard, u32 address, u32 value);
void SetFramePatch(const Core::CPUThreadGuard& guard, u32 address, std::vector<u8> value);
const std::vector<MemoryPatch>& GetPatches() const;
void UnsetPatch(u32 address);
void EnablePatch(std::size_t index);
void DisablePatch(std::size_t index);
void UnsetPatch(const Core::CPUThreadGuard& guard, u32 address);
void EnablePatch(const Core::CPUThreadGuard& guard, std::size_t index);
void DisablePatch(const Core::CPUThreadGuard& guard, std::size_t index);
bool HasEnabledPatch(u32 address) const;
void RemovePatch(std::size_t index);
void ClearPatches();
virtual void ApplyExistingPatch(std::size_t index) = 0;
void RemovePatch(const Core::CPUThreadGuard& guard, std::size_t index);
void ClearPatches(const Core::CPUThreadGuard& guard);
virtual void ApplyExistingPatch(const Core::CPUThreadGuard& guard, std::size_t index) = 0;
protected:
virtual void Patch(std::size_t index) = 0;
virtual void Patch(const Core::CPUThreadGuard& guard, std::size_t index) = 0;
virtual void UnPatch(std::size_t index) = 0;
std::vector<MemoryPatch> m_patches;

View File

@ -11,6 +11,11 @@
#include "Common/CommonTypes.h"
namespace Core
{
class CPUThreadGuard;
};
namespace Common::Debug
{
struct PartialContext
@ -41,7 +46,7 @@ public:
LWPThread, // devkitPro libogc thread
};
virtual PartialContext GetContext() const = 0;
virtual PartialContext GetContext(const Core::CPUThreadGuard& guard) const = 0;
virtual u32 GetAddress() const = 0;
virtual u16 GetState() const = 0;
virtual bool IsSuspended() const = 0;
@ -53,8 +58,8 @@ public:
virtual std::size_t GetStackSize() const = 0;
virtual s32 GetErrno() const = 0;
// Implementation specific, used to store arbitrary data
virtual std::string GetSpecific() const = 0;
virtual bool IsValid() const = 0;
virtual std::string GetSpecific(const Core::CPUThreadGuard& guard) const = 0;
virtual bool IsValid(const Core::CPUThreadGuard& guard) const = 0;
};
using Threads = std::vector<std::unique_ptr<ThreadView>>;

View File

@ -16,6 +16,11 @@ struct MemoryPatch;
struct Watch;
} // namespace Common::Debug
namespace Core
{
class CPUThreadGuard;
} // namespace Core
namespace Common
{
class DebugInterface
@ -42,24 +47,29 @@ public:
virtual void ClearWatches() = 0;
// Memory Patches
virtual void SetPatch(u32 address, u32 value) = 0;
virtual void SetPatch(u32 address, std::vector<u8> value) = 0;
virtual void SetFramePatch(u32 address, u32 value) = 0;
virtual void SetFramePatch(u32 address, std::vector<u8> value) = 0;
virtual void SetPatch(const Core::CPUThreadGuard& guard, u32 address, u32 value) = 0;
virtual void SetPatch(const Core::CPUThreadGuard& guard, u32 address, std::vector<u8> value) = 0;
virtual void SetFramePatch(const Core::CPUThreadGuard& guard, u32 address, u32 value) = 0;
virtual void SetFramePatch(const Core::CPUThreadGuard& guard, u32 address,
std::vector<u8> value) = 0;
virtual const std::vector<Debug::MemoryPatch>& GetPatches() const = 0;
virtual void UnsetPatch(u32 address) = 0;
virtual void EnablePatch(std::size_t index) = 0;
virtual void DisablePatch(std::size_t index) = 0;
virtual void UnsetPatch(const Core::CPUThreadGuard& guard, u32 address) = 0;
virtual void EnablePatch(const Core::CPUThreadGuard& guard, std::size_t index) = 0;
virtual void DisablePatch(const Core::CPUThreadGuard& guard, std::size_t index) = 0;
virtual bool HasEnabledPatch(u32 address) const = 0;
virtual void RemovePatch(std::size_t index) = 0;
virtual void ClearPatches() = 0;
virtual void ApplyExistingPatch(std::size_t index) = 0;
virtual void RemovePatch(const Core::CPUThreadGuard& guard, std::size_t index) = 0;
virtual void ClearPatches(const Core::CPUThreadGuard& guard) = 0;
virtual void ApplyExistingPatch(const Core::CPUThreadGuard& guard, std::size_t index) = 0;
// Threads
virtual Debug::Threads GetThreads() const = 0;
virtual Debug::Threads GetThreads(const Core::CPUThreadGuard& guard) const = 0;
virtual std::string Disassemble(u32 /*address*/) const { return "NODEBUGGER"; }
virtual std::string GetRawMemoryString(int /*memory*/, u32 /*address*/) const
virtual std::string Disassemble(const Core::CPUThreadGuard* /*guard*/, u32 /*address*/) const
{
return "NODEBUGGER";
}
virtual std::string GetRawMemoryString(const Core::CPUThreadGuard& /*guard*/, int /*memory*/,
u32 /*address*/) const
{
return "NODEBUGGER";
}
@ -72,10 +82,20 @@ public:
virtual void ClearAllMemChecks() {}
virtual bool IsMemCheck(u32 /*address*/, size_t /*size*/) const { return false; }
virtual void ToggleMemCheck(u32 /*address*/, bool /*read*/, bool /*write*/, bool /*log*/) {}
virtual u32 ReadMemory(u32 /*address*/) const { return 0; }
virtual void WriteExtraMemory(int /*memory*/, u32 /*value*/, u32 /*address*/) {}
virtual u32 ReadExtraMemory(int /*memory*/, u32 /*address*/) const { return 0; }
virtual u32 ReadInstruction(u32 /*address*/) const { return 0; }
virtual u32 ReadMemory(const Core::CPUThreadGuard& /*guard*/, u32 /*address*/) const { return 0; }
virtual void WriteExtraMemory(const Core::CPUThreadGuard& /*guard*/, int /*memory*/,
u32 /*value*/, u32 /*address*/)
{
}
virtual u32 ReadExtraMemory(const Core::CPUThreadGuard& /*guard*/, int /*memory*/,
u32 /*address*/) const
{
return 0;
}
virtual u32 ReadInstruction(const Core::CPUThreadGuard& /*guard*/, u32 /*address*/) const
{
return 0;
}
virtual std::optional<u32>
GetMemoryAddressFromInstruction(const std::string& /*instruction*/) const
{
@ -85,8 +105,11 @@ public:
virtual void SetPC(u32 /*address*/) {}
virtual void Step() {}
virtual void RunToBreakpoint() {}
virtual u32 GetColor(u32 /*address*/) const { return 0xFFFFFFFF; }
virtual u32 GetColor(const Core::CPUThreadGuard* /*guard*/, u32 /*address*/) const
{
return 0xFFFFFFFF;
}
virtual std::string GetDescription(u32 /*address*/) const = 0;
virtual void Clear() = 0;
virtual void Clear(const Core::CPUThreadGuard& guard) = 0;
};
} // namespace Common

View File

@ -15,6 +15,11 @@
#include "Common/CommonTypes.h"
namespace Core
{
class CPUThreadGuard;
}
namespace Common
{
struct SCall
@ -68,7 +73,7 @@ public:
virtual ~SymbolDB();
virtual Symbol* GetSymbolFromAddr(u32 addr) { return nullptr; }
virtual Symbol* AddFunction(u32 start_addr) { return nullptr; }
virtual Symbol* AddFunction(const Core::CPUThreadGuard& guard, u32 start_addr) { return nullptr; }
void AddCompleteSymbol(const Symbol& symbol);
Symbol* GetSymbolFromName(std::string_view name);

View File

@ -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;
}),

View File

@ -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();

View File

@ -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;
}

View File

@ -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 @@ public:
//
// 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 @@ private:
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 @@ public:
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;

View File

@ -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);
}

View File

@ -27,7 +27,7 @@ public:
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

View File

@ -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;
}
}

View File

@ -36,7 +36,7 @@ public:
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;

View File

@ -103,41 +103,47 @@ 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));
@ -145,9 +151,10 @@ TryReadValueFromEmulatedMemory(u32 addr, PowerPC::RequestedAddressSpace space)
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));
@ -155,9 +162,10 @@ TryReadValueFromEmulatedMemory(u32 addr, PowerPC::RequestedAddressSpace space)
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));
@ -165,9 +173,10 @@ TryReadValueFromEmulatedMemory(u32 addr, PowerPC::RequestedAddressSpace space)
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));
@ -175,22 +184,25 @@ TryReadValueFromEmulatedMemory(u32 addr, PowerPC::RequestedAddressSpace space)
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,12 +455,12 @@ 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)
@ -455,19 +468,19 @@ Cheats::SearchErrorCode Cheats::CheatSearchSession<T>::RunSearch()
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; });
}
}

View File

@ -14,6 +14,11 @@
#include "Common/Result.h"
#include "Core/PowerPC/MMU.h"
namespace Core
{
class CPUThreadGuard;
};
namespace Cheats
{
enum class CompareType
@ -108,7 +113,7 @@ 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);
@ -116,7 +121,7 @@ NewSearch(const std::vector<MemoryRange>& memory_ranges,
// 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 @@ public:
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 @@ public:
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;

View File

@ -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();

View File

@ -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);

View File

@ -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

View File

@ -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();

View File

@ -39,29 +39,30 @@ 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);
}
}
}
@ -69,11 +70,12 @@ static void WalkTheStack(Core::System& system, const std::function<void(u32)>& s
// 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)";

View File

@ -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();

View File

@ -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,46 +64,47 @@ 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
@ -111,11 +112,11 @@ 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

View File

@ -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 @@ public:
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;

View File

@ -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,24 +164,26 @@ 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
@ -188,19 +191,19 @@ const std::vector<Common::Debug::MemoryPatch>& PPCDebugInterface::GetPatches() c
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
@ -208,45 +211,45 @@ 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,14 +293,18 @@ 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--" : "--------";
}
@ -309,17 +312,18 @@ std::string PPCDebugInterface::GetRawMemoryString(int memory, u32 address) const
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();
}

View File

@ -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 @@ public:
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 @@ public:
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;

View File

@ -14,40 +14,40 @@
#include "Core/PowerPC/MMU.h"
#include "Core/PowerPC/PPCSymbolDB.h"
void RSOHeaderView::Load(u32 address)
void RSOHeaderView::Load(const Core::CPUThreadGuard& guard, u32 address)
{
m_address = address;
m_header.entry.next_entry = PowerPC::HostRead_U32(address);
m_header.entry.prev_entry = PowerPC::HostRead_U32(address + 0x04);
m_header.entry.section_count = PowerPC::HostRead_U32(address + 0x08);
m_header.entry.section_table_offset = PowerPC::HostRead_U32(address + 0xC);
m_header.entry.name_offset = PowerPC::HostRead_U32(address + 0x10);
m_header.entry.name_size = PowerPC::HostRead_U32(address + 0x14);
m_header.entry.version = PowerPC::HostRead_U32(address + 0x18);
m_header.entry.bss_size = PowerPC::HostRead_U32(address + 0x1C);
m_header.section_info.prolog_section_index = PowerPC::HostRead_U8(address + 0x20);
m_header.section_info.epilog_section_index = PowerPC::HostRead_U8(address + 0x21);
m_header.section_info.unresolved_section_index = PowerPC::HostRead_U8(address + 0x22);
m_header.section_info.bss_section_index = PowerPC::HostRead_U8(address + 0x23);
m_header.section_info.prolog_offset = PowerPC::HostRead_U32(address + 0x24);
m_header.section_info.epilog_offset = PowerPC::HostRead_U32(address + 0x28);
m_header.section_info.unresolved_offset = PowerPC::HostRead_U32(address + 0x2C);
m_header.relocation_tables.internals_offset = PowerPC::HostRead_U32(address + 0x30);
m_header.relocation_tables.internals_size = PowerPC::HostRead_U32(address + 0x34);
m_header.relocation_tables.externals_offset = PowerPC::HostRead_U32(address + 0x38);
m_header.relocation_tables.externals_size = PowerPC::HostRead_U32(address + 0x3C);
m_header.symbol_tables.exports_offset = PowerPC::HostRead_U32(address + 0x40);
m_header.symbol_tables.exports_size = PowerPC::HostRead_U32(address + 0x44);
m_header.symbol_tables.exports_name_table = PowerPC::HostRead_U32(address + 0x48);
m_header.symbol_tables.imports_offset = PowerPC::HostRead_U32(address + 0x4C);
m_header.symbol_tables.imports_size = PowerPC::HostRead_U32(address + 0x50);
m_header.symbol_tables.imports_name_table = PowerPC::HostRead_U32(address + 0x54);
m_header.entry.next_entry = PowerPC::HostRead_U32(guard, address);
m_header.entry.prev_entry = PowerPC::HostRead_U32(guard, address + 0x04);
m_header.entry.section_count = PowerPC::HostRead_U32(guard, address + 0x08);
m_header.entry.section_table_offset = PowerPC::HostRead_U32(guard, address + 0xC);
m_header.entry.name_offset = PowerPC::HostRead_U32(guard, address + 0x10);
m_header.entry.name_size = PowerPC::HostRead_U32(guard, address + 0x14);
m_header.entry.version = PowerPC::HostRead_U32(guard, address + 0x18);
m_header.entry.bss_size = PowerPC::HostRead_U32(guard, address + 0x1C);
m_header.section_info.prolog_section_index = PowerPC::HostRead_U8(guard, address + 0x20);
m_header.section_info.epilog_section_index = PowerPC::HostRead_U8(guard, address + 0x21);
m_header.section_info.unresolved_section_index = PowerPC::HostRead_U8(guard, address + 0x22);
m_header.section_info.bss_section_index = PowerPC::HostRead_U8(guard, address + 0x23);
m_header.section_info.prolog_offset = PowerPC::HostRead_U32(guard, address + 0x24);
m_header.section_info.epilog_offset = PowerPC::HostRead_U32(guard, address + 0x28);
m_header.section_info.unresolved_offset = PowerPC::HostRead_U32(guard, address + 0x2C);
m_header.relocation_tables.internals_offset = PowerPC::HostRead_U32(guard, address + 0x30);
m_header.relocation_tables.internals_size = PowerPC::HostRead_U32(guard, address + 0x34);
m_header.relocation_tables.externals_offset = PowerPC::HostRead_U32(guard, address + 0x38);
m_header.relocation_tables.externals_size = PowerPC::HostRead_U32(guard, address + 0x3C);
m_header.symbol_tables.exports_offset = PowerPC::HostRead_U32(guard, address + 0x40);
m_header.symbol_tables.exports_size = PowerPC::HostRead_U32(guard, address + 0x44);
m_header.symbol_tables.exports_name_table = PowerPC::HostRead_U32(guard, address + 0x48);
m_header.symbol_tables.imports_offset = PowerPC::HostRead_U32(guard, address + 0x4C);
m_header.symbol_tables.imports_size = PowerPC::HostRead_U32(guard, address + 0x50);
m_header.symbol_tables.imports_name_table = PowerPC::HostRead_U32(guard, address + 0x54);
// Prevent an invalid name going wild
if (m_header.entry.name_size < 0x100)
m_name = PowerPC::HostGetString(m_header.entry.name_offset, m_header.entry.name_size);
m_name = PowerPC::HostGetString(guard, m_header.entry.name_offset, m_header.entry.name_size);
else
m_name = PowerPC::HostGetString(m_header.entry.name_offset, 0x100);
m_name = PowerPC::HostGetString(guard, m_header.entry.name_offset, 0x100);
}
u32 RSOHeaderView::GetNextEntry() const
@ -170,14 +170,14 @@ u32 RSOHeaderView::GetImportsNameTable() const
return m_header.symbol_tables.imports_name_table;
}
void RSOSectionsView::Load(u32 address, std::size_t count)
void RSOSectionsView::Load(const Core::CPUThreadGuard& guard, u32 address, std::size_t count)
{
m_address = address;
for (std::size_t i = 0; i < count; ++i)
{
RSOSection section;
section.offset = PowerPC::HostRead_U32(address);
section.size = PowerPC::HostRead_U32(address + 4);
section.offset = PowerPC::HostRead_U32(guard, address);
section.size = PowerPC::HostRead_U32(guard, address + 4);
m_sections.emplace_back(std::move(section));
address += sizeof(RSOSection);
}
@ -198,15 +198,15 @@ const std::vector<RSOSection>& RSOSectionsView::GetSections() const
return m_sections;
}
void RSOImportsView::Load(u32 address, std::size_t count)
void RSOImportsView::Load(const Core::CPUThreadGuard& guard, u32 address, std::size_t count)
{
m_address = address;
for (std::size_t i = 0; i < count; ++i)
{
RSOImport rso_import;
rso_import.name_offset = PowerPC::HostRead_U32(address);
rso_import.code_offset = PowerPC::HostRead_U32(address + 4);
rso_import.entry_offset = PowerPC::HostRead_U32(address + 8);
rso_import.name_offset = PowerPC::HostRead_U32(guard, address);
rso_import.code_offset = PowerPC::HostRead_U32(guard, address + 4);
rso_import.entry_offset = PowerPC::HostRead_U32(guard, address + 8);
m_imports.emplace_back(std::move(rso_import));
address += sizeof(RSOImport);
}
@ -227,16 +227,16 @@ const std::vector<RSOImport>& RSOImportsView::GetImports() const
return m_imports;
}
void RSOExportsView::Load(u32 address, std::size_t count)
void RSOExportsView::Load(const Core::CPUThreadGuard& guard, u32 address, std::size_t count)
{
m_address = address;
for (std::size_t i = 0; i < count; ++i)
{
RSOExport rso_export;
rso_export.name_offset = PowerPC::HostRead_U32(address);
rso_export.code_offset = PowerPC::HostRead_U32(address + 4);
rso_export.section_index = PowerPC::HostRead_U32(address + 8);
rso_export.hash = PowerPC::HostRead_U32(address + 12);
rso_export.name_offset = PowerPC::HostRead_U32(guard, address);
rso_export.code_offset = PowerPC::HostRead_U32(guard, address + 4);
rso_export.section_index = PowerPC::HostRead_U32(guard, address + 8);
rso_export.hash = PowerPC::HostRead_U32(guard, address + 12);
m_exports.emplace_back(std::move(rso_export));
address += sizeof(RSOExport);
}
@ -257,15 +257,15 @@ const std::vector<RSOExport>& RSOExportsView::GetExports() const
return m_exports;
}
void RSOInternalsView::Load(u32 address, std::size_t count)
void RSOInternalsView::Load(const Core::CPUThreadGuard& guard, u32 address, std::size_t count)
{
m_address = address;
for (std::size_t i = 0; i < count; ++i)
{
RSOInternalsEntry entry;
entry.r_offset = PowerPC::HostRead_U32(address);
entry.r_info = PowerPC::HostRead_U32(address + 4);
entry.r_addend = PowerPC::HostRead_U32(address + 8);
entry.r_offset = PowerPC::HostRead_U32(guard, address);
entry.r_info = PowerPC::HostRead_U32(guard, address + 4);
entry.r_addend = PowerPC::HostRead_U32(guard, address + 8);
m_entries.emplace_back(std::move(entry));
address += sizeof(RSOInternalsEntry);
}
@ -286,15 +286,15 @@ const std::vector<RSOInternalsEntry>& RSOInternalsView::GetEntries() const
return m_entries;
}
void RSOExternalsView::Load(u32 address, std::size_t count)
void RSOExternalsView::Load(const Core::CPUThreadGuard& guard, u32 address, std::size_t count)
{
m_address = address;
for (std::size_t i = 0; i < count; ++i)
{
RSOExternalsEntry entry;
entry.r_offset = PowerPC::HostRead_U32(address);
entry.r_info = PowerPC::HostRead_U32(address + 4);
entry.r_addend = PowerPC::HostRead_U32(address + 8);
entry.r_offset = PowerPC::HostRead_U32(guard, address);
entry.r_info = PowerPC::HostRead_U32(guard, address + 4);
entry.r_addend = PowerPC::HostRead_U32(guard, address + 8);
m_entries.emplace_back(std::move(entry));
address += sizeof(RSOExternalsEntry);
}
@ -315,71 +315,71 @@ const std::vector<RSOExternalsEntry>& RSOExternalsView::GetEntries() const
return m_entries;
}
void RSOView::LoadHeader(u32 address)
void RSOView::LoadHeader(const Core::CPUThreadGuard& guard, u32 address)
{
m_address = address;
m_header.Load(address);
m_header.Load(guard, address);
}
void RSOView::LoadSections()
void RSOView::LoadSections(const Core::CPUThreadGuard& guard)
{
m_sections.Load(m_header.GetSectionTableOffset(), m_header.GetSectionCount());
m_sections.Load(guard, m_header.GetSectionTableOffset(), m_header.GetSectionCount());
}
void RSOView::LoadImports()
void RSOView::LoadImports(const Core::CPUThreadGuard& guard)
{
const std::size_t size = m_header.GetImportsSize();
if (size % sizeof(RSOImport) != 0)
WARN_LOG_FMT(SYMBOLS, "RSO Imports Table has an incoherent size ({:08x})", size);
m_imports.Load(m_header.GetImportsOffset(), size / sizeof(RSOImport));
m_imports.Load(guard, m_header.GetImportsOffset(), size / sizeof(RSOImport));
}
void RSOView::LoadExports()
void RSOView::LoadExports(const Core::CPUThreadGuard& guard)
{
const std::size_t size = m_header.GetExportsSize();
if (size % sizeof(RSOExport) != 0)
WARN_LOG_FMT(SYMBOLS, "RSO Exports Table has an incoherent size ({:08x})", size);
m_exports.Load(m_header.GetExportsOffset(), size / sizeof(RSOExport));
m_exports.Load(guard, m_header.GetExportsOffset(), size / sizeof(RSOExport));
}
void RSOView::LoadInternals()
void RSOView::LoadInternals(const Core::CPUThreadGuard& guard)
{
const std::size_t size = m_header.GetInternalsSize();
if (size % sizeof(RSOInternalsEntry) != 0)
WARN_LOG_FMT(SYMBOLS, "RSO Internals Relocation Table has an incoherent size ({:08x})", size);
m_imports.Load(m_header.GetInternalsOffset(), size / sizeof(RSOInternalsEntry));
m_imports.Load(guard, m_header.GetInternalsOffset(), size / sizeof(RSOInternalsEntry));
}
void RSOView::LoadExternals()
void RSOView::LoadExternals(const Core::CPUThreadGuard& guard)
{
const std::size_t size = m_header.GetExternalsSize();
if (size % sizeof(RSOExternalsEntry) != 0)
WARN_LOG_FMT(SYMBOLS, "RSO Externals Relocation Table has an incoherent size ({:08x})", size);
m_imports.Load(m_header.GetExternalsOffset(), size / sizeof(RSOExternalsEntry));
m_imports.Load(guard, m_header.GetExternalsOffset(), size / sizeof(RSOExternalsEntry));
}
void RSOView::LoadAll(u32 address)
void RSOView::LoadAll(const Core::CPUThreadGuard& guard, u32 address)
{
LoadHeader(address);
LoadSections();
LoadImports();
LoadExports();
LoadInternals();
LoadExternals();
LoadHeader(guard, address);
LoadSections(guard);
LoadImports(guard);
LoadExports(guard);
LoadInternals(guard);
LoadExternals(guard);
}
void RSOView::Apply(PPCSymbolDB* symbol_db) const
void RSOView::Apply(const Core::CPUThreadGuard& guard, PPCSymbolDB* symbol_db) const
{
for (const RSOExport& rso_export : GetExports())
{
u32 address = GetExportAddress(rso_export);
if (address != 0)
{
Common::Symbol* symbol = symbol_db->AddFunction(address);
Common::Symbol* symbol = symbol_db->AddFunction(guard, address);
if (!symbol)
symbol = symbol_db->GetSymbolFromAddr(address);
const std::string export_name = GetExportName(rso_export);
const std::string export_name = GetExportName(guard, rso_export);
if (symbol)
{
// Function symbol
@ -388,7 +388,7 @@ void RSOView::Apply(PPCSymbolDB* symbol_db) const
else
{
// Data symbol
symbol_db->AddKnownSymbol(address, 0, export_name, Common::Symbol::Type::Data);
symbol_db->AddKnownSymbol(guard, address, 0, export_name, Common::Symbol::Type::Data);
}
}
}
@ -440,9 +440,10 @@ const RSOImport& RSOView::GetImport(std::size_t index) const
return m_imports.GetImport(index);
}
std::string RSOView::GetImportName(const RSOImport& rso_import) const
std::string RSOView::GetImportName(const Core::CPUThreadGuard& guard,
const RSOImport& rso_import) const
{
return PowerPC::HostGetString(m_header.GetImportsNameTable() + rso_import.name_offset);
return PowerPC::HostGetString(guard, m_header.GetImportsNameTable() + rso_import.name_offset);
}
const std::vector<RSOImport>& RSOView::GetImports() const
@ -460,9 +461,10 @@ const RSOExport& RSOView::GetExport(std::size_t index) const
return m_exports.GetExport(index);
}
std::string RSOView::GetExportName(const RSOExport& rso_export) const
std::string RSOView::GetExportName(const Core::CPUThreadGuard& guard,
const RSOExport& rso_export) const
{
return PowerPC::HostGetString(m_header.GetExportsNameTable() + rso_export.name_offset);
return PowerPC::HostGetString(guard, m_header.GetExportsNameTable() + rso_export.name_offset);
}
u32 RSOView::GetExportAddress(const RSOExport& rso_export) const
@ -515,14 +517,14 @@ const std::string& RSOView::GetName() const
return m_header.GetName();
}
std::string RSOView::GetName(const RSOImport& rso_import) const
std::string RSOView::GetName(const Core::CPUThreadGuard& guard, const RSOImport& rso_import) const
{
return GetImportName(rso_import);
return GetImportName(guard, rso_import);
}
std::string RSOView::GetName(const RSOExport& rso_export) const
std::string RSOView::GetName(const Core::CPUThreadGuard& guard, const RSOExport& rso_export) const
{
return GetExportName(rso_export);
return GetExportName(guard, rso_export);
}
u32 RSOView::GetProlog() const
@ -561,33 +563,33 @@ u32 RSOView::GetUnresolved() const
return 0;
}
bool RSOChainView::Load(u32 address)
bool RSOChainView::Load(const Core::CPUThreadGuard& guard, u32 address)
{
// Load node
RSOView node;
node.LoadHeader(address);
node.LoadHeader(guard, address);
DEBUG_LOG_FMT(SYMBOLS, "RSOChain node name: {}", node.GetName());
m_chain.emplace_front(std::move(node));
if (LoadNextChain(m_chain.front()) && LoadPrevChain(m_chain.front()))
if (LoadNextChain(guard, m_chain.front()) && LoadPrevChain(guard, m_chain.front()))
{
for (RSOView& view : m_chain)
{
view.LoadSections();
view.LoadExports();
view.LoadImports();
view.LoadExternals();
view.LoadInternals();
view.LoadSections(guard);
view.LoadExports(guard);
view.LoadImports(guard);
view.LoadExternals(guard);
view.LoadInternals(guard);
}
return true;
}
return false;
}
void RSOChainView::Apply(PPCSymbolDB* symbol_db) const
void RSOChainView::Apply(const Core::CPUThreadGuard& guard, PPCSymbolDB* symbol_db) const
{
for (const RSOView& rso_view : m_chain)
rso_view.Apply(symbol_db);
rso_view.Apply(guard, symbol_db);
}
void RSOChainView::Clear()
@ -600,7 +602,7 @@ const std::list<RSOView>& RSOChainView::GetChain() const
return m_chain;
}
bool RSOChainView::LoadNextChain(const RSOView& view)
bool RSOChainView::LoadNextChain(const Core::CPUThreadGuard& guard, const RSOView& view)
{
u32 prev_address = view.GetAddress();
u32 next_address = view.GetNextEntry();
@ -608,7 +610,7 @@ bool RSOChainView::LoadNextChain(const RSOView& view)
while (next_address != 0)
{
RSOView next_node;
next_node.LoadHeader(next_address);
next_node.LoadHeader(guard, next_address);
if (prev_address != next_node.GetPrevEntry())
{
@ -625,7 +627,7 @@ bool RSOChainView::LoadNextChain(const RSOView& view)
return true;
}
bool RSOChainView::LoadPrevChain(const RSOView& view)
bool RSOChainView::LoadPrevChain(const Core::CPUThreadGuard& guard, const RSOView& view)
{
u32 prev_address = view.GetPrevEntry();
u32 next_address = view.GetAddress();
@ -633,7 +635,7 @@ bool RSOChainView::LoadPrevChain(const RSOView& view)
while (prev_address != 0)
{
RSOView prev_node;
prev_node.LoadHeader(prev_address);
prev_node.LoadHeader(guard, prev_address);
if (next_address != prev_node.GetNextEntry())
{

View File

@ -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 @@ private:
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 @@ private:
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 @@ private:
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 @@ private:
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 @@ private:
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 @@ private:
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 @@ public:
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 @@ public:
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 @@ private:
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;
};

View File

@ -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. "

View File

@ -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&);

View File

@ -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
{

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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,7 +48,7 @@ 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();
@ -56,32 +56,32 @@ void HLE_GeneralDebugPrint(ParameterType parameter_type)
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,7 +132,7 @@ 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();
@ -140,7 +140,7 @@ void HLE_LogDPrint(ParameterType parameter_type)
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));
@ -148,20 +148,20 @@ void HLE_LogDPrint(ParameterType parameter_type)
// 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();
@ -169,21 +169,21 @@ void HLE_LogFPrint(ParameterType parameter_type)
// 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));
@ -191,28 +191,30 @@ void HLE_LogFPrint(ParameterType parameter_type)
// 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;
}
}

View File

@ -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

View File

@ -8,20 +8,22 @@
HLE::SystemVABI::VAList::~VAList() = default;
u32 HLE::SystemVABI::VAList::GetGPR(u32 gpr) const
u32 HLE::SystemVABI::VAList::GetGPR(const Core::CPUThreadGuard&, u32 gpr) const
{
return m_system.GetPPCState().gpr[gpr];
}
double HLE::SystemVABI::VAList::GetFPR(u32 fpr) const
double HLE::SystemVABI::VAList::GetFPR(const Core::CPUThreadGuard&, u32 fpr) const
{
return m_system.GetPPCState().ps[fpr].PS0AsDouble();
}
HLE::SystemVABI::VAListStruct::VAListStruct(Core::System& system, u32 address)
: VAList(system, 0), m_va_list{PowerPC::HostRead_U8(address), PowerPC::HostRead_U8(address + 1),
PowerPC::HostRead_U32(address + 4),
PowerPC::HostRead_U32(address + 8)},
HLE::SystemVABI::VAListStruct::VAListStruct(Core::System& system, const Core::CPUThreadGuard& guard,
u32 address)
: VAList(system, 0), m_va_list{PowerPC::HostRead_U8(guard, address),
PowerPC::HostRead_U8(guard, address + 1),
PowerPC::HostRead_U32(guard, address + 4),
PowerPC::HostRead_U32(guard, address + 8)},
m_address(address), m_has_fpr_area(system.GetPPCState().cr.GetBit(6) == 1)
{
m_stack = m_va_list.overflow_arg_area;
@ -39,7 +41,7 @@ u32 HLE::SystemVABI::VAListStruct::GetFPRArea() const
return GetGPRArea() + 4 * 8;
}
u32 HLE::SystemVABI::VAListStruct::GetGPR(u32 gpr) const
u32 HLE::SystemVABI::VAListStruct::GetGPR(const Core::CPUThreadGuard& guard, u32 gpr) const
{
if (gpr < 3 || gpr > 10)
{
@ -47,10 +49,10 @@ u32 HLE::SystemVABI::VAListStruct::GetGPR(u32 gpr) const
return 0;
}
const u32 gpr_address = Common::AlignUp(GetGPRArea() + 4 * (gpr - 3), 4);
return PowerPC::HostRead_U32(gpr_address);
return PowerPC::HostRead_U32(guard, gpr_address);
}
double HLE::SystemVABI::VAListStruct::GetFPR(u32 fpr) const
double HLE::SystemVABI::VAListStruct::GetFPR(const Core::CPUThreadGuard& guard, u32 fpr) const
{
if (!m_has_fpr_area || fpr < 1 || fpr > 8)
{
@ -58,5 +60,5 @@ double HLE::SystemVABI::VAListStruct::GetFPR(u32 fpr) const
return 0.0;
}
const u32 fpr_address = Common::AlignUp(GetFPRArea() + 8 * (fpr - 1), 8);
return PowerPC::HostRead_F64(fpr_address);
return PowerPC::HostRead_F64(guard, fpr_address);
}

View File

@ -13,8 +13,9 @@
namespace Core
{
class CPUThreadGuard;
class System;
}
} // namespace Core
namespace HLE::SystemVABI
{
@ -47,14 +48,14 @@ public:
// 0 - arg_ARGPOINTER
template <typename T, typename std::enable_if_t<IS_ARG_POINTER<T>>* = nullptr>
T GetArg()
T GetArg(const Core::CPUThreadGuard& guard)
{
T obj;
u32 addr = GetArg<u32>();
u32 addr = GetArg<u32>(guard);
for (size_t i = 0; i < sizeof(T); i += 1, addr += 1)
{
reinterpret_cast<u8*>(&obj)[i] = PowerPC::HostRead_U8(addr);
reinterpret_cast<u8*>(&obj)[i] = PowerPC::HostRead_U8(guard, addr);
}
return obj;
@ -62,20 +63,20 @@ public:
// 1 - arg_WORD
template <typename T, typename std::enable_if_t<IS_WORD<T>>* = nullptr>
T GetArg()
T GetArg(const Core::CPUThreadGuard& guard)
{
static_assert(!std::is_pointer<T>(), "VAList doesn't support pointers");
u64 value;
if (m_gpr <= m_gpr_max)
{
value = GetGPR(m_gpr);
value = GetGPR(guard, m_gpr);
m_gpr += 1;
}
else
{
m_stack = Common::AlignUp(m_stack, 4);
value = PowerPC::HostRead_U32(m_stack);
value = PowerPC::HostRead_U32(guard, m_stack);
m_stack += 4;
}
@ -84,7 +85,7 @@ public:
// 2 - arg_DOUBLEWORD
template <typename T, typename std::enable_if_t<IS_DOUBLE_WORD<T>>* = nullptr>
T GetArg()
T GetArg(const Core::CPUThreadGuard& guard)
{
u64 value;
@ -92,13 +93,13 @@ public:
m_gpr += 1;
if (m_gpr < m_gpr_max)
{
value = static_cast<u64>(GetGPR(m_gpr)) << 32 | GetGPR(m_gpr + 1);
value = static_cast<u64>(GetGPR(guard, m_gpr)) << 32 | GetGPR(guard, m_gpr + 1);
m_gpr += 2;
}
else
{
m_stack = Common::AlignUp(m_stack, 8);
value = PowerPC::HostRead_U64(m_stack);
value = PowerPC::HostRead_U64(guard, m_stack);
m_stack += 8;
}
@ -107,19 +108,19 @@ public:
// 3 - arg_ARGREAL
template <typename T, typename std::enable_if_t<IS_ARG_REAL<T>>* = nullptr>
T GetArg()
T GetArg(const Core::CPUThreadGuard& guard)
{
double value;
if (m_fpr <= m_fpr_max)
{
value = GetFPR(m_fpr);
value = GetFPR(guard, m_fpr);
m_fpr += 1;
}
else
{
m_stack = Common::AlignUp(m_stack, 8);
value = PowerPC::HostRead_F64(m_stack);
value = PowerPC::HostRead_F64(guard, m_stack);
m_stack += 8;
}
@ -128,9 +129,9 @@ public:
// Helper
template <typename T>
T GetArgT()
T GetArgT(const Core::CPUThreadGuard& guard)
{
return static_cast<T>(GetArg<T>());
return static_cast<T>(GetArg<T>(guard));
}
protected:
@ -142,8 +143,8 @@ protected:
u32 m_stack;
private:
virtual u32 GetGPR(u32 gpr) const;
virtual double GetFPR(u32 fpr) const;
virtual u32 GetGPR(const Core::CPUThreadGuard& guard, u32 gpr) const;
virtual double GetFPR(const Core::CPUThreadGuard& guard, u32 fpr) const;
};
// See System V ABI (SVR4) for more details
@ -155,7 +156,7 @@ private:
class VAListStruct : public VAList
{
public:
explicit VAListStruct(Core::System& system, u32 address);
explicit VAListStruct(Core::System& system, const Core::CPUThreadGuard& guard, u32 address);
~VAListStruct() = default;
private:
@ -173,8 +174,8 @@ private:
u32 GetGPRArea() const;
u32 GetFPRArea() const;
u32 GetGPR(u32 gpr) const override;
double GetFPR(u32 fpr) const override;
u32 GetGPR(const Core::CPUThreadGuard& guard, u32 gpr) const override;
double GetFPR(const Core::CPUThreadGuard& guard, u32 fpr) const override;
};
} // namespace HLE::SystemVABI

View File

@ -14,48 +14,48 @@
namespace AddressSpace
{
u16 Accessors::ReadU16(u32 address) const
u16 Accessors::ReadU16(const Core::CPUThreadGuard& guard, u32 address) const
{
u32 result = ReadU8(address);
result = result << 8 | ReadU8(address + 1);
u32 result = ReadU8(guard, address);
result = result << 8 | ReadU8(guard, address + 1);
return result;
}
void Accessors::WriteU16(u32 address, u16 value)
void Accessors::WriteU16(const Core::CPUThreadGuard& guard, u32 address, u16 value)
{
WriteU8(address, value & 0xff);
WriteU8(address + 1, (value >> 8) & 0xff);
WriteU8(guard, address, value & 0xff);
WriteU8(guard, address + 1, (value >> 8) & 0xff);
}
u32 Accessors::ReadU32(u32 address) const
u32 Accessors::ReadU32(const Core::CPUThreadGuard& guard, u32 address) const
{
u32 result = ReadU16(address);
result = result << 16 | ReadU16(address + 2);
u32 result = ReadU16(guard, address);
result = result << 16 | ReadU16(guard, address + 2);
return result;
}
void Accessors::WriteU32(u32 address, u32 value)
void Accessors::WriteU32(const Core::CPUThreadGuard& guard, u32 address, u32 value)
{
WriteU16(address, value & 0xffff);
WriteU16(address + 2, (value >> 16) & 0xffff);
WriteU16(guard, address, value & 0xffff);
WriteU16(guard, address + 2, (value >> 16) & 0xffff);
}
u64 Accessors::ReadU64(u32 address) const
u64 Accessors::ReadU64(const Core::CPUThreadGuard& guard, u32 address) const
{
u64 result = ReadU32(address);
result = result << 32 | ReadU32(address + 4);
u64 result = ReadU32(guard, address);
result = result << 32 | ReadU32(guard, address + 4);
return result;
}
void Accessors::WriteU64(u32 address, u64 value)
void Accessors::WriteU64(const Core::CPUThreadGuard& guard, u32 address, u64 value)
{
WriteU32(address, value & 0xffffffff);
WriteU32(address + 4, (value >> 32) & 0xffffffff);
WriteU32(guard, address, value & 0xffffffff);
WriteU32(guard, address + 4, (value >> 32) & 0xffffffff);
}
float Accessors::ReadF32(u32 address) const
float Accessors::ReadF32(const Core::CPUThreadGuard& guard, u32 address) const
{
return Common::BitCast<float>(ReadU32(address));
return Common::BitCast<float>(ReadU32(guard, address));
}
Accessors::iterator Accessors::begin() const
@ -68,8 +68,9 @@ Accessors::iterator Accessors::end() const
return nullptr;
}
std::optional<u32> Accessors::Search(u32 haystack_start, const u8* needle_start,
std::size_t needle_size, bool forwards) const
std::optional<u32> Accessors::Search(const Core::CPUThreadGuard& guard, u32 haystack_start,
const u8* needle_start, std::size_t needle_size,
bool forwards) const
{
return std::nullopt;
}
@ -80,18 +81,49 @@ Accessors::~Accessors()
struct EffectiveAddressSpaceAccessors : Accessors
{
bool IsValidAddress(u32 address) const override { return PowerPC::HostIsRAMAddress(address); }
u8 ReadU8(u32 address) const override { return PowerPC::HostRead_U8(address); }
void WriteU8(u32 address, u8 value) override { PowerPC::HostWrite_U8(value, address); }
u16 ReadU16(u32 address) const override { return PowerPC::HostRead_U16(address); }
void WriteU16(u32 address, u16 value) override { PowerPC::HostWrite_U16(value, address); }
u32 ReadU32(u32 address) const override { return PowerPC::HostRead_U32(address); }
void WriteU32(u32 address, u32 value) override { PowerPC::HostWrite_U32(value, address); }
u64 ReadU64(u32 address) const override { return PowerPC::HostRead_U64(address); }
void WriteU64(u32 address, u64 value) override { PowerPC::HostWrite_U64(value, address); }
float ReadF32(u32 address) const override { return PowerPC::HostRead_F32(address); };
bool IsValidAddress(const Core::CPUThreadGuard& guard, u32 address) const override
{
return PowerPC::HostIsRAMAddress(guard, address);
}
u8 ReadU8(const Core::CPUThreadGuard& guard, u32 address) const override
{
return PowerPC::HostRead_U8(guard, address);
}
void WriteU8(const Core::CPUThreadGuard& guard, u32 address, u8 value) override
{
PowerPC::HostWrite_U8(guard, value, address);
}
u16 ReadU16(const Core::CPUThreadGuard& guard, u32 address) const override
{
return PowerPC::HostRead_U16(guard, address);
}
void WriteU16(const Core::CPUThreadGuard& guard, u32 address, u16 value) override
{
PowerPC::HostWrite_U16(guard, value, address);
}
u32 ReadU32(const Core::CPUThreadGuard& guard, u32 address) const override
{
return PowerPC::HostRead_U32(guard, address);
}
void WriteU32(const Core::CPUThreadGuard& guard, u32 address, u32 value) override
{
PowerPC::HostWrite_U32(guard, value, address);
}
u64 ReadU64(const Core::CPUThreadGuard& guard, u32 address) const override
{
return PowerPC::HostRead_U64(guard, address);
}
void WriteU64(const Core::CPUThreadGuard& guard, u32 address, u64 value) override
{
PowerPC::HostWrite_U64(guard, value, address);
}
float ReadF32(const Core::CPUThreadGuard& guard, u32 address) const override
{
return PowerPC::HostRead_F32(guard, address);
};
bool Matches(u32 haystack_start, const u8* needle_start, std::size_t needle_size) const
bool Matches(const Core::CPUThreadGuard& guard, u32 haystack_start, const u8* needle_start,
std::size_t needle_size) const
{
auto& system = Core::System::GetInstance();
auto& memory = system.GetMemory();
@ -100,7 +132,7 @@ struct EffectiveAddressSpaceAccessors : Accessors
u32 offset = haystack_start & 0x0000fff;
do
{
if (!PowerPC::HostIsRAMAddress(page_base))
if (!PowerPC::HostIsRAMAddress(guard, page_base))
{
return false;
}
@ -137,7 +169,8 @@ struct EffectiveAddressSpaceAccessors : Accessors
return (needle_size == 0);
}
std::optional<u32> Search(u32 haystack_start, const u8* needle_start, std::size_t needle_size,
std::optional<u32> Search(const Core::CPUThreadGuard& guard, u32 haystack_start,
const u8* needle_start, std::size_t needle_size,
bool forward) const override
{
u32 haystack_address = haystack_start;
@ -150,11 +183,11 @@ struct EffectiveAddressSpaceAccessors : Accessors
const u32 haystack_offset_change = forward ? 1 : -1;
do
{
if (PowerPC::HostIsRAMAddress(haystack_address))
if (PowerPC::HostIsRAMAddress(guard, haystack_address))
{
do
{
if (Matches(haystack_address, needle_start, needle_size))
if (Matches(guard, haystack_address, needle_start, needle_size))
{
return std::optional<u32>(haystack_address);
}
@ -173,17 +206,17 @@ struct EffectiveAddressSpaceAccessors : Accessors
struct AuxiliaryAddressSpaceAccessors : Accessors
{
static constexpr u32 aram_base_address = 0;
bool IsValidAddress(u32 address) const override
bool IsValidAddress(const Core::CPUThreadGuard& guard, u32 address) const override
{
return !SConfig::GetInstance().bWii && (address - aram_base_address) < GetSize();
}
u8 ReadU8(u32 address) const override
u8 ReadU8(const Core::CPUThreadGuard& guard, u32 address) const override
{
const u8* base = DSP::GetARAMPtr();
return base[address];
}
void WriteU8(u32 address, u8 value) override
void WriteU8(const Core::CPUThreadGuard& guard, u32 address, u8 value) override
{
u8* base = DSP::GetARAMPtr();
base[address] = value;
@ -193,10 +226,11 @@ struct AuxiliaryAddressSpaceAccessors : Accessors
iterator end() const override { return DSP::GetARAMPtr() + GetSize(); }
std::optional<u32> Search(u32 haystack_offset, const u8* needle_start, std::size_t needle_size,
std::optional<u32> Search(const Core::CPUThreadGuard& guard, u32 haystack_offset,
const u8* needle_start, std::size_t needle_size,
bool forward) const override
{
if (!IsValidAddress(haystack_offset))
if (!IsValidAddress(guard, haystack_offset))
{
return std::nullopt;
}
@ -243,42 +277,44 @@ struct CompositeAddressSpaceAccessors : Accessors
{
}
bool IsValidAddress(u32 address) const override
bool IsValidAddress(const Core::CPUThreadGuard& guard, u32 address) const override
{
return FindAppropriateAccessor(address) != m_accessor_mappings.end();
return FindAppropriateAccessor(guard, address) != m_accessor_mappings.end();
}
u8 ReadU8(u32 address) const override
u8 ReadU8(const Core::CPUThreadGuard& guard, u32 address) const override
{
auto mapping = FindAppropriateAccessor(address);
auto mapping = FindAppropriateAccessor(guard, address);
if (mapping == m_accessor_mappings.end())
{
return 0;
}
return mapping->accessors->ReadU8(address - mapping->base);
return mapping->accessors->ReadU8(guard, address - mapping->base);
}
void WriteU8(u32 address, u8 value) override
void WriteU8(const Core::CPUThreadGuard& guard, u32 address, u8 value) override
{
auto mapping = FindAppropriateAccessor(address);
auto mapping = FindAppropriateAccessor(guard, address);
if (mapping == m_accessor_mappings.end())
{
return;
}
return mapping->accessors->WriteU8(address - mapping->base, value);
return mapping->accessors->WriteU8(guard, address - mapping->base, value);
}
std::optional<u32> Search(u32 haystack_offset, const u8* needle_start, std::size_t needle_size,
std::optional<u32> Search(const Core::CPUThreadGuard& guard, u32 haystack_offset,
const u8* needle_start, std::size_t needle_size,
bool forward) const override
{
for (const AccessorMapping& mapping : m_accessor_mappings)
{
u32 mapping_offset = haystack_offset - mapping.base;
if (!mapping.accessors->IsValidAddress(mapping_offset))
if (!mapping.accessors->IsValidAddress(guard, mapping_offset))
{
continue;
}
auto result = mapping.accessors->Search(mapping_offset, needle_start, needle_size, forward);
auto result =
mapping.accessors->Search(guard, mapping_offset, needle_start, needle_size, forward);
if (result.has_value())
{
return std::optional<u32>(*result + mapping.base);
@ -289,18 +325,20 @@ struct CompositeAddressSpaceAccessors : Accessors
private:
std::vector<AccessorMapping> m_accessor_mappings;
std::vector<AccessorMapping>::iterator FindAppropriateAccessor(u32 address)
std::vector<AccessorMapping>::iterator FindAppropriateAccessor(const Core::CPUThreadGuard& guard,
u32 address)
{
return std::find_if(m_accessor_mappings.begin(), m_accessor_mappings.end(),
[address](const AccessorMapping& a) {
return a.accessors->IsValidAddress(address - a.base);
[&guard, address](const AccessorMapping& a) {
return a.accessors->IsValidAddress(guard, address - a.base);
});
}
std::vector<AccessorMapping>::const_iterator FindAppropriateAccessor(u32 address) const
std::vector<AccessorMapping>::const_iterator
FindAppropriateAccessor(const Core::CPUThreadGuard& guard, u32 address) const
{
return std::find_if(m_accessor_mappings.begin(), m_accessor_mappings.end(),
[address](const AccessorMapping& a) {
return a.accessors->IsValidAddress(address - a.base);
[&guard, address](const AccessorMapping& a) {
return a.accessors->IsValidAddress(guard, address - a.base);
});
}
};
@ -310,13 +348,19 @@ struct SmallBlockAccessors : Accessors
SmallBlockAccessors() = default;
SmallBlockAccessors(u8** alloc_base_, u32 size_) : alloc_base{alloc_base_}, size{size_} {}
bool IsValidAddress(u32 address) const override
bool IsValidAddress(const Core::CPUThreadGuard& guard, u32 address) const override
{
return (*alloc_base != nullptr) && (address < size);
}
u8 ReadU8(u32 address) const override { return (*alloc_base)[address]; }
u8 ReadU8(const Core::CPUThreadGuard& guard, u32 address) const override
{
return (*alloc_base)[address];
}
void WriteU8(u32 address, u8 value) override { (*alloc_base)[address] = value; }
void WriteU8(const Core::CPUThreadGuard& guard, u32 address, u8 value) override
{
(*alloc_base)[address] = value;
}
iterator begin() const override { return *alloc_base; }
@ -325,11 +369,12 @@ struct SmallBlockAccessors : Accessors
return (*alloc_base == nullptr) ? nullptr : (*alloc_base + size);
}
std::optional<u32> Search(u32 haystack_offset, const u8* needle_start, std::size_t needle_size,
std::optional<u32> Search(const Core::CPUThreadGuard& guard, u32 haystack_offset,
const u8* needle_start, std::size_t needle_size,
bool forward) const override
{
if (!IsValidAddress(haystack_offset) ||
!IsValidAddress(haystack_offset + static_cast<u32>(needle_size) - 1))
if (!IsValidAddress(guard, haystack_offset) ||
!IsValidAddress(guard, haystack_offset + static_cast<u32>(needle_size) - 1))
{
return std::nullopt;
}
@ -364,9 +409,12 @@ private:
struct NullAccessors : Accessors
{
bool IsValidAddress(u32 address) const override { return false; }
u8 ReadU8(u32 address) const override { return 0; }
void WriteU8(u32 address, u8 value) override {}
bool IsValidAddress(const Core::CPUThreadGuard& guard, u32 address) const override
{
return false;
}
u8 ReadU8(const Core::CPUThreadGuard& guard, u32 address) const override { return 0; }
void WriteU8(const Core::CPUThreadGuard& guard, u32 address, u8 value) override {}
};
static EffectiveAddressSpaceAccessors s_effective_address_space_accessors;

View File

@ -7,6 +7,11 @@
#include "Common/CommonTypes.h"
namespace Core
{
class CPUThreadGuard;
}
namespace AddressSpace
{
enum class Type
@ -23,24 +28,25 @@ struct Accessors
{
using iterator = const u8*;
virtual bool IsValidAddress(u32 address) const = 0;
virtual u8 ReadU8(u32 address) const = 0;
virtual void WriteU8(u32 address, u8 value) = 0;
virtual bool IsValidAddress(const Core::CPUThreadGuard& guard, u32 address) const = 0;
virtual u8 ReadU8(const Core::CPUThreadGuard& guard, u32 address) const = 0;
virtual void WriteU8(const Core::CPUThreadGuard& guard, u32 address, u8 value) = 0;
// overrideable naive implementations of below are defined
virtual u16 ReadU16(u32 address) const;
virtual void WriteU16(u32 address, u16 value);
virtual u32 ReadU32(u32 address) const;
virtual void WriteU32(u32 address, u32 value);
virtual u64 ReadU64(u32 address) const;
virtual void WriteU64(u32 address, u64 value);
virtual float ReadF32(u32 address) const;
virtual u16 ReadU16(const Core::CPUThreadGuard& guard, u32 address) const;
virtual void WriteU16(const Core::CPUThreadGuard& guard, u32 address, u16 value);
virtual u32 ReadU32(const Core::CPUThreadGuard& guard, u32 address) const;
virtual void WriteU32(const Core::CPUThreadGuard& guard, u32 address, u32 value);
virtual u64 ReadU64(const Core::CPUThreadGuard& guard, u32 address) const;
virtual void WriteU64(const Core::CPUThreadGuard& guard, u32 address, u64 value);
virtual float ReadF32(const Core::CPUThreadGuard& guard, u32 address) const;
virtual iterator begin() const;
virtual iterator end() const;
virtual std::optional<u32> Search(u32 haystack_offset, const u8* needle_start,
std::size_t needle_size, bool forward) const;
virtual std::optional<u32> Search(const Core::CPUThreadGuard& guard, u32 haystack_offset,
const u8* needle_start, std::size_t needle_size,
bool forward) const;
virtual ~Accessors();
};

View File

@ -17,6 +17,7 @@
#include "Common/CommonTypes.h"
#include "Common/Logging/Log.h"
#include "Common/Timer.h"
#include "Core/Boot/AncastTypes.h"
#include "Core/Boot/DolReader.h"
#include "Core/Boot/ElfReader.h"
@ -912,7 +913,10 @@ static void FinishPPCBootstrap(Core::System& system, u64 userdata, s64 cycles_la
else
ReleasePPC();
SConfig::OnNewTitleLoad();
ASSERT(Core::IsCPUThread());
Core::CPUThreadGuard guard;
SConfig::OnNewTitleLoad(guard);
INFO_LOG_FMT(IOS, "Bootstrapping done.");
}

View File

@ -6,11 +6,13 @@
#include <cstring>
#include <utility>
#include "Common/Assert.h"
#include "Common/CommonTypes.h"
#include "Common/FileUtil.h"
#include "Common/Logging/Log.h"
#include "Common/MsgHandler.h"
#include "Common/Swap.h"
#include "Core/Config/MainSettings.h"
#include "Core/ConfigManager.h"
#include "Core/Core.h"
@ -57,6 +59,10 @@ bool Load()
{
auto& system = Core::System::GetInstance();
auto& memory = system.GetMemory();
ASSERT(Core::IsCPUThread());
Core::CPUThreadGuard guard;
memory.Write_U32(0x00000000, ADDRESS_INIT_SEMAPHORE);
memory.Write_U32(0x09142001, 0x3180);
@ -69,7 +75,7 @@ bool Load()
g_symbolDB.Clear();
Host_NotifyMapLoaded();
}
if (g_symbolDB.LoadMap(File::GetUserPath(D_MAPS_IDX) + "mios-ipl.map"))
if (g_symbolDB.LoadMap(guard, File::GetUserPath(D_MAPS_IDX) + "mios-ipl.map"))
{
::HLE::Clear();
::HLE::PatchFunctions(system);
@ -93,7 +99,7 @@ bool Load()
NOTICE_LOG_FMT(IOS, "IPL ready.");
SConfig::GetInstance().m_is_mios = true;
DVDInterface::UpdateRunningGameMetadata();
SConfig::OnNewTitleLoad();
SConfig::OnNewTitleLoad(guard);
return true;
}
} // namespace IOS::HLE::MIOS

View File

@ -67,19 +67,19 @@ bool MemoryWatcher::OpenSocket(const std::string& path)
return m_fd >= 0;
}
u32 MemoryWatcher::ChasePointer(const std::string& line)
u32 MemoryWatcher::ChasePointer(const Core::CPUThreadGuard& guard, const std::string& line)
{
u32 value = 0;
for (u32 offset : m_addresses[line])
{
value = PowerPC::HostRead_U32(value + offset);
if (!PowerPC::HostIsRAMAddress(value))
value = PowerPC::HostRead_U32(guard, value + offset);
if (!PowerPC::HostIsRAMAddress(guard, value))
break;
}
return value;
}
std::string MemoryWatcher::ComposeMessages()
std::string MemoryWatcher::ComposeMessages(const Core::CPUThreadGuard& guard)
{
std::ostringstream message_stream;
message_stream << std::hex;
@ -89,7 +89,7 @@ std::string MemoryWatcher::ComposeMessages()
std::string address = entry.first;
u32& current_value = entry.second;
u32 new_value = ChasePointer(address);
u32 new_value = ChasePointer(guard, address);
if (new_value != current_value)
{
// Update the value
@ -101,12 +101,12 @@ std::string MemoryWatcher::ComposeMessages()
return message_stream.str();
}
void MemoryWatcher::Step()
void MemoryWatcher::Step(const Core::CPUThreadGuard& guard)
{
if (!m_running)
return;
std::string message = ComposeMessages();
std::string message = ComposeMessages(guard);
sendto(m_fd, message.c_str(), message.size() + 1, 0, reinterpret_cast<sockaddr*>(&m_addr),
sizeof(m_addr));
}

View File

@ -11,6 +11,11 @@
#include <sys/un.h>
#include <vector>
namespace Core
{
class CPUThreadGuard;
}
// MemoryWatcher reads a file containing in-game memory addresses and outputs
// changes to those memory addresses to a unix domain socket as the game runs.
//
@ -24,15 +29,15 @@ class MemoryWatcher final
public:
MemoryWatcher();
~MemoryWatcher();
void Step();
void Step(const Core::CPUThreadGuard& guard);
private:
bool LoadAddresses(const std::string& path);
bool OpenSocket(const std::string& path);
void ParseLine(const std::string& line);
u32 ChasePointer(const std::string& line);
std::string ComposeMessages();
u32 ChasePointer(const Core::CPUThreadGuard& guard, const std::string& line);
std::string ComposeMessages(const Core::CPUThreadGuard& guard);
bool m_running = false;

View File

@ -28,6 +28,7 @@
#include "Core/CheatCodes.h"
#include "Core/Config/SessionSettings.h"
#include "Core/ConfigManager.h"
#include "Core/Core.h"
#include "Core/Debugger/PPCDebugInterface.h"
#include "Core/GeckoCode.h"
#include "Core/GeckoCodeConfig.h"
@ -230,7 +231,7 @@ void LoadPatches()
LoadSpeedhacks("Speedhacks", merged);
}
static void ApplyPatches(const std::vector<Patch>& patches)
static void ApplyPatches(const Core::CPUThreadGuard& guard, const std::vector<Patch>& patches)
{
for (const Patch& patch : patches)
{
@ -244,16 +245,17 @@ static void ApplyPatches(const std::vector<Patch>& patches)
switch (entry.type)
{
case PatchType::Patch8Bit:
if (!entry.conditional || PowerPC::HostRead_U8(addr) == static_cast<u8>(comparand))
PowerPC::HostWrite_U8(static_cast<u8>(value), addr);
if (!entry.conditional || PowerPC::HostRead_U8(guard, addr) == static_cast<u8>(comparand))
PowerPC::HostWrite_U8(guard, static_cast<u8>(value), addr);
break;
case PatchType::Patch16Bit:
if (!entry.conditional || PowerPC::HostRead_U16(addr) == static_cast<u16>(comparand))
PowerPC::HostWrite_U16(static_cast<u16>(value), addr);
if (!entry.conditional ||
PowerPC::HostRead_U16(guard, addr) == static_cast<u16>(comparand))
PowerPC::HostWrite_U16(guard, static_cast<u16>(value), addr);
break;
case PatchType::Patch32Bit:
if (!entry.conditional || PowerPC::HostRead_U32(addr) == comparand)
PowerPC::HostWrite_U32(value, addr);
if (!entry.conditional || PowerPC::HostRead_U32(guard, addr) == comparand)
PowerPC::HostWrite_U32(guard, value, addr);
break;
default:
// unknown patchtype
@ -264,19 +266,20 @@ static void ApplyPatches(const std::vector<Patch>& patches)
}
}
static void ApplyMemoryPatches(std::span<const std::size_t> memory_patch_indices)
static void ApplyMemoryPatches(const Core::CPUThreadGuard& guard,
std::span<const std::size_t> memory_patch_indices)
{
std::lock_guard lock(s_on_frame_memory_mutex);
for (std::size_t index : memory_patch_indices)
{
PowerPC::debug_interface.ApplyExistingPatch(index);
PowerPC::debug_interface.ApplyExistingPatch(guard, index);
}
}
// Requires MSR.DR, MSR.IR
// There's no perfect way to do this, it's just a heuristic.
// We require at least 2 stack frames, if the stack is shallower than that then it won't work.
static bool IsStackSane()
static bool IsStackValid(const Core::CPUThreadGuard& guard)
{
auto& system = Core::System::GetInstance();
auto& ppc_state = system.GetPPCState();
@ -285,19 +288,19 @@ static bool IsStackSane()
// Check the stack pointer
u32 SP = ppc_state.gpr[1];
if (!PowerPC::HostIsRAMAddress(SP))
if (!PowerPC::HostIsRAMAddress(guard, SP))
return false;
// Read the frame pointer from the stack (find 2nd frame from top), assert that it makes sense
u32 next_SP = PowerPC::HostRead_U32(SP);
if (next_SP <= SP || !PowerPC::HostIsRAMAddress(next_SP) ||
!PowerPC::HostIsRAMAddress(next_SP + 4))
u32 next_SP = PowerPC::HostRead_U32(guard, SP);
if (next_SP <= SP || !PowerPC::HostIsRAMAddress(guard, next_SP) ||
!PowerPC::HostIsRAMAddress(guard, next_SP + 4))
return false;
// Check the link register makes sense (that it points to a valid IBAT address)
const u32 address = PowerPC::HostRead_U32(next_SP + 4);
return PowerPC::HostIsInstructionRAMAddress(address) &&
0 != PowerPC::HostRead_Instruction(address);
const u32 address = PowerPC::HostRead_U32(guard, next_SP + 4);
return PowerPC::HostIsInstructionRAMAddress(guard, address) &&
0 != PowerPC::HostRead_Instruction(guard, address);
}
void AddMemoryPatch(std::size_t index)
@ -318,11 +321,14 @@ bool ApplyFramePatches()
auto& system = Core::System::GetInstance();
auto& ppc_state = system.GetPPCState();
ASSERT(Core::IsCPUThread());
Core::CPUThreadGuard guard;
// Because we're using the VI Interrupt to time this instead of patching the game with a
// callback hook we can end up catching the game in an exception vector.
// We deal with this by returning false so that SystemTimers will reschedule us in a few cycles
// where we can try again after the CPU hopefully returns back to the normal instruction flow.
if (!ppc_state.msr.DR || !ppc_state.msr.IR || !IsStackSane())
if (!ppc_state.msr.DR || !ppc_state.msr.IR || !IsStackValid(guard))
{
DEBUG_LOG_FMT(ACTIONREPLAY,
"Need to retry later. CPU configuration is currently incorrect. PC = {:#010x}, "
@ -331,12 +337,12 @@ bool ApplyFramePatches()
return false;
}
ApplyPatches(s_on_frame);
ApplyMemoryPatches(s_on_frame_memory);
ApplyPatches(guard, s_on_frame);
ApplyMemoryPatches(guard, s_on_frame_memory);
// Run the Gecko code handler
Gecko::RunCodeHandler();
ActionReplay::RunAllActive();
Gecko::RunCodeHandler(guard);
ActionReplay::RunAllActive(guard);
return true;
}

View File

@ -23,57 +23,57 @@
#include "Core/System.h"
template <typename T>
static T HostRead(u32 address);
static T HostRead(const Core::CPUThreadGuard& guard, u32 address);
template <typename T>
static void HostWrite(T var, u32 address);
static void HostWrite(const Core::CPUThreadGuard& guard, T var, u32 address);
template <>
u8 HostRead(u32 address)
u8 HostRead(const Core::CPUThreadGuard& guard, u32 address)
{
return PowerPC::HostRead_U8(address);
return PowerPC::HostRead_U8(guard, address);
}
template <>
u16 HostRead(u32 address)
u16 HostRead(const Core::CPUThreadGuard& guard, u32 address)
{
return PowerPC::HostRead_U16(address);
return PowerPC::HostRead_U16(guard, address);
}
template <>
u32 HostRead(u32 address)
u32 HostRead(const Core::CPUThreadGuard& guard, u32 address)
{
return PowerPC::HostRead_U32(address);
return PowerPC::HostRead_U32(guard, address);
}
template <>
u64 HostRead(u32 address)
u64 HostRead(const Core::CPUThreadGuard& guard, u32 address)
{
return PowerPC::HostRead_U64(address);
return PowerPC::HostRead_U64(guard, address);
}
template <>
void HostWrite(u8 var, u32 address)
void HostWrite(const Core::CPUThreadGuard& guard, u8 var, u32 address)
{
PowerPC::HostWrite_U8(var, address);
PowerPC::HostWrite_U8(guard, var, address);
}
template <>
void HostWrite(u16 var, u32 address)
void HostWrite(const Core::CPUThreadGuard& guard, u16 var, u32 address)
{
PowerPC::HostWrite_U16(var, address);
PowerPC::HostWrite_U16(guard, var, address);
}
template <>
void HostWrite(u32 var, u32 address)
void HostWrite(const Core::CPUThreadGuard& guard, u32 var, u32 address)
{
PowerPC::HostWrite_U32(var, address);
PowerPC::HostWrite_U32(guard, var, address);
}
template <>
void HostWrite(u64 var, u32 address)
void HostWrite(const Core::CPUThreadGuard& guard, u64 var, u32 address)
{
PowerPC::HostWrite_U64(var, address);
PowerPC::HostWrite_U64(guard, var, address);
}
template <typename T, typename U = T>
@ -81,8 +81,9 @@ static double HostReadFunc(expr_func* f, vec_expr_t* args, void* c)
{
if (vec_len(args) != 1)
return 0;
const auto* guard = reinterpret_cast<const Core::CPUThreadGuard*>(c);
const u32 address = static_cast<u32>(expr_eval(&vec_nth(args, 0)));
return Common::BitCast<T>(HostRead<U>(address));
return Common::BitCast<T>(HostRead<U>(*guard, address));
}
template <typename T, typename U = T>
@ -90,9 +91,10 @@ static double HostWriteFunc(expr_func* f, vec_expr_t* args, void* c)
{
if (vec_len(args) != 2)
return 0;
const auto* guard = reinterpret_cast<const Core::CPUThreadGuard*>(c);
const T var = static_cast<T>(expr_eval(&vec_nth(args, 0)));
const u32 address = static_cast<u32>(expr_eval(&vec_nth(args, 1)));
HostWrite<U>(Common::BitCast<U>(var), address);
HostWrite<U>(*guard, Common::BitCast<U>(var), address);
return var;
}
@ -110,7 +112,8 @@ static double CallstackFunc(expr_func* f, vec_expr_t* args, void* c)
return 0;
std::vector<Dolphin_Debugger::CallstackEntry> stack;
bool success = Dolphin_Debugger::GetCallstack(Core::System::GetInstance(), stack);
const auto* guard = reinterpret_cast<const Core::CPUThreadGuard*>(c);
bool success = Dolphin_Debugger::GetCallstack(Core::System::GetInstance(), *guard, stack);
if (!success)
return 0;
@ -225,7 +228,13 @@ double Expression::Evaluate() const
{
SynchronizeBindings(SynchronizeDirection::From);
double result = expr_eval(m_expr.get());
double result;
{
Core::CPUThreadGuard guard;
m_expr->param.func.context = &guard;
result = expr_eval(m_expr.get());
m_expr->param.func.context = nullptr;
}
SynchronizeBindings(SynchronizeDirection::To);

View File

@ -12,6 +12,11 @@
struct expr;
struct expr_var_list;
namespace Core
{
class CPUThreadGuard;
}
struct ExprDeleter
{
void operator()(expr* expression) const;

View File

@ -25,6 +25,7 @@ typedef SSIZE_T ssize_t;
#include <unistd.h>
#endif
#include "Common/Assert.h"
#include "Common/Event.h"
#include "Common/Logging/Log.h"
#include "Common/SocketContext.h"
@ -799,7 +800,7 @@ static void WriteRegister()
SendReply("OK");
}
static void ReadMemory()
static void ReadMemory(const Core::CPUThreadGuard& guard)
{
static u8 reply[GDB_BFR_MAX - 4];
u32 addr, len;
@ -819,7 +820,7 @@ static void ReadMemory()
if (len * 2 > sizeof reply)
SendReply("E01");
if (!PowerPC::HostIsRAMAddress(addr))
if (!PowerPC::HostIsRAMAddress(guard, addr))
return SendReply("E00");
auto& system = Core::System::GetInstance();
@ -830,7 +831,7 @@ static void ReadMemory()
SendReply((char*)reply);
}
static void WriteMemory()
static void WriteMemory(const Core::CPUThreadGuard& guard)
{
u32 addr, len;
u32 i;
@ -846,7 +847,7 @@ static void WriteMemory()
len = (len << 4) | Hex2char(s_cmd_bfr[i++]);
INFO_LOG_FMT(GDB_STUB, "gdb: write memory: {:08x} bytes to {:08x}", len, addr);
if (!PowerPC::HostIsRAMAddress(addr))
if (!PowerPC::HostIsRAMAddress(guard, addr))
return SendReply("E00");
auto& system = Core::System::GetInstance();
@ -990,11 +991,19 @@ void ProcessCommands(bool loop_until_continue)
WriteRegister();
break;
case 'm':
ReadMemory();
{
ASSERT(Core::IsCPUThread());
Core::CPUThreadGuard guard;
ReadMemory(guard);
break;
}
case 'M':
{
WriteMemory();
ASSERT(Core::IsCPUThread());
Core::CPUThreadGuard guard;
WriteMemory(guard);
auto& system = Core::System::GetInstance();
auto& ppc_state = system.GetPPCState();
ppc_state.iCache.Reset();

View File

@ -14,6 +14,7 @@
#include "Common/Logging/Log.h"
#include "Common/StringUtil.h"
#include "Core/Config/MainSettings.h"
#include "Core/Core.h"
#include "Core/CoreTiming.h"
#include "Core/Debugger/Debugger_SymbolMap.h"
#include "Core/HLE/HLE.h"
@ -339,11 +340,14 @@ void Interpreter::Run()
void Interpreter::unknown_instruction(UGeckoInstruction inst)
{
const u32 opcode = PowerPC::HostRead_U32(last_pc);
ASSERT(Core::IsCPUThread());
Core::CPUThreadGuard guard;
const u32 opcode = PowerPC::HostRead_U32(guard, last_pc);
const std::string disasm = Common::GekkoDisassembler::Disassemble(opcode, last_pc);
NOTICE_LOG_FMT(POWERPC, "Last PC = {:08x} : {}", last_pc, disasm);
Dolphin_Debugger::PrintCallstack(Core::System::GetInstance(), Common::Log::LogType::POWERPC,
Common::Log::LogLevel::LNOTICE);
Dolphin_Debugger::PrintCallstack(Core::System::GetInstance(), guard,
Common::Log::LogType::POWERPC, Common::Log::LogLevel::LNOTICE);
NOTICE_LOG_FMT(
POWERPC,
"\nIntCPU: Unknown instruction {:08x} at PC = {:08x} last_PC = {:08x} LR = {:08x}\n",

View File

@ -6,6 +6,7 @@
#include "Common/Assert.h"
#include "Common/CommonTypes.h"
#include "Core/ConfigManager.h"
#include "Core/Core.h"
#include "Core/HLE/HLE.h"
#include "Core/PowerPC/Interpreter/ExceptionUtils.h"
#include "Core/PowerPC/PowerPC.h"
@ -95,7 +96,11 @@ void Interpreter::bclrx(UGeckoInstruction inst)
void Interpreter::HLEFunction(UGeckoInstruction inst)
{
m_end_block = true;
HLE::Execute(PowerPC::ppcState.pc, inst.hex);
ASSERT(Core::IsCPUThread());
Core::CPUThreadGuard guard;
HLE::Execute(guard, PowerPC::ppcState.pc, inst.hex);
}
void Interpreter::rfi(UGeckoInstruction inst)

View File

@ -16,6 +16,7 @@
#include <fmt/format.h>
#include "Common/Assert.h"
#include "Common/ChunkFile.h"
#include "Common/CommonTypes.h"
#include "Common/IOFile.h"
@ -271,8 +272,12 @@ void CompileExceptionCheck(ExceptionType type)
{
if (type == ExceptionType::FIFOWrite)
{
ASSERT(Core::IsCPUThread());
Core::CPUThreadGuard guard;
// Check in case the code has been replaced since: do we need to do this?
const OpType optype = PPCTables::GetOpInfo(PowerPC::HostRead_U32(PowerPC::ppcState.pc))->type;
const OpType optype =
PPCTables::GetOpInfo(PowerPC::HostRead_U32(guard, PowerPC::ppcState.pc))->type;
if (optype != OpType::Store && optype != OpType::StoreFP && optype != OpType::StorePS)
return;
}

View File

@ -519,17 +519,18 @@ TryReadInstResult TryReadInstruction(u32 address)
return TryReadInstResult{true, from_bat, hex, address};
}
u32 HostRead_Instruction(const u32 address)
u32 HostRead_Instruction(const Core::CPUThreadGuard& guard, const u32 address)
{
auto& system = Core::System::GetInstance();
auto& memory = system.GetMemory();
return ReadFromHardware<XCheckTLBFlag::OpcodeNoException, u32>(memory, address);
}
std::optional<ReadResult<u32>> HostTryReadInstruction(const u32 address,
std::optional<ReadResult<u32>> HostTryReadInstruction(const Core::CPUThreadGuard& guard,
const u32 address,
RequestedAddressSpace space)
{
if (!HostIsInstructionRAMAddress(address, space))
if (!HostIsInstructionRAMAddress(guard, address, space))
return std::nullopt;
auto& system = Core::System::GetInstance();
@ -648,9 +649,10 @@ float Read_F32(const u32 address)
}
template <typename T>
static std::optional<ReadResult<T>> HostTryReadUX(const u32 address, RequestedAddressSpace space)
static std::optional<ReadResult<T>> HostTryReadUX(const Core::CPUThreadGuard& guard,
const u32 address, RequestedAddressSpace space)
{
if (!HostIsRAMAddress(address, space))
if (!HostIsRAMAddress(guard, address, space))
return std::nullopt;
auto& system = Core::System::GetInstance();
@ -681,37 +683,43 @@ static std::optional<ReadResult<T>> HostTryReadUX(const u32 address, RequestedAd
return std::nullopt;
}
std::optional<ReadResult<u8>> HostTryReadU8(u32 address, RequestedAddressSpace space)
std::optional<ReadResult<u8>> HostTryReadU8(const Core::CPUThreadGuard& guard, u32 address,
RequestedAddressSpace space)
{
return HostTryReadUX<u8>(address, space);
return HostTryReadUX<u8>(guard, address, space);
}
std::optional<ReadResult<u16>> HostTryReadU16(u32 address, RequestedAddressSpace space)
std::optional<ReadResult<u16>> HostTryReadU16(const Core::CPUThreadGuard& guard, u32 address,
RequestedAddressSpace space)
{
return HostTryReadUX<u16>(address, space);
return HostTryReadUX<u16>(guard, address, space);
}
std::optional<ReadResult<u32>> HostTryReadU32(u32 address, RequestedAddressSpace space)
std::optional<ReadResult<u32>> HostTryReadU32(const Core::CPUThreadGuard& guard, u32 address,
RequestedAddressSpace space)
{
return HostTryReadUX<u32>(address, space);
return HostTryReadUX<u32>(guard, address, space);
}
std::optional<ReadResult<u64>> HostTryReadU64(u32 address, RequestedAddressSpace space)
std::optional<ReadResult<u64>> HostTryReadU64(const Core::CPUThreadGuard& guard, u32 address,
RequestedAddressSpace space)
{
return HostTryReadUX<u64>(address, space);
return HostTryReadUX<u64>(guard, address, space);
}
std::optional<ReadResult<float>> HostTryReadF32(u32 address, RequestedAddressSpace space)
std::optional<ReadResult<float>> HostTryReadF32(const Core::CPUThreadGuard& guard, u32 address,
RequestedAddressSpace space)
{
const auto result = HostTryReadUX<u32>(address, space);
const auto result = HostTryReadUX<u32>(guard, address, space);
if (!result)
return std::nullopt;
return ReadResult<float>(result->translated, Common::BitCast<float>(result->value));
}
std::optional<ReadResult<double>> HostTryReadF64(u32 address, RequestedAddressSpace space)
std::optional<ReadResult<double>> HostTryReadF64(const Core::CPUThreadGuard& guard, u32 address,
RequestedAddressSpace space)
{
const auto result = HostTryReadUX<u64>(address, space);
const auto result = HostTryReadUX<u64>(guard, address, space);
if (!result)
return std::nullopt;
return ReadResult<double>(result->translated, Common::BitCast<double>(result->value));
@ -780,70 +788,70 @@ void Write_F64(const double var, const u32 address)
Write_U64(integral, address);
}
u8 HostRead_U8(const u32 address)
u8 HostRead_U8(const Core::CPUThreadGuard& guard, const u32 address)
{
auto& system = Core::System::GetInstance();
auto& memory = system.GetMemory();
return ReadFromHardware<XCheckTLBFlag::NoException, u8>(memory, address);
}
u16 HostRead_U16(const u32 address)
u16 HostRead_U16(const Core::CPUThreadGuard& guard, const u32 address)
{
auto& system = Core::System::GetInstance();
auto& memory = system.GetMemory();
return ReadFromHardware<XCheckTLBFlag::NoException, u16>(memory, address);
}
u32 HostRead_U32(const u32 address)
u32 HostRead_U32(const Core::CPUThreadGuard& guard, const u32 address)
{
auto& system = Core::System::GetInstance();
auto& memory = system.GetMemory();
return ReadFromHardware<XCheckTLBFlag::NoException, u32>(memory, address);
}
u64 HostRead_U64(const u32 address)
u64 HostRead_U64(const Core::CPUThreadGuard& guard, const u32 address)
{
auto& system = Core::System::GetInstance();
auto& memory = system.GetMemory();
return ReadFromHardware<XCheckTLBFlag::NoException, u64>(memory, address);
}
float HostRead_F32(const u32 address)
float HostRead_F32(const Core::CPUThreadGuard& guard, const u32 address)
{
const u32 integral = HostRead_U32(address);
const u32 integral = HostRead_U32(guard, address);
return Common::BitCast<float>(integral);
}
double HostRead_F64(const u32 address)
double HostRead_F64(const Core::CPUThreadGuard& guard, const u32 address)
{
const u64 integral = HostRead_U64(address);
const u64 integral = HostRead_U64(guard, address);
return Common::BitCast<double>(integral);
}
void HostWrite_U8(const u32 var, const u32 address)
void HostWrite_U8(const Core::CPUThreadGuard& guard, const u32 var, const u32 address)
{
auto& system = Core::System::GetInstance();
auto& memory = system.GetMemory();
WriteToHardware<XCheckTLBFlag::NoException>(system, memory, address, var, 1);
}
void HostWrite_U16(const u32 var, const u32 address)
void HostWrite_U16(const Core::CPUThreadGuard& guard, const u32 var, const u32 address)
{
auto& system = Core::System::GetInstance();
auto& memory = system.GetMemory();
WriteToHardware<XCheckTLBFlag::NoException>(system, memory, address, var, 2);
}
void HostWrite_U32(const u32 var, const u32 address)
void HostWrite_U32(const Core::CPUThreadGuard& guard, const u32 var, const u32 address)
{
auto& system = Core::System::GetInstance();
auto& memory = system.GetMemory();
WriteToHardware<XCheckTLBFlag::NoException>(system, memory, address, var, 4);
}
void HostWrite_U64(const u64 var, const u32 address)
void HostWrite_U64(const Core::CPUThreadGuard& guard, const u64 var, const u32 address)
{
auto& system = Core::System::GetInstance();
auto& memory = system.GetMemory();
@ -853,24 +861,25 @@ void HostWrite_U64(const u64 var, const u32 address)
static_cast<u32>(var), 4);
}
void HostWrite_F32(const float var, const u32 address)
void HostWrite_F32(const Core::CPUThreadGuard& guard, const float var, const u32 address)
{
const u32 integral = Common::BitCast<u32>(var);
HostWrite_U32(integral, address);
HostWrite_U32(guard, integral, address);
}
void HostWrite_F64(const double var, const u32 address)
void HostWrite_F64(const Core::CPUThreadGuard& guard, const double var, const u32 address)
{
const u64 integral = Common::BitCast<u64>(var);
HostWrite_U64(integral, address);
HostWrite_U64(guard, integral, address);
}
static std::optional<WriteResult> HostTryWriteUX(const u32 var, const u32 address, const u32 size,
static std::optional<WriteResult> HostTryWriteUX(const Core::CPUThreadGuard& guard, const u32 var,
const u32 address, const u32 size,
RequestedAddressSpace space)
{
if (!HostIsRAMAddress(address, space))
if (!HostIsRAMAddress(guard, address, space))
return std::nullopt;
auto& system = Core::System::GetInstance();
@ -895,56 +904,56 @@ static std::optional<WriteResult> HostTryWriteUX(const u32 var, const u32 addres
return std::nullopt;
}
std::optional<WriteResult> HostTryWriteU8(const u32 var, const u32 address,
RequestedAddressSpace space)
std::optional<WriteResult> HostTryWriteU8(const Core::CPUThreadGuard& guard, const u32 var,
const u32 address, RequestedAddressSpace space)
{
return HostTryWriteUX(var, address, 1, space);
return HostTryWriteUX(guard, var, address, 1, space);
}
std::optional<WriteResult> HostTryWriteU16(const u32 var, const u32 address,
RequestedAddressSpace space)
std::optional<WriteResult> HostTryWriteU16(const Core::CPUThreadGuard& guard, const u32 var,
const u32 address, RequestedAddressSpace space)
{
return HostTryWriteUX(var, address, 2, space);
return HostTryWriteUX(guard, var, address, 2, space);
}
std::optional<WriteResult> HostTryWriteU32(const u32 var, const u32 address,
RequestedAddressSpace space)
std::optional<WriteResult> HostTryWriteU32(const Core::CPUThreadGuard& guard, const u32 var,
const u32 address, RequestedAddressSpace space)
{
return HostTryWriteUX(var, address, 4, space);
return HostTryWriteUX(guard, var, address, 4, space);
}
std::optional<WriteResult> HostTryWriteU64(const u64 var, const u32 address,
RequestedAddressSpace space)
std::optional<WriteResult> HostTryWriteU64(const Core::CPUThreadGuard& guard, const u64 var,
const u32 address, RequestedAddressSpace space)
{
const auto result = HostTryWriteUX(static_cast<u32>(var >> 32), address, 4, space);
const auto result = HostTryWriteUX(guard, static_cast<u32>(var >> 32), address, 4, space);
if (!result)
return result;
return HostTryWriteUX(static_cast<u32>(var), address + 4, 4, space);
return HostTryWriteUX(guard, static_cast<u32>(var), address + 4, 4, space);
}
std::optional<WriteResult> HostTryWriteF32(const float var, const u32 address,
RequestedAddressSpace space)
std::optional<WriteResult> HostTryWriteF32(const Core::CPUThreadGuard& guard, const float var,
const u32 address, RequestedAddressSpace space)
{
const u32 integral = Common::BitCast<u32>(var);
return HostTryWriteU32(integral, address, space);
return HostTryWriteU32(guard, integral, address, space);
}
std::optional<WriteResult> HostTryWriteF64(const double var, const u32 address,
RequestedAddressSpace space)
std::optional<WriteResult> HostTryWriteF64(const Core::CPUThreadGuard& guard, const double var,
const u32 address, RequestedAddressSpace space)
{
const u64 integral = Common::BitCast<u64>(var);
return HostTryWriteU64(integral, address, space);
return HostTryWriteU64(guard, integral, address, space);
}
std::string HostGetString(u32 address, size_t size)
std::string HostGetString(const Core::CPUThreadGuard& guard, u32 address, size_t size)
{
std::string s;
do
{
if (!HostIsRAMAddress(address))
if (!HostIsRAMAddress(guard, address))
break;
u8 res = HostRead_U8(address);
u8 res = HostRead_U8(guard, address);
if (!res)
break;
s += static_cast<char>(res);
@ -953,10 +962,11 @@ std::string HostGetString(u32 address, size_t size)
return s;
}
std::optional<ReadResult<std::string>> HostTryReadString(u32 address, size_t size,
std::optional<ReadResult<std::string>> HostTryReadString(const Core::CPUThreadGuard& guard,
u32 address, size_t size,
RequestedAddressSpace space)
{
auto c = HostTryReadU8(address, space);
auto c = HostTryReadU8(guard, address, space);
if (!c)
return std::nullopt;
if (c->value == 0)
@ -967,7 +977,7 @@ std::optional<ReadResult<std::string>> HostTryReadString(u32 address, size_t siz
while (size == 0 || s.length() < size)
{
++address;
const auto res = HostTryReadU8(address, space);
const auto res = HostTryReadU8(guard, address, space);
if (!res || res->value == 0)
break;
s += static_cast<char>(res->value);
@ -1024,7 +1034,7 @@ static bool IsRAMAddress(Memory::MemoryManager& memory, u32 address, bool transl
return false;
}
bool HostIsRAMAddress(u32 address, RequestedAddressSpace space)
bool HostIsRAMAddress(const Core::CPUThreadGuard& guard, u32 address, RequestedAddressSpace space)
{
auto& system = Core::System::GetInstance();
auto& memory = system.GetMemory();
@ -1045,7 +1055,8 @@ bool HostIsRAMAddress(u32 address, RequestedAddressSpace space)
return false;
}
bool HostIsInstructionRAMAddress(u32 address, RequestedAddressSpace space)
bool HostIsInstructionRAMAddress(const Core::CPUThreadGuard& guard, u32 address,
RequestedAddressSpace space)
{
// Instructions are always 32bit aligned.
if (address & 3)

View File

@ -10,6 +10,11 @@
#include "Common/CommonTypes.h"
namespace Core
{
class CPUThreadGuard;
};
namespace PowerPC
{
// Routines for debugger UI, cheats, etc. to access emulated memory from the
@ -27,14 +32,14 @@ enum class RequestedAddressSpace
// If the read fails (eg. address does not correspond to a mapped address in the current address
// space), a PanicAlert will be shown to the user and zero (or an empty string for the string case)
// will be returned.
u8 HostRead_U8(u32 address);
u16 HostRead_U16(u32 address);
u32 HostRead_U32(u32 address);
u64 HostRead_U64(u32 address);
float HostRead_F32(u32 address);
double HostRead_F64(u32 address);
u32 HostRead_Instruction(u32 address);
std::string HostGetString(u32 address, size_t size = 0);
u8 HostRead_U8(const Core::CPUThreadGuard& guard, u32 address);
u16 HostRead_U16(const Core::CPUThreadGuard& guard, u32 address);
u32 HostRead_U32(const Core::CPUThreadGuard& guard, u32 address);
u64 HostRead_U64(const Core::CPUThreadGuard& guard, u32 address);
float HostRead_F32(const Core::CPUThreadGuard& guard, u32 address);
double HostRead_F64(const Core::CPUThreadGuard& guard, u32 address);
u32 HostRead_Instruction(const Core::CPUThreadGuard& guard, u32 address);
std::string HostGetString(const Core::CPUThreadGuard& guard, u32 address, size_t size = 0);
template <typename T>
struct ReadResult
@ -57,32 +62,39 @@ struct ReadResult
// value and information on whether the given address had to be translated or not. Unlike the
// HostRead functions, this does not raise a user-visible alert on failure.
std::optional<ReadResult<u8>>
HostTryReadU8(u32 address, RequestedAddressSpace space = RequestedAddressSpace::Effective);
HostTryReadU8(const Core::CPUThreadGuard& guard, u32 address,
RequestedAddressSpace space = RequestedAddressSpace::Effective);
std::optional<ReadResult<u16>>
HostTryReadU16(u32 address, RequestedAddressSpace space = RequestedAddressSpace::Effective);
HostTryReadU16(const Core::CPUThreadGuard& guard, u32 address,
RequestedAddressSpace space = RequestedAddressSpace::Effective);
std::optional<ReadResult<u32>>
HostTryReadU32(u32 address, RequestedAddressSpace space = RequestedAddressSpace::Effective);
HostTryReadU32(const Core::CPUThreadGuard& guard, u32 address,
RequestedAddressSpace space = RequestedAddressSpace::Effective);
std::optional<ReadResult<u64>>
HostTryReadU64(u32 address, RequestedAddressSpace space = RequestedAddressSpace::Effective);
HostTryReadU64(const Core::CPUThreadGuard& guard, u32 address,
RequestedAddressSpace space = RequestedAddressSpace::Effective);
std::optional<ReadResult<float>>
HostTryReadF32(u32 address, RequestedAddressSpace space = RequestedAddressSpace::Effective);
HostTryReadF32(const Core::CPUThreadGuard& guard, u32 address,
RequestedAddressSpace space = RequestedAddressSpace::Effective);
std::optional<ReadResult<double>>
HostTryReadF64(u32 address, RequestedAddressSpace space = RequestedAddressSpace::Effective);
HostTryReadF64(const Core::CPUThreadGuard& guard, u32 address,
RequestedAddressSpace space = RequestedAddressSpace::Effective);
std::optional<ReadResult<u32>>
HostTryReadInstruction(u32 address, RequestedAddressSpace space = RequestedAddressSpace::Effective);
HostTryReadInstruction(const Core::CPUThreadGuard& guard, u32 address,
RequestedAddressSpace space = RequestedAddressSpace::Effective);
std::optional<ReadResult<std::string>>
HostTryReadString(u32 address, size_t size = 0,
HostTryReadString(const Core::CPUThreadGuard& guard, u32 address, size_t size = 0,
RequestedAddressSpace space = RequestedAddressSpace::Effective);
// Writes a value to emulated memory using the currently active MMU settings.
// If the write fails (eg. address does not correspond to a mapped address in the current address
// space), a PanicAlert will be shown to the user.
void HostWrite_U8(u32 var, u32 address);
void HostWrite_U16(u32 var, u32 address);
void HostWrite_U32(u32 var, u32 address);
void HostWrite_U64(u64 var, u32 address);
void HostWrite_F32(float var, u32 address);
void HostWrite_F64(double var, u32 address);
void HostWrite_U8(const Core::CPUThreadGuard& guard, u32 var, u32 address);
void HostWrite_U16(const Core::CPUThreadGuard& guard, u32 var, u32 address);
void HostWrite_U32(const Core::CPUThreadGuard& guard, u32 var, u32 address);
void HostWrite_U64(const Core::CPUThreadGuard& guard, u64 var, u32 address);
void HostWrite_F32(const Core::CPUThreadGuard& guard, float var, u32 address);
void HostWrite_F64(const Core::CPUThreadGuard& guard, double var, u32 address);
struct WriteResult
{
@ -98,30 +110,31 @@ struct WriteResult
// address had to be translated or not. Unlike the HostWrite functions, this does not raise a
// user-visible alert on failure.
std::optional<WriteResult>
HostTryWriteU8(u32 var, const u32 address,
HostTryWriteU8(const Core::CPUThreadGuard& guard, u32 var, const u32 address,
RequestedAddressSpace space = RequestedAddressSpace::Effective);
std::optional<WriteResult>
HostTryWriteU16(u32 var, const u32 address,
HostTryWriteU16(const Core::CPUThreadGuard& guard, u32 var, const u32 address,
RequestedAddressSpace space = RequestedAddressSpace::Effective);
std::optional<WriteResult>
HostTryWriteU32(u32 var, const u32 address,
HostTryWriteU32(const Core::CPUThreadGuard& guard, u32 var, const u32 address,
RequestedAddressSpace space = RequestedAddressSpace::Effective);
std::optional<WriteResult>
HostTryWriteU64(u64 var, const u32 address,
HostTryWriteU64(const Core::CPUThreadGuard& guard, u64 var, const u32 address,
RequestedAddressSpace space = RequestedAddressSpace::Effective);
std::optional<WriteResult>
HostTryWriteF32(float var, const u32 address,
HostTryWriteF32(const Core::CPUThreadGuard& guard, float var, const u32 address,
RequestedAddressSpace space = RequestedAddressSpace::Effective);
std::optional<WriteResult>
HostTryWriteF64(double var, const u32 address,
HostTryWriteF64(const Core::CPUThreadGuard& guard, double var, const u32 address,
RequestedAddressSpace space = RequestedAddressSpace::Effective);
// Returns whether a read or write to the given address will resolve to a RAM access in the given
// address space.
bool HostIsRAMAddress(u32 address, RequestedAddressSpace space = RequestedAddressSpace::Effective);
bool HostIsRAMAddress(const Core::CPUThreadGuard& guard, u32 address,
RequestedAddressSpace space = RequestedAddressSpace::Effective);
// Same as HostIsRAMAddress, but uses IBAT instead of DBAT.
bool HostIsInstructionRAMAddress(u32 address,
bool HostIsInstructionRAMAddress(const Core::CPUThreadGuard& guard, u32 address,
RequestedAddressSpace space = RequestedAddressSpace::Effective);
// Routines for the CPU core to access memory.

View File

@ -74,7 +74,8 @@ static u32 EvaluateBranchTarget(UGeckoInstruction instr, u32 pc)
// Also collect which internal branch goes the farthest.
// If any one goes farther than the blr or rfi, assume that there is more than
// one blr or rfi, and keep scanning.
bool AnalyzeFunction(u32 startAddr, Common::Symbol& func, u32 max_size)
bool AnalyzeFunction(const Core::CPUThreadGuard& guard, u32 startAddr, Common::Symbol& func,
u32 max_size)
{
if (func.name.empty())
func.Rename(fmt::format("zz_{:08x}_", startAddr));
@ -91,15 +92,18 @@ bool AnalyzeFunction(u32 startAddr, Common::Symbol& func, u32 max_size)
for (u32 addr = startAddr; true; addr += 4)
{
func.size += 4;
if (func.size >= JitBase::code_buffer_size * 4 || !PowerPC::HostIsInstructionRAMAddress(addr))
if (func.size >= JitBase::code_buffer_size * 4 ||
!PowerPC::HostIsInstructionRAMAddress(guard, addr))
{
return false;
}
if (max_size && func.size > max_size)
{
func.address = startAddr;
func.analyzed = true;
func.size -= 4;
func.hash = HashSignatureDB::ComputeCodeChecksum(startAddr, addr - 4);
func.hash = HashSignatureDB::ComputeCodeChecksum(guard, startAddr, addr - 4);
if (numInternalBranches == 0)
func.flags |= Common::FFLAG_STRAIGHT;
return true;
@ -121,7 +125,7 @@ bool AnalyzeFunction(u32 startAddr, Common::Symbol& func, u32 max_size)
// Let's calc the checksum and get outta here
func.address = startAddr;
func.analyzed = true;
func.hash = HashSignatureDB::ComputeCodeChecksum(startAddr, addr);
func.hash = HashSignatureDB::ComputeCodeChecksum(guard, startAddr, addr);
if (numInternalBranches == 0)
func.flags |= Common::FFLAG_STRAIGHT;
return true;
@ -167,12 +171,13 @@ bool AnalyzeFunction(u32 startAddr, Common::Symbol& func, u32 max_size)
}
}
bool ReanalyzeFunction(u32 start_addr, Common::Symbol& func, u32 max_size)
bool ReanalyzeFunction(const Core::CPUThreadGuard& guard, u32 start_addr, Common::Symbol& func,
u32 max_size)
{
ASSERT_MSG(SYMBOLS, func.analyzed, "The function wasn't previously analyzed!");
func.analyzed = false;
return AnalyzeFunction(start_addr, func, max_size);
return AnalyzeFunction(guard, start_addr, func, max_size);
}
// Second pass analysis, done after the first pass is done for all functions
@ -256,7 +261,8 @@ bool PPCAnalyzer::CanSwapAdjacentOps(const CodeOp& a, const CodeOp& b) const
// called by another function. Therefore, let's scan the
// entire space for bl operations and find what functions
// get called.
static void FindFunctionsFromBranches(u32 startAddr, u32 endAddr, Common::SymbolDB* func_db)
static void FindFunctionsFromBranches(const Core::CPUThreadGuard& guard, u32 startAddr, u32 endAddr,
Common::SymbolDB* func_db)
{
for (u32 addr = startAddr; addr < endAddr; addr += 4)
{
@ -274,9 +280,9 @@ static void FindFunctionsFromBranches(u32 startAddr, u32 endAddr, Common::Symbol
u32 target = SignExt26(instr.LI << 2);
if (!instr.AA)
target += addr;
if (PowerPC::HostIsRAMAddress(target))
if (PowerPC::HostIsRAMAddress(guard, target))
{
func_db->AddFunction(target);
func_db->AddFunction(guard, target);
}
}
}
@ -288,7 +294,7 @@ static void FindFunctionsFromBranches(u32 startAddr, u32 endAddr, Common::Symbol
}
}
static void FindFunctionsFromHandlers(PPCSymbolDB* func_db)
static void FindFunctionsFromHandlers(const Core::CPUThreadGuard& guard, PPCSymbolDB* func_db)
{
static const std::map<u32, const char* const> handlers = {
{0x80000100, "system_reset_exception_handler"},
@ -314,7 +320,7 @@ static void FindFunctionsFromHandlers(PPCSymbolDB* func_db)
if (read_result.valid && PPCTables::IsValidInstruction(read_result.hex))
{
// Check if this function is already mapped
Common::Symbol* f = func_db->AddFunction(entry.first);
Common::Symbol* f = func_db->AddFunction(guard, entry.first);
if (!f)
continue;
f->Rename(entry.second);
@ -322,7 +328,8 @@ static void FindFunctionsFromHandlers(PPCSymbolDB* func_db)
}
}
static void FindFunctionsAfterReturnInstruction(PPCSymbolDB* func_db)
static void FindFunctionsAfterReturnInstruction(const Core::CPUThreadGuard& guard,
PPCSymbolDB* func_db)
{
std::vector<u32> funcAddrs;
@ -346,7 +353,7 @@ static void FindFunctionsAfterReturnInstruction(PPCSymbolDB* func_db)
if (read_result.valid && PPCTables::IsValidInstruction(read_result.hex))
{
// check if this function is already mapped
Common::Symbol* f = func_db->AddFunction(location);
Common::Symbol* f = func_db->AddFunction(guard, location);
if (!f)
break;
else
@ -358,12 +365,13 @@ static void FindFunctionsAfterReturnInstruction(PPCSymbolDB* func_db)
}
}
void FindFunctions(u32 startAddr, u32 endAddr, PPCSymbolDB* func_db)
void FindFunctions(const Core::CPUThreadGuard& guard, u32 startAddr, u32 endAddr,
PPCSymbolDB* func_db)
{
// Step 1: Find all functions
FindFunctionsFromBranches(startAddr, endAddr, func_db);
FindFunctionsFromHandlers(func_db);
FindFunctionsAfterReturnInstruction(func_db);
FindFunctionsFromBranches(guard, startAddr, endAddr, func_db);
FindFunctionsFromHandlers(guard, func_db);
FindFunctionsAfterReturnInstruction(guard, func_db);
// Step 2:
func_db->FillInCallers();

View File

@ -19,6 +19,11 @@ namespace Common
struct Symbol;
}
namespace Core
{
class CPUThreadGuard;
}
namespace PPCAnalyst
{
struct CodeOp // 16B
@ -201,8 +206,11 @@ private:
bool m_enable_div_by_zero_exceptions = false;
};
void FindFunctions(u32 startAddr, u32 endAddr, PPCSymbolDB* func_db);
bool AnalyzeFunction(u32 startAddr, Common::Symbol& func, u32 max_size = 0);
bool ReanalyzeFunction(u32 start_addr, Common::Symbol& func, u32 max_size = 0);
void FindFunctions(const Core::CPUThreadGuard& guard, u32 startAddr, u32 endAddr,
PPCSymbolDB* func_db);
bool AnalyzeFunction(const Core::CPUThreadGuard& guard, u32 startAddr, Common::Symbol& func,
u32 max_size = 0);
bool ReanalyzeFunction(const Core::CPUThreadGuard& guard, u32 start_addr, Common::Symbol& func,
u32 max_size = 0);
} // namespace PPCAnalyst

View File

@ -32,14 +32,14 @@ PPCSymbolDB::PPCSymbolDB() : debugger{&PowerPC::debug_interface}
PPCSymbolDB::~PPCSymbolDB() = default;
// Adds the function to the list, unless it's already there
Common::Symbol* PPCSymbolDB::AddFunction(u32 start_addr)
Common::Symbol* PPCSymbolDB::AddFunction(const Core::CPUThreadGuard& guard, u32 start_addr)
{
// It's already in the list
if (m_functions.find(start_addr) != m_functions.end())
return nullptr;
Common::Symbol symbol;
if (!PPCAnalyst::AnalyzeFunction(start_addr, symbol))
if (!PPCAnalyst::AnalyzeFunction(guard, start_addr, symbol))
return nullptr;
m_functions[start_addr] = std::move(symbol);
@ -49,8 +49,8 @@ Common::Symbol* PPCSymbolDB::AddFunction(u32 start_addr)
return ptr;
}
void PPCSymbolDB::AddKnownSymbol(u32 startAddr, u32 size, const std::string& name,
Common::Symbol::Type type)
void PPCSymbolDB::AddKnownSymbol(const Core::CPUThreadGuard& guard, u32 startAddr, u32 size,
const std::string& name, Common::Symbol::Type type)
{
auto iter = m_functions.find(startAddr);
if (iter != m_functions.end())
@ -58,7 +58,7 @@ void PPCSymbolDB::AddKnownSymbol(u32 startAddr, u32 size, const std::string& nam
// already got it, let's just update name, checksum & size to be sure.
Common::Symbol* tempfunc = &iter->second;
tempfunc->Rename(name);
tempfunc->hash = HashSignatureDB::ComputeCodeChecksum(startAddr, startAddr + size - 4);
tempfunc->hash = HashSignatureDB::ComputeCodeChecksum(guard, startAddr, startAddr + size - 4);
tempfunc->type = type;
tempfunc->size = size;
}
@ -71,7 +71,7 @@ void PPCSymbolDB::AddKnownSymbol(u32 startAddr, u32 size, const std::string& nam
tf.address = startAddr;
if (tf.type == Common::Symbol::Type::Function)
{
PPCAnalyst::AnalyzeFunction(startAddr, tf, size);
PPCAnalyst::AnalyzeFunction(guard, startAddr, tf, size);
// Do not truncate symbol when a size is expected
if (size != 0 && tf.size != size)
{
@ -224,7 +224,7 @@ void PPCSymbolDB::LogFunctionCall(u32 addr)
// This one can load both leftover map files on game discs (like Zelda), and mapfiles
// produced by SaveSymbolMap below.
// bad=true means carefully load map files that might not be from exactly the right version
bool PPCSymbolDB::LoadMap(const std::string& filename, bool bad)
bool PPCSymbolDB::LoadMap(const Core::CPUThreadGuard& guard, const std::string& filename, bool bad)
{
File::IOFile f(filename, "r");
if (!f)
@ -407,8 +407,8 @@ bool PPCSymbolDB::LoadMap(const std::string& filename, bool bad)
if (strlen(name) > 0)
{
// Can't compute the checksum if not in RAM
bool good = !bad && PowerPC::HostIsInstructionRAMAddress(vaddress) &&
PowerPC::HostIsInstructionRAMAddress(vaddress + size - 4);
bool good = !bad && PowerPC::HostIsInstructionRAMAddress(guard, vaddress) &&
PowerPC::HostIsInstructionRAMAddress(guard, vaddress + size - 4);
if (!good)
{
// check for BLR before function
@ -423,10 +423,10 @@ bool PPCSymbolDB::LoadMap(const std::string& filename, bool bad)
if (good)
{
++good_count;
if (section_name == ".text" || section_name == ".init")
AddKnownSymbol(vaddress, size, name, Common::Symbol::Type::Function);
else
AddKnownSymbol(vaddress, size, name, Common::Symbol::Type::Data);
const Common::Symbol::Type type = section_name == ".text" || section_name == ".init" ?
Common::Symbol::Type::Function :
Common::Symbol::Type::Data;
AddKnownSymbol(guard, vaddress, size, name, type);
}
else
{
@ -485,7 +485,7 @@ bool PPCSymbolDB::SaveSymbolMap(const std::string& filename) const
// Notes:
// - Dolphin doesn't load back code maps
// - It's a custom code map format
bool PPCSymbolDB::SaveCodeMap(const std::string& filename) const
bool PPCSymbolDB::SaveCodeMap(const Core::CPUThreadGuard& guard, const std::string& filename) const
{
constexpr int SYMBOL_NAME_LIMIT = 30;
File::IOFile f(filename, "w");
@ -515,7 +515,7 @@ bool PPCSymbolDB::SaveCodeMap(const std::string& filename) const
// Write the code
for (u32 address = symbol.address; address < next_address; address += 4)
{
const std::string disasm = debugger->Disassemble(address);
const std::string disasm = debugger->Disassemble(&guard, address);
f.WriteString(fmt::format("{0:08x} {1:<{2}.{3}} {4}\n", address, symbol.name,
SYMBOL_NAME_LIMIT, SYMBOL_NAME_LIMIT, disasm));
}

View File

@ -12,6 +12,11 @@
#include "Core/Debugger/PPCDebugInterface.h"
namespace Core
{
class CPUThreadGuard;
}
// This has functionality overlapping Debugger_Symbolmap. Should merge that stuff in here later.
class PPCSymbolDB : public Common::SymbolDB
{
@ -19,8 +24,9 @@ public:
PPCSymbolDB();
~PPCSymbolDB() override;
Common::Symbol* AddFunction(u32 start_addr) override;
void AddKnownSymbol(u32 startAddr, u32 size, const std::string& name,
Common::Symbol* AddFunction(const Core::CPUThreadGuard& guard, u32 start_addr) override;
void AddKnownSymbol(const Core::CPUThreadGuard& guard, u32 startAddr, u32 size,
const std::string& name,
Common::Symbol::Type type = Common::Symbol::Type::Function);
Common::Symbol* GetSymbolFromAddr(u32 addr) override;
@ -29,9 +35,9 @@ public:
void FillInCallers();
bool LoadMap(const std::string& filename, bool bad = false);
bool LoadMap(const Core::CPUThreadGuard& guard, const std::string& filename, bool bad = false);
bool SaveSymbolMap(const std::string& filename) const;
bool SaveCodeMap(const std::string& filename) const;
bool SaveCodeMap(const Core::CPUThreadGuard& guard, const std::string& filename) const;
void PrintCalls(u32 funcAddr) const;
void PrintCallers(u32 funcAddr) const;

View File

@ -101,7 +101,7 @@ bool GetRefs(MEGASignature* sig, std::istringstream* iss)
return true;
}
bool Compare(u32 address, u32 size, const MEGASignature& sig)
bool Compare(const Core::CPUThreadGuard& guard, u32 address, u32 size, const MEGASignature& sig)
{
if (size != sig.code.size() * sizeof(u32))
return false;
@ -109,8 +109,10 @@ bool Compare(u32 address, u32 size, const MEGASignature& sig)
for (size_t i = 0; i < sig.code.size(); ++i)
{
if (sig.code[i] != 0 &&
PowerPC::HostRead_U32(static_cast<u32>(address + i * sizeof(u32))) != sig.code[i])
PowerPC::HostRead_U32(guard, static_cast<u32>(address + i * sizeof(u32))) != sig.code[i])
{
return false;
}
}
return true;
}
@ -156,14 +158,14 @@ bool MEGASignatureDB::Save(const std::string& file_path) const
return false;
}
void MEGASignatureDB::Apply(PPCSymbolDB* symbol_db) const
void MEGASignatureDB::Apply(const Core::CPUThreadGuard& guard, PPCSymbolDB* symbol_db) const
{
for (auto& it : symbol_db->AccessSymbols())
{
auto& symbol = it.second;
for (const auto& sig : m_signatures)
{
if (Compare(symbol.address, symbol.size, sig))
if (Compare(guard, symbol.address, symbol.size, sig))
{
symbol.name = sig.name;
INFO_LOG_FMT(SYMBOLS, "Found {} at {:08x} (size: {:08x})!", sig.name, symbol.address,
@ -180,7 +182,8 @@ void MEGASignatureDB::Populate(const PPCSymbolDB* func_db, const std::string& fi
ERROR_LOG_FMT(SYMBOLS, "MEGA database can't be populated yet.");
}
bool MEGASignatureDB::Add(u32 startAddr, u32 size, const std::string& name)
bool MEGASignatureDB::Add(const Core::CPUThreadGuard& guard, u32 startAddr, u32 size,
const std::string& name)
{
ERROR_LOG_FMT(SYMBOLS, "Can't add symbol to MEGA database yet.");
return false;

View File

@ -9,6 +9,11 @@
#include "Common/CommonTypes.h"
#include "Core/PowerPC/SignatureDB/SignatureDB.h"
namespace Core
{
class CPUThreadGuard;
}
class PPCSymbolDB;
struct MEGASignatureReference
@ -46,10 +51,11 @@ public:
bool Save(const std::string& file_path) const override;
void List() const override;
void Apply(PPCSymbolDB* symbol_db) const override;
void Apply(const Core::CPUThreadGuard& guard, PPCSymbolDB* symbol_db) const override;
void Populate(const PPCSymbolDB* func_db, const std::string& filter = "") override;
bool Add(u32 startAddr, u32 size, const std::string& name) override;
bool Add(const Core::CPUThreadGuard& guard, u32 startAddr, u32 size,
const std::string& name) override;
private:
std::vector<MEGASignature> m_signatures;

View File

@ -76,20 +76,22 @@ void SignatureDB::Populate(const PPCSymbolDB* func_db, const std::string& filter
m_handler->Populate(func_db, filter);
}
void SignatureDB::Apply(PPCSymbolDB* func_db) const
void SignatureDB::Apply(const Core::CPUThreadGuard& guard, PPCSymbolDB* func_db) const
{
m_handler->Apply(func_db);
m_handler->Apply(guard, func_db);
}
bool SignatureDB::Add(u32 start_addr, u32 size, const std::string& name)
bool SignatureDB::Add(const Core::CPUThreadGuard& guard, u32 start_addr, u32 size,
const std::string& name)
{
return m_handler->Add(start_addr, size, name);
return m_handler->Add(guard, start_addr, size, name);
}
// Adds a known function to the hash database
bool HashSignatureDB::Add(u32 startAddr, u32 size, const std::string& name)
bool HashSignatureDB::Add(const Core::CPUThreadGuard& guard, u32 startAddr, u32 size,
const std::string& name)
{
u32 hash = ComputeCodeChecksum(startAddr, startAddr + size - 4);
u32 hash = ComputeCodeChecksum(guard, startAddr, startAddr + size - 4);
DBFunc temp_dbfunc;
temp_dbfunc.size = size;
@ -119,7 +121,7 @@ void HashSignatureDB::Clear()
m_database.clear();
}
void HashSignatureDB::Apply(PPCSymbolDB* symbol_db) const
void HashSignatureDB::Apply(const Core::CPUThreadGuard& guard, PPCSymbolDB* symbol_db) const
{
for (const auto& entry : m_database)
{
@ -158,12 +160,13 @@ void HashSignatureDB::Populate(const PPCSymbolDB* symbol_db, const std::string&
}
}
u32 HashSignatureDB::ComputeCodeChecksum(u32 offsetStart, u32 offsetEnd)
u32 HashSignatureDB::ComputeCodeChecksum(const Core::CPUThreadGuard& guard, u32 offsetStart,
u32 offsetEnd)
{
u32 sum = 0;
for (u32 offset = offsetStart; offset <= offsetEnd; offset += 4)
{
u32 opcode = PowerPC::HostRead_Instruction(offset);
u32 opcode = PowerPC::HostRead_Instruction(guard, offset);
u32 op = opcode & 0xFC000000;
u32 op2 = 0;
u32 op3 = 0;

View File

@ -11,6 +11,11 @@
// You're not meant to keep around SignatureDB objects persistently. Use 'em, throw them away.
namespace Core
{
class CPUThreadGuard;
}
class PPCSymbolDB;
class SignatureDBFormatHandler;
@ -33,9 +38,9 @@ public:
void List() const;
void Populate(const PPCSymbolDB* func_db, const std::string& filter = "");
void Apply(PPCSymbolDB* func_db) const;
void Apply(const Core::CPUThreadGuard& guard, PPCSymbolDB* func_db) const;
bool Add(u32 start_addr, u32 size, const std::string& name);
bool Add(const Core::CPUThreadGuard& guard, u32 start_addr, u32 size, const std::string& name);
private:
std::unique_ptr<SignatureDBFormatHandler> m_handler;
@ -52,9 +57,10 @@ public:
virtual void List() const = 0;
virtual void Populate(const PPCSymbolDB* func_db, const std::string& filter = "") = 0;
virtual void Apply(PPCSymbolDB* func_db) const = 0;
virtual void Apply(const Core::CPUThreadGuard& guard, PPCSymbolDB* func_db) const = 0;
virtual bool Add(u32 startAddr, u32 size, const std::string& name) = 0;
virtual bool Add(const Core::CPUThreadGuard& guard, u32 startAddr, u32 size,
const std::string& name) = 0;
};
class HashSignatureDB : public SignatureDBFormatHandler
@ -69,15 +75,16 @@ public:
};
using FuncDB = std::map<u32, DBFunc>;
static u32 ComputeCodeChecksum(u32 offsetStart, u32 offsetEnd);
static u32 ComputeCodeChecksum(const Core::CPUThreadGuard& guard, u32 offsetStart, u32 offsetEnd);
void Clear() override;
void List() const override;
void Populate(const PPCSymbolDB* func_db, const std::string& filter = "") override;
void Apply(PPCSymbolDB* func_db) const override;
void Apply(const Core::CPUThreadGuard& guard, PPCSymbolDB* func_db) const override;
bool Add(u32 startAddr, u32 size, const std::string& name) override;
bool Add(const Core::CPUThreadGuard& guard, u32 startAddr, u32 size,
const std::string& name) override;
protected:
// Map from signature to function. We store the DB in this map because it optimizes the

View File

@ -477,30 +477,31 @@ void ApplyPatchesToFiles(const std::vector<Patch>& patches, PatchIndex index,
}
}
static bool MemoryMatchesAt(u32 offset, const std::vector<u8>& value)
static bool MemoryMatchesAt(const Core::CPUThreadGuard& guard, u32 offset,
const std::vector<u8>& value)
{
for (u32 i = 0; i < value.size(); ++i)
{
auto result = PowerPC::HostTryReadU8(offset + i);
auto result = PowerPC::HostTryReadU8(guard, offset + i);
if (!result || result->value != value[i])
return false;
}
return true;
}
static void ApplyMemoryPatch(u32 offset, const std::vector<u8>& value,
const std::vector<u8>& original)
static void ApplyMemoryPatch(const Core::CPUThreadGuard& guard, u32 offset,
const std::vector<u8>& value, const std::vector<u8>& original)
{
if (value.empty())
return;
if (!original.empty() && !MemoryMatchesAt(offset, original))
if (!original.empty() && !MemoryMatchesAt(guard, offset, original))
return;
auto& system = Core::System::GetInstance();
const u32 size = static_cast<u32>(value.size());
for (u32 i = 0; i < size; ++i)
PowerPC::HostTryWriteU8(value[i], offset + i);
PowerPC::HostTryWriteU8(guard, value[i], offset + i);
const u32 overlapping_hook_count = HLE::UnpatchRange(system, offset, offset + size);
if (overlapping_hook_count != 0)
{
@ -516,17 +517,18 @@ static std::vector<u8> GetMemoryPatchValue(const Patch& patch, const Memory& mem
return memory_patch.m_value;
}
static void ApplyMemoryPatch(const Patch& patch, const Memory& memory_patch)
static void ApplyMemoryPatch(const Core::CPUThreadGuard& guard, const Patch& patch,
const Memory& memory_patch)
{
if (memory_patch.m_offset == 0)
return;
ApplyMemoryPatch(memory_patch.m_offset | 0x80000000, GetMemoryPatchValue(patch, memory_patch),
memory_patch.m_original);
ApplyMemoryPatch(guard, memory_patch.m_offset | 0x80000000,
GetMemoryPatchValue(patch, memory_patch), memory_patch.m_original);
}
static void ApplySearchMemoryPatch(const Patch& patch, const Memory& memory_patch, u32 ram_start,
u32 length)
static void ApplySearchMemoryPatch(const Core::CPUThreadGuard& guard, const Patch& patch,
const Memory& memory_patch, u32 ram_start, u32 length)
{
if (memory_patch.m_original.empty() || memory_patch.m_align == 0)
return;
@ -535,16 +537,16 @@ static void ApplySearchMemoryPatch(const Patch& patch, const Memory& memory_patc
for (u32 i = 0; i < length - (stride - 1); i += stride)
{
const u32 address = ram_start + i;
if (MemoryMatchesAt(address, memory_patch.m_original))
if (MemoryMatchesAt(guard, address, memory_patch.m_original))
{
ApplyMemoryPatch(address, GetMemoryPatchValue(patch, memory_patch), {});
ApplyMemoryPatch(guard, address, GetMemoryPatchValue(patch, memory_patch), {});
break;
}
}
}
static void ApplyOcarinaMemoryPatch(const Patch& patch, const Memory& memory_patch, u32 ram_start,
u32 length)
static void ApplyOcarinaMemoryPatch(const Core::CPUThreadGuard& guard, const Patch& patch,
const Memory& memory_patch, u32 ram_start, u32 length)
{
if (memory_patch.m_offset == 0)
return;
@ -557,19 +559,19 @@ static void ApplyOcarinaMemoryPatch(const Patch& patch, const Memory& memory_pat
{
// first find the pattern
const u32 address = ram_start + i;
if (MemoryMatchesAt(address, value))
if (MemoryMatchesAt(guard, address, value))
{
for (; i < length; i += 4)
{
// from the pattern find the next blr instruction
const u32 blr_address = ram_start + i;
auto blr = PowerPC::HostTryReadU32(blr_address);
auto blr = PowerPC::HostTryReadU32(guard, blr_address);
if (blr && blr->value == 0x4e800020)
{
// and replace it with a jump to the given offset
const u32 target = memory_patch.m_offset | 0x80000000;
const u32 jmp = ((target - blr_address) & 0x03fffffc) | 0x48000000;
PowerPC::HostTryWriteU32(jmp, blr_address);
PowerPC::HostTryWriteU32(guard, jmp, blr_address);
const u32 overlapping_hook_count =
HLE::UnpatchRange(system, blr_address, blr_address + 4);
if (overlapping_hook_count != 0)
@ -584,7 +586,7 @@ static void ApplyOcarinaMemoryPatch(const Patch& patch, const Memory& memory_pat
}
}
void ApplyGeneralMemoryPatches(const std::vector<Patch>& patches)
void ApplyGeneralMemoryPatches(const Core::CPUThreadGuard& guard, const std::vector<Patch>& patches)
{
auto& system = Core::System::GetInstance();
auto& system_memory = system.GetMemory();
@ -597,14 +599,15 @@ void ApplyGeneralMemoryPatches(const std::vector<Patch>& patches)
continue;
if (memory.m_search)
ApplySearchMemoryPatch(patch, memory, 0x80000000, system_memory.GetRamSize());
ApplySearchMemoryPatch(guard, patch, memory, 0x80000000, system_memory.GetRamSize());
else
ApplyMemoryPatch(patch, memory);
ApplyMemoryPatch(guard, patch, memory);
}
}
}
void ApplyApploaderMemoryPatches(const std::vector<Patch>& patches, u32 ram_address, u32 ram_length)
void ApplyApploaderMemoryPatches(const Core::CPUThreadGuard& guard,
const std::vector<Patch>& patches, u32 ram_address, u32 ram_length)
{
for (const auto& patch : patches)
{
@ -614,9 +617,9 @@ void ApplyApploaderMemoryPatches(const std::vector<Patch>& patches, u32 ram_addr
continue;
if (memory.m_ocarina)
ApplyOcarinaMemoryPatch(patch, memory, ram_address, ram_length);
ApplyOcarinaMemoryPatch(guard, patch, memory, ram_address, ram_length);
else
ApplySearchMemoryPatch(patch, memory, ram_address, ram_length);
ApplySearchMemoryPatch(guard, patch, memory, ram_address, ram_length);
}
}
}

View File

@ -11,6 +11,11 @@
#include "DiscIO/DirectoryBlob.h"
#include "DiscIO/RiivolutionParser.h"
namespace Core
{
class CPUThreadGuard;
}
namespace DiscIO::Riivolution
{
struct SavegameRedirect
@ -74,8 +79,10 @@ enum class PatchIndex
void ApplyPatchesToFiles(const std::vector<Patch>& patches, PatchIndex index,
std::vector<DiscIO::FSTBuilderNode>* fst,
DiscIO::FSTBuilderNode* dol_node);
void ApplyGeneralMemoryPatches(const std::vector<Patch>& patches);
void ApplyApploaderMemoryPatches(const std::vector<Patch>& patches, u32 ram_address,
void ApplyGeneralMemoryPatches(const Core::CPUThreadGuard& guard,
const std::vector<Patch>& patches);
void ApplyApploaderMemoryPatches(const Core::CPUThreadGuard& guard,
const std::vector<Patch>& patches, u32 ram_address,
u32 ram_length);
std::optional<SavegameRedirect>
ExtractSavegameRedirect(const std::vector<Patch>& riivolution_patches);

View File

@ -36,6 +36,7 @@
#include "Core/CheatGeneration.h"
#include "Core/CheatSearch.h"
#include "Core/ConfigManager.h"
#include "Core/Core.h"
#include "Core/PowerPC/PowerPC.h"
#include "DolphinQt/Config/CheatCodeEditor.h"
@ -286,7 +287,11 @@ void CheatSearchWidget::OnNextScanClicked()
return;
}
}
Cheats::SearchErrorCode error_code = m_session->RunSearch();
const Cheats::SearchErrorCode error_code = [this] {
Core::CPUThreadGuard guard;
return m_session->RunSearch(guard);
}();
if (error_code == Cheats::SearchErrorCode::Success)
{
@ -391,7 +396,13 @@ bool CheatSearchWidget::RefreshValues()
}
tmp->SetFilterType(Cheats::FilterType::DoNotFilter);
if (tmp->RunSearch() != Cheats::SearchErrorCode::Success)
const Cheats::SearchErrorCode error_code = [&tmp] {
Core::CPUThreadGuard guard;
return tmp->RunSearch(guard);
}();
if (error_code != Cheats::SearchErrorCode::Success)
{
m_info_label_1->setText(tr("Refresh failed. Please run the game for a bit and try again."));
return false;

View File

@ -483,7 +483,11 @@ void CodeDiffDialog::OnSetBLR()
Common::Symbol* symbol = g_symbolDB.GetSymbolFromAddr(item->data(Qt::UserRole).toUInt());
if (!symbol)
return;
PowerPC::debug_interface.SetPatch(symbol->address, 0x4E800020);
{
Core::CPUThreadGuard guard;
PowerPC::debug_interface.SetPatch(guard, symbol->address, 0x4E800020);
}
int row = item->row();
m_matching_results_table->item(row, 0)->setForeground(QBrush(Qt::red));

View File

@ -175,14 +175,15 @@ CodeViewWidget::CodeViewWidget()
Update();
});
connect(&Settings::Instance(), &Settings::ThemeChanged, this, &CodeViewWidget::Update);
connect(&Settings::Instance(), &Settings::ThemeChanged, this,
qOverload<>(&CodeViewWidget::Update));
}
CodeViewWidget::~CodeViewWidget() = default;
static u32 GetBranchFromAddress(u32 addr)
static u32 GetBranchFromAddress(const Core::CPUThreadGuard& guard, u32 addr)
{
std::string disasm = PowerPC::debug_interface.Disassemble(addr);
std::string disasm = PowerPC::debug_interface.Disassemble(&guard, addr);
size_t pos = disasm.find("->0x");
if (pos == std::string::npos)
@ -248,6 +249,26 @@ static bool IsInstructionLoadStore(std::string_view ins)
}
void CodeViewWidget::Update()
{
if (!isVisible())
return;
if (m_updating)
return;
if (Core::GetState() == Core::State::Paused)
{
Core::CPUThreadGuard guard;
Update(&guard);
}
else
{
// If the core is running, blank out the view of memory instead of reading anything.
Update(nullptr);
}
}
void CodeViewWidget::Update(const Core::CPUThreadGuard* guard)
{
if (!isVisible())
return;
@ -284,11 +305,11 @@ void CodeViewWidget::Update()
for (int i = 0; i < rowCount(); i++)
{
const u32 addr = AddressForRow(i);
const u32 color = PowerPC::debug_interface.GetColor(addr);
const u32 color = PowerPC::debug_interface.GetColor(guard, addr);
auto* bp_item = new QTableWidgetItem;
auto* addr_item = new QTableWidgetItem(QStringLiteral("%1").arg(addr, 8, 16, QLatin1Char('0')));
std::string disas = PowerPC::debug_interface.Disassemble(addr);
std::string disas = PowerPC::debug_interface.Disassemble(guard, addr);
auto split = disas.find('\t');
std::string ins = (split == std::string::npos ? disas : disas.substr(0, split));
@ -332,9 +353,9 @@ void CodeViewWidget::Update()
hex_str = param.substr(pos);
}
if (hex_str.length() == VALID_BRANCH_LENGTH && desc != "---")
if (guard && hex_str.length() == VALID_BRANCH_LENGTH && desc != "---")
{
u32 branch_addr = GetBranchFromAddress(addr);
u32 branch_addr = GetBranchFromAddress(*guard, addr);
CodeViewBranch& branch = m_branches.emplace_back();
branch.src_addr = addr;
branch.dst_addr = branch_addr;
@ -514,15 +535,20 @@ void CodeViewWidget::SetAddress(u32 address, SetAddressUpdate update)
void CodeViewWidget::ReplaceAddress(u32 address, ReplaceWith replace)
{
PowerPC::debug_interface.SetPatch(address, replace == ReplaceWith::BLR ? 0x4e800020 : 0x60000000);
Update();
Core::CPUThreadGuard guard;
PowerPC::debug_interface.SetPatch(guard, address,
replace == ReplaceWith::BLR ? 0x4e800020 : 0x60000000);
Update(&guard);
}
void CodeViewWidget::OnContextMenu()
{
QMenu* menu = new QMenu(this);
bool running = Core::GetState() != Core::State::Uninitialized;
const bool running = Core::GetState() != Core::State::Uninitialized;
const bool paused = Core::GetState() == Core::State::Paused;
const u32 addr = GetContextAddress();
@ -567,14 +593,25 @@ void CodeViewWidget::OnContextMenu()
menu->addAction(tr("Restore instruction"), this, &CodeViewWidget::OnRestoreInstruction);
QString target;
if (addr == PowerPC::ppcState.pc && running && Core::GetState() == Core::State::Paused)
bool valid_load_store = false;
bool follow_branch_enabled = false;
if (paused)
{
const std::string line = PowerPC::debug_interface.Disassemble(PowerPC::ppcState.pc);
const auto target_it = std::find(line.begin(), line.end(), '\t');
const auto target_end = std::find(target_it, line.end(), ',');
Core::CPUThreadGuard guard;
const std::string disasm = PowerPC::debug_interface.Disassemble(&guard, PowerPC::ppcState.pc);
if (target_it != line.end() && target_end != line.end())
target = QString::fromStdString(std::string{target_it + 1, target_end});
if (addr == PowerPC::ppcState.pc)
{
const auto target_it = std::find(disasm.begin(), disasm.end(), '\t');
const auto target_end = std::find(target_it, disasm.end(), ',');
if (target_it != disasm.end() && target_end != disasm.end())
target = QString::fromStdString(std::string{target_it + 1, target_end});
}
valid_load_store = IsInstructionLoadStore(disasm);
follow_branch_enabled = GetBranchFromAddress(guard, addr);
}
auto* run_until_menu = menu->addMenu(tr("Run until (ignoring breakpoints)"));
@ -589,18 +626,17 @@ void CodeViewWidget::OnContextMenu()
[this] { AutoStep(CodeTrace::AutoStop::Changed); });
run_until_menu->setEnabled(!target.isEmpty());
follow_branch_action->setEnabled(running && GetBranchFromAddress(addr));
follow_branch_action->setEnabled(follow_branch_enabled);
for (auto* action : {copy_address_action, copy_line_action, copy_hex_action, function_action,
ppc_action, insert_blr_action, insert_nop_action, replace_action})
{
action->setEnabled(running);
}
for (auto* action : {symbol_rename_action, symbol_size_action, symbol_end_action})
action->setEnabled(has_symbol);
const bool valid_load_store = Core::GetState() == Core::State::Paused &&
IsInstructionLoadStore(PowerPC::debug_interface.Disassemble(addr));
for (auto* action : {copy_target_memory, show_target_memory})
{
action->setEnabled(valid_load_store);
@ -617,6 +653,8 @@ void CodeViewWidget::AutoStep(CodeTrace::AutoStop option)
// Autosteps and follows value in the target (left-most) register. The Used and Changed options
// silently follows target through reshuffles in memory and registers and stops on use or update.
Core::CPUThreadGuard guard;
CodeTrace code_trace;
bool repeat = false;
@ -628,7 +666,7 @@ void CodeViewWidget::AutoStep(CodeTrace::AutoStop option)
do
{
// Run autostep then update codeview
const AutoStepResults results = code_trace.AutoStepping(repeat, option);
const AutoStepResults results = code_trace.AutoStepping(guard, repeat, option);
emit Host::GetInstance()->UpdateDisasmDialog();
repeat = true;
@ -703,16 +741,24 @@ void CodeViewWidget::OnCopyTargetAddress()
if (Core::GetState() != Core::State::Paused)
return;
const std::string code_line = PowerPC::debug_interface.Disassemble(GetContextAddress());
const u32 addr = GetContextAddress();
const std::string code_line = [addr] {
Core::CPUThreadGuard guard;
return PowerPC::debug_interface.Disassemble(&guard, addr);
}();
if (!IsInstructionLoadStore(code_line))
return;
const std::optional<u32> addr =
const std::optional<u32> target_addr =
PowerPC::debug_interface.GetMemoryAddressFromInstruction(code_line);
if (addr)
QApplication::clipboard()->setText(QStringLiteral("%1").arg(*addr, 8, 16, QLatin1Char('0')));
{
QApplication::clipboard()->setText(
QStringLiteral("%1").arg(*target_addr, 8, 16, QLatin1Char('0')));
}
}
void CodeViewWidget::OnShowInMemory()
@ -725,24 +771,33 @@ void CodeViewWidget::OnShowTargetInMemory()
if (Core::GetState() != Core::State::Paused)
return;
const std::string code_line = PowerPC::debug_interface.Disassemble(GetContextAddress());
const u32 addr = GetContextAddress();
const std::string code_line = [addr] {
Core::CPUThreadGuard guard;
return PowerPC::debug_interface.Disassemble(&guard, addr);
}();
if (!IsInstructionLoadStore(code_line))
return;
const std::optional<u32> addr =
const std::optional<u32> target_addr =
PowerPC::debug_interface.GetMemoryAddressFromInstruction(code_line);
if (addr)
emit ShowMemory(*addr);
emit ShowMemory(*target_addr);
}
void CodeViewWidget::OnCopyCode()
{
const u32 addr = GetContextAddress();
QApplication::clipboard()->setText(
QString::fromStdString(PowerPC::debug_interface.Disassemble(addr)));
const std::string text = [addr] {
Core::CPUThreadGuard guard;
return PowerPC::debug_interface.Disassemble(&guard, addr);
}();
QApplication::clipboard()->setText(QString::fromStdString(text));
}
void CodeViewWidget::OnCopyFunction()
@ -754,13 +809,18 @@ void CodeViewWidget::OnCopyFunction()
return;
std::string text = symbol->name + "\r\n";
// we got a function
const u32 start = symbol->address;
const u32 end = start + symbol->size;
for (u32 addr = start; addr != end; addr += 4)
{
const std::string disasm = PowerPC::debug_interface.Disassemble(addr);
fmt::format_to(std::back_inserter(text), "{:08x}: {}\r\n", addr, disasm);
Core::CPUThreadGuard guard;
// we got a function
const u32 start = symbol->address;
const u32 end = start + symbol->size;
for (u32 addr = start; addr != end; addr += 4)
{
const std::string disasm = PowerPC::debug_interface.Disassemble(&guard, addr);
fmt::format_to(std::back_inserter(text), "{:08x}: {}\r\n", addr, disasm);
}
}
QApplication::clipboard()->setText(QString::fromStdString(text));
@ -769,7 +829,11 @@ void CodeViewWidget::OnCopyFunction()
void CodeViewWidget::OnCopyHex()
{
const u32 addr = GetContextAddress();
const u32 instruction = PowerPC::debug_interface.ReadInstruction(addr);
const u32 instruction = [addr] {
Core::CPUThreadGuard guard;
return PowerPC::debug_interface.ReadInstruction(guard, addr);
}();
QApplication::clipboard()->setText(
QStringLiteral("%1").arg(instruction, 8, 16, QLatin1Char('0')));
@ -795,9 +859,11 @@ void CodeViewWidget::OnAddFunction()
{
const u32 addr = GetContextAddress();
g_symbolDB.AddFunction(addr);
Core::CPUThreadGuard guard;
g_symbolDB.AddFunction(guard, addr);
emit SymbolsChanged();
Update();
Update(&guard);
}
void CodeViewWidget::OnInsertBLR()
@ -818,7 +884,10 @@ void CodeViewWidget::OnFollowBranch()
{
const u32 addr = GetContextAddress();
u32 branch_addr = GetBranchFromAddress(addr);
const u32 branch_addr = [addr] {
Core::CPUThreadGuard guard;
return GetBranchFromAddress(guard, addr);
}();
if (!branch_addr)
return;
@ -879,9 +948,11 @@ void CodeViewWidget::OnSetSymbolSize()
if (!good)
return;
PPCAnalyst::ReanalyzeFunction(symbol->address, *symbol, size);
Core::CPUThreadGuard guard;
PPCAnalyst::ReanalyzeFunction(guard, symbol->address, *symbol, size);
emit SymbolsChanged();
Update();
Update(&guard);
}
void CodeViewWidget::OnSetSymbolEndAddress()
@ -905,37 +976,43 @@ void CodeViewWidget::OnSetSymbolEndAddress()
if (!good)
return;
PPCAnalyst::ReanalyzeFunction(symbol->address, *symbol, address - symbol->address);
Core::CPUThreadGuard guard;
PPCAnalyst::ReanalyzeFunction(guard, symbol->address, *symbol, address - symbol->address);
emit SymbolsChanged();
Update();
Update(&guard);
}
void CodeViewWidget::OnReplaceInstruction()
{
Core::CPUThreadGuard guard;
const u32 addr = GetContextAddress();
if (!PowerPC::HostIsInstructionRAMAddress(addr))
if (!PowerPC::HostIsInstructionRAMAddress(guard, addr))
return;
const PowerPC::TryReadInstResult read_result = PowerPC::TryReadInstruction(addr);
if (!read_result.valid)
return;
PatchInstructionDialog dialog(this, addr, PowerPC::debug_interface.ReadInstruction(addr));
PatchInstructionDialog dialog(this, addr, PowerPC::debug_interface.ReadInstruction(guard, addr));
if (dialog.exec() == QDialog::Accepted)
{
PowerPC::debug_interface.SetPatch(addr, dialog.GetCode());
Update();
PowerPC::debug_interface.SetPatch(guard, addr, dialog.GetCode());
Update(&guard);
}
}
void CodeViewWidget::OnRestoreInstruction()
{
Core::CPUThreadGuard guard;
const u32 addr = GetContextAddress();
PowerPC::debug_interface.UnsetPatch(addr);
Update();
PowerPC::debug_interface.UnsetPatch(guard, addr);
Update(&guard);
}
void CodeViewWidget::resizeEvent(QResizeEvent*)

View File

@ -15,6 +15,11 @@ class QMouseEvent;
class QResizeEvent;
class QShowEvent;
namespace Core
{
class CPUThreadGuard;
};
struct CodeViewBranch;
class BranchDisplayDelegate;
@ -39,6 +44,7 @@ public:
// Set tighter row height. Set BP column sizing. This needs to run when font type changes.
void FontBasedSizing();
void Update();
void Update(const Core::CPUThreadGuard* guard);
void ToggleBreakpoint();
void AddBreakpoint();

View File

@ -329,7 +329,10 @@ void CodeWidget::UpdateCallstack()
std::vector<Dolphin_Debugger::CallstackEntry> stack;
bool success = Dolphin_Debugger::GetCallstack(Core::System::GetInstance(), stack);
const bool success = [&stack] {
Core::CPUThreadGuard guard;
return Dolphin_Debugger::GetCallstack(Core::System::GetInstance(), guard, stack);
}();
if (!success)
{
@ -452,7 +455,11 @@ void CodeWidget::StepOver()
if (!CPU::IsStepping())
return;
UGeckoInstruction inst = PowerPC::HostRead_Instruction(PowerPC::ppcState.pc);
const UGeckoInstruction inst = [] {
Core::CPUThreadGuard guard;
return PowerPC::HostRead_Instruction(guard, PowerPC::ppcState.pc);
}();
if (inst.LK)
{
PowerPC::breakpoints.ClearAllTemporary();
@ -485,48 +492,51 @@ void CodeWidget::StepOut()
if (!CPU::IsStepping())
return;
CPU::PauseAndLock(true, false);
PowerPC::breakpoints.ClearAllTemporary();
// Keep stepping until the next return instruction or timeout after five seconds
using clock = std::chrono::steady_clock;
clock::time_point timeout = clock::now() + std::chrono::seconds(5);
PowerPC::CoreMode old_mode = PowerPC::GetMode();
PowerPC::SetMode(PowerPC::CoreMode::Interpreter);
// Loop until either the current instruction is a return instruction with no Link flag
// or a breakpoint is detected so it can step at the breakpoint. If the PC is currently
// on a breakpoint, skip it.
UGeckoInstruction inst = PowerPC::HostRead_Instruction(PowerPC::ppcState.pc);
do
{
if (WillInstructionReturn(inst))
{
PowerPC::SingleStep();
break;
}
Core::CPUThreadGuard guard;
if (inst.LK)
PowerPC::breakpoints.ClearAllTemporary();
PowerPC::CoreMode old_mode = PowerPC::GetMode();
PowerPC::SetMode(PowerPC::CoreMode::Interpreter);
// Loop until either the current instruction is a return instruction with no Link flag
// or a breakpoint is detected so it can step at the breakpoint. If the PC is currently
// on a breakpoint, skip it.
UGeckoInstruction inst = PowerPC::HostRead_Instruction(guard, PowerPC::ppcState.pc);
do
{
// Step over branches
u32 next_pc = PowerPC::ppcState.pc + 4;
do
if (WillInstructionReturn(inst))
{
PowerPC::SingleStep();
} while (PowerPC::ppcState.pc != next_pc && clock::now() < timeout &&
!PowerPC::breakpoints.IsAddressBreakPoint(PowerPC::ppcState.pc));
}
else
{
PowerPC::SingleStep();
}
break;
}
inst = PowerPC::HostRead_Instruction(PowerPC::ppcState.pc);
} while (clock::now() < timeout &&
!PowerPC::breakpoints.IsAddressBreakPoint(PowerPC::ppcState.pc));
if (inst.LK)
{
// Step over branches
u32 next_pc = PowerPC::ppcState.pc + 4;
do
{
PowerPC::SingleStep();
} while (PowerPC::ppcState.pc != next_pc && clock::now() < timeout &&
!PowerPC::breakpoints.IsAddressBreakPoint(PowerPC::ppcState.pc));
}
else
{
PowerPC::SingleStep();
}
PowerPC::SetMode(old_mode);
CPU::PauseAndLock(false, false);
inst = PowerPC::HostRead_Instruction(guard, PowerPC::ppcState.pc);
} while (clock::now() < timeout &&
!PowerPC::breakpoints.IsAddressBreakPoint(PowerPC::ppcState.pc));
PowerPC::SetMode(old_mode);
}
emit Host::GetInstance()->UpdateDisasmDialog();

View File

@ -18,6 +18,7 @@
#include <fmt/printf.h>
#include "Common/Align.h"
#include "Common/Assert.h"
#include "Common/FloatUtils.h"
#include "Common/StringUtil.h"
#include "Common/Swap.h"
@ -46,6 +47,8 @@ constexpr int SCROLLBAR_PAGESTEP = 250;
constexpr int SCROLLBAR_MAXIMUM = 20000;
constexpr int SCROLLBAR_CENTER = SCROLLBAR_MAXIMUM / 2;
const QString INVALID_MEMORY = QStringLiteral("-");
class MemoryViewTable final : public QTableWidget
{
public:
@ -151,11 +154,13 @@ public:
u32 end_address = address + static_cast<u32>(bytes.size()) - 1;
AddressSpace::Accessors* accessors = AddressSpace::GetAccessors(m_view->GetAddressSpace());
if (!bytes.empty() && accessors->IsValidAddress(address) &&
accessors->IsValidAddress(end_address))
Core::CPUThreadGuard guard;
if (!bytes.empty() && accessors->IsValidAddress(guard, address) &&
accessors->IsValidAddress(guard, end_address))
{
for (const u8 c : bytes)
accessors->WriteU8(address++, c);
accessors->WriteU8(guard, address++, c);
}
m_view->Update();
@ -190,8 +195,9 @@ MemoryViewWidget::MemoryViewWidget(QWidget* parent) : QWidget(parent)
connect(&Settings::Instance(), &Settings::DebugFontChanged, this, &MemoryViewWidget::UpdateFont);
connect(&Settings::Instance(), &Settings::EmulationStateChanged, this,
&MemoryViewWidget::UpdateColumns);
connect(Host::GetInstance(), &Host::UpdateDisasmDialog, this, &MemoryViewWidget::UpdateColumns);
qOverload<>(&MemoryViewWidget::UpdateColumns));
connect(Host::GetInstance(), &Host::UpdateDisasmDialog, this,
qOverload<>(&MemoryViewWidget::UpdateColumns));
connect(&Settings::Instance(), &Settings::ThemeChanged, this, &MemoryViewWidget::Update);
// Also calls create table.
@ -322,13 +328,13 @@ void MemoryViewWidget::CreateTable()
bp_item->setData(USER_ROLE_VALUE_TYPE, static_cast<int>(Type::Null));
// Row Addresses
auto* row_item = new QTableWidgetItem(QStringLiteral("-"));
auto* row_item = new QTableWidgetItem(INVALID_MEMORY);
row_item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
row_item->setData(USER_ROLE_IS_ROW_BREAKPOINT_CELL, false);
row_item->setData(USER_ROLE_VALUE_TYPE, static_cast<int>(Type::Null));
// Data item
auto* item = new QTableWidgetItem(QStringLiteral("-"));
auto* item = new QTableWidgetItem(INVALID_MEMORY);
item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable);
item->setData(USER_ROLE_IS_ROW_BREAKPOINT_CELL, false);
@ -430,6 +436,24 @@ void MemoryViewWidget::Update()
}
void MemoryViewWidget::UpdateColumns()
{
// Check if table is created
if (m_table->item(1, 1) == nullptr)
return;
if (Core::GetState() == Core::State::Paused)
{
Core::CPUThreadGuard guard;
UpdateColumns(&guard);
}
else
{
// If the core is running, blank out the view of memory instead of reading anything.
UpdateColumns(nullptr);
}
}
void MemoryViewWidget::UpdateColumns(const Core::CPUThreadGuard* guard)
{
// Check if table is created
if (m_table->item(1, 1) == nullptr)
@ -445,7 +469,7 @@ void MemoryViewWidget::UpdateColumns()
const u32 cell_address = cell_item->data(USER_ROLE_CELL_ADDRESS).toUInt();
const Type type = static_cast<Type>(cell_item->data(USER_ROLE_VALUE_TYPE).toInt());
cell_item->setText(ValueToString(cell_address, type));
cell_item->setText(guard ? ValueToString(*guard, cell_address, type) : INVALID_MEMORY);
// Set search address to selected / colored
if (cell_address == m_address_highlight)
@ -454,55 +478,56 @@ void MemoryViewWidget::UpdateColumns()
}
}
QString MemoryViewWidget::ValueToString(u32 address, Type type)
// May only be called if we have taken on the role of the CPU thread
QString MemoryViewWidget::ValueToString(const Core::CPUThreadGuard& guard, u32 address, Type type)
{
const AddressSpace::Accessors* accessors = AddressSpace::GetAccessors(m_address_space);
if (!accessors->IsValidAddress(address) || Core::GetState() != Core::State::Paused)
return QStringLiteral("-");
if (!accessors->IsValidAddress(guard, address))
return INVALID_MEMORY;
switch (type)
{
case Type::Hex8:
{
const u8 value = accessors->ReadU8(address);
const u8 value = accessors->ReadU8(guard, address);
return QStringLiteral("%1").arg(value, 2, 16, QLatin1Char('0'));
}
case Type::ASCII:
{
const char value = accessors->ReadU8(address);
const char value = accessors->ReadU8(guard, address);
return IsPrintableCharacter(value) ? QString{QChar::fromLatin1(value)} :
QString{QChar::fromLatin1('.')};
}
case Type::Hex16:
{
const u16 value = accessors->ReadU16(address);
const u16 value = accessors->ReadU16(guard, address);
return QStringLiteral("%1").arg(value, 4, 16, QLatin1Char('0'));
}
case Type::Hex32:
{
const u32 value = accessors->ReadU32(address);
const u32 value = accessors->ReadU32(guard, address);
return QStringLiteral("%1").arg(value, 8, 16, QLatin1Char('0'));
}
case Type::Hex64:
{
const u64 value = accessors->ReadU64(address);
const u64 value = accessors->ReadU64(guard, address);
return QStringLiteral("%1").arg(value, 16, 16, QLatin1Char('0'));
}
case Type::Unsigned8:
return QString::number(accessors->ReadU8(address));
return QString::number(accessors->ReadU8(guard, address));
case Type::Unsigned16:
return QString::number(accessors->ReadU16(address));
return QString::number(accessors->ReadU16(guard, address));
case Type::Unsigned32:
return QString::number(accessors->ReadU32(address));
return QString::number(accessors->ReadU32(guard, address));
case Type::Signed8:
return QString::number(Common::BitCast<s8>(accessors->ReadU8(address)));
return QString::number(Common::BitCast<s8>(accessors->ReadU8(guard, address)));
case Type::Signed16:
return QString::number(Common::BitCast<s16>(accessors->ReadU16(address)));
return QString::number(Common::BitCast<s16>(accessors->ReadU16(guard, address)));
case Type::Signed32:
return QString::number(Common::BitCast<s32>(accessors->ReadU32(address)));
return QString::number(Common::BitCast<s32>(accessors->ReadU32(guard, address)));
case Type::Float32:
{
QString string = QString::number(accessors->ReadF32(address), 'g', 4);
QString string = QString::number(accessors->ReadF32(guard, address), 'g', 4);
// Align to first digit.
if (!string.startsWith(QLatin1Char('-')))
string.prepend(QLatin1Char(' '));
@ -511,7 +536,8 @@ QString MemoryViewWidget::ValueToString(u32 address, Type type)
}
case Type::Double:
{
QString string = QString::number(Common::BitCast<double>(accessors->ReadU64(address)), 'g', 4);
QString string =
QString::number(Common::BitCast<double>(accessors->ReadU64(guard, address)), 'g', 4);
// Align to first digit.
if (!string.startsWith(QLatin1Char('-')))
string.prepend(QLatin1Char(' '));
@ -519,7 +545,7 @@ QString MemoryViewWidget::ValueToString(u32 address, Type type)
return string;
}
default:
return QStringLiteral("-");
return INVALID_MEMORY;
}
}
@ -823,7 +849,11 @@ void MemoryViewWidget::OnCopyHex(u32 addr)
const auto length = GetTypeSize(m_type);
const AddressSpace::Accessors* accessors = AddressSpace::GetAccessors(m_address_space);
u64 value = accessors->ReadU64(addr);
const u64 value = [addr, accessors] {
Core::CPUThreadGuard guard;
return accessors->ReadU64(guard, addr);
}();
QApplication::clipboard()->setText(
QStringLiteral("%1").arg(value, sizeof(u64) * 2, 16, QLatin1Char('0')).left(length * 2));
@ -839,10 +869,14 @@ void MemoryViewWidget::OnContextMenu(const QPoint& pos)
return;
const u32 addr = item_selected->data(USER_ROLE_CELL_ADDRESS).toUInt();
const AddressSpace::Accessors* accessors = AddressSpace::GetAccessors(m_address_space);
const bool item_has_value =
item_selected->data(USER_ROLE_VALUE_TYPE).toInt() != static_cast<int>(Type::Null) &&
accessors->IsValidAddress(addr);
[this, addr] {
const AddressSpace::Accessors* accessors = AddressSpace::GetAccessors(m_address_space);
Core::CPUThreadGuard guard;
return accessors->IsValidAddress(guard, addr);
}();
auto* menu = new QMenu(this);

View File

@ -15,6 +15,11 @@ namespace AddressSpace
enum class Type;
}
namespace Core
{
class CPUThreadGuard;
}
class MemoryViewTable;
class MemoryViewWidget final : public QWidget
@ -75,9 +80,10 @@ private:
void OnCopyHex(u32 addr);
void UpdateBreakpointTags();
void UpdateColumns();
void UpdateColumns(const Core::CPUThreadGuard* guard);
void ScrollbarActionTriggered(int action);
void ScrollbarSliderReleased();
QString ValueToString(u32 address, Type type);
QString ValueToString(const Core::CPUThreadGuard& guard, u32 address, Type type);
MemoryViewTable* m_table;
QScrollBar* m_scrollbar;

View File

@ -495,7 +495,9 @@ void MemoryWidget::SetAddress(u32 address)
{
AddressSpace::Accessors* accessors =
AddressSpace::GetAccessors(m_memory_view->GetAddressSpace());
good = accessors->IsValidAddress(current_addr);
Core::CPUThreadGuard guard;
good = accessors->IsValidAddress(guard, current_addr);
}
if (m_search_address->findText(current_text) == -1 && good)
@ -651,17 +653,20 @@ void MemoryWidget::OnSetValue()
return;
}
Core::CPUThreadGuard guard;
AddressSpace::Accessors* accessors = AddressSpace::GetAccessors(m_memory_view->GetAddressSpace());
u32 end_address = target_addr.address + static_cast<u32>(bytes.size()) - 1;
if (!accessors->IsValidAddress(target_addr.address) || !accessors->IsValidAddress(end_address))
if (!accessors->IsValidAddress(guard, target_addr.address) ||
!accessors->IsValidAddress(guard, end_address))
{
ModalMessageBox::critical(this, tr("Error"), tr("Target address range is invalid."));
return;
}
for (const char c : bytes)
accessors->WriteU8(target_addr.address++, static_cast<u8>(c));
accessors->WriteU8(guard, target_addr.address++, static_cast<u8>(c));
Update();
}
@ -710,8 +715,10 @@ void MemoryWidget::OnSetValueFromFile()
AddressSpace::Accessors* accessors = AddressSpace::GetAccessors(m_memory_view->GetAddressSpace());
Core::CPUThreadGuard guard;
for (u8 b : file_contents)
accessors->WriteU8(target_addr.address++, b);
accessors->WriteU8(guard, target_addr.address++, b);
Update();
}
@ -822,11 +829,15 @@ void MemoryWidget::FindValue(bool next)
target_addr.address += next ? 1 : -1;
}
AddressSpace::Accessors* accessors = AddressSpace::GetAccessors(m_memory_view->GetAddressSpace());
const std::optional<u32> found_addr = [&] {
AddressSpace::Accessors* accessors =
AddressSpace::GetAccessors(m_memory_view->GetAddressSpace());
const auto found_addr =
accessors->Search(target_addr.address, reinterpret_cast<const u8*>(search_for.data()),
static_cast<u32>(search_for.size()), next);
Core::CPUThreadGuard guard;
return accessors->Search(guard, target_addr.address,
reinterpret_cast<const u8*>(search_for.data()),
static_cast<u32>(search_for.size()), next);
}();
if (found_addr.has_value())
{

View File

@ -20,6 +20,11 @@ class QRadioButton;
class QShowEvent;
class QSplitter;
namespace Core
{
class CPUThreadGuard;
}
class MemoryWidget : public QDockWidget
{
Q_OBJECT

View File

@ -295,7 +295,11 @@ void RegisterWidget::AutoStep(const std::string& reg) const
while (true)
{
const AutoStepResults results = trace.AutoStepping(true);
const AutoStepResults results = [&trace] {
Core::CPUThreadGuard guard;
return trace.AutoStepping(guard, true);
}();
emit Host::GetInstance()->UpdateDisasmDialog();
if (!results.timed_out)

View File

@ -256,7 +256,9 @@ void ThreadWidget::Update()
{
m_thread_table->setRowCount(0);
UpdateThreadContext({});
UpdateThreadCallstack({});
Core::CPUThreadGuard guard;
UpdateThreadCallstack(guard, {});
}
if (emu_state != Core::State::Paused)
return;
@ -264,8 +266,8 @@ void ThreadWidget::Update()
const auto format_hex = [](u32 value) {
return QStringLiteral("%1").arg(value, 8, 16, QLatin1Char('0'));
};
const auto format_hex_from = [&format_hex](u32 addr) {
addr = PowerPC::HostIsRAMAddress(addr) ? PowerPC::HostRead_U32(addr) : 0;
const auto format_hex_from = [&format_hex](const Core::CPUThreadGuard& guard, u32 addr) {
addr = PowerPC::HostIsRAMAddress(guard, addr) ? PowerPC::HostRead_U32(guard, addr) : 0;
return format_hex(addr);
};
const auto get_state = [](u16 thread_state) {
@ -298,35 +300,41 @@ void ThreadWidget::Update()
.arg(start, 8, 16, QLatin1Char('0'));
};
// YAGCD - Section 4.2.1.4 Dolphin OS Globals
m_current_context->setText(format_hex_from(0x800000D4));
m_current_thread->setText(format_hex_from(0x800000E4));
m_default_thread->setText(format_hex_from(0x800000D8));
m_queue_head->setText(format_hex_from(0x800000DC));
m_queue_tail->setText(format_hex_from(0x800000E0));
// Thread group
m_threads = PowerPC::debug_interface.GetThreads();
int i = 0;
m_thread_table->setRowCount(i);
for (const auto& thread : m_threads)
{
m_thread_table->insertRow(i);
m_thread_table->setItem(i, 0, new QTableWidgetItem(format_hex(thread->GetAddress())));
m_thread_table->setItem(i, 1, new QTableWidgetItem(get_state(thread->GetState())));
m_thread_table->setItem(i, 2, new QTableWidgetItem(QString::number(thread->IsDetached())));
m_thread_table->setItem(i, 3, new QTableWidgetItem(QString::number(thread->IsSuspended())));
m_thread_table->setItem(i, 4,
new QTableWidgetItem(get_priority(thread->GetBasePriority(),
thread->GetEffectivePriority())));
m_thread_table->setItem(
i, 5, new QTableWidgetItem(get_stack(thread->GetStackEnd(), thread->GetStackStart())));
m_thread_table->setItem(i, 6, new QTableWidgetItem(QString::number(thread->GetErrno())));
m_thread_table->setItem(i, 7,
new QTableWidgetItem(QString::fromStdString(thread->GetSpecific())));
i += 1;
Core::CPUThreadGuard guard;
// YAGCD - Section 4.2.1.4 Dolphin OS Globals
m_current_context->setText(format_hex_from(guard, 0x800000D4));
m_current_thread->setText(format_hex_from(guard, 0x800000E4));
m_default_thread->setText(format_hex_from(guard, 0x800000D8));
m_queue_head->setText(format_hex_from(guard, 0x800000DC));
m_queue_tail->setText(format_hex_from(guard, 0x800000E0));
// Thread group
m_threads = PowerPC::debug_interface.GetThreads(guard);
int i = 0;
m_thread_table->setRowCount(i);
for (const auto& thread : m_threads)
{
m_thread_table->insertRow(i);
m_thread_table->setItem(i, 0, new QTableWidgetItem(format_hex(thread->GetAddress())));
m_thread_table->setItem(i, 1, new QTableWidgetItem(get_state(thread->GetState())));
m_thread_table->setItem(i, 2, new QTableWidgetItem(QString::number(thread->IsDetached())));
m_thread_table->setItem(i, 3, new QTableWidgetItem(QString::number(thread->IsSuspended())));
m_thread_table->setItem(i, 4,
new QTableWidgetItem(get_priority(thread->GetBasePriority(),
thread->GetEffectivePriority())));
m_thread_table->setItem(
i, 5, new QTableWidgetItem(get_stack(thread->GetStackEnd(), thread->GetStackStart())));
m_thread_table->setItem(i, 6, new QTableWidgetItem(QString::number(thread->GetErrno())));
m_thread_table->setItem(
i, 7, new QTableWidgetItem(QString::fromStdString(thread->GetSpecific(guard))));
i += 1;
}
}
m_thread_table->resizeColumnsToContents();
m_thread_table->resizeRowsToContents();
@ -425,7 +433,8 @@ void ThreadWidget::UpdateThreadContext(const Common::Debug::PartialContext& cont
m_context_table->resizeColumnsToContents();
}
void ThreadWidget::UpdateThreadCallstack(const Common::Debug::PartialContext& context)
void ThreadWidget::UpdateThreadCallstack(const Core::CPUThreadGuard& guard,
const Common::Debug::PartialContext& context)
{
m_callstack_table->setRowCount(0);
@ -439,13 +448,13 @@ void ThreadWidget::UpdateThreadCallstack(const Common::Debug::PartialContext& co
u32 sp = context.gpr->at(1);
for (int i = 0; i < 16; i++)
{
if (sp == 0 || sp == 0xffffffff || !PowerPC::HostIsRAMAddress(sp))
if (sp == 0 || sp == 0xffffffff || !PowerPC::HostIsRAMAddress(guard, sp))
break;
m_callstack_table->insertRow(i);
m_callstack_table->setItem(i, 0, new QTableWidgetItem(format_hex(sp)));
if (PowerPC::HostIsRAMAddress(sp + 4))
if (PowerPC::HostIsRAMAddress(guard, sp + 4))
{
const u32 lr_save = PowerPC::HostRead_U32(sp + 4);
const u32 lr_save = PowerPC::HostRead_U32(guard, sp + 4);
m_callstack_table->setItem(i, 2, new QTableWidgetItem(format_hex(lr_save)));
m_callstack_table->setItem(i, 3,
new QTableWidgetItem(QString::fromStdString(
@ -455,18 +464,19 @@ void ThreadWidget::UpdateThreadCallstack(const Common::Debug::PartialContext& co
{
m_callstack_table->setItem(i, 2, new QTableWidgetItem(QStringLiteral("--------")));
}
sp = PowerPC::HostRead_U32(sp);
sp = PowerPC::HostRead_U32(guard, sp);
m_callstack_table->setItem(i, 1, new QTableWidgetItem(format_hex(sp)));
}
}
void ThreadWidget::OnSelectionChanged(int row)
{
Core::CPUThreadGuard guard;
Common::Debug::PartialContext context;
if (row >= 0 && size_t(row) < m_threads.size())
context = m_threads[row]->GetContext();
context = m_threads[row]->GetContext(guard);
UpdateThreadContext(context);
UpdateThreadCallstack(context);
UpdateThreadCallstack(guard, context);
}

View File

@ -47,7 +47,8 @@ private:
void Update();
void UpdateThreadContext(const Common::Debug::PartialContext& context);
void UpdateThreadCallstack(const Common::Debug::PartialContext& context);
void UpdateThreadCallstack(const Core::CPUThreadGuard& guard,
const Common::Debug::PartialContext& context);
void OnSelectionChanged(int row);
QGroupBox* m_state;

View File

@ -161,6 +161,8 @@ void WatchWidget::Update()
// i18n: Floating-point (non-integer) number
tr("Float"), tr("Locked")});
Core::CPUThreadGuard guard;
for (int i = 0; i < size; i++)
{
const auto& entry = PowerPC::debug_interface.GetWatch(i);
@ -181,18 +183,18 @@ void WatchWidget::Update()
QBrush brush = QPalette().brush(QPalette::Text);
if (!Core::IsRunning() || !PowerPC::HostIsRAMAddress(entry.address))
if (!Core::IsRunning() || !PowerPC::HostIsRAMAddress(guard, entry.address))
brush.setColor(Qt::red);
if (Core::IsRunning())
{
if (PowerPC::HostIsRAMAddress(entry.address))
if (PowerPC::HostIsRAMAddress(guard, entry.address))
{
hex->setText(QStringLiteral("%1").arg(PowerPC::HostRead_U32(entry.address), 8, 16,
hex->setText(QStringLiteral("%1").arg(PowerPC::HostRead_U32(guard, entry.address), 8, 16,
QLatin1Char('0')));
decimal->setText(QString::number(PowerPC::HostRead_U32(entry.address)));
string->setText(QString::fromStdString(PowerPC::HostGetString(entry.address, 32)));
floatValue->setText(QString::number(PowerPC::HostRead_F32(entry.address)));
decimal->setText(QString::number(PowerPC::HostRead_U32(guard, entry.address)));
string->setText(QString::fromStdString(PowerPC::HostGetString(guard, entry.address, 32)));
floatValue->setText(QString::number(PowerPC::HostRead_F32(guard, entry.address)));
lockValue->setCheckState(entry.locked ? Qt::Checked : Qt::Unchecked);
}
}
@ -279,11 +281,13 @@ void WatchWidget::OnLoad()
return;
}
Core::CPUThreadGuard guard;
if (ini.GetLines("Watches", &watches, false))
{
for (const auto& watch : PowerPC::debug_interface.GetWatches())
{
PowerPC::debug_interface.UnsetPatch(watch.address);
PowerPC::debug_interface.UnsetPatch(guard, watch.address);
}
PowerPC::debug_interface.ClearWatches();
PowerPC::debug_interface.LoadWatchesFromStrings(watches);
@ -387,17 +391,19 @@ void WatchWidget::OnItemChanged(QTableWidgetItem* item)
if (good)
{
Core::CPUThreadGuard guard;
if (column == COLUMN_INDEX_ADDRESS)
{
const auto& watch = PowerPC::debug_interface.GetWatch(row);
PowerPC::debug_interface.UnsetPatch(watch.address);
PowerPC::debug_interface.UnsetPatch(guard, watch.address);
PowerPC::debug_interface.UpdateWatchAddress(row, value);
if (watch.locked)
LockWatchAddress(value);
LockWatchAddress(guard, value);
}
else
{
PowerPC::HostWrite_U32(value, PowerPC::debug_interface.GetWatch(row).address);
PowerPC::HostWrite_U32(guard, value, PowerPC::debug_interface.GetWatch(row).address);
}
}
else
@ -410,10 +416,11 @@ void WatchWidget::OnItemChanged(QTableWidgetItem* item)
{
PowerPC::debug_interface.UpdateWatchLockedState(row, item->checkState() == Qt::Checked);
const auto& watch = PowerPC::debug_interface.GetWatch(row);
Core::CPUThreadGuard guard;
if (watch.locked)
LockWatchAddress(watch.address);
LockWatchAddress(guard, watch.address);
else
PowerPC::debug_interface.UnsetPatch(watch.address);
PowerPC::debug_interface.UnsetPatch(guard, watch.address);
break;
}
}
@ -422,9 +429,9 @@ void WatchWidget::OnItemChanged(QTableWidgetItem* item)
}
}
void WatchWidget::LockWatchAddress(u32 address)
void WatchWidget::LockWatchAddress(const Core::CPUThreadGuard& guard, u32 address)
{
const std::string memory_data_as_string = PowerPC::HostGetString(address, 4);
const std::string memory_data_as_string = PowerPC::HostGetString(guard, address, 4);
std::vector<u8> bytes;
for (const char c : memory_data_as_string)
@ -432,42 +439,48 @@ void WatchWidget::LockWatchAddress(u32 address)
bytes.push_back(static_cast<u8>(c));
}
PowerPC::debug_interface.SetFramePatch(address, bytes);
PowerPC::debug_interface.SetFramePatch(guard, address, bytes);
}
void WatchWidget::DeleteSelectedWatches()
{
std::vector<int> row_indices;
for (const auto& index : m_table->selectionModel()->selectedRows())
{
const auto* item = m_table->item(index.row(), index.column());
const auto row_variant = item->data(Qt::UserRole);
if (row_variant.isNull())
continue;
Core::CPUThreadGuard guard;
std::vector<int> row_indices;
for (const auto& index : m_table->selectionModel()->selectedRows())
{
const auto* item = m_table->item(index.row(), index.column());
const auto row_variant = item->data(Qt::UserRole);
if (row_variant.isNull())
continue;
row_indices.push_back(row_variant.toInt());
}
row_indices.push_back(row_variant.toInt());
}
// Sort greatest to smallest, so we
// don't stomp on existing indices
std::sort(row_indices.begin(), row_indices.end(), std::greater{});
for (const int row : row_indices)
{
DeleteWatch(row);
// Sort greatest to smallest, so we don't stomp on existing indices
std::sort(row_indices.begin(), row_indices.end(), std::greater{});
for (const int row : row_indices)
{
DeleteWatch(guard, row);
}
}
Update();
}
void WatchWidget::DeleteWatch(int row)
void WatchWidget::DeleteWatch(const Core::CPUThreadGuard& guard, int row)
{
PowerPC::debug_interface.UnsetPatch(PowerPC::debug_interface.GetWatch(row).address);
PowerPC::debug_interface.UnsetPatch(guard, PowerPC::debug_interface.GetWatch(row).address);
PowerPC::debug_interface.RemoveWatch(row);
}
void WatchWidget::DeleteWatchAndUpdate(int row)
{
DeleteWatch(row);
{
Core::CPUThreadGuard guard;
DeleteWatch(guard, row);
}
Update();
}
@ -489,18 +502,21 @@ void WatchWidget::AddWatch(QString name, u32 addr)
void WatchWidget::LockSelectedWatches()
{
for (const auto& index : m_table->selectionModel()->selectedRows())
{
const auto* item = m_table->item(index.row(), index.column());
const auto row_variant = item->data(Qt::UserRole);
if (row_variant.isNull())
continue;
const int row = row_variant.toInt();
const auto& watch = PowerPC::debug_interface.GetWatch(row);
if (watch.locked)
continue;
PowerPC::debug_interface.UpdateWatchLockedState(row, true);
LockWatchAddress(watch.address);
Core::CPUThreadGuard guard;
for (const auto& index : m_table->selectionModel()->selectedRows())
{
const auto* item = m_table->item(index.row(), index.column());
const auto row_variant = item->data(Qt::UserRole);
if (row_variant.isNull())
continue;
const int row = row_variant.toInt();
const auto& watch = PowerPC::debug_interface.GetWatch(row);
if (watch.locked)
continue;
PowerPC::debug_interface.UpdateWatchLockedState(row, true);
LockWatchAddress(guard, watch.address);
}
}
Update();
@ -508,18 +524,21 @@ void WatchWidget::LockSelectedWatches()
void WatchWidget::UnlockSelectedWatches()
{
for (const auto& index : m_table->selectionModel()->selectedRows())
{
const auto* item = m_table->item(index.row(), index.column());
const auto row_variant = item->data(Qt::UserRole);
if (row_variant.isNull())
continue;
const int row = row_variant.toInt();
const auto& watch = PowerPC::debug_interface.GetWatch(row);
if (!watch.locked)
continue;
PowerPC::debug_interface.UpdateWatchLockedState(row, false);
PowerPC::debug_interface.UnsetPatch(watch.address);
Core::CPUThreadGuard guard;
for (const auto& index : m_table->selectionModel()->selectedRows())
{
const auto* item = m_table->item(index.row(), index.column());
const auto row_variant = item->data(Qt::UserRole);
if (row_variant.isNull())
continue;
const int row = row_variant.toInt();
const auto& watch = PowerPC::debug_interface.GetWatch(row);
if (!watch.locked)
continue;
PowerPC::debug_interface.UpdateWatchLockedState(row, false);
PowerPC::debug_interface.UnsetPatch(guard, watch.address);
}
}
Update();

View File

@ -14,6 +14,11 @@ class QTableWidget;
class QTableWidgetItem;
class QToolBar;
namespace Core
{
class CPUThreadGuard;
};
class WatchWidget : public QDockWidget
{
Q_OBJECT
@ -46,9 +51,9 @@ private:
void ShowContextMenu();
void OnItemChanged(QTableWidgetItem* item);
void LockWatchAddress(u32 address);
void LockWatchAddress(const Core::CPUThreadGuard& guard, u32 address);
void DeleteSelectedWatches();
void DeleteWatch(int row);
void DeleteWatch(const Core::CPUThreadGuard& guard, int row);
void DeleteWatchAndUpdate(int row);
void AddWatchBreakpoint(int row);
void ShowInMemory(int row);

View File

@ -1186,25 +1186,29 @@ void MenuBar::ClearSymbols()
void MenuBar::GenerateSymbolsFromAddress()
{
Core::CPUThreadGuard guard;
auto& system = Core::System::GetInstance();
auto& memory = system.GetMemory();
PPCAnalyst::FindFunctions(Memory::MEM1_BASE_ADDR,
PPCAnalyst::FindFunctions(guard, Memory::MEM1_BASE_ADDR,
Memory::MEM1_BASE_ADDR + memory.GetRamSizeReal(), &g_symbolDB);
emit NotifySymbolsUpdated();
}
void MenuBar::GenerateSymbolsFromSignatureDB()
{
Core::CPUThreadGuard guard;
auto& system = Core::System::GetInstance();
auto& memory = system.GetMemory();
PPCAnalyst::FindFunctions(Memory::MEM1_BASE_ADDR,
PPCAnalyst::FindFunctions(guard, Memory::MEM1_BASE_ADDR,
Memory::MEM1_BASE_ADDR + memory.GetRamSizeReal(), &g_symbolDB);
SignatureDB db(SignatureDB::HandlerType::DSY);
if (db.Load(File::GetSysDirectory() + TOTALDB))
{
db.Apply(&g_symbolDB);
db.Apply(guard, &g_symbolDB);
ModalMessageBox::information(
this, tr("Information"),
tr("Generated symbol names from '%1'").arg(QString::fromStdString(TOTALDB)));
@ -1240,10 +1244,12 @@ void MenuBar::GenerateSymbolsFromRSO()
return;
}
Core::CPUThreadGuard guard;
RSOChainView rso_chain;
if (rso_chain.Load(static_cast<u32>(address)))
if (rso_chain.Load(guard, static_cast<u32>(address)))
{
rso_chain.Apply(&g_symbolDB);
rso_chain.Apply(guard, &g_symbolDB);
emit NotifySymbolsUpdated();
}
else
@ -1293,9 +1299,12 @@ void MenuBar::GenerateSymbolsFromRSOAuto()
RSOChainView rso_chain;
const u32 address = item.mid(0, item.indexOf(QLatin1Char(' '))).toUInt(nullptr, 16);
if (rso_chain.Load(address))
Core::CPUThreadGuard guard;
if (rso_chain.Load(guard, address))
{
rso_chain.Apply(&g_symbolDB);
rso_chain.Apply(guard, &g_symbolDB);
emit NotifySymbolsUpdated();
}
else
@ -1306,6 +1315,8 @@ void MenuBar::GenerateSymbolsFromRSOAuto()
RSOVector MenuBar::DetectRSOModules(ParallelProgressDialog& progress)
{
Core::CPUThreadGuard guard;
constexpr std::array<std::string_view, 2> search_for = {".elf", ".plf"};
const AddressSpace::Accessors* accessors =
@ -1324,8 +1335,8 @@ RSOVector MenuBar::DetectRSOModules(ParallelProgressDialog& progress)
return matches;
}
auto found_addr =
accessors->Search(next, reinterpret_cast<const u8*>(str.data()), str.size() + 1, true);
auto found_addr = accessors->Search(guard, next, reinterpret_cast<const u8*>(str.data()),
str.size() + 1, true);
if (!found_addr.has_value())
break;
@ -1334,13 +1345,13 @@ RSOVector MenuBar::DetectRSOModules(ParallelProgressDialog& progress)
// Non-null data can precede the module name.
// Get the maximum name length that a module could have.
auto get_max_module_name_len = [found_addr] {
auto get_max_module_name_len = [&guard, found_addr] {
constexpr u32 MODULE_NAME_MAX_LENGTH = 260;
u32 len = 0;
for (; len < MODULE_NAME_MAX_LENGTH; ++len)
{
const auto res = PowerPC::HostRead_U8(*found_addr - (len + 1));
const auto res = PowerPC::HostRead_U8(guard, *found_addr - (len + 1));
if (!std::isprint(res))
{
break;
@ -1375,12 +1386,12 @@ RSOVector MenuBar::DetectRSOModules(ParallelProgressDialog& progress)
// Get the field (Module Name Offset) that point to the string
const auto module_name_offset_addr =
accessors->Search(lookup_addr, ref.data(), ref.size(), false);
accessors->Search(guard, lookup_addr, ref.data(), ref.size(), false);
if (!module_name_offset_addr.has_value())
continue;
// The next 4 bytes should be the module name length
module_name_length = accessors->ReadU32(*module_name_offset_addr + 4);
module_name_length = accessors->ReadU32(guard, *module_name_offset_addr + 4);
if (module_name_length == max_name_length - i + str.length())
{
found_addr = module_name_offset_addr;
@ -1392,11 +1403,11 @@ RSOVector MenuBar::DetectRSOModules(ParallelProgressDialog& progress)
if (!found)
continue;
const auto module_name_offset = accessors->ReadU32(*found_addr);
const auto module_name_offset = accessors->ReadU32(guard, *found_addr);
// Go to the beginning of the RSO header
matches.emplace_back(*found_addr - 16,
PowerPC::HostGetString(module_name_offset, module_name_length));
PowerPC::HostGetString(guard, module_name_offset, module_name_length));
progress.SetLabelText(tr("Modules found: %1").arg(matches.size()));
}
@ -1416,11 +1427,16 @@ void MenuBar::LoadSymbolMap()
if (!map_exists)
{
g_symbolDB.Clear();
PPCAnalyst::FindFunctions(Memory::MEM1_BASE_ADDR + 0x1300000,
Memory::MEM1_BASE_ADDR + memory.GetRamSizeReal(), &g_symbolDB);
SignatureDB db(SignatureDB::HandlerType::DSY);
if (db.Load(File::GetSysDirectory() + TOTALDB))
db.Apply(&g_symbolDB);
{
Core::CPUThreadGuard guard;
PPCAnalyst::FindFunctions(guard, Memory::MEM1_BASE_ADDR + 0x1300000,
Memory::MEM1_BASE_ADDR + memory.GetRamSizeReal(), &g_symbolDB);
SignatureDB db(SignatureDB::HandlerType::DSY);
if (db.Load(File::GetSysDirectory() + TOTALDB))
db.Apply(guard, &g_symbolDB);
}
ModalMessageBox::warning(this, tr("Warning"),
tr("'%1' not found, scanning for common functions instead")
@ -1505,7 +1521,13 @@ void MenuBar::SaveCode()
const std::string path =
writable_map_file.substr(0, writable_map_file.find_last_of('.')) + "_code.map";
if (!g_symbolDB.SaveCodeMap(path))
bool success;
{
Core::CPUThreadGuard guard;
success = g_symbolDB.SaveCodeMap(guard, path);
}
if (!success)
{
ModalMessageBox::warning(
this, tr("Error"),
@ -1515,7 +1537,9 @@ void MenuBar::SaveCode()
bool MenuBar::TryLoadMapFile(const QString& path, const bool bad)
{
if (!g_symbolDB.LoadMap(path.toStdString(), bad))
Core::CPUThreadGuard guard;
if (!g_symbolDB.LoadMap(guard, path.toStdString(), bad))
{
ModalMessageBox::warning(this, tr("Error"), tr("Failed to load map file '%1'").arg(path));
return false;
@ -1596,7 +1620,10 @@ void MenuBar::ApplySignatureFile()
const std::string load_path = file.toStdString();
SignatureDB db(load_path);
db.Load(load_path);
db.Apply(&g_symbolDB);
{
Core::CPUThreadGuard guard;
db.Apply(guard, &g_symbolDB);
}
db.List();
auto& system = Core::System::GetInstance();
HLE::PatchFunctions(system);
@ -1665,12 +1692,14 @@ void MenuBar::SearchInstruction()
auto& system = Core::System::GetInstance();
auto& memory = system.GetMemory();
Core::CPUThreadGuard guard;
bool found = false;
for (u32 addr = Memory::MEM1_BASE_ADDR; addr < Memory::MEM1_BASE_ADDR + memory.GetRamSizeReal();
addr += 4)
{
const auto ins_name =
QString::fromStdString(PPCTables::GetInstructionName(PowerPC::HostRead_U32(addr)));
QString::fromStdString(PPCTables::GetInstructionName(PowerPC::HostRead_U32(guard, addr)));
if (op == ins_name)
{
NOTICE_LOG_FMT(POWERPC, "Found {} at {:08x}", op.toStdString(), addr);