Merge pull request #11554 from JosJuice/host-lock-cpu

DolphinQt: Properly lock CPU before accessing emulated memory
This commit is contained in:
Scott Mansell 2023-02-13 16:08:36 +13:00 committed by GitHub
commit a4729a026f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
79 changed files with 1799 additions and 1187 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

@ -322,14 +322,17 @@ void CodeWidget::Update()
void CodeWidget::UpdateCallstack()
{
if (Core::GetState() == Core::State::Starting)
return;
m_callstack_list->clear();
if (Core::GetState() != Core::State::Paused)
return;
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);