Skip to content
Permalink
Browse files
Merge pull request #9522 from AdmiralCurtiss/host-read-optional
Core/MMU: Add HostTryRead/HostTryWrite functions to allow specifying the desired address space, and return whether it succeeded.
  • Loading branch information
leoetlino committed Jul 7, 2021
2 parents 4b5dabf + c292351 commit 1450e97
Show file tree
Hide file tree
Showing 2 changed files with 308 additions and 10 deletions.
@@ -3,6 +3,7 @@

#include "Core/PowerPC/MMU.h"

#include <cassert>
#include <cstddef>
#include <cstring>
#include <string>
@@ -427,6 +428,36 @@ u32 HostRead_Instruction(const u32 address)
return ReadFromHardware<XCheckTLBFlag::OpcodeNoException, u32>(address);
}

TryReadResult<u32> HostTryReadInstruction(const u32 address, RequestedAddressSpace space)
{
if (!HostIsInstructionRAMAddress(address, space))
return TryReadResult<u32>();

switch (space)
{
case RequestedAddressSpace::Effective:
{
const u32 value = ReadFromHardware<XCheckTLBFlag::OpcodeNoException, u32>(address);
return TryReadResult<u32>(!!MSR.DR, value);
}
case RequestedAddressSpace::Physical:
{
const u32 value = ReadFromHardware<XCheckTLBFlag::OpcodeNoException, u32, true>(address);
return TryReadResult<u32>(false, value);
}
case RequestedAddressSpace::Virtual:
{
if (!MSR.DR)
return TryReadResult<u32>();
const u32 value = ReadFromHardware<XCheckTLBFlag::OpcodeNoException, u32>(address);
return TryReadResult<u32>(true, value);
}
}

assert(0);
return TryReadResult<u32>();
}

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<float>(integral);
}

template <typename T>
static TryReadResult<T> HostTryReadUX(const u32 address, RequestedAddressSpace space)
{
if (!HostIsRAMAddress(address, space))
return TryReadResult<T>();

switch (space)
{
case RequestedAddressSpace::Effective:
{
T value = ReadFromHardware<XCheckTLBFlag::NoException, T>(address);
return TryReadResult<T>(!!MSR.DR, std::move(value));
}
case RequestedAddressSpace::Physical:
{
T value = ReadFromHardware<XCheckTLBFlag::NoException, T, true>(address);
return TryReadResult<T>(false, std::move(value));
}
case RequestedAddressSpace::Virtual:
{
if (!MSR.DR)
return TryReadResult<T>();
T value = ReadFromHardware<XCheckTLBFlag::NoException, T>(address);
return TryReadResult<T>(true, std::move(value));
}
}

assert(0);
return TryReadResult<T>();
}

TryReadResult<u8> HostTryReadU8(u32 address, RequestedAddressSpace space)
{
return HostTryReadUX<u8>(address, space);
}

TryReadResult<u16> HostTryReadU16(u32 address, RequestedAddressSpace space)
{
return HostTryReadUX<u16>(address, space);
}

TryReadResult<u32> HostTryReadU32(u32 address, RequestedAddressSpace space)
{
return HostTryReadUX<u32>(address, space);
}

TryReadResult<u64> HostTryReadU64(u32 address, RequestedAddressSpace space)
{
return HostTryReadUX<u64>(address, space);
}

TryReadResult<float> HostTryReadF32(u32 address, RequestedAddressSpace space)
{
const auto result = HostTryReadUX<u32>(address, space);
if (!result)
return TryReadResult<float>();
return TryReadResult<float>(result.translated, Common::BitCast<float>(result.value));
}

