Skip to content

Commit f8f46b5

Browse files
authored
Merge pull request #733 from twevs/memoryobserver
Added Memory Observer.
2 parents 1ab8625 + 70fca59 commit f8f46b5

File tree

6 files changed

+298
-0
lines changed

6 files changed

+298
-0
lines changed

src/gui/gui.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -911,6 +911,7 @@ in Configuration->Emulation, restart PCSX-Redux, then try again.)"));
911911
m_biosEditor.MenuItem();
912912
ImGui::EndMenu();
913913
}
914+
ImGui::MenuItem(_("Show Memory Observer"), nullptr, &m_memoryObserver.m_show);
914915
ImGui::MenuItem(_("Show Interrupts Scaler"), nullptr, &m_showInterruptsScaler);
915916
ImGui::MenuItem(_("Kernel Events"), nullptr, &m_events.m_show);
916917
ImGui::MenuItem(_("Kernel Calls"), nullptr, &m_kernelLog.m_show);
@@ -1107,6 +1108,10 @@ in Configuration->Emulation, restart PCSX-Redux, then try again.)"));
11071108
m_breakpoints.draw(_("Breakpoints"));
11081109
}
11091110

1111+
if (m_memoryObserver.m_show) {
1112+
m_memoryObserver.draw(_("Memory Observer"));
1113+
}
1114+
11101115
changed |= about();
11111116
interruptsScaler();
11121117

src/gui/gui.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
#include "magic_enum/include/magic_enum.hpp"
5151
#include "support/eventbus.h"
5252
#include "support/settings.h"
53+
#include "widgets/memory_observer.h"
5354

