From 477522210e5152f7cfdbaa3f64d773fe0be10383 Mon Sep 17 00:00:00 2001 From: Robbie Date: Fri, 2 Mar 2018 15:40:29 -0600 Subject: [PATCH] Refactor debugger_frame into subclasses. Mostly trying to simplify breakpoints. --- rpcs3/Emu/Cell/PPUThread.cpp | 14 +- rpcs3/rpcs3.vcxproj | 105 +++++ rpcs3/rpcs3.vcxproj.filters | 42 ++ rpcs3/rpcs3qt/auto_pause_settings_dialog.h | 2 +- rpcs3/rpcs3qt/breakpoint_handler.cpp | 32 ++ rpcs3/rpcs3qt/breakpoint_handler.h | 44 ++ rpcs3/rpcs3qt/breakpoint_list.cpp | 151 +++++++ rpcs3/rpcs3qt/breakpoint_list.h | 36 ++ rpcs3/rpcs3qt/cg_disasm_window.h | 8 +- rpcs3/rpcs3qt/debugger_frame.cpp | 451 +++++---------------- rpcs3/rpcs3qt/debugger_frame.h | 73 +--- rpcs3/rpcs3qt/debugger_list.cpp | 183 +++++++++ rpcs3/rpcs3qt/debugger_list.h | 48 +++ rpcs3/rpcs3qt/game_list.h | 2 +- rpcs3/rpcs3qt/game_list_grid_delegate.h | 4 +- rpcs3/rpcs3qt/gs_frame.h | 2 +- rpcs3/rpcs3qt/msg_dialog_frame.h | 4 +- rpcs3/rpcs3qt/table_item_delegate.h | 2 +- rpcs3/rpcs3qt/trophy_tree_widget_item.h | 3 +- 19 files changed, 780 insertions(+), 426 deletions(-) create mode 100644 rpcs3/rpcs3qt/breakpoint_handler.cpp create mode 100644 rpcs3/rpcs3qt/breakpoint_handler.h create mode 100644 rpcs3/rpcs3qt/breakpoint_list.cpp create mode 100644 rpcs3/rpcs3qt/breakpoint_list.h create mode 100644 rpcs3/rpcs3qt/debugger_list.cpp create mode 100644 rpcs3/rpcs3qt/debugger_list.h diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index afdb55df14..db10680317 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -313,7 +313,7 @@ static bool ppu_break(ppu_thread& ppu, ppu_opcode_t op) } // Set or remove breakpoint -extern void ppu_breakpoint(u32 addr) +extern void ppu_breakpoint(u32 addr, bool isAdding) { if (g_cfg.core.ppu_decoder == ppu_decoder_type::llvm) { @@ -322,16 +322,16 @@ extern void ppu_breakpoint(u32 addr) const auto _break = ::narrow(reinterpret_cast(&ppu_break)); - if (ppu_ref(addr) == _break) - { - // Remove breakpoint - ppu_ref(addr) = ppu_cache(addr); - } - else + if (isAdding) { // Set breakpoint ppu_ref(addr) = _break; } + else + { + // Remove breakpoint + ppu_ref(addr) = ppu_cache(addr); + } } void ppu_thread::on_spawn() diff --git a/rpcs3/rpcs3.vcxproj b/rpcs3/rpcs3.vcxproj index f8c5be0681..2523f86638 100644 --- a/rpcs3/rpcs3.vcxproj +++ b/rpcs3/rpcs3.vcxproj @@ -361,6 +361,11 @@ true true + + true + true + true + true true @@ -371,6 +376,11 @@ true true + + true + true + true + true true @@ -506,6 +516,11 @@ true true + + true + true + true + true true @@ -516,6 +531,11 @@ true true + + true + true + true + true true @@ -661,6 +681,11 @@ true true + + true + true + true + true true @@ -671,6 +696,11 @@ true true + + true + true + true + true true @@ -806,6 +836,11 @@ true true + + true + true + true + true true @@ -816,6 +851,11 @@ true true + + true + true + true + true true @@ -932,6 +972,9 @@ true + + + @@ -1308,7 +1351,69 @@ .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_OPENGL_LIB -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_QML_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -DQT_WINEXTRAS_LIB -DLLVM_AVAILABLE -D_SCL_SECURE_NO_WARNINGS -DWITH_DISCORD_RPC -D_UNICODE "-I.\..\Vulkan\Vulkan-LoaderAndValidationLayers\include" "-I.\.." "-I.\..\3rdparty\minidx12\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtOpenGL" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtQml" "-I$(QTDIR)\include\QtNetwork" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)\." "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" + + + + + + + + + + + + + + + + + + + + + + + + + + + + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing %(Identity)... + .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_NO_DEBUG -DQT_OPENGL_LIB -DQT_WIDGETS_LIB -DQT_QUICK_LIB -DQT_GUI_LIB -DQT_QML_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -DBRANCH= -DLLVM_AVAILABLE -D_UNICODE "-I.\..\Vulkan\Vulkan-LoaderAndValidationLayers\include" "-I.\..\3rdparty\minidx12\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtOpenGL" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtQuick" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtQml" "-I$(QTDIR)\include\QtNetwork" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)\." "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing %(Identity)... + .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_OPENGL_LIB -DQT_WIDGETS_LIB -DQT_QUICK_LIB -DQT_GUI_LIB -DQT_QML_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -DQT_WINEXTRAS_LIB -D_SCL_SECURE_NO_WARNINGS -D_UNICODE "-I.\..\Vulkan\Vulkan-LoaderAndValidationLayers\include" "-I.\.." "-I.\..\3rdparty\minidx12\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtOpenGL" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtQuick" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtQml" "-I$(QTDIR)\include\QtNetwork" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)\." "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing %(Identity)... + .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_NO_DEBUG -DQT_OPENGL_LIB -DQT_WIDGETS_LIB -DQT_QUICK_LIB -DQT_GUI_LIB -DQT_QML_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -D_UNICODE "-I.\..\Vulkan\Vulkan-LoaderAndValidationLayers\include" "-I.\..\3rdparty\minidx12\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtOpenGL" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtQuick" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtQml" "-I$(QTDIR)\include\QtNetwork" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)\." "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing %(Identity)... + .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_OPENGL_LIB -DQT_WIDGETS_LIB -DQT_QUICK_LIB -DQT_GUI_LIB -DQT_QML_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -DQT_WINEXTRAS_LIB -DLLVM_AVAILABLE -D_SCL_SECURE_NO_WARNINGS -D_UNICODE "-I.\..\Vulkan\Vulkan-LoaderAndValidationLayers\include" "-I.\.." "-I.\..\3rdparty\minidx12\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtOpenGL" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtQuick" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtQml" "-I$(QTDIR)\include\QtNetwork" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)\." "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" + + + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing %(Identity)... + .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_NO_DEBUG -DQT_OPENGL_LIB -DQT_WIDGETS_LIB -DQT_QUICK_LIB -DQT_GUI_LIB -DQT_QML_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -DBRANCH= -DLLVM_AVAILABLE -D_UNICODE "-I.\..\Vulkan\Vulkan-LoaderAndValidationLayers\include" "-I.\..\3rdparty\minidx12\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtOpenGL" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtQuick" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtQml" "-I$(QTDIR)\include\QtNetwork" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)\." "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing %(Identity)... + .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_OPENGL_LIB -DQT_WIDGETS_LIB -DQT_QUICK_LIB -DQT_GUI_LIB -DQT_QML_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -DQT_WINEXTRAS_LIB -D_SCL_SECURE_NO_WARNINGS -D_UNICODE "-I.\..\Vulkan\Vulkan-LoaderAndValidationLayers\include" "-I.\.." "-I.\..\3rdparty\minidx12\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtOpenGL" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtQuick" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtQml" "-I$(QTDIR)\include\QtNetwork" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)\." "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing %(Identity)... + .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_NO_DEBUG -DQT_OPENGL_LIB -DQT_WIDGETS_LIB -DQT_QUICK_LIB -DQT_GUI_LIB -DQT_QML_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -D_UNICODE "-I.\..\Vulkan\Vulkan-LoaderAndValidationLayers\include" "-I.\..\3rdparty\minidx12\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtOpenGL" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtQuick" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtQml" "-I$(QTDIR)\include\QtNetwork" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)\." "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing %(Identity)... + .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_OPENGL_LIB -DQT_WIDGETS_LIB -DQT_QUICK_LIB -DQT_GUI_LIB -DQT_QML_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -DQT_WINEXTRAS_LIB -DLLVM_AVAILABLE -D_SCL_SECURE_NO_WARNINGS -D_UNICODE "-I.\..\Vulkan\Vulkan-LoaderAndValidationLayers\include" "-I.\.." "-I.\..\3rdparty\minidx12\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtOpenGL" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtQuick" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtQml" "-I$(QTDIR)\include\QtNetwork" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)\." "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" + diff --git a/rpcs3/rpcs3.vcxproj.filters b/rpcs3/rpcs3.vcxproj.filters index 147c1b3106..df4bc743ce 100644 --- a/rpcs3/rpcs3.vcxproj.filters +++ b/rpcs3/rpcs3.vcxproj.filters @@ -605,6 +605,39 @@ Generated Files\Debug - LLVM + + Gui\debugger + + + Gui\debugger + + + Generated Files\Release - LLVM + + + Generated Files\Debug + + + Generated Files\Release + + + Generated Files\Debug - LLVM + + + Gui\debugger + + + Generated Files\Release - LLVM + + + Generated Files\Debug + + + Generated Files\Release + + + Generated Files\Debug - LLVM + @@ -813,6 +846,15 @@ Gui\debugger + + Gui\debugger + + + Gui\debugger + + + Gui\debugger + diff --git a/rpcs3/rpcs3qt/auto_pause_settings_dialog.h b/rpcs3/rpcs3qt/auto_pause_settings_dialog.h index b3a2e394d1..62afb65769 100644 --- a/rpcs3/rpcs3qt/auto_pause_settings_dialog.h +++ b/rpcs3/rpcs3qt/auto_pause_settings_dialog.h @@ -43,7 +43,7 @@ public Q_SLOTS: void OnRemove(); private Q_SLOTS: void ShowContextMenu(const QPoint &pos); - void keyPressEvent(QKeyEvent *event); + void keyPressEvent(QKeyEvent *event) override; }; class AutoPauseConfigDialog : public QDialog diff --git a/rpcs3/rpcs3qt/breakpoint_handler.cpp b/rpcs3/rpcs3qt/breakpoint_handler.cpp new file mode 100644 index 0000000000..ad553891ae --- /dev/null +++ b/rpcs3/rpcs3qt/breakpoint_handler.cpp @@ -0,0 +1,32 @@ +#include "breakpoint_handler.h" + +extern void ppu_breakpoint(u32 loc, bool isAdding); + +breakpoint_handler::breakpoint_handler() :m_breakpoints() +{ + +} + +breakpoint_handler::~breakpoint_handler() +{ + +} + +bool breakpoint_handler::HasBreakpoint(u32 loc) const +{ + return m_breakpoints.find(loc) != m_breakpoints.end(); +} + +bool breakpoint_handler::AddBreakpoint(u32 loc) +{ + m_breakpoints.insert(loc); + ppu_breakpoint(loc, true); + return true; +} + +bool breakpoint_handler::RemoveBreakpoint(u32 loc) +{ + m_breakpoints.erase(loc); + ppu_breakpoint(loc, false); + return true; +} diff --git a/rpcs3/rpcs3qt/breakpoint_handler.h b/rpcs3/rpcs3qt/breakpoint_handler.h new file mode 100644 index 0000000000..351c874ce4 --- /dev/null +++ b/rpcs3/rpcs3qt/breakpoint_handler.h @@ -0,0 +1,44 @@ +#pragma once +#include "stdafx.h" +#include "Emu/Cell/PPUThread.h" +#include + +enum class breakpoint_types +{ + bp_read = 0x1, + bp_write = 0x2, + bp_exec = 0x4, +}; + +/* +* This class acts as a layer between the UI and Emu for breakpoints. +*/ +class breakpoint_handler +{ + +public: + breakpoint_handler(); + ~breakpoint_handler(); + + /** + * Returns true iff breakpoint exists at loc. + * TODO: Add arg for flags, gameid, and maybe even thread if it should be thread local breakpoint.... breakpoint struct is probably what'll happen + */ + bool HasBreakpoint(u32 loc) const; + + /** + * Returns true if added successfully. TODO: flags + */ + bool AddBreakpoint(u32 loc); + + + /** + * Returns true if removed breakpoint at loc successfully. + */ + bool RemoveBreakpoint(u32 loc); + +private: + // TODO : generalize to hold multiple games and handle flags.Probably do : std::map>. + // Although, externally, they'll only be accessed by loc (I think) so a map of maps may also do? + std::set m_breakpoints; //! Holds all breakpoints. +}; diff --git a/rpcs3/rpcs3qt/breakpoint_list.cpp b/rpcs3/rpcs3qt/breakpoint_list.cpp new file mode 100644 index 0000000000..e76059f39f --- /dev/null +++ b/rpcs3/rpcs3qt/breakpoint_list.cpp @@ -0,0 +1,151 @@ +#include "breakpoint_list.h" + +#include "Emu/Cell/SPUThread.h" + +#include + +constexpr auto qstr = QString::fromStdString; + +breakpoint_list::breakpoint_list(QWidget* parent, breakpoint_handler* handler) : QListWidget(parent), m_breakpoint_handler(handler) +{ + setEditTriggers(QAbstractItemView::NoEditTriggers); + setContextMenuPolicy(Qt::CustomContextMenu); + setSelectionMode(QAbstractItemView::ExtendedSelection); + + // connects + connect(this, &QListWidget::itemDoubleClicked, this, &breakpoint_list::OnBreakpointListDoubleClicked); + connect(this, &QListWidget::customContextMenuRequested, this, &breakpoint_list::OnBreakpointListRightClicked); +} + +/** +* It's unfortunate I need a method like this to sync these. Should ponder a cleaner way to do this. +*/ +void breakpoint_list::UpdateCPUData(std::weak_ptr cpu, std::shared_ptr disasm) +{ + this->cpu = cpu; + m_disasm = disasm; +} + +void breakpoint_list::ClearBreakpoints() +{ + while (count()) + { + auto* currentItem = takeItem(0); + u32 loc = currentItem->data(Qt::UserRole).value(); + m_breakpoint_handler->RemoveBreakpoint(loc); + delete currentItem; + } +} + +void breakpoint_list::RemoveBreakpoint(u32 addr) +{ + m_breakpoint_handler->RemoveBreakpoint(addr); + + for (int i = 0; i < count(); i++) + { + QListWidgetItem* currentItem = item(i); + + if (currentItem->data(Qt::UserRole).value() == addr) + { + delete takeItem(i); + break; + } + } + + Q_EMIT RequestShowAddress(addr); +} + +void breakpoint_list::AddBreakpoint(u32 pc) +{ + m_breakpoint_handler->AddBreakpoint(pc); + + const auto cpu = this->cpu.lock(); + const u32 cpu_offset = cpu->id_type() != 1 ? static_cast(*cpu).offset : 0; + m_disasm->offset = (u8*)vm::base(cpu_offset); + + m_disasm->disasm(m_disasm->dump_pc = pc); + + QString breakpointItemText = qstr(m_disasm->last_opcode); + + breakpointItemText.remove(10, 13); + + QListWidgetItem* breakpointItem = new QListWidgetItem(breakpointItemText); + breakpointItem->setTextColor(m_text_color_bp); + breakpointItem->setBackgroundColor(m_color_bp); + QVariant pcVariant; + pcVariant.setValue(pc); + breakpointItem->setData(Qt::UserRole, pcVariant); + addItem(breakpointItem); +} + +/** +* If breakpoint exists, we remove it, else add new one. Yeah, it'd be nicer from a code logic to have it be set/reset. But, that logic has to happen somewhere anyhow. +*/ +void breakpoint_list::HandleBreakpointRequest(u32 loc) +{ + if (m_breakpoint_handler->HasBreakpoint(loc)) + { + RemoveBreakpoint(loc); + } + else + { + const auto cpu = this->cpu.lock(); + + if (cpu->id_type() == 1 && vm::check_addr(loc)) + { + AddBreakpoint(loc); + } + } +} + +void breakpoint_list::OnBreakpointListDoubleClicked() +{ + u32 address = currentItem()->data(Qt::UserRole).value(); + Q_EMIT RequestShowAddress(address); +} + +void breakpoint_list::OnBreakpointListRightClicked(const QPoint &pos) +{ + if (!itemAt(pos)) + { + return; + } + + QMenu* menu = new QMenu(); + + if (selectedItems().count() == 1) + { + menu->addAction("Rename"); + menu->addSeparator(); + } + + QAction* m_breakpoint_list_delete = new QAction("Delete", this); + m_breakpoint_list_delete->setShortcut(Qt::Key_Delete); + m_breakpoint_list_delete->setShortcutContext(Qt::WidgetShortcut); + addAction(m_breakpoint_list_delete); + connect(m_breakpoint_list_delete, &QAction::triggered, this, &breakpoint_list::OnBreakpointListDelete); + + menu->addAction(m_breakpoint_list_delete); + + QAction* selectedItem = menu->exec(QCursor::pos()); + if (selectedItem) + { + if (selectedItem->text() == "Rename") + { + QListWidgetItem* currentItem = selectedItems().at(0); + + currentItem->setFlags(currentItem->flags() | Qt::ItemIsEditable); + editItem(currentItem); + } + } +} + +void breakpoint_list::OnBreakpointListDelete() +{ + int selectedCount = selectedItems().count(); + + for (int i = selectedCount - 1; i >= 0; i--) + { + RemoveBreakpoint(item(i)->data(Qt::UserRole).value()); + } +} diff --git a/rpcs3/rpcs3qt/breakpoint_list.h b/rpcs3/rpcs3qt/breakpoint_list.h new file mode 100644 index 0000000000..4d607a6287 --- /dev/null +++ b/rpcs3/rpcs3qt/breakpoint_list.h @@ -0,0 +1,36 @@ +#pragma once + +#include "stdafx.h" +#include "Emu/CPU/CPUDisAsm.h" + +#include "breakpoint_handler.h" + +#include + +class breakpoint_list : public QListWidget +{ + Q_OBJECT + +public: + breakpoint_list(QWidget* parent, breakpoint_handler* handler); + void UpdateCPUData(std::weak_ptr cpu, std::shared_ptr disasm); + void ClearBreakpoints(); + void AddBreakpoint(u32 addr); + void RemoveBreakpoint(u32 addr); + + QColor m_text_color_bp; + QColor m_color_bp; +Q_SIGNALS: + void RequestShowAddress(u32 addr); +public Q_SLOTS: + void HandleBreakpointRequest(u32 addr); +private Q_SLOTS: + void OnBreakpointListDoubleClicked(); + void OnBreakpointListRightClicked(const QPoint &pos); + void OnBreakpointListDelete(); +private: + breakpoint_handler* m_breakpoint_handler; + + std::weak_ptr cpu; + std::shared_ptr m_disasm; +}; diff --git a/rpcs3/rpcs3qt/cg_disasm_window.h b/rpcs3/rpcs3qt/cg_disasm_window.h index 1bd34ab457..e8f8a3672f 100644 --- a/rpcs3/rpcs3qt/cg_disasm_window.h +++ b/rpcs3/rpcs3qt/cg_disasm_window.h @@ -29,8 +29,8 @@ public: explicit cg_disasm_window(std::shared_ptr xSettings); protected: - void dropEvent(QDropEvent* ev); - void dragEnterEvent(QDragEnterEvent* ev); - void dragMoveEvent(QDragMoveEvent* ev); - void dragLeaveEvent(QDragLeaveEvent* ev); + void dropEvent(QDropEvent* ev) override; + void dragEnterEvent(QDragEnterEvent* ev) override; + void dragMoveEvent(QDragMoveEvent* ev) override; + void dragLeaveEvent(QDragLeaveEvent* ev) override; }; diff --git a/rpcs3/rpcs3qt/debugger_frame.cpp b/rpcs3/rpcs3qt/debugger_frame.cpp index 9869d0ba9a..21f1b54adf 100644 --- a/rpcs3/rpcs3qt/debugger_frame.cpp +++ b/rpcs3/rpcs3qt/debugger_frame.cpp @@ -1,6 +1,7 @@ #include "debugger_frame.h" #include "qt_utils.h" +#include #include #include #include @@ -24,11 +25,13 @@ debugger_frame::debugger_frame(std::shared_ptr settings, QWidget * QVBoxLayout* vbox_p_main = new QVBoxLayout(); QHBoxLayout* hbox_b_main = new QHBoxLayout(); - m_list = new debugger_list(this, settings); - m_breakpoints_list = new QListWidget(this); - m_breakpoints_list->setEditTriggers(QAbstractItemView::NoEditTriggers); - m_breakpoints_list->setContextMenuPolicy(Qt::CustomContextMenu); - m_breakpoints_list->setSelectionMode(QAbstractItemView::ExtendedSelection); + + m_breakpoint_handler = new breakpoint_handler(); + m_debugger_list = new debugger_list(this, settings, m_breakpoint_handler); + m_debugger_list->installEventFilter(this); + + m_breakpoint_list = new breakpoint_list(this, m_breakpoint_handler); + m_choice_units = new QComboBox(this); m_choice_units->setSizeAdjustPolicy(QComboBox::AdjustToContents); m_choice_units->setMaxVisibleItems(30); @@ -40,11 +43,6 @@ debugger_frame::debugger_frame(std::shared_ptr settings, QWidget * m_choice_units->completer()->setMaxVisibleItems(30); m_choice_units->completer()->setFilterMode(Qt::MatchContains); - m_breakpoints_list_delete = new QAction("Delete", m_breakpoints_list); - m_breakpoints_list_delete->setShortcut(Qt::Key_Delete); - m_breakpoints_list_delete->setShortcutContext(Qt::WidgetShortcut); - m_breakpoints_list->addAction(m_breakpoints_list_delete); - m_go_to_addr = new QPushButton(tr("Go To Address"), this); m_go_to_pc = new QPushButton(tr("Go To PC"), this); m_btn_capture = new QPushButton(tr("Capture"), this); @@ -70,17 +68,17 @@ debugger_frame::debugger_frame(std::shared_ptr settings, QWidget * m_regs->setLineWrapMode(QTextEdit::NoWrap); m_regs->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); - m_list->setFont(m_mono); + m_debugger_list->setFont(m_mono); m_regs->setFont(m_mono); m_right_splitter = new QSplitter(this); m_right_splitter->setOrientation(Qt::Vertical); m_right_splitter->addWidget(m_regs); - m_right_splitter->addWidget(m_breakpoints_list); + m_right_splitter->addWidget(m_breakpoint_list); m_right_splitter->setStretchFactor(0, 1); m_splitter = new QSplitter(this); - m_splitter->addWidget(m_list); + m_splitter->addWidget(m_debugger_list); m_splitter->addWidget(m_right_splitter); QHBoxLayout* hbox_w_list = new QHBoxLayout(); @@ -93,15 +91,8 @@ debugger_frame::debugger_frame(std::shared_ptr settings, QWidget * body->setLayout(vbox_p_main); setWidget(body); - m_list->setWindowTitle(tr("ASM")); - for (uint i = 0; i < m_list->m_item_count; ++i) - { - m_list->insertItem(i, new QListWidgetItem("")); - } - m_list->setSizeAdjustPolicy(QListWidget::AdjustToContents); - connect(m_go_to_addr, &QAbstractButton::clicked, this, &debugger_frame::ShowGotoAddressDialog); - connect(m_go_to_pc, &QAbstractButton::clicked, this, &debugger_frame::Show_PC); + connect(m_go_to_pc, &QAbstractButton::clicked, this, &debugger_frame::ShowPC); connect(m_btn_capture, &QAbstractButton::clicked, [=]() { @@ -139,11 +130,10 @@ debugger_frame::debugger_frame(std::shared_ptr settings, QWidget * connect(m_choice_units, static_cast(&QComboBox::currentIndexChanged), this, &debugger_frame::OnSelectUnit); connect(this, &QDockWidget::visibilityChanged, this, &debugger_frame::EnableUpdateTimer); - connect(m_breakpoints_list, &QListWidget::itemDoubleClicked, this, &debugger_frame::OnBreakpointList_doubleClicked); - connect(m_breakpoints_list, &QListWidget::customContextMenuRequested, this, &debugger_frame::OnBreakpointList_rightClicked); - connect(m_breakpoints_list_delete, &QAction::triggered, this, &debugger_frame::OnBreakpointList_delete); + connect(m_debugger_list, &debugger_list::BreakpointRequested, m_breakpoint_list, &breakpoint_list::HandleBreakpointRequest); + connect(m_breakpoint_list, &breakpoint_list::RequestShowAddress, m_debugger_list, &debugger_list::ShowAddress); - m_list->ShowAddress(m_list->m_pc); + m_debugger_list->ShowAddress(m_debugger_list->m_pc); UpdateUnitList(); } @@ -154,15 +144,25 @@ void debugger_frame::SaveSettings() void debugger_frame::ChangeColors() { - if (m_list) + if (m_debugger_list) { - m_list->m_color_bp = gui::utils::get_label_color("debugger_frame_breakpoint", QPalette::Background); - m_list->m_color_pc = gui::utils::get_label_color("debugger_frame_pc", QPalette::Background); - m_list->m_text_color_bp = gui::utils::get_label_color("debugger_frame_breakpoint");; - m_list->m_text_color_pc = gui::utils::get_label_color("debugger_frame_pc");; + m_debugger_list->m_color_bp = m_breakpoint_list->m_color_bp = gui::utils::get_label_color("debugger_frame_breakpoint", QPalette::Background); + m_debugger_list->m_color_pc = gui::utils::get_label_color("debugger_frame_pc", QPalette::Background); + m_debugger_list->m_text_color_bp = m_breakpoint_list->m_text_color_bp = gui::utils::get_label_color("debugger_frame_breakpoint");; + m_debugger_list->m_text_color_pc = gui::utils::get_label_color("debugger_frame_pc");; } } +bool debugger_frame::eventFilter(QObject* object, QEvent* ev) +{ + // There's no overlap between keys so returning true wouldn't matter. + if (object == m_debugger_list && ev->type() == QEvent::KeyPress) + { + keyPressEvent(static_cast(ev)); + } + return false; +} + void debugger_frame::closeEvent(QCloseEvent *event) { QDockWidget::closeEvent(event); @@ -195,11 +195,61 @@ void debugger_frame::hideEvent(QHideEvent * event) QDockWidget::hideEvent(event); } -#include +void debugger_frame::keyPressEvent(QKeyEvent* event) +{ + const auto cpu = this->cpu.lock(); + int i = m_debugger_list->currentRow(); -std::map g_breakpoints; + if (!isActiveWindow() || i < 0 || !cpu) + { + return; + } + + const u32 start_pc = m_debugger_list->m_pc - m_debugger_list->m_item_count * 4; + const u32 pc = start_pc + i * 4; + + if (QApplication::keyboardModifiers() & Qt::ControlModifier) + { + switch (event->key()) + { + case Qt::Key_G: + { + ShowGotoAddressDialog(); + return; + } + } + } + else + { + switch (event->key()) + { + case Qt::Key_E: + { + instruction_editor_dialog* dlg = new instruction_editor_dialog(this, pc, cpu, m_disasm.get()); + dlg->show(); + return; + } + case Qt::Key_R: + { + register_editor_dialog* dlg = new register_editor_dialog(this, pc, cpu, m_disasm.get()); + dlg->show(); + return; + } + + case Qt::Key_F10: + { + DoStep(true); + return; + } + case Qt::Key_F11: + { + DoStep(false); + return; + } + } + } +} -extern void ppu_breakpoint(u32 addr); u32 debugger_frame::GetPc() const { @@ -308,7 +358,7 @@ void debugger_frame::OnSelectUnit() m_current_choice = m_choice_units->currentText(); m_no_thread_selected = m_current_choice == NoThreadString; - m_list->m_no_thread_selected = m_no_thread_selected; + m_debugger_list->m_no_thread_selected = m_no_thread_selected; m_disasm.reset(); cpu.reset(); @@ -338,6 +388,8 @@ void debugger_frame::OnSelectUnit() } } + m_debugger_list->UpdateCPUData(this->cpu, m_disasm); + m_breakpoint_list->UpdateCPUData(this->cpu, m_disasm); DoUpdate(); } @@ -346,11 +398,11 @@ void debugger_frame::DoUpdate() // Check if we need to disable a step over bp if (m_last_step_over_breakpoint != -1 && GetPc() == m_last_step_over_breakpoint) { - ppu_breakpoint(m_last_step_over_breakpoint); + m_breakpoint_handler->RemoveBreakpoint(m_last_step_over_breakpoint); m_last_step_over_breakpoint = -1; } - Show_PC(); + ShowPC(); WriteRegs(); } @@ -370,11 +422,6 @@ void debugger_frame::WriteRegs() m_regs->verticalScrollBar()->setValue(loc); } -void debugger_frame::OnUpdate() -{ - //WriteRegs(); -} - void debugger_frame::ShowGotoAddressDialog() { QDialog* diag = new QDialog(this); @@ -463,7 +510,7 @@ void debugger_frame::ShowGotoAddressDialog() address_preview_label->setText(expression_input->text()); } - m_list->ShowAddress(address); + m_debugger_list->ShowAddress(address); } diag->deleteLater(); @@ -509,9 +556,14 @@ u64 debugger_frame::EvaluateExpression(const QString& expression) return static_cast(scriptEngine.evaluate(expression).toNumber()); } -void debugger_frame::Show_PC() +void debugger_frame::ClearBreakpoints() { - m_list->ShowAddress(GetPc()); + m_breakpoint_list->ClearBreakpoints(); +} + +void debugger_frame::ShowPC() +{ + m_debugger_list->ShowAddress(GetPc()); } void debugger_frame::DoStep(bool stepOver) @@ -534,13 +586,15 @@ void debugger_frame::DoStep(bool stepOver) // Set breakpoint on next instruction u32 next_instruction_pc = current_instruction_pc + 4; - ppu_breakpoint(next_instruction_pc); + m_breakpoint_handler->AddBreakpoint(next_instruction_pc); // Undefine previous step over breakpoint if it hasnt been already // This can happen when the user steps over a branch that doesn't return to itself if (m_last_step_over_breakpoint != -1) - ppu_breakpoint(m_last_step_over_breakpoint); - + { + m_breakpoint_handler->RemoveBreakpoint(next_instruction_pc); + } + m_last_step_over_breakpoint = next_instruction_pc; } @@ -564,306 +618,3 @@ void debugger_frame::EnableButtons(bool enable) m_btn_step_over->setEnabled(enable); m_btn_run->setEnabled(enable); } - -void debugger_frame::ClearBreakpoints() -{ - //This is required to actually delete the breakpoints, not just visually. - for (std::map::iterator it = g_breakpoints.begin(); it != g_breakpoints.end(); it++) - m_list->RemoveBreakPoint(it->first, false); - - g_breakpoints.clear(); -} - -void debugger_frame::OnBreakpointList_doubleClicked() -{ - u32 address = m_breakpoints_list->currentItem()->data(Qt::UserRole).value(); - m_list->ShowAddress(address); - m_list->setCurrentRow(16); -} - -void debugger_frame::OnBreakpointList_rightClicked(const QPoint &pos) -{ - if (m_breakpoints_list->itemAt(pos) == NULL) - return; - - QMenu* menu = new QMenu(); - - if (m_breakpoints_list->selectedItems().count() == 1) - { - menu->addAction("Rename"); - menu->addSeparator(); - } - - menu->addAction(m_breakpoints_list_delete); - - QAction* selectedItem = menu->exec(QCursor::pos()); - if (selectedItem != NULL) - { - if (selectedItem->text() == "Rename") - { - QListWidgetItem* currentItem = m_breakpoints_list->selectedItems().at(0); - - currentItem->setFlags(currentItem->flags() | Qt::ItemIsEditable); - m_breakpoints_list->editItem(currentItem); - } - } -} - -void debugger_frame::OnBreakpointList_delete() -{ - int selectedCount = m_breakpoints_list->selectedItems().count(); - - for (int i = selectedCount - 1; i >= 0; i--) - { - m_list->RemoveBreakPoint(m_breakpoints_list->item(i)->data(Qt::UserRole).value()); - } -} - -debugger_list::debugger_list(debugger_frame* parent, std::shared_ptr settings) : QListWidget(parent) -{ - m_pc = 0; - m_item_count = 30; - m_debugFrame = parent; - m_center_shown_addresses = settings->GetValue(gui::d_centerPC).toBool(); -}; - -u32 debugger_list::GetCenteredAddress(u32 address) -{ - return address - ((m_item_count / 2) * 4); -} - -void debugger_list::ShowAddress(u32 addr) -{ - if (m_center_shown_addresses) - { - m_pc = GetCenteredAddress(addr); - } - else - { - m_pc = addr; - } - - const auto cpu = m_debugFrame->cpu.lock(); - - if (!cpu) - { - for (uint i = 0; i < m_item_count; ++i, m_pc += 4) - { - item(i)->setText(qstr(fmt::format("[%08x] illegal address", m_pc))); - } - } - else - { - const bool is_spu = cpu->id_type() != 1; - const u32 cpu_offset = is_spu ? static_cast(*cpu).offset : 0; - const u32 address_limits = is_spu ? 0x3ffff : ~0; - m_pc &= address_limits; - m_debugFrame->m_disasm->offset = (u8*)vm::base(cpu_offset); - for (uint i = 0, count = 4; isetText((IsBreakPoint(m_pc) ? ">>> " : " ") + qstr(fmt::format("[%08x] illegal address", m_pc))); - count = 4; - continue; - } - - count = m_debugFrame->m_disasm->disasm(m_debugFrame->m_disasm->dump_pc = m_pc); - - item(i)->setText((IsBreakPoint(m_pc) ? ">>> " : " ") + qstr(m_debugFrame->m_disasm->last_opcode)); - - if (test(cpu->state & cpu_state_pause) && m_pc == m_debugFrame->GetPc()) - { - item(i)->setTextColor(m_text_color_pc); - item(i)->setBackgroundColor(m_color_pc); - } - else if (IsBreakPoint(m_pc)) - { - item(i)->setTextColor(m_text_color_bp); - item(i)->setBackgroundColor(m_color_bp); - } - else - { - item(i)->setTextColor(palette().color(foregroundRole())); - item(i)->setBackgroundColor(palette().color(backgroundRole())); - } - } - } - - setLineWidth(-1); -} - -bool debugger_list::IsBreakPoint(u32 pc) -{ - return g_breakpoints.count(pc) != 0; -} - -void debugger_list::AddBreakPoint(u32 pc) -{ - g_breakpoints.emplace(pc, false); - ppu_breakpoint(pc); - - const auto cpu = m_debugFrame->cpu.lock(); - const u32 cpu_offset = cpu->id_type() != 1 ? static_cast(*cpu).offset : 0; - m_debugFrame->m_disasm->offset = (u8*)vm::base(cpu_offset); - - m_debugFrame->m_disasm->disasm(m_debugFrame->m_disasm->dump_pc = pc); - - QString breakpointItemText = qstr(m_debugFrame->m_disasm->last_opcode); - - breakpointItemText.remove(10, 13); - - QListWidgetItem* breakpointItem = new QListWidgetItem(breakpointItemText); - breakpointItem->setTextColor(m_text_color_bp); - breakpointItem->setBackgroundColor(m_color_bp); - QVariant pcVariant; - pcVariant.setValue(pc); - breakpointItem->setData(Qt::UserRole, pcVariant); - m_debugFrame->m_breakpoints_list->addItem(breakpointItem); -} - -void debugger_list::RemoveBreakPoint(u32 pc, bool eraseFromMap) -{ - if (eraseFromMap) - g_breakpoints.erase(pc); - - ppu_breakpoint(pc); - - int breakpointsListCount = m_debugFrame->m_breakpoints_list->count(); - - for (int i = 0; i < breakpointsListCount; i++) - { - QListWidgetItem* currentItem = m_debugFrame->m_breakpoints_list->item(i); - - if (currentItem->data(Qt::UserRole).value() == pc) - { - delete m_debugFrame->m_breakpoints_list->takeItem(i); - break; - } - } - - ShowAddress(m_pc - (m_item_count) * 4); - -} - -void debugger_list::keyPressEvent(QKeyEvent* event) -{ - if (!isActiveWindow()) - { - return; - } - - const auto cpu = m_debugFrame->cpu.lock(); - long i = currentRow(); - - if (i < 0 || !cpu) - { - return; - } - - const u32 start_pc = m_pc - m_item_count * 4; - const u32 pc = start_pc + i * 4; - - if (QApplication::keyboardModifiers() & Qt::ControlModifier) - { - switch (event->key()) - { - case Qt::Key_G: - m_debugFrame->ShowGotoAddressDialog(); - return; - } - } - else - { - switch (event->key()) - { - case Qt::Key_PageUp: ShowAddress(m_pc - (m_item_count * 2) * 4); return; - case Qt::Key_PageDown: ShowAddress(m_pc); return; - case Qt::Key_Up: ShowAddress(m_pc - (m_item_count + 1) * 4); return; - case Qt::Key_Down: ShowAddress(m_pc - (m_item_count - 1) * 4); return; - case Qt::Key_E: - { - instruction_editor_dialog* dlg = new instruction_editor_dialog(this, pc, cpu, m_debugFrame->m_disasm.get()); - dlg->show(); - return; - } - case Qt::Key_R: - { - register_editor_dialog* dlg = new register_editor_dialog(this, pc, cpu, m_debugFrame->m_disasm.get()); - dlg->show(); - return; - } - - case Qt::Key_F10: - m_debugFrame->DoStep(true); - return; - - case Qt::Key_F11: - m_debugFrame->DoStep(false); - return; - } - } -} - -void debugger_list::mouseDoubleClickEvent(QMouseEvent* event) -{ - if (event->button() == Qt::LeftButton && !Emu.IsStopped() && !m_no_thread_selected) - { - long i = currentRow(); - if (i < 0) return; - - const u32 start_pc = m_pc - m_item_count * 4; - const u32 pc = start_pc + i * 4; - - if (IsBreakPoint(pc)) - { - RemoveBreakPoint(pc); - } - else - { - const auto cpu = m_debugFrame->cpu.lock(); - - if (cpu->id_type() == 1 && vm::check_addr(pc)) - { - AddBreakPoint(pc); - } - } - - ShowAddress(start_pc); - } -} - -void debugger_list::wheelEvent(QWheelEvent* event) -{ - QPoint numSteps = event->angleDelta() / 8 / 15; // http://doc.qt.io/qt-5/qwheelevent.html#pixelDelta - const int value = numSteps.y(); - - ShowAddress(m_pc - (event->modifiers() == Qt::ControlModifier ? m_item_count * (value + 1) : m_item_count + value) * 4); -} - -void debugger_list::resizeEvent(QResizeEvent* event) -{ - Q_UNUSED(event); - - if (count() < 1 || visualItemRect(item(0)).height() < 1) - { - return; - } - - m_item_count = (rect().height() - frameWidth() * 2) / visualItemRect(item(0)).height(); - - clear(); - - for (u32 i = 0; i < m_item_count; ++i) - { - insertItem(i, new QListWidgetItem("")); - } - - if (horizontalScrollBar()) - { - m_item_count--; - delete item(m_item_count); - } - - ShowAddress(m_pc - m_item_count * 4); -} diff --git a/rpcs3/rpcs3qt/debugger_frame.h b/rpcs3/rpcs3qt/debugger_frame.h index 8dd7d6ec99..7d6ba85248 100644 --- a/rpcs3/rpcs3qt/debugger_frame.h +++ b/rpcs3/rpcs3qt/debugger_frame.h @@ -13,21 +13,16 @@ #include "Emu/Cell/SPUDisAsm.h" #include "Emu/Cell/PPUInterpreter.h" +#include "breakpoint_handler.h" #include "custom_dock_widget.h" #include "instruction_editor_dialog.h" #include "register_editor_dialog.h" #include "gui_settings.h" +#include "debugger_list.h" +#include "breakpoint_list.h" -#include -#include -#include -#include -#include -#include -#include #include - -class debugger_list; +#include class debugger_frame : public custom_dock_widget { @@ -37,7 +32,7 @@ class debugger_frame : public custom_dock_widget const QString RunString = tr("Run"); const QString PauseString = tr("Pause"); - debugger_list* m_list; + debugger_list* m_debugger_list; QSplitter* m_right_splitter; QFont m_mono; QTextEdit* m_regs; @@ -51,7 +46,6 @@ class debugger_frame : public custom_dock_widget QString m_current_choice; QTimer* m_update; QSplitter* m_splitter; - QAction* m_breakpoints_list_delete; u64 m_threads_created = 0; u64 m_threads_deleted = 0; @@ -60,13 +54,15 @@ class debugger_frame : public custom_dock_widget u32 m_last_step_over_breakpoint = -1; bool m_no_thread_selected = true; + std::shared_ptr m_disasm; + std::weak_ptr cpu; + + breakpoint_list* m_breakpoint_list; + breakpoint_handler* m_breakpoint_handler; + std::shared_ptr xgui_settings; public: - std::unique_ptr m_disasm; - QListWidget* m_breakpoints_list; - std::weak_ptr cpu; - explicit debugger_frame(std::shared_ptr settings, QWidget *parent = 0); void SaveSettings(); @@ -79,17 +75,18 @@ public: void DoUpdate(); void WriteRegs(); void EnableButtons(bool enable); - void ClearBreakpoints(); void ShowGotoAddressDialog(); u64 EvaluateExpression(const QString& expression); + void ClearBreakpoints(); // Fallthrough method into breakpoint_list. - void OnUpdate(); - + /** Needed so key press events work when other objects are selected in debugger_frame. */ + bool eventFilter(QObject* object, QEvent* event) override; protected: /** Override inherited method from Qt to allow signalling when close happened.*/ void closeEvent(QCloseEvent* event) override; void showEvent(QShowEvent* event) override; void hideEvent(QHideEvent* event) override; + void keyPressEvent(QKeyEvent* event) override; Q_SIGNALS: void DebugFrameClosed(); @@ -98,45 +95,9 @@ public Q_SLOTS: void DoStep(bool stepOver = false); private Q_SLOTS: - void OnBreakpointList_doubleClicked(); - void OnBreakpointList_rightClicked(const QPoint &pos); - void OnBreakpointList_delete(); void OnSelectUnit(); - void Show_PC(); + void ShowPC(); void EnableUpdateTimer(bool state); }; -class debugger_list : public QListWidget -{ - Q_OBJECT; - - debugger_frame* m_debugFrame; - -public: - u32 m_pc; - u32 m_item_count; - bool m_no_thread_selected; - QColor m_color_bp; - QColor m_color_pc; - QColor m_text_color_bp; - QColor m_text_color_pc; - bool m_center_shown_addresses; - -public: - debugger_list(debugger_frame* parent, std::shared_ptr settings); - void ShowAddress(u32 addr); - void RemoveBreakPoint(u32 pc, bool eraseFromMap = true); - bool IsBreakPoint(u32 pc); - void AddBreakPoint(u32 pc); - -private: - u32 GetCenteredAddress(u32 address); - -protected: - void keyPressEvent(QKeyEvent* event); - void mouseDoubleClickEvent(QMouseEvent* event); - void wheelEvent(QWheelEvent* event); - void resizeEvent(QResizeEvent* event); -}; - -Q_DECLARE_METATYPE(u32); \ No newline at end of file +Q_DECLARE_METATYPE(u32); diff --git a/rpcs3/rpcs3qt/debugger_list.cpp b/rpcs3/rpcs3qt/debugger_list.cpp new file mode 100644 index 0000000000..a3b8a454c2 --- /dev/null +++ b/rpcs3/rpcs3qt/debugger_list.cpp @@ -0,0 +1,183 @@ +#include "debugger_list.h" + +#include "Emu/Cell/SPUThread.h" +#include "Emu/System.h" + +#include +#include +#include + +#include + +constexpr auto qstr = QString::fromStdString; + +debugger_list::debugger_list(QWidget* parent, std::shared_ptr settings, breakpoint_handler* handler) : QListWidget(parent), m_breakpoint_handler(handler), + xgui_settings(settings), m_pc(0), m_item_count(30) +{ + setWindowTitle(tr("ASM")); + for (uint i = 0; i < m_item_count; ++i) + { + insertItem(i, new QListWidgetItem("")); + } + setSizeAdjustPolicy(QListWidget::AdjustToContents); +} + +void debugger_list::UpdateCPUData(std::weak_ptr cpu, std::shared_ptr disasm) +{ + this->cpu = cpu; + m_disasm = disasm; +} + +u32 debugger_list::GetPc() const +{ + const auto cpu = this->cpu.lock(); + + if (!cpu) + { + return 0; + } + + return cpu->id_type() == 1 ? static_cast(cpu.get())->cia : static_cast(cpu.get())->pc; +} + +u32 debugger_list::GetCenteredAddress(u32 address) const +{ + return address - ((m_item_count / 2) * 4); +} + +void debugger_list::ShowAddress(u32 addr) +{ + auto IsBreakpoint = [this](u32 pc) + { + return m_breakpoint_handler->HasBreakpoint(pc); + }; + + if (xgui_settings->GetValue(gui::d_centerPC).toBool()) + { + m_pc = GetCenteredAddress(addr); + } + else + { + m_pc = addr; + } + + const auto cpu = this->cpu.lock(); + + if (!cpu) + { + for (uint i = 0; i < m_item_count; ++i, m_pc += 4) + { + item(i)->setText(qstr(fmt::format("[%08x] illegal address", m_pc))); + } + } + else + { + const bool is_spu = cpu->id_type() != 1; + const u32 cpu_offset = is_spu ? static_cast(*cpu).offset : 0; + const u32 address_limits = is_spu ? 0x3ffff : ~0; + m_pc &= address_limits; + m_disasm->offset = (u8*)vm::base(cpu_offset); + for (uint i = 0, count = 4; isetText((IsBreakpoint(m_pc) ? ">>> " : " ") + qstr(fmt::format("[%08x] illegal address", m_pc))); + count = 4; + continue; + } + + count = m_disasm->disasm(m_disasm->dump_pc = m_pc); + + item(i)->setText((IsBreakpoint(m_pc) ? ">>> " : " ") + qstr(m_disasm->last_opcode)); + + if (test(cpu->state & cpu_state_pause) && m_pc == GetPc()) + { + item(i)->setTextColor(m_text_color_pc); + item(i)->setBackgroundColor(m_color_pc); + } + else if (IsBreakpoint(m_pc)) + { + item(i)->setTextColor(m_text_color_bp); + item(i)->setBackgroundColor(m_color_bp); + } + else + { + item(i)->setTextColor(palette().color(foregroundRole())); + item(i)->setBackgroundColor(palette().color(backgroundRole())); + } + } + } + + setLineWidth(-1); +} + +void debugger_list::keyPressEvent(QKeyEvent* event) +{ + if (!isActiveWindow() || currentRow() < 0 || !this->cpu.lock()) + { + return; + } + + switch (event->key()) + { + case Qt::Key_PageUp: ShowAddress(m_pc - (m_item_count * 2) * 4); return; + case Qt::Key_PageDown: ShowAddress(m_pc); return; + case Qt::Key_Up: ShowAddress(m_pc - (m_item_count + 1) * 4); return; + case Qt::Key_Down: ShowAddress(m_pc - (m_item_count - 1) * 4); return; + default: break; + } +} + +void debugger_list::mouseDoubleClickEvent(QMouseEvent* event) +{ + if (event->button() == Qt::LeftButton && !Emu.IsStopped() && !m_no_thread_selected) + { + int i = currentRow(); + if (i < 0) return; + + const u32 start_pc = m_pc - m_item_count * 4; + const u32 pc = start_pc + i * 4; + + // Let debugger_frame know about breakpoint. + // Other option is to add to breakpoint manager directly and have a signal there instead. + // Either the flow goes from debugger_list->breakpoint_manager->debugger_frame, or it goes debugger_list->debugger_frame, and I felt this was easier to read for now. + Q_EMIT BreakpointRequested(pc); + + ShowAddress(start_pc); + } +} + +void debugger_list::wheelEvent(QWheelEvent* event) +{ + QPoint numSteps = event->angleDelta() / 8 / 15; // http://doc.qt.io/qt-5/qwheelevent.html#pixelDelta + const int value = numSteps.y(); + + ShowAddress(m_pc - (event->modifiers() == Qt::ControlModifier ? m_item_count * (value + 1) : m_item_count + value) * 4); +} + +void debugger_list::resizeEvent(QResizeEvent* event) +{ + Q_UNUSED(event); + + if (count() < 1 || visualItemRect(item(0)).height() < 1) + { + return; + } + + m_item_count = (rect().height() - frameWidth() * 2) / visualItemRect(item(0)).height(); + + clear(); + + for (u32 i = 0; i < m_item_count; ++i) + { + insertItem(i, new QListWidgetItem("")); + } + + if (horizontalScrollBar()) + { + m_item_count--; + delete item(m_item_count); + } + + ShowAddress(m_pc - m_item_count * 4); +} diff --git a/rpcs3/rpcs3qt/debugger_list.h b/rpcs3/rpcs3qt/debugger_list.h new file mode 100644 index 0000000000..b68871327c --- /dev/null +++ b/rpcs3/rpcs3qt/debugger_list.h @@ -0,0 +1,48 @@ +#pragma once + +#include "breakpoint_handler.h" +#include "gui_settings.h" + +#include "Emu/CPU/CPUThread.h" +#include "Emu/CPU/CPUDisAsm.h" + +#include + +class debugger_list : public QListWidget +{ + Q_OBJECT + +public: + u32 m_pc; + u32 m_item_count; + bool m_no_thread_selected; + QColor m_color_bp; + QColor m_color_pc; + QColor m_text_color_bp; + QColor m_text_color_pc; + +Q_SIGNALS: + void BreakpointRequested(u32 loc); +public: + debugger_list(QWidget* parent, std::shared_ptr settings, breakpoint_handler* handler); + void UpdateCPUData(std::weak_ptr cpu, std::shared_ptr disasm); +public Q_SLOTS: + void ShowAddress(u32 addr); +protected: + void keyPressEvent(QKeyEvent* event) override; + void mouseDoubleClickEvent(QMouseEvent* event) override; + void wheelEvent(QWheelEvent* event) override; + void resizeEvent(QResizeEvent* event) override; +private: + /** + * It really upsetted me I had to copy this code to make debugger_list/frame not circularly dependent. + */ + u32 GetPc() const; + u32 GetCenteredAddress(u32 address) const; + + std::shared_ptr xgui_settings; + + breakpoint_handler* m_breakpoint_handler; + std::weak_ptr cpu; + std::shared_ptr m_disasm; +}; diff --git a/rpcs3/rpcs3qt/game_list.h b/rpcs3/rpcs3qt/game_list.h index bb8e902185..ca739b7455 100644 --- a/rpcs3/rpcs3qt/game_list.h +++ b/rpcs3/rpcs3qt/game_list.h @@ -10,7 +10,7 @@ class game_list : public QTableWidget { private: - void mousePressEvent(QMouseEvent *event) + void mousePressEvent(QMouseEvent *event) override { if (!indexAt(event->pos()).isValid() || itemAt(event->pos())->data(Qt::UserRole) < 0) { diff --git a/rpcs3/rpcs3qt/game_list_grid_delegate.h b/rpcs3/rpcs3qt/game_list_grid_delegate.h index 017ce83043..1115a2a1a5 100644 --- a/rpcs3/rpcs3qt/game_list_grid_delegate.h +++ b/rpcs3/rpcs3qt/game_list_grid_delegate.h @@ -8,8 +8,8 @@ class game_list_grid_delegate : public QAbstractItemDelegate public: game_list_grid_delegate(const QSize& imageSize, const qreal& margin_factor, const qreal& margin_ratio, const QFont& font, const QColor& font_color, QObject *parent = 0); - void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const; - QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index) const; + void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const override; + QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index) const override; void setItemSize(const QSize& size); virtual ~game_list_grid_delegate(); private: diff --git a/rpcs3/rpcs3qt/gs_frame.h b/rpcs3/rpcs3qt/gs_frame.h index 7c581f16a7..690fb366da 100644 --- a/rpcs3/rpcs3qt/gs_frame.h +++ b/rpcs3/rpcs3qt/gs_frame.h @@ -30,7 +30,7 @@ public: wm_event get_default_wm_event() const override; protected: virtual void paintEvent(QPaintEvent *event); - virtual void showEvent(QShowEvent *event); + virtual void showEvent(QShowEvent *event) override; void keyPressEvent(QKeyEvent *keyEvent) override; void OnFullScreen(); diff --git a/rpcs3/rpcs3qt/msg_dialog_frame.h b/rpcs3/rpcs3qt/msg_dialog_frame.h index 52deb5bbfd..a24711aab7 100644 --- a/rpcs3/rpcs3qt/msg_dialog_frame.h +++ b/rpcs3/rpcs3qt/msg_dialog_frame.h @@ -88,7 +88,7 @@ public: } } private: - void keyPressEvent(QKeyEvent* event) + void keyPressEvent(QKeyEvent* event) override { // this won't work with Alt+F4, the window still closes if (m_disable_cancel && event->key() == Qt::Key_Escape) @@ -100,7 +100,7 @@ private: QDialog::keyPressEvent(event); } } - void closeEvent(QCloseEvent* event) + void closeEvent(QCloseEvent* event) override { // spontaneous: don't close on external system level events like Alt+F4 if (m_disable_cancel && event->spontaneous()) diff --git a/rpcs3/rpcs3qt/table_item_delegate.h b/rpcs3/rpcs3qt/table_item_delegate.h index 9c0b768d1f..05c3961f90 100644 --- a/rpcs3/rpcs3qt/table_item_delegate.h +++ b/rpcs3/rpcs3qt/table_item_delegate.h @@ -7,5 +7,5 @@ class table_item_delegate : public QItemDelegate { public: explicit table_item_delegate(QObject *parent = 0) : QItemDelegate(parent) {} - virtual void drawFocus(QPainter * /*painter*/, const QStyleOptionViewItem & /*option*/, const QRect & /*rect*/) const {} + virtual void drawFocus(QPainter * /*painter*/, const QStyleOptionViewItem & /*option*/, const QRect & /*rect*/) const override {} }; diff --git a/rpcs3/rpcs3qt/trophy_tree_widget_item.h b/rpcs3/rpcs3qt/trophy_tree_widget_item.h index c96f3c72c3..5252d048cb 100644 --- a/rpcs3/rpcs3qt/trophy_tree_widget_item.h +++ b/rpcs3/rpcs3qt/trophy_tree_widget_item.h @@ -11,7 +11,8 @@ public: trophy_tree_widget_item(QTreeWidget* parent) : QTreeWidgetItem(parent) {}; trophy_tree_widget_item(QTreeWidgetItem* parent) : QTreeWidgetItem(parent) {}; private: - bool operator<(const QTreeWidgetItem &other) const { + bool operator<(const QTreeWidgetItem &other) const override + { auto GetTrophyRank = [](const QString& name) { if (name.toLower() == "bronze")