diff --git a/Source/Core/Core/PowerPC/MMU.cpp b/Source/Core/Core/PowerPC/MMU.cpp index 30dbb8105e..e5d4bebdb8 100644 --- a/Source/Core/Core/PowerPC/MMU.cpp +++ b/Source/Core/Core/PowerPC/MMU.cpp @@ -3,6 +3,7 @@ #include "Core/PowerPC/MMU.h" +#include #include #include #include @@ -427,6 +428,36 @@ u32 HostRead_Instruction(const u32 address) return ReadFromHardware(address); } +TryReadResult HostTryReadInstruction(const u32 address, RequestedAddressSpace space) +{ + if (!HostIsInstructionRAMAddress(address, space)) + return TryReadResult(); + + switch (space) + { + case RequestedAddressSpace::Effective: + { + const u32 value = ReadFromHardware(address); + return TryReadResult(!!MSR.DR, value); + } + case RequestedAddressSpace::Physical: + { + const u32 value = ReadFromHardware(address); + return TryReadResult(false, value); + } + case RequestedAddressSpace::Virtual: + { + if (!MSR.DR) + return TryReadResult(); + const u32 value = ReadFromHardware(address); + return TryReadResult(true, value); + } + } + + assert(0); + return TryReadResult(); +} + static void Memcheck(u32 address, u32 var, bool write, size_t size) { if (PowerPC::memchecks.HasAny()) @@ -499,6 +530,73 @@ float Read_F32(const u32 address) return Common::BitCast(integral); } +template +static TryReadResult HostTryReadUX(const u32 address, RequestedAddressSpace space) +{ + if (!HostIsRAMAddress(address, space)) + return TryReadResult(); + + switch (space) + { + case RequestedAddressSpace::Effective: + { + T value = ReadFromHardware(address); + return TryReadResult(!!MSR.DR, std::move(value)); + } + case RequestedAddressSpace::Physical: + { + T value = ReadFromHardware(address); + return TryReadResult(false, std::move(value)); + } + case RequestedAddressSpace::Virtual: + { + if (!MSR.DR) + return TryReadResult(); + T value = ReadFromHardware(address); + return TryReadResult(true, std::move(value)); + } + } + + assert(0); + return TryReadResult(); +} + +TryReadResult HostTryReadU8(u32 address, RequestedAddressSpace space) +{ + return HostTryReadUX(address, space); +} + +TryReadResult HostTryReadU16(u32 address, RequestedAddressSpace space) +{ + return HostTryReadUX(address, space); +} + +TryReadResult HostTryReadU32(u32 address, RequestedAddressSpace space) +{ + return HostTryReadUX(address, space); +} + +TryReadResult HostTryReadU64(u32 address, RequestedAddressSpace space) +{ + return HostTryReadUX(address, space); +} + +TryReadResult HostTryReadF32(u32 address, RequestedAddressSpace space) +{ + const auto result = HostTryReadUX(address, space); + if (!result) + return TryReadResult(); + return TryReadResult(result.translated, Common::BitCast(result.value)); +} + +TryReadResult HostTryReadF64(u32 address, RequestedAddressSpace space) +{ + const auto result = HostTryReadUX(address, space); + if (!result) + return TryReadResult(); + return TryReadResult(result.translated, Common::BitCast(result.value)); +} + u32 Read_U8_ZX(const u32 address) { return Read_U8(address); @@ -623,6 +721,63 @@ void HostWrite_F64(const double var, const u32 address) HostWrite_U64(integral, address); } +template +static TryWriteResult HostTryWriteUX(const T var, const u32 address, RequestedAddressSpace space) +{ + if (!HostIsRAMAddress(address, space)) + return TryWriteResult(); + + switch (space) + { + case RequestedAddressSpace::Effective: + WriteToHardware(address, var); + return TryWriteResult(!!MSR.DR); + case RequestedAddressSpace::Physical: + WriteToHardware(address, var); + return TryWriteResult(false); + case RequestedAddressSpace::Virtual: + if (!MSR.DR) + return TryWriteResult(); + WriteToHardware(address, var); + return TryWriteResult(true); + } + + assert(0); + return TryWriteResult(); +} + +TryWriteResult HostTryWriteU8(const u8 var, const u32 address, RequestedAddressSpace space) +{ + return HostTryWriteUX(var, address, space); +} + +TryWriteResult HostTryWriteU16(const u16 var, const u32 address, RequestedAddressSpace space) +{ + return HostTryWriteUX(var, address, space); +} + +TryWriteResult HostTryWriteU32(const u32 var, const u32 address, RequestedAddressSpace space) +{ + return HostTryWriteUX(var, address, space); +} + +TryWriteResult HostTryWriteU64(const u64 var, const u32 address, RequestedAddressSpace space) +{ + return HostTryWriteUX(var, address, space); +} + +TryWriteResult HostTryWriteF32(const float var, const u32 address, RequestedAddressSpace space) +{ + const u32 integral = Common::BitCast(var); + return HostTryWriteU32(integral, address, space); +} + +TryWriteResult HostTryWriteF64(const double var, const u32 address, RequestedAddressSpace space) +{ + const u64 integral = Common::BitCast(var); + return HostTryWriteU64(integral, address, space); +} + std::string HostGetString(u32 address, size_t size) { std::string s; @@ -639,6 +794,27 @@ std::string HostGetString(u32 address, size_t size) return s; } +TryReadResult HostTryReadString(u32 address, size_t size, RequestedAddressSpace space) +{ + auto c = HostTryReadU8(address, space); + if (!c) + return TryReadResult(); + if (c.value == 0) + return TryReadResult(c.translated, ""); + + std::string s; + s += static_cast(c.value); + while (size == 0 || s.length() < size) + { + ++address; + const auto res = HostTryReadU8(address, space); + if (!res || res.value == 0) + break; + s += static_cast(res.value); + } + return TryReadResult(c.translated, std::move(s)); +} + bool IsOptimizableRAMAddress(const u32 address) { if (PowerPC::memchecks.HasAny()) @@ -688,15 +864,44 @@ static bool IsRAMAddress(u32 address, bool translate) return false; } -bool HostIsRAMAddress(u32 address) +bool HostIsRAMAddress(u32 address, RequestedAddressSpace space) { - return IsRAMAddress(address, MSR.DR); + switch (space) + { + case RequestedAddressSpace::Effective: + return IsRAMAddress(address, MSR.DR); + case RequestedAddressSpace::Physical: + return IsRAMAddress(address, false); + case RequestedAddressSpace::Virtual: + if (!MSR.DR) + return false; + return IsRAMAddress(address, true); + } + + assert(0); + return false; } -bool HostIsInstructionRAMAddress(u32 address) +bool HostIsInstructionRAMAddress(u32 address, RequestedAddressSpace space) { // Instructions are always 32bit aligned. - return !(address & 3) && IsRAMAddress(address, MSR.IR); + if (!(address & 3)) + return false; + + switch (space) + { + case RequestedAddressSpace::Effective: + return IsRAMAddress(address, MSR.IR); + case RequestedAddressSpace::Physical: + return IsRAMAddress(address, false); + case RequestedAddressSpace::Virtual: + if (!MSR.IR) + return false; + return IsRAMAddress(address, true); + } + + assert(0); + return false; } void DMA_LCToMemory(const u32 mem_address, const u32 cache_address, const u32 num_blocks) diff --git a/Source/Core/Core/PowerPC/MMU.h b/Source/Core/Core/PowerPC/MMU.h index 82c4f8e7eb..9191cea2ab 100644 --- a/Source/Core/Core/PowerPC/MMU.h +++ b/Source/Core/Core/PowerPC/MMU.h @@ -14,7 +14,19 @@ namespace PowerPC { // Routines for debugger UI, cheats, etc. to access emulated memory from the // perspective of the CPU. Not for use by core emulation routines. -// Use "Host_" prefix. +// Use "Host" prefix. + +enum class RequestedAddressSpace +{ + Effective, // whatever the current MMU state is + Physical, // as if the MMU was turned off + Virtual, // specifically want MMU turned on, fails if off +}; + +// Reads a value from emulated memory using the currently active MMU settings. +// 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); @@ -22,7 +34,58 @@ 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); +template +struct TryReadResult +{ + // whether the read succeeded; if false, the other fields should not be touched + bool success; + + // whether the address had to be translated (given address was treated as virtual) or not (given + // address was treated as physical) + bool translated; + + // the actual value that was read + T value; + + TryReadResult() : success(false) {} + TryReadResult(bool translated_, T&& value_) + : success(true), translated(translated_), value(std::move(value_)) + { + } + TryReadResult(bool translated_, const T& value_) + : success(true), translated(translated_), value(value_) + { + } + explicit operator bool() const { return success; } +}; + +// Try to read a value from emulated memory at the given address in the given memory space. +// If the read succeeds, the returned TryReadResult contains the read 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. +TryReadResult HostTryReadU8(u32 address, + RequestedAddressSpace space = RequestedAddressSpace::Effective); +TryReadResult HostTryReadU16(u32 address, + RequestedAddressSpace space = RequestedAddressSpace::Effective); +TryReadResult HostTryReadU32(u32 address, + RequestedAddressSpace space = RequestedAddressSpace::Effective); +TryReadResult HostTryReadU64(u32 address, + RequestedAddressSpace space = RequestedAddressSpace::Effective); +TryReadResult HostTryReadF32(u32 address, + RequestedAddressSpace space = RequestedAddressSpace::Effective); +TryReadResult +HostTryReadF64(u32 address, RequestedAddressSpace space = RequestedAddressSpace::Effective); +TryReadResult +HostTryReadInstruction(u32 address, RequestedAddressSpace space = RequestedAddressSpace::Effective); +TryReadResult +HostTryReadString(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(u8 var, u32 address); void HostWrite_U16(u16 var, u32 address); void HostWrite_U32(u32 var, u32 address); @@ -30,14 +93,44 @@ void HostWrite_U64(u64 var, u32 address); void HostWrite_F32(float var, u32 address); void HostWrite_F64(double var, u32 address); -std::string HostGetString(u32 address, size_t size = 0); +struct TryWriteResult +{ + // whether the write succeeded; if false, the other fields should not be touched + bool success; -// Returns whether a read or write to the given address will resolve to a RAM -// access given the current CPU state. -bool HostIsRAMAddress(u32 address); + // whether the address had to be translated (given address was treated as virtual) or not (given + // address was treated as physical) + bool translated; + + TryWriteResult() : success(false) {} + TryWriteResult(bool translated_) : success(true), translated(translated_) {} + explicit operator bool() const { return success; } +}; + +// Try to a write a value to memory at the given address in the given memory space. +// If the write succeeds, the returned TryWriteResult contains information on whether the given +// address had to be translated or not. Unlike the HostWrite functions, this does not raise a +// user-visible alert on failure. +TryWriteResult HostTryWriteU8(u8 var, const u32 address, + RequestedAddressSpace space = RequestedAddressSpace::Effective); +TryWriteResult HostTryWriteU16(u16 var, const u32 address, + RequestedAddressSpace space = RequestedAddressSpace::Effective); +TryWriteResult HostTryWriteU32(u32 var, const u32 address, + RequestedAddressSpace space = RequestedAddressSpace::Effective); +TryWriteResult HostTryWriteU64(u64 var, const u32 address, + RequestedAddressSpace space = RequestedAddressSpace::Effective); +TryWriteResult HostTryWriteF32(float var, const u32 address, + RequestedAddressSpace space = RequestedAddressSpace::Effective); +TryWriteResult HostTryWriteF64(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); // Same as HostIsRAMAddress, but uses IBAT instead of DBAT. -bool HostIsInstructionRAMAddress(u32 address); +bool HostIsInstructionRAMAddress(u32 address, + RequestedAddressSpace space = RequestedAddressSpace::Effective); // Routines for the CPU core to access memory.