From cf6d06c56a94a0293183738472aabc08c96d3deb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 4 Nov 2024 01:05:36 +0100 Subject: [PATCH 1/6] Start work on imdbg --- CMakeLists.txt | 6 +- Core/Debugger/DebugInterface.h | 2 +- Core/Debugger/SymbolMap.h | 1 + Core/Debugger/WebSocket/DisasmSubscriber.cpp | 2 +- Core/MIPS/MIPSDebugInterface.cpp | 22 +- Core/MIPS/MIPSDebugInterface.h | 2 +- UI/EmuScreen.cpp | 14 +- UI/EmuScreen.h | 4 + UI/ImDebugger/ImDebugger.cpp | 142 +++ UI/ImDebugger/ImDebugger.h | 63 + UI/ImDebugger/ImDisasmView.cpp | 1168 ++++++++++++++++++ UI/ImDebugger/ImDisasmView.h | 178 +++ UI/UI.vcxproj | 4 + UI/UI.vcxproj.filters | 15 + UWP/CoreUWP/CoreUWP.vcxproj | 2 +- UWP/UI_UWP/UI_UWP.vcxproj | 4 + UWP/UI_UWP/UI_UWP.vcxproj.filters | 17 + Windows/Debugger/CtrlDisAsmView.cpp | 14 +- Windows/Debugger/CtrlDisAsmView.h | 6 +- Windows/Debugger/CtrlRegisterList.cpp | 4 +- Windows/Debugger/Debugger_Disasm.cpp | 2 +- android/jni/Android.mk | 2 + ext/imgui/imgui_impl_platform.cpp | 2 +- 23 files changed, 1645 insertions(+), 31 deletions(-) create mode 100644 UI/ImDebugger/ImDebugger.cpp create mode 100644 UI/ImDebugger/ImDebugger.h create mode 100644 UI/ImDebugger/ImDisasmView.cpp create mode 100644 UI/ImDebugger/ImDisasmView.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 0428b487dd..c8a8b914f3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -175,7 +175,7 @@ option(USE_LIBNX "Set to ON to build for Switch(libnx)" OFF) option(USE_FFMPEG "Build with FFMPEG support" ON) option(USE_DISCORD "Build with Discord support" ON) option(USE_MINIUPNPC "Build with miniUPnPc support" ON) -option(USE_ARMIPS "Build with armips support in API/debugger" ON) +option(USE_ARMIPS "Build with armips support in API/debuggerdebugger" ON) option(USE_SYSTEM_SNAPPY "Dynamically link against system snappy" ${USE_SYSTEM_SNAPPY}) option(USE_SYSTEM_FFMPEG "Dynamically link against system FFMPEG" ${USE_SYSTEM_FFMPEG}) option(USE_SYSTEM_LIBZIP "Dynamically link against system libzip" ${USE_SYSTEM_LIBZIP}) @@ -1519,6 +1519,10 @@ endif() list(APPEND NativeAppSource android/jni/TestRunner.cpp + UI/ImDebugger/ImDebugger.cpp + UI/ImDebugger/ImDebugger.h + UI/ImDebugger/ImDisasmView.cpp + UI/ImDebugger/ImDisasmView.h UI/DiscordIntegration.cpp UI/NativeApp.cpp UI/BackgroundAudio.h diff --git a/Core/Debugger/DebugInterface.h b/Core/Debugger/DebugInterface.h index 7a77b6ac0a..34e354b9c0 100644 --- a/Core/Debugger/DebugInterface.h +++ b/Core/Debugger/DebugInterface.h @@ -39,7 +39,7 @@ public: virtual void setPC(unsigned int address) {} virtual void step() {} virtual void runToBreakpoint() {} - virtual int getColor(unsigned int address){return 0xFFFFFFFF;} + virtual int getColor(unsigned int address, bool darkMode) const {return darkMode ? 0xFF101010 : 0xFFFFFFFF;} virtual std::string getDescription(unsigned int address) {return "";} virtual bool initExpression(const char* exp, PostfixExpression& dest) { return false; }; virtual bool parseExpression(PostfixExpression& exp, u32& dest) { return false; }; diff --git a/Core/Debugger/SymbolMap.h b/Core/Debugger/SymbolMap.h index defe069838..b3e1529779 100644 --- a/Core/Debugger/SymbolMap.h +++ b/Core/Debugger/SymbolMap.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include "Common/CommonTypes.h" diff --git a/Core/Debugger/WebSocket/DisasmSubscriber.cpp b/Core/Debugger/WebSocket/DisasmSubscriber.cpp index eea7921ba4..499952930d 100644 --- a/Core/Debugger/WebSocket/DisasmSubscriber.cpp +++ b/Core/Debugger/WebSocket/DisasmSubscriber.cpp @@ -100,7 +100,7 @@ void WebSocketDisasmState::WriteDisasmLine(JsonWriter &json, const DisassemblyLi } else { json.writeNull("macroEncoding"); } - int c = currentDebugMIPS->getColor(addr) & 0x00FFFFFF; + int c = currentDebugMIPS->getColor(addr, false) & 0x00FFFFFF; json.writeString("backgroundColor", StringFromFormat("#%02x%02x%02x", c & 0xFF, (c >> 8) & 0xFF, c >> 16)); json.writeString("name", l.name); json.writeString("params", l.params); diff --git a/Core/MIPS/MIPSDebugInterface.cpp b/Core/MIPS/MIPSDebugInterface.cpp index f3b18ebbc6..d31983741a 100644 --- a/Core/MIPS/MIPSDebugInterface.cpp +++ b/Core/MIPS/MIPSDebugInterface.cpp @@ -243,15 +243,21 @@ void MIPSDebugInterface::toggleBreakpoint(unsigned int address) } -int MIPSDebugInterface::getColor(unsigned int address) -{ - int colors[6] = {0xe0FFFF,0xFFe0e0,0xe8e8FF,0xFFe0FF,0xe0FFe0,0xFFFFe0}; - int n=g_symbolMap->GetFunctionNum(address); - if (n==-1) return 0xFFFFFF; - return colors[n%6]; +int MIPSDebugInterface::getColor(unsigned int address, bool darkMode) const { + int colors[6] = { 0xe0FFFF, 0xFFe0e0, 0xe8e8FF, 0xFFe0FF, 0xe0FFe0, 0xFFFFe0 }; + int colorsDark[6] = { ~0xe0FFFF, ~0xFFe0e0, ~0xe8e8FF, ~0xFFe0FF, ~0xe0FFe0, ~0xFFFFe0 }; + + int n = g_symbolMap->GetFunctionNum(address); + if (n == -1) { + return DebugInterface::getColor(address, darkMode); + } else if (darkMode) { + return colorsDark[n % ARRAY_SIZE(colorsDark)]; + } else { + return colors[n % ARRAY_SIZE(colors)]; + } } -std::string MIPSDebugInterface::getDescription(unsigned int address) -{ + +std::string MIPSDebugInterface::getDescription(unsigned int address) { return g_symbolMap->GetDescription(address); } diff --git a/Core/MIPS/MIPSDebugInterface.h b/Core/MIPS/MIPSDebugInterface.h index 6d5cec88a2..2a6e88f66f 100644 --- a/Core/MIPS/MIPSDebugInterface.h +++ b/Core/MIPS/MIPSDebugInterface.h @@ -40,7 +40,7 @@ public: void setPC(unsigned int address) override { cpu->pc = address; } void step() override {} void runToBreakpoint() override; - int getColor(unsigned int address) override; + int getColor(unsigned int address, bool darkMode) const override; std::string getDescription(unsigned int address) override; bool initExpression(const char* exp, PostfixExpression& dest) override; bool parseExpression(PostfixExpression& exp, u32& dest) override; diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index ad8ced0bf0..da44e949c0 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -73,8 +73,9 @@ using namespace std::placeholders; #include "Core/Debugger/SymbolMap.h" #include "Core/RetroAchievements.h" #include "Core/SaveState.h" +#include "UI/ImDebugger/ImDebugger.h" #include "Core/HLE/__sceAudio.h" -#include "Core/HLE/proAdhoc.h" +// #include "Core/HLE/proAdhoc.h" #include "Core/HW/Display.h" #include "UI/BackgroundAudio.h" @@ -1240,6 +1241,9 @@ UI::EventReturn EmuScreen::OnResume(UI::EventParams ¶ms) { return UI::EVENT_DONE; } +// To avoid including proAdhoc.h, which includes a lot of stuff. +int GetChatMessageCount(); + void EmuScreen::update() { using namespace UI; @@ -1635,18 +1639,20 @@ ScreenRenderFlags EmuScreen::render(ScreenRenderMode mode) { if (imguiVisible_ && !imguiInited_) { imguiInited_ = true; + imDebugger_ = std::make_unique(); ImGui_ImplThin3d_Init(draw); } if (imguiVisible_ && imguiInited_) { + _dbg_assert_(imDebugger_); + ImGui_ImplPlatform_NewFrame(); ImGui_ImplThin3d_NewFrame(draw, ui_draw2d.GetDrawMatrix()); - // Draw imgui on top. For now, all we have is the demo window. ImGui::NewFrame(); - ImGui::ShowDemoWindow(nullptr); - ImGui::Render(); + imDebugger_->Frame(currentDebugMIPS); + ImGui::Render(); ImGui_ImplThin3d_RenderDrawData(ImGui::GetDrawData(), draw); } return flags; diff --git a/UI/EmuScreen.h b/UI/EmuScreen.h index 2abca66c16..3200c633ff 100644 --- a/UI/EmuScreen.h +++ b/UI/EmuScreen.h @@ -29,6 +29,8 @@ #include "Core/KeyMap.h" #include "Core/ControlMapper.h" +#include "UI/ImDebugger/ImDebugger.h" + struct AxisInput; class AsyncImageFileView; @@ -129,6 +131,8 @@ private: ControlMapper controlMapper_; + std::unique_ptr imDebugger_ = nullptr; + bool imguiInited_ = false; bool imguiVisible_ = false; }; diff --git a/UI/ImDebugger/ImDebugger.cpp b/UI/ImDebugger/ImDebugger.cpp new file mode 100644 index 0000000000..ca941e8499 --- /dev/null +++ b/UI/ImDebugger/ImDebugger.cpp @@ -0,0 +1,142 @@ + +#include "ext/imgui/imgui_internal.h" + +#include "Common/StringUtils.h" +#include "Core/Core.h" +#include "Core/Debugger/DebugInterface.h" +#include "Core/Debugger/DisassemblyManager.h" +#include "Core/Debugger/Breakpoints.h" +#include "Core/MIPS/MIPSDebugInterface.h" +#include "Core/MIPS/MIPSTables.h" +#include "Core/Debugger/SymbolMap.h" +#include "Core/MemMap.h" +#include "Common/System/Request.h" + +#include "UI/ImDebugger/ImDebugger.h" + +void ImDebugger::Frame(MIPSDebugInterface *mipsDebug) { + // Snapshot the coreState to avoid inconsistency. + const CoreState coreState = ::coreState; + + if (ImGui::BeginMainMenuBar()) { + if (ImGui::BeginMenu("Debug")) { + if (coreState == CoreState::CORE_STEPPING) { + if (ImGui::MenuItem("Run")) { + Core_Resume(); + } + if (ImGui::MenuItem("Step Into", "F11")) { + Core_RequestSingleStep(CPUStepType::Into, 1); + } + if (ImGui::MenuItem("Step Over", "F10")) { + Core_RequestSingleStep(CPUStepType::Over, 1); + } + if (ImGui::MenuItem("Step Out", "Shift+F11")) { + Core_RequestSingleStep(CPUStepType::Out, 1); + } + } else { + if (ImGui::MenuItem("Break")) { + Core_Break("Menu:Break"); + } + } + ImGui::Separator(); + if (ImGui::MenuItem("Toggle Breakpoint")) { + // TODO + } + ImGui::EndMenu(); + } + if (ImGui::BeginMenu("Window")) { + ImGui::Checkbox("Dear ImGUI Demo", &demoOpen_); + ImGui::Checkbox("CPU debugger", &disasmOpen_); + ImGui::EndMenu(); + } + ImGui::EndMainMenuBar(); + } + + if (demoOpen_) { + ImGui::ShowDemoWindow(&demoOpen_); + } + + if (disasmOpen_) { + disasm_.Draw(mipsDebug, &disasmOpen_); + } + + ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it. + ImGui::Text("This is some useful text."); // Display some text (you can use a format strings too) + ImGui::End(); +} + +void ImDisasmWindow::Draw(MIPSDebugInterface *mipsDebug, bool *open) { + char title[256]; + snprintf(title, sizeof(title), "%s - Disassembly", "Allegrex MIPS"); + + disasmView_.setDebugger(mipsDebug); + + ImGui::SetNextWindowSize(ImVec2(520, 600), ImGuiCond_FirstUseEver); + if (!ImGui::Begin(title, open)) { + ImGui::End(); + return; + } + + if (ImGui::SmallButton("Run")) { + Core_Resume(); + } + + ImGui::SameLine(); + if (ImGui::SmallButton("Pause")) { + Core_Break("Pause"); + } + + ImGui::SameLine(); + if (ImGui::SmallButton("Step Into")) { + u32 stepSize = disasmView_.getInstructionSizeAt(mipsDebug->GetPC()); + Core_RequestSingleStep(CPUStepType::Into, stepSize); + } + + ImGui::SameLine(); + if (ImGui::SmallButton("Step Over")) { + Core_RequestSingleStep(CPUStepType::Over, 0); + } + + ImGui::SameLine(); + if (ImGui::SmallButton("Step Out")) { + Core_RequestSingleStep(CPUStepType::Out, 0); + } + + ImGui::SameLine(); + if (ImGui::SmallButton("Goto PC")) { + disasmView_.gotoPC(); + } + + ImGui::SameLine(); + ImGui::Checkbox("Follow PC", &followPC_); + + ImGui::SetNextItemWidth(100); + if (ImGui::InputScalar("Go to addr: ", ImGuiDataType_U32, &gotoAddr_, NULL, NULL, "%08X", ImGuiInputTextFlags_EnterReturnsTrue)) { + disasmView_.setCurAddress(gotoAddr_); + disasmView_.scrollAddressIntoView(); + } + + if (ImGui::BeginTable("main", 2)) { + ImGui::TableSetupColumn("left", ImGuiTableColumnFlags_WidthFixed); + ImGui::TableSetupColumn("right", ImGuiTableColumnFlags_WidthStretch); + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImVec2 sz = ImGui::GetContentRegionAvail(); + if (ImGui::BeginListBox("##symbols", ImVec2(150.0, sz.y - ImGui::GetTextLineHeightWithSpacing() * 2))) { + std::vector syms = g_symbolMap->GetAllSymbols(SymbolType::ST_FUNCTION); + for (auto &sym : syms) { + if (ImGui::Selectable(sym.name.c_str(), false)) { + disasmView_.setCurAddress(sym.address); + disasmView_.scrollAddressIntoView(); + } + } + ImGui::EndListBox(); + } + + ImGui::TableSetColumnIndex(1); + // Draw border and background color + disasmView_.Draw(ImGui::GetWindowDrawList()); + ImGui::EndTable(); + } + ImGui::End(); +} diff --git a/UI/ImDebugger/ImDebugger.h b/UI/ImDebugger/ImDebugger.h new file mode 100644 index 0000000000..91b1cc8d05 --- /dev/null +++ b/UI/ImDebugger/ImDebugger.h @@ -0,0 +1,63 @@ +#pragma once + +#include +#include +#include + +#include "ext/imgui/imgui.h" + +#include "Common/CommonTypes.h" +#include "Common/Log.h" + +#include "Core/Debugger/DisassemblyManager.h" +#include "Core/Debugger/DebugInterface.h" + +#include "UI/ImDebugger/ImDisasmView.h" + +// This is the main state container of the whole Dear ImGUI-based in-game cross-platform debugger. +// +// Code conventions for ImGUI in PPSSPP +// * If windows/objects need state, prefix the class name with Im and just store straight in parent struct + +class MIPSDebugInterface; + + +// Corresponds to the CDisasm dialog +class ImDisasmWindow { +public: + void Draw(MIPSDebugInterface *mipsDebug, bool *open); + + void PCChanged() { + pcChanged_ = true; + } + +private: + // We just keep the state directly in the window. Can refactor later. + + enum { + INVALID_ADDR = 0xFFFFFFFF, + }; + + u32 gotoAddr_ = 0x1000; + + bool followPC_ = true; + bool pcChanged_ = false; + + ImDisasmView disasmView_; +}; + +class ImLuaConsole { +public: + // Stub +}; + +struct ImDebugger { + void Frame(MIPSDebugInterface *mipsDebug); + + ImDisasmWindow disasm_; + ImLuaConsole luaConsole_; + + // Open variables. + bool disasmOpen_ = true; + bool demoOpen_ = false; +}; diff --git a/UI/ImDebugger/ImDisasmView.cpp b/UI/ImDebugger/ImDisasmView.cpp new file mode 100644 index 0000000000..d68d131ec0 --- /dev/null +++ b/UI/ImDebugger/ImDisasmView.cpp @@ -0,0 +1,1168 @@ + +#include "ext/imgui/imgui_internal.h" + +#include "Common/StringUtils.h" +#include "Core/Core.h" +#include "Core/Debugger/DebugInterface.h" +#include "Core/Debugger/DisassemblyManager.h" +#include "Core/Debugger/Breakpoints.h" +#include "Core/MIPS/MIPSDebugInterface.h" +#include "Core/MIPS/MIPSTables.h" +#include "Core/Debugger/SymbolMap.h" +#include "Core/MemMap.h" +#include "Common/System/Request.h" + +#include "Core/Core.h" +#include "Core/CoreParameter.h" +#include "UI/ImDebugger/ImDisasmView.h" + +ImDisasmView::ImDisasmView() { + curAddress_ = 0; + showHex_ = false; + hasFocus_ = false; + keyTaken = false; + + matchAddress_ = -1; + searching_ = false; + searchQuery_.clear(); + windowStart_ = curAddress_; + displaySymbols_ = true; +} + +ImDisasmView::~ImDisasmView() { + manager.clear(); +} + +void ImDisasmView::scanVisibleFunctions() { + manager.analyze(windowStart_, manager.getNthNextAddress(windowStart_, visibleRows_) - windowStart_); +} + +static ImColor scaleColor(ImColor color, float factor) { + if (factor <= 0.0f) { + return color; + } + color.Value.x = std::min(color.Value.x * factor, 1.0f); + color.Value.y = std::min(color.Value.y * factor, 1.0f); + color.Value.z = std::min(color.Value.z * factor, 1.0f); + return color; +} + +bool ImDisasmView::getDisasmAddressText(u32 address, char* dest, bool abbreviateLabels, bool showData) { + if (!PSP_IsInited()) + return false; + + if (displaySymbols_) { + const std::string addressSymbol = g_symbolMap->GetLabelString(address); + if (!addressSymbol.empty()) { + for (int k = 0; addressSymbol[k] != 0; k++) { + // abbreviate long names + if (abbreviateLabels && k == 16 && addressSymbol[k + 1] != 0) { + *dest++ = '+'; + break; + } + *dest++ = addressSymbol[k]; + } + *dest++ = ':'; + *dest = 0; + return true; + } else { + sprintf(dest, " %08X", address); + return false; + } + } else { + if (showData) { + u32 encoding = Memory::IsValidAddress(address) ? Memory::Read_Instruction(address, true).encoding : 0; + sprintf(dest, "%08X %08X", address, encoding); + } else { + sprintf(dest, "%08X", address); + } + return false; + } +} + +// 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) { + /* + auto memLock = Memory::Lock(); + if (!Core_IsStepping()) { + MessageBox(wnd, L"Cannot change code while the core is running!", L"Error", MB_OK); + return; + } + std::string op; + bool result = InputBox_GetString(MainWindow::GetHInstance(), wnd, L"Assemble opcode", defaultText, op, InputBoxFlags::Default); + if (!result) { + return; + } + + // check if it changes registers first + auto separator = op.find('='); + if (separator != std::string::npos) + { + std::string registerName = trimString(op.substr(0, separator)); + std::string expression = trimString(op.substr(separator + 1)); + + u32 value; + if (parseExpression(expression.c_str(), debugger, value) == true) + { + for (int cat = 0; cat < debugger->GetNumCategories(); cat++) + { + for (int reg = 0; reg < debugger->GetNumRegsInCategory(cat); reg++) + { + if (strcasecmp(debugger->GetRegName(cat, reg).c_str(), registerName.c_str()) == 0) + { + debugger->SetRegValue(cat, reg, value); + Reporting::NotifyDebugger(); + SendMessage(GetParent(wnd), WM_DEB_UPDATE, 0, 0); + return; + } + } + } + } + + // try to assemble the input if it failed + } + + result = MIPSAsm::MipsAssembleOpcode(op.c_str(), debugger, address); + Reporting::NotifyDebugger(); + if (result == true) + { + scanVisibleFunctions(); + + if (address == curAddress) + gotoAddr(manager.getNthNextAddress(curAddress, 1)); + + redraw(); + } else { + std::wstring error = ConvertUTF8ToWString(MIPSAsm::GetAssembleError()); + MessageBox(wnd, error.c_str(), L"Error", MB_OK); + } + */ +} + +void ImDisasmView::drawBranchLine(ImDrawList *drawList, Rect rect, std::map &addressPositions, const BranchLine &line) { + u32 windowEnd = manager.getNthNextAddress(windowStart_, visibleRows_); + + float topY; + float bottomY; + if (line.first < windowStart_) { + topY = -1; + } else if (line.first >= windowEnd) { + topY = rect.bottom + 1.0f; + } else { + topY = (float)addressPositions[line.first] + rowHeight_ / 2; + } + + if (line.second < windowStart_) { + bottomY = -1; + } else if (line.second >= windowEnd) { + bottomY = rect.bottom + 1.0f; + } else { + bottomY = (float)addressPositions[line.second] + rowHeight_ / 2; + } + + if ((topY < 0 && bottomY < 0) || (topY > rect.bottom && bottomY > rect.bottom)) + { + return; + } + + ImColor pen; + + // highlight line in a different color if it affects the currently selected opcode + if (line.first == curAddress_ || line.second == curAddress_) { + pen = ImColor(0xFF257AFA); + } else { + pen = ImColor(0xFFFF3020); + } + + float x = (float)pixelPositions_.arrowsStart + (float)line.laneIndex * 8.0f; + + float curX, curY; + auto moveTo = [&](float x, float y) { + curX = x; + curY = y; + }; + auto lineTo = [&](float x, float y) { + drawList->AddLine(ImVec2(rect.left + curX, rect.top + curY), ImVec2(rect.left + (float)x, rect.top + (float)y), pen, 1.0f); + curX = x; + curY = y; + }; + + if (topY < 0) { // first is not visible, but second is + moveTo(x - 2.f, bottomY); + lineTo(x + 2.f, bottomY); + lineTo(x + 2.f, 0.0f); + + if (line.type == LINE_DOWN) { + moveTo(x, bottomY - 4.f); + lineTo(x - 4.f, bottomY); + lineTo(x + 1.f, bottomY + 5.f); + } + } else if (bottomY > rect.bottom) {// second is not visible, but first is + moveTo(x - 2.f, topY); + lineTo(x + 2.f, topY); + lineTo(x + 2.f, rect.bottom); + + if (line.type == LINE_UP) { + moveTo(x, topY - 4.f); + lineTo(x - 4.f, topY); + lineTo(x + 1.f, topY + 5.f); + } + } else { // both are visible + if (line.type == LINE_UP) { + moveTo(x - 2.f, bottomY); + lineTo(x + 2.f, bottomY); + lineTo(x + 2.f, topY); + lineTo(x - 4.f, topY); + + moveTo(x, topY - 4.f); + lineTo(x - 4.f, topY); + lineTo(x + 1.f, topY + 5.f); + } else { + moveTo(x - 2.f, topY); + lineTo(x + 2.f, topY); + lineTo(x + 2.f, bottomY); + lineTo(x - 4.f, bottomY); + + moveTo(x, bottomY - 4.f); + lineTo(x - 4.f, bottomY); + lineTo(x + 1.f, bottomY + 5.f); + } + } +} + +std::set ImDisasmView::getSelectedLineArguments() { + std::set args; + DisassemblyLineInfo line; + for (u32 addr = selectRangeStart_; addr < selectRangeEnd_; addr += 4) { + manager.getLine(addr, displaySymbols_, line); + size_t p = 0, nextp = line.params.find(','); + while (nextp != line.params.npos) { + args.emplace(line.params.substr(p, nextp - p)); + p = nextp + 1; + nextp = line.params.find(',', p); + } + if (p < line.params.size()) { + args.emplace(line.params.substr(p)); + } + } + return args; +} + +void ImDisasmView::drawArguments(ImDrawList *drawList, Rect rc, const DisassemblyLineInfo &line, int x, int 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()); + return; + } + + ImColor highlightedColor(0xFFaabb00); + if (textColor == 0xFF0000ff) { + highlightedColor = ImColor(0xFFaabb77); + } + + float curX = (float)x, curY = (float)y; + + 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()); + ImVec2 sz = ImGui::CalcTextSize(text.data(), text.data() + text.size(), false, -1.0f); + curX += sz.x; + }; + + size_t p = 0, nextp = line.params.find(','); + while (nextp != line.params.npos) { + const std::string arg = line.params.substr(p, nextp - p); + if (currentArguments.find(arg) != currentArguments.end() && textColor != 0xffffff) { + curColor = highlightedColor; + } + Print(arg); + curColor = textColor; + p = nextp + 1; + nextp = line.params.find(',', p); + Print(","); + } + if (p < line.params.size()) { + const std::string arg = line.params.substr(p); + if (currentArguments.find(arg) != currentArguments.end() && textColor != 0xffffff) { + curColor = highlightedColor; + } + Print(arg); + curColor = textColor; + } +} + +void ImDisasmView::Draw(ImDrawList *drawList) { + ImVec2 canvas_p0 = ImGui::GetCursorScreenPos(); // ImDrawList API uses screen coordinates! + ImVec2 canvas_sz = ImGui::GetContentRegionAvail(); // Resize canvas to what's available + int lineHeight = (int)ImGui::GetTextLineHeightWithSpacing(); + if (canvas_sz.x < 50.0f) canvas_sz.x = 50.0f; + if (canvas_sz.y < 50.0f) canvas_sz.y = 50.0f; + canvas_sz.y -= lineHeight * 2; + + // This will catch our interactions + bool pressed = ImGui::InvisibleButton("canvas", canvas_sz, ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonRight); + + if (pressed) { + INFO_LOG(Log::System, "Pressed"); + } + ImGui::SetItemKeyOwner(ImGuiKey_MouseWheelY); + + 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)); + + Rect rect; + rect.left = canvas_p0.x; + rect.top = canvas_p0.y; + rect.right = canvas_p1.x; + rect.bottom = canvas_p1.y; + + rowHeight_ = ImGui::GetTextLineHeightWithSpacing(); + charWidth_ = ImGui::CalcTextSize("W", nullptr, false, -1.0f).x; + calculatePixelPositions(); + + visibleRows_ = (int)((rect.bottom - rect.top + rowHeight_ - 1.f) / rowHeight_); + + auto memLock = Memory::Lock(); + if (!debugger->isAlive()) { + return; + } + + //HICON breakPoint = (HICON)LoadIcon(GetModuleHandle(0), (LPCWSTR)IDI_STOP); + //HICON breakPointDisable = (HICON)LoadIcon(GetModuleHandle(0), (LPCWSTR)IDI_STOPDISABLE); + + unsigned int address = windowStart_; + std::map addressPositions; + + const std::set currentArguments = getSelectedLineArguments(); + DisassemblyLineInfo line; + for (int i = 0; i < visibleRows_; i++) { + manager.getLine(address, displaySymbols_, line); + + int rowY1 = rowHeight_ * i; + int rowY2 = rowHeight_ * (i + 1); + + addressPositions[address] = rowY1; + + // draw background + ImColor backgroundColor = ImColor(0xFF000000 | debugger->getColor(address, true)); + ImColor textColor = 0xFFFFFFFF; + + if (isInInterval(address, line.totalSize, debugger->getPC())) { + backgroundColor = scaleColor(backgroundColor, 1.05f); + } + + if (address >= selectRangeStart_ && address < selectRangeEnd_ && searching_ == false) { + if (hasFocus_) { + backgroundColor = ImColor(address == curAddress_ ? 0xFFFF8822 : 0xFFFF9933); + textColor = ImColor(0xFF000000); + } else { + backgroundColor = ImColor(0xFFC0C0C0); + } + } + + drawList->AddRectFilled(ImVec2(rect.left, rect.top + rowY1), ImVec2(rect.right, rect.top + rowY1 + rowHeight_), backgroundColor); + + // display breakpoint, if any + bool enabled; + if (CBreakPoints::IsAddressBreakPoint(address, &enabled)) { + if (enabled) + textColor = 0x0000FF; + float yOffset = std::max(-1.0f, (rowHeight_ - 14.f + 1.f) / 2.0f); + // drawList->AddCircleFilled(ImVec2(canvas_p0.x + lineHeight * 0.5f, lineStart.y + lineHeight * 0.5f), lineHeight * 0.45f, 0xFF0000FF, 12); + // DrawIconEx(hdc, 2, rowY1 + 1 + yOffset, enabled ? breakPoint : breakPointDisable, 32, 32, 0, 0, DI_NORMAL); + } + + char addressText[64]; + getDisasmAddressText(address, addressText, true, line.type == DISTYPE_OPCODE); + drawList->AddText(ImVec2(rect.left + pixelPositions_.addressStart, rect.top + rowY1 + 2), textColor, addressText); + + if (isInInterval(address, line.totalSize, debugger->getPC())) { + // emoji??? + // drawList->AddText(ImVec2(pixelPositions.opcodeStart - 8, rowY1), textColor, "\x25A0"); + } + + // display whether the condition of a branch is met + if (line.info.isConditional && address == debugger->getPC()) { + line.params += line.info.conditionMet ? " ; true" : " ; false"; + } + + drawArguments(drawList, rect, line, pixelPositions_.argumentsStart, rowY1 + 2, textColor, currentArguments); + + // The actual opcode. + // Should be bold! + drawList->AddText(ImVec2(rect.left + pixelPositions_.opcodeStart, rect.top + rowY1 + 2), 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]); + } + + ImGuiIO& io = ImGui::GetIO(); + const bool is_hovered = ImGui::IsItemHovered(); // Hovered + const bool is_active = ImGui::IsItemActive(); // Held + ImVec2 mousePos = ImVec2(io.MousePos.x - canvas_p0.x, io.MousePos.y - canvas_p0.y); + if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) { + INFO_LOG(Log::CPU, "Mousedown %f,%f", mousePos.x, mousePos.y); + onMouseDown(mousePos.x, mousePos.y, 1); + } + if (ImGui::IsMouseReleased(ImGuiMouseButton_Left)) { + INFO_LOG(Log::CPU, "Mouseup %f,%f", mousePos.x, mousePos.y); + onMouseUp(mousePos.x, mousePos.y, 1); + } + if (ImGui::IsMouseDragging(ImGuiMouseButton_Left)) { + INFO_LOG(Log::CPU, "Mousedrag %f,%f", mousePos.x, mousePos.y); + onMouseMove(mousePos.x, mousePos.y, 1); + } + if (pressed) { + INFO_LOG(Log::CPU, "Clicked %f,%f", mousePos.x, mousePos.y); + if (mousePos.x < lineHeight) { // Left column + // Toggle breakpoint at dragAddr_. + // debugger->toggleBreakpoint() + + // system->GetBreakpoints(core)->AddExecBreakpoint(dragAddr_); + bpPopup_ = true; + } else { + // disasmView_.selectedAddr_ = dragAddr_; + bpPopup_ = false; + } + } + + ImGui::OpenPopupOnItemClick("context", ImGuiPopupFlags_MouseButtonRight); + PopupMenu(); + + drawList->PopClipRect(); +} + +void ImDisasmView::onVScroll(int amount) { + /* + switch (wParam & 0xFFFF) { + case SB_LINEDOWN: + windowStart_ = manager.getNthNextAddress(windowStart_, 1); + break; + case SB_LINEUP: + windowStart_ = manager.getNthPreviousAddress(windowStart_, 1); + break; + case SB_PAGEDOWN: + windowStart_ = manager.getNthNextAddress(windowStart_, visibleRows_); + break; + case SB_PAGEUP: + windowStart_ = manager.getNthPreviousAddress(windowStart_, visibleRows_); + break; + default: + return; + } + */ + scanVisibleFunctions(); +} + +void ImDisasmView::followBranch() { + DisassemblyLineInfo line; + manager.getLine(curAddress_, true, line); + + if (line.type == DISTYPE_OPCODE || line.type == DISTYPE_MACRO) { + if (line.info.isBranch) { + jumpStack_.push_back(curAddress_); + gotoAddr(line.info.branchTarget); + } else if (line.info.hasRelevantAddress) { + // well, not exactly a branch, but we can do something anyway + // SendMessage(GetParent(wnd), WM_DEB_GOTOHEXEDIT, line.info.relevantAddress, 0); + // SetFocus(wnd); + } + } else if (line.type == DISTYPE_DATA) { + // jump to the start of the current line + // SendMessage(GetParent(wnd), WM_DEB_GOTOHEXEDIT, curAddress, 0); + // SetFocus(wnd); + } +} + +void ImDisasmView::onChar(int c) { + if (keyTaken) return; + + char str[2]; + str[0] = c; + str[1] = 0; + assembleOpcode(curAddress_, str); +} + + +void ImDisasmView::editBreakpoint() { + /* + BreakpointWindow win(wnd, debugger); + + bool exists = false; + if (CBreakPoints::IsAddressBreakPoint(curAddress)) { + auto breakpoints = CBreakPoints::GetBreakpoints(); + for (size_t i = 0; i < breakpoints.size(); i++) { + if (breakpoints[i].addr == curAddress) { + win.loadFromBreakpoint(breakpoints[i]); + exists = true; + break; + } + } + } + + if (!exists) { + win.initBreakpoint(curAddress); + } + + if (win.exec()) { + if (exists) + CBreakPoints::RemoveBreakPoint(curAddress); + win.addBreakpoint(); + } + */ +} + +void ImDisasmView::onKeyDown(ImGuiKey key) { + u32 windowEnd = manager.getNthNextAddress(windowStart_, visibleRows_); + keyTaken = true; + + /* + if (KeyDownAsync(VK_CONTROL)) { + switch (tolower(wParam & 0xFFFF)) { + case 'f': + case 's': + search(false); + break; + case 'c': + case VK_INSERT: + CopyInstructions(selectRangeStart, selectRangeEnd, CopyInstructionsMode::DISASM); + break; + case 'x': + disassembleToFile(); + break; + case 'a': + assembleOpcode(curAddress, ""); + break; + case 'g': + { + u32 addr; + if (executeExpressionWindow(wnd, debugger, addr) == false) return; + gotoAddr(addr); + } + break; + case 'e': // edit breakpoint + editBreakpoint(); + break; + case 'd': // toogle breakpoint enabled + toggleBreakpoint(true); + break; + case VK_UP: + scrollWindow(-1); + scanVisibleFunctions(); + break; + case VK_DOWN: + scrollWindow(1); + scanVisibleFunctions(); + break; + case VK_NEXT: + setCurAddress(manager.getNthPreviousAddress(windowEnd, 1), KeyDownAsync(VK_SHIFT)); + break; + case VK_PRIOR: + setCurAddress(windowStart_, KeyDownAsync(VK_SHIFT)); + break; + } + } else { + switch (wParam & 0xFFFF) { + case VK_DOWN: + setCurAddress(manager.getNthNextAddress(curAddress, 1), KeyDownAsync(VK_SHIFT)); + scrollAddressIntoView(); + break; + case VK_UP: + setCurAddress(manager.getNthPreviousAddress(curAddress, 1), KeyDownAsync(VK_SHIFT)); + scrollAddressIntoView(); + break; + case VK_NEXT: + if (manager.getNthNextAddress(curAddress, 1) != windowEnd && curAddressIsVisible()) { + setCurAddress(manager.getNthPreviousAddress(windowEnd, 1), KeyDownAsync(VK_SHIFT)); + scrollAddressIntoView(); + } else { + setCurAddress(manager.getNthNextAddress(windowEnd, visibleRows_ - 1), KeyDownAsync(VK_SHIFT)); + scrollAddressIntoView(); + } + break; + case VK_PRIOR: + if (curAddress_ != windowStart_ && curAddressIsVisible()) { + setCurAddress(windowStart_, KeyDownAsync(VK_SHIFT)); + scrollAddressIntoView(); + } else { + setCurAddress(manager.getNthPreviousAddress(windowStart_, visibleRows_), KeyDownAsync(VK_SHIFT)); + scrollAddressIntoView(); + } + break; + case VK_LEFT: + if (jumpStack.empty()) + { + gotoPC(); + } else { + u32 addr = jumpStack[jumpStack.size() - 1]; + jumpStack.pop_back(); + gotoAddr(addr); + } + return; + case VK_RIGHT: + followBranch(); + return; + case VK_TAB: + displaySymbols_ = !displaySymbols_; + break; + case VK_SPACE: + debugger->toggleBreakpoint(curAddress); + break; + case VK_F3: + search(true); + break; + default: + keyTaken = false; + return; + } + } + */ +} + +void ImDisasmView::scrollAddressIntoView() { + u32 windowEnd = manager.getNthNextAddress(windowStart_, visibleRows_); + + if (curAddress_ < windowStart_) + windowStart_ = curAddress_; + else if (curAddress_ >= windowEnd) + windowStart_ = manager.getNthPreviousAddress(curAddress_, visibleRows_ - 1); + + scanVisibleFunctions(); +} + +bool ImDisasmView::curAddressIsVisible() { + u32 windowEnd = manager.getNthNextAddress(windowStart_, visibleRows_); + return curAddress_ >= windowStart_ && curAddress_ < windowEnd; +} + +void ImDisasmView::toggleBreakpoint(bool toggleEnabled) { + bool enabled; + if (CBreakPoints::IsAddressBreakPoint(curAddress_, &enabled)) { + if (!enabled) { + // enable disabled breakpoints + CBreakPoints::ChangeBreakPoint(curAddress_, true); + } else if (!toggleEnabled && CBreakPoints::GetBreakPointCondition(curAddress_) != nullptr) { + // don't just delete a breakpoint with a custom condition + /* + int ret = MessageBox(wnd, L"This breakpoint has a custom condition.\nDo you want to remove it?", L"Confirmation", MB_YESNO); + if (ret == IDYES) + CBreakPoints::RemoveBreakPoint(curAddress); + */ + } else if (toggleEnabled) { + // disable breakpoint + CBreakPoints::ChangeBreakPoint(curAddress_, false); + } else { + // otherwise just remove breakpoint + CBreakPoints::RemoveBreakPoint(curAddress_); + } + } else { + CBreakPoints::AddBreakPoint(curAddress_); + } +} + +void ImDisasmView::onMouseDown(int x, int y, int button) { + u32 newAddress = yToAddress(y); + bool extend = ImGui::IsKeyDown(ImGuiKey_LeftShift); + if (button == 1) { + if (newAddress == curAddress_ && hasFocus_) { + toggleBreakpoint(); + } + } else if (button == 2) { + // Maintain the current selection if right clicking into it. + if (newAddress >= selectRangeStart_ && newAddress < selectRangeEnd_) + extend = true; + } + setCurAddress(newAddress, extend); +} + +void ImDisasmView::CopyInstructions(u32 startAddr, u32 endAddr, CopyInstructionsMode mode) { + _assert_msg_((startAddr & 3) == 0, "readMemory() can't handle unaligned reads"); + + if (mode != CopyInstructionsMode::DISASM) { + 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; + p += snprintf(p, end - p, "%08X", data); + + // Don't leave a trailing newline. + if (pos + instructionSize < endAddr && p < end) + p += snprintf(p, end - p, "\r\n"); + } + System_CopyStringToClipboard(temp); + delete[] temp; + } else { + std::string disassembly = disassembleRange(startAddr, endAddr - startAddr); + System_CopyStringToClipboard(disassembly); + } +} + +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::onMouseUp(int x, int y, int button) { + if (button == 1) { + setCurAddress(yToAddress(y), ImGui::IsKeyDown(ImGuiKey_LeftShift)); + } +} + +void ImDisasmView::PopupMenu() { + 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); + } + 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("Edit symbol")) { + EditSymbolsWindow esw(wnd, debugger); + if (esw.exec()) { + esw.eval(); + SendMessage(GetParent(wnd), WM_DEB_MAPLOADED, 0, 0); + redraw(); + } + } + */ + if (ImGui::MenuItem("Set PC to here")) { + debugger->setPC(curAddress_); + } + if (ImGui::MenuItem("Follow branch")) { + followBranch(); + } + if (ImGui::MenuItem("Run to here")) { + // CBreakPoints::AddBreakPoint(pos, true); + // Core_Resume(); + } + ImGui::Separator(); + if (ImGui::MenuItem("Assemble")) { + assembleOpcode(curAddress_, ""); + } + if (ImGui::MenuItem("NOP instructions (destructive)")) { + NopInstructions(selectRangeStart_, selectRangeEnd_); + } + ImGui::Separator(); + if (ImGui::MenuItem("Rename function")) { + u32 funcBegin = g_symbolMap->GetFunctionStart(curAddress_); + /* + if (funcBegin != -1) { + char name[256]; + std::string newname; + truncate_cpy(name, g_symbolMap->GetLabelString(funcBegin).c_str()); + if (InputBox_GetString(MainWindow::GetHInstance(), MainWindow::GetHWND(), L"New function name", name, newname)) { + g_symbolMap->SetLabelName(newname.c_str(), funcBegin); + u32 funcSize = g_symbolMap->GetFunctionSize(funcBegin); + MIPSAnalyst::RegisterFunction(funcBegin, funcSize, newname.c_str()); + MIPSAnalyst::UpdateHashMap(); + MIPSAnalyst::ApplyHashMap(); + mapReloaded_ = true; + } + } else { + MessageBox(MainWindow::GetHWND(), L"No symbol selected", 0, 0); + } + */ + } + if (ImGui::MenuItem("Remove function")) { + u32 funcBegin = g_symbolMap->GetFunctionStart(curAddress_); + if (funcBegin != -1) { + u32 prevBegin = g_symbolMap->GetFunctionStart(funcBegin - 1); + if (prevBegin != -1) + { + u32 expandedSize = g_symbolMap->GetFunctionSize(prevBegin) + g_symbolMap->GetFunctionSize(funcBegin); + g_symbolMap->SetFunctionSize(prevBegin, expandedSize); + } + + g_symbolMap->RemoveFunction(funcBegin, true); + g_symbolMap->SortSymbols(); + manager.clear(); + + mapReloaded_ = true; + } else { + statusBarText_ = "WARNING: unable to find function symbol here"; + } + } + if (ImGui::MenuItem("Add function")) { + char statusBarTextBuff[256]; + u32 prevBegin = g_symbolMap->GetFunctionStart(curAddress_); + if (prevBegin != -1) { + if (prevBegin == curAddress_) { + snprintf(statusBarTextBuff, 256, "WARNING: There's already a function entry point at this adress"); + statusBarText_ = statusBarTextBuff; + } else { + char symname[128]; + u32 prevSize = g_symbolMap->GetFunctionSize(prevBegin); + u32 newSize = curAddress_ - prevBegin; + g_symbolMap->SetFunctionSize(prevBegin, newSize); + + newSize = prevSize - newSize; + snprintf(symname, 128, "u_un_%08X", curAddress_); + g_symbolMap->AddFunction(symname, curAddress_, newSize); + g_symbolMap->SortSymbols(); + manager.clear(); + + mapReloaded_ = true; + } + } else { + char symname[128]; + int newSize = selectRangeEnd_ - selectRangeStart_; + snprintf(symname, 128, "u_un_%08X", selectRangeStart_); + g_symbolMap->AddFunction(symname, selectRangeStart_, newSize); + g_symbolMap->SortSymbols(); + + mapReloaded_ = true; + } + } + if (ImGui::MenuItem("Disassemble to file")) { + disassembleToFile(); + } + ImGui::EndPopup(); + } +} + +void ImDisasmView::onMouseMove(int x, int y, int button) { + if ((button & 1) != 0) { + setCurAddress(yToAddress(y), ImGui::IsKeyDown(ImGuiKey_LeftShift)); + } +} + +void ImDisasmView::updateStatusBarText() { + auto memLock = Memory::Lock(); + if (!PSP_IsInited()) + return; + + char text[512]; + DisassemblyLineInfo line; + manager.getLine(curAddress_, true, line); + + text[0] = 0; + if (line.type == DISTYPE_OPCODE || line.type == DISTYPE_MACRO) { + if (line.info.hasRelevantAddress && IsLikelyStringAt(line.info.relevantAddress)) { + snprintf(text, sizeof(text), "[%08X] = \"%s\"", line.info.relevantAddress, Memory::GetCharPointer(line.info.relevantAddress)); + } + + if (line.info.isDataAccess) { + if (!Memory::IsValidAddress(line.info.dataAddress)) { + snprintf(text, sizeof(text), "Invalid address %08X", line.info.dataAddress); + } else { + bool isFloat = MIPSGetInfo(line.info.encodedOpcode) & (IS_FPU | IS_VFPU); + switch (line.info.dataSize) { + case 1: + snprintf(text, sizeof(text), "[%08X] = %02X", line.info.dataAddress, Memory::Read_U8(line.info.dataAddress)); + break; + case 2: + snprintf(text, sizeof(text), "[%08X] = %04X", line.info.dataAddress, Memory::Read_U16(line.info.dataAddress)); + break; + case 4: + { + u32 dataInt = Memory::Read_U32(line.info.dataAddress); + u32 dataFloat = Memory::Read_Float(line.info.dataAddress); + std::string dataString; + if (isFloat) + dataString = StringFromFormat("%08X / %f", dataInt, dataFloat); + else + dataString = StringFromFormat("%08X", dataInt); + + const std::string addressSymbol = g_symbolMap->GetLabelString(dataInt); + if (!addressSymbol.empty()) { + snprintf(text, sizeof(text), "[%08X] = %s (%s)", line.info.dataAddress, addressSymbol.c_str(), dataString.c_str()); + } else { + snprintf(text, sizeof(text), "[%08X] = %s", line.info.dataAddress, dataString.c_str()); + } + break; + } + case 16: + { + uint32_t dataInt[4]; + float dataFloat[4]; + for (int i = 0; i < 4; ++i) { + dataInt[i] = Memory::Read_U32(line.info.dataAddress + i * 4); + dataFloat[i] = Memory::Read_Float(line.info.dataAddress + i * 4); + } + std::string dataIntString = StringFromFormat("%08X,%08X,%08X,%08X", dataInt[0], dataInt[1], dataInt[2], dataInt[3]); + std::string dataFloatString = StringFromFormat("%f,%f,%f,%f", dataFloat[0], dataFloat[1], dataFloat[2], dataFloat[3]); + + snprintf(text, sizeof(text), "[%08X] = %s / %s", line.info.dataAddress, dataIntString.c_str(), dataFloatString.c_str()); + break; + } + } + } + } + + if (line.info.isBranch) { + const std::string addressSymbol = g_symbolMap->GetLabelString(line.info.branchTarget); + if (addressSymbol.empty()) { + snprintf(text, sizeof(text), "%08X", line.info.branchTarget); + } else { + snprintf(text, sizeof(text), "%08X = %s", line.info.branchTarget, addressSymbol.c_str()); + } + } + } else if (line.type == DISTYPE_DATA) { + u32 start = g_symbolMap->GetDataStart(curAddress_); + if (start == -1) + start = curAddress_; + + u32 diff = curAddress_ - start; + const std::string label = g_symbolMap->GetLabelString(start); + + if (!label.empty()) { + if (diff != 0) + snprintf(text, sizeof(text), "%08X (%s) + %08X", start, label.c_str(), diff); + else + snprintf(text, sizeof(text), "%08X (%s)", start, label.c_str()); + } else { + if (diff != 0) + snprintf(text, sizeof(text), "%08X + %08X", start, diff); + else + snprintf(text, sizeof(text), "%08X", start); + } + } + + statusBarText_ = text; + + const std::string label = g_symbolMap->GetLabelString(line.info.opcodeAddress); + if (!label.empty()) { + statusBarText_ = label; + } +} + +u32 ImDisasmView::yToAddress(int y) { + int line = y / rowHeight_; + return manager.getNthNextAddress(windowStart_, line); +} + +void ImDisasmView::calculatePixelPositions() { + pixelPositions_.addressStart = 16; + pixelPositions_.opcodeStart = pixelPositions_.addressStart + 18 * charWidth_; + pixelPositions_.argumentsStart = pixelPositions_.opcodeStart + 9 * charWidth_; + pixelPositions_.arrowsStart = pixelPositions_.argumentsStart + 30 * charWidth_; +} + +void ImDisasmView::search(bool continueSearch) { + auto memLock = Memory::Lock(); + u32 searchAddress; + + if (continueSearch == false || searchQuery_[0] == 0) { + /* + if (InputBox_GetString(MainWindow::GetHInstance(), MainWindow::GetHWND(), L"Search for:", searchQuery, searchQuery) == false + || searchQuery[0] == 0) { + SetFocus(wnd); + return; + } + */ + + for (size_t i = 0; i < searchQuery_.size(); i++) { + searchQuery_[i] = tolower(searchQuery_[i]); + } + searchAddress = manager.getNthNextAddress(curAddress_, 1); + } else { + searchAddress = manager.getNthNextAddress(matchAddress_, 1); + } + + // limit address to sensible ranges + if (searchAddress < 0x04000000) + searchAddress = 0x04000000; + if (searchAddress >= 0x04200000 && searchAddress < 0x08000000) + searchAddress = 0x08000000; + if (searchAddress >= 0x0A000000) { + // MessageBox(wnd, L"Not found", L"Search", MB_OK); + return; + } + + searching_ = true; + + DisassemblyLineInfo lineInfo; + while (searchAddress < 0x0A000000) { + manager.getLine(searchAddress, displaySymbols_, lineInfo); + + char addressText[64]; + getDisasmAddressText(searchAddress, addressText, true, lineInfo.type == DISTYPE_OPCODE); + + const char* opcode = lineInfo.name.c_str(); + const char* arguments = lineInfo.params.c_str(); + + char merged[512]; + int mergePos = 0; + + // I'm doing it manually to convert everything to lowercase at the same time + for (int i = 0; addressText[i] != 0; i++) merged[mergePos++] = tolower(addressText[i]); + merged[mergePos++] = ' '; + for (int i = 0; opcode[i] != 0; i++) merged[mergePos++] = tolower(opcode[i]); + merged[mergePos++] = ' '; + for (int i = 0; arguments[i] != 0; i++) merged[mergePos++] = tolower(arguments[i]); + merged[mergePos] = 0; + + // match! + if (strstr(merged, searchQuery_.c_str()) != NULL) { + matchAddress_ = searchAddress; + searching_ = false; + gotoAddr(searchAddress); + return; + } + + // cancel search + if ((searchAddress % 256) == 0 && ImGui::GetKeyPressedAmount(ImGuiKey_Escape, 0.0f, 0.0f)) { + searching_ = false; + return; + } + + searchAddress = manager.getNthNextAddress(searchAddress, 1); + if (searchAddress >= 0x04200000 && searchAddress < 0x08000000) searchAddress = 0x08000000; + } + + // MessageBox(wnd, L"Not found", L"Search", MB_OK); + + searching_ = false; +} + +std::string ImDisasmView::disassembleRange(u32 start, u32 size) { + auto memLock = Memory::Lock(); + std::string result; + + // 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); + + if (info.isBranch && g_symbolMap->GetLabelString(info.branchTarget).empty()) { + if (branchAddresses.find(info.branchTarget) == branchAddresses.end()) { + branchAddresses.insert(info.branchTarget); + } + } + } + + u32 disAddress = start; + bool previousLabel = true; + DisassemblyLineInfo line; + while (disAddress < start + size) { + char addressText[64], buffer[512]; + + manager.getLine(disAddress, displaySymbols_, line); + bool isLabel = getDisasmAddressText(disAddress, addressText, false, line.type == DISTYPE_OPCODE); + + if (isLabel) { + if (!previousLabel) result += "\r\n"; + sprintf(buffer, "%s\r\n\r\n", addressText); + result += buffer; + } else if (branchAddresses.find(disAddress) != branchAddresses.end()) { + if (!previousLabel) result += "\r\n"; + sprintf(buffer, "pos_%08X:\r\n\r\n", disAddress); + result += buffer; + } + + if (line.info.isBranch && !line.info.isBranchToRegister + && g_symbolMap->GetLabelString(line.info.branchTarget).empty() + && branchAddresses.find(line.info.branchTarget) != branchAddresses.end()) + { + sprintf(buffer, "pos_%08X", line.info.branchTarget); + line.params = line.params.substr(0, line.params.find("0x")) + buffer; + } + + sprintf(buffer, "\t%s\t%s\r\n", line.name.c_str(), line.params.c_str()); + result += buffer; + previousLabel = isLabel; + disAddress += line.totalSize; + } + + return result; +} + +void ImDisasmView::disassembleToFile() { // get size + /* + u32 size; + if (executeExpressionWindow(wnd, debugger, size) == false) + return; + if (size == 0 || size > 10 * 1024 * 1024) { + MessageBox(wnd, L"Invalid size!", L"Error", MB_OK); + return; + } + + std::string filename; + if (W32Util::BrowseForFileName(false, nullptr, L"Save Disassembly As...", nullptr, L"All Files\0*.*\0\0", nullptr, filename)) { + std::wstring fileName = ConvertUTF8ToWString(filename); + FILE *output = _wfopen(fileName.c_str(), L"wb"); + if (output == nullptr) { + MessageBox(wnd, L"Could not open file!", L"Error", MB_OK); + return; + } + + std::string disassembly = disassembleRange(curAddress, size); + fprintf(output, "%s", disassembly.c_str()); + + fclose(output); + MessageBox(wnd, L"Finished!", L"Done", MB_OK); + } + */ +} + +void ImDisasmView::getOpcodeText(u32 address, char* dest, int bufsize) { + DisassemblyLineInfo line; + address = manager.getStartAddress(address); + manager.getLine(address, displaySymbols_, line); + snprintf(dest, bufsize, "%s %s", line.name.c_str(), line.params.c_str()); +} + +void ImDisasmView::scrollStepping(u32 newPc) { + u32 windowEnd = manager.getNthNextAddress(windowStart_, visibleRows_); + + newPc = manager.getStartAddress(newPc); + if (newPc >= windowEnd || newPc >= manager.getNthPreviousAddress(windowEnd, 1)) + { + windowStart_ = manager.getNthPreviousAddress(newPc, visibleRows_ - 2); + } +} + +u32 ImDisasmView::getInstructionSizeAt(u32 address) { + u32 start = manager.getStartAddress(address); + u32 next = manager.getNthNextAddress(start, 1); + return next - address; +} diff --git a/UI/ImDebugger/ImDisasmView.h b/UI/ImDebugger/ImDisasmView.h new file mode 100644 index 0000000000..7a88f65ebf --- /dev/null +++ b/UI/ImDebugger/ImDisasmView.h @@ -0,0 +1,178 @@ +#pragma once +#include +#include +#include + +#include "ext/imgui/imgui.h" + +#include "Common/CommonTypes.h" +#include "Common/Log.h" + +#include "Core/Debugger/DisassemblyManager.h" +#include "Core/Debugger/DebugInterface.h" + +// Corresponds to CtrlDisAsmView +// TODO: Fold out common code. +class ImDisasmView { +public: + ImDisasmView(); + ~ImDisasmView(); + + void Draw(ImDrawList *drawList); + + void PopupMenu(); + + void onChar(int c); + void onVScroll(int amount); + void onKeyDown(ImGuiKey key); + void onMouseDown(int x, int y, int button); + void onMouseUp(int x, int y, int button); + void onMouseMove(int x, int y, int button); + void scrollAddressIntoView(); + bool curAddressIsVisible(); + void scanVisibleFunctions(); + void clearFunctions() { manager.clear(); }; + + void getOpcodeText(u32 address, char* dest, int bufsize); + u32 yToAddress(int y); + + void setDebugger(DebugInterface *deb) { + if (debugger != deb) { + debugger = deb; + curAddress_ = debugger->getPC(); + manager.setCpu(deb); + } + } + DebugInterface *getDebugger() { + return debugger; + } + + void scrollStepping(u32 newPc); + u32 getInstructionSizeAt(u32 address); + + void gotoAddr(unsigned int addr) { + if (positionLocked_ != 0) + return; + u32 windowEnd = manager.getNthNextAddress(windowStart_, visibleRows_); + u32 newAddress = manager.getStartAddress(addr); + + if (newAddress < windowStart_ || newAddress >= windowEnd) { + windowStart_ = manager.getNthPreviousAddress(newAddress, visibleRows_ / 2); + } + + setCurAddress(newAddress); + scanVisibleFunctions(); + } + void gotoPC() { + gotoAddr(debugger->getPC()); + } + u32 getSelection() { + return curAddress_; + } + + void setShowMode(bool s) { + showHex_ = s; + } + + void toggleBreakpoint(bool toggleEnabled = false); + void editBreakpoint(); + + void scrollWindow(int lines) { + if (lines < 0) + windowStart_ = manager.getNthPreviousAddress(windowStart_, abs(lines)); + else + windowStart_ = manager.getNthNextAddress(windowStart_, lines); + scanVisibleFunctions(); + } + + void setCurAddress(u32 newAddress, bool extend = false) { + newAddress = manager.getStartAddress(newAddress); + const u32 after = manager.getNthNextAddress(newAddress, 1); + curAddress_ = newAddress; + selectRangeStart_ = extend ? std::min(selectRangeStart_, newAddress) : newAddress; + selectRangeEnd_ = extend ? std::max(selectRangeEnd_, after) : after; + updateStatusBarText(); + } + + void LockPosition() { + positionLocked_++; + } + void UnlockPosition() { + positionLocked_--; + _assert_(positionLocked_ >= 0); + } + + // Check these every frame! + const std::string &StatusBarText() const { + return statusBarText_; + } + bool SymbolMapReloaded() { + bool retval = mapReloaded_; + mapReloaded_ = false; + return retval; + } + +private: + enum class CopyInstructionsMode { + OPCODES, + DISASM, + ADDRESSES, + }; + + struct Rect { + float left; + float top; + float right; + float bottom; + }; + + void assembleOpcode(u32 address, const std::string &defaultText); + std::string disassembleRange(u32 start, u32 size); + void disassembleToFile(); + void search(bool continueSearch); + void followBranch(); + 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 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, int x, int y, ImColor textColor, const std::set ¤tArguments); + + DisassemblyManager manager; + u32 curAddress_ = 0; + u32 selectRangeStart_ = 0; + u32 selectRangeEnd_ = 0; + float rowHeight_ = 0.f; + float charWidth_ = 0.f; + + bool bpPopup_ = false; + bool hasFocus_ = true; + bool showHex_ = false; + + DebugInterface *debugger = nullptr; + + u32 windowStart_ = 0; + int visibleRows_ = 1; + bool displaySymbols_ = true; + + struct { + int addressStart; + int opcodeStart; + int argumentsStart; + int arrowsStart; + } pixelPositions_; + + std::vector jumpStack_; + + std::string searchQuery_; + int matchAddress_; + bool searching_ = false; + bool keyTaken = false; + bool mapReloaded_ = false; + + int positionLocked_ = 0; + + std::string statusBarText_; +}; diff --git a/UI/UI.vcxproj b/UI/UI.vcxproj index e1fcc93938..f8fbc3f26f 100644 --- a/UI/UI.vcxproj +++ b/UI/UI.vcxproj @@ -51,6 +51,8 @@ + + @@ -91,6 +93,8 @@ + + diff --git a/UI/UI.vcxproj.filters b/UI/UI.vcxproj.filters index ba5da5148c..288c8556a0 100644 --- a/UI/UI.vcxproj.filters +++ b/UI/UI.vcxproj.filters @@ -98,6 +98,12 @@ Screens + + ImDebugger + + + ImDebugger + @@ -196,6 +202,12 @@ Screens + + ImDebugger + + + ImDebugger + @@ -204,5 +216,8 @@ {071baa63-c681-4ad6-945b-e126645d1b55} + + {fda6bc55-1386-4650-a274-44fac9605ea3} + \ No newline at end of file diff --git a/UWP/CoreUWP/CoreUWP.vcxproj b/UWP/CoreUWP/CoreUWP.vcxproj index 802a98d4e5..b9fc66b1e6 100644 --- a/UWP/CoreUWP/CoreUWP.vcxproj +++ b/UWP/CoreUWP/CoreUWP.vcxproj @@ -1023,4 +1023,4 @@ - + \ No newline at end of file diff --git a/UWP/UI_UWP/UI_UWP.vcxproj b/UWP/UI_UWP/UI_UWP.vcxproj index 40838af5ed..88f2bc539e 100644 --- a/UWP/UI_UWP/UI_UWP.vcxproj +++ b/UWP/UI_UWP/UI_UWP.vcxproj @@ -126,6 +126,8 @@ + + @@ -166,6 +168,8 @@ + + diff --git a/UWP/UI_UWP/UI_UWP.vcxproj.filters b/UWP/UI_UWP/UI_UWP.vcxproj.filters index b7bb978d11..656c6c61e2 100644 --- a/UWP/UI_UWP/UI_UWP.vcxproj.filters +++ b/UWP/UI_UWP/UI_UWP.vcxproj.filters @@ -39,6 +39,12 @@ + + ImDebugger + + + ImDebugger + @@ -79,5 +85,16 @@ + + ImDebugger + + + ImDebugger + + + + + {4013cb89-6145-451c-9cb1-d63d01f66bd5} + \ No newline at end of file diff --git a/Windows/Debugger/CtrlDisAsmView.cpp b/Windows/Debugger/CtrlDisAsmView.cpp index 9ce66398f9..501cc51cbb 100644 --- a/Windows/Debugger/CtrlDisAsmView.cpp +++ b/Windows/Debugger/CtrlDisAsmView.cpp @@ -60,7 +60,7 @@ void CtrlDisAsmView::deinit() //UnregisterClass(szClassName, hInst) } -void CtrlDisAsmView::scanFunctions() +void CtrlDisAsmView::scanVisibleFunctions() { manager.analyze(windowStart,manager.getNthNextAddress(windowStart,visibleRows)-windowStart); } @@ -322,7 +322,7 @@ void CtrlDisAsmView::assembleOpcode(u32 address, const std::string &defaultText) Reporting::NotifyDebugger(); if (result == true) { - scanFunctions(); + scanVisibleFunctions(); if (address == curAddress) gotoAddr(manager.getNthNextAddress(curAddress,1)); @@ -527,7 +527,7 @@ void CtrlDisAsmView::onPaint(WPARAM wParam, LPARAM lParam) addressPositions[address] = rowY1; // draw background - COLORREF backgroundColor = whiteBackground ? 0xFFFFFF : debugger->getColor(address); + COLORREF backgroundColor = whiteBackground ? 0xFFFFFF : debugger->getColor(address, false); COLORREF textColor = 0x000000; if (isInInterval(address,line.totalSize,debugger->getPC())) @@ -641,7 +641,7 @@ void CtrlDisAsmView::onVScroll(WPARAM wParam, LPARAM lParam) return; } - scanFunctions(); + scanVisibleFunctions(); redraw(); } @@ -750,11 +750,11 @@ void CtrlDisAsmView::onKeyDown(WPARAM wParam, LPARAM lParam) break; case VK_UP: scrollWindow(-1); - scanFunctions(); + scanVisibleFunctions(); break; case VK_DOWN: scrollWindow(1); - scanFunctions(); + scanVisibleFunctions(); break; case VK_NEXT: setCurAddress(manager.getNthPreviousAddress(windowEnd,1),KeyDownAsync(VK_SHIFT)); @@ -836,7 +836,7 @@ void CtrlDisAsmView::scrollAddressIntoView() else if (curAddress >= windowEnd) windowStart = manager.getNthPreviousAddress(curAddress,visibleRows-1); - scanFunctions(); + scanVisibleFunctions(); } bool CtrlDisAsmView::curAddressIsVisible() diff --git a/Windows/Debugger/CtrlDisAsmView.h b/Windows/Debugger/CtrlDisAsmView.h index 4bfd1c0fc9..fd6fcf3907 100644 --- a/Windows/Debugger/CtrlDisAsmView.h +++ b/Windows/Debugger/CtrlDisAsmView.h @@ -100,7 +100,7 @@ public: void scrollAddressIntoView(); bool curAddressIsVisible(); void redraw(); - void scanFunctions(); + void scanVisibleFunctions(); void clearFunctions() { manager.clear(); }; void getOpcodeText(u32 address, char* dest, int bufsize); @@ -135,7 +135,7 @@ public: } setCurAddress(newAddress); - scanFunctions(); + scanVisibleFunctions(); redraw(); } void gotoPC() @@ -162,7 +162,7 @@ public: else windowStart = manager.getNthNextAddress(windowStart,lines); - scanFunctions(); + scanVisibleFunctions(); redraw(); } diff --git a/Windows/Debugger/CtrlRegisterList.cpp b/Windows/Debugger/CtrlRegisterList.cpp index 8482ddb112..0e1d634002 100644 --- a/Windows/Debugger/CtrlRegisterList.cpp +++ b/Windows/Debugger/CtrlRegisterList.cpp @@ -576,8 +576,8 @@ void CtrlRegisterList::onMouseMove(WPARAM wParam, LPARAM lParam, int button) int CtrlRegisterList::yToIndex(int y) { -// int ydiff=y-rect.bottom/2-rowHeight/2; -// ydiff=(int)(floorf((float)ydiff / (float)rowHeight))+1; +// int ydiff=y-rect.bottom/2-rowHeight_/2; +// ydiff=(int)(floorf((float)ydiff / (float)rowHeight_))+1; // return curAddress + ydiff * align; int n = (y/rowHeight) - 1; if (n<0) n=0; diff --git a/Windows/Debugger/Debugger_Disasm.cpp b/Windows/Debugger/Debugger_Disasm.cpp index 8935cd975e..7a91b3ec3f 100644 --- a/Windows/Debugger/Debugger_Disasm.cpp +++ b/Windows/Debugger/Debugger_Disasm.cpp @@ -707,7 +707,7 @@ void CDisasm::SetDebugMode(bool _bDebug, bool switchPC) if (switchPC) ptr->gotoPC(); - ptr->scanFunctions(); + ptr->scanVisibleFunctions(); } else { diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 445eb33aab..c9d0750407 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -875,6 +875,8 @@ LOCAL_SRC_FILES := \ $(SRC)/android/jni/AndroidVulkanContext.cpp \ $(SRC)/android/jni/AndroidAudio.cpp \ $(SRC)/android/jni/OpenSLContext.cpp \ + $(SRC)/UI/ImDebugger/ImDebugger.cpp \ + $(SRC)/UI/ImDebugger/ImDisasmView.cpp \ $(SRC)/UI/AudioCommon.cpp \ $(SRC)/UI/BackgroundAudio.cpp \ $(SRC)/UI/DiscordIntegration.cpp \ diff --git a/ext/imgui/imgui_impl_platform.cpp b/ext/imgui/imgui_impl_platform.cpp index 62344d2c76..d6604d0d86 100644 --- a/ext/imgui/imgui_impl_platform.cpp +++ b/ext/imgui/imgui_impl_platform.cpp @@ -8,7 +8,7 @@ #include "imgui_impl_platform.h" void ImGui_ImplPlatform_KeyEvent(const KeyInput &key) { - ImGuiIO& io = ImGui::GetIO(); + ImGuiIO &io = ImGui::GetIO(); if (key.flags & KEY_DOWN) { // Specially handle scroll events and any other special keys. From 84a0293e54f96efc582aafcfe1da717cf3b83ee1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 6 Nov 2024 15:53:38 +0100 Subject: [PATCH 2/6] Add basic register view --- Core/Debugger/DebugInterface.h | 3 +++ Core/MIPS/MIPSDebugInterface.h | 1 + UI/ImDebugger/ImDebugger.cpp | 37 ++++++++++++++++++++++++++++++++++ UI/ImDebugger/ImDebugger.h | 1 + UI/ImDebugger/ImDisasmView.cpp | 9 +++++---- UI/ImDebugger/ImDisasmView.h | 2 +- 6 files changed, 48 insertions(+), 5 deletions(-) diff --git a/Core/Debugger/DebugInterface.h b/Core/Debugger/DebugInterface.h index 34e354b9c0..c8c40f18f4 100644 --- a/Core/Debugger/DebugInterface.h +++ b/Core/Debugger/DebugInterface.h @@ -51,6 +51,9 @@ public: virtual const char *GetName() = 0; virtual u32 GetGPR32Value(int reg) {return 0;} virtual void SetGPR32Value(int reg) {} + virtual float GetFPR32Value(int reg) { return -1.0f; } + virtual float GetVPR32Value(int reg) { return -1.0f; } + virtual u32 GetPC() = 0; virtual void SetPC(u32 _pc) = 0; virtual u32 GetLR() {return GetPC();} diff --git a/Core/MIPS/MIPSDebugInterface.h b/Core/MIPS/MIPSDebugInterface.h index 2a6e88f66f..61144c5aba 100644 --- a/Core/MIPS/MIPSDebugInterface.h +++ b/Core/MIPS/MIPSDebugInterface.h @@ -48,6 +48,7 @@ public: //overridden functions const char *GetName() override; u32 GetGPR32Value(int reg) override { return cpu->r[reg]; } + float GetFPR32Value(int reg) override { return cpu->f[reg]; } u32 GetPC() override { return cpu->pc; } u32 GetLR() override { return cpu->r[MIPS_REG_RA]; } void DisAsm(u32 pc, char *out, size_t outSize) override; diff --git a/UI/ImDebugger/ImDebugger.cpp b/UI/ImDebugger/ImDebugger.cpp index ca941e8499..f747118c3a 100644 --- a/UI/ImDebugger/ImDebugger.cpp +++ b/UI/ImDebugger/ImDebugger.cpp @@ -14,6 +14,38 @@ #include "UI/ImDebugger/ImDebugger.h" +void DrawRegisterView(MIPSDebugInterface *mipsDebug, bool *open) { + if (!ImGui::Begin("Registers", open)) { + ImGui::End(); + return; + } + + if (ImGui::BeginTabBar("RegisterGroups", ImGuiTabBarFlags_None)) { + if (ImGui::BeginTabItem("GPR")) { + for (int i = 0; i < 32; i++) { + const u32 value = mipsDebug->GetGPR32Value(i); + ImGui::Text("%s: %08x (%d)", mipsDebug->GetRegName(0, i).c_str(), value, value); + } + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("FPR")) { + for (int i = 0; i < 32; i++) { + float fvalue = mipsDebug->GetFPR32Value(i); + u32 fivalue; + memcpy(&fivalue, &fvalue, sizeof(fivalue)); + ImGui::Text("%s: %0.6f (%08x)", mipsDebug->GetRegName(1, i).c_str(), fvalue, fivalue); + } + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("VPR")) { + ImGui::Text("TODO"); + ImGui::EndTabItem(); + } + ImGui::EndTabBar(); + } + ImGui::End(); +} + void ImDebugger::Frame(MIPSDebugInterface *mipsDebug) { // Snapshot the coreState to avoid inconsistency. const CoreState coreState = ::coreState; @@ -47,6 +79,7 @@ void ImDebugger::Frame(MIPSDebugInterface *mipsDebug) { if (ImGui::BeginMenu("Window")) { ImGui::Checkbox("Dear ImGUI Demo", &demoOpen_); ImGui::Checkbox("CPU debugger", &disasmOpen_); + ImGui::Checkbox("Registers", ®sOpen_); ImGui::EndMenu(); } ImGui::EndMainMenuBar(); @@ -60,6 +93,10 @@ void ImDebugger::Frame(MIPSDebugInterface *mipsDebug) { disasm_.Draw(mipsDebug, &disasmOpen_); } + if (regsOpen_) { + DrawRegisterView(mipsDebug, ®sOpen_); + } + ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it. ImGui::Text("This is some useful text."); // Display some text (you can use a format strings too) ImGui::End(); diff --git a/UI/ImDebugger/ImDebugger.h b/UI/ImDebugger/ImDebugger.h index 91b1cc8d05..9711db050b 100644 --- a/UI/ImDebugger/ImDebugger.h +++ b/UI/ImDebugger/ImDebugger.h @@ -60,4 +60,5 @@ struct ImDebugger { // Open variables. bool disasmOpen_ = true; bool demoOpen_ = false; + bool regsOpen_ = true; }; diff --git a/UI/ImDebugger/ImDisasmView.cpp b/UI/ImDebugger/ImDisasmView.cpp index d68d131ec0..25bf864aa8 100644 --- a/UI/ImDebugger/ImDisasmView.cpp +++ b/UI/ImDebugger/ImDisasmView.cpp @@ -156,7 +156,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, Rect rect, std::map &addressPositions, const BranchLine &line) { u32 windowEnd = manager.getNthNextAddress(windowStart_, visibleRows_); float topY; @@ -185,6 +185,7 @@ void ImDisasmView::drawBranchLine(ImDrawList *drawList, Rect rect, std::map addressPositions; + std::map addressPositions; const std::set currentArguments = getSelectedLineArguments(); DisassemblyLineInfo line; for (int i = 0; i < visibleRows_; i++) { manager.getLine(address, displaySymbols_, line); - int rowY1 = rowHeight_ * i; - int rowY2 = rowHeight_ * (i + 1); + float rowY1 = rowHeight_ * i; + float rowY2 = rowHeight_ * (i + 1); addressPositions[address] = rowY1; diff --git a/UI/ImDebugger/ImDisasmView.h b/UI/ImDebugger/ImDisasmView.h index 7a88f65ebf..2279508b8e 100644 --- a/UI/ImDebugger/ImDisasmView.h +++ b/UI/ImDebugger/ImDisasmView.h @@ -134,7 +134,7 @@ 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, ImDisasmView::Rect rc, std::map &addressPositions, const BranchLine &line); void CopyInstructions(u32 startAddr, u32 endAddr, CopyInstructionsMode mode); void NopInstructions(u32 startAddr, u32 endAddr); std::set getSelectedLineArguments(); From 9c92978fcc33db04fa8f8cb120edb2d629be0146 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 7 Nov 2024 10:17:26 +0100 Subject: [PATCH 3/6] ImGui debugger: assorted work --- Core/HLE/sceDisplay.cpp | 2 + UI/ImDebugger/ImDebugger.cpp | 21 ++++-- UI/ImDebugger/ImDebugger.h | 3 +- UI/ImDebugger/ImDisasmView.cpp | 117 +++++++++++++++++------------- UI/ImDebugger/ImDisasmView.h | 18 ++--- Windows/EmuThread.cpp | 2 +- ext/imgui/imgui_impl_platform.cpp | 1 + 7 files changed, 93 insertions(+), 71 deletions(-) diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index cca85b09fd..cb1a8b4c30 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -651,6 +651,8 @@ void __DisplayFlip(int cyclesLate) { if (fbReallyDirty) { DisplayFireActualFlip(); } + } else { + WARN_LOG(Log::sceDisplay, "Core_NextFrame returned false"); } } diff --git a/UI/ImDebugger/ImDebugger.cpp b/UI/ImDebugger/ImDebugger.cpp index f747118c3a..9c4ae46077 100644 --- a/UI/ImDebugger/ImDebugger.cpp +++ b/UI/ImDebugger/ImDebugger.cpp @@ -23,8 +23,14 @@ void DrawRegisterView(MIPSDebugInterface *mipsDebug, bool *open) { if (ImGui::BeginTabBar("RegisterGroups", ImGuiTabBarFlags_None)) { if (ImGui::BeginTabItem("GPR")) { for (int i = 0; i < 32; i++) { - const u32 value = mipsDebug->GetGPR32Value(i); - ImGui::Text("%s: %08x (%d)", mipsDebug->GetRegName(0, i).c_str(), value, value); + const int value = mipsDebug->GetGPR32Value(i); + if (value >= -1000000 && value <= 1000000) { + // Display small values in decimal. + ImGui::Text("%s: %08x (%d)", mipsDebug->GetRegName(0, i).c_str(), value, value); + } else { + // But not big ones to reduce clutter. + ImGui::Text("%s: %08x", mipsDebug->GetRegName(0, i).c_str(), value); + } } ImGui::EndTabItem(); } @@ -90,7 +96,7 @@ void ImDebugger::Frame(MIPSDebugInterface *mipsDebug) { } if (disasmOpen_) { - disasm_.Draw(mipsDebug, &disasmOpen_); + disasm_.Draw(mipsDebug, &disasmOpen_, coreState); } if (regsOpen_) { @@ -102,7 +108,7 @@ void ImDebugger::Frame(MIPSDebugInterface *mipsDebug) { ImGui::End(); } -void ImDisasmWindow::Draw(MIPSDebugInterface *mipsDebug, bool *open) { +void ImDisasmWindow::Draw(MIPSDebugInterface *mipsDebug, bool *open, CoreState coreState) { char title[256]; snprintf(title, sizeof(title), "%s - Disassembly", "Allegrex MIPS"); @@ -114,14 +120,18 @@ void ImDisasmWindow::Draw(MIPSDebugInterface *mipsDebug, bool *open) { return; } + ImGui::BeginDisabled(coreState != CORE_STEPPING); if (ImGui::SmallButton("Run")) { Core_Resume(); } + ImGui::EndDisabled(); ImGui::SameLine(); + ImGui::BeginDisabled(coreState != CORE_RUNNING); if (ImGui::SmallButton("Pause")) { Core_Break("Pause"); } + ImGui::EndDisabled(); ImGui::SameLine(); if (ImGui::SmallButton("Step Into")) { @@ -171,9 +181,10 @@ void ImDisasmWindow::Draw(MIPSDebugInterface *mipsDebug, bool *open) { } ImGui::TableSetColumnIndex(1); - // Draw border and background color disasmView_.Draw(ImGui::GetWindowDrawList()); ImGui::EndTable(); + + ImGui::Text("%s", disasmView_.StatusBarText().c_str()); } ImGui::End(); } diff --git a/UI/ImDebugger/ImDebugger.h b/UI/ImDebugger/ImDebugger.h index 9711db050b..5a1aef1f7e 100644 --- a/UI/ImDebugger/ImDebugger.h +++ b/UI/ImDebugger/ImDebugger.h @@ -8,6 +8,7 @@ #include "Common/CommonTypes.h" #include "Common/Log.h" +#include "Core/Core.h" #include "Core/Debugger/DisassemblyManager.h" #include "Core/Debugger/DebugInterface.h" @@ -25,7 +26,7 @@ class MIPSDebugInterface; // Corresponds to the CDisasm dialog class ImDisasmWindow { public: - void Draw(MIPSDebugInterface *mipsDebug, bool *open); + void Draw(MIPSDebugInterface *mipsDebug, bool *open, CoreState coreState); void PCChanged() { pcChanged_ = true; diff --git a/UI/ImDebugger/ImDisasmView.cpp b/UI/ImDebugger/ImDisasmView.cpp index 25bf864aa8..c248df47ae 100644 --- a/UI/ImDebugger/ImDisasmView.cpp +++ b/UI/ImDebugger/ImDisasmView.cpp @@ -33,7 +33,7 @@ ImDisasmView::~ImDisasmView() { manager.clear(); } -void ImDisasmView::scanVisibleFunctions() { +void ImDisasmView::ScanVisibleFunctions() { manager.analyze(windowStart_, manager.getNthNextAddress(windowStart_, visibleRows_) - windowStart_); } @@ -143,7 +143,7 @@ void ImDisasmView::assembleOpcode(u32 address, const std::string &defaultText) { Reporting::NotifyDebugger(); if (result == true) { - scanVisibleFunctions(); + ScanVisibleFunctions(); if (address == curAddress) gotoAddr(manager.getNthNextAddress(curAddress, 1)); @@ -314,15 +314,20 @@ void ImDisasmView::drawArguments(ImDrawList *drawList, Rect rc, const Disassembl } void ImDisasmView::Draw(ImDrawList *drawList) { + // TODO: Don't need to do these every frame. + rowHeight_ = ImGui::GetTextLineHeightWithSpacing(); + charWidth_ = ImGui::CalcTextSize("W", nullptr, false, -1.0f).x; + ImVec2 canvas_p0 = ImGui::GetCursorScreenPos(); // ImDrawList API uses screen coordinates! ImVec2 canvas_sz = ImGui::GetContentRegionAvail(); // Resize canvas to what's available - int lineHeight = (int)ImGui::GetTextLineHeightWithSpacing(); if (canvas_sz.x < 50.0f) canvas_sz.x = 50.0f; if (canvas_sz.y < 50.0f) canvas_sz.y = 50.0f; - canvas_sz.y -= lineHeight * 2; + canvas_sz.y -= rowHeight_ * 2.0f; // space for status bar // 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"); @@ -333,6 +338,9 @@ void ImDisasmView::Draw(ImDrawList *drawList) { drawList->PushClipRect(canvas_p0, canvas_p1, true); drawList->AddRectFilled(canvas_p0, canvas_p1, IM_COL32(25, 25, 25, 255)); + if (is_active) { + drawList->AddRect(canvas_p0, canvas_p1, IM_COL32(255, 255, 255, 255)); + } Rect rect; rect.left = canvas_p0.x; @@ -340,8 +348,6 @@ void ImDisasmView::Draw(ImDrawList *drawList) { rect.right = canvas_p1.x; rect.bottom = canvas_p1.y; - rowHeight_ = ImGui::GetTextLineHeightWithSpacing(); - charWidth_ = ImGui::CalcTextSize("W", nullptr, false, -1.0f).x; calculatePixelPositions(); visibleRows_ = (int)((rect.bottom - rect.top + rowHeight_ - 1.f) / rowHeight_); @@ -351,9 +357,6 @@ void ImDisasmView::Draw(ImDrawList *drawList) { return; } - //HICON breakPoint = (HICON)LoadIcon(GetModuleHandle(0), (LPCWSTR)IDI_STOP); - //HICON breakPointDisable = (HICON)LoadIcon(GetModuleHandle(0), (LPCWSTR)IDI_STOPDISABLE); - unsigned int address = windowStart_; std::map addressPositions; @@ -372,7 +375,7 @@ void ImDisasmView::Draw(ImDrawList *drawList) { ImColor textColor = 0xFFFFFFFF; if (isInInterval(address, line.totalSize, debugger->getPC())) { - backgroundColor = scaleColor(backgroundColor, 1.05f); + backgroundColor = scaleColor(backgroundColor, 1.15f); } if (address >= selectRangeStart_ && address < selectRangeEnd_ && searching_ == false) { @@ -389,11 +392,11 @@ void ImDisasmView::Draw(ImDrawList *drawList) { // display breakpoint, if any bool enabled; if (CBreakPoints::IsAddressBreakPoint(address, &enabled)) { - if (enabled) - textColor = 0x0000FF; + ImColor breakColor = 0xFF0000FF; + if (!enabled) + breakColor = 0xFF909090; float yOffset = std::max(-1.0f, (rowHeight_ - 14.f + 1.f) / 2.0f); - // drawList->AddCircleFilled(ImVec2(canvas_p0.x + lineHeight * 0.5f, lineStart.y + lineHeight * 0.5f), lineHeight * 0.45f, 0xFF0000FF, 12); - // DrawIconEx(hdc, 2, rowY1 + 1 + yOffset, enabled ? breakPoint : breakPointDisable, 32, 32, 0, 0, DI_NORMAL); + drawList->AddCircleFilled(ImVec2(canvas_p0.x + rowHeight_ * 0.5f, canvas_p0.y + rowY1 + rowHeight_ * 0.5f), rowHeight_ * 0.4f, breakColor, 12); } char addressText[64]; @@ -401,8 +404,10 @@ void ImDisasmView::Draw(ImDrawList *drawList) { drawList->AddText(ImVec2(rect.left + pixelPositions_.addressStart, rect.top + rowY1 + 2), textColor, addressText); if (isInInterval(address, line.totalSize, debugger->getPC())) { - // emoji??? - // drawList->AddText(ImVec2(pixelPositions.opcodeStart - 8, rowY1), textColor, "\x25A0"); + // Show the current PC with a little square. + drawList->AddRectFilled( + ImVec2(canvas_p0.x + pixelPositions_.opcodeStart - rowHeight_, canvas_p0.y + rowY1 + 2), + ImVec2(canvas_p0.x + pixelPositions_.opcodeStart - 4, canvas_p0.y + rowY1 + rowHeight_ - 2), 0xFFFFFFFF); } // display whether the condition of a branch is met @@ -425,28 +430,49 @@ void ImDisasmView::Draw(ImDrawList *drawList) { } ImGuiIO& io = ImGui::GetIO(); - const bool is_hovered = ImGui::IsItemHovered(); // Hovered - const bool is_active = ImGui::IsItemActive(); // Held ImVec2 mousePos = ImVec2(io.MousePos.x - canvas_p0.x, io.MousePos.y - canvas_p0.y); - if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) { - INFO_LOG(Log::CPU, "Mousedown %f,%f", mousePos.x, mousePos.y); + if (is_hovered && ImGui::IsMouseClicked(ImGuiMouseButton_Left)) { + INFO_LOG(Log::CPU, "Mousedown %f,%f active:%d hover:%d", mousePos.x, mousePos.y, is_active, is_hovered); onMouseDown(mousePos.x, mousePos.y, 1); } if (ImGui::IsMouseReleased(ImGuiMouseButton_Left)) { - INFO_LOG(Log::CPU, "Mouseup %f,%f", mousePos.x, mousePos.y); - onMouseUp(mousePos.x, mousePos.y, 1); + INFO_LOG(Log::CPU, "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::CPU, "Mousedrag %f,%f", mousePos.x, mousePos.y); - onMouseMove(mousePos.x, mousePos.y, 1); + INFO_LOG(Log::CPU, "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_ = manager.getNthPreviousAddress(windowStart_, 4); + } else if (io.MouseWheel < 0.0f) { + windowStart_ = manager.getNthNextAddress(windowStart_, 4); + } + } + if (ImGui::IsKeyPressed(ImGuiKey_PageDown)) { + windowStart_ = manager.getNthNextAddress(windowStart_, visibleRows_); + } + if (ImGui::IsKeyPressed(ImGuiKey_PageUp)) { + windowStart_ = manager.getNthPreviousAddress(windowStart_, visibleRows_); + } + + int coreStep = Core_GetSteppingCounter(); + if (coreStep != lastSteppingCount_) { + // A step has happened since last time. This means that we should re-center the cursor. + lastSteppingCount_ = coreStep; + } + if (pressed) { INFO_LOG(Log::CPU, "Clicked %f,%f", mousePos.x, mousePos.y); - if (mousePos.x < lineHeight) { // Left column + if (mousePos.x < rowHeight_) { // Left column // Toggle breakpoint at dragAddr_. - // debugger->toggleBreakpoint() - - // system->GetBreakpoints(core)->AddExecBreakpoint(dragAddr_); + debugger->toggleBreakpoint(curAddress_); bpPopup_ = true; } else { // disasmView_.selectedAddr_ = dragAddr_; @@ -460,26 +486,13 @@ void ImDisasmView::Draw(ImDrawList *drawList) { drawList->PopClipRect(); } -void ImDisasmView::onVScroll(int amount) { - /* - switch (wParam & 0xFFFF) { - case SB_LINEDOWN: - windowStart_ = manager.getNthNextAddress(windowStart_, 1); - break; - case SB_LINEUP: - windowStart_ = manager.getNthPreviousAddress(windowStart_, 1); - break; - case SB_PAGEDOWN: - windowStart_ = manager.getNthNextAddress(windowStart_, visibleRows_); - break; - case SB_PAGEUP: - windowStart_ = manager.getNthPreviousAddress(windowStart_, visibleRows_); - break; - default: - return; +void ImDisasmView::ScrollRelative(int amount) { + if (amount > 0) { + windowStart_ = manager.getNthNextAddress(windowStart_, amount); + } else if (amount < 0) { + windowStart_ = manager.getNthPreviousAddress(windowStart_, amount); } - */ - scanVisibleFunctions(); + ScanVisibleFunctions(); } void ImDisasmView::followBranch() { @@ -576,11 +589,11 @@ void ImDisasmView::onKeyDown(ImGuiKey key) { break; case VK_UP: scrollWindow(-1); - scanVisibleFunctions(); + ScanVisibleFunctions(); break; case VK_DOWN: scrollWindow(1); - scanVisibleFunctions(); + ScanVisibleFunctions(); break; case VK_NEXT: setCurAddress(manager.getNthPreviousAddress(windowEnd, 1), KeyDownAsync(VK_SHIFT)); @@ -655,7 +668,7 @@ void ImDisasmView::scrollAddressIntoView() { else if (curAddress_ >= windowEnd) windowStart_ = manager.getNthPreviousAddress(curAddress_, visibleRows_ - 1); - scanVisibleFunctions(); + ScanVisibleFunctions(); } bool ImDisasmView::curAddressIsVisible() { @@ -741,7 +754,9 @@ void ImDisasmView::NopInstructions(u32 selectRangeStart, u32 selectRangeEnd) { void ImDisasmView::onMouseUp(int x, int y, int button) { if (button == 1) { - setCurAddress(yToAddress(y), ImGui::IsKeyDown(ImGuiKey_LeftShift)); + if (ImGui::IsKeyDown(ImGuiKey_LeftShift)) { + setCurAddress(yToAddress(y), true); + } } } diff --git a/UI/ImDebugger/ImDisasmView.h b/UI/ImDebugger/ImDisasmView.h index 2279508b8e..8c4f17d8a8 100644 --- a/UI/ImDebugger/ImDisasmView.h +++ b/UI/ImDebugger/ImDisasmView.h @@ -22,15 +22,16 @@ public: void PopupMenu(); + void ScrollRelative(int amount); + void onChar(int c); - void onVScroll(int amount); void onKeyDown(ImGuiKey key); void onMouseDown(int x, int y, int button); void onMouseUp(int x, int y, int button); void onMouseMove(int x, int y, int button); void scrollAddressIntoView(); bool curAddressIsVisible(); - void scanVisibleFunctions(); + void ScanVisibleFunctions(); void clearFunctions() { manager.clear(); }; void getOpcodeText(u32 address, char* dest, int bufsize); @@ -61,7 +62,7 @@ public: } setCurAddress(newAddress); - scanVisibleFunctions(); + ScanVisibleFunctions(); } void gotoPC() { gotoAddr(debugger->getPC()); @@ -69,22 +70,12 @@ public: u32 getSelection() { return curAddress_; } - void setShowMode(bool s) { showHex_ = s; } - void toggleBreakpoint(bool toggleEnabled = false); void editBreakpoint(); - void scrollWindow(int lines) { - if (lines < 0) - windowStart_ = manager.getNthPreviousAddress(windowStart_, abs(lines)); - else - windowStart_ = manager.getNthNextAddress(windowStart_, lines); - scanVisibleFunctions(); - } - void setCurAddress(u32 newAddress, bool extend = false) { newAddress = manager.getStartAddress(newAddress); const u32 after = manager.getNthNextAddress(newAddress, 1); @@ -173,6 +164,7 @@ private: bool mapReloaded_ = false; int positionLocked_ = 0; + int lastSteppingCount_ = 0; std::string statusBarText_; }; diff --git a/Windows/EmuThread.cpp b/Windows/EmuThread.cpp index 28908510cc..7e574c4346 100644 --- a/Windows/EmuThread.cpp +++ b/Windows/EmuThread.cpp @@ -295,7 +295,7 @@ void MainThreadFunc() { } } } else { - while (GetUIState() != UISTATE_EXIT) { + while (GetUIState() != UISTATE_EXIT) { // && GetUIState() != UISTATE_EXCEPTION // We're here again, so the game quit. Restart Core_Run() which controls the UI. // This way they can load a new game. if (!Core_IsActive()) diff --git a/ext/imgui/imgui_impl_platform.cpp b/ext/imgui/imgui_impl_platform.cpp index d6604d0d86..5fa614497e 100644 --- a/ext/imgui/imgui_impl_platform.cpp +++ b/ext/imgui/imgui_impl_platform.cpp @@ -88,6 +88,7 @@ void ImGui_ImplPlatform_NewFrame() { io.DisplaySize = ImVec2(g_display.pixel_xres, g_display.pixel_yres); io.DisplayFramebufferScale = ImVec2(1.0f, 1.0f); io.DeltaTime = now - lastTime; + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; lastTime = now; } From 99eb0bbd648660ec659d8d2d3f18767863f96112 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 7 Nov 2024 10:37:33 +0100 Subject: [PATCH 4/6] Improve register view, fix follow PC, some cleanup --- Core/Debugger/DebugInterface.h | 44 ++++++++++++------------ Core/MIPS/MIPSDebugInterface.h | 6 ++++ UI/ImDebugger/ImDebugger.cpp | 62 ++++++++++++++++++++++++---------- UI/ImDebugger/ImDebugger.h | 7 ---- UI/ImDebugger/ImDisasmView.cpp | 3 ++ UI/ImDebugger/ImDisasmView.h | 4 +++ 6 files changed, 80 insertions(+), 46 deletions(-) diff --git a/Core/Debugger/DebugInterface.h b/Core/Debugger/DebugInterface.h index c8c40f18f4..37f5bc7c12 100644 --- a/Core/Debugger/DebugInterface.h +++ b/Core/Debugger/DebugInterface.h @@ -28,14 +28,14 @@ class DebugInterface { public: virtual int getInstructionSize(int instruction) {return 1;} - virtual bool isAlive() {return true;} - virtual bool isBreakpoint(unsigned int address) {return false;} - virtual void setBreakpoint(unsigned int address){} - virtual void clearBreakpoint(unsigned int address){} - virtual void clearAllBreakpoints() {} - virtual void toggleBreakpoint(unsigned int address){} - virtual unsigned int readMemory(unsigned int address){return 0;} - virtual unsigned int getPC() {return 0;} + virtual bool isAlive() = 0; + virtual bool isBreakpoint(unsigned int address) = 0; + virtual void setBreakpoint(unsigned int address) = 0; + virtual void clearBreakpoint(unsigned int address) = 0; + virtual void clearAllBreakpoints() = 0; + virtual void toggleBreakpoint(unsigned int address) = 0; + virtual unsigned int readMemory(unsigned int address) {return 0;} + virtual unsigned int getPC() = 0; virtual void setPC(unsigned int address) {} virtual void step() {} virtual void runToBreakpoint() {} @@ -44,30 +44,32 @@ public: virtual bool initExpression(const char* exp, PostfixExpression& dest) { return false; }; virtual bool parseExpression(PostfixExpression& exp, u32& dest) { return false; }; - virtual u32 GetHi() { return 0; }; - virtual u32 GetLo() { return 0; }; + virtual u32 GetHi() = 0; + virtual u32 GetLo() = 0; + virtual u32 GetLLBit() = 0; + virtual void SetHi(u32 val) { }; virtual void SetLo(u32 val) { }; virtual const char *GetName() = 0; - virtual u32 GetGPR32Value(int reg) {return 0;} - virtual void SetGPR32Value(int reg) {} + virtual u32 GetGPR32Value(int reg) = 0; + virtual void SetGPR32Value(int reg, u32 value) = 0; virtual float GetFPR32Value(int reg) { return -1.0f; } virtual float GetVPR32Value(int reg) { return -1.0f; } virtual u32 GetPC() = 0; virtual void SetPC(u32 _pc) = 0; - virtual u32 GetLR() {return GetPC();} - virtual void DisAsm(u32 pc, char *out, size_t outSize) { - snprintf(out, outSize, "[%08x] UNKNOWN", pc); - } + virtual u32 GetLR() = 0; + + virtual void DisAsm(u32 pc, char *out, size_t outSize) = 0; + // More stuff for debugger - virtual int GetNumCategories() {return 0;} - virtual int GetNumRegsInCategory(int cat) {return 0;} - virtual const char *GetCategoryName(int cat) {return 0;} - virtual std::string GetRegName(int cat, int index) { return ""; } + virtual int GetNumCategories() = 0; + virtual int GetNumRegsInCategory(int cat) = 0; + virtual const char *GetCategoryName(int cat) = 0; + virtual std::string GetRegName(int cat, int index) = 0; virtual void PrintRegValue(int cat, int index, char *out, size_t outSize) { snprintf(out, outSize, "%08X", GetGPR32Value(index)); } - virtual u32 GetRegValue(int cat, int index) {return 0;} + virtual u32 GetRegValue(int cat, int index) = 0; virtual void SetRegValue(int cat, int index, u32 value) {} }; diff --git a/Core/MIPS/MIPSDebugInterface.h b/Core/MIPS/MIPSDebugInterface.h index 61144c5aba..50d180e3d3 100644 --- a/Core/MIPS/MIPSDebugInterface.h +++ b/Core/MIPS/MIPSDebugInterface.h @@ -49,6 +49,8 @@ public: const char *GetName() override; u32 GetGPR32Value(int reg) override { return cpu->r[reg]; } float GetFPR32Value(int reg) override { return cpu->f[reg]; } + 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]; } void DisAsm(u32 pc, char *out, size_t outSize) override; @@ -77,6 +79,10 @@ public: return cpu->hi; } + u32 GetLLBit() override { + return cpu->llBit; + } + u32 GetLo() override { return cpu->lo; } diff --git a/UI/ImDebugger/ImDebugger.cpp b/UI/ImDebugger/ImDebugger.cpp index 9c4ae46077..4d88aecc7d 100644 --- a/UI/ImDebugger/ImDebugger.cpp +++ b/UI/ImDebugger/ImDebugger.cpp @@ -22,24 +22,54 @@ void DrawRegisterView(MIPSDebugInterface *mipsDebug, bool *open) { if (ImGui::BeginTabBar("RegisterGroups", ImGuiTabBarFlags_None)) { if (ImGui::BeginTabItem("GPR")) { - for (int i = 0; i < 32; i++) { - const int value = mipsDebug->GetGPR32Value(i); - if (value >= -1000000 && value <= 1000000) { - // Display small values in decimal. - ImGui::Text("%s: %08x (%d)", mipsDebug->GetRegName(0, i).c_str(), value, value); - } else { - // But not big ones to reduce clutter. - ImGui::Text("%s: %08x", mipsDebug->GetRegName(0, i).c_str(), value); + if (ImGui::BeginTable("gpr", 3, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH)) { + ImGui::TableSetupColumn("regname", ImGuiTableColumnFlags_WidthFixed); + ImGui::TableSetupColumn("value", ImGuiTableColumnFlags_WidthFixed); + ImGui::TableSetupColumn("value_i", ImGuiTableColumnFlags_WidthStretch); + ImGui::TableNextRow(); + + auto gprLine = [&](const char *regname, int value) { + ImGui::TableSetColumnIndex(0); + ImGui::Text("%s", regname); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%08x", value); + if (value >= -1000000 && value <= 1000000) { + ImGui::TableSetColumnIndex(2); + ImGui::Text("%d", value); + } + ImGui::TableNextRow(); + }; + for (int i = 0; i < 32; i++) { + gprLine(mipsDebug->GetRegName(0, i).c_str(), mipsDebug->GetGPR32Value(i)); } + gprLine("hi", mipsDebug->GetHi()); + gprLine("lo", mipsDebug->GetLo()); + gprLine("pc", mipsDebug->GetPC()); + gprLine("ll", mipsDebug->GetLLBit()); + ImGui::EndTable(); } + ImGui::EndTabItem(); } if (ImGui::BeginTabItem("FPR")) { - for (int i = 0; i < 32; i++) { - float fvalue = mipsDebug->GetFPR32Value(i); - u32 fivalue; - memcpy(&fivalue, &fvalue, sizeof(fivalue)); - ImGui::Text("%s: %0.6f (%08x)", mipsDebug->GetRegName(1, i).c_str(), fvalue, fivalue); + if (ImGui::BeginTable("fpr", 3, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH)) { + ImGui::TableSetupColumn("regname", ImGuiTableColumnFlags_WidthFixed); + ImGui::TableSetupColumn("value", ImGuiTableColumnFlags_WidthFixed); + ImGui::TableSetupColumn("value_i", ImGuiTableColumnFlags_WidthStretch); + ImGui::TableNextRow(); + for (int i = 0; i < 32; i++) { + float fvalue = mipsDebug->GetFPR32Value(i); + u32 fivalue; + memcpy(&fivalue, &fvalue, sizeof(fivalue)); + ImGui::TableSetColumnIndex(0); + ImGui::Text("%s", mipsDebug->GetRegName(1, i).c_str()); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%0.7f", fvalue); + ImGui::TableSetColumnIndex(2); + ImGui::Text("%08x", fivalue); + ImGui::TableNextRow(); + } + ImGui::EndTable(); } ImGui::EndTabItem(); } @@ -102,10 +132,6 @@ void ImDebugger::Frame(MIPSDebugInterface *mipsDebug) { if (regsOpen_) { DrawRegisterView(mipsDebug, ®sOpen_); } - - ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it. - ImGui::Text("This is some useful text."); // Display some text (you can use a format strings too) - ImGui::End(); } void ImDisasmWindow::Draw(MIPSDebugInterface *mipsDebug, bool *open, CoreState coreState) { @@ -155,7 +181,7 @@ void ImDisasmWindow::Draw(MIPSDebugInterface *mipsDebug, bool *open, CoreState c } ImGui::SameLine(); - ImGui::Checkbox("Follow PC", &followPC_); + ImGui::Checkbox("Follow PC", &disasmView_.followPC_); ImGui::SetNextItemWidth(100); if (ImGui::InputScalar("Go to addr: ", ImGuiDataType_U32, &gotoAddr_, NULL, NULL, "%08X", ImGuiInputTextFlags_EnterReturnsTrue)) { diff --git a/UI/ImDebugger/ImDebugger.h b/UI/ImDebugger/ImDebugger.h index 5a1aef1f7e..b0f458a26b 100644 --- a/UI/ImDebugger/ImDebugger.h +++ b/UI/ImDebugger/ImDebugger.h @@ -28,10 +28,6 @@ class ImDisasmWindow { public: void Draw(MIPSDebugInterface *mipsDebug, bool *open, CoreState coreState); - void PCChanged() { - pcChanged_ = true; - } - private: // We just keep the state directly in the window. Can refactor later. @@ -41,9 +37,6 @@ private: u32 gotoAddr_ = 0x1000; - bool followPC_ = true; - bool pcChanged_ = false; - ImDisasmView disasmView_; }; diff --git a/UI/ImDebugger/ImDisasmView.cpp b/UI/ImDebugger/ImDisasmView.cpp index c248df47ae..a573c1e5e0 100644 --- a/UI/ImDebugger/ImDisasmView.cpp +++ b/UI/ImDebugger/ImDisasmView.cpp @@ -465,6 +465,9 @@ void ImDisasmView::Draw(ImDrawList *drawList) { 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; } diff --git a/UI/ImDebugger/ImDisasmView.h b/UI/ImDebugger/ImDisasmView.h index 8c4f17d8a8..7cbc232129 100644 --- a/UI/ImDebugger/ImDisasmView.h +++ b/UI/ImDebugger/ImDisasmView.h @@ -18,6 +18,10 @@ public: ImDisasmView(); ~ImDisasmView(); + // Public variables bounds to imgui checkboxes + bool followPC_ = true; + + void Draw(ImDrawList *drawList); void PopupMenu(); From dd26bcf1af3118265c9de284b8e074a5730d5d19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 7 Nov 2024 11:06:10 +0100 Subject: [PATCH 5/6] Cache symbols and use a clipper to speed up the symbol list. --- Core/Debugger/SymbolMap.h | 2 +- UI/ImDebugger/ImDebugger.cpp | 18 +++++++++++++----- UI/ImDebugger/ImDebugger.h | 3 +++ 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/Core/Debugger/SymbolMap.h b/Core/Debugger/SymbolMap.h index b3e1529779..d888539027 100644 --- a/Core/Debugger/SymbolMap.h +++ b/Core/Debugger/SymbolMap.h @@ -21,7 +21,6 @@ #include #include #include -#include #include #include "Common/CommonTypes.h" @@ -163,6 +162,7 @@ private: // This is indexed by the end address of the module. std::map activeModuleEnds; + // Module ID, index typedef std::pair SymbolKey; // These are indexed by the module id and relative address in the module. diff --git a/UI/ImDebugger/ImDebugger.cpp b/UI/ImDebugger/ImDebugger.cpp index 4d88aecc7d..03d42cc467 100644 --- a/UI/ImDebugger/ImDebugger.cpp +++ b/UI/ImDebugger/ImDebugger.cpp @@ -196,13 +196,21 @@ void ImDisasmWindow::Draw(MIPSDebugInterface *mipsDebug, bool *open, CoreState c ImGui::TableSetColumnIndex(0); ImVec2 sz = ImGui::GetContentRegionAvail(); if (ImGui::BeginListBox("##symbols", ImVec2(150.0, sz.y - ImGui::GetTextLineHeightWithSpacing() * 2))) { - std::vector syms = g_symbolMap->GetAllSymbols(SymbolType::ST_FUNCTION); - for (auto &sym : syms) { - if (ImGui::Selectable(sym.name.c_str(), false)) { - disasmView_.setCurAddress(sym.address); - disasmView_.scrollAddressIntoView(); + if (symCache_.empty()) { + symCache_ = g_symbolMap->GetAllSymbols(SymbolType::ST_FUNCTION); + } + ImGuiListClipper clipper; + clipper.Begin((int)symCache_.size(), -1); + while (clipper.Step()) { + for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) { + if (ImGui::Selectable(symCache_[i].name.c_str(), false)) { + disasmView_.setCurAddress(symCache_[i].address); + disasmView_.scrollAddressIntoView(); + } } } + + clipper.End(); ImGui::EndListBox(); } diff --git a/UI/ImDebugger/ImDebugger.h b/UI/ImDebugger/ImDebugger.h index b0f458a26b..574aad01ae 100644 --- a/UI/ImDebugger/ImDebugger.h +++ b/UI/ImDebugger/ImDebugger.h @@ -37,6 +37,9 @@ private: u32 gotoAddr_ = 0x1000; + // Symbol cache + std::vector symCache_; + ImDisasmView disasmView_; }; From 0535f5ebc1acade2a53035112bc3f9ac26a2bc12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 7 Nov 2024 11:27:42 +0100 Subject: [PATCH 6/6] Fix black background in old debugger --- UI/ImDebugger/ImDebugger.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/UI/ImDebugger/ImDebugger.cpp b/UI/ImDebugger/ImDebugger.cpp index 03d42cc467..1bbda24935 100644 --- a/UI/ImDebugger/ImDebugger.cpp +++ b/UI/ImDebugger/ImDebugger.cpp @@ -141,11 +141,21 @@ void ImDisasmWindow::Draw(MIPSDebugInterface *mipsDebug, bool *open, CoreState c disasmView_.setDebugger(mipsDebug); ImGui::SetNextWindowSize(ImVec2(520, 600), ImGuiCond_FirstUseEver); - if (!ImGui::Begin(title, open)) { + if (!ImGui::Begin(title, open, ImGuiWindowFlags_NoNavInputs)) { ImGui::End(); return; } + if (ImGui::IsWindowFocused()) { + // Process stepping keyboard shortcuts. + if (ImGui::IsKeyPressed(ImGuiKey_F10)) { + Core_RequestSingleStep(CPUStepType::Over, 0); + } + if (ImGui::IsKeyPressed(ImGuiKey_F11)) { + Core_RequestSingleStep(CPUStepType::Into, 0); + } + } + ImGui::BeginDisabled(coreState != CORE_STEPPING); if (ImGui::SmallButton("Run")) { Core_Resume();