TryReadResult<double> HostTryReadF64(u32 address, RequestedAddressSpace space)
{
const auto result = HostTryReadUX<u64>(address, space);
if (!result)
return TryReadResult<double>();
return TryReadResult<double>(result.translated, Common::BitCast<double>(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 <typename T>
static TryWriteResult HostTryWriteUX(const T var, const u32 address, RequestedAddressSpace space)
{
if (!HostIsRAMAddress(address, space))
return TryWriteResult();

switch (space)
{
case RequestedAddressSpace::Effective:
WriteToHardware<XCheckTLBFlag::NoException, T>(address, var);
return TryWriteResult(!!MSR.DR);
case RequestedAddressSpace::Physical:
WriteToHardware<XCheckTLBFlag::NoException, T, true>(address, var);
return TryWriteResult(false);
case RequestedAddressSpace::Virtual:
if (!MSR.DR)
return TryWriteResult();
WriteToHardware<XCheckTLBFlag::NoException, T>(address, var);
return TryWriteResult(true);
}

assert(0);
return TryWriteResult();
}

TryWriteResult HostTryWriteU8(const u8 var, const u32 address, RequestedAddressSpace space)
{
return HostTryWriteUX<u8>(var, address, space);
}

TryWriteResult HostTryWriteU16(const u16 var, const u32 address, RequestedAddressSpace space)
{
return HostTryWriteUX<u16>(var, address, space);
}

TryWriteResult HostTryWriteU32(const u32 var, const u32 address, RequestedAddressSpace space)
{
return HostTryWriteUX<u32>(var, address, space);
}

TryWriteResult HostTryWriteU64(const u64 var, const u32 address, RequestedAddressSpace space)
{
return HostTryWriteUX<u64>(var, address, space);
}

TryWriteResult HostTryWriteF32(const float var, const u32 address, RequestedAddressSpace space)
{
const u32 integral = Common::BitCast<u32>(var);
return HostTryWriteU32(integral, address, space);
}

TryWriteResult HostTryWriteF64(const double var, const u32 address, RequestedAddressSpace space)
{
const u64 integral = Common::BitCast<u64>(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<std::string> HostTryReadString(u32 address, size_t size, RequestedAddressSpace space)
{
auto c = HostTryReadU8(address, space);
if (!c)
return TryReadResult<std::string>();
if (c.value == 0)
return TryReadResult<std::string>(c.translated, "");

std::string s;
s += static_cast<char>(c.value);
while (size == 0 || s.length() < size)
{
++address;
const auto res = HostTryReadU8(address, space);
if (!res || res.value == 0)
break;
s += static_cast<char>(res.value);
}
return TryReadResult<std::string>(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<XCheckTLBFlag::NoException>(address, MSR.DR);
switch (space)
{
case RequestedAddressSpace::Effective:
return IsRAMAddress<XCheckTLBFlag::NoException>(address, MSR.DR);
case RequestedAddressSpace::Physical:
return IsRAMAddress<XCheckTLBFlag::NoException>(address, false);
case RequestedAddressSpace::Virtual:
if (!MSR.DR)
return false;
return IsRAMAddress<XCheckTLBFlag::NoException>(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<XCheckTLBFlag::OpcodeNoException>(address, MSR.IR);
if (!(address & 3))
return false;

switch (space)
{
case RequestedAddressSpace::Effective:
return IsRAMAddress<XCheckTLBFlag::OpcodeNoException>(address, MSR.IR);
case RequestedAddressSpace::Physical:
return IsRAMAddress<XCheckTLBFlag::OpcodeNoException>(address, false);
case RequestedAddressSpace::Virtual:
if (!MSR.IR)
return false;
return IsRAMAddress<XCheckTLBFlag::OpcodeNoException>(address, true);
}

assert(0);
return false;
}

void DMA_LCToMemory(const u32 mem_address, const u32 cache_address, const u32 num_blocks)
@@ -14,30 +14,123 @@ 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);
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 <typename T>
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<u8> HostTryReadU8(u32 address,
RequestedAddressSpace space = RequestedAddressSpace::Effective);
TryReadResult<u16> HostTryReadU16(u32 address,
RequestedAddressSpace space = RequestedAddressSpace::Effective);
TryReadResult<u32> HostTryReadU32(u32 address,
RequestedAddressSpace space = RequestedAddressSpace::Effective);
TryReadResult<u64> HostTryReadU64(u32 address,
RequestedAddressSpace space = RequestedAddressSpace::Effective);
TryReadResult<float> HostTryReadF32(u32 address,
RequestedAddressSpace space = RequestedAddressSpace::Effective);
TryReadResult<double>
HostTryReadF64(u32 address, RequestedAddressSpace space = RequestedAddressSpace::Effective);
TryReadResult<u32>
HostTryReadInstruction(u32 address, RequestedAddressSpace space = RequestedAddressSpace::Effective);
TryReadResult<std::string>
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);
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;

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

// 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);
// 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.

0 comments on commit 1450e97

Please sign in to comment.