4 changed files with 248 additions and 0 deletions
-
2src/core/CMakeLists.txt
-
2src/core/core.cpp
-
186src/core/memory/freezer.cpp
-
58src/core/memory/freezer.h
@ -0,0 +1,186 @@ |
|||
// Copyright 2019 yuzu Emulator Project
|
|||
// Licensed under GPLv2 or any later version
|
|||
// Refer to the license.txt file included.
|
|||
|
|||
#include "common/assert.h"
|
|||
#include "core/core.h"
|
|||
#include "core/core_timing_util.h"
|
|||
#include "core/memory.h"
|
|||
#include "core/memory/freezer.h"
|
|||
|
|||
namespace Memory { |
|||
|
|||
constexpr s64 MEMORY_FREEZER_TICKS = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 60); |
|||
|
|||
namespace { |
|||
|
|||
u64 MemoryReadWidth(u8 width, VAddr addr) { |
|||
switch (width) { |
|||
case 1: |
|||
return Read8(addr); |
|||
case 2: |
|||
return Read16(addr); |
|||
case 4: |
|||
return Read32(addr); |
|||
case 8: |
|||
return Read64(addr); |
|||
default: |
|||
UNREACHABLE(); |
|||
return 0; |
|||
} |
|||
} |
|||
|
|||
void MemoryWriteWidth(u8 width, VAddr addr, u64 value) { |
|||
switch (width) { |
|||
case 1: |
|||
Write8(addr, static_cast<u8>(value)); |
|||
break; |
|||
case 2: |
|||
Write16(addr, static_cast<u16>(value)); |
|||
break; |
|||
case 4: |
|||
Write32(addr, static_cast<u32>(value)); |
|||
break; |
|||
case 8: |
|||
Write64(addr, value); |
|||
break; |
|||
default: |
|||
UNREACHABLE(); |
|||
} |
|||
} |
|||
|
|||
} // Anonymous namespace
|
|||
|
|||
Freezer::Freezer(Core::Timing::CoreTiming& core_timing) : core_timing(core_timing) { |
|||
event = core_timing.RegisterEvent( |
|||
"MemoryFreezer::FrameCallback", |
|||
[this](u64 userdata, s64 cycles_late) { FrameCallback(userdata, cycles_late); }); |
|||
core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS, event); |
|||
} |
|||
|
|||
Freezer::~Freezer() { |
|||
core_timing.UnscheduleEvent(event, 0); |
|||
} |
|||
|
|||
void Freezer::SetActive(bool active) { |
|||
if (!this->active.exchange(active)) { |
|||
FillEntryReads(); |
|||
core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS, event); |
|||
LOG_DEBUG(Common_Memory, "Memory freezer activated!"); |
|||
} else { |
|||
LOG_DEBUG(Common_Memory, "Memory freezer deactivated!"); |
|||
} |
|||
} |
|||
|
|||
bool Freezer::IsActive() const { |
|||
return active.load(); |
|||
} |
|||
|
|||
void Freezer::Clear() { |
|||
std::lock_guard<std::recursive_mutex> lock(entries_mutex); |
|||
|
|||
LOG_DEBUG(Common_Memory, "Clearing all frozen memory values."); |
|||
|
|||
entries.clear(); |
|||
} |
|||
|
|||
u64 Freezer::Freeze(VAddr address, u8 width) { |
|||
std::lock_guard<std::recursive_mutex> lock(entries_mutex); |
|||
|
|||
const auto current_value = MemoryReadWidth(width, address); |
|||
entries.push_back({address, width, current_value}); |
|||
|
|||
LOG_DEBUG(Common_Memory, |
|||
"Freezing memory for address={:016X}, width={:02X}, current_value={:016X}", address, |
|||
width, current_value); |
|||
|
|||
return current_value; |
|||
} |
|||
|
|||
void Freezer::Unfreeze(VAddr address) { |
|||
std::lock_guard<std::recursive_mutex> lock(entries_mutex); |
|||
|
|||
LOG_DEBUG(Common_Memory, "Unfreezing memory for address={:016X}", address); |
|||
|
|||
entries.erase( |
|||
std::remove_if(entries.begin(), entries.end(), |
|||
[&address](const Entry& entry) { return entry.address == address; }), |
|||
entries.end()); |
|||
} |
|||
|
|||
bool Freezer::IsFrozen(VAddr address) { |
|||
std::lock_guard<std::recursive_mutex> lock(entries_mutex); |
|||
|
|||
return std::find_if(entries.begin(), entries.end(), [&address](const Entry& entry) { |
|||
return entry.address == address; |
|||
}) != entries.end(); |
|||
} |
|||
|
|||
void Freezer::SetFrozenValue(VAddr address, u64 value) { |
|||
std::lock_guard<std::recursive_mutex> lock(entries_mutex); |
|||
|
|||
const auto iter = std::find_if(entries.begin(), entries.end(), [&address](const Entry& entry) { |
|||
return entry.address == address; |
|||
}); |
|||
|
|||
if (iter == entries.end()) { |
|||
LOG_ERROR(Common_Memory, |
|||
"Tried to set freeze value for address={:016X} that is not frozen!", address); |
|||
return; |
|||
} |
|||
|
|||
LOG_DEBUG(Common_Memory, |
|||
"Manually overridden freeze value for address={:016X}, width={:02X} to value={:016X}", |
|||
iter->address, iter->width, value); |
|||
iter->value = value; |
|||
} |
|||
|
|||
std::optional<Freezer::Entry> Freezer::GetEntry(VAddr address) { |
|||
std::lock_guard<std::recursive_mutex> lock(entries_mutex); |
|||
|
|||
const auto iter = std::find_if(entries.begin(), entries.end(), [&address](const Entry& entry) { |
|||
return entry.address == address; |
|||
}); |
|||
|
|||
if (iter == entries.end()) { |
|||
return std::nullopt; |
|||
} |
|||
|
|||
return *iter; |
|||
} |
|||
|
|||
std::vector<Freezer::Entry> Freezer::GetEntries() { |
|||
std::lock_guard<std::recursive_mutex> lock(entries_mutex); |
|||
|
|||
return entries; |
|||
} |
|||
|
|||
void Freezer::FrameCallback(u64 userdata, s64 cycles_late) { |
|||
if (!active.load()) { |
|||
LOG_DEBUG(Common_Memory, "Memory freezer has been deactivated, ending callback events."); |
|||
return; |
|||
} |
|||
|
|||
std::lock_guard<std::recursive_mutex> lock(entries_mutex); |
|||
|
|||
for (const auto& entry : entries) { |
|||
LOG_DEBUG(Common_Memory, |
|||
"Enforcing memory freeze at address={:016X}, value={:016X}, width={:02X}", |
|||
entry.address, entry.value, entry.width); |
|||
MemoryWriteWidth(entry.width, entry.address, entry.value); |
|||
} |
|||
|
|||
core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS - cycles_late, event); |
|||
} |
|||
|
|||
void Freezer::FillEntryReads() { |
|||
std::lock_guard<std::recursive_mutex> lock(entries_mutex); |
|||
|
|||
LOG_DEBUG(Common_Memory, "Updating memory freeze entries to current values."); |
|||
|
|||
for (auto& entry : entries) { |
|||
entry.value = MemoryReadWidth(entry.width, entry.address); |
|||
} |
|||
} |
|||
|
|||
} // namespace Memory
|
|||
@ -0,0 +1,58 @@ |
|||
// Copyright 2019 yuzu Emulator Project |
|||
// Licensed under GPLv2 or any later version |
|||
// Refer to the license.txt file included. |
|||
|
|||
#pragma once |
|||
|
|||
#include <optional> |
|||
#include <vector> |
|||
#include "common/common_types.h" |
|||
#include "core/core_timing.h" |
|||
|
|||
namespace Core { |
|||
class System; |
|||
} // namespace Core |
|||
|
|||
namespace Memory { |
|||
|
|||
// A class that will effectively freeze memory values. |
|||
class Freezer { |
|||
public: |
|||
struct Entry { |
|||
VAddr address; |
|||
u8 width; |
|||
u64 value; |
|||
}; |
|||
|
|||
Freezer(Core::Timing::CoreTiming& core_timing); |
|||
~Freezer(); |
|||
|
|||
void SetActive(bool active); |
|||
bool IsActive() const; |
|||
|
|||
void Clear(); |
|||
|
|||
u64 Freeze(VAddr address, u8 width); |
|||
void Unfreeze(VAddr address); |
|||
|
|||
bool IsFrozen(VAddr address); |
|||
void SetFrozenValue(VAddr address, u64 value); |
|||
|
|||
std::optional<Entry> GetEntry(VAddr address); |
|||
|
|||
std::vector<Entry> GetEntries(); |
|||
|
|||
private: |
|||
void FrameCallback(u64 userdata, s64 cycles_late); |
|||
void FillEntryReads(); |
|||
|
|||
std::atomic_bool active{false}; |
|||
|
|||
std::recursive_mutex entries_mutex; |
|||
std::vector<Entry> entries; |
|||
|
|||
Core::Timing::EventType* event; |
|||
Core::Timing::CoreTiming& core_timing; |
|||
}; |
|||
|
|||
} // namespace Memory |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue