diff --git a/pcsx2-qt/Debugger/DebuggerWindow.cpp b/pcsx2-qt/Debugger/DebuggerWindow.cpp index 8a8e587ead..4ab710d902 100644 --- a/pcsx2-qt/Debugger/DebuggerWindow.cpp +++ b/pcsx2-qt/Debugger/DebuggerWindow.cpp @@ -7,8 +7,9 @@ #include "DebugTools/DebugInterface.h" #include "DebugTools/Breakpoints.h" +#include "DebugTools/MIPSAnalyst.h" +#include "DebugTools/MipsStackWalk.h" #include "DebugTools/SymbolImporter.h" -#include "VMManager.h" #include "QtHost.h" #include "MainWindow.h" #include "AnalysisOptionsDialog.h" @@ -111,14 +112,6 @@ void DebuggerWindow::clearToolBarState() restoreState(m_default_toolbar_state); } -// There is no straightforward way to set the tab text to bold in Qt -// Sorry colour blind people, but this is the best we can do for now -void DebuggerWindow::setTabActiveStyle(BreakPointCpu enabledCpu) -{ - //m_ui.cpuTabs->tabBar()->setTabTextColor(m_ui.cpuTabs->indexOf(m_cpuWidget_r5900), (enabledCpu == BREAKPOINT_EE) ? Qt::red : this->palette().text().color()); - //m_ui.cpuTabs->tabBar()->setTabTextColor(m_ui.cpuTabs->indexOf(m_cpuWidget_r3000), (enabledCpu == BREAKPOINT_IOP) ? Qt::red : this->palette().text().color()); -} - void DebuggerWindow::onVMStateChanged() { if (!QtHost::IsVMPaused()) @@ -128,7 +121,6 @@ void DebuggerWindow::onVMStateChanged() m_ui.actionStepInto->setEnabled(false); m_ui.actionStepOver->setEnabled(false); m_ui.actionStepOut->setEnabled(false); - setTabActiveStyle(BREAKPOINT_IOP_AND_EE); } else { @@ -142,18 +134,7 @@ void DebuggerWindow::onVMStateChanged() if (CBreakPoints::GetBreakpointTriggered()) { const BreakPointCpu triggeredCpu = CBreakPoints::GetBreakpointTriggeredCpu(); - setTabActiveStyle(triggeredCpu); - switch (triggeredCpu) - { - case BREAKPOINT_EE: - //m_ui.cpuTabs->setCurrentWidget(m_cpuWidget_r5900); - break; - case BREAKPOINT_IOP: - //m_ui.cpuTabs->setCurrentWidget(m_cpuWidget_r3000); - break; - default: - break; - } + m_dock_manager->switchToLayoutWithCPU(triggeredCpu); Host::RunOnCPUThread([] { CBreakPoints::ClearTemporaryBreakPoints(); CBreakPoints::SetBreakpointTriggered(false, BREAKPOINT_IOP_AND_EE); @@ -174,20 +155,140 @@ void DebuggerWindow::onRunPause() void DebuggerWindow::onStepInto() { - //CpuWidget* currentCpu = static_cast(m_ui.cpuTabs->currentWidget()); - //currentCpu->onStepInto(); + DebugInterface* cpu = currentCPU(); + if (!cpu) + return; + + if (!cpu->isAlive() || !cpu->isCpuPaused()) + return; + + // Allow the cpu to skip this pc if it is a breakpoint + CBreakPoints::SetSkipFirst(cpu->getCpuType(), cpu->getPC()); + + const u32 pc = cpu->getPC(); + const MIPSAnalyst::MipsOpcodeInfo info = MIPSAnalyst::GetOpcodeInfo(cpu, pc); + + u32 bpAddr = pc + 0x4; // Default to the next instruction + + if (info.isBranch) + { + if (!info.isConditional) + { + bpAddr = info.branchTarget; + } + else + { + if (info.conditionMet) + { + bpAddr = info.branchTarget; + } + else + { + bpAddr = pc + (2 * 4); // Skip branch delay slot + } + } + } + + if (info.isSyscall) + bpAddr = info.branchTarget; // Syscalls are always taken + + Host::RunOnCPUThread([cpu, bpAddr] { + CBreakPoints::AddBreakPoint(cpu->getCpuType(), bpAddr, true); + cpu->resumeCpu(); + }); + + repaint(); } void DebuggerWindow::onStepOver() { - //CpuWidget* currentCpu = static_cast(m_ui.cpuTabs->currentWidget()); - //currentCpu->onStepOver(); + DebugInterface* cpu = currentCPU(); + if (!cpu) + return; + + if (!cpu->isAlive() || !cpu->isCpuPaused()) + return; + + const u32 pc = cpu->getPC(); + const MIPSAnalyst::MipsOpcodeInfo info = MIPSAnalyst::GetOpcodeInfo(cpu, pc); + + u32 bpAddr = pc + 0x4; // Default to the next instruction + + if (info.isBranch) + { + if (!info.isConditional) + { + if (info.isLinkedBranch) // jal, jalr + { + // it's a function call with a delay slot - skip that too + bpAddr += 4; + } + else // j, ... + { + // in case of absolute branches, set the breakpoint at the branch target + bpAddr = info.branchTarget; + } + } + else // beq, ... + { + if (info.conditionMet) + { + bpAddr = info.branchTarget; + } + else + { + bpAddr = pc + (2 * 4); // Skip branch delay slot + } + } + } + + Host::RunOnCPUThread([cpu, bpAddr] { + CBreakPoints::AddBreakPoint(cpu->getCpuType(), bpAddr, true); + cpu->resumeCpu(); + }); + + this->repaint(); } void DebuggerWindow::onStepOut() { - //CpuWidget* currentCpu = static_cast(m_ui.cpuTabs->currentWidget()); - //currentCpu->onStepOut(); + DebugInterface* cpu = currentCPU(); + if (!cpu) + return; + + if (!cpu->isAlive() || !cpu->isCpuPaused()) + return; + + // Allow the cpu to skip this pc if it is a breakpoint + CBreakPoints::SetSkipFirst(cpu->getCpuType(), cpu->getPC()); + + std::vector stack_frames; + for (const auto& thread : cpu->GetThreadList()) + { + if (thread->Status() == ThreadStatus::THS_RUN) + { + stack_frames = MipsStackWalk::Walk( + cpu, + cpu->getPC(), + cpu->getRegister(0, 31), + cpu->getRegister(0, 29), + thread->EntryPoint(), + thread->StackTop()); + break; + } + } + + if (stack_frames.size() < 2) + return; + + u32 breakpoint_pc = stack_frames.at(1).pc; + + Host::RunOnCPUThread([cpu, breakpoint_pc] { + CBreakPoints::AddBreakPoint(cpu->getCpuType(), breakpoint_pc, true); + cpu->resumeCpu(); + }); + + this->repaint(); } void DebuggerWindow::onAnalyse() @@ -210,6 +311,15 @@ void DebuggerWindow::closeEvent(QCloseEvent* event) deleteLater(); } +DebugInterface* DebuggerWindow::currentCPU() +{ + std::optional maybe_cpu = m_dock_manager->cpu(); + if (!maybe_cpu.has_value()) + return nullptr; + + return &DebugInterface::get(*maybe_cpu); +} + void DebuggerWindow::setupDefaultToolBarState() { // Hiding all the toolbars lets us save the default state of the window with diff --git a/pcsx2-qt/Debugger/DebuggerWindow.h b/pcsx2-qt/Debugger/DebuggerWindow.h index b90b629076..425bb2fc24 100644 --- a/pcsx2-qt/Debugger/DebuggerWindow.h +++ b/pcsx2-qt/Debugger/DebuggerWindow.h @@ -38,6 +38,8 @@ protected: void closeEvent(QCloseEvent* event); private: + DebugInterface* currentCPU(); + void setupDefaultToolBarState(); Ui::DebuggerWindow m_ui; @@ -49,8 +51,6 @@ private: DockManager* m_dock_manager; QByteArray m_default_toolbar_state; - - void setTabActiveStyle(BreakPointCpu toggledCPU); }; extern DebuggerWindow* g_debugger_window; diff --git a/pcsx2-qt/Debugger/Docking/DockManager.cpp b/pcsx2-qt/Debugger/Docking/DockManager.cpp index 96c363a7fe..75f8481f6a 100644 --- a/pcsx2-qt/Debugger/Docking/DockManager.cpp +++ b/pcsx2-qt/Debugger/Docking/DockManager.cpp @@ -118,6 +118,20 @@ void DockManager::switchToLayout(DockLayout::Index layout_index) } } +bool DockManager::switchToLayoutWithCPU(BreakPointCpu cpu) +{ + for (DockLayout::Index i = 0; i < m_layouts.size(); i++) + { + if (m_layouts[i].cpu() == cpu) + { + switchToLayout(i); + return true; + } + } + + return false; +} + void DockManager::loadLayouts() { m_layouts.clear(); @@ -601,6 +615,14 @@ void DockManager::updateToolBarLockState() toolbar->setMovable(!m_layout_locked || toolbar->isFloating()); } +std::optional DockManager::cpu() +{ + if (m_current_layout == DockLayout::INVALID_INDEX) + return std::nullopt; + + return m_layouts.at(m_current_layout).cpu(); +} + KDDockWidgets::Core::DockWidget* DockManager::dockWidgetFactory(const QString& name) { if (!g_debugger_window) diff --git a/pcsx2-qt/Debugger/Docking/DockManager.h b/pcsx2-qt/Debugger/Docking/DockManager.h index fb921625e5..a82ff4f1da 100644 --- a/pcsx2-qt/Debugger/Docking/DockManager.h +++ b/pcsx2-qt/Debugger/Docking/DockManager.h @@ -51,6 +51,7 @@ public: bool deleteLayout(DockLayout::Index layout_index); void switchToLayout(DockLayout::Index layout_index); + bool switchToLayoutWithCPU(BreakPointCpu cpu); void loadLayouts(); bool saveLayouts(); @@ -79,6 +80,8 @@ public: void setLayoutLocked(bool locked); void updateToolBarLockState(); + std::optional cpu(); + private: static KDDockWidgets::Core::DockWidget* dockWidgetFactory(const QString& name); static bool dragAboutToStart(KDDockWidgets::Core::Draggable* draggable);