5455
#if defined(__APPLE__)
5556
#define GL_SHADER_VERSION "#version 410\n"
@@ -247,6 +248,7 @@ class GUI final {
247248
MemoryEditorWrapper m_scratchPadEditor;
248249
MemoryEditorWrapper m_hwrEditor;
249250
MemoryEditorWrapper m_biosEditor;
251+
Widgets::MemoryObserver m_memoryObserver;
250252
Widgets::MemcardManager m_memcardManager;
251253
Widgets::Registers m_registers;
252254
Widgets::Assembly m_assembly;

src/gui/widgets/memory_observer.cc

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
/***************************************************************************
2+
* Copyright (C) 2022 PCSX-Redux authors *
3+
* *
4+
* This program is free software; you can redistribute it and/or modify *
5+
* it under the terms of the GNU General Public License as published by *
6+
* the Free Software Foundation; either version 2 of the License, or *
7+
* (at your option) any later version. *
8+
* *
9+
* This program is distributed in the hope that it will be useful, *
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12+
* GNU General Public License for more details. *
13+
* *
14+
* You should have received a copy of the GNU General Public License *
15+
* along with this program; if not, write to the *
16+
* Free Software Foundation, Inc., *
17+
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *
18+
***************************************************************************/
19+
20+
#include "gui/widgets/memory_observer.h"
21+
22+
#include <magic_enum/include/magic_enum.hpp>
23+
24+
#include "core/psxemulator.h"
25+
#include "core/psxmem.h"
26+
#include "core/system.h"
27+
28+
void PCSX::Widgets::MemoryObserver::draw(const char* title) {
29+
if (!ImGui::Begin(title, &m_show)) {
30+
ImGui::End();
31+
return;
32+
}
33+
34+
const uint8_t* memData = g_emulator->m_psxMem->g_psxM;
35+
const uint32_t memSize = 1024 * 1024 * (g_emulator->settings.get<PCSX::Emulator::Setting8MB>() ? 8 : 2);
36+
const uint32_t memBase = 0x80000000;
37+
const auto stride = static_cast<uint8_t>(m_scanAlignment);
38+
39+
if (m_AddressValuePairs.empty() && ImGui::Button("First scan")) {
40+
41+
int memValue = 0;
42+
43+
for (uint32_t i = 0; i < memSize; ++i) {
44+
if (i != 0 && i % stride == 0) {
45+
switch (m_scanType) {
46+
case ScanType::ExactValue:
47+
if (memValue == m_value) {
48+
m_AddressValuePairs.push_back({memBase + i - stride, memValue});
49+
}
50+
break;
51+
case ScanType::BiggerThan:
52+
if (memValue > m_value) {
53+
m_AddressValuePairs.push_back({memBase + i - stride, memValue});
54+
}
55+
break;
56+
case ScanType::SmallerThan:
57+
if (memValue < m_value) {
58+
m_AddressValuePairs.push_back({memBase + i - stride, memValue});
59+
}
60+
break;
61+
case ScanType::Changed:
62+
case ScanType::Unchanged:
63+
case ScanType::Increased:
64+
case ScanType::Decreased:
65+
break;
66+
case ScanType::UnknownInitialValue:
67+
m_AddressValuePairs.push_back({memBase + i - stride, memValue});
68+
break;
69+
}
70+
71+
memValue = 0;
72+
}
73+
74+
const uint8_t currentByte = memData[i];
75+
const uint8_t leftShift = 8 * (stride - 1 - i % stride);
76+
const uint32_t mask = 0xffffffff ^ (0xff << leftShift);
77+
const int byteToWrite = currentByte << leftShift;
78+
memValue = (memValue & mask) | byteToWrite;
79+
}
80+
}
81+
82+
if (!m_AddressValuePairs.empty() && ImGui::Button("Next scan")) {
83+
auto doesntMatchCriterion = [this, memData, memSize, memBase, stride](
84+
const AddressValuePair& addressValuePair) {
85+
const uint32_t address = addressValuePair.address;
86+
const int memValue = getMemValue(address, memData, memSize, memBase, stride);
87+
88+
switch (m_scanType) {
89+
case ScanType::ExactValue:
90+
return memValue != m_value;
91+
case ScanType::BiggerThan:
92+
return memValue <= m_value;
93+
case ScanType::SmallerThan:
94+
return memValue >= m_value;
95+
case ScanType::Changed:
96+
return memValue == addressValuePair.scannedValue;
97+
case ScanType::Unchanged:
98+
return memValue != addressValuePair.scannedValue;
99+
case ScanType::Increased:
100+
return memValue <= addressValuePair.scannedValue;
101+
case ScanType::Decreased:
102+
return memValue >= addressValuePair.scannedValue;
103+
case ScanType::UnknownInitialValue:
104+
return true;
105+
}
106+
107+
return true;
108+
};
109+
110+
std::erase_if(m_AddressValuePairs, doesntMatchCriterion);
111+
112+
if (m_AddressValuePairs.empty()) {
113+
m_scanType = ScanType::ExactValue;
114+
} else {
115+
for (auto& addressValuePair : m_AddressValuePairs) {
116+
addressValuePair.scannedValue =
117+
getMemValue(addressValuePair.address, memData, memSize, memBase, stride);
118+
}
119+
}
120+
}
121+
122+
if (!m_AddressValuePairs.empty() && ImGui::Button("New scan")) {
123+
m_AddressValuePairs.clear();
124+
m_scanType = ScanType::ExactValue;
125+
}
126+
127+
ImGui::Checkbox("Hex", &m_hex);
128+
ImGui::InputInt("Value", &m_value, 1, 100,
129+
m_hex ? ImGuiInputTextFlags_CharsHexadecimal : ImGuiInputTextFlags_CharsDecimal);
130+
131+
const auto currentScanAlignment = magic_enum::enum_name(m_scanAlignment);
132+
if (ImGui::BeginCombo(_("Scan alignment"), currentScanAlignment.data())) {
133+
for (auto v : magic_enum::enum_values<ScanAlignment>()) {
134+
bool selected = (v == m_scanAlignment);
135+
auto name = magic_enum::enum_name(v);
136+
if (ImGui::Selectable(name.data(), selected)) {
137+
m_scanAlignment = v;
138+
}
139+
if (selected) {
140+
ImGui::SetItemDefaultFocus();
141+
}
142+
}
143+
ImGui::EndCombo();
144+
}
145+
146+
const auto currentScanType = magic_enum::enum_name(m_scanType);
147+
if (ImGui::BeginCombo(_("Scan type"), currentScanType.data())) {
148+
for (auto v : magic_enum::enum_values<ScanType>()) {
149+
bool selected = (v == m_scanType);
150+
auto name = magic_enum::enum_name(v);
151+
if (ImGui::Selectable(name.data(), selected)) {
152+
m_scanType = v;
153+
}
154+
if (selected) {
155+
ImGui::SetItemDefaultFocus();
156+
}
157+
}
158+
ImGui::EndCombo();
159+
}
160+
161+
static constexpr ImGuiTableFlags flags = ImGuiTableFlags_RowBg | ImGuiTableFlags_Resizable |
162+
ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable |
163+
ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV;
164+
if (ImGui::BeginTable("Found values", 4, flags)) {
165+
ImGui::TableSetupColumn("Address");
166+
ImGui::TableSetupColumn("Current value");
167+
ImGui::TableSetupColumn("Scanned value");
168+
ImGui::TableSetupColumn("Access");
169+
ImGui::TableHeadersRow();
170+
171+
const auto valueDisplayFormat = m_hex ? "%x" : "%i";
172+
173+
ImGuiListClipper clipper;
174+
clipper.Begin(m_AddressValuePairs.size());
175+
while (clipper.Step()) {
176+
for (int row = clipper.DisplayStart; row < clipper.DisplayEnd; ++row) {
177+
const auto& addressValuePair = m_AddressValuePairs[row];
178+
const uint32_t currentAddress = addressValuePair.address;
179+
180+
ImGui::TableNextRow();
181+
ImGui::TableSetColumnIndex(0);
182+
ImGui::Text("%x", currentAddress);
183+
ImGui::TableSetColumnIndex(1);
184+
ImGui::Text(valueDisplayFormat, getMemValue(currentAddress, memData, memSize, memBase, stride));
185+
ImGui::TableSetColumnIndex(2);
186+
ImGui::Text(valueDisplayFormat, addressValuePair.scannedValue);
187+
ImGui::TableSetColumnIndex(3);
188+
auto buttonName = fmt::format(_("Show in memory editor##{}"), row);
189+
if (ImGui::Button(buttonName.c_str())) {
190+
const uint32_t editorAddress = currentAddress - memBase;
191+
g_system->m_eventBus->signal(PCSX::Events::GUI::JumpToMemory{editorAddress, stride});
192+
}
193+
}
194+
}
195+
ImGui::EndTable();
196+
}
197+
}
198+
199+
int PCSX::Widgets::MemoryObserver::getMemValue(uint32_t absoluteAddress, const uint8_t* memData, uint32_t memSize,
200+
uint32_t memBase, uint8_t stride) {
201+
int memValue = 0;
202+
const uint32_t relativeAddress = absoluteAddress - memBase;
203+
assert(relativeAddress < memSize);
204+
for (uint32_t i = relativeAddress; i < relativeAddress + stride; ++i) {
205+
const uint8_t currentByte = memData[i];
206+
const uint8_t leftShift = 8 * (stride - 1 - i % stride);
207+
const uint32_t mask = 0xffffffff ^ (0xff << leftShift);
208+
const int byteToWrite = currentByte << leftShift;
209+
memValue = (memValue & mask) | byteToWrite;
210+
}
211+
return memValue;
212+
}

src/gui/widgets/memory_observer.h

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/***************************************************************************
2+
* Copyright (C) 2022 PCSX-Redux authors *
3+
* *
4+
* This program is free software; you can redistribute it and/or modify *
5+
* it under the terms of the GNU General Public License as published by *
6+
* the Free Software Foundation; either version 2 of the License, or *
7+
* (at your option) any later version. *
8+
* *
9+
* This program is distributed in the hope that it will be useful, *
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12+
* GNU General Public License for more details. *
13+
* *
14+
* You should have received a copy of the GNU General Public License *
15+
* along with this program; if not, write to the *
16+
* Free Software Foundation, Inc., *
17+
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *
18+
***************************************************************************/
19+
20+
#pragma once
21+
22+
#include <stdint.h>
23+
24+
#include <vector>
25+
26+
#include "imgui.h"
27+
28+
namespace PCSX {
29+
30+
namespace Widgets {
31+
32+
class MemoryObserver {
33+
public:
34+
void draw(const char* title);
35+
bool m_show = false;
36+
37+
enum class ScanType {
38+
ExactValue,
39+
BiggerThan,
40+
SmallerThan,
41+
Changed,
42+
Unchanged,
43+
Increased,
44+
Decreased,
45+
UnknownInitialValue
46+
};
47+
48+
enum class ScanAlignment : uint8_t {
49+
OneByte = 1,
50+
TwoBytes = 2,
51+
FourBytes = 4
52+
};
53+
54+
struct AddressValuePair {
55+
uint32_t address = 0;
56+
int scannedValue = 0;
57+
};
58+
59+
private:
60+
static int getMemValue(uint32_t absoluteAddress, const uint8_t* memData, uint32_t memSize, uint32_t memBase, uint8_t stride);
61+
62+
ScanType m_scanType = ScanType::ExactValue;
63+
ScanAlignment m_scanAlignment = ScanAlignment::OneByte;
64+
std::vector<AddressValuePair> m_AddressValuePairs;
65+
bool m_hex = false;
66+
int m_value = 0;
67+
};
68+
69+
}
70+
71+
}

vsprojects/gui/gui.vcxproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@
195195
<ClCompile Include="..\..\src\gui\widgets\luaeditor.cc" />
196196
<ClCompile Include="..\..\src\gui\widgets\luainspector.cc" />
197197
<ClCompile Include="..\..\src\gui\widgets\memcard_manager.cc" />
198+
<ClCompile Include="..\..\src\gui\widgets\memory_observer.cc" />
198199
<ClCompile Include="..\..\src\gui\widgets\registers.cc" />
199200
<ClCompile Include="..\..\src\gui\widgets\shader-editor.cc" />
200201
<ClCompile Include="..\..\src\gui\widgets\source.cc" />
@@ -220,6 +221,7 @@
220221
<ClInclude Include="..\..\src\gui\widgets\luaeditor.h" />
221222
<ClInclude Include="..\..\src\gui\widgets\luainspector.h" />
222223
<ClInclude Include="..\..\src\gui\widgets\memcard_manager.h" />
224+
<ClInclude Include="..\..\src\gui\widgets\memory_observer.h" />
223225
<ClInclude Include="..\..\src\gui\widgets\registers.h" />
224226
<ClInclude Include="..\..\src\gui\widgets\shader-editor.h" />
225227
<ClInclude Include="..\..\src\gui\widgets\source.h" />

vsprojects/gui/gui.vcxproj.filters

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@
7676
<ClCompile Include="..\..\src\gui\widgets\callstacks.cc">
7777
<Filter>Source Files\widgets</Filter>
7878
</ClCompile>
79+
<ClCompile Include="..\..\src\gui\widgets\memory_observer.cc">
80+
<Filter>Source Files\widgets</Filter>
81+
</ClCompile>
7982
</ItemGroup>
8083
<ItemGroup>
8184
<ClInclude Include="..\..\src\gui\gui.h">
@@ -150,6 +153,9 @@
150153
<ClInclude Include="..\..\src\gui\widgets\callstacks.h">
151154
<Filter>Header Files\widgets</Filter>
152155
</ClInclude>
156+
<ClInclude Include="..\..\src\gui\widgets\memory_observer.h">
157+
<Filter>Header Files\widgets</Filter>
158+
</ClInclude>
153159
</ItemGroup>
154160
<ItemGroup>
155161
<None Include="packages.config" />

0 commit comments

Comments
 (0)