diff --git a/CMakeLists.txt b/CMakeLists.txt index 89e6347f1b..61473590ef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1528,6 +1528,8 @@ list(APPEND NativeAppSource UI/ImDebugger/ImGe.h UI/ImDebugger/ImDisasmView.cpp UI/ImDebugger/ImDisasmView.h + UI/ImDebugger/ImMemView.cpp + UI/ImDebugger/ImMemView.h UI/ImDebugger/ImStructViewer.cpp UI/ImDebugger/ImStructViewer.h UI/DiscordIntegration.cpp diff --git a/Core/Debugger/DebugInterface.h b/Core/Debugger/DebugInterface.h index 0c82696654..2ae2174412 100644 --- a/Core/Debugger/DebugInterface.h +++ b/Core/Debugger/DebugInterface.h @@ -57,7 +57,7 @@ public: virtual u32 GetPC() = 0; virtual void SetPC(u32 _pc) = 0; - virtual u32 GetLR() = 0; + virtual u32 GetRA() = 0; virtual void DisAsm(u32 pc, char *out, size_t outSize) = 0; diff --git a/Core/Debugger/DisassemblyManager.h b/Core/Debugger/DisassemblyManager.h index c0f990d853..291f1bdd4e 100644 --- a/Core/Debugger/DisassemblyManager.h +++ b/Core/Debugger/DisassemblyManager.h @@ -200,7 +200,7 @@ public: void clear(); - void setCpu(DebugInterface* _cpu) { cpu = _cpu; }; + static void setCpu(DebugInterface* _cpu) { cpu = _cpu; }; void setMaxParamChars(int num) { maxParamChars = num; clear(); }; void getLine(u32 address, bool insertSymbols, DisassemblyLineInfo &dest, DebugInterface *cpuDebug = nullptr); void analyze(u32 address, u32 size); diff --git a/Core/HLE/KernelThreadDebugInterface.h b/Core/HLE/KernelThreadDebugInterface.h index eb3c754675..b6372f83a3 100644 --- a/Core/HLE/KernelThreadDebugInterface.h +++ b/Core/HLE/KernelThreadDebugInterface.h @@ -28,7 +28,7 @@ public: u32 GetGPR32Value(int reg) override { return ctx.r[reg]; } u32 GetPC() override { return ctx.pc; } - u32 GetLR() override { return ctx.r[MIPS_REG_RA]; } + u32 GetRA() override { return ctx.r[MIPS_REG_RA]; } void SetPC(u32 _pc) override { ctx.pc = _pc; } void PrintRegValue(int cat, int index, char *out, size_t outSize) override { diff --git a/Core/HLE/proAdhoc.cpp b/Core/HLE/proAdhoc.cpp index d4b283dabb..81028d0824 100644 --- a/Core/HLE/proAdhoc.cpp +++ b/Core/HLE/proAdhoc.cpp @@ -1327,7 +1327,7 @@ void timeoutFriendsRecursive(SceNetAdhocctlPeerInfo * node, int32_t* count) { } void sendChat(const std::string &chatString) { - SceNetAdhocctlChatPacketC2S chat; + SceNetAdhocctlChatPacketC2S chat{}; chat.base.opcode = OPCODE_CHAT; //TODO check network inited, check send success or not, chatlog.pushback error on failed send, pushback error on not connected if (friendFinderRunning) { diff --git a/Core/MIPS/MIPSDebugInterface.h b/Core/MIPS/MIPSDebugInterface.h index ac62511af0..7899f22436 100644 --- a/Core/MIPS/MIPSDebugInterface.h +++ b/Core/MIPS/MIPSDebugInterface.h @@ -50,7 +50,7 @@ public: void SetGPR32Value(int reg, u32 value) override { cpu->r[reg] = value; } u32 GetPC() override { return cpu->pc; } - u32 GetLR() override { return cpu->r[MIPS_REG_RA]; } + u32 GetRA() override { return cpu->r[MIPS_REG_RA]; } u32 GetFPCond() override { return cpu->fpcond; } void DisAsm(u32 pc, char *out, size_t outSize) override; void SetPC(u32 _pc) override { cpu->pc = _pc; } diff --git a/UI/ImDebugger/ImDebugger.cpp b/UI/ImDebugger/ImDebugger.cpp index 520da9c502..435d761137 100644 --- a/UI/ImDebugger/ImDebugger.cpp +++ b/UI/ImDebugger/ImDebugger.cpp @@ -35,12 +35,35 @@ // GPU things #include "GPU/Common/GPUDebugInterface.h" +#include "GPU/Debugger/Stepping.h" #include "UI/ImDebugger/ImDebugger.h" #include "UI/ImDebugger/ImGe.h" extern bool g_TakeScreenshot; +void ShowInMemoryViewerMenuItem(uint32_t addr, ImControl &control) { + if (ImGui::BeginMenu("Show in memory viewer")) { + for (int i = 0; i < 4; i++) { + if (ImGui::MenuItem(ImMemWindow::Title(i))) { + control.command = { ImCmd::SHOW_IN_MEMORY_VIEWER, addr, (u32)i }; + } + } + ImGui::EndMenu(); + } +} + +void ShowInWindowMenuItems(uint32_t addr, ImControl &control) { + // Enable when we implement the memory viewer + ShowInMemoryViewerMenuItem(addr, control); + if (ImGui::MenuItem("Show in CPU debugger")) { + control.command = { ImCmd::SHOW_IN_CPU_DISASM, addr }; + } + if (ImGui::MenuItem("Show in GE debugger")) { + control.command = { ImCmd::SHOW_IN_GE_DISASM, addr }; + } +} + // TODO: Style it. // Left click performs the preferred action, if any. Right click opens a menu for more. void ImClickableAddress(uint32_t addr, ImControl &control, ImCmd cmd) { @@ -56,18 +79,7 @@ void ImClickableAddress(uint32_t addr, ImControl &control, ImCmd cmd) { System_CopyStringToClipboard(temp); } ImGui::Separator(); - // Enable when we implement the memory viewer - if (cmd != ImCmd::SHOW_IN_MEMORY_VIEWER) { - if (ImGui::MenuItem("Show in memory viewer 1")) { - control.command = { ImCmd::SHOW_IN_MEMORY_VIEWER, addr }; - } - } - if (ImGui::MenuItem("Show in CPU debugger")) { - control.command = { ImCmd::SHOW_IN_CPU_DISASM, addr }; - } - if (ImGui::MenuItem("Show in GE debugger")) { - control.command = { ImCmd::SHOW_IN_GE_DISASM, addr }; - } + ShowInWindowMenuItems(addr, control); ImGui::EndPopup(); } } @@ -879,6 +891,27 @@ void ImDebugger::Frame(MIPSDebugInterface *mipsDebug, GPUDebugInterface *gpuDebu return; } + // TODO: Pass mipsDebug in where needed instead. + DisassemblyManager::setCpu(mipsDebug); + disasm_.View().setDebugger(mipsDebug); + for (int i = 0; i < 4; i++) { + mem_[i].View().setDebugger(mipsDebug); + } + + // Watch the step counters to figure out when to update things. + + if (lastCpuStepCount_ != Core_GetSteppingCounter()) { + lastCpuStepCount_ = Core_GetSteppingCounter(); + disasm_.View().NotifyStep(); + } + + if (lastGpuStepCount_ != GPUStepping::GetSteppingCounter()) { + // A GPU step has happened since last time. This means that we should re-center the cursor. + // Snapshot(); + lastGpuStepCount_ = GPUStepping::GetSteppingCounter(); + geDebugger_.View().NotifyStep(); + } + ImControl control{}; if (ImGui::BeginMainMenuBar()) { @@ -951,6 +984,12 @@ void ImDebugger::Frame(MIPSDebugInterface *mipsDebug, GPUDebugInterface *gpuDebu ImGui::MenuItem("Callstacks", nullptr, &cfg_.callstackOpen); ImGui::MenuItem("Breakpoints", nullptr, &cfg_.breakpointsOpen); ImGui::MenuItem("Scheduler", nullptr, &cfg_.schedulerOpen); + ImGui::Separator(); + for (int i = 0; i < 4; i++) { + char title[64]; + snprintf(title, sizeof(title), "Memory %d", i + 1); + ImGui::MenuItem(title, nullptr, &cfg_.memViewOpen[i]); + } ImGui::EndMenu(); } if (ImGui::BeginMenu("HLE")) { @@ -999,7 +1038,7 @@ void ImDebugger::Frame(MIPSDebugInterface *mipsDebug, GPUDebugInterface *gpuDebu } if (cfg_.disasmOpen) { - disasm_.Draw(mipsDebug, cfg_, coreState); + disasm_.Draw(mipsDebug, cfg_, control, coreState); } if (cfg_.regsOpen) { @@ -1074,13 +1113,37 @@ void ImDebugger::Frame(MIPSDebugInterface *mipsDebug, GPUDebugInterface *gpuDebu DrawSchedulerView(cfg_); } + for (int i = 0; i < 4; i++) { + if (cfg_.memViewOpen[i]) { + mem_[i].Draw(mipsDebug, cfg_, control, i); + } + } + // Process UI commands switch (control.command.cmd) { case ImCmd::SHOW_IN_CPU_DISASM: disasm_.View().gotoAddr(control.command.param); + cfg_.disasmOpen = true; + ImGui::SetWindowFocus(disasm_.Title()); break; case ImCmd::SHOW_IN_GE_DISASM: geDebugger_.View().GotoAddr(control.command.param); + cfg_.geDebuggerOpen = true; + ImGui::SetWindowFocus(geDebugger_.Title()); + break; + case ImCmd::SHOW_IN_MEMORY_VIEWER: + { + u32 index = control.command.param2; + _dbg_assert_(index < 4); + mem_[index].GotoAddr(control.command.param); + cfg_.memViewOpen[index] = true; + ImGui::SetWindowFocus(ImMemWindow::Title(index)); + break; + } + case ImCmd::TRIGGER_FIND_POPUP: + // TODO + break; + case ImCmd::NONE: break; } } @@ -1089,14 +1152,56 @@ void ImDebugger::Snapshot() { } -void ImDisasmWindow::Draw(MIPSDebugInterface *mipsDebug, ImConfig &cfg, CoreState coreState) { - char title[256]; - snprintf(title, sizeof(title), "%s - Disassembly", "Allegrex MIPS"); +void ImMemWindow::Draw(MIPSDebugInterface *mipsDebug, ImConfig &cfg, ImControl &control, int index) { + ImGui::SetNextWindowSize(ImVec2(520, 600), ImGuiCond_FirstUseEver); + if (!ImGui::Begin(Title(index), &cfg.memViewOpen[index])) { + ImGui::End(); + return; + } + // Toolbars + + if (ImGui::InputScalar("Go to addr: ", ImGuiDataType_U32, &gotoAddr_, NULL, NULL, "%08X", ImGuiInputTextFlags_EnterReturnsTrue)) { + memView_.gotoAddr(gotoAddr_); + } + + // Main views - list of interesting addresses to the left, memory view to the right. + if (ImGui::BeginChild("addr_list", ImVec2(200.0f, 0.0))) { + if (ImGui::Selectable("Scratch")) { + GotoAddr(0x00010000); + } + if (ImGui::Selectable("Kernel RAM")) { + GotoAddr(0x08000000); + } + if (ImGui::Selectable("User RAM")) { + GotoAddr(0x08800000); + } + if (ImGui::Selectable("VRAM")) { + GotoAddr(0x08800000); + } + ImGui::EndChild(); + } + ImGui::SameLine(); + ImGui::BeginGroup(); + if (ImGui::BeginChild("memview", ImVec2(0, -ImGui::GetFrameHeightWithSpacing()))) { + memView_.Draw(ImGui::GetWindowDrawList()); + ImGui::EndChild(); + } + ImGui::TextUnformatted(memView_.StatusMessage().c_str()); + ImGui::EndGroup(); + ImGui::End(); +} + +const char *ImMemWindow::Title(int index) { + static const char *const titles[4] = { "Memory 1", "Memory 2", "Memory 3", "Memory 4" }; + return titles[index]; +} + +void ImDisasmWindow::Draw(MIPSDebugInterface *mipsDebug, ImConfig &cfg, ImControl &control, CoreState coreState) { disasmView_.setDebugger(mipsDebug); ImGui::SetNextWindowSize(ImVec2(520, 600), ImGuiCond_FirstUseEver); - if (!ImGui::Begin(title, &cfg.disasmOpen, ImGuiWindowFlags_NoNavInputs)) { + if (!ImGui::Begin(Title(), &cfg.disasmOpen, ImGuiWindowFlags_NoNavInputs)) { ImGui::End(); return; } @@ -1185,11 +1290,11 @@ void ImDisasmWindow::Draw(MIPSDebugInterface *mipsDebug, ImConfig &cfg, CoreStat ImGui::SameLine(); if (ImGui::SmallButton("Goto PC")) { - disasmView_.gotoPC(); + disasmView_.GotoPC(); } ImGui::SameLine(); - if (ImGui::SmallButton("Goto LR")) { - disasmView_.gotoLR(); + if (ImGui::SmallButton("Goto RA")) { + disasmView_.GotoRA(); } if (ImGui::BeginPopup("disSearch")) { @@ -1278,7 +1383,7 @@ void ImDisasmWindow::Draw(MIPSDebugInterface *mipsDebug, ImConfig &cfg, CoreStat } ImGui::TableSetColumnIndex(1); - disasmView_.Draw(ImGui::GetWindowDrawList()); + disasmView_.Draw(ImGui::GetWindowDrawList(), control); ImGui::EndTable(); ImGui::TextUnformatted(disasmView_.StatusBarText().c_str()); @@ -1347,6 +1452,11 @@ void ImConfig::SyncConfig(IniFile *ini, bool save) { sync.Sync("geDebuggerOpen", &geDebuggerOpen, false); sync.Sync("geStateOpen", &geStateOpen, false); sync.Sync("schedulerOpen", &schedulerOpen, false); + for (int i = 0; i < 4; i++) { + char name[64]; + snprintf(name, sizeof(name), "memory%dOpen", i + 1); + sync.Sync(name, &memViewOpen[i], false); + } sync.SetSection(ini->GetOrCreateSection("Settings")); sync.Sync("displayLatched", &displayLatched, false); diff --git a/UI/ImDebugger/ImDebugger.h b/UI/ImDebugger/ImDebugger.h index 036b6162ac..1af95f0169 100644 --- a/UI/ImDebugger/ImDebugger.h +++ b/UI/ImDebugger/ImDebugger.h @@ -16,6 +16,7 @@ #include "Core/Debugger/DebugInterface.h" #include "UI/ImDebugger/ImDisasmView.h" +#include "UI/ImDebugger/ImMemView.h" #include "UI/ImDebugger/ImStructViewer.h" #include "UI/ImDebugger/ImGe.h" @@ -31,13 +32,16 @@ struct ImConfig; // Corresponds to the CDisasm dialog class ImDisasmWindow { public: - void Draw(MIPSDebugInterface *mipsDebug, ImConfig &cfg, CoreState coreState); + void Draw(MIPSDebugInterface *mipsDebug, ImConfig &cfg, ImControl &control, CoreState coreState); ImDisasmView &View() { return disasmView_; } void DirtySymbolMap() { symsDirty_ = true; } + const char *Title() const { + return "CPU Debugger"; + } private: // We just keep the state directly in the window. Can refactor later. @@ -46,7 +50,7 @@ private: INVALID_ADDR = 0xFFFFFFFF, }; - u32 gotoAddr_ = 0x1000; + u32 gotoAddr_ = 0x08800000; // Symbol cache std::vector symCache_; @@ -58,6 +62,39 @@ private: char searchTerm_[64]{}; }; +// Corresponds to the CMemView dialog +class ImMemWindow { +public: + void Draw(MIPSDebugInterface *mipsDebug, ImConfig &cfg, ImControl &control, int index); + ImMemView &View() { + return memView_; + } + void DirtySymbolMap() { + symsDirty_ = true; + } + void GotoAddr(u32 addr) { + memView_.gotoAddr(addr); + } + static const char *Title(int index); + +private: + // We just keep the state directly in the window. Can refactor later. + enum { + INVALID_ADDR = 0xFFFFFFFF, + }; + + // Symbol cache + std::vector symCache_; + bool symsDirty_ = true; + int selectedSymbol_ = -1; + char selectedSymbolName_[128]; + + ImMemView memView_; + char searchTerm_[64]{}; + + u32 gotoAddr_ = 0x08800000; +}; + struct ImConfig { // Defaults for saved settings are set in SyncConfig. @@ -82,6 +119,7 @@ struct ImConfig { bool geDebuggerOpen; bool geStateOpen; bool schedulerOpen; + bool memViewOpen[4]; // HLE explorer settings // bool filterByUsed = true; @@ -108,12 +146,13 @@ enum class ImCmd { TRIGGER_FIND_POPUP, SHOW_IN_CPU_DISASM, SHOW_IN_GE_DISASM, - SHOW_IN_MEMORY_VIEWER, + SHOW_IN_MEMORY_VIEWER, // param is address, param2 is viewer index }; struct ImCommand { ImCmd cmd; uint32_t param; + uint32_t param2; }; struct ImControl { @@ -139,11 +178,17 @@ private: ImDisasmWindow disasm_; ImGeDebuggerWindow geDebugger_; ImGeStateWindow geStateWindow_; + ImMemWindow mem_[4]; // We support 4 separate instances of the memory viewer. ImStructViewer structViewer_; + int lastCpuStepCount_ = -1; + int lastGpuStepCount_ = -1; + // Open variables. ImConfig cfg_{}; }; -// Simple custom controls +// Simple custom controls and utilities. void ImClickableAddress(uint32_t addr, ImControl &control, ImCmd cmd); +void ShowInWindowMenuItems(uint32_t addr, ImControl &control); +void ShowInMemoryViewerMenuItem(uint32_t addr, ImControl &control); diff --git a/UI/ImDebugger/ImDisasmView.cpp b/UI/ImDebugger/ImDisasmView.cpp index 48d1a123ce..e0d53513f7 100644 --- a/UI/ImDebugger/ImDisasmView.cpp +++ b/UI/ImDebugger/ImDisasmView.cpp @@ -1,9 +1,9 @@ - #include "ext/imgui/imgui_internal.h" #include "ext/imgui/imgui_impl_thin3d.h" #include "Common/StringUtils.h" #include "Common/Log.h" +#include "Common/Math/geom2d.h" #include "Core/Core.h" #include "Core/Debugger/DebugInterface.h" #include "Core/Debugger/DisassemblyManager.h" @@ -17,6 +17,7 @@ #include "Core/Core.h" #include "Core/CoreParameter.h" #include "UI/ImDebugger/ImDisasmView.h" +#include "UI/ImDebugger/ImDebugger.h" ImDisasmView::ImDisasmView() { curAddress_ = 0; @@ -82,24 +83,6 @@ bool ImDisasmView::getDisasmAddressText(u32 address, char* dest, bool abbreviate } } -// TODO: Replace with another impl. -/* -static std::string trimString(std::string input) { - size_t pos = input.find_first_not_of(" \t"); - if (pos != 0 && pos != std::string::npos) { - input = input.erase(0, pos); - } - - pos = input.find_last_not_of(" \t"); - if (pos != std::string::npos) { - size_t size = input.length() - pos - 1; - input = input.erase(pos + 1, size); - } - - return input; -} -*/ - void ImDisasmView::assembleOpcode(u32 address, const std::string &defaultText) { /* if (!Core_IsStepping()) { @@ -157,7 +140,7 @@ void ImDisasmView::assembleOpcode(u32 address, const std::string &defaultText) { */ } -void ImDisasmView::drawBranchLine(ImDrawList *drawList, Rect rect, std::map &addressPositions, const BranchLine &line) { +void ImDisasmView::drawBranchLine(ImDrawList *drawList, Bounds rect, std::map &addressPositions, const BranchLine &line) { u32 windowEnd = manager.getNthNextAddress(windowStart_, visibleRows_); float topY; @@ -165,7 +148,7 @@ void ImDisasmView::drawBranchLine(ImDrawList *drawList, Rect rect, std::map= windowEnd) { - topY = rect.bottom + 1.0f; + topY = rect.y2() + 1.0f; } else { topY = (float)addressPositions[line.first] + rowHeight_ / 2; } @@ -173,12 +156,12 @@ void ImDisasmView::drawBranchLine(ImDrawList *drawList, Rect rect, std::map= windowEnd) { - bottomY = rect.bottom + 1.0f; + bottomY = rect.y2() + 1.0f; } else { bottomY = (float)addressPositions[line.second] + rowHeight_ / 2; } - if ((topY < 0 && bottomY < 0) || (topY > rect.bottom && bottomY > rect.bottom)) { + if ((topY < 0 && bottomY < 0) || (topY > rect.y2() && bottomY > rect.y2())) { return; } @@ -200,7 +183,7 @@ void ImDisasmView::drawBranchLine(ImDrawList *drawList, Rect rect, std::mapAddLine(ImVec2(rect.left + curX, rect.top + curY), ImVec2(rect.left + (float)x, rect.top + (float)y), pen, 1.0f); + drawList->AddLine(ImVec2(rect.x + curX, rect.y + curY), ImVec2(rect.x + (float)x, rect.y + (float)y), pen, 1.0f); curX = x; curY = y; }; @@ -215,10 +198,10 @@ void ImDisasmView::drawBranchLine(ImDrawList *drawList, Rect rect, std::map rect.bottom) {// second is not visible, but first is + } else if (bottomY > rect.y2()) {// second is not visible, but first is moveTo(x - 2.f, topY); lineTo(x + 2.f, topY); - lineTo(x + 2.f, rect.bottom); + lineTo(x + 2.f, rect.y2()); if (line.type == LINE_UP) { moveTo(x, topY - 4.f); @@ -266,13 +249,13 @@ std::set ImDisasmView::getSelectedLineArguments() { return args; } -void ImDisasmView::drawArguments(ImDrawList *drawList, Rect rc, const DisassemblyLineInfo &line, float x, float y, ImColor textColor, const std::set ¤tArguments) { +void ImDisasmView::drawArguments(ImDrawList *drawList, Bounds rc, const DisassemblyLineInfo &line, float x, float y, ImColor textColor, const std::set ¤tArguments) { if (line.params.empty()) { return; } // Don't highlight the selected lines. if (isInInterval(selectRangeStart_, selectRangeEnd_ - selectRangeStart_, line.info.opcodeAddress)) { - drawList->AddText(ImVec2((float)(rc.left + x), (float)(rc.top + y)), textColor, line.params.data(), line.params.data() + line.params.size()); + drawList->AddText(ImVec2((float)(rc.x + x), (float)(rc.y + y)), textColor, line.params.data(), line.params.data() + line.params.size()); return; } @@ -286,7 +269,7 @@ void ImDisasmView::drawArguments(ImDrawList *drawList, Rect rc, const Disassembl ImColor curColor = textColor; auto Print = [&](std::string_view text) { - drawList->AddText(ImVec2(rc.left + curX, rc.top + curY), curColor, text.data(), text.data() + text.size()); + drawList->AddText(ImVec2(rc.x + curX, rc.y + curY), curColor, text.data(), text.data() + text.size()); ImVec2 sz = ImGui::CalcTextSize(text.data(), text.data() + text.size(), false, -1.0f); curX += sz.x; }; @@ -313,8 +296,8 @@ void ImDisasmView::drawArguments(ImDrawList *drawList, Rect rc, const Disassembl } } -void ImDisasmView::Draw(ImDrawList *drawList) { - if (!debugger->isAlive()) { +void ImDisasmView::Draw(ImDrawList *drawList, ImControl &control) { + if (!debugger_->isAlive()) { return; } @@ -340,7 +323,7 @@ void ImDisasmView::Draw(ImDrawList *drawList) { } ImGui::SetItemKeyOwner(ImGuiKey_MouseWheelY); - ImVec2 canvas_p1 = ImVec2(canvas_p0.x + canvas_sz.x, canvas_p0.y + canvas_sz.y); + const ImVec2 canvas_p1 = ImVec2(canvas_p0.x + canvas_sz.x, canvas_p0.y + canvas_sz.y); drawList->PushClipRect(canvas_p0, canvas_p1, true); drawList->AddRectFilled(canvas_p0, canvas_p1, IM_COL32(25, 25, 25, 255)); @@ -348,15 +331,15 @@ void ImDisasmView::Draw(ImDrawList *drawList) { drawList->AddRect(canvas_p0, canvas_p1, IM_COL32(255, 255, 255, 255)); } - Rect rect; - rect.left = canvas_p0.x; - rect.top = canvas_p0.y; - rect.right = canvas_p1.x; - rect.bottom = canvas_p1.y; + Bounds bounds; + bounds.x = canvas_p0.x; + bounds.y = canvas_p0.y; + bounds.w = canvas_p1.x - canvas_p0.x; + bounds.h = canvas_p1.y - canvas_p0.y; calculatePixelPositions(); - visibleRows_ = (int)((rect.bottom - rect.top + rowHeight_ - 1.f) / rowHeight_); + visibleRows_ = (int)((bounds.h + rowHeight_ - 1.f) / rowHeight_); unsigned int address = windowStart_; std::map addressPositions; @@ -364,7 +347,7 @@ void ImDisasmView::Draw(ImDrawList *drawList) { const std::set currentArguments = getSelectedLineArguments(); DisassemblyLineInfo line; - const u32 pc = debugger->GetPC(); + const u32 pc = debugger_->GetPC(); for (int i = 0; i < visibleRows_; i++) { manager.getLine(address, displaySymbols_, line); @@ -375,7 +358,7 @@ void ImDisasmView::Draw(ImDrawList *drawList) { addressPositions[address] = rowY1; // draw background - ImColor backgroundColor = ImColor(0xFF000000 | debugger->getColor(address, true)); + ImColor backgroundColor = ImColor(0xFF000000 | debugger_->getColor(address, true)); ImColor textColor = 0xFFFFFFFF; if (isInInterval(address, line.totalSize, pc)) { @@ -391,7 +374,7 @@ void ImDisasmView::Draw(ImDrawList *drawList) { } } - drawList->AddRectFilled(ImVec2(rect.left, rect.top + rowY1), ImVec2(rect.right, rect.top + rowY1 + rowHeight_), backgroundColor); + drawList->AddRectFilled(ImVec2(bounds.x, bounds.y + rowY1), ImVec2(bounds.x2(), bounds.y + rowY1 + rowHeight_), backgroundColor); // display breakpoint, if any bool enabled; @@ -405,7 +388,7 @@ void ImDisasmView::Draw(ImDrawList *drawList) { char addressText[64]; getDisasmAddressText(address, addressText, true, line.type == DISTYPE_OPCODE); - drawList->AddText(ImVec2(rect.left + pixelPositions_.addressStart, rect.top + rowY1 + 2), textColor, addressText); + drawList->AddText(ImVec2(bounds.x + pixelPositions_.addressStart, bounds.y + rowY1 + 2), textColor, addressText); if (isInInterval(address, line.totalSize, pc)) { // Show the current PC with a little triangle. @@ -421,18 +404,18 @@ void ImDisasmView::Draw(ImDrawList *drawList) { line.params += line.info.conditionMet ? " ; true" : " ; false"; } - drawArguments(drawList, rect, line, pixelPositions_.argumentsStart, rowY1 + 2.f, textColor, currentArguments); + drawArguments(drawList, bounds, line, pixelPositions_.argumentsStart, rowY1 + 2.f, textColor, currentArguments); // The actual opcode. // Should be bold! - drawList->AddText(ImVec2(rect.left + pixelPositions_.opcodeStart, rect.top + rowY1 + 2.f), textColor, line.name.c_str()); + drawList->AddText(ImVec2(bounds.x + pixelPositions_.opcodeStart, bounds.y + rowY1 + 2.f), textColor, line.name.c_str()); address += line.totalSize; } std::vector branchLines = manager.getBranchLines(windowStart_, address - windowStart_); for (size_t i = 0; i < branchLines.size(); i++) { - drawBranchLine(drawList, rect, addressPositions, branchLines[i]); + drawBranchLine(drawList, bounds, addressPositions, branchLines[i]); } ImGuiIO& io = ImGui::GetIO(); @@ -468,20 +451,11 @@ void ImDisasmView::Draw(ImDrawList *drawList) { ProcessKeyboardShortcuts(ImGui::IsItemFocused()); - int coreStep = Core_GetSteppingCounter(); - if (coreStep != lastSteppingCount_) { - // A step has happened since last time. This means that we should re-center the cursor. - if (followPC_) { - gotoPC(); - } - lastSteppingCount_ = coreStep; - } - if (pressed) { // INFO_LOG(Log::System, "Clicked %f,%f", mousePos.x, mousePos.y); if (mousePos.x < rowHeight_) { // Left column // Toggle breakpoint at dragAddr_. - debugger->toggleBreakpoint(curAddress_); + debugger_->toggleBreakpoint(curAddress_); bpPopup_ = true; } else { // disasmView_.selectedAddr_ = dragAddr_; @@ -492,11 +466,17 @@ void ImDisasmView::Draw(ImDrawList *drawList) { ImGui_PopFont(); ImGui::OpenPopupOnItemClick("context", ImGuiPopupFlags_MouseButtonRight); - PopupMenu(); + PopupMenu(control); drawList->PopClipRect(); } +void ImDisasmView::NotifyStep() { + if (followPC_) { + GotoPC(); + } +} + void ImDisasmView::ScrollRelative(int amount) { if (amount > 0) { windowStart_ = manager.getNthNextAddress(windowStart_, amount); @@ -528,7 +508,8 @@ void ImDisasmView::FollowBranch() { } void ImDisasmView::onChar(int c) { - if (keyTaken) return; + if (keyTaken) + return; char str[2]; str[0] = c; @@ -638,7 +619,7 @@ void ImDisasmView::ProcessKeyboardShortcuts(bool focused) { } if (ImGui::IsKeyPressed(ImGuiKey_LeftArrow)) { if (jumpStack_.empty()) { - gotoPC(); + GotoPC(); } else { u32 addr = jumpStack_[jumpStack_.size() - 1]; jumpStack_.pop_back(); @@ -758,14 +739,14 @@ void ImDisasmView::CopyInstructions(u32 startAddr, u32 endAddr, CopyInstructions _assert_msg_((startAddr & 3) == 0, "readMemory() can't handle unaligned reads"); if (mode != CopyInstructionsMode::DISASM) { - int instructionSize = debugger->getInstructionSize(0); + int instructionSize = debugger_->getInstructionSize(0); int count = (endAddr - startAddr) / instructionSize; int space = count * 32; char *temp = new char[space]; char *p = temp, *end = temp + space; for (u32 pos = startAddr; pos < endAddr && p < end; pos += instructionSize) { - u32 data = mode == CopyInstructionsMode::OPCODES ? debugger->readMemory(pos) : pos; + u32 data = mode == CopyInstructionsMode::OPCODES ? debugger_->readMemory(pos) : pos; p += snprintf(p, end - p, "%08X", data); // Don't leave a trailing newline. @@ -780,39 +761,27 @@ void ImDisasmView::CopyInstructions(u32 startAddr, u32 endAddr, CopyInstructions } } -void ImDisasmView::NopInstructions(u32 selectRangeStart, u32 selectRangeEnd) { - for (u32 addr = selectRangeStart; addr < selectRangeEnd; addr += 4) { - Memory::Write_U32(0, addr); - } - - if (currentMIPS) { - currentMIPS->InvalidateICache(selectRangeStart, selectRangeEnd - selectRangeStart); - } -} - -void ImDisasmView::PopupMenu() { +void ImDisasmView::PopupMenu(ImControl &control) { bool renameFunctionPopup = false; if (ImGui::BeginPopup("context")) { ImGui::Text("Address: %08x", curAddress_); if (ImGui::MenuItem("Toggle breakpoint", "F9")) { toggleBreakpoint(); } - if (ImGui::MenuItem("Go to in memory view")) { - // SendMessage(GetParent(wnd), WM_DEB_GOTOHEXEDIT, curAddress, 0); + ShowInMemoryViewerMenuItem(curAddress_, control); + if (ImGui::MenuItem("Copy address")) { + CopyInstructions(selectRangeStart_, selectRangeEnd_, CopyInstructionsMode::ADDRESSES); } if (ImGui::MenuItem("Copy instruction (disasm)")) { CopyInstructions(selectRangeStart_, selectRangeEnd_, CopyInstructionsMode::DISASM); } - if (ImGui::MenuItem("Copy address")) { - CopyInstructions(selectRangeStart_, selectRangeEnd_, CopyInstructionsMode::ADDRESSES); - } if (ImGui::MenuItem("Copy instruction (hex)")) { CopyInstructions(selectRangeStart_, selectRangeEnd_, CopyInstructionsMode::OPCODES); } ImGui::Separator(); if (ImGui::MenuItem("Set PC to here")) { - debugger->SetPC(curAddress_); + debugger_->SetPC(curAddress_); } if (ImGui::MenuItem("Follow branch")) { FollowBranch(); @@ -829,7 +798,12 @@ void ImDisasmView::PopupMenu() { assembleOpcode(curAddress_, ""); } if (ImGui::MenuItem("NOP instructions (destructive)")) { - NopInstructions(selectRangeStart_, selectRangeEnd_); + for (u32 addr = selectRangeStart_; addr < selectRangeEnd_; addr += 4) { + Memory::Write_U32(0, addr); + } + if (currentMIPS) { + currentMIPS->InvalidateICache(selectRangeStart_, selectRangeEnd_ - selectRangeStart_); + } } ImGui::Separator(); if (ImGui::MenuItem("Rename function")) { @@ -1112,8 +1086,8 @@ std::string ImDisasmView::disassembleRange(u32 start, u32 size) { // gather all branch targets without labels std::set branchAddresses; - for (u32 i = 0; i < size; i += debugger->getInstructionSize(0)) { - MIPSAnalyst::MipsOpcodeInfo info = MIPSAnalyst::GetOpcodeInfo(debugger, start + i); + for (u32 i = 0; i < size; i += debugger_->getInstructionSize(0)) { + MIPSAnalyst::MipsOpcodeInfo info = MIPSAnalyst::GetOpcodeInfo(debugger_, start + i); if (info.isBranch && g_symbolMap->GetLabelString(info.branchTarget).empty()) { if (branchAddresses.find(info.branchTarget) == branchAddresses.end()) { diff --git a/UI/ImDebugger/ImDisasmView.h b/UI/ImDebugger/ImDisasmView.h index 00906c45cb..70c81c5320 100644 --- a/UI/ImDebugger/ImDisasmView.h +++ b/UI/ImDebugger/ImDisasmView.h @@ -8,11 +8,13 @@ #include "ext/imgui/imgui.h" #include "Common/CommonTypes.h" +#include "Common/Math/geom2d.h" #include "Core/Debugger/DisassemblyManager.h" #include "Core/Debugger/DebugInterface.h" struct ImConfig; +struct ImControl; // Corresponds to CtrlDisAsmView // TODO: Fold out common code. @@ -24,9 +26,10 @@ public: // Public variables bounds to imgui checkboxes bool followPC_ = true; - void Draw(ImDrawList *drawList); + void Draw(ImDrawList *drawList, ImControl &control); - void PopupMenu(); + void PopupMenu(ImControl &control); + void NotifyStep(); void ScrollRelative(int amount); @@ -43,14 +46,14 @@ public: u32 yToAddress(float y); void setDebugger(DebugInterface *deb) { - if (debugger != deb) { - debugger = deb; - curAddress_ = debugger->GetPC(); + if (debugger_ != deb) { + debugger_ = deb; + curAddress_ = debugger_->GetPC(); manager.setCpu(deb); } } DebugInterface *getDebugger() { - return debugger; + return debugger_; } void scrollStepping(u32 newPc); @@ -69,11 +72,11 @@ public: setCurAddress(newAddress); ScanVisibleFunctions(); } - void gotoPC() { - gotoAddr(debugger->GetPC()); + void GotoPC() { + gotoAddr(debugger_->GetPC()); } - void gotoLR() { - gotoAddr(debugger->GetLR()); + void GotoRA() { + gotoAddr(debugger_->GetRA()); } u32 getSelection() { return curAddress_; @@ -120,13 +123,6 @@ private: ADDRESSES, }; - struct Rect { - float left; - float top; - float right; - float bottom; - }; - void ProcessKeyboardShortcuts(bool focused); void assembleOpcode(u32 address, const std::string &defaultText); std::string disassembleRange(u32 start, u32 size); @@ -135,11 +131,10 @@ private: void calculatePixelPositions(); bool getDisasmAddressText(u32 address, char* dest, bool abbreviateLabels, bool showData); void updateStatusBarText(); - void drawBranchLine(ImDrawList *list, ImDisasmView::Rect rc, std::map &addressPositions, const BranchLine &line); + void drawBranchLine(ImDrawList *list, Bounds rc, std::map &addressPositions, const BranchLine &line); void CopyInstructions(u32 startAddr, u32 endAddr, CopyInstructionsMode mode); - void NopInstructions(u32 startAddr, u32 endAddr); std::set getSelectedLineArguments(); - void drawArguments(ImDrawList *list, ImDisasmView::Rect rc, const DisassemblyLineInfo &line, float x, float y, ImColor textColor, const std::set ¤tArguments); + void drawArguments(ImDrawList *list, Bounds rc, const DisassemblyLineInfo &line, float x, float y, ImColor textColor, const std::set ¤tArguments); DisassemblyManager manager; u32 curAddress_ = 0; @@ -152,7 +147,7 @@ private: bool hasFocus_ = true; bool showHex_ = false; - DebugInterface *debugger = nullptr; + DebugInterface *debugger_ = nullptr; u32 windowStart_ = 0; int visibleRows_ = 1; @@ -163,7 +158,7 @@ private: int opcodeStart; int argumentsStart; int arrowsStart; - } pixelPositions_; + } pixelPositions_{}; std::vector jumpStack_; @@ -174,9 +169,8 @@ private: bool mapReloaded_ = false; int positionLocked_ = 0; - int lastSteppingCount_ = 0; std::string statusBarText_; - u32 funcBegin_; - char funcNameTemp_[128]; + u32 funcBegin_ = 0; + char funcNameTemp_[128]{}; }; diff --git a/UI/ImDebugger/ImGe.cpp b/UI/ImDebugger/ImGe.cpp index a6a450b588..52fae57567 100644 --- a/UI/ImDebugger/ImGe.cpp +++ b/UI/ImDebugger/ImGe.cpp @@ -14,6 +14,7 @@ #include "GPU/Debugger/State.h" #include "GPU/Debugger/GECommandTable.h" #include "GPU/Debugger/Breakpoints.h" +#include "GPU/Debugger/Stepping.h" #include "GPU/Debugger/Debugger.h" #include "GPU/GPUState.h" @@ -82,6 +83,12 @@ void DrawDebugStatsWindow(ImConfig &cfg) { ImGui::End(); } +void ImGeDisasmView::NotifyStep() { + if (followPC_) { + gotoPC_ = true; + } +} + void ImGeDisasmView::Draw(GPUDebugInterface *gpuDebug) { const u32 branchColor = 0xFFA0FFFF; const u32 gteColor = 0xFFFFEFA0; @@ -123,7 +130,7 @@ void ImGeDisasmView::Draw(GPUDebugInterface *gpuDebug) { } if (pc != 0xFFFFFFFF) { - if (gotoPC_ || followPC_) { + if (gotoPC_) { selectedAddr_ = pc; gotoPC_ = false; } @@ -238,7 +245,7 @@ void ImGeDisasmView::Draw(GPUDebugInterface *gpuDebug) { void ImGeDebuggerWindow::Draw(ImConfig &cfg, ImControl &control, GPUDebugInterface *gpuDebug) { ImGui::SetNextWindowSize(ImVec2(520, 600), ImGuiCond_FirstUseEver); - if (!ImGui::Begin("GE Debugger", &cfg.geDebuggerOpen)) { + if (!ImGui::Begin(Title(), &cfg.geDebuggerOpen)) { ImGui::End(); return; } diff --git a/UI/ImDebugger/ImGe.h b/UI/ImDebugger/ImGe.h index a001977cf4..f4cadbda75 100644 --- a/UI/ImDebugger/ImGe.h +++ b/UI/ImDebugger/ImGe.h @@ -28,6 +28,8 @@ public: selectedAddr_ = addr; } + void NotifyStep(); + private: u32 selectedAddr_ = INVALID_ADDR; u32 dragAddr_ = INVALID_ADDR; @@ -52,6 +54,9 @@ public: ImGeDisasmView &View() { return disasmView_; } + const char *Title() const { + return "GE Debugger"; + } private: ImGeDisasmView disasmView_; diff --git a/UI/ImDebugger/ImMemView.cpp b/UI/ImDebugger/ImMemView.cpp new file mode 100644 index 0000000000..b3eb3e5824 --- /dev/null +++ b/UI/ImDebugger/ImMemView.cpp @@ -0,0 +1,856 @@ +#include +#include +#include +#include + +#include "ext/imgui/imgui.h" +#include "ext/imgui/imgui_internal.h" +#include "ext/imgui/imgui_impl_thin3d.h" + +#include "ext/xxhash.h" +#include "Common/StringUtils.h" +#include "Core/Config.h" +#include "Core/MemMap.h" +#include "Core/Reporting.h" +#include "Core/RetroAchievements.h" +#include "Common/System/Display.h" + +#include "UI/ImDebugger/ImDebugger.h" +#include "UI/ImDebugger/ImMemView.h" + +ImMemView::ImMemView() { + windowStart_ = curAddress_; + selectRangeStart_ = curAddress_; + selectRangeEnd_ = curAddress_ + 1; + lastSelectReset_ = curAddress_; +} + +ImMemView::~ImMemView() {} + +/* +LRESULT CALLBACK ImMemView::wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { + if (GET_WHEEL_DELTA_WPARAM(wParam) > 0) { + ccp->ScrollWindow(-3, GotoModeFromModifiers(false)); + } else if (GET_WHEEL_DELTA_WPARAM(wParam) < 0) { + ccp->ScrollWindow(3, GotoModeFromModifiers(false)); + } + return DefWindowProc(hwnd, msg, wParam, lParam); +} +*/ + +static uint32_t pickTagColor(std::string_view tag) { + uint32_t colors[6] = { 0xFF301010, 0xFF103030, 0xFF403010, 0xFF103000, 0xFF301030, 0xFF101030 }; + int which = XXH3_64bits(tag.data(), tag.length()) % ARRAY_SIZE(colors); + return colors[which]; +} + +void ImMemView::Draw(ImDrawList *drawList) { + if (!debugger_->isAlive()) { + return; + } + + ImGui_PushFixedFont(); + + rowHeight_ = ImGui::GetTextLineHeightWithSpacing(); + charWidth_ = ImGui::CalcTextSize("W", nullptr, false, -1.0f).x; + charHeight_ = ImGui::GetTextLineHeight(); + + offsetPositionY_ = offsetLine * rowHeight_; + + addressStartX_ = charWidth_; + hexStartX_ = addressStartX_ + 9 * charWidth_; + asciiStartX_ = hexStartX_ + (rowSize_ * 3 + 1) * charWidth_; + + const ImVec2 canvas_p0 = ImGui::GetCursorScreenPos(); // ImDrawList API uses screen coordinates! + const ImVec2 canvas_sz = ImGui::GetContentRegionAvail(); // Resize canvas to what's available + const ImVec2 canvas_p1 = ImVec2(canvas_p0.x + canvas_sz.x, canvas_p0.y + canvas_sz.y); + + // This will catch our interactions + bool pressed = ImGui::InvisibleButton("canvas", canvas_sz, ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonRight); + const bool is_hovered = ImGui::IsItemHovered(); // Hovered + const bool is_active = ImGui::IsItemActive(); // Held + + if (pressed) { + // INFO_LOG(Log::System, "Pressed"); + } + ImGui::SetItemKeyOwner(ImGuiKey_MouseWheelY); + + visibleRows_ = canvas_sz.y / rowHeight_; + + if (displayOffsetScale_) { + // visibleRows_ is calculated based on the size of the control, but X rows have already been used for the offsets and are no longer usable + visibleRows_ -= offsetSpace; + } + + Bounds bounds; + bounds.x = canvas_p0.x; + bounds.y = canvas_p0.y; + bounds.w = canvas_p1.x - canvas_p0.x; + bounds.h = canvas_p1.y - canvas_p0.y; + + drawList->PushClipRect(canvas_p0, canvas_p1, true); + drawList->AddRectFilled(canvas_p0, canvas_p1, IM_COL32(25, 25, 25, 255)); + + if (displayOffsetScale_) + drawOffsetScale(drawList); + + std::vector memRangeInfo = FindMemInfoByFlag(highlightFlags_, windowStart_, (visibleRows_ + 1) * rowSize_); + + _assert_msg_(((windowStart_ | rowSize_) & 3) == 0, "readMemory() can't handle unaligned reads"); + + // draw one extra row that may be partially visible + for (int i = 0; i < visibleRows_ + 1; i++) { + int rowY = rowHeight_ * i; + // Skip the first X rows to make space for the offsets. + if (displayOffsetScale_) + rowY += rowHeight_ * offsetSpace; + + char temp[32]; + uint32_t address = windowStart_ + i * rowSize_; + snprintf(temp, sizeof(temp), "%08X", address); + drawList->AddText(ImVec2(canvas_p0.x + addressStartX_, canvas_p0.y + rowY), IM_COL32(0xE0, 0xE0, 0xE0, 0xFF), temp); + + union { + uint32_t words[4]; + uint8_t bytes[16]; + } memory; + int valid = debugger_ != nullptr && debugger_->isAlive() ? Memory::ValidSize(address, 16) / 4 : 0; + for (int i = 0; i < valid; ++i) { + memory.words[i] = debugger_->readMemory(address + i * 4); + } + + for (int j = 0; j < rowSize_; j++) { + const uint32_t byteAddress = (address + j) & ~0xC0000000; + std::string_view tag; + bool tagContinues = false; + for (const auto &info : memRangeInfo) { + if (info.start <= byteAddress && info.start + info.size > byteAddress) { + tag = info.tag; // doesn't take ownership! + tagContinues = byteAddress + 1 < info.start + info.size; + } + } + + int hexX = hexStartX_ + j * 3 * charWidth_; + int hexLen = 2; + int asciiX = asciiStartX_ + j * (charWidth_ + 2); + + char c; + if (valid) { + snprintf(temp, sizeof(temp), "%02X ", memory.bytes[j]); + c = (char)memory.bytes[j]; + if (memory.bytes[j] < 32 || memory.bytes[j] >= 128) + c = '.'; + } else { + truncate_cpy(temp, "??"); + c = '.'; + } + + ImColor standardBG = 0xFF000000; + bool continueRect = false; + + ImColor hexBGCol = 0; + ImColor hexTextCol = 0xFFFFFFFF; + ImColor asciiBGCol = 0; + ImColor asciiTextCol = 0xFFFFFFFF; + int underline = -1; + + const ImColor primarySelFg = 0xFFFFFFFF; + const ImColor primarySelBg = 0xFFFF9933; + const ImColor secondarySelFg = 0xFFFFFFFF; + const ImColor secondarySelBg = 0xFF808080; + + if (address + j >= selectRangeStart_ && address + j < selectRangeEnd_ && !searching_) { + if (asciiSelected_) { + hexBGCol = secondarySelBg; + hexTextCol = secondarySelFg; + asciiBGCol = primarySelBg; + asciiTextCol = primarySelFg; + } else { + hexBGCol = primarySelBg; + hexTextCol = primarySelFg; + asciiBGCol = secondarySelBg; + asciiTextCol = secondarySelFg; + if (address + j == curAddress_) + underline = selectedNibble_; + } + if (!tag.empty() && tagContinues) { + continueRect = true; + } + } else if (!tag.empty()) { + hexBGCol = pickTagColor(tag); + continueRect = tagContinues; + asciiBGCol = hexBGCol; + } + + ImColor fg = hexTextCol; + ImColor bg = hexBGCol; + // SelectObject(hdc, underline == 0 ? (HGDIOBJ)underlineFont : (HGDIOBJ)font); + if (bg != 0) { + int bgWidth = 2; // continueRect ? 3 : 2; + drawList->AddRectFilled(ImVec2(canvas_p0.x + hexX - 1, canvas_p0.y + rowY), ImVec2(canvas_p0.x + hexX + charWidth_ * bgWidth, canvas_p0.y + rowY + charHeight_), bg); + drawList->AddText(ImVec2(canvas_p0.x + hexX, canvas_p0.y + rowY), fg, &temp[0], &temp[2]); + } + if (underline >= 0) { + float x = canvas_p0.x + hexX + underline * charWidth_; + drawList->AddRectFilled(ImVec2(x, canvas_p0.y + rowY + charHeight_ - 2), ImVec2(x + charWidth_, canvas_p0.y + rowY + charHeight_), IM_COL32(0xFF, 0xFF, 0xFF, 0xFF)); + } + + fg = asciiTextCol; + bg = asciiBGCol; + drawList->AddRectFilled(ImVec2(canvas_p0.x + asciiX, canvas_p0.y + rowY), ImVec2(canvas_p0.x + asciiX + charWidth_, canvas_p0.y + rowY + charHeight_), bg); + drawList->AddText(ImVec2(canvas_p0.x + asciiX, canvas_p0.y + rowY), fg, &c, &c + 1); + } + } + + ImGuiIO& io = ImGui::GetIO(); + ImVec2 mousePos = ImVec2(io.MousePos.x - canvas_p0.x, io.MousePos.y - canvas_p0.y); + if (is_hovered && ImGui::IsMouseClicked(ImGuiMouseButton_Left)) { + // INFO_LOG(Log::System, "Mousedown %f,%f active:%d hover:%d", mousePos.x, mousePos.y, is_active, is_hovered); + onMouseDown(mousePos.x, mousePos.y, 1); + } + if (is_hovered && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) { + // INFO_LOG(Log::CPU, "Mousedown %f,%f active:%d hover:%d", mousePos.x, mousePos.y, is_active, is_hovered); + onMouseDown(mousePos.x, mousePos.y, 2); + } + if (ImGui::IsMouseReleased(ImGuiMouseButton_Left)) { + // INFO_LOG(Log::System, "Mouseup %f,%f active:%d hover:%d", mousePos.x, mousePos.y, is_active, is_hovered); + if (is_hovered) { + onMouseUp(mousePos.x, mousePos.y, 1); + } + } + if (ImGui::IsMouseDragging(ImGuiMouseButton_Left)) { + // INFO_LOG(Log::System, "Mousedrag %f,%f active:%d hover:%d", mousePos.x, mousePos.y, is_active, is_hovered); + if (is_hovered) { + onMouseMove(mousePos.x, mousePos.y, 1); + } + } + + if (is_hovered) { + if (io.MouseWheel > 0.0f) { // TODO: Scale steps by the value. + windowStart_ -= rowSize_ * 4; + } else if (io.MouseWheel < 0.0f) { + windowStart_ += rowSize_ * 4; + } + } + + ProcessKeyboardShortcuts(ImGui::IsItemFocused()); + + ImGui_PopFont(); + + ImGui::OpenPopupOnItemClick("memcontext", ImGuiPopupFlags_MouseButtonRight); + PopupMenu(); + + drawList->PopClipRect(); +} + +// We don't have a scroll bar - though maybe we should build one. +/* +void ImMemView::onVScroll(WPARAM wParam, LPARAM lParam) { + switch (wParam & 0xFFFF) { + case SB_LINEDOWN: + ScrollWindow(1, GotoModeFromModifiers(false)); + break; + case SB_LINEUP: + ScrollWindow(-1, GotoModeFromModifiers(false)); + break; + case SB_PAGEDOWN: + ScrollWindow(visibleRows_, GotoModeFromModifiers(false)); + break; + case SB_PAGEUP: + ScrollWindow(-visibleRows_, GotoModeFromModifiers(false)); + break; + default: + return; + } +} +*/ + +void ImMemView::ProcessKeyboardShortcuts(bool focused) { + if (!focused) { + return; + } + + ImGuiIO& io = ImGui::GetIO(); + + if (io.KeyMods & ImGuiMod_Ctrl) { + if (ImGui::IsKeyPressed(ImGuiKey_G)) { + /* + u32 addr; + if (executeExpressionWindow(wnd, debugger_, addr) == false) + return; + gotoAddr(addr); + return; + */ + } + if (ImGui::IsKeyPressed(ImGuiKey_F)) { + search(false); + } + if (ImGui::IsKeyPressed(ImGuiKey_C)) { + search(true); + } + } else { + if (ImGui::IsKeyPressed(ImGuiKey_DownArrow)) { + ScrollCursor(rowSize_, GotoModeFromModifiers(false)); + } + if (ImGui::IsKeyPressed(ImGuiKey_UpArrow)) { + ScrollCursor(-rowSize_, GotoModeFromModifiers(false)); + } + if (ImGui::IsKeyPressed(ImGuiKey_LeftArrow)) { + ScrollCursor(-1, GotoModeFromModifiers(false)); + } + if (ImGui::IsKeyPressed(ImGuiKey_RightArrow)) { + ScrollCursor(1, GotoModeFromModifiers(false)); + } + if (ImGui::IsKeyPressed(ImGuiKey_PageDown)) { + ScrollWindow(visibleRows_, GotoModeFromModifiers(false)); + } + if (ImGui::IsKeyPressed(ImGuiKey_PageUp)) { + ScrollWindow(-visibleRows_, GotoModeFromModifiers(false)); + } + } +} + +void ImMemView::onChar(int c) { + if (!PSP_IsInited()) + return; + + ImGuiIO& io = ImGui::GetIO(); + + if (io.KeyMods & ImGuiMod_Ctrl) { + return; + } + + if (!Memory::IsValidAddress(curAddress_)) { + ScrollCursor(1, GotoMode::RESET); + return; + } + + bool active = Core_IsActive(); + if (active) + Core_Break("memory.access", curAddress_); + + if (asciiSelected_) { + Memory::WriteUnchecked_U8((u8)c, curAddress_); + ScrollCursor(1, GotoMode::RESET); + } else { + c = tolower(c); + int inputValue = -1; + + if (c >= '0' && c <= '9') inputValue = c - '0'; + if (c >= 'a' && c <= 'f') inputValue = c - 'a' + 10; + + if (inputValue >= 0) { + int shiftAmount = (1 - selectedNibble_) * 4; + + u8 oldValue = Memory::ReadUnchecked_U8(curAddress_); + oldValue &= ~(0xF << shiftAmount); + u8 newValue = oldValue | (inputValue << shiftAmount); + Memory::WriteUnchecked_U8(newValue, curAddress_); + ScrollCursor(1, GotoMode::RESET); + } + } + + Reporting::NotifyDebugger(); + if (active) + Core_Resume(); +} + +ImMemView::GotoMode ImMemView::GotoModeFromModifiers(bool isRightClick) { + GotoMode mode = GotoMode::RESET; + auto &io = ImGui::GetIO(); + if (isRightClick) { + mode = GotoMode::RESET_IF_OUTSIDE; + } else if (io.KeyMods & ImGuiMod_Shift) { + if (io.KeyMods & ImGuiMod_Ctrl) + mode = GotoMode::EXTEND; + else + mode = GotoMode::FROM_CUR; + } + return mode; +} + +void ImMemView::onMouseDown(float x, float y, int button) { + if (Achievements::HardcoreModeActive()) + return; + + GotoPoint(x, y, GotoModeFromModifiers(button == 2)); +} + +void ImMemView::PopupMenu() { + if (ImGui::BeginPopup("memcontext")) { + + int32_t selectedSize = selectRangeEnd_ - selectRangeStart_; + bool enable16 = !asciiSelected_ && (selectedSize == 1 || (selectedSize & 1) == 0); + bool enable32 = !asciiSelected_ && (selectedSize == 1 || (selectedSize & 3) == 0); + /* + if (ImGui::MenuItem("Dump memory")) { + DumpMemoryWindow dump(wnd, debugger_); + dump.exec(); + break; + } + */ + + if (ImGui::MenuItem("Copy value (8-bit)")) { + size_t tempSize = 3 * selectedSize + 1; + char *temp = new char[tempSize]; + memset(temp, 0, tempSize); + + // it's admittedly not really useful like this + if (asciiSelected_) { + for (uint32_t p = selectRangeStart_; p != selectRangeEnd_; ++p) { + uint8_t c = Memory::IsValidAddress(p) ? Memory::ReadUnchecked_U8(p) : '.'; + if (c < 32 || c >= 128) + c = '.'; + temp[p - selectRangeStart_] = c; + } + } else { + char *pos = temp; + for (uint32_t p = selectRangeStart_; p != selectRangeEnd_; ++p) { + uint8_t c = Memory::IsValidAddress(p) ? Memory::ReadUnchecked_U8(p) : 0xFF; + pos += snprintf(pos, tempSize - (pos - temp + 1), "%02X ", c); + } + // Clear the last space. + if (pos > temp) + *(pos - 1) = '\0'; + } + + System_CopyStringToClipboard(temp); + delete[] temp; + } + + if (ImGui::MenuItem("Copy value (16-bit)")) { + size_t tempSize = 5 * ((selectedSize + 1) / 2) + 1; + char *temp = new char[tempSize]; + memset(temp, 0, tempSize); + + char *pos = temp; + for (uint32_t p = selectRangeStart_; p < selectRangeEnd_; p += 2) { + uint16_t c = Memory::IsValidRange(p, 2) ? Memory::ReadUnchecked_U16(p) : 0xFFFF; + pos += snprintf(pos, tempSize - (pos - temp + 1), "%04X ", c); + } + // Clear the last space. + if (pos > temp) + *(pos - 1) = '\0'; + + System_CopyStringToClipboard(temp); + delete[] temp; + } + + if (ImGui::MenuItem("Copy value (32-bit)")) { + size_t tempSize = 9 * ((selectedSize + 3) / 4) + 1; + char *temp = new char[tempSize]; + memset(temp, 0, tempSize); + + char *pos = temp; + for (uint32_t p = selectRangeStart_; p < selectRangeEnd_; p += 4) { + uint32_t c = Memory::IsValidRange(p, 4) ? Memory::ReadUnchecked_U32(p) : 0xFFFFFFFF; + pos += snprintf(pos, tempSize - (pos - temp + 1), "%08X ", c); + } + // Clear the last space. + if (pos > temp) + *(pos - 1) = '\0'; + + System_CopyStringToClipboard(temp); + delete[] temp; + } + + if (ImGui::MenuItem("Copy value (float32)")) { + char temp[64]; + snprintf(temp, sizeof(temp), "%f", Memory::IsValidAddress(curAddress_) ? Memory::Read_Float(curAddress_) : NAN); + System_CopyStringToClipboard(temp); + } + /* + case ID_MEMVIEW_EXTENTBEGIN: + { + std::vector memRangeInfo = FindMemInfoByFlag(highlightFlags_, curAddress_, 1); + uint32_t addr = curAddress_; + for (MemBlockInfo info : memRangeInfo) { + addr = info.start; + } + gotoAddr(addr); + break; + } + + case ID_MEMVIEW_EXTENTEND: + { + std::vector memRangeInfo = FindMemInfoByFlag(highlightFlags_, curAddress_, 1); + uint32_t addr = curAddress_; + for (MemBlockInfo info : memRangeInfo) { + addr = info.start + info.size - 1; + } + gotoAddr(addr); + break; + } + */ + if (ImGui::MenuItem("Copy address")) { + char temp[24]; + snprintf(temp, sizeof(temp), "0x%08X", curAddress_); + System_CopyStringToClipboard(temp); + } + + if (ImGui::MenuItem("Goto in disasm")) { + /* + if (disasmWindow) { + disasmWindow->Goto(curAddress_); + disasmWindow->Show(true); + } + */ + } + ImGui::EndPopup(); + } +} + +void ImMemView::onMouseUp(float x, float y, int button) { + if (button == 2) { + + // HMENU menu = GetContextMenu(ContextMenuID::MEMVIEW); + // EnableMenuItem(menu, ID_MEMVIEW_COPYVALUE_16, enable16 ? MF_ENABLED : MF_GRAYED); + // EnableMenuItem(menu, ID_MEMVIEW_COPYVALUE_32, enable32 ? MF_ENABLED : MF_GRAYED); + // EnableMenuItem(menu, ID_MEMVIEW_COPYFLOAT_32, enable32 ? MF_ENABLED : MF_GRAYED); + + } + + /* + int x = LOWORD(lParam); + int y = HIWORD(lParam); + ReleaseCapture(); + */ + GotoPoint(x, y, GotoModeFromModifiers(button == 2)); +} + +void ImMemView::onMouseMove(float x, float y, int button) { + if (Achievements::HardcoreModeActive()) + return; + + if (button & 1) { + GotoPoint(x, y, GotoModeFromModifiers(button == 2)); + } +} + +void ImMemView::updateStatusBarText() { + std::vector memRangeInfo = FindMemInfoByFlag(highlightFlags_, curAddress_, 1); + + char text[512]; + snprintf(text, sizeof(text), "%08X", curAddress_); + // There should only be one. + for (MemBlockInfo info : memRangeInfo) { + snprintf(text, sizeof(text), "%08X - %s %08X-%08X (at PC %08X / %lld ticks)", curAddress_, info.tag.c_str(), info.start, info.start + info.size, info.pc, info.ticks); + } + statusMessage_ = text; +} + +void ImMemView::UpdateSelectRange(uint32_t target, GotoMode mode) { + if (mode == GotoMode::FROM_CUR && lastSelectReset_ == 0) { + lastSelectReset_ = curAddress_; + } + + switch (mode) { + case GotoMode::RESET: + selectRangeStart_ = target; + selectRangeEnd_ = target + 1; + lastSelectReset_ = target; + break; + + case GotoMode::RESET_IF_OUTSIDE: + if (target < selectRangeStart_ || target >= selectRangeEnd_) { + selectRangeStart_ = target; + selectRangeEnd_ = target + 1; + lastSelectReset_ = target; + } + break; + + case GotoMode::FROM_CUR: + selectRangeStart_ = lastSelectReset_ > target ? target : lastSelectReset_; + selectRangeEnd_ = selectRangeStart_ == lastSelectReset_ ? target + 1 : lastSelectReset_ + 1; + break; + + case GotoMode::EXTEND: + if (target < selectRangeStart_) + selectRangeStart_ = target; + if (target > selectRangeEnd_) + selectRangeEnd_ = target; + break; + } + curAddress_ = target; +} + +void ImMemView::GotoPoint(int x, int y, GotoMode mode) { + int line = y / rowHeight_; + int lineAddress = windowStart_ + line * rowSize_; + + if (displayOffsetScale_) { + // ignore clicks on the offset space + if (line < offsetSpace) { + updateStatusBarText(); + return; + } + // since each row has been written X rows down from where the window expected it to be written the target of the clicks must be adjusted + lineAddress -= rowSize_ * offsetSpace; + } + + uint32_t target = curAddress_; + uint32_t targetNibble = selectedNibble_; + bool targetAscii = asciiSelected_; + if (x >= asciiStartX_) { + int col = (x - asciiStartX_) / (charWidth_ + 2); + if (col >= rowSize_) + return; + + targetAscii = true; + target = lineAddress + col; + targetNibble = 0; + } else if (x >= hexStartX_) { + int col = (x - hexStartX_) / charWidth_; + if ((col / 3) >= rowSize_) + return; + + switch (col % 3) { + case 0: targetNibble = 0; break; + case 1: targetNibble = 1; break; + // case 2: return; // don't change position when clicking on the space + case 2: targetNibble = 0; break; // TODO: split the difference? Anyway, this feels better. + } + + targetAscii = false; + target = lineAddress + col / 3; + } + + if (target != curAddress_ || targetNibble != selectedNibble_ || targetAscii != asciiSelected_) { + selectedNibble_ = targetNibble; + asciiSelected_ = targetAscii; + UpdateSelectRange(target, mode); + updateStatusBarText(); + } +} + +void ImMemView::gotoAddr(unsigned int addr) { + int lines = visibleRows_; + u32 windowEnd = windowStart_ + lines * rowSize_; + + curAddress_ = addr; + lastSelectReset_ = curAddress_; + selectRangeStart_ = curAddress_; + selectRangeEnd_ = curAddress_ + 1; + selectedNibble_ = 0; + + if (curAddress_ < windowStart_ || curAddress_ >= windowEnd) { + windowStart_ = curAddress_ & ~15; + } + + updateStatusBarText(); +} + +void ImMemView::ScrollWindow(int lines, GotoMode mode) { + windowStart_ += lines * rowSize_; + + UpdateSelectRange(curAddress_ + lines * rowSize_, mode); + + updateStatusBarText(); +} + +void ImMemView::ScrollCursor(int bytes, GotoMode mode) { + if (!asciiSelected_ && bytes == 1) { + if (selectedNibble_ == 0) { + selectedNibble_ = 1; + bytes = 0; + } else { + selectedNibble_ = 0; + } + } else if (!asciiSelected_ && bytes == -1) { + if (selectedNibble_ == 0) { + selectedNibble_ = 1; + } else { + selectedNibble_ = 0; + bytes = 0; + } + } + + UpdateSelectRange(curAddress_ + bytes, mode); + + u32 windowEnd = windowStart_ + visibleRows_ * rowSize_; + if (curAddress_ < windowStart_) { + windowStart_ = curAddress_ & ~15; + } else if (curAddress_ >= windowEnd) { + windowStart_ = (curAddress_ - (visibleRows_ - 1) * rowSize_) & ~15; + } + + updateStatusBarText(); +} + +bool ImMemView::ParseSearchString(const std::string &query, bool asHex, std::vector &data) { + data.clear(); + if (!asHex) { + for (size_t i = 0; i < query.length(); i++) { + data.push_back(query[i]); + } + return true; + } + + for (size_t index = 0; index < query.size(); ) { + if (isspace(query[index])) { + index++; + continue; + } + + u8 value = 0; + for (int i = 0; i < 2 && index < query.size(); i++) { + char c = tolower(query[index++]); + if (c >= 'a' && c <= 'f') { + value |= (c - 'a' + 10) << (1 - i) * 4; + } else if (c >= '0' && c <= '9') { + value |= (c - '0') << (1 - i) * 4; + } else { + return false; + } + } + + data.push_back(value); + } + + return true; +} + +std::vector ImMemView::searchString(const std::string &searchQuery) { + std::vector searchResAddrs; + + if (!PSP_IsInited()) + return searchResAddrs; + + std::vector searchData; + if (!ParseSearchString(searchQuery, false, searchData)) + return searchResAddrs; + + if (searchData.empty()) + return searchResAddrs; + + std::vector> memoryAreas; + memoryAreas.emplace_back(PSP_GetScratchpadMemoryBase(), PSP_GetScratchpadMemoryEnd()); + // Ignore the video memory mirrors. + memoryAreas.emplace_back(PSP_GetVidMemBase(), 0x04200000); + memoryAreas.emplace_back(PSP_GetKernelMemoryBase(), PSP_GetUserMemoryEnd()); + + for (const auto &area : memoryAreas) { + const u32 segmentStart = area.first; + const u32 segmentEnd = area.second - (u32)searchData.size(); + + for (u32 pos = segmentStart; pos < segmentEnd; pos++) { + if ((pos % 256) == 0 && ImGui::IsKeyDown(ImGuiKey_Escape)) { + return searchResAddrs; + } + + const u8 *ptr = Memory::GetPointerUnchecked(pos); + if (memcmp(ptr, searchData.data(), searchData.size()) == 0) { + searchResAddrs.push_back(pos); + } + } + } + + return searchResAddrs; +}; + +void ImMemView::search(bool continueSearch) { + /* + if (!PSP_IsInited()) + return; + + u32 searchAddress = 0; + u32 segmentStart = 0; + u32 segmentEnd = 0; + if (continueSearch == false || searchQuery_.empty()) { + if (InputBox_GetString(GetModuleHandle(NULL), wnd, L"Search for", searchQuery_, searchQuery_) == false) { + SetFocus(wnd); + return; + } + SetFocus(wnd); + searchAddress = curAddress_ + 1; + } else { + searchAddress = matchAddress_ + 1; + } + + std::vector searchData; + if (!ParseSearchString(searchQuery_, !asciiSelected_, searchData)) { + statusMessage_ = "Invalid search text."; + return; + } + + std::vector> memoryAreas; + // Ignore the video memory mirrors. + memoryAreas.emplace_back(PSP_GetVidMemBase(), 0x04200000); + memoryAreas.emplace_back(PSP_GetKernelMemoryBase(), PSP_GetUserMemoryEnd()); + memoryAreas.emplace_back(PSP_GetScratchpadMemoryBase(), PSP_GetScratchpadMemoryEnd()); + + searching_ = true; + redraw(); // so the cursor is disabled + + for (size_t i = 0; i < memoryAreas.size(); i++) { + segmentStart = memoryAreas[i].first; + segmentEnd = memoryAreas[i].second; + + // better safe than sorry, I guess + if (!Memory::IsValidAddress(segmentStart)) + continue; + const u8 *dataPointer = Memory::GetPointerUnchecked(segmentStart); + + if (searchAddress < segmentStart) + searchAddress = segmentStart; + if (searchAddress >= segmentEnd) + continue; + + int index = searchAddress - segmentStart; + int endIndex = segmentEnd - segmentStart - (int)searchData.size(); + + while (index < endIndex) { + // cancel search + if ((index % 256) == 0 && ImGui::IsKeyDown(ImGuiKey_Escape)) { + searching_ = false; + return; + } + if (memcmp(&dataPointer[index], searchData.data(), searchData.size()) == 0) { + matchAddress_ = index + segmentStart; + searching_ = false; + gotoAddr(matchAddress_); + return; + } + index++; + } + } + + statusMessage_ = "Not found"; + searching_ = false; + redraw(); + */ +} + +void ImMemView::drawOffsetScale(ImDrawList *drawList) { + int currentX = addressStartX_; + + drawList->AddText(ImVec2(currentX, offsetPositionY_), 0xFF600000, "Offset"); + // SetTextColor(hdc, 0x600000); + // TextOutA(hdc, currentX, offsetPositionY_, "Offset", 6); + + // the start offset, the size of the hex addresses and one space + currentX = addressStartX_ + ((8 + 1) * charWidth_); + + char temp[64]; + for (int i = 0; i < 16; i++) { + snprintf(temp, sizeof(temp), "%02X", i); + drawList->AddText(ImVec2(currentX, offsetPositionY_), 0xFF600000, temp); + currentX += 3 * charWidth_; // hex and space + } +} + +void ImMemView::toggleOffsetScale(CommonToggles toggle) { + if (toggle == On) + displayOffsetScale_ = true; + else if (toggle == Off) + displayOffsetScale_ = false; + + updateStatusBarText(); +} + +void ImMemView::setHighlightType(MemBlockFlags flags) { + if (highlightFlags_ != flags) { + highlightFlags_ = flags; + updateStatusBarText(); + } +} diff --git a/UI/ImDebugger/ImMemView.h b/UI/ImDebugger/ImMemView.h new file mode 100644 index 0000000000..22a0420d66 --- /dev/null +++ b/UI/ImDebugger/ImMemView.h @@ -0,0 +1,120 @@ +#pragma once + + +#include +#include +#include "ext/imgui/imgui.h" + +#include "Core/Debugger/DebugInterface.h" +#include "Core/Debugger/MemBlockInfo.h" + +enum OffsetSpacing { + offsetSpace = 3, // the number of blank lines that should be left to make space for the offsets + offsetLine = 1, // the line on which the offsets should be written +}; + +enum CommonToggles { + On, + Off, +}; + +class ImMemView { +public: + ImMemView(); + ~ImMemView(); + + void setDebugger(DebugInterface *deb) { + debugger_ = deb; + } + DebugInterface *getDebugger() { + return debugger_; + } + std::vector searchString(const std::string &searchQuery); + void Draw(ImDrawList *drawList); + // void onVScroll(WPARAM wParam, LPARAM lParam); + // void onKeyDown(WPARAM wParam, LPARAM lParam); + void onChar(int c); + void onMouseDown(float x, float y, int button); + void onMouseUp(float x, float y, int button); + void onMouseMove(float x, float y, int button); + void gotoAddr(unsigned int addr); + + void drawOffsetScale(ImDrawList *drawList); + void toggleOffsetScale(CommonToggles toggle); + void setHighlightType(MemBlockFlags flags); + + const std::string &StatusMessage() const { + return statusMessage_; + } + +private: + void ProcessKeyboardShortcuts(bool focused); + + bool ParseSearchString(const std::string &query, bool asHex, std::vector &data); + void updateStatusBarText(); + void search(bool continueSearch); + + enum class GotoMode { + RESET, + RESET_IF_OUTSIDE, + FROM_CUR, + EXTEND, + }; + + static GotoMode GotoModeFromModifiers(bool isRightClick); + void UpdateSelectRange(uint32_t target, GotoMode mode); + void GotoPoint(int x, int y, GotoMode mode); + void ScrollWindow(int lines, GotoMode mdoe); + void ScrollCursor(int bytes, GotoMode mdoe); + void PopupMenu(); + + static wchar_t szClassName[]; + DebugInterface *debugger_ = nullptr; + + MemBlockFlags highlightFlags_ = MemBlockFlags::ALLOC; + + // Current cursor position. + uint32_t curAddress_ = 0x08800000; + // Selected range, which should always be around the cursor. + uint32_t selectRangeStart_ = 0; + uint32_t selectRangeEnd_ = 0; + // Last select reset position, for selecting ranges. + uint32_t lastSelectReset_ = 0; + // Address of the first displayed byte. + uint32_t windowStart_ = 0x08800000; + // Number of bytes displayed per row. + int rowSize_ = 16; + + // Width of one monospace character (to maintain grid.) + int charWidth_ = 0; + // Height of one monospace character (to maintain grid.) + int charHeight_ = 0; + // Height of one row of bytes. + int rowHeight_ = 0; + // Y position of offset header (at top.) + int offsetPositionY_ = 0; + // X position of addresses (at left.) + int addressStartX_ = 0; + // X position of hex display. + int hexStartX_ = 0; + // X position of text display. + int asciiStartX_ = 0; + // Whether cursor is within text display or hex display. + bool asciiSelected_ = false; + // Which nibble is selected, if in hex display. 0 means leftmost, i.e. most significant. + int selectedNibble_ = 0; + + bool displayOffsetScale_ = false; + + // Number of rows visible as of last redraw. + int visibleRows_ = 0; + + // Last used search query, used when continuing a search. + std::string searchQuery_; + // Address of last match when continuing search. + uint32_t matchAddress_ = 0xFFFFFFFF; + // Whether a search is in progress. + bool searching_ = false; + + std::string statusMessage_; +}; diff --git a/UI/UI.vcxproj b/UI/UI.vcxproj index 48f672fd7f..741a3406f6 100644 --- a/UI/UI.vcxproj +++ b/UI/UI.vcxproj @@ -54,6 +54,7 @@ + @@ -98,6 +99,7 @@ + diff --git a/UI/UI.vcxproj.filters b/UI/UI.vcxproj.filters index 9d9583c5f6..45b9a0ff00 100644 --- a/UI/UI.vcxproj.filters +++ b/UI/UI.vcxproj.filters @@ -110,6 +110,9 @@ ImDebugger + + ImDebugger + @@ -220,6 +223,9 @@ ImDebugger + + ImDebugger + diff --git a/UWP/UI_UWP/UI_UWP.vcxproj b/UWP/UI_UWP/UI_UWP.vcxproj index b905828f70..7d73b44439 100644 --- a/UWP/UI_UWP/UI_UWP.vcxproj +++ b/UWP/UI_UWP/UI_UWP.vcxproj @@ -129,6 +129,7 @@ + @@ -173,6 +174,7 @@ + diff --git a/UWP/UI_UWP/UI_UWP.vcxproj.filters b/UWP/UI_UWP/UI_UWP.vcxproj.filters index a731a9744b..9103eefc70 100644 --- a/UWP/UI_UWP/UI_UWP.vcxproj.filters +++ b/UWP/UI_UWP/UI_UWP.vcxproj.filters @@ -51,6 +51,9 @@ ImDebugger + + ImDebugger + @@ -103,6 +106,9 @@ ImDebugger + + ImDebugger + diff --git a/Windows/Debugger/CtrlMemView.h b/Windows/Debugger/CtrlMemView.h index 3c57ecb2aa..e006e531e9 100644 --- a/Windows/Debugger/CtrlMemView.h +++ b/Windows/Debugger/CtrlMemView.h @@ -1,16 +1,10 @@ #pragma once ////////////////////////////////////////////////////////////////////////// -//CtrlDisAsmView -// CtrlDisAsmView.cpp +// CtrlMemView ////////////////////////////////////////////////////////////////////////// -//This Win32 control is made to be flexible and usable with -//every kind of CPU architecture that has fixed width instruction words. -//Just supply it an instance of a class derived from Debugger, with all methods -//overridden for full functionality. -// //To add to a dialog box, just draw a User Control in the dialog editor, -//and set classname to "CtrlDisAsmView". you also need to call CtrlDisAsmView::init() +//and set classname to "CtrlMemView". you also need to call CtrlMemView::init() //before opening this dialog, to register the window class. // //To get a class instance to be able to access it, just use getFrom(HWND wnd). diff --git a/Windows/Debugger/Debugger_Disasm.cpp b/Windows/Debugger/Debugger_Disasm.cpp index 2711c990b1..dc03d9ab35 100644 --- a/Windows/Debugger/Debugger_Disasm.cpp +++ b/Windows/Debugger/Debugger_Disasm.cpp @@ -444,9 +444,9 @@ BOOL CDisasm::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) { UpdateDialog(); } break; - case IDC_GOTOLR: + case IDC_GOTORA: { - ptr->gotoAddr(cpu->GetLR()); + ptr->gotoAddr(cpu->GetRA()); SetFocus(GetDlgItem(m_hDlg, IDC_DISASMVIEW)); } break; @@ -696,7 +696,7 @@ void CDisasm::SetDebugMode(bool _bDebug, bool switchPC) EnableWindow(GetDlgItem(hDlg, IDC_STEPHLE), TRUE); EnableWindow(GetDlgItem(hDlg, IDC_STEPOUT), TRUE); EnableWindow(GetDlgItem(hDlg, IDC_GOTOPC), TRUE); - EnableWindow(GetDlgItem(hDlg, IDC_GOTOLR), TRUE); + EnableWindow(GetDlgItem(hDlg, IDC_GOTORA), TRUE); CtrlDisAsmView *ptr = DisAsmView(); ptr->setDontRedraw(false); if (switchPC) @@ -715,7 +715,7 @@ void CDisasm::SetDebugMode(bool _bDebug, bool switchPC) EnableWindow(GetDlgItem(hDlg, IDC_STEPHLE), FALSE); EnableWindow(GetDlgItem(hDlg, IDC_STEPOUT), FALSE); EnableWindow(GetDlgItem(hDlg, IDC_GOTOPC), FALSE); - EnableWindow(GetDlgItem(hDlg, IDC_GOTOLR), FALSE); + EnableWindow(GetDlgItem(hDlg, IDC_GOTORA), FALSE); CtrlRegisterList *reglist = CtrlRegisterList::getFrom(GetDlgItem(m_hDlg,IDC_REGLIST)); reglist->redraw(); } diff --git a/Windows/ppsspp.rc b/Windows/ppsspp.rc index c146f12261..4f48db5f46 100644 --- a/Windows/ppsspp.rc +++ b/Windows/ppsspp.rc @@ -180,7 +180,7 @@ BEGIN EDITTEXT IDC_THREADNAME,370,3,150,10,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER EDITTEXT IDC_ADDRESS,11,24,91,13,ES_AUTOHSCROLL | ES_WANTRETURN PUSHBUTTON "PC",IDC_GOTOPC,11,40,15,13 - PUSHBUTTON "RA",IDC_GOTOLR,28,40,14,13 + PUSHBUTTON "RA",IDC_GOTORA,28,40,14,13 COMBOBOX IDC_GOTOINT,43,40,60,76,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP CONTROL "Custom1",IDC_DISASMVIEW,"CtrlDisAsmView",WS_BORDER | WS_TABSTOP,111,16,397,304 GROUPBOX "Go to",IDC_STATIC,5,12,102,47 diff --git a/Windows/resource.h b/Windows/resource.h index d7fce4a416..4eb9bd1419 100644 --- a/Windows/resource.h +++ b/Windows/resource.h @@ -70,7 +70,7 @@ #define IDC_STEP 1009 #define IDC_VERSION 1010 #define IDC_MEMVIEW 1069 -#define IDC_GOTOLR 1070 +#define IDC_GOTORA 1070 #define IDC_GOTOINT 1071 #define IDC_MEMSORT 1073 #define IDC_SYMBOLS 1097 diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 786b267169..10ab9a5f77 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -879,6 +879,7 @@ LOCAL_SRC_FILES := \ $(SRC)/UI/ImDebugger/ImDebugger.cpp \ $(SRC)/UI/ImDebugger/ImGe.cpp \ $(SRC)/UI/ImDebugger/ImDisasmView.cpp \ + $(SRC)/UI/ImDebugger/ImMemView.cpp \ $(SRC)/UI/ImDebugger/ImStructViewer.cpp \ $(SRC)/UI/AudioCommon.cpp \ $(SRC)/UI/BackgroundAudio.cpp \ diff --git a/ext/imgui/imgui_impl_thin3d.cpp b/ext/imgui/imgui_impl_thin3d.cpp index 430c59fd6d..ab7b561546 100644 --- a/ext/imgui/imgui_impl_thin3d.cpp +++ b/ext/imgui/imgui_impl_thin3d.cpp @@ -313,6 +313,10 @@ void ImGui_PopFont() { ImGui::PopFont(); } +ImFont *ImGui_GetFixedFont() { + return g_fixedFont; +} + void ImGui_ImplThin3d_Shutdown() { BackendData* bd = ImGui_ImplThin3d_GetBackendData(); IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?"); diff --git a/ext/imgui/imgui_impl_thin3d.h b/ext/imgui/imgui_impl_thin3d.h index 39d40b8f4f..04edae30ca 100644 --- a/ext/imgui/imgui_impl_thin3d.h +++ b/ext/imgui/imgui_impl_thin3d.h @@ -55,6 +55,10 @@ IMGUI_IMPL_API ImTextureID ImGui_ImplThin3d_AddFBAsTextureTemp(Draw::Framebuffer void ImGui_PushFixedFont(); void ImGui_PopFont(); +// To get metrics etc. +ImFont *ImGui_GetFixedFont(); + + // Helper structure to hold the data needed by one rendering context into one OS window // (Used by example's main.cpp. Used by multi-viewport features. Probably NOT used by your own engine/app.) struct ImGui_ImplThin3dH_Window {