diff --git a/pcsx2-qt/CMakeLists.txt b/pcsx2-qt/CMakeLists.txt index a6e56d4e5a..7aa4f3e348 100644 --- a/pcsx2-qt/CMakeLists.txt +++ b/pcsx2-qt/CMakeLists.txt @@ -138,6 +138,8 @@ target_sources(pcsx2-qt PRIVATE Debugger/Models/BreakpointModel.h Debugger/Models/ThreadModel.cpp Debugger/Models/ThreadModel.h + Debugger/Models/StackModel.cpp + Debugger/Models/StackModel.h Tools/InputRecording/NewInputRecordingDlg.cpp Tools/InputRecording/NewInputRecordingDlg.h Tools/InputRecording/NewInputRecordingDlg.ui diff --git a/pcsx2-qt/Debugger/CpuWidget.cpp b/pcsx2-qt/Debugger/CpuWidget.cpp index b6f9d1ec84..a36fa9be0b 100644 --- a/pcsx2-qt/Debugger/CpuWidget.cpp +++ b/pcsx2-qt/Debugger/CpuWidget.cpp @@ -43,6 +43,7 @@ CpuWidget::CpuWidget(QWidget* parent, DebugInterface& cpu) : m_cpu(cpu) , m_bpModel(cpu) , m_threadModel(cpu) + , m_stackModel(cpu) { m_ui.setupUi(this); @@ -70,8 +71,11 @@ CpuWidget::CpuWidget(QWidget* parent, DebugInterface& cpu) m_ui.threadList->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); m_ui.threadList->setModel(&m_threadModel); - connect(m_ui.stackframeList, &QTableWidget::customContextMenuRequested, this, &CpuWidget::onStackListContextMenu); - connect(m_ui.stackframeList, &QTableWidget::cellDoubleClicked, this, &CpuWidget::onStackListDoubleClick); + connect(m_ui.stackList, &QTableView::customContextMenuRequested, this, &CpuWidget::onStackListContextMenu); + connect(m_ui.stackList, &QTableView::doubleClicked, this, &CpuWidget::onStackListDoubleClick); + + m_ui.stackList->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); + m_ui.stackList->setModel(&m_stackModel); connect(m_ui.tabWidgetRegFunc, &QTabWidget::currentChanged, [this](int i) {if(i == 1){updateFunctionList(true);} }); connect(m_ui.listFunctions, &QListWidget::customContextMenuRequested, this, &CpuWidget::onFuncListContextMenu); @@ -471,72 +475,46 @@ void CpuWidget::onFuncListDoubleClick(QListWidgetItem* item) void CpuWidget::updateStackFrames() { - m_ui.stackframeList->setRowCount(0); - - m_stacklistObjects = MipsStackWalk::Walk(&m_cpu, m_cpu.getPC(), m_cpu.getRegister(0, 31), m_cpu.getRegister(0, 29), - m_activeThread.data.entry_init, m_activeThread.data.stack); - - for (size_t i = 0; i < m_stacklistObjects.size(); i++) - { - m_ui.stackframeList->insertRow(i); - - const auto& stackFrame = m_stacklistObjects.at(i); - - QTableWidgetItem* entryItem = new QTableWidgetItem(); - entryItem->setText(FilledQStringFromValue(stackFrame.entry, 16)); - entryItem->setFlags(Qt::ItemFlag::ItemIsSelectable | Qt::ItemFlag::ItemIsEnabled); - m_ui.stackframeList->setItem(i, 0, entryItem); - - QTableWidgetItem* entryName = new QTableWidgetItem(); - entryName->setText(m_cpu.GetSymbolMap().GetLabelString(stackFrame.entry).c_str()); - entryName->setFlags(Qt::ItemFlag::ItemIsSelectable | Qt::ItemFlag::ItemIsEnabled); - m_ui.stackframeList->setItem(i, 1, entryName); - - QTableWidgetItem* entryPC = new QTableWidgetItem(); - entryPC->setText(FilledQStringFromValue(stackFrame.pc, 16)); - entryPC->setFlags(Qt::ItemFlag::ItemIsSelectable | Qt::ItemFlag::ItemIsEnabled); - m_ui.stackframeList->setItem(i, 2, entryPC); - - QTableWidgetItem* entryOpcode = new QTableWidgetItem(); - entryOpcode->setText(m_ui.disassemblyWidget->GetLineDisasm(stackFrame.pc)); - entryOpcode->setFlags(Qt::ItemFlag::ItemIsSelectable | Qt::ItemFlag::ItemIsEnabled); - m_ui.stackframeList->setItem(i, 3, entryOpcode); - - QTableWidgetItem* entrySP = new QTableWidgetItem(); - entrySP->setText(FilledQStringFromValue(stackFrame.sp, 16)); - entrySP->setFlags(Qt::ItemFlag::ItemIsSelectable | Qt::ItemFlag::ItemIsEnabled); - m_ui.stackframeList->setItem(i, 4, entrySP); - - QTableWidgetItem* entryStackSize = new QTableWidgetItem(); - entryStackSize->setText(QString::number(stackFrame.stackSize)); - entryStackSize->setFlags(Qt::ItemFlag::ItemIsSelectable | Qt::ItemFlag::ItemIsEnabled); - m_ui.stackframeList->setItem(i, 5, entryStackSize); - } + m_stackModel.refreshData(); } void CpuWidget::onStackListContextMenu(QPoint pos) { - if (!m_stacklistContextMenu) - { - m_stacklistContextMenu = new QMenu(m_ui.stackframeList); + if (!m_ui.stackList->selectionModel()->hasSelection()) + return; - QAction* copyAction = new QAction(tr("Copy"), m_ui.stackframeList); - connect(copyAction, &QAction::triggered, [this] { - const auto& items = m_ui.stackframeList->selectedItems(); - if (!items.size()) - return; - QApplication::clipboard()->setText(items.first()->text()); - }); - m_stacklistContextMenu->addAction(copyAction); - } + QMenu* contextMenu = new QMenu(tr("Stack List Context Menu"), m_ui.stackList); - m_stacklistContextMenu->exec(m_ui.stackframeList->mapToGlobal(pos)); + QAction* actionCopy = new QAction(tr("Copy"), m_ui.stackList); + connect(actionCopy, &QAction::triggered, [this]() { + const auto* selModel = m_ui.stackList->selectionModel(); + + if (!selModel->hasSelection()) + return; + + QGuiApplication::clipboard()->setText(m_ui.stackList->model()->data(selModel->currentIndex()).toString()); + }); + contextMenu->addAction(actionCopy); + + contextMenu->popup(m_ui.stackList->mapToGlobal(pos)); } -void CpuWidget::onStackListDoubleClick(int row, int column) +void CpuWidget::onStackListDoubleClick(const QModelIndex& index) { - const auto& entry = m_stacklistObjects.at(row); - m_ui.disassemblyWidget->gotoAddress(entry.pc); + switch (index.column()) + { + case StackModel::StackModel::ENTRY: + case StackModel::StackModel::ENTRY_LABEL: + m_ui.disassemblyWidget->gotoAddress(m_ui.stackList->model()->data(m_ui.stackList->model()->index(index.row(), StackModel::StackColumns::ENTRY), Qt::UserRole).toUInt()); + break; + case StackModel::StackModel::SP: + m_ui.memoryviewWidget->gotoAddress(m_ui.stackList->model()->data(index, Qt::UserRole).toUInt()); + m_ui.tabWidget->setCurrentWidget(m_ui.tab_memory); + break; + default: // Default to PC + m_ui.disassemblyWidget->gotoAddress(m_ui.stackList->model()->data(m_ui.stackList->model()->index(index.row(), StackModel::StackColumns::PC), Qt::UserRole).toUInt()); + break; + } } template diff --git a/pcsx2-qt/Debugger/CpuWidget.h b/pcsx2-qt/Debugger/CpuWidget.h index b182ba96f7..a7298e4870 100644 --- a/pcsx2-qt/Debugger/CpuWidget.h +++ b/pcsx2-qt/Debugger/CpuWidget.h @@ -24,6 +24,7 @@ #include "Models/BreakpointModel.h" #include "Models/ThreadModel.h" +#include "Models/StackModel.h" #include "QtHost.h" #include @@ -65,7 +66,7 @@ public slots: void updateStackFrames(); void onStackListContextMenu(QPoint pos); - void onStackListDoubleClick(int row, int column); + void onStackListDoubleClick(const QModelIndex& index); void updateFunctionList(bool whenEmpty = false); void onFuncListContextMenu(QPoint pos); @@ -102,9 +103,9 @@ private: BreakpointModel m_bpModel; ThreadModel m_threadModel; + StackModel m_stackModel; std::vector m_threadlistObjects; - EEThread m_activeThread; std::vector m_stacklistObjects; bool m_demangleFunctions = true; diff --git a/pcsx2-qt/Debugger/CpuWidget.ui b/pcsx2-qt/Debugger/CpuWidget.ui index 447c30d7c7..19f3d0f09d 100644 --- a/pcsx2-qt/Debugger/CpuWidget.ui +++ b/pcsx2-qt/Debugger/CpuWidget.ui @@ -457,7 +457,7 @@ - Call Stack + Active Call Stack @@ -476,7 +476,7 @@ 0 - + Qt::CustomContextMenu @@ -495,48 +495,6 @@ false - - 0 - - - 6 - - - true - - - false - - - - Entry - - - - - Name - - - - - PC - - - - - Opcode - - - - - SP - - - - - Frame Size - - diff --git a/pcsx2-qt/Debugger/Models/StackModel.cpp b/pcsx2-qt/Debugger/Models/StackModel.cpp new file mode 100644 index 0000000000..2f79ae0d98 --- /dev/null +++ b/pcsx2-qt/Debugger/Models/StackModel.cpp @@ -0,0 +1,126 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2023 PCSX2 Dev Team + * + * PCSX2 is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with PCSX2. + * If not, see . + */ + +#include "PrecompiledHeader.h" + +#include "StackModel.h" +#include "DebugTools/MipsStackWalk.h" +#include "DebugTools/BiosDebugData.h" +#include "QtUtils.h" + +StackModel::StackModel(DebugInterface& cpu, QObject* parent) + : QAbstractTableModel(parent) + , m_cpu(cpu) +{ +} + +int StackModel::rowCount(const QModelIndex&) const +{ + return m_stackFrames.size(); +} + +int StackModel::columnCount(const QModelIndex&) const +{ + return StackModel::COLUMN_COUNT; +} + +QVariant StackModel::data(const QModelIndex& index, int role) const +{ + if (role == Qt::DisplayRole) + { + const auto& stackFrame = m_stackFrames.at(index.row()); + + switch (index.column()) + { + case StackModel::ENTRY: + return QtUtils::FilledQStringFromValue(stackFrame.entry, 16); + case StackModel::ENTRY_LABEL: + return m_cpu.GetSymbolMap().GetLabelString(stackFrame.entry).c_str(); + case StackModel::PC: + return QtUtils::FilledQStringFromValue(stackFrame.pc, 16); + case StackModel::PC_OPCODE: + return m_cpu.disasm(stackFrame.pc, true).c_str(); + case StackModel::SP: + return QtUtils::FilledQStringFromValue(stackFrame.sp, 16); + case StackModel::SIZE: + return QString::number(stackFrame.stackSize); + } + } + else if (role == Qt::UserRole) + { + const auto& stackFrame = m_stackFrames.at(index.row()); + switch (index.column()) + { + case StackModel::ENTRY: + return stackFrame.entry; + case StackModel::ENTRY_LABEL: + return m_cpu.GetSymbolMap().GetLabelString(stackFrame.entry).c_str(); + case StackModel::PC: + return stackFrame.pc; + case StackModel::PC_OPCODE: + return m_cpu.disasm(stackFrame.pc, true).c_str(); + case StackModel::SP: + return stackFrame.sp; + case StackModel::SIZE: + return stackFrame.stackSize; + } + } + return QVariant(); +} + +QVariant StackModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role == Qt::DisplayRole && orientation == Qt::Horizontal) + { + switch (section) + { + case StackColumns::ENTRY: + return tr("ENTRY"); + case StackColumns::ENTRY_LABEL: + return tr("LABEL"); + case StackColumns::PC: + return tr("PC"); + case StackColumns::PC_OPCODE: + return tr("INSTRUCTION"); + case StackColumns::SP: + return tr("STACK POINTER"); + case StackColumns::SIZE: + return tr("SIZE"); + default: + return QVariant(); + } + } + return QVariant(); +} + +void StackModel::refreshData() +{ + if (m_cpu.getCpuType() == BREAKPOINT_IOP) + return; + + // Hopefully in the near future we can get a stack frame for + // each thread + beginResetModel(); + for (const auto& thread : getEEThreads()) + { + if (thread.data.status == THS_RUN) + { + m_stackFrames = MipsStackWalk::Walk(&m_cpu, m_cpu.getPC(), m_cpu.getRegister(0, 31), m_cpu.getRegister(0, 29), + thread.data.entry_init, thread.data.stack); + break; + } + } + endResetModel(); +} diff --git a/pcsx2-qt/Debugger/Models/StackModel.h b/pcsx2-qt/Debugger/Models/StackModel.h new file mode 100644 index 0000000000..df8bb535d0 --- /dev/null +++ b/pcsx2-qt/Debugger/Models/StackModel.h @@ -0,0 +1,51 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2023 PCSX2 Dev Team + * + * PCSX2 is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with PCSX2. + * If not, see . + */ + +#pragma once + +#include + +#include "DebugTools/DebugInterface.h" +#include "DebugTools/MipsStackWalk.h" + +class StackModel : public QAbstractTableModel +{ + Q_OBJECT + +public: + enum StackColumns : int + { + ENTRY = 0, + ENTRY_LABEL, + PC, + PC_OPCODE, + SP, + SIZE, + COLUMN_COUNT + }; + + explicit StackModel(DebugInterface& cpu, QObject* parent = nullptr); + + int rowCount(const QModelIndex& parent = QModelIndex()) const override; + int columnCount(const QModelIndex& parent = QModelIndex()) const override; + QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role) const override; + + void refreshData(); + +private: + DebugInterface& m_cpu; + std::vector m_stackFrames; +}; diff --git a/pcsx2-qt/Debugger/Models/ThreadModel.cpp b/pcsx2-qt/Debugger/Models/ThreadModel.cpp index 5cca4c5e03..a5754b7d19 100644 --- a/pcsx2-qt/Debugger/Models/ThreadModel.cpp +++ b/pcsx2-qt/Debugger/Models/ThreadModel.cpp @@ -28,10 +28,7 @@ ThreadModel::ThreadModel(DebugInterface& cpu, QObject* parent) int ThreadModel::rowCount(const QModelIndex&) const { if (m_cpu.getCpuType() == BREAKPOINT_EE) - { - Console.Warning("%d", getEEThreads().size()); return getEEThreads().size(); - } else return 0; } @@ -139,4 +136,4 @@ void ThreadModel::refreshData() { beginResetModel(); endResetModel(); -} \ No newline at end of file +} diff --git a/pcsx2-qt/Debugger/Models/ThreadModel.h b/pcsx2-qt/Debugger/Models/ThreadModel.h index f615878b86..7f34e13560 100644 --- a/pcsx2-qt/Debugger/Models/ThreadModel.h +++ b/pcsx2-qt/Debugger/Models/ThreadModel.h @@ -63,4 +63,4 @@ private: {WAIT_SEMA, tr("SEMAPHORE")}}; DebugInterface& m_cpu; -}; \ No newline at end of file +}; diff --git a/pcsx2-qt/pcsx2-qt.vcxproj b/pcsx2-qt/pcsx2-qt.vcxproj index fd6c4abe01..dee3c01bc6 100644 --- a/pcsx2-qt/pcsx2-qt.vcxproj +++ b/pcsx2-qt/pcsx2-qt.vcxproj @@ -150,6 +150,7 @@ + @@ -232,6 +233,7 @@ + @@ -284,6 +286,7 @@ + diff --git a/pcsx2-qt/pcsx2-qt.vcxproj.filters b/pcsx2-qt/pcsx2-qt.vcxproj.filters index e79d8c3b33..5d392fc6a3 100644 --- a/pcsx2-qt/pcsx2-qt.vcxproj.filters +++ b/pcsx2-qt/pcsx2-qt.vcxproj.filters @@ -283,6 +283,9 @@ Debugger\Models + + Debugger\Models + moc @@ -307,6 +310,9 @@ moc + + moc + @@ -450,6 +456,9 @@ Debugger\Models + + Debugger\Models +