summaryrefslogtreecommitdiff
path: root/src/core/memory/freezer.cpp
diff options
context:
space:
mode:
authorGravatar Zach Hilman2019-05-30 08:52:20 -0400
committerGravatar Zach Hilman2019-06-20 19:22:04 -0400
commit1b7d619914c8d132dbffb8b6a5bb3a1aab686f9d (patch)
tree2ced924df90e1a8c0cbd41ef251733a84b48301e /src/core/memory/freezer.cpp
parentMerge pull request #2596 from FernandoS27/revert-2590 (diff)
downloadyuzu-1b7d619914c8d132dbffb8b6a5bb3a1aab686f9d.tar.gz
yuzu-1b7d619914c8d132dbffb8b6a5bb3a1aab686f9d.tar.xz
yuzu-1b7d619914c8d132dbffb8b6a5bb3a1aab686f9d.zip
memory: Add class to manage and enforce memory freezing
Diffstat (limited to 'src/core/memory/freezer.cpp')
-rw-r--r--src/core/memory/freezer.cpp186
1 files changed, 186 insertions, 0 deletions
diff --git a/src/core/memory/freezer.cpp b/src/core/memory/freezer.cpp
new file mode 100644
index 000000000..1d0ccf328
--- /dev/null
+++ b/src/core/memory/freezer.cpp
@@ -0,0 +1,186 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/assert.h"
6#include "core/core.h"
7#include "core/core_timing_util.h"
8#include "core/memory.h"
9#include "core/memory/freezer.h"
10
11namespace Memory {
12
13constexpr s64 MEMORY_FREEZER_TICKS = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 60);
14
15namespace {
16
17u64 MemoryReadWidth(u8 width, VAddr addr) {
18 switch (width) {
19 case 1:
20 return Read8(addr);
21 case 2:
22 return Read16(addr);
23 case 4:
24 return Read32(addr);
25 case 8:
26 return Read64(addr);
27 default:
28 UNREACHABLE();
29 return 0;
30 }
31}
32
33void MemoryWriteWidth(u8 width, VAddr addr, u64 value) {
34 switch (width) {
35 case 1:
36 Write8(addr, static_cast<u8>(value));
37 break;
38 case 2:
39 Write16(addr, static_cast<u16>(value));
40 break;
41 case 4:
42 Write32(addr, static_cast<u32>(value));
43 break;
44 case 8:
45 Write64(addr, value);
46 break;
47 default:
48 UNREACHABLE();
49 }
50}
51
52} // Anonymous namespace
53
54Freezer::Freezer(Core::Timing::CoreTiming& core_timing) : core_timing(core_timing) {
55 event = core_timing.RegisterEvent(
56 "MemoryFreezer::FrameCallback",
57 [this](u64 userdata, s64 cycles_late) { FrameCallback(userdata, cycles_late); });
58 core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS, event);
59}
60
61Freezer::~Freezer() {
62 core_timing.UnscheduleEvent(event, 0);
63}
64
65void Freezer::SetActive(bool active) {
66 if (!this->active.exchange(active)) {
67 FillEntryReads();
68 core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS, event);
69 LOG_DEBUG(Common_Memory, "Memory freezer activated!");
70 } else {
71 LOG_DEBUG(Common_Memory, "Memory freezer deactivated!");
72 }
73}
74
75bool Freezer::IsActive() const {
76 return active.load();
77}
78
79void Freezer::Clear() {
80 std::lock_guard<std::recursive_mutex> lock(entries_mutex);
81
82 LOG_DEBUG(Common_Memory, "Clearing all frozen memory values.");
83
84 entries.clear();
85}
86
87u64 Freezer::Freeze(VAddr address, u8 width) {
88 std::lock_guard<std::recursive_mutex> lock(entries_mutex);
89
90 const auto current_value = MemoryReadWidth(width, address);
91 entries.push_back({address, width, current_value});
92
93 LOG_DEBUG(Common_Memory,
94 "Freezing memory for address={:016X}, width={:02X}, current_value={:016X}", address,
95 width, current_value);
96
97 return current_value;
98}
99
100void Freezer::Unfreeze(VAddr address) {
101 std::lock_guard<std::recursive_mutex> lock(entries_mutex);
102
103 LOG_DEBUG(Common_Memory, "Unfreezing memory for address={:016X}", address);
104
105 entries.erase(
106 std::remove_if(entries.begin(), entries.end(),
107 [&address](const Entry& entry) { return entry.address == address; }),
108 entries.end());
109}
110
111bool Freezer::IsFrozen(VAddr address) {
112 std::lock_guard<std::recursive_mutex> lock(entries_mutex);
113
114 return std::find_if(entries.begin(), entries.end(), [&address](const Entry& entry) {
115 return entry.address == address;
116 }) != entries.end();
117}
118
119void Freezer::SetFrozenValue(VAddr address, u64 value) {
120 std::lock_guard<std::recursive_mutex> lock(entries_mutex);
121
122 const auto iter = std::find_if(entries.begin(), entries.end(), [&address](const Entry& entry) {
123 return entry.address == address;
124 });
125
126 if (iter == entries.end()) {
127 LOG_ERROR(Common_Memory,
128 "Tried to set freeze value for address={:016X} that is not frozen!", address);
129 return;
130 }
131
132 LOG_DEBUG(Common_Memory,
133 "Manually overridden freeze value for address={:016X}, width={:02X} to value={:016X}",
134 iter->address, iter->width, value);
135 iter->value = value;
136}
137
138std::optional<Freezer::Entry> Freezer::GetEntry(VAddr address) {
139 std::lock_guard<std::recursive_mutex> lock(entries_mutex);
140
141 const auto iter = std::find_if(entries.begin(), entries.end(), [&address](const Entry& entry) {
142 return entry.address == address;
143 });
144
145 if (iter == entries.end()) {
146 return std::nullopt;
147 }
148
149 return *iter;
150}
151
152std::vector<Freezer::Entry> Freezer::GetEntries() {
153 std::lock_guard<std::recursive_mutex> lock(entries_mutex);
154
155 return entries;
156}
157
158void Freezer::FrameCallback(u64 userdata, s64 cycles_late) {
159 if (!active.load()) {
160 LOG_DEBUG(Common_Memory, "Memory freezer has been deactivated, ending callback events.");
161 return;
162 }
163
164 std::lock_guard<std::recursive_mutex> lock(entries_mutex);
165
166 for (const auto& entry : entries) {
167 LOG_DEBUG(Common_Memory,
168 "Enforcing memory freeze at address={:016X}, value={:016X}, width={:02X}",
169 entry.address, entry.value, entry.width);
170 MemoryWriteWidth(entry.width, entry.address, entry.value);
171 }
172
173 core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS - cycles_late, event);
174}
175
176void Freezer::FillEntryReads() {
177 std::lock_guard<std::recursive_mutex> lock(entries_mutex);
178
179 LOG_DEBUG(Common_Memory, "Updating memory freeze entries to current values.");
180
181 for (auto& entry : entries) {
182 entry.value = MemoryReadWidth(entry.width, entry.address);
183 }
184}
185
186} // namespace Memory