From 8489e1fc7d1839ce8e3a261a48b38a2891b2ff1a Mon Sep 17 00:00:00 2001 From: mateusfavarin Date: Sun, 1 Aug 2021 03:23:03 -0300 Subject: [PATCH 01/34] Decrement bp counter when removing a bp from the list --- src/core/cpu_core.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/cpu_core.cpp b/src/core/cpu_core.cpp index ce0867e629..a10ed06158 100644 --- a/src/core/cpu_core.cpp +++ b/src/core/cpu_core.cpp @@ -1719,6 +1719,7 @@ bool RemoveBreakpoint(VirtualMemoryAddress address) g_host_interface->TranslateString("DebuggerMessage", "Removed breakpoint at 0x%08X."), address); s_breakpoints.erase(it); + s_breakpoint_counter--; UpdateDebugDispatcherFlag(); if (address == s_last_breakpoint_check_pc) From cb06da759ed0f6e797945cf12807c9372b2bad1f Mon Sep 17 00:00:00 2001 From: mateusfavarin Date: Sun, 1 Aug 2021 03:36:34 -0300 Subject: [PATCH 02/34] Fix default bp counter when cleared --- src/core/cpu_core.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/cpu_core.cpp b/src/core/cpu_core.cpp index a10ed06158..e0dfb9754b 100644 --- a/src/core/cpu_core.cpp +++ b/src/core/cpu_core.cpp @@ -1731,7 +1731,7 @@ bool RemoveBreakpoint(VirtualMemoryAddress address) void ClearBreakpoints() { s_breakpoints.clear(); - s_breakpoint_counter = 0; + s_breakpoint_counter = 1; s_last_breakpoint_check_pc = INVALID_BREAKPOINT_PC; UpdateDebugDispatcherFlag(); } From f98ccfdc29e32d8cca487447812684e208b4afbc Mon Sep 17 00:00:00 2001 From: mateusfavarin Date: Sun, 1 Aug 2021 04:07:25 -0300 Subject: [PATCH 03/34] Fix bp list UI not updating when clearing all bps --- src/duckstation-qt/debuggerwindow.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/duckstation-qt/debuggerwindow.cpp b/src/duckstation-qt/debuggerwindow.cpp index 8a0d708460..7eb35d07e6 100644 --- a/src/duckstation-qt/debuggerwindow.cpp +++ b/src/duckstation-qt/debuggerwindow.cpp @@ -170,6 +170,7 @@ void DebuggerWindow::onToggleBreakpointTriggered() void DebuggerWindow::onClearBreakpointsTriggered() { clearBreakpoints(); + refreshBreakpointList(); } void DebuggerWindow::onStepIntoActionTriggered() From d3d1a4182a71e9b107fed2801792576e7e0e7e4c Mon Sep 17 00:00:00 2001 From: mateusfavarin Date: Sun, 1 Aug 2021 04:51:53 -0300 Subject: [PATCH 04/34] Fix bp ids not updating when a bp is removed --- src/core/cpu_core.cpp | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/core/cpu_core.cpp b/src/core/cpu_core.cpp index e0dfb9754b..07a20ed51f 100644 --- a/src/core/cpu_core.cpp +++ b/src/core/cpu_core.cpp @@ -1710,15 +1710,29 @@ bool AddBreakpointWithCallback(VirtualMemoryAddress address, BreakpointCallback bool RemoveBreakpoint(VirtualMemoryAddress address) { - auto it = std::find_if(s_breakpoints.begin(), s_breakpoints.end(), - [address](const Breakpoint& bp) { return bp.address == address; }); - if (it == s_breakpoints.end()) + u32 id; + u32 count = static_cast(s_breakpoints.size()); + u32 index = count; + for (u32 i = 0; i < count; i++) + { + if (s_breakpoints[i].address == address) + { + id = s_breakpoints[i].number; + index = i; + break; + } + } + + if (index == count) return false; g_host_interface->ReportFormattedDebuggerMessage( g_host_interface->TranslateString("DebuggerMessage", "Removed breakpoint at 0x%08X."), address); - s_breakpoints.erase(it); + for (u32 i = index + 1; i < count; i++) + s_breakpoints[i].number--; + + s_breakpoints.erase(s_breakpoints.begin() + index); s_breakpoint_counter--; UpdateDebugDispatcherFlag(); From 12b29dd6fd676593513aebe85b3a05efa81299c2 Mon Sep 17 00:00:00 2001 From: mateusfavarin Date: Sun, 1 Aug 2021 21:44:38 -0300 Subject: [PATCH 05/34] Add conditional bp UI --- src/core/cpu_core.cpp | 2 - src/duckstation-qt/cheatmanagerdialog.cpp | 2 +- src/duckstation-qt/debuggerwindow.cpp | 6 +- src/duckstation-qt/qtutils.cpp | 78 ++++++++++++++++++++++- src/duckstation-qt/qtutils.h | 5 +- 5 files changed, 84 insertions(+), 9 deletions(-) diff --git a/src/core/cpu_core.cpp b/src/core/cpu_core.cpp index 07a20ed51f..6298057742 100644 --- a/src/core/cpu_core.cpp +++ b/src/core/cpu_core.cpp @@ -1710,14 +1710,12 @@ bool AddBreakpointWithCallback(VirtualMemoryAddress address, BreakpointCallback bool RemoveBreakpoint(VirtualMemoryAddress address) { - u32 id; u32 count = static_cast(s_breakpoints.size()); u32 index = count; for (u32 i = 0; i < count; i++) { if (s_breakpoints[i].address == address) { - id = s_breakpoints[i].number; index = i; break; } diff --git a/src/duckstation-qt/cheatmanagerdialog.cpp b/src/duckstation-qt/cheatmanagerdialog.cpp index a6eac41b9e..d4c426baa1 100644 --- a/src/duckstation-qt/cheatmanagerdialog.cpp +++ b/src/duckstation-qt/cheatmanagerdialog.cpp @@ -695,7 +695,7 @@ void CheatManagerDialog::addToWatchClicked() void CheatManagerDialog::addManualWatchAddressClicked() { - std::optional address = QtUtils::PromptForAddress(this, windowTitle(), tr("Enter manual address:"), false); + std::optional address = QtUtils::PromptForAddress(this, windowTitle(), false); if (!address.has_value()) return; diff --git a/src/duckstation-qt/debuggerwindow.cpp b/src/duckstation-qt/debuggerwindow.cpp index 7eb35d07e6..66d4a73553 100644 --- a/src/duckstation-qt/debuggerwindow.cpp +++ b/src/duckstation-qt/debuggerwindow.cpp @@ -104,7 +104,7 @@ void DebuggerWindow::onGoToPCTriggered() void DebuggerWindow::onGoToAddressTriggered() { std::optional address = - QtUtils::PromptForAddress(this, windowTitle(), tr("Enter code address:"), true); + QtUtils::PromptForAddress(this, windowTitle(), true); if (!address.has_value()) return; @@ -114,7 +114,7 @@ void DebuggerWindow::onGoToAddressTriggered() void DebuggerWindow::onDumpAddressTriggered() { std::optional address = - QtUtils::PromptForAddress(this, windowTitle(), tr("Enter memory address:"), false); + QtUtils::PromptForAddress(this, windowTitle(), false); if (!address.has_value()) return; @@ -145,7 +145,7 @@ void DebuggerWindow::onFollowAddressTriggered() void DebuggerWindow::onAddBreakpointTriggered() { std::optional address = - QtUtils::PromptForAddress(this, windowTitle(), tr("Enter code address:"), true); + QtUtils::PromptForDebugAddress(this, windowTitle()); if (!address.has_value()) return; diff --git a/src/duckstation-qt/qtutils.cpp b/src/duckstation-qt/qtutils.cpp index 94a2922d76..19ab739736 100644 --- a/src/duckstation-qt/qtutils.cpp +++ b/src/duckstation-qt/qtutils.cpp @@ -5,10 +5,14 @@ #include #include #include +#include #include #include +#include +#include #include #include +#include #include #include #include @@ -745,7 +749,7 @@ void FillComboBoxWithEmulationSpeeds(QComboBox* cb) } } -std::optional PromptForAddress(QWidget* parent, const QString& title, const QString& label, bool code) +std::optional PromptForAddress(QWidget* parent, const QString& title, bool code) { const QString address_str( QInputDialog::getText(parent, title, qApp->translate("DebuggerWindow", "Enter memory address:"))); @@ -759,7 +763,7 @@ std::optional PromptForAddress(QWidget* parent, const QString& title, else address = address_str.toUInt(&ok, 16); if (code) - address = address & 0xFFFFFFFC; // disassembly address should be divisible by 4 so make sure + address = address & 0xFFFFFFC0; // disassembly address should be divisible by 4 so make sure if (!ok) { @@ -772,4 +776,74 @@ std::optional PromptForAddress(QWidget* parent, const QString& title, return address; } +std::optional PromptForDebugAddress(QWidget* parent, const QString& title) +{ + QDialog* d = new QDialog(parent); + d->setWindowTitle("New Breakpoint"); + + QGridLayout* grid = new QGridLayout(); + + QLabel* address_label = new QLabel(parent); + address_label->setText("Enter memory address:"); + + QLineEdit* address_line = new QLineEdit(); + + QLabel* dbg_label = new QLabel(parent); + dbg_label->setText("Break when this address is:"); + + QCheckBox* is_read = new QCheckBox("Read"); + QCheckBox* is_write = new QCheckBox("Written"); + QCheckBox* is_changed = new QCheckBox("Changed"); + QCheckBox* is_exec = new QCheckBox("Executed"); + + QDialogButtonBox* button_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + + QObject::connect(button_box, SIGNAL(accepted()), d, SLOT(accept())); + QObject::connect(button_box, SIGNAL(rejected()), d, SLOT(reject())); + + grid->addWidget(address_label, 0, 0); + grid->addWidget(address_line, 1, 0); + grid->addWidget(dbg_label, 2, 0); + grid->addWidget(is_read, 3, 0); + grid->addWidget(is_write, 3, 1); + grid->addWidget(is_changed, 4, 0); + grid->addWidget(is_exec, 4, 1); + grid->addWidget(button_box, 5, 0); + + d->setLayout(grid); + int res = d->exec(); + + if (res == QDialog::Accepted) + { + if (is_read->checkState() == Qt::Unchecked && is_write->checkState() == Qt::Unchecked && + is_changed->checkState() == Qt::Unchecked && is_exec->checkState() == Qt::Unchecked) + { + is_exec->setCheckState(Qt::Checked); // if nothing was selected, assume it is an exec breakpoint + } + + QString address_str = address_line->text(); + bool ok; + uint address; + if (address_str.startsWith("0x")) + address = address_str.mid(2).toUInt(&ok, 16); + else + address = address_str.toUInt(&ok, 16); + if (is_exec->checkState() == Qt::Checked) + address = address & 0xFFFFFFFC; // disassembly address should be divisible by 4 so make sure + + if (!ok) + { + QMessageBox::critical( + parent, title, + qApp->translate("New Breakpoint", "Invalid address. It should be in hex (0x12345678 or 12345678)")); + return std::nullopt; + } + return address; + } + else + { + return std::nullopt; + } +} + } // namespace QtUtils diff --git a/src/duckstation-qt/qtutils.h b/src/duckstation-qt/qtutils.h index fb18a836db..81618367cc 100644 --- a/src/duckstation-qt/qtutils.h +++ b/src/duckstation-qt/qtutils.h @@ -72,6 +72,9 @@ void FillComboBoxWithMSAAModes(QComboBox* cb); void FillComboBoxWithEmulationSpeeds(QComboBox* cb); /// Prompts for an address in hex. -std::optional PromptForAddress(QWidget* parent, const QString& title, const QString& label, bool code); +std::optional PromptForAddress(QWidget* parent, const QString& title, bool code); + +/// Prompts for an address in hex, with debug conditions +std::optional PromptForDebugAddress(QWidget* parent, const QString& title); } // namespace QtUtils \ No newline at end of file From 5364eabe90c2e4a35fbae3334f86aeda479c3aa7 Mon Sep 17 00:00:00 2001 From: mateusfavarin Date: Sun, 1 Aug 2021 21:46:26 -0300 Subject: [PATCH 06/34] Fix typo --- src/duckstation-qt/qtutils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/duckstation-qt/qtutils.cpp b/src/duckstation-qt/qtutils.cpp index 19ab739736..d03334c4d9 100644 --- a/src/duckstation-qt/qtutils.cpp +++ b/src/duckstation-qt/qtutils.cpp @@ -763,7 +763,7 @@ std::optional PromptForAddress(QWidget* parent, const QString& title, else address = address_str.toUInt(&ok, 16); if (code) - address = address & 0xFFFFFFC0; // disassembly address should be divisible by 4 so make sure + address = address & 0xFFFFFFFC; // disassembly address should be divisible by 4 so make sure if (!ok) { From e66b633ca3984cb5ac969858baa3ded78a741d1f Mon Sep 17 00:00:00 2001 From: mateusfavarin Date: Mon, 2 Aug 2021 02:32:50 -0300 Subject: [PATCH 07/34] UI tweaks, add data structs that will encapsulate the new debugging logic --- src/core/types.h | 16 ++++++ src/duckstation-qt/debuggerwindow.cpp | 10 ++-- src/duckstation-qt/qtutils.cpp | 76 +++++++++++++++++++++------ src/duckstation-qt/qtutils.h | 3 +- 4 files changed, 82 insertions(+), 23 deletions(-) diff --git a/src/core/types.h b/src/core/types.h index d197ad62bf..f924d7ba7e 100644 --- a/src/core/types.h +++ b/src/core/types.h @@ -17,6 +17,22 @@ enum class MemoryAccessSize : u32 Word }; +struct DebugAddress +{ + VirtualMemoryAddress address; + u32 last_value; + MemoryAccessSize address_size; + u8 debug_type = 0; +}; + +enum DebugConditionType : u8 +{ + Read = 1, + Written = 2, + Changed = 4, + Executed = 8 +}; + using TickCount = s32; enum class ConsoleRegion diff --git a/src/duckstation-qt/debuggerwindow.cpp b/src/duckstation-qt/debuggerwindow.cpp index 66d4a73553..dd4d54a866 100644 --- a/src/duckstation-qt/debuggerwindow.cpp +++ b/src/duckstation-qt/debuggerwindow.cpp @@ -144,18 +144,18 @@ void DebuggerWindow::onFollowAddressTriggered() void DebuggerWindow::onAddBreakpointTriggered() { - std::optional address = - QtUtils::PromptForDebugAddress(this, windowTitle()); - if (!address.has_value()) + DebugAddress addr = QtUtils::PromptForDebugAddress(this, windowTitle()); + + if (!addr.debug_type) return; - if (CPU::HasBreakpointAtAddress(address.value())) + if (CPU::HasBreakpointAtAddress(addr.address)) { QMessageBox::critical(this, windowTitle(), tr("A breakpoint already exists at this address.")); return; } - toggleBreakpoint(address.value()); + toggleBreakpoint(addr.address); } void DebuggerWindow::onToggleBreakpointTriggered() diff --git a/src/duckstation-qt/qtutils.cpp b/src/duckstation-qt/qtutils.cpp index d03334c4d9..08a671e596 100644 --- a/src/duckstation-qt/qtutils.cpp +++ b/src/duckstation-qt/qtutils.cpp @@ -1,6 +1,7 @@ #include "qtutils.h" #include "common/byte_stream.h" #include "common/make_array.h" +#include "core/types.h" #include #include #include @@ -776,10 +777,12 @@ std::optional PromptForAddress(QWidget* parent, const QString& title, return address; } -std::optional PromptForDebugAddress(QWidget* parent, const QString& title) +DebugAddress PromptForDebugAddress(QWidget* parent, const QString& title) { - QDialog* d = new QDialog(parent); - d->setWindowTitle("New Breakpoint"); + DebugAddress ret; + + QDialog* display = new QDialog(parent); + display->setWindowTitle("New Breakpoint"); QGridLayout* grid = new QGridLayout(); @@ -788,30 +791,45 @@ std::optional PromptForDebugAddress(QWidget* parent, const QString& ti QLineEdit* address_line = new QLineEdit(); + QLabel* address_size_label = new QLabel(parent); + address_size_label->setText("Enter data size:"); + + QComboBox* address_size_box = new QComboBox(); + address_size_box->addItems(QStringList{"1 byte", "2 bytes", "4 bytes"}); + QLabel* dbg_label = new QLabel(parent); dbg_label->setText("Break when this address is:"); + QWidget* checkbox_display = new QWidget(); + QGridLayout* checkbox_grid = new QGridLayout(); + QCheckBox* is_read = new QCheckBox("Read"); QCheckBox* is_write = new QCheckBox("Written"); QCheckBox* is_changed = new QCheckBox("Changed"); QCheckBox* is_exec = new QCheckBox("Executed"); + checkbox_display->setLayout(checkbox_grid); + + checkbox_grid->addWidget(is_read, 0, 0); + checkbox_grid->addWidget(is_write, 0, 1); + checkbox_grid->addWidget(is_changed, 1, 0); + checkbox_grid->addWidget(is_exec, 1, 1); + QDialogButtonBox* button_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); - QObject::connect(button_box, SIGNAL(accepted()), d, SLOT(accept())); - QObject::connect(button_box, SIGNAL(rejected()), d, SLOT(reject())); + QObject::connect(button_box, SIGNAL(accepted()), display, SLOT(accept())); + QObject::connect(button_box, SIGNAL(rejected()), display, SLOT(reject())); grid->addWidget(address_label, 0, 0); grid->addWidget(address_line, 1, 0); - grid->addWidget(dbg_label, 2, 0); - grid->addWidget(is_read, 3, 0); - grid->addWidget(is_write, 3, 1); - grid->addWidget(is_changed, 4, 0); - grid->addWidget(is_exec, 4, 1); - grid->addWidget(button_box, 5, 0); - - d->setLayout(grid); - int res = d->exec(); + grid->addWidget(address_size_label, 2, 0); + grid->addWidget(address_size_box, 3, 0); + grid->addWidget(dbg_label, 4, 0); + grid->addWidget(checkbox_display, 5, 0, Qt::AlignLeft); + grid->addWidget(button_box, 7, 0, Qt::AlignCenter); + + display->setLayout(grid); + int res = display->exec(); if (res == QDialog::Accepted) { @@ -836,13 +854,37 @@ std::optional PromptForDebugAddress(QWidget* parent, const QString& ti QMessageBox::critical( parent, title, qApp->translate("New Breakpoint", "Invalid address. It should be in hex (0x12345678 or 12345678)")); - return std::nullopt; + return ret; } - return address; + + if (is_read->checkState() == Qt::Checked) + ret.debug_type = ret.debug_type | DebugConditionType::Read; + if (is_write->checkState() == Qt::Checked) + ret.debug_type = ret.debug_type | DebugConditionType::Written; + if (is_changed->checkState() == Qt::Checked) + ret.debug_type = ret.debug_type | DebugConditionType::Changed; + if (is_exec->checkState() == Qt::Checked) + ret.debug_type = ret.debug_type | DebugConditionType::Executed; + + switch (address_size_box->currentIndex()) + { + case 0: + ret.address_size = MemoryAccessSize::Byte; + break; + case 1: + ret.address_size = MemoryAccessSize::HalfWord; + break; + case 2: + ret.address_size = MemoryAccessSize::Word; + break; + } + + ret.address = address; + return ret; } else { - return std::nullopt; + return ret; } } diff --git a/src/duckstation-qt/qtutils.h b/src/duckstation-qt/qtutils.h index 81618367cc..e60f8a173c 100644 --- a/src/duckstation-qt/qtutils.h +++ b/src/duckstation-qt/qtutils.h @@ -1,4 +1,5 @@ #pragma once +#include #include #include #include @@ -75,6 +76,6 @@ void FillComboBoxWithEmulationSpeeds(QComboBox* cb); std::optional PromptForAddress(QWidget* parent, const QString& title, bool code); /// Prompts for an address in hex, with debug conditions -std::optional PromptForDebugAddress(QWidget* parent, const QString& title); +DebugAddress PromptForDebugAddress(QWidget* parent, const QString& title); } // namespace QtUtils \ No newline at end of file From 11e06ccdf44f1af1979944c6e2ff9238a7be9d01 Mon Sep 17 00:00:00 2001 From: mateusfavarin Date: Mon, 2 Aug 2021 11:04:41 -0300 Subject: [PATCH 08/34] Change address size field to be a line --- src/core/types.h | 3 +-- src/duckstation-qt/qtutils.cpp | 46 +++++++++++++++++----------------- 2 files changed, 24 insertions(+), 25 deletions(-) diff --git a/src/core/types.h b/src/core/types.h index f924d7ba7e..8bff4547eb 100644 --- a/src/core/types.h +++ b/src/core/types.h @@ -20,8 +20,7 @@ enum class MemoryAccessSize : u32 struct DebugAddress { VirtualMemoryAddress address; - u32 last_value; - MemoryAccessSize address_size; + u32 size; u8 debug_type = 0; }; diff --git a/src/duckstation-qt/qtutils.cpp b/src/duckstation-qt/qtutils.cpp index 08a671e596..8b9172288e 100644 --- a/src/duckstation-qt/qtutils.cpp +++ b/src/duckstation-qt/qtutils.cpp @@ -787,15 +787,14 @@ DebugAddress PromptForDebugAddress(QWidget* parent, const QString& title) QGridLayout* grid = new QGridLayout(); QLabel* address_label = new QLabel(parent); - address_label->setText("Enter memory address:"); + address_label->setText("Enter memory address (hex):"); QLineEdit* address_line = new QLineEdit(); QLabel* address_size_label = new QLabel(parent); address_size_label->setText("Enter data size:"); - QComboBox* address_size_box = new QComboBox(); - address_size_box->addItems(QStringList{"1 byte", "2 bytes", "4 bytes"}); + QLineEdit* address_size_line = new QLineEdit(); QLabel* dbg_label = new QLabel(parent); dbg_label->setText("Break when this address is:"); @@ -823,7 +822,7 @@ DebugAddress PromptForDebugAddress(QWidget* parent, const QString& title) grid->addWidget(address_label, 0, 0); grid->addWidget(address_line, 1, 0); grid->addWidget(address_size_label, 2, 0); - grid->addWidget(address_size_box, 3, 0); + grid->addWidget(address_size_line, 3, 0); grid->addWidget(dbg_label, 4, 0); grid->addWidget(checkbox_display, 5, 0, Qt::AlignLeft); grid->addWidget(button_box, 7, 0, Qt::AlignCenter); @@ -833,6 +832,22 @@ DebugAddress PromptForDebugAddress(QWidget* parent, const QString& title) if (res == QDialog::Accepted) { + bool ok; + QString size_str = address_size_line->text(); + u32 address_size; + if (size_str.startsWith("0x")) + address_size = size_str.mid(2).toUInt(&ok, 16); + else + address_size = size_str.toUInt(&ok); + + if (!ok) + { + QMessageBox::critical( + parent, title, + qApp->translate("New Breakpoint", "Invalid size. It should be in hex (0xF) or decimal (15).")); + return ret; + } + if (is_read->checkState() == Qt::Unchecked && is_write->checkState() == Qt::Unchecked && is_changed->checkState() == Qt::Unchecked && is_exec->checkState() == Qt::Unchecked) { @@ -840,8 +855,7 @@ DebugAddress PromptForDebugAddress(QWidget* parent, const QString& title) } QString address_str = address_line->text(); - bool ok; - uint address; + u32 address; if (address_str.startsWith("0x")) address = address_str.mid(2).toUInt(&ok, 16); else @@ -866,26 +880,12 @@ DebugAddress PromptForDebugAddress(QWidget* parent, const QString& title) if (is_exec->checkState() == Qt::Checked) ret.debug_type = ret.debug_type | DebugConditionType::Executed; - switch (address_size_box->currentIndex()) - { - case 0: - ret.address_size = MemoryAccessSize::Byte; - break; - case 1: - ret.address_size = MemoryAccessSize::HalfWord; - break; - case 2: - ret.address_size = MemoryAccessSize::Word; - break; - } - + ret.size = address_size; ret.address = address; return ret; } - else - { - return ret; - } + + return ret; } } // namespace QtUtils From e8d74418043f915dc8950fdbdc140606b5b91c3c Mon Sep 17 00:00:00 2001 From: mateusfavarin Date: Mon, 2 Aug 2021 17:22:53 -0300 Subject: [PATCH 09/34] Update data structure for s_breakpoints --- src/core/cpu_core.cpp | 57 +++++++++++++++------------ src/core/cpu_core.h | 8 ++-- src/core/gdb_protocol.cpp | 12 +++++- src/core/types.h | 2 +- src/duckstation-qt/debuggerwindow.cpp | 38 ++++++++++++------ src/duckstation-qt/debuggerwindow.h | 2 +- src/duckstation-qt/qtutils.cpp | 23 +++++++---- 7 files changed, 88 insertions(+), 54 deletions(-) diff --git a/src/core/cpu_core.cpp b/src/core/cpu_core.cpp index 6298057742..38b157435e 100644 --- a/src/core/cpu_core.cpp +++ b/src/core/cpu_core.cpp @@ -1650,7 +1650,7 @@ bool HasBreakpointAtAddress(VirtualMemoryAddress address) { for (const Breakpoint& bp : s_breakpoints) { - if (bp.address == address) + if (bp.dbg.address == address) return true; } @@ -1675,46 +1675,46 @@ BreakpointList GetBreakpointList(bool include_auto_clear, bool include_callbacks return bps; } -bool AddBreakpoint(VirtualMemoryAddress address, bool auto_clear, bool enabled) +bool AddBreakpoint(DebugAddress dbg, bool auto_clear, bool enabled) { - if (HasBreakpointAtAddress(address)) + if (HasBreakpointAtAddress(dbg.address)) return false; - Log_InfoPrintf("Adding breakpoint at %08X, auto clear = %u", address, static_cast(auto_clear)); + Log_InfoPrintf("Adding breakpoint at %08X, auto clear = %u", dbg.address, static_cast(auto_clear)); - Breakpoint bp{address, nullptr, auto_clear ? 0 : s_breakpoint_counter++, 0, auto_clear, enabled}; + Breakpoint bp{dbg, nullptr, auto_clear ? 0 : s_breakpoint_counter++, 0, auto_clear, enabled}; s_breakpoints.push_back(std::move(bp)); UpdateDebugDispatcherFlag(); if (!auto_clear) { g_host_interface->ReportFormattedDebuggerMessage( - g_host_interface->TranslateString("DebuggerMessage", "Added breakpoint at 0x%08X."), address); + g_host_interface->TranslateString("DebuggerMessage", "Added breakpoint at 0x%08X."), dbg.address); } return true; } -bool AddBreakpointWithCallback(VirtualMemoryAddress address, BreakpointCallback callback) +bool AddBreakpointWithCallback(DebugAddress dbg, BreakpointCallback callback) { - if (HasBreakpointAtAddress(address)) + if (HasBreakpointAtAddress(dbg.address)) return false; - Log_InfoPrintf("Adding breakpoint with callback at %08X", address); + Log_InfoPrintf("Adding breakpoint with callback at %08X", dbg.address); - Breakpoint bp{address, callback, 0, 0, false, true}; + Breakpoint bp{dbg, callback, 0, 0, false, true}; s_breakpoints.push_back(std::move(bp)); UpdateDebugDispatcherFlag(); return true; } -bool RemoveBreakpoint(VirtualMemoryAddress address) +bool RemoveBreakpoint(DebugAddress dbg) { u32 count = static_cast(s_breakpoints.size()); u32 index = count; for (u32 i = 0; i < count; i++) { - if (s_breakpoints[i].address == address) + if (s_breakpoints[i].dbg.address == dbg.address) { index = i; break; @@ -1725,7 +1725,7 @@ bool RemoveBreakpoint(VirtualMemoryAddress address) return false; g_host_interface->ReportFormattedDebuggerMessage( - g_host_interface->TranslateString("DebuggerMessage", "Removed breakpoint at 0x%08X."), address); + g_host_interface->TranslateString("DebuggerMessage", "Removed breakpoint at 0x%08X."), dbg.address); for (u32 i = index + 1; i < count; i++) s_breakpoints[i].number--; @@ -1734,7 +1734,7 @@ bool RemoveBreakpoint(VirtualMemoryAddress address) s_breakpoint_counter--; UpdateDebugDispatcherFlag(); - if (address == s_last_breakpoint_check_pc) + if (dbg.address == s_last_breakpoint_check_pc) s_last_breakpoint_check_pc = INVALID_BREAKPOINT_PC; return true; @@ -1750,13 +1750,16 @@ void ClearBreakpoints() bool AddStepOverBreakpoint() { - u32 bp_pc = g_state.regs.pc; + DebugAddress bp_pc; + bp_pc.address = g_state.regs.pc; + bp_pc.debug_type = DebugType::Executed; + bp_pc.size = 4; Instruction inst; - if (!SafeReadInstruction(bp_pc, &inst.bits)) + if (!SafeReadInstruction(bp_pc.address, &inst.bits)) return false; - bp_pc += sizeof(Instruction); + bp_pc.address += sizeof(Instruction); if (!IsCallInstruction(inst)) { @@ -1765,7 +1768,7 @@ bool AddStepOverBreakpoint() return false; } - if (!SafeReadInstruction(bp_pc, &inst.bits)) + if (!SafeReadInstruction(bp_pc.address, &inst.bits)) return false; if (IsBranchInstruction(inst)) @@ -1776,10 +1779,10 @@ bool AddStepOverBreakpoint() } // skip the delay slot - bp_pc += sizeof(Instruction); + bp_pc.address += sizeof(Instruction); g_host_interface->ReportFormattedDebuggerMessage( - g_host_interface->TranslateString("DebuggerMessage", "Stepping over to 0x%08X."), bp_pc); + g_host_interface->TranslateString("DebuggerMessage", "Stepping over to 0x%08X."), bp_pc.address); return AddBreakpoint(bp_pc, true); } @@ -1787,13 +1790,17 @@ bool AddStepOverBreakpoint() bool AddStepOutBreakpoint(u32 max_instructions_to_search) { // find the branch-to-ra instruction. - u32 ret_pc = g_state.regs.pc; + DebugAddress ret_pc; + ret_pc.address = g_state.regs.pc; + ret_pc.debug_type = DebugType::Executed; + ret_pc.size = 4; + for (u32 i = 0; i < max_instructions_to_search; i++) { - ret_pc += sizeof(Instruction); + ret_pc.address += sizeof(Instruction); Instruction inst; - if (!SafeReadInstruction(ret_pc, &inst.bits)) + if (!SafeReadInstruction(ret_pc.address, &inst.bits)) { g_host_interface->ReportFormattedDebuggerMessage( g_host_interface->TranslateString("DebuggerMessage", @@ -1805,7 +1812,7 @@ bool AddStepOutBreakpoint(u32 max_instructions_to_search) if (IsReturnInstruction(inst)) { g_host_interface->ReportFormattedDebuggerMessage( - g_host_interface->TranslateString("DebuggerMessage", "Stepping out to 0x%08X."), ret_pc); + g_host_interface->TranslateString("DebuggerMessage", "Stepping out to 0x%08X."), ret_pc.address); return AddBreakpoint(ret_pc, true); } @@ -1843,7 +1850,7 @@ ALWAYS_INLINE_RELEASE static bool BreakpointCheck() for (u32 i = 0; i < count;) { Breakpoint& bp = s_breakpoints[i]; - if (!bp.enabled || bp.address != pc) + if (!bp.enabled || bp.dbg.address != pc) { i++; continue; diff --git a/src/core/cpu_core.h b/src/core/cpu_core.h index 4595b085a3..fe707c6622 100644 --- a/src/core/cpu_core.h +++ b/src/core/cpu_core.h @@ -167,7 +167,7 @@ using BreakpointCallback = bool (*)(VirtualMemoryAddress address); struct Breakpoint { - VirtualMemoryAddress address; + DebugAddress dbg; BreakpointCallback callback; u32 number; u32 hit_count; @@ -181,9 +181,9 @@ using BreakpointList = std::vector; bool HasAnyBreakpoints(); bool HasBreakpointAtAddress(VirtualMemoryAddress address); BreakpointList GetBreakpointList(bool include_auto_clear = false, bool include_callbacks = false); -bool AddBreakpoint(VirtualMemoryAddress address, bool auto_clear = false, bool enabled = true); -bool AddBreakpointWithCallback(VirtualMemoryAddress address, BreakpointCallback callback); -bool RemoveBreakpoint(VirtualMemoryAddress address); +bool AddBreakpoint(DebugAddress dbg, bool auto_clear = false, bool enabled = true); +bool AddBreakpointWithCallback(DebugAddress dbg, BreakpointCallback callback); +bool RemoveBreakpoint(DebugAddress dbg); void ClearBreakpoints(); bool AddStepOverBreakpoint(); bool AddStepOutBreakpoint(u32 max_instructions_to_search = 1000); diff --git a/src/core/gdb_protocol.cpp b/src/core/gdb_protocol.cpp index 309981ee4d..2d54fc321e 100644 --- a/src/core/gdb_protocol.cpp +++ b/src/core/gdb_protocol.cpp @@ -213,7 +213,11 @@ static std::optional Cmd$z1(const std::string_view& data) { auto address = StringUtil::FromChars(data, 16); if (address) { - CPU::RemoveBreakpoint(*address); + DebugAddress dbg; + dbg.address = *address; + dbg.debug_type = DebugType::Executed; + dbg.size = 4; + CPU::RemoveBreakpoint(dbg); return { "OK" }; } else { @@ -226,7 +230,11 @@ static std::optional Cmd$Z1(const std::string_view& data) { auto address = StringUtil::FromChars(data, 16); if (address) { - CPU::AddBreakpoint(*address, false); + DebugAddress dbg; + dbg.address = *address; + dbg.debug_type = DebugType::Executed; + dbg.size = 4; + CPU::AddBreakpoint(dbg, false); return { "OK" }; } else { diff --git a/src/core/types.h b/src/core/types.h index 8bff4547eb..f4228ed026 100644 --- a/src/core/types.h +++ b/src/core/types.h @@ -24,7 +24,7 @@ struct DebugAddress u8 debug_type = 0; }; -enum DebugConditionType : u8 +enum DebugType : u8 { Read = 1, Written = 2, diff --git a/src/duckstation-qt/debuggerwindow.cpp b/src/duckstation-qt/debuggerwindow.cpp index dd4d54a866..2efc87fac2 100644 --- a/src/duckstation-qt/debuggerwindow.cpp +++ b/src/duckstation-qt/debuggerwindow.cpp @@ -92,7 +92,11 @@ void DebuggerWindow::onRunToCursorTriggered() return; } - CPU::AddBreakpoint(addr.value(), true, true); + DebugAddress dbg; + dbg.address = addr.value(); + dbg.debug_type = DebugType::Executed; + dbg.size = 4; + CPU::AddBreakpoint(dbg, true, true); QtHostInterface::GetInstance()->pauseSystem(false); } @@ -144,18 +148,18 @@ void DebuggerWindow::onFollowAddressTriggered() void DebuggerWindow::onAddBreakpointTriggered() { - DebugAddress addr = QtUtils::PromptForDebugAddress(this, windowTitle()); + DebugAddress dbg = QtUtils::PromptForDebugAddress(this, windowTitle()); - if (!addr.debug_type) + if (!dbg.debug_type) return; - if (CPU::HasBreakpointAtAddress(addr.address)) + if (CPU::HasBreakpointAtAddress(dbg.address)) { QMessageBox::critical(this, windowTitle(), tr("A breakpoint already exists at this address.")); return; } - toggleBreakpoint(addr.address); + toggleBreakpoint(dbg); } void DebuggerWindow::onToggleBreakpointTriggered() @@ -164,7 +168,11 @@ void DebuggerWindow::onToggleBreakpointTriggered() if (!address.has_value()) return; - toggleBreakpoint(address.value()); + DebugAddress dbg; + dbg.address = address.value(); + dbg.debug_type = DebugType::Executed; + dbg.size = 4; + toggleBreakpoint(dbg); } void DebuggerWindow::onClearBreakpointsTriggered() @@ -215,11 +223,15 @@ void DebuggerWindow::onCodeViewItemActivated(QModelIndex index) return; const VirtualMemoryAddress address = m_code_model->getAddressForIndex(index); + DebugAddress dbg; + dbg.address = address; + dbg.debug_type = DebugType::Executed; + dbg.size = 4; switch (index.column()) { case 0: // breakpoint case 3: // disassembly - toggleBreakpoint(address); + toggleBreakpoint(dbg); break; case 1: // address @@ -492,21 +504,21 @@ void DebuggerWindow::setMemoryViewRegion(Bus::MemoryRegion region) m_ui.memoryView->repaint(); } -void DebuggerWindow::toggleBreakpoint(VirtualMemoryAddress address) +void DebuggerWindow::toggleBreakpoint(DebugAddress dbg) { - const bool new_bp_state = !CPU::HasBreakpointAtAddress(address); + const bool new_bp_state = !CPU::HasBreakpointAtAddress(dbg.address); if (new_bp_state) { - if (!CPU::AddBreakpoint(address, false)) + if (!CPU::AddBreakpoint(dbg, false)) return; } else { - if (!CPU::RemoveBreakpoint(address)) + if (!CPU::RemoveBreakpoint(dbg)) return; } - m_code_model->setBreakpointState(address, new_bp_state); + m_code_model->setBreakpointState(dbg.address, new_bp_state); refreshBreakpointList(); } @@ -566,7 +578,7 @@ void DebuggerWindow::refreshBreakpointList() item->setFlags(item->flags() | Qt::ItemIsUserCheckable); item->setCheckState(0, bp.enabled ? Qt::Checked : Qt::Unchecked); item->setText(0, QString::asprintf("%u", bp.number)); - item->setText(1, QString::asprintf("0x%08X", bp.address)); + item->setText(1, QString::asprintf("0x%08X", bp.dbg.address)); item->setText(2, QString::asprintf("%u", bp.hit_count)); m_ui.breakpointsWidget->addTopLevelItem(item); } diff --git a/src/duckstation-qt/debuggerwindow.h b/src/duckstation-qt/debuggerwindow.h index e7486b7a29..17065cc0ad 100644 --- a/src/duckstation-qt/debuggerwindow.h +++ b/src/duckstation-qt/debuggerwindow.h @@ -62,7 +62,7 @@ private Q_SLOTS: void createModels(); void setUIEnabled(bool enabled); void setMemoryViewRegion(Bus::MemoryRegion region); - void toggleBreakpoint(VirtualMemoryAddress address); + void toggleBreakpoint(DebugAddress dbg); void clearBreakpoints(); std::optional getSelectedCodeAddress(); bool tryFollowLoadStore(VirtualMemoryAddress address); diff --git a/src/duckstation-qt/qtutils.cpp b/src/duckstation-qt/qtutils.cpp index 8b9172288e..8915970d1b 100644 --- a/src/duckstation-qt/qtutils.cpp +++ b/src/duckstation-qt/qtutils.cpp @@ -842,10 +842,17 @@ DebugAddress PromptForDebugAddress(QWidget* parent, const QString& title) if (!ok) { - QMessageBox::critical( - parent, title, - qApp->translate("New Breakpoint", "Invalid size. It should be in hex (0xF) or decimal (15).")); - return ret; + if (is_exec->checkState() == Qt::Checked) + { + ret.size = 4; // assume 4 bytes for execution breakpoints + } + else + { + QMessageBox::critical( + parent, title, + qApp->translate("New Breakpoint", "Invalid size. It should be in hex (0xF) or decimal (15).")); + return ret; + } } if (is_read->checkState() == Qt::Unchecked && is_write->checkState() == Qt::Unchecked && @@ -872,13 +879,13 @@ DebugAddress PromptForDebugAddress(QWidget* parent, const QString& title) } if (is_read->checkState() == Qt::Checked) - ret.debug_type = ret.debug_type | DebugConditionType::Read; + ret.debug_type = ret.debug_type | DebugType::Read; if (is_write->checkState() == Qt::Checked) - ret.debug_type = ret.debug_type | DebugConditionType::Written; + ret.debug_type = ret.debug_type | DebugType::Written; if (is_changed->checkState() == Qt::Checked) - ret.debug_type = ret.debug_type | DebugConditionType::Changed; + ret.debug_type = ret.debug_type | DebugType::Changed; if (is_exec->checkState() == Qt::Checked) - ret.debug_type = ret.debug_type | DebugConditionType::Executed; + ret.debug_type = ret.debug_type | DebugType::Executed; ret.size = address_size; ret.address = address; From 511e55c2f14eaf7b75089b0b1f0931a1eed1197e Mon Sep 17 00:00:00 2001 From: mateusfavarin Date: Tue, 3 Aug 2021 02:05:11 -0300 Subject: [PATCH 10/34] Implement condition breakpoints --- src/core/cpu_core.cpp | 184 ++++++++++++++++++++++++++++++--- src/duckstation-qt/qtutils.cpp | 70 ++++++++----- 2 files changed, 212 insertions(+), 42 deletions(-) diff --git a/src/core/cpu_core.cpp b/src/core/cpu_core.cpp index 38b157435e..0e9995cdc1 100644 --- a/src/core/cpu_core.cpp +++ b/src/core/cpu_core.cpp @@ -35,7 +35,8 @@ static bool s_trace_to_log = false; static constexpr u32 INVALID_BREAKPOINT_PC = UINT32_C(0xFFFFFFFF); static std::vector s_breakpoints; static u32 s_breakpoint_counter = 1; -static u32 s_last_breakpoint_check_pc = INVALID_BREAKPOINT_PC; +static u32 s_last_execution_breakpoint_check_pc = INVALID_BREAKPOINT_PC; +static u32 s_last_data_breakpoint_check_pc = INVALID_BREAKPOINT_PC; static bool s_single_step = false; bool IsTraceEnabled() @@ -95,7 +96,8 @@ void Initialize() g_state.use_debug_dispatcher = false; s_breakpoints.clear(); s_breakpoint_counter = 1; - s_last_breakpoint_check_pc = INVALID_BREAKPOINT_PC; + s_last_execution_breakpoint_check_pc = INVALID_BREAKPOINT_PC; + s_last_data_breakpoint_check_pc = INVALID_BREAKPOINT_PC; s_single_step = false; UpdateFastmemBase(); @@ -539,6 +541,67 @@ ALWAYS_INLINE_RELEASE void Cop0DataBreakpointCheck(VirtualMemoryAddress address) DispatchCop0Breakpoint(); } +template +ALWAYS_INLINE_RELEASE void DataBreakpointCheck(VirtualMemoryAddress address, bool changed=false) +{ + if (System::IsPaused()) + return; + + if constexpr (type == MemoryAccessType::Read) + { + u32 count = static_cast(s_breakpoints.size()); + for (u32 i = 0; i < count; i++) + { + Breakpoint& bp = s_breakpoints[i]; + if (!bp.enabled || !(bp.dbg.debug_type & DebugType::Read)) + continue; + + if (bp.dbg.address <= address && bp.dbg.address + bp.dbg.size > address) + { + bp.hit_count++; + g_host_interface->ReportFormattedDebuggerMessage("Hit read breakpoint %u at 0x%08X.", bp.number, g_state.regs.pc); + g_host_interface->PauseSystem(true); + return; + } + } + } + else + { + if constexpr (type == MemoryAccessType::Write) + { + u32 count = static_cast(s_breakpoints.size()); + for (u32 i = 0; i < count; i++) + { + Breakpoint& bp = s_breakpoints[i]; + if (!bp.enabled) + continue; + if (bp.dbg.debug_type & DebugType::Written) + { + if (bp.dbg.address <= address && bp.dbg.address + bp.dbg.size > address) + { + bp.hit_count++; + g_host_interface->ReportFormattedDebuggerMessage("Hit write breakpoint %u at 0x%08X.", bp.number, + g_state.regs.pc); + g_host_interface->PauseSystem(true); + return; + } + } + if ((bp.dbg.debug_type & DebugType::Changed) && (changed)) + { + if (bp.dbg.address <= address && bp.dbg.address + bp.dbg.size > address) + { + bp.hit_count++; + g_host_interface->ReportFormattedDebuggerMessage("Hit changed breakpoint %u at 0x%08X %08X %08X.", bp.number, + g_state.regs.pc); + g_host_interface->PauseSystem(true); + return; + } + } + } + } + } +} + static void TracePrintInstruction() { const u32 pc = g_state.current_instruction_pc; @@ -627,6 +690,91 @@ void DisassembleAndPrint(u32 addr, u32 instructions_before /* = 0 */, u32 instru } } +ALWAYS_INLINE_RELEASE static bool ConditionalBreakpointLookAhead(u32 inst_bits) +{ + const u32 pc = g_state.regs.pc; + Instruction inst = g_state.current_instruction; + inst.bits = inst_bits; + + if (System::IsPaused() || pc == s_last_data_breakpoint_check_pc) + return false; + + // Skip nops. Makes PGXP-CPU quicker, but also the regular interpreter. + if (inst.bits == 0) + return false; + + switch (inst.op) + { + case InstructionOp::lb: + case InstructionOp::lbu: + { + const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32(); + DataBreakpointCheck(addr); + } + break; + case InstructionOp::lh: + case InstructionOp::lhu: + { + const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32(); + DataBreakpointCheck(addr); + DataBreakpointCheck(addr + 1); + } + break; + case InstructionOp::lw: + case InstructionOp::lwl: + case InstructionOp::lwr: + { + const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32(); + DataBreakpointCheck(addr); + DataBreakpointCheck(addr + 1); + DataBreakpointCheck(addr + 2); + DataBreakpointCheck(addr + 3); + } + break; + case InstructionOp::sb: + { + const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32(); + u8 old_value; + if (!ReadMemoryByte(addr, &old_value)) + return false; + u8 new_value = Truncate8(ReadReg(inst.i.rt)); + DataBreakpointCheck(addr, old_value != new_value); + } + break; + case InstructionOp::sh: + { + const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32(); + u16 old_value; + if (!ReadMemoryHalfWord(addr, &old_value)) + return false; + u16 new_value = Truncate16(ReadReg(inst.i.rt)); + DataBreakpointCheck(addr, (old_value & 0xFF) != (new_value & 0xFF)); + DataBreakpointCheck(addr + 1, (old_value & 0xFF00) != (new_value & 0xFF00)); + } + break; + case InstructionOp::sw: + { + const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32(); + u32 old_value; + if (!ReadMemoryWord(addr, &old_value)) + return false; + u32 new_value = ReadReg(inst.i.rt); + DataBreakpointCheck(addr, (old_value & 0xFF) != (new_value & 0xFF)); + DataBreakpointCheck(addr + 1, (old_value & 0xFF00) != (new_value & 0xFF00)); + DataBreakpointCheck(addr + 2, (old_value & 0xFF0000) != (new_value & 0xFF0000)); + DataBreakpointCheck(addr + 3, (old_value & 0xFF000000) != (new_value & 0xFF000000)); + } + break; + case InstructionOp::swl: + case InstructionOp::swr: + // + break; + } + + s_last_data_breakpoint_check_pc = pc; + return System::IsPaused(); +} + template ALWAYS_INLINE_RELEASE static void ExecuteInstruction() { @@ -1734,8 +1882,11 @@ bool RemoveBreakpoint(DebugAddress dbg) s_breakpoint_counter--; UpdateDebugDispatcherFlag(); - if (dbg.address == s_last_breakpoint_check_pc) - s_last_breakpoint_check_pc = INVALID_BREAKPOINT_PC; + if (dbg.address == s_last_execution_breakpoint_check_pc) + s_last_execution_breakpoint_check_pc = INVALID_BREAKPOINT_PC; + + if (dbg.address == s_last_data_breakpoint_check_pc) + s_last_data_breakpoint_check_pc = INVALID_BREAKPOINT_PC; return true; } @@ -1744,7 +1895,8 @@ void ClearBreakpoints() { s_breakpoints.clear(); s_breakpoint_counter = 1; - s_last_breakpoint_check_pc = INVALID_BREAKPOINT_PC; + s_last_execution_breakpoint_check_pc = INVALID_BREAKPOINT_PC; + s_last_data_breakpoint_check_pc = INVALID_BREAKPOINT_PC; UpdateDebugDispatcherFlag(); } @@ -1826,7 +1978,7 @@ bool AddStepOutBreakpoint(u32 max_instructions_to_search) return false; } -ALWAYS_INLINE_RELEASE static bool BreakpointCheck() +ALWAYS_INLINE_RELEASE static bool ExecutionBreakpointCheck() { const u32 pc = g_state.regs.pc; @@ -1836,11 +1988,11 @@ ALWAYS_INLINE_RELEASE static bool BreakpointCheck() { ForceDispatcherExit(); s_single_step = false; - s_last_breakpoint_check_pc = pc; + s_last_execution_breakpoint_check_pc = pc; return false; } - if (pc == s_last_breakpoint_check_pc) + if (pc == s_last_execution_breakpoint_check_pc) { // we don't want to trigger the same breakpoint which just paused us repeatedly. return false; @@ -1850,7 +2002,7 @@ ALWAYS_INLINE_RELEASE static bool BreakpointCheck() for (u32 i = 0; i < count;) { Breakpoint& bp = s_breakpoints[i]; - if (!bp.enabled || bp.dbg.address != pc) + if (!bp.enabled || !(bp.dbg.debug_type & DebugType::Executed) || bp.dbg.address != pc) { i++; continue; @@ -1885,13 +2037,13 @@ ALWAYS_INLINE_RELEASE static bool BreakpointCheck() } else { - g_host_interface->ReportFormattedDebuggerMessage("Hit breakpoint %u at 0x%08X.", bp.number, pc); + g_host_interface->ReportFormattedDebuggerMessage("Hit exec breakpoint %u at 0x%08X.", bp.number, pc); i++; } } } - s_last_breakpoint_check_pc = pc; + s_last_execution_breakpoint_check_pc = pc; return System::IsPaused(); } @@ -1913,11 +2065,11 @@ static void ExecuteImpl() { Cop0ExecutionBreakpointCheck(); - if (BreakpointCheck()) - { - // continue is measurably faster than break on msvc for some reason - continue; - } + if (ExecutionBreakpointCheck()) + continue; // continue is measurably faster than break on msvc for some reason + + if (ConditionalBreakpointLookAhead(g_state.next_instruction.bits)) + continue; // continue is measurably faster than break on msvc for some reason } g_state.interrupt_delay = false; diff --git a/src/duckstation-qt/qtutils.cpp b/src/duckstation-qt/qtutils.cpp index 8915970d1b..958306685e 100644 --- a/src/duckstation-qt/qtutils.cpp +++ b/src/duckstation-qt/qtutils.cpp @@ -797,7 +797,7 @@ DebugAddress PromptForDebugAddress(QWidget* parent, const QString& title) QLineEdit* address_size_line = new QLineEdit(); QLabel* dbg_label = new QLabel(parent); - dbg_label->setText("Break when this address is:"); + dbg_label->setText("Break when this memory is:"); QWidget* checkbox_display = new QWidget(); QGridLayout* checkbox_grid = new QGridLayout(); @@ -832,35 +832,13 @@ DebugAddress PromptForDebugAddress(QWidget* parent, const QString& title) if (res == QDialog::Accepted) { - bool ok; - QString size_str = address_size_line->text(); - u32 address_size; - if (size_str.startsWith("0x")) - address_size = size_str.mid(2).toUInt(&ok, 16); - else - address_size = size_str.toUInt(&ok); - - if (!ok) - { - if (is_exec->checkState() == Qt::Checked) - { - ret.size = 4; // assume 4 bytes for execution breakpoints - } - else - { - QMessageBox::critical( - parent, title, - qApp->translate("New Breakpoint", "Invalid size. It should be in hex (0xF) or decimal (15).")); - return ret; - } - } - if (is_read->checkState() == Qt::Unchecked && is_write->checkState() == Qt::Unchecked && is_changed->checkState() == Qt::Unchecked && is_exec->checkState() == Qt::Unchecked) { is_exec->setCheckState(Qt::Checked); // if nothing was selected, assume it is an exec breakpoint } + bool ok; QString address_str = address_line->text(); u32 address; if (address_str.startsWith("0x")) @@ -878,6 +856,8 @@ DebugAddress PromptForDebugAddress(QWidget* parent, const QString& title) return ret; } + ret.address = address; + if (is_read->checkState() == Qt::Checked) ret.debug_type = ret.debug_type | DebugType::Read; if (is_write->checkState() == Qt::Checked) @@ -887,8 +867,46 @@ DebugAddress PromptForDebugAddress(QWidget* parent, const QString& title) if (is_exec->checkState() == Qt::Checked) ret.debug_type = ret.debug_type | DebugType::Executed; - ret.size = address_size; - ret.address = address; + QString size_str = address_size_line->text(); + u32 address_size; + if (size_str.startsWith("0x")) + address_size = size_str.mid(2).toUInt(&ok, 16); + else + address_size = size_str.toUInt(&ok); + + if (!ok) + { + if (is_exec->checkState() == Qt::Checked) + { + ret.size = 4; // assume 4 bytes for execution breakpoints + } + else + { + if (size_str.size() == 0) + { + // assume the user is using the greatest possible size among word, halfword and byte + if (!(address & 0x3)) + ret.size = 4; + else if (!(address & 0x1)) + ret.size = 2; + else + ret.size = 1; + } + else + { + QMessageBox::critical( + parent, title, + qApp->translate("New Breakpoint", "Invalid size. It should be in hex (0xF) or decimal (15).")); + ret.debug_type = 0; + return ret; + } + } + } + else + { + ret.size = address_size; + } + return ret; } From a83f259434779604192eb05a3b6503b7334dd4c1 Mon Sep 17 00:00:00 2001 From: mateusfavarin Date: Tue, 3 Aug 2021 02:23:45 -0300 Subject: [PATCH 11/34] Add default textline values for the DebugPrompt --- src/core/cpu_core.cpp | 2 +- src/duckstation-qt/debuggerwindow.cpp | 2 +- src/duckstation-qt/qtutils.cpp | 5 ++++- src/duckstation-qt/qtutils.h | 2 +- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/core/cpu_core.cpp b/src/core/cpu_core.cpp index 0e9995cdc1..ae606a0e41 100644 --- a/src/core/cpu_core.cpp +++ b/src/core/cpu_core.cpp @@ -591,7 +591,7 @@ ALWAYS_INLINE_RELEASE void DataBreakpointCheck(VirtualMemoryAddress address, boo if (bp.dbg.address <= address && bp.dbg.address + bp.dbg.size > address) { bp.hit_count++; - g_host_interface->ReportFormattedDebuggerMessage("Hit changed breakpoint %u at 0x%08X %08X %08X.", bp.number, + g_host_interface->ReportFormattedDebuggerMessage("Hit changed breakpoint %u at 0x%08X.", bp.number, g_state.regs.pc); g_host_interface->PauseSystem(true); return; diff --git a/src/duckstation-qt/debuggerwindow.cpp b/src/duckstation-qt/debuggerwindow.cpp index 2efc87fac2..586c4d51df 100644 --- a/src/duckstation-qt/debuggerwindow.cpp +++ b/src/duckstation-qt/debuggerwindow.cpp @@ -148,7 +148,7 @@ void DebuggerWindow::onFollowAddressTriggered() void DebuggerWindow::onAddBreakpointTriggered() { - DebugAddress dbg = QtUtils::PromptForDebugAddress(this, windowTitle()); + DebugAddress dbg = QtUtils::PromptForDebugAddress(this, windowTitle(), "", ""); if (!dbg.debug_type) return; diff --git a/src/duckstation-qt/qtutils.cpp b/src/duckstation-qt/qtutils.cpp index 958306685e..def7e8d9d9 100644 --- a/src/duckstation-qt/qtutils.cpp +++ b/src/duckstation-qt/qtutils.cpp @@ -777,7 +777,8 @@ std::optional PromptForAddress(QWidget* parent, const QString& title, return address; } -DebugAddress PromptForDebugAddress(QWidget* parent, const QString& title) +DebugAddress PromptForDebugAddress(QWidget* parent, const QString& title, const QString& default_address, + const QString& default_size) { DebugAddress ret; @@ -790,11 +791,13 @@ DebugAddress PromptForDebugAddress(QWidget* parent, const QString& title) address_label->setText("Enter memory address (hex):"); QLineEdit* address_line = new QLineEdit(); + address_line->setText(default_address); QLabel* address_size_label = new QLabel(parent); address_size_label->setText("Enter data size:"); QLineEdit* address_size_line = new QLineEdit(); + address_size_line->setText(default_size); QLabel* dbg_label = new QLabel(parent); dbg_label->setText("Break when this memory is:"); diff --git a/src/duckstation-qt/qtutils.h b/src/duckstation-qt/qtutils.h index e60f8a173c..4bdf39def7 100644 --- a/src/duckstation-qt/qtutils.h +++ b/src/duckstation-qt/qtutils.h @@ -76,6 +76,6 @@ void FillComboBoxWithEmulationSpeeds(QComboBox* cb); std::optional PromptForAddress(QWidget* parent, const QString& title, bool code); /// Prompts for an address in hex, with debug conditions -DebugAddress PromptForDebugAddress(QWidget* parent, const QString& title); +DebugAddress PromptForDebugAddress(QWidget* parent, const QString& title, const QString& default_address, const QString& default_size); } // namespace QtUtils \ No newline at end of file From f511bcc32d4f0f07095438e655a270332d865cf4 Mon Sep 17 00:00:00 2001 From: mateusfavarin Date: Tue, 3 Aug 2021 04:05:27 -0300 Subject: [PATCH 12/34] Fix bp not disabling via checkbox --- src/core/cpu_core.cpp | 5 +++++ src/core/cpu_core.h | 1 + src/duckstation-qt/debuggerwindow.cpp | 19 ++++++++++++++++++- src/duckstation-qt/debuggerwindow.h | 1 + 4 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/core/cpu_core.cpp b/src/core/cpu_core.cpp index ae606a0e41..aba2b5d80e 100644 --- a/src/core/cpu_core.cpp +++ b/src/core/cpu_core.cpp @@ -1823,6 +1823,11 @@ BreakpointList GetBreakpointList(bool include_auto_clear, bool include_callbacks return bps; } +void SetBreakpointEnable(int index, bool is_enable) +{ + s_breakpoints[index].enabled = is_enable; +} + bool AddBreakpoint(DebugAddress dbg, bool auto_clear, bool enabled) { if (HasBreakpointAtAddress(dbg.address)) diff --git a/src/core/cpu_core.h b/src/core/cpu_core.h index fe707c6622..a766523ee7 100644 --- a/src/core/cpu_core.h +++ b/src/core/cpu_core.h @@ -187,6 +187,7 @@ bool RemoveBreakpoint(DebugAddress dbg); void ClearBreakpoints(); bool AddStepOverBreakpoint(); bool AddStepOutBreakpoint(u32 max_instructions_to_search = 1000); +void SetBreakpointEnable(int index, bool is_enable); extern bool TRACE_EXECUTION; diff --git a/src/duckstation-qt/debuggerwindow.cpp b/src/duckstation-qt/debuggerwindow.cpp index 586c4d51df..d23d1289d6 100644 --- a/src/duckstation-qt/debuggerwindow.cpp +++ b/src/duckstation-qt/debuggerwindow.cpp @@ -245,6 +245,22 @@ void DebuggerWindow::onCodeViewItemActivated(QModelIndex index) } } +void DebuggerWindow::onBreakpointsWidgetItemChanged(QTreeWidgetItem* item, int column) +{ + if (column == 0) + { + bool ok; + int index = item->text(0).toInt(&ok); + if (ok) + { + if (item->checkState(column) == Qt::Unchecked) + CPU::SetBreakpointEnable(index - 1, false); + else + CPU::SetBreakpointEnable(index - 1, true); + } + } +} + void DebuggerWindow::onMemorySearchTriggered() { m_ui.memoryView->clearHighlightRange(); @@ -410,6 +426,7 @@ void DebuggerWindow::connectSignals() connect(m_ui.actionClearBreakpoints, &QAction::triggered, this, &DebuggerWindow::onClearBreakpointsTriggered); connect(m_ui.actionClose, &QAction::triggered, this, &DebuggerWindow::close); connect(m_ui.codeView, &QTreeView::activated, this, &DebuggerWindow::onCodeViewItemActivated); + connect(m_ui.breakpointsWidget, &QTreeWidget::itemChanged, this, &DebuggerWindow::onBreakpointsWidgetItemChanged); connect(m_ui.memoryRegionRAM, &QRadioButton::clicked, [this]() { setMemoryViewRegion(Bus::MemoryRegion::RAM); }); connect(m_ui.memoryRegionEXP1, &QRadioButton::clicked, [this]() { setMemoryViewRegion(Bus::MemoryRegion::EXP1); }); @@ -575,7 +592,7 @@ void DebuggerWindow::refreshBreakpointList() for (const CPU::Breakpoint& bp : bps) { QTreeWidgetItem* item = new QTreeWidgetItem(); - item->setFlags(item->flags() | Qt::ItemIsUserCheckable); + item->setFlags(item->flags() | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable); item->setCheckState(0, bp.enabled ? Qt::Checked : Qt::Unchecked); item->setText(0, QString::asprintf("%u", bp.number)); item->setText(1, QString::asprintf("0x%08X", bp.dbg.address)); diff --git a/src/duckstation-qt/debuggerwindow.h b/src/duckstation-qt/debuggerwindow.h index 17065cc0ad..cb70b43d88 100644 --- a/src/duckstation-qt/debuggerwindow.h +++ b/src/duckstation-qt/debuggerwindow.h @@ -53,6 +53,7 @@ private Q_SLOTS: void onCodeViewItemActivated(QModelIndex index); void onMemorySearchTriggered(); void onMemorySearchStringChanged(const QString&); + void onBreakpointsWidgetItemChanged(QTreeWidgetItem* item, int column); private: From 411852d3bf6c519e77018c5dcecffedb140fd4f8 Mon Sep 17 00:00:00 2001 From: mateusfavarin Date: Tue, 3 Aug 2021 16:01:26 -0300 Subject: [PATCH 13/34] Change instruction format to hex always use Hex for consistency --- src/core/cpu_disasm.cpp | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/core/cpu_disasm.cpp b/src/core/cpu_disasm.cpp index 6caacc409a..c117d05384 100644 --- a/src/core/cpu_disasm.cpp +++ b/src/core/cpu_disasm.cpp @@ -170,6 +170,12 @@ static void FormatInstruction(String* dest, const Instruction inst, u32 pc, cons { dest->Clear(); + if (inst.bits == 0) + { + dest->AppendString("nop"); + return; + } + const char* str = format; while (*str != '\0') { @@ -197,36 +203,43 @@ static void FormatInstruction(String* dest, const Instruction inst, u32 pc, cons } else if (std::strncmp(str, "shamt", 5) == 0) { - dest->AppendFormattedString("%d", ZeroExtend32(inst.r.shamt.GetValue())); + const s32 shamt = static_cast(ZeroExtend32(inst.r.shamt.GetValue())); + if (shamt < 0) + dest->AppendFormattedString("-%x", shamt * -1); + else + dest->AppendFormattedString("%x", shamt); str += 5; } else if (std::strncmp(str, "immu", 4) == 0) { - dest->AppendFormattedString("%u", inst.i.imm_zext32()); + dest->AppendFormattedString("0x%04x", inst.i.imm_zext32()); str += 4; } else if (std::strncmp(str, "imm", 3) == 0) { // dest->AppendFormattedString("%d", static_cast(inst.i.imm_sext32())); - dest->AppendFormattedString("%04x", inst.i.imm_zext32()); + dest->AppendFormattedString("0x%04x", inst.i.imm_zext32()); str += 3; } else if (std::strncmp(str, "rel", 3) == 0) { const u32 target = (pc + UINT32_C(4)) + (inst.i.imm_sext32() << 2); - dest->AppendFormattedString("%08x", target); + dest->AppendFormattedString("0x%08x", target); str += 3; } else if (std::strncmp(str, "offsetrs", 8) == 0) { const s32 offset = static_cast(inst.i.imm_sext32()); - dest->AppendFormattedString("%d(%s)", offset, GetRegName(inst.i.rs)); + if (offset < 0) + dest->AppendFormattedString("-0x%x(%s)", offset * -1, GetRegName(inst.i.rs)); + else + dest->AppendFormattedString("0x%x(%s)", offset, GetRegName(inst.i.rs)); str += 8; } else if (std::strncmp(str, "jt", 2) == 0) { const u32 target = ((pc + UINT32_C(4)) & UINT32_C(0xF0000000)) | (inst.j.target << 2); - dest->AppendFormattedString("%08x", target); + dest->AppendFormattedString("0x%08x", target); str += 2; } else if (std::strncmp(str, "copcc", 5) == 0) From a40286e197f2be8072e100f3f9cc863d679743df Mon Sep 17 00:00:00 2001 From: mateusfavarin Date: Tue, 3 Aug 2021 17:48:41 -0300 Subject: [PATCH 14/34] Add the possibility to edit current breakpoints --- src/core/cpu_core.cpp | 10 +++++++++ src/core/cpu_core.h | 2 ++ src/duckstation-qt/debuggerwindow.cpp | 29 +++++++++++++++++++++++++++ src/duckstation-qt/debuggerwindow.h | 1 + src/duckstation-qt/qtutils.cpp | 7 ++++++- src/duckstation-qt/qtutils.h | 5 ++++- 6 files changed, 52 insertions(+), 2 deletions(-) diff --git a/src/core/cpu_core.cpp b/src/core/cpu_core.cpp index aba2b5d80e..b8311c20f7 100644 --- a/src/core/cpu_core.cpp +++ b/src/core/cpu_core.cpp @@ -1828,6 +1828,16 @@ void SetBreakpointEnable(int index, bool is_enable) s_breakpoints[index].enabled = is_enable; } +DebugAddress GetBreakpointDebugAddress(int index) +{ + return s_breakpoints[index].dbg; +} + +void SetBreakpointDebugAddress(int index, DebugAddress dbg) +{ + s_breakpoints[index].dbg = dbg; +} + bool AddBreakpoint(DebugAddress dbg, bool auto_clear, bool enabled) { if (HasBreakpointAtAddress(dbg.address)) diff --git a/src/core/cpu_core.h b/src/core/cpu_core.h index a766523ee7..a0028fd959 100644 --- a/src/core/cpu_core.h +++ b/src/core/cpu_core.h @@ -188,6 +188,8 @@ void ClearBreakpoints(); bool AddStepOverBreakpoint(); bool AddStepOutBreakpoint(u32 max_instructions_to_search = 1000); void SetBreakpointEnable(int index, bool is_enable); +DebugAddress GetBreakpointDebugAddress(int index); +void SetBreakpointDebugAddress(int index, DebugAddress dbg); extern bool TRACE_EXECUTION; diff --git a/src/duckstation-qt/debuggerwindow.cpp b/src/duckstation-qt/debuggerwindow.cpp index d23d1289d6..893ac2be85 100644 --- a/src/duckstation-qt/debuggerwindow.cpp +++ b/src/duckstation-qt/debuggerwindow.cpp @@ -261,6 +261,33 @@ void DebuggerWindow::onBreakpointsWidgetItemChanged(QTreeWidgetItem* item, int c } } +void DebuggerWindow::onBreakpointWidgetItemDoubleClicked(QTreeWidgetItem* item, int column) +{ + bool ok; + int index = item->text(0).toInt(&ok); + if (ok) + { + DebugAddress curr_dbg = CPU::GetBreakpointDebugAddress(index - 1); + DebugAddress new_dbg = QtUtils::PromptForDebugAddress(this, windowTitle(), item->text(1).mid(2), QString::number(curr_dbg.size), + (curr_dbg.debug_type & DebugType::Read) ? Qt::Checked : Qt::Unchecked, + (curr_dbg.debug_type & DebugType::Written) ? Qt::Checked : Qt::Unchecked, + (curr_dbg.debug_type & DebugType::Changed) ? Qt::Checked : Qt::Unchecked, + (curr_dbg.debug_type & DebugType::Executed) ? Qt::Checked : Qt::Unchecked); + if (curr_dbg.address == new_dbg.address) + CPU::SetBreakpointDebugAddress(index - 1, new_dbg); + else + { + if (CPU::HasBreakpointAtAddress(new_dbg.address)) + QMessageBox::critical(this, windowTitle(), tr("A breakpoint already exists at this address.")); + else + { + CPU::AddBreakpoint(new_dbg); + refreshBreakpointList(); + } + } + } +} + void DebuggerWindow::onMemorySearchTriggered() { m_ui.memoryView->clearHighlightRange(); @@ -427,6 +454,8 @@ void DebuggerWindow::connectSignals() connect(m_ui.actionClose, &QAction::triggered, this, &DebuggerWindow::close); connect(m_ui.codeView, &QTreeView::activated, this, &DebuggerWindow::onCodeViewItemActivated); connect(m_ui.breakpointsWidget, &QTreeWidget::itemChanged, this, &DebuggerWindow::onBreakpointsWidgetItemChanged); + connect(m_ui.breakpointsWidget, &QTreeWidget::itemDoubleClicked, this, + &DebuggerWindow::onBreakpointWidgetItemDoubleClicked); connect(m_ui.memoryRegionRAM, &QRadioButton::clicked, [this]() { setMemoryViewRegion(Bus::MemoryRegion::RAM); }); connect(m_ui.memoryRegionEXP1, &QRadioButton::clicked, [this]() { setMemoryViewRegion(Bus::MemoryRegion::EXP1); }); diff --git a/src/duckstation-qt/debuggerwindow.h b/src/duckstation-qt/debuggerwindow.h index cb70b43d88..13808260b6 100644 --- a/src/duckstation-qt/debuggerwindow.h +++ b/src/duckstation-qt/debuggerwindow.h @@ -54,6 +54,7 @@ private Q_SLOTS: void onMemorySearchTriggered(); void onMemorySearchStringChanged(const QString&); void onBreakpointsWidgetItemChanged(QTreeWidgetItem* item, int column); + void onBreakpointWidgetItemDoubleClicked(QTreeWidgetItem* item, int column); private: diff --git a/src/duckstation-qt/qtutils.cpp b/src/duckstation-qt/qtutils.cpp index def7e8d9d9..14c31ca448 100644 --- a/src/duckstation-qt/qtutils.cpp +++ b/src/duckstation-qt/qtutils.cpp @@ -778,7 +778,8 @@ std::optional PromptForAddress(QWidget* parent, const QString& title, } DebugAddress PromptForDebugAddress(QWidget* parent, const QString& title, const QString& default_address, - const QString& default_size) + const QString& default_size, int default_read, int default_write, + int default_changed, int default_exec) { DebugAddress ret; @@ -806,9 +807,13 @@ DebugAddress PromptForDebugAddress(QWidget* parent, const QString& title, const QGridLayout* checkbox_grid = new QGridLayout(); QCheckBox* is_read = new QCheckBox("Read"); + is_read->setCheckState((Qt::CheckState) default_read); QCheckBox* is_write = new QCheckBox("Written"); + is_write->setCheckState((Qt::CheckState) default_write); QCheckBox* is_changed = new QCheckBox("Changed"); + is_changed->setCheckState((Qt::CheckState) default_changed); QCheckBox* is_exec = new QCheckBox("Executed"); + is_exec->setCheckState((Qt::CheckState) default_exec); checkbox_display->setLayout(checkbox_grid); diff --git a/src/duckstation-qt/qtutils.h b/src/duckstation-qt/qtutils.h index 4bdf39def7..52442841af 100644 --- a/src/duckstation-qt/qtutils.h +++ b/src/duckstation-qt/qtutils.h @@ -76,6 +76,9 @@ void FillComboBoxWithEmulationSpeeds(QComboBox* cb); std::optional PromptForAddress(QWidget* parent, const QString& title, bool code); /// Prompts for an address in hex, with debug conditions -DebugAddress PromptForDebugAddress(QWidget* parent, const QString& title, const QString& default_address, const QString& default_size); +DebugAddress PromptForDebugAddress(QWidget* parent, const QString& title, const QString& default_address, + const QString& default_size, int default_read = Qt::Unchecked, + int default_write = Qt::Unchecked, int default_changed = Qt::Unchecked, + int default_exec = Qt::Unchecked); } // namespace QtUtils \ No newline at end of file From 1253f692a30d8ec5cca06a90dddcc60340ae6e2f Mon Sep 17 00:00:00 2001 From: mateusfavarin Date: Tue, 3 Aug 2021 18:21:50 -0300 Subject: [PATCH 15/34] Breakpoint editor: fix cancel button not working --- src/duckstation-qt/debuggerwindow.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/duckstation-qt/debuggerwindow.cpp b/src/duckstation-qt/debuggerwindow.cpp index 893ac2be85..bd2b9d2a78 100644 --- a/src/duckstation-qt/debuggerwindow.cpp +++ b/src/duckstation-qt/debuggerwindow.cpp @@ -273,6 +273,8 @@ void DebuggerWindow::onBreakpointWidgetItemDoubleClicked(QTreeWidgetItem* item, (curr_dbg.debug_type & DebugType::Written) ? Qt::Checked : Qt::Unchecked, (curr_dbg.debug_type & DebugType::Changed) ? Qt::Checked : Qt::Unchecked, (curr_dbg.debug_type & DebugType::Executed) ? Qt::Checked : Qt::Unchecked); + if (!new_dbg.debug_type) + return; if (curr_dbg.address == new_dbg.address) CPU::SetBreakpointDebugAddress(index - 1, new_dbg); else From 18d93de30c738e57e0b0ae87ddfcbd6995be88ae Mon Sep 17 00:00:00 2001 From: mateusfavarin Date: Tue, 3 Aug 2021 19:52:57 -0300 Subject: [PATCH 16/34] Add menu to edit and delete breakpoints --- src/duckstation-qt/debuggerwindow.cpp | 41 ++++++++++++++++++++++++++- src/duckstation-qt/debuggerwindow.h | 3 ++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/duckstation-qt/debuggerwindow.cpp b/src/duckstation-qt/debuggerwindow.cpp index bd2b9d2a78..043e659570 100644 --- a/src/duckstation-qt/debuggerwindow.cpp +++ b/src/duckstation-qt/debuggerwindow.cpp @@ -261,7 +261,7 @@ void DebuggerWindow::onBreakpointsWidgetItemChanged(QTreeWidgetItem* item, int c } } -void DebuggerWindow::onBreakpointWidgetItemDoubleClicked(QTreeWidgetItem* item, int column) +void DebuggerWindow::editBreakpoint(QTreeWidgetItem* item) { bool ok; int index = item->text(0).toInt(&ok); @@ -290,6 +290,42 @@ void DebuggerWindow::onBreakpointWidgetItemDoubleClicked(QTreeWidgetItem* item, } } +void DebuggerWindow::deleteBreakpoint(QTreeWidgetItem* item) +{ + bool ok; + int index = item->text(0).toInt(&ok); + if (ok) + { + DebugAddress dbg = CPU::GetBreakpointDebugAddress(index - 1); + CPU::RemoveBreakpoint(dbg); + refreshBreakpointList(); + } +} + +void DebuggerWindow::onBreakpointWidgetItemDoubleClicked(QTreeWidgetItem* item, int column) +{ + editBreakpoint(item); +} + +void DebuggerWindow::onBreakpointsWidgetMenuRequested(const QPoint& pos) +{ + QTreeWidgetItem* item = m_ui.breakpointsWidget->itemAt(pos); + if (!item) + return; + + QMenu menu(tr("Breakpoint menu"), this); + menu.addAction("Edit"); + menu.addAction("Delete"); + QAction* action = menu.exec(m_ui.breakpointsWidget->viewport()->mapToGlobal(pos)); + + if (action == nullptr) + return; + if (action->text() == "Edit") + editBreakpoint(item); + else if (action->text() == "Delete") + deleteBreakpoint(item); +} + void DebuggerWindow::onMemorySearchTriggered() { m_ui.memoryView->clearHighlightRange(); @@ -456,6 +492,8 @@ void DebuggerWindow::connectSignals() connect(m_ui.actionClose, &QAction::triggered, this, &DebuggerWindow::close); connect(m_ui.codeView, &QTreeView::activated, this, &DebuggerWindow::onCodeViewItemActivated); connect(m_ui.breakpointsWidget, &QTreeWidget::itemChanged, this, &DebuggerWindow::onBreakpointsWidgetItemChanged); + connect(m_ui.breakpointsWidget, &QWidget::customContextMenuRequested, this, + &DebuggerWindow::onBreakpointsWidgetMenuRequested); connect(m_ui.breakpointsWidget, &QTreeWidget::itemDoubleClicked, this, &DebuggerWindow::onBreakpointWidgetItemDoubleClicked); @@ -498,6 +536,7 @@ void DebuggerWindow::createModels() m_ui.breakpointsWidget->setColumnWidth(1, 80); m_ui.breakpointsWidget->setColumnWidth(2, 40); m_ui.breakpointsWidget->setRootIsDecorated(false); + m_ui.breakpointsWidget->setContextMenuPolicy(Qt::CustomContextMenu); } void DebuggerWindow::setUIEnabled(bool enabled) diff --git a/src/duckstation-qt/debuggerwindow.h b/src/duckstation-qt/debuggerwindow.h index 13808260b6..8c58a0edb8 100644 --- a/src/duckstation-qt/debuggerwindow.h +++ b/src/duckstation-qt/debuggerwindow.h @@ -55,6 +55,7 @@ private Q_SLOTS: void onMemorySearchStringChanged(const QString&); void onBreakpointsWidgetItemChanged(QTreeWidgetItem* item, int column); void onBreakpointWidgetItemDoubleClicked(QTreeWidgetItem* item, int column); + void onBreakpointsWidgetMenuRequested(const QPoint& pos); private: @@ -71,6 +72,8 @@ private Q_SLOTS: void scrollToCodeAddress(VirtualMemoryAddress address); bool scrollToMemoryAddress(VirtualMemoryAddress address); void refreshBreakpointList(); + void editBreakpoint(QTreeWidgetItem* item); + void deleteBreakpoint(QTreeWidgetItem* item); Ui::DebuggerWindow m_ui; From 5cec416c2cb55c89a65d6814e75c25144bf8106c Mon Sep 17 00:00:00 2001 From: mateusfavarin Date: Tue, 3 Aug 2021 20:06:56 -0300 Subject: [PATCH 17/34] Conditional data bp: add support for lwl, lwr, swl, swr --- src/core/cpu_core.cpp | 43 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/src/core/cpu_core.cpp b/src/core/cpu_core.cpp index b8311c20f7..52fe48b4e9 100644 --- a/src/core/cpu_core.cpp +++ b/src/core/cpu_core.cpp @@ -721,8 +721,6 @@ ALWAYS_INLINE_RELEASE static bool ConditionalBreakpointLookAhead(u32 inst_bits) } break; case InstructionOp::lw: - case InstructionOp::lwl: - case InstructionOp::lwr: { const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32(); DataBreakpointCheck(addr); @@ -731,6 +729,17 @@ ALWAYS_INLINE_RELEASE static bool ConditionalBreakpointLookAhead(u32 inst_bits) DataBreakpointCheck(addr + 3); } break; + case InstructionOp::lwl: + case InstructionOp::lwr: + { + const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32(); + const VirtualMemoryAddress aligned_addr = addr & ~UINT32_C(3); + DataBreakpointCheck(aligned_addr); + DataBreakpointCheck(aligned_addr + 1); + DataBreakpointCheck(aligned_addr + 2); + DataBreakpointCheck(aligned_addr + 3); + } + break; case InstructionOp::sb: { const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32(); @@ -767,8 +776,34 @@ ALWAYS_INLINE_RELEASE static bool ConditionalBreakpointLookAhead(u32 inst_bits) break; case InstructionOp::swl: case InstructionOp::swr: - // - break; + { + const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32(); + const VirtualMemoryAddress aligned_addr = addr & ~UINT32_C(3); + u32 old_value; + if (!ReadMemoryWord(aligned_addr, &old_value)) + return false; + + const u32 reg_value = ReadReg(inst.i.rt); + const u8 shift = (Truncate8(addr) & u8(3)) * u8(8); + + u32 new_value; + if (inst.op == InstructionOp::swl) + { + const u32 mem_mask = UINT32_C(0xFFFFFF00) << shift; + new_value = (old_value & mem_mask) | (reg_value >> (24 - shift)); + } + else + { + const u32 mem_mask = UINT32_C(0x00FFFFFF) >> (24 - shift); + new_value = (old_value & mem_mask) | (reg_value << shift); + } + + DataBreakpointCheck(aligned_addr, (old_value & 0xFF) != (new_value & 0xFF)); + DataBreakpointCheck(aligned_addr + 1, (old_value & 0xFF00) != (new_value & 0xFF00)); + DataBreakpointCheck(aligned_addr + 2, (old_value & 0xFF0000) != (new_value & 0xFF0000)); + DataBreakpointCheck(aligned_addr + 3, (old_value & 0xFF000000) != (new_value & 0xFF000000)); + } + break; } s_last_data_breakpoint_check_pc = pc; From 6e5f938b3b4c4d0625f29b48fadab335f85ebe04 Mon Sep 17 00:00:00 2001 From: mateusfavarin Date: Wed, 4 Aug 2021 18:28:31 -0300 Subject: [PATCH 18/34] Fix breakpoint being created in the edit mode --- src/duckstation-qt/debuggerwindow.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/duckstation-qt/debuggerwindow.cpp b/src/duckstation-qt/debuggerwindow.cpp index 043e659570..5938a9211d 100644 --- a/src/duckstation-qt/debuggerwindow.cpp +++ b/src/duckstation-qt/debuggerwindow.cpp @@ -283,6 +283,7 @@ void DebuggerWindow::editBreakpoint(QTreeWidgetItem* item) QMessageBox::critical(this, windowTitle(), tr("A breakpoint already exists at this address.")); else { + CPU::RemoveBreakpoint(curr_dbg); CPU::AddBreakpoint(new_dbg); refreshBreakpointList(); } From e4690b38150db99c1fd3fb7dc5752fa46fb0d59f Mon Sep 17 00:00:00 2001 From: mateusfavarin Date: Thu, 5 Aug 2021 02:13:48 -0300 Subject: [PATCH 19/34] Make CPU registers editable --- src/duckstation-qt/debuggermodels.cpp | 62 +++++++++++++++++++++++++++ src/duckstation-qt/debuggermodels.h | 7 +++ src/duckstation-qt/debuggerwindow.cpp | 2 + 3 files changed, 71 insertions(+) diff --git a/src/duckstation-qt/debuggermodels.cpp b/src/duckstation-qt/debuggermodels.cpp index b842c69521..661c5b0817 100644 --- a/src/duckstation-qt/debuggermodels.cpp +++ b/src/duckstation-qt/debuggermodels.cpp @@ -6,6 +6,8 @@ #include #include #include +#include +#include static constexpr int NUM_COLUMNS = 5; static constexpr int STACK_RANGE = 128; @@ -288,6 +290,16 @@ DebuggerRegistersModel::DebuggerRegistersModel(QObject* parent /*= nullptr*/) : DebuggerRegistersModel::~DebuggerRegistersModel() {} +void DebuggerRegistersModel::setCodeModel(DebuggerCodeModel* model) +{ + code_model = model; +} + +void DebuggerRegistersModel::setCodeView(QTreeView* view) +{ + code_view = view; +} + int DebuggerRegistersModel::rowCount(const QModelIndex& parent /*= QModelIndex()*/) const { return static_cast(CPU::Reg::count); @@ -327,6 +339,8 @@ QVariant DebuggerRegistersModel::data(const QModelIndex& index, int role /*= Qt: if (CPU::g_state.regs.r[reg_index] != m_old_reg_values[reg_index]) return QColor(255, 50, 50); } + else if (role == Qt::EditRole) + return QString::asprintf("%08X", CPU::g_state.regs.r[reg_index]); } break; @@ -357,6 +371,54 @@ QVariant DebuggerRegistersModel::headerData(int section, Qt::Orientation orienta } } +Qt::ItemFlags DebuggerRegistersModel::flags(const QModelIndex& index) const +{ + if (index.column() == 1) + return Qt::ItemIsEditable | QAbstractItemModel::flags(index); + return QAbstractItemModel::flags(index); +} + +bool DebuggerRegistersModel::setData(const QModelIndex& index, const QVariant& value, int role) +{ + if (index.isValid() && role == Qt::EditRole) + { + bool ok; + QString value_str = value.toString(); + u32 reg_value; + if (value_str.startsWith("0x")) + reg_value = value_str.mid(2).toUInt(&ok, 16); + else + reg_value = value_str.toUInt(&ok, 16); + if (ok) + { + u32 reg_index = static_cast(index.row()); + if (reg_index == 34) // special case for PC + { + CPU::g_state.regs.npc = reg_value & 0xFFFFFFFC; // making sure it's divisible by 4 + CPU::FlushPipeline(); + } + else if (reg_index == 35) // special case for NPC + CPU::g_state.regs.npc = reg_value & 0xFFFFFFFC; // making sure it's divisible by 4 + else + CPU::g_state.regs.r[reg_index] = reg_value; + + emit dataChanged(index, index, {role}); + + // Scrolling to PC + code_model->setPC(CPU::g_state.regs.pc); + code_model->ensureAddressVisible(CPU::g_state.regs.pc); + int row = code_model->getRowForAddress(CPU::g_state.regs.pc); + if (row >= 0) + { + qApp->processEvents(QEventLoop::ExcludeUserInputEvents); + code_view->scrollTo(code_model->index(row, 0)); + } + return true; + } + } + return false; +} + void DebuggerRegistersModel::invalidateView() { beginResetModel(); diff --git a/src/duckstation-qt/debuggermodels.h b/src/duckstation-qt/debuggermodels.h index fcbbb5f7d0..b0e4337a8a 100644 --- a/src/duckstation-qt/debuggermodels.h +++ b/src/duckstation-qt/debuggermodels.h @@ -4,6 +4,7 @@ #include #include #include +#include #include class DebuggerCodeModel : public QAbstractTableModel @@ -54,17 +55,23 @@ class DebuggerRegistersModel : public QAbstractListModel public: DebuggerRegistersModel(QObject* parent = nullptr); virtual ~DebuggerRegistersModel(); + void setCodeModel(DebuggerCodeModel* model); + void setCodeView(QTreeView* view); virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override; virtual int columnCount(const QModelIndex& parent = QModelIndex()) const override; virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + virtual Qt::ItemFlags flags(const QModelIndex& index) const override; + virtual bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override; void invalidateView(); void saveCurrentValues(); private: u32 m_old_reg_values[static_cast(CPU::Reg::count)] = {}; + DebuggerCodeModel* code_model; + QTreeView* code_view; }; class DebuggerStackModel : public QAbstractListModel diff --git a/src/duckstation-qt/debuggerwindow.cpp b/src/duckstation-qt/debuggerwindow.cpp index 5938a9211d..ec25889b26 100644 --- a/src/duckstation-qt/debuggerwindow.cpp +++ b/src/duckstation-qt/debuggerwindow.cpp @@ -527,6 +527,8 @@ void DebuggerWindow::createModels() m_ui.codeView->setColumnWidth(4, m_ui.codeView->width() - (40 + 80 + 80 + 250)); m_registers_model = std::make_unique(); + m_registers_model.get()->setCodeModel(m_code_model.get()); + m_registers_model.get()->setCodeView(m_ui.codeView); m_ui.registerView->setModel(m_registers_model.get()); // m_ui->registerView->resizeRowsToContents(); From c77da8b860f1d2d2cbe4fa25b3b501b762c2bcb2 Mon Sep 17 00:00:00 2001 From: mateusfavarin Date: Fri, 6 Aug 2021 16:33:40 -0300 Subject: [PATCH 20/34] Make disasm comments follow the selected addr in the codeView --- src/duckstation-qt/debuggermodels.cpp | 9 +++++++-- src/duckstation-qt/debuggermodels.h | 2 ++ src/duckstation-qt/debuggerwindow.cpp | 22 +++++++++++++++++++++- src/duckstation-qt/debuggerwindow.h | 3 +++ 4 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/duckstation-qt/debuggermodels.cpp b/src/duckstation-qt/debuggermodels.cpp index 661c5b0817..24d8812f4a 100644 --- a/src/duckstation-qt/debuggermodels.cpp +++ b/src/duckstation-qt/debuggermodels.cpp @@ -1,5 +1,6 @@ #include "debuggermodels.h" #include "core/cpu_core.h" +#include #include "core/cpu_core_private.h" #include "core/cpu_disasm.h" #include @@ -7,7 +8,6 @@ #include #include #include -#include static constexpr int NUM_COLUMNS = 5; static constexpr int STACK_RANGE = 128; @@ -97,7 +97,7 @@ QVariant DebuggerCodeModel::data(const QModelIndex& index, int role /*= Qt::Disp case 4: { // Comment - if (address != m_last_pc) + if (address != m_last_pc && address != selected_address) return QVariant(); u32 instruction_bits; @@ -243,6 +243,11 @@ void DebuggerCodeModel::setPC(VirtualMemoryAddress pc) } } +void DebuggerCodeModel::setCurrentSelectedAddress(VirtualMemoryAddress address) +{ + selected_address = address; +} + void DebuggerCodeModel::ensureAddressVisible(VirtualMemoryAddress address) { updateRegion(address); diff --git a/src/duckstation-qt/debuggermodels.h b/src/duckstation-qt/debuggermodels.h index b0e4337a8a..5f9f0c705b 100644 --- a/src/duckstation-qt/debuggermodels.h +++ b/src/duckstation-qt/debuggermodels.h @@ -28,6 +28,7 @@ class DebuggerCodeModel : public QAbstractTableModel VirtualMemoryAddress getAddressForIndex(QModelIndex index) const; void setPC(VirtualMemoryAddress pc); void ensureAddressVisible(VirtualMemoryAddress address); + void setCurrentSelectedAddress(VirtualMemoryAddress address); void setBreakpointList(std::vector bps); void setBreakpointState(VirtualMemoryAddress address, bool enabled); void clearBreakpoints(); @@ -42,6 +43,7 @@ class DebuggerCodeModel : public QAbstractTableModel VirtualMemoryAddress m_code_region_start = 0; VirtualMemoryAddress m_code_region_end = 0; VirtualMemoryAddress m_last_pc = 0; + VirtualMemoryAddress selected_address = 0xFFFFFFFF; std::vector m_breakpoints; QPixmap m_pc_pixmap; diff --git a/src/duckstation-qt/debuggerwindow.cpp b/src/duckstation-qt/debuggerwindow.cpp index ec25889b26..3dd646b021 100644 --- a/src/duckstation-qt/debuggerwindow.cpp +++ b/src/duckstation-qt/debuggerwindow.cpp @@ -13,8 +13,8 @@ DebuggerWindow::DebuggerWindow(QWidget* parent /* = nullptr */) { m_ui.setupUi(this); setupAdditionalUi(); - connectSignals(); createModels(); + connectSignals(); setMemoryViewRegion(Bus::MemoryRegion::RAM); setUIEnabled(false); } @@ -217,6 +217,23 @@ void DebuggerWindow::onStepOutActionTriggered() QtHostInterface::GetInstance()->pauseSystem(false); } +void DebuggerWindow::refreshCodeViewSelectedAddress() +{ + std::optional address = getSelectedCodeAddress(); + if (address.has_value()) + m_code_model->setCurrentSelectedAddress(address.value()); +} + +void DebuggerWindow::onCodeViewPressed(QModelIndex index) +{ + refreshCodeViewSelectedAddress(); +} + +void DebuggerWindow::onCodeViewCurrentChanged(QModelIndex current, QModelIndex previous) +{ + refreshCodeViewSelectedAddress(); +} + void DebuggerWindow::onCodeViewItemActivated(QModelIndex index) { if (!index.isValid()) @@ -491,7 +508,10 @@ void DebuggerWindow::connectSignals() connect(m_ui.actionToggleBreakpoint, &QAction::triggered, this, &DebuggerWindow::onToggleBreakpointTriggered); connect(m_ui.actionClearBreakpoints, &QAction::triggered, this, &DebuggerWindow::onClearBreakpointsTriggered); connect(m_ui.actionClose, &QAction::triggered, this, &DebuggerWindow::close); + connect(m_ui.codeView, &QTreeView::pressed, this, &DebuggerWindow::onCodeViewPressed); connect(m_ui.codeView, &QTreeView::activated, this, &DebuggerWindow::onCodeViewItemActivated); + connect(m_ui.codeView->selectionModel(), &QItemSelectionModel::currentChanged, this, + &DebuggerWindow::onCodeViewCurrentChanged); connect(m_ui.breakpointsWidget, &QTreeWidget::itemChanged, this, &DebuggerWindow::onBreakpointsWidgetItemChanged); connect(m_ui.breakpointsWidget, &QWidget::customContextMenuRequested, this, &DebuggerWindow::onBreakpointsWidgetMenuRequested); diff --git a/src/duckstation-qt/debuggerwindow.h b/src/duckstation-qt/debuggerwindow.h index 8c58a0edb8..b5a8d5e7bf 100644 --- a/src/duckstation-qt/debuggerwindow.h +++ b/src/duckstation-qt/debuggerwindow.h @@ -50,6 +50,8 @@ private Q_SLOTS: void onStepIntoActionTriggered(); void onStepOverActionTriggered(); void onStepOutActionTriggered(); + void onCodeViewPressed(QModelIndex index); + void onCodeViewCurrentChanged(QModelIndex current, QModelIndex previous); void onCodeViewItemActivated(QModelIndex index); void onMemorySearchTriggered(); void onMemorySearchStringChanged(const QString&); @@ -74,6 +76,7 @@ private Q_SLOTS: void refreshBreakpointList(); void editBreakpoint(QTreeWidgetItem* item); void deleteBreakpoint(QTreeWidgetItem* item); + void refreshCodeViewSelectedAddress(); Ui::DebuggerWindow m_ui; From 71b5f37ea2d6cc5822fe1b30875c22ec1ab2026f Mon Sep 17 00:00:00 2001 From: mateusfavarin Date: Fri, 6 Aug 2021 16:41:09 -0300 Subject: [PATCH 21/34] CPU Reg edit: only scroll to PC if PC was changed --- src/duckstation-qt/debuggermodels.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/duckstation-qt/debuggermodels.cpp b/src/duckstation-qt/debuggermodels.cpp index 24d8812f4a..c5bb602cc8 100644 --- a/src/duckstation-qt/debuggermodels.cpp +++ b/src/duckstation-qt/debuggermodels.cpp @@ -401,6 +401,16 @@ bool DebuggerRegistersModel::setData(const QModelIndex& index, const QVariant& v { CPU::g_state.regs.npc = reg_value & 0xFFFFFFFC; // making sure it's divisible by 4 CPU::FlushPipeline(); + + // Scrolling to PC + code_model->setPC(CPU::g_state.regs.pc); + code_model->ensureAddressVisible(CPU::g_state.regs.pc); + int row = code_model->getRowForAddress(CPU::g_state.regs.pc); + if (row >= 0) + { + qApp->processEvents(QEventLoop::ExcludeUserInputEvents); + code_view->scrollTo(code_model->index(row, 0)); + } } else if (reg_index == 35) // special case for NPC CPU::g_state.regs.npc = reg_value & 0xFFFFFFFC; // making sure it's divisible by 4 @@ -409,15 +419,6 @@ bool DebuggerRegistersModel::setData(const QModelIndex& index, const QVariant& v emit dataChanged(index, index, {role}); - // Scrolling to PC - code_model->setPC(CPU::g_state.regs.pc); - code_model->ensureAddressVisible(CPU::g_state.regs.pc); - int row = code_model->getRowForAddress(CPU::g_state.regs.pc); - if (row >= 0) - { - qApp->processEvents(QEventLoop::ExcludeUserInputEvents); - code_view->scrollTo(code_model->index(row, 0)); - } return true; } } From 90b5e4bab554d82d966d201f32fc32e3aceeb91d Mon Sep 17 00:00:00 2001 From: mateusfavarin Date: Fri, 6 Aug 2021 18:49:10 -0300 Subject: [PATCH 22/34] Make stack editable --- src/duckstation-qt/debuggermodels.cpp | 59 +++++++++++++++++++++------ src/duckstation-qt/debuggermodels.h | 2 + 2 files changed, 48 insertions(+), 13 deletions(-) diff --git a/src/duckstation-qt/debuggermodels.cpp b/src/duckstation-qt/debuggermodels.cpp index c5bb602cc8..4735802052 100644 --- a/src/duckstation-qt/debuggermodels.cpp +++ b/src/duckstation-qt/debuggermodels.cpp @@ -1,6 +1,6 @@ #include "debuggermodels.h" #include "core/cpu_core.h" -#include +#include "core/cpu_core.cpp" #include "core/cpu_core_private.h" #include "core/cpu_disasm.h" #include @@ -401,22 +401,22 @@ bool DebuggerRegistersModel::setData(const QModelIndex& index, const QVariant& v { CPU::g_state.regs.npc = reg_value & 0xFFFFFFFC; // making sure it's divisible by 4 CPU::FlushPipeline(); - - // Scrolling to PC - code_model->setPC(CPU::g_state.regs.pc); - code_model->ensureAddressVisible(CPU::g_state.regs.pc); - int row = code_model->getRowForAddress(CPU::g_state.regs.pc); - if (row >= 0) - { - qApp->processEvents(QEventLoop::ExcludeUserInputEvents); - code_view->scrollTo(code_model->index(row, 0)); - } } else if (reg_index == 35) // special case for NPC CPU::g_state.regs.npc = reg_value & 0xFFFFFFFC; // making sure it's divisible by 4 else CPU::g_state.regs.r[reg_index] = reg_value; + // Update code view comments with the new register value + code_model->setPC(CPU::g_state.regs.pc); + code_model->ensureAddressVisible(CPU::g_state.regs.pc); + int row = code_model->getRowForAddress(CPU::g_state.regs.pc); + if (row >= 0) + { + qApp->processEvents(QEventLoop::ExcludeUserInputEvents); + code_view->scrollTo(code_model->index(row, 0)); + } + emit dataChanged(index, index, {role}); return true; @@ -456,7 +456,7 @@ QVariant DebuggerStackModel::data(const QModelIndex& index, int role /*= Qt::Dis if (index.column() < 0 || index.column() > 1) return QVariant(); - if (role != Qt::DisplayRole) + if (role != Qt::DisplayRole && role != Qt::EditRole) return QVariant(); const u32 sp = CPU::g_state.regs.sp; @@ -470,7 +470,33 @@ QVariant DebuggerStackModel::data(const QModelIndex& index, int role /*= Qt::Dis if (!CPU::SafeReadMemoryWord(address, &value)) return tr(""); - return QString::asprintf("0x%08X", ZeroExtend32(value)); + if (role == Qt::DisplayRole) + return QString::asprintf("0x%08X", ZeroExtend32(value)); + + return QString::asprintf("%08X", ZeroExtend32(value)); +} + +bool DebuggerStackModel::setData(const QModelIndex& index, const QVariant& value, int role) +{ + if (index.isValid() && role == Qt::EditRole) + { + bool ok; + QString value_str = value.toString(); + u32 stack_value; + if (value_str.startsWith("0x")) + stack_value = value_str.mid(2).toUInt(&ok, 16); + else + stack_value = value_str.toUInt(&ok, 16); + if (ok) + { + const u32 sp = CPU::g_state.regs.sp; + const VirtualMemoryAddress address = + (sp - static_cast(STACK_RANGE * STACK_VALUE_SIZE)) + static_cast(index.row()) * STACK_VALUE_SIZE; + std::memcpy(&Bus::g_ram[address & Bus::g_ram_mask], &stack_value, sizeof(stack_value)); + return true; + } + } + return false; } QVariant DebuggerStackModel::headerData(int section, Qt::Orientation orientation, int role /*= Qt::DisplayRole*/) const @@ -492,6 +518,13 @@ QVariant DebuggerStackModel::headerData(int section, Qt::Orientation orientation } } +Qt::ItemFlags DebuggerStackModel::flags(const QModelIndex& index) const +{ + if (index.column() == 1) + return Qt::ItemIsEditable | QAbstractItemModel::flags(index); + return QAbstractItemModel::flags(index); +} + void DebuggerStackModel::invalidateView() { beginResetModel(); diff --git a/src/duckstation-qt/debuggermodels.h b/src/duckstation-qt/debuggermodels.h index 5f9f0c705b..75c4cee039 100644 --- a/src/duckstation-qt/debuggermodels.h +++ b/src/duckstation-qt/debuggermodels.h @@ -87,7 +87,9 @@ class DebuggerStackModel : public QAbstractListModel virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override; virtual int columnCount(const QModelIndex& parent = QModelIndex()) const override; virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; + virtual bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override; virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + virtual Qt::ItemFlags flags(const QModelIndex& index) const override; void invalidateView(); }; From 8a7efb2b06552230abd3fed1a28a6fa8623399d3 Mon Sep 17 00:00:00 2001 From: mateusfavarin Date: Sat, 7 Aug 2021 19:21:33 -0300 Subject: [PATCH 23/34] Add string as a search pattern in the memory view --- src/duckstation-qt/debuggerwindow.cpp | 96 ++++++++++++++++----------- 1 file changed, 57 insertions(+), 39 deletions(-) diff --git a/src/duckstation-qt/debuggerwindow.cpp b/src/duckstation-qt/debuggerwindow.cpp index 3dd646b021..54e0d72f4d 100644 --- a/src/duckstation-qt/debuggerwindow.cpp +++ b/src/duckstation-qt/debuggerwindow.cpp @@ -349,6 +349,7 @@ void DebuggerWindow::onMemorySearchTriggered() m_ui.memoryView->clearHighlightRange(); const QString pattern_str = m_ui.memorySearchString->text(); + qsizetype str_size = pattern_str.length(); if (pattern_str.isEmpty()) return; @@ -357,55 +358,72 @@ void DebuggerWindow::onMemorySearchTriggered() u8 spattern = 0; u8 smask = 0; bool msb = false; + bool is_string = false; - pattern.reserve(static_cast(pattern_str.length()) / 2); - mask.reserve(static_cast(pattern_str.length()) / 2); + pattern.reserve(static_cast(str_size) / 2); + mask.reserve(static_cast(str_size) / 2); - for (int i = 0; i < pattern_str.length(); i++) + if (pattern_str[0].unicode() == '"' && pattern_str[str_size - 1].unicode() == '"') { - const QChar ch = pattern_str[i]; - if (ch == ' ') - continue; - - if (ch == '?') - { - spattern = (spattern << 4); - smask = (smask << 4); - } - else if (ch.isDigit()) - { - spattern = (spattern << 4) | static_cast(ch.digitValue()); - smask = (smask << 4) | 0xF; - } - else if (ch.unicode() >= 'a' && ch.unicode() <= 'f') - { - spattern = (spattern << 4) | (0xA + static_cast(ch.unicode() - 'a')); - smask = (smask << 4) | 0xF; - } - else if (ch.unicode() >= 'A' && ch.unicode() <= 'F') - { - spattern = (spattern << 4) | (0xA + static_cast(ch.unicode() - 'A')); - smask = (smask << 4) | 0xF; - } - else - { - QMessageBox::critical(this, windowTitle(), - tr("Invalid search pattern. It should contain hex digits or question marks.")); - return; - } - - if (msb) + is_string = true; + smask = 0xFF; + for (int i = 1; i < str_size - 1; i++) { + spattern = static_cast(pattern_str[i].toLatin1()); pattern.push_back(spattern); mask.push_back(smask); spattern = 0; - smask = 0; } + } + + if (!is_string) + { + for (int i = 0; i < str_size; i++) + { + const QChar ch = pattern_str[i]; + if (ch == ' ') + continue; - msb = !msb; + if (ch == '?') + { + spattern = (spattern << 4); + smask = (smask << 4); + } + else if (ch.isDigit()) + { + spattern = (spattern << 4) | static_cast(ch.digitValue()); + smask = (smask << 4) | 0xF; + } + else if (ch.unicode() >= 'a' && ch.unicode() <= 'f') + { + spattern = (spattern << 4) | (0xA + static_cast(ch.unicode() - 'a')); + smask = (smask << 4) | 0xF; + } + else if (ch.unicode() >= 'A' && ch.unicode() <= 'F') + { + spattern = (spattern << 4) | (0xA + static_cast(ch.unicode() - 'A')); + smask = (smask << 4) | 0xF; + } + else + { + QMessageBox::critical(this, windowTitle(), + tr("Invalid search pattern or string. Patterns should contain hex digits or question marks; strings should start and end with double quotes.")); + return; + } + + if (msb) + { + pattern.push_back(spattern); + mask.push_back(smask); + spattern = 0; + smask = 0; + } + + msb = !msb; + } } - if (msb) + if (msb && !is_string) { // partial byte on the end spattern = (spattern << 4); @@ -417,7 +435,7 @@ void DebuggerWindow::onMemorySearchTriggered() if (pattern.empty()) { QMessageBox::critical(this, windowTitle(), - tr("Invalid search pattern. It should contain hex digits or question marks.")); + tr("Invalid search pattern or string. Patterns should contain hex digits or question marks; strings should start and end with double quotes.")); return; } From 157662dd0e4265a9940050c934f3306233ce0d83 Mon Sep 17 00:00:00 2001 From: mateusfavarin Date: Sat, 7 Aug 2021 20:04:56 -0300 Subject: [PATCH 24/34] Change LookAhead memread function --- src/core/cpu_core.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/cpu_core.cpp b/src/core/cpu_core.cpp index 52fe48b4e9..f7654f0c05 100644 --- a/src/core/cpu_core.cpp +++ b/src/core/cpu_core.cpp @@ -744,7 +744,7 @@ ALWAYS_INLINE_RELEASE static bool ConditionalBreakpointLookAhead(u32 inst_bits) { const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32(); u8 old_value; - if (!ReadMemoryByte(addr, &old_value)) + if (!SafeReadMemoryByte(addr, &old_value)) return false; u8 new_value = Truncate8(ReadReg(inst.i.rt)); DataBreakpointCheck(addr, old_value != new_value); @@ -754,7 +754,7 @@ ALWAYS_INLINE_RELEASE static bool ConditionalBreakpointLookAhead(u32 inst_bits) { const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32(); u16 old_value; - if (!ReadMemoryHalfWord(addr, &old_value)) + if (!SafeReadMemoryHalfWord(addr, &old_value)) return false; u16 new_value = Truncate16(ReadReg(inst.i.rt)); DataBreakpointCheck(addr, (old_value & 0xFF) != (new_value & 0xFF)); @@ -765,7 +765,7 @@ ALWAYS_INLINE_RELEASE static bool ConditionalBreakpointLookAhead(u32 inst_bits) { const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32(); u32 old_value; - if (!ReadMemoryWord(addr, &old_value)) + if (!SafeReadMemoryWord(addr, &old_value)) return false; u32 new_value = ReadReg(inst.i.rt); DataBreakpointCheck(addr, (old_value & 0xFF) != (new_value & 0xFF)); @@ -780,7 +780,7 @@ ALWAYS_INLINE_RELEASE static bool ConditionalBreakpointLookAhead(u32 inst_bits) const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32(); const VirtualMemoryAddress aligned_addr = addr & ~UINT32_C(3); u32 old_value; - if (!ReadMemoryWord(aligned_addr, &old_value)) + if (!SafeReadMemoryWord(aligned_addr, &old_value)) return false; const u32 reg_value = ReadReg(inst.i.rt); From 09bf37614fa27956bdc063ffcbf3bedde8abf82e Mon Sep 17 00:00:00 2001 From: mateusfavarin Date: Sat, 7 Aug 2021 23:46:27 -0300 Subject: [PATCH 25/34] Add a range check in DataBreakpointCheck --- src/core/cpu_core.cpp | 79 ++++++++++++++++++++++++------------------- 1 file changed, 45 insertions(+), 34 deletions(-) diff --git a/src/core/cpu_core.cpp b/src/core/cpu_core.cpp index f7654f0c05..e1ffec5e68 100644 --- a/src/core/cpu_core.cpp +++ b/src/core/cpu_core.cpp @@ -541,8 +541,34 @@ ALWAYS_INLINE_RELEASE void Cop0DataBreakpointCheck(VirtualMemoryAddress address) DispatchCop0Breakpoint(); } +// range1 = [a, b]: represents any range of addresses that can have a breakpoint +// range2 = [c, d]: represents the current address being checked, and it's at most a word +// return: a mask of data intersection +ALWAYS_INLINE_RELEASE u32 AddressRangeIntersection(u32 a, u32 b, u32 c, u32 d) +{ + if (d < b) // if an intersection is impossible + return 0; + if (c > b) // if an intersection is impossible + return 0; + + u32 min = std::max(a, c); + u32 max = std::min(b, d); + if (a <= min && b >= max) + { + u32 mask = 0; + while (max > min) + { + mask = mask << 4; + max--; + mask |= 0xFF; + } + return mask; + } + return 0; +} + template -ALWAYS_INLINE_RELEASE void DataBreakpointCheck(VirtualMemoryAddress address, bool changed=false) +ALWAYS_INLINE_RELEASE void DataBreakpointCheck(VirtualMemoryAddress address, u32 size, u32 old_val=0, u32 new_val=0) { if (System::IsPaused()) return; @@ -556,7 +582,7 @@ ALWAYS_INLINE_RELEASE void DataBreakpointCheck(VirtualMemoryAddress address, boo if (!bp.enabled || !(bp.dbg.debug_type & DebugType::Read)) continue; - if (bp.dbg.address <= address && bp.dbg.address + bp.dbg.size > address) + if (AddressRangeIntersection(bp.dbg.address, bp.dbg.address + bp.dbg.size, address, address + size)) { bp.hit_count++; g_host_interface->ReportFormattedDebuggerMessage("Hit read breakpoint %u at 0x%08X.", bp.number, g_state.regs.pc); @@ -575,9 +601,10 @@ ALWAYS_INLINE_RELEASE void DataBreakpointCheck(VirtualMemoryAddress address, boo Breakpoint& bp = s_breakpoints[i]; if (!bp.enabled) continue; + if (bp.dbg.debug_type & DebugType::Written) { - if (bp.dbg.address <= address && bp.dbg.address + bp.dbg.size > address) + if (AddressRangeIntersection(bp.dbg.address, bp.dbg.address + bp.dbg.size, address, address + size)) { bp.hit_count++; g_host_interface->ReportFormattedDebuggerMessage("Hit write breakpoint %u at 0x%08X.", bp.number, @@ -585,10 +612,12 @@ ALWAYS_INLINE_RELEASE void DataBreakpointCheck(VirtualMemoryAddress address, boo g_host_interface->PauseSystem(true); return; } + return; } - if ((bp.dbg.debug_type & DebugType::Changed) && (changed)) + if (bp.dbg.debug_type & DebugType::Changed) { - if (bp.dbg.address <= address && bp.dbg.address + bp.dbg.size > address) + u32 mask = AddressRangeIntersection(bp.dbg.address, bp.dbg.address + bp.dbg.size, address, address + size); + if (mask && ((old_val & mask) != (new_val & mask))) { bp.hit_count++; g_host_interface->ReportFormattedDebuggerMessage("Hit changed breakpoint %u at 0x%08X.", bp.number, @@ -690,11 +719,10 @@ void DisassembleAndPrint(u32 addr, u32 instructions_before /* = 0 */, u32 instru } } -ALWAYS_INLINE_RELEASE static bool ConditionalBreakpointLookAhead(u32 inst_bits) +ALWAYS_INLINE_RELEASE static bool ConditionalBreakpointLookAhead() { const u32 pc = g_state.regs.pc; - Instruction inst = g_state.current_instruction; - inst.bits = inst_bits; + Instruction inst = g_state.next_instruction; if (System::IsPaused() || pc == s_last_data_breakpoint_check_pc) return false; @@ -709,24 +737,20 @@ ALWAYS_INLINE_RELEASE static bool ConditionalBreakpointLookAhead(u32 inst_bits) case InstructionOp::lbu: { const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32(); - DataBreakpointCheck(addr); + DataBreakpointCheck(addr, sizeof(u8)); } break; case InstructionOp::lh: case InstructionOp::lhu: { const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32(); - DataBreakpointCheck(addr); - DataBreakpointCheck(addr + 1); + DataBreakpointCheck(addr, sizeof(u16)); } break; case InstructionOp::lw: { const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32(); - DataBreakpointCheck(addr); - DataBreakpointCheck(addr + 1); - DataBreakpointCheck(addr + 2); - DataBreakpointCheck(addr + 3); + DataBreakpointCheck(addr, sizeof(u32)); } break; case InstructionOp::lwl: @@ -734,10 +758,7 @@ ALWAYS_INLINE_RELEASE static bool ConditionalBreakpointLookAhead(u32 inst_bits) { const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32(); const VirtualMemoryAddress aligned_addr = addr & ~UINT32_C(3); - DataBreakpointCheck(aligned_addr); - DataBreakpointCheck(aligned_addr + 1); - DataBreakpointCheck(aligned_addr + 2); - DataBreakpointCheck(aligned_addr + 3); + DataBreakpointCheck(aligned_addr, sizeof(u32)); } break; case InstructionOp::sb: @@ -747,7 +768,7 @@ ALWAYS_INLINE_RELEASE static bool ConditionalBreakpointLookAhead(u32 inst_bits) if (!SafeReadMemoryByte(addr, &old_value)) return false; u8 new_value = Truncate8(ReadReg(inst.i.rt)); - DataBreakpointCheck(addr, old_value != new_value); + DataBreakpointCheck(addr, sizeof(u8), old_value, new_value); } break; case InstructionOp::sh: @@ -757,8 +778,7 @@ ALWAYS_INLINE_RELEASE static bool ConditionalBreakpointLookAhead(u32 inst_bits) if (!SafeReadMemoryHalfWord(addr, &old_value)) return false; u16 new_value = Truncate16(ReadReg(inst.i.rt)); - DataBreakpointCheck(addr, (old_value & 0xFF) != (new_value & 0xFF)); - DataBreakpointCheck(addr + 1, (old_value & 0xFF00) != (new_value & 0xFF00)); + DataBreakpointCheck(addr, sizeof(u16), old_value, new_value); } break; case InstructionOp::sw: @@ -768,10 +788,7 @@ ALWAYS_INLINE_RELEASE static bool ConditionalBreakpointLookAhead(u32 inst_bits) if (!SafeReadMemoryWord(addr, &old_value)) return false; u32 new_value = ReadReg(inst.i.rt); - DataBreakpointCheck(addr, (old_value & 0xFF) != (new_value & 0xFF)); - DataBreakpointCheck(addr + 1, (old_value & 0xFF00) != (new_value & 0xFF00)); - DataBreakpointCheck(addr + 2, (old_value & 0xFF0000) != (new_value & 0xFF0000)); - DataBreakpointCheck(addr + 3, (old_value & 0xFF000000) != (new_value & 0xFF000000)); + DataBreakpointCheck(addr, sizeof(u32), old_value, new_value); } break; case InstructionOp::swl: @@ -798,10 +815,7 @@ ALWAYS_INLINE_RELEASE static bool ConditionalBreakpointLookAhead(u32 inst_bits) new_value = (old_value & mem_mask) | (reg_value << shift); } - DataBreakpointCheck(aligned_addr, (old_value & 0xFF) != (new_value & 0xFF)); - DataBreakpointCheck(aligned_addr + 1, (old_value & 0xFF00) != (new_value & 0xFF00)); - DataBreakpointCheck(aligned_addr + 2, (old_value & 0xFF0000) != (new_value & 0xFF0000)); - DataBreakpointCheck(aligned_addr + 3, (old_value & 0xFF000000) != (new_value & 0xFF000000)); + DataBreakpointCheck(aligned_addr, sizeof(u32), old_value, new_value); } break; } @@ -2115,10 +2129,7 @@ static void ExecuteImpl() { Cop0ExecutionBreakpointCheck(); - if (ExecutionBreakpointCheck()) - continue; // continue is measurably faster than break on msvc for some reason - - if (ConditionalBreakpointLookAhead(g_state.next_instruction.bits)) + if (ExecutionBreakpointCheck() || ConditionalBreakpointLookAhead()) continue; // continue is measurably faster than break on msvc for some reason } From 844ab257d60ac502e5168b682c050527d3606873 Mon Sep 17 00:00:00 2001 From: mateusfavarin Date: Sun, 8 Aug 2021 22:42:07 -0300 Subject: [PATCH 26/34] Change bp status bar message --- src/core/cpu_core.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/core/cpu_core.cpp b/src/core/cpu_core.cpp index e1ffec5e68..8fa3b8f699 100644 --- a/src/core/cpu_core.cpp +++ b/src/core/cpu_core.cpp @@ -585,7 +585,8 @@ ALWAYS_INLINE_RELEASE void DataBreakpointCheck(VirtualMemoryAddress address, u32 if (AddressRangeIntersection(bp.dbg.address, bp.dbg.address + bp.dbg.size, address, address + size)) { bp.hit_count++; - g_host_interface->ReportFormattedDebuggerMessage("Hit read breakpoint %u at 0x%08X.", bp.number, g_state.regs.pc); + g_host_interface->ReportFormattedDebuggerMessage("Hit read breakpoint at PC=0x%08X, reading address 0x%08X", + g_state.regs.pc, bp.dbg.address); g_host_interface->PauseSystem(true); return; } @@ -607,8 +608,8 @@ ALWAYS_INLINE_RELEASE void DataBreakpointCheck(VirtualMemoryAddress address, u32 if (AddressRangeIntersection(bp.dbg.address, bp.dbg.address + bp.dbg.size, address, address + size)) { bp.hit_count++; - g_host_interface->ReportFormattedDebuggerMessage("Hit write breakpoint %u at 0x%08X.", bp.number, - g_state.regs.pc); + g_host_interface->ReportFormattedDebuggerMessage("Hit write breakpoint at PC=0x%08X, writing to address 0x%08X", + g_state.regs.pc, bp.dbg.address); g_host_interface->PauseSystem(true); return; } @@ -620,8 +621,8 @@ ALWAYS_INLINE_RELEASE void DataBreakpointCheck(VirtualMemoryAddress address, u32 if (mask && ((old_val & mask) != (new_val & mask))) { bp.hit_count++; - g_host_interface->ReportFormattedDebuggerMessage("Hit changed breakpoint %u at 0x%08X.", bp.number, - g_state.regs.pc); + g_host_interface->ReportFormattedDebuggerMessage("Hit data changed breakpoint at PC=0x%08X, writing to address 0x%08X", + g_state.regs.pc, bp.dbg.address); g_host_interface->PauseSystem(true); return; } @@ -2101,7 +2102,7 @@ ALWAYS_INLINE_RELEASE static bool ExecutionBreakpointCheck() } else { - g_host_interface->ReportFormattedDebuggerMessage("Hit exec breakpoint %u at 0x%08X.", bp.number, pc); + g_host_interface->ReportFormattedDebuggerMessage("Hit execution breakpoint at PC=0x%08X.", pc); i++; } } From a39ddd41479638eacfd7f28d097ecc32ae6d658e Mon Sep 17 00:00:00 2001 From: mateusfavarin Date: Wed, 25 Aug 2021 13:03:23 -0300 Subject: [PATCH 27/34] Fix range address tests --- src/core/cpu_core.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/core/cpu_core.cpp b/src/core/cpu_core.cpp index 8fa3b8f699..db65fe3e16 100644 --- a/src/core/cpu_core.cpp +++ b/src/core/cpu_core.cpp @@ -546,9 +546,10 @@ ALWAYS_INLINE_RELEASE void Cop0DataBreakpointCheck(VirtualMemoryAddress address) // return: a mask of data intersection ALWAYS_INLINE_RELEASE u32 AddressRangeIntersection(u32 a, u32 b, u32 c, u32 d) { - if (d < b) // if an intersection is impossible + // test impossible intersections + if (d < a) return 0; - if (c > b) // if an intersection is impossible + if (c > b) return 0; u32 min = std::max(a, c); @@ -559,9 +560,15 @@ ALWAYS_INLINE_RELEASE u32 AddressRangeIntersection(u32 a, u32 b, u32 c, u32 d) while (max > min) { mask = mask << 4; - max--; mask |= 0xFF; + max--; + } + while (min > c) + { + mask = mask << 4; + min--; } + return mask; } return 0; @@ -1848,7 +1855,7 @@ bool HasBreakpointAtAddress(VirtualMemoryAddress address) { for (const Breakpoint& bp : s_breakpoints) { - if (bp.dbg.address == address) + if (address >= bp.dbg.address && bp.dbg.address + bp.dbg.size > address) return true; } From 2228e337b7031b3c0157ad748b446356200b1f5c Mon Sep 17 00:00:00 2001 From: mateusfavarin Date: Thu, 9 Sep 2021 14:44:25 -0300 Subject: [PATCH 28/34] Fix crash when closing the debugger --- dep/msvc/qt | 2 +- src/core/cpu_core.cpp | 25 ------------------------- src/core/cpu_core_private.h | 24 ++++++++++++++++++++++++ src/duckstation-qt/debuggermodels.cpp | 1 - src/duckstation-qt/debuggerwindow.cpp | 9 +++++++++ src/duckstation-qt/debuggerwindow.h | 2 ++ src/duckstation-qt/mainwindow.cpp | 2 ++ src/duckstation-qt/qthostinterface.h | 2 +- 8 files changed, 39 insertions(+), 28 deletions(-) diff --git a/dep/msvc/qt b/dep/msvc/qt index fb90181218..4d453164df 160000 --- a/dep/msvc/qt +++ b/dep/msvc/qt @@ -1 +1 @@ -Subproject commit fb9018121818293b72e9f5064b2cb202e713c501 +Subproject commit 4d453164dfd7b0793653e59ba70ed64a2f5ca2a4 diff --git a/src/core/cpu_core.cpp b/src/core/cpu_core.cpp index db65fe3e16..3b3cadbf86 100644 --- a/src/core/cpu_core.cpp +++ b/src/core/cpu_core.cpp @@ -22,7 +22,6 @@ namespace CPU { static void SetPC(u32 new_pc); static void UpdateLoadDelay(); static void Branch(u32 target); -static void FlushPipeline(); State g_state; bool g_using_interpreter = false; @@ -324,30 +323,6 @@ ALWAYS_INLINE_RELEASE static void UpdateLoadDelay() g_state.next_load_delay_reg = Reg::count; } -ALWAYS_INLINE_RELEASE static void FlushPipeline() -{ - // loads are flushed - g_state.next_load_delay_reg = Reg::count; - if (g_state.load_delay_reg != Reg::count) - { - g_state.regs.r[static_cast(g_state.load_delay_reg)] = g_state.load_delay_value; - g_state.load_delay_reg = Reg::count; - } - - // not in a branch delay slot - g_state.branch_was_taken = false; - g_state.next_instruction_is_branch_delay_slot = false; - g_state.current_instruction_pc = g_state.regs.pc; - - // prefetch the next instruction - FetchInstruction(); - - // and set it as the next one to execute - g_state.current_instruction.bits = g_state.next_instruction.bits; - g_state.current_instruction_in_branch_delay_slot = false; - g_state.current_instruction_was_branch_taken = false; -} - ALWAYS_INLINE static u32 ReadReg(Reg rs) { return g_state.regs.r[static_cast(rs)]; diff --git a/src/core/cpu_core_private.h b/src/core/cpu_core_private.h index 1be1b13a7a..7d44cda7c9 100644 --- a/src/core/cpu_core_private.h +++ b/src/core/cpu_core_private.h @@ -111,6 +111,30 @@ bool WriteMemoryWord(VirtualMemoryAddress addr, u32 value); void* GetDirectReadMemoryPointer(VirtualMemoryAddress address, MemoryAccessSize size, TickCount* read_ticks); void* GetDirectWriteMemoryPointer(VirtualMemoryAddress address, MemoryAccessSize size); +ALWAYS_INLINE_RELEASE void FlushPipeline() +{ + // loads are flushed + g_state.next_load_delay_reg = Reg::count; + if (g_state.load_delay_reg != Reg::count) + { + g_state.regs.r[static_cast(g_state.load_delay_reg)] = g_state.load_delay_value; + g_state.load_delay_reg = Reg::count; + } + + // not in a branch delay slot + g_state.branch_was_taken = false; + g_state.next_instruction_is_branch_delay_slot = false; + g_state.current_instruction_pc = g_state.regs.pc; + + // prefetch the next instruction + FetchInstruction(); + + // and set it as the next one to execute + g_state.current_instruction.bits = g_state.next_instruction.bits; + g_state.current_instruction_in_branch_delay_slot = false; + g_state.current_instruction_was_branch_taken = false; +} + ALWAYS_INLINE void AddGTETicks(TickCount ticks) { g_state.gte_completion_tick = g_state.pending_ticks + ticks + 1; diff --git a/src/duckstation-qt/debuggermodels.cpp b/src/duckstation-qt/debuggermodels.cpp index 4735802052..0d9173baa6 100644 --- a/src/duckstation-qt/debuggermodels.cpp +++ b/src/duckstation-qt/debuggermodels.cpp @@ -1,6 +1,5 @@ #include "debuggermodels.h" #include "core/cpu_core.h" -#include "core/cpu_core.cpp" #include "core/cpu_core_private.h" #include "core/cpu_disasm.h" #include diff --git a/src/duckstation-qt/debuggerwindow.cpp b/src/duckstation-qt/debuggerwindow.cpp index 54e0d72f4d..150ab7971c 100644 --- a/src/duckstation-qt/debuggerwindow.cpp +++ b/src/duckstation-qt/debuggerwindow.cpp @@ -1,6 +1,7 @@ #include "debuggerwindow.h" #include "core/cpu_core_private.h" #include "debuggermodels.h" +#include "mainwindow.h" #include "qthostinterface.h" #include "qtutils.h" #include @@ -21,8 +22,16 @@ DebuggerWindow::DebuggerWindow(QWidget* parent /* = nullptr */) DebuggerWindow::~DebuggerWindow() = default; +void DebuggerWindow::SetActive(bool status) +{ + DebuggerWindow::is_active = status; +} + void DebuggerWindow::onEmulationPaused(bool paused) { + if (!DebuggerWindow::is_active) + return; + if (paused) { setUIEnabled(true); diff --git a/src/duckstation-qt/debuggerwindow.h b/src/duckstation-qt/debuggerwindow.h index b5a8d5e7bf..2c7e313559 100644 --- a/src/duckstation-qt/debuggerwindow.h +++ b/src/duckstation-qt/debuggerwindow.h @@ -20,6 +20,7 @@ class DebuggerWindow : public QMainWindow public: explicit DebuggerWindow(QWidget* parent = nullptr); ~DebuggerWindow(); + void SetActive(bool status); Q_SIGNALS: void closed(); @@ -77,6 +78,7 @@ private Q_SLOTS: void editBreakpoint(QTreeWidgetItem* item); void deleteBreakpoint(QTreeWidgetItem* item); void refreshCodeViewSelectedAddress(); + bool is_active = false; Ui::DebuggerWindow m_ui; diff --git a/src/duckstation-qt/mainwindow.cpp b/src/duckstation-qt/mainwindow.cpp index c76c47979b..dc4558ab88 100644 --- a/src/duckstation-qt/mainwindow.cpp +++ b/src/duckstation-qt/mainwindow.cpp @@ -1826,6 +1826,7 @@ void MainWindow::openCPUDebugger() m_debugger_window->setWindowIcon(windowIcon()); connect(m_debugger_window, &DebuggerWindow::closed, this, &MainWindow::onCPUDebuggerClosed); m_debugger_window->show(); + m_debugger_window->SetActive(true); // the debugger will miss the pause event above (or we were already paused), so fire it now m_debugger_window->onEmulationPaused(true); @@ -1834,6 +1835,7 @@ void MainWindow::openCPUDebugger() void MainWindow::onCPUDebuggerClosed() { Assert(m_debugger_window); + m_debugger_window->SetActive(false); m_debugger_window->deleteLater(); m_debugger_window = nullptr; } diff --git a/src/duckstation-qt/qthostinterface.h b/src/duckstation-qt/qthostinterface.h index b3f8324028..146d074e0e 100644 --- a/src/duckstation-qt/qthostinterface.h +++ b/src/duckstation-qt/qthostinterface.h @@ -67,6 +67,7 @@ public Q_SLOTS: bool AddValueToStringList(const char* section, const char* key, const char* value); bool RemoveValueFromStringList(const char* section, const char* key, const char* value); void RemoveSettingValue(const char* section, const char* key); + void RunLater(std::function func) override; TinyString TranslateString(const char* context, const char* str, const char* disambiguation = nullptr, int n = -1) const override; @@ -222,7 +223,6 @@ private Q_SLOTS: void ApplySettings(bool display_osd_messages) override; void SetMouseMode(bool relative, bool hide_cursor) override; - void RunLater(std::function func) override; private: enum : u32 From 8c46371f162e070e1496c40bbd6d4ba08dfe1b30 Mon Sep 17 00:00:00 2001 From: mateusfavarin Date: Thu, 9 Sep 2021 14:49:26 -0300 Subject: [PATCH 29/34] Remove unnecessary import --- src/duckstation-qt/debuggerwindow.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/duckstation-qt/debuggerwindow.cpp b/src/duckstation-qt/debuggerwindow.cpp index 150ab7971c..3399393ced 100644 --- a/src/duckstation-qt/debuggerwindow.cpp +++ b/src/duckstation-qt/debuggerwindow.cpp @@ -1,7 +1,6 @@ #include "debuggerwindow.h" #include "core/cpu_core_private.h" #include "debuggermodels.h" -#include "mainwindow.h" #include "qthostinterface.h" #include "qtutils.h" #include From 1a95f1f4c3b43fc0e76642b071766f25313a2287 Mon Sep 17 00:00:00 2001 From: mateusfavarin Date: Thu, 9 Sep 2021 15:02:18 -0300 Subject: [PATCH 30/34] Fix crash when loading a save state with the debugger unpaused --- src/core/cdrom.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/core/cdrom.cpp b/src/core/cdrom.cpp index a4fd3b4edd..e7d5752738 100644 --- a/src/core/cdrom.cpp +++ b/src/core/cdrom.cpp @@ -444,6 +444,12 @@ u8 CDROM::ReadRegister(u32 offset) case 2: // always data FIFO { + if (m_data_fifo.IsEmpty()) + { + Log_DevPrint("Data FIFO empty on read"); + return 0x00; + } + const u8 value = m_data_fifo.Pop(); UpdateStatusRegister(); Log_DebugPrintf("CDROM read data FIFO -> 0x%08X", ZeroExtend32(value)); From 752a4f41ced91674790d123078418d07de6cabc1 Mon Sep 17 00:00:00 2001 From: mateusfavarin Date: Sat, 25 Sep 2021 17:52:52 -0300 Subject: [PATCH 31/34] Make instructions editable --- src/duckstation-qt/debuggermodels.cpp | 35 +++++++++++++++++++++++++-- src/duckstation-qt/debuggermodels.h | 2 ++ src/duckstation-qt/qthostinterface.h | 1 - 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/src/duckstation-qt/debuggermodels.cpp b/src/duckstation-qt/debuggermodels.cpp index 0d9173baa6..1552c2abc2 100644 --- a/src/duckstation-qt/debuggermodels.cpp +++ b/src/duckstation-qt/debuggermodels.cpp @@ -2,12 +2,15 @@ #include "core/cpu_core.h" #include "core/cpu_core_private.h" #include "core/cpu_disasm.h" +#include "core/system.h" #include #include #include #include #include +Log_SetChannel(CPU::Core); + static constexpr int NUM_COLUMNS = 5; static constexpr int STACK_RANGE = 128; static constexpr u32 STACK_VALUE_SIZE = sizeof(u32); @@ -182,6 +185,34 @@ QVariant DebuggerCodeModel::headerData(int section, Qt::Orientation orientation, } } +Qt::ItemFlags DebuggerCodeModel::flags(const QModelIndex& index) const +{ + if (index.column() == 2) + return Qt::ItemIsEditable | QAbstractItemModel::flags(index); + return QAbstractItemModel::flags(index); +} + +bool DebuggerCodeModel::setData(const QModelIndex& index, const QVariant& value, int role) +{ + if (System::IsPaused() && index.isValid() && role == Qt::EditRole) + { + bool ok; + QString value_str = value.toString(); + u32 instruction; + if (value_str.startsWith("0x")) + instruction = value_str.mid(2).toUInt(&ok, 16); + else + instruction = value_str.toUInt(&ok, 16); + if (ok) + { + const VirtualMemoryAddress address = static_cast(getAddressForRow(index.row())); + std::memcpy(&Bus::g_ram[address & Bus::g_ram_mask], &instruction, sizeof(instruction)); + return true; + } + } + return false; +} + bool DebuggerCodeModel::updateRegion(VirtualMemoryAddress address) { CPU::Segment segment = CPU::GetSegmentForAddress(address); @@ -384,7 +415,7 @@ Qt::ItemFlags DebuggerRegistersModel::flags(const QModelIndex& index) const bool DebuggerRegistersModel::setData(const QModelIndex& index, const QVariant& value, int role) { - if (index.isValid() && role == Qt::EditRole) + if (System::IsPaused() && index.isValid() && role == Qt::EditRole) { bool ok; QString value_str = value.toString(); @@ -477,7 +508,7 @@ QVariant DebuggerStackModel::data(const QModelIndex& index, int role /*= Qt::Dis bool DebuggerStackModel::setData(const QModelIndex& index, const QVariant& value, int role) { - if (index.isValid() && role == Qt::EditRole) + if (System::IsPaused() && index.isValid() && role == Qt::EditRole) { bool ok; QString value_str = value.toString(); diff --git a/src/duckstation-qt/debuggermodels.h b/src/duckstation-qt/debuggermodels.h index 75c4cee039..c919db36d5 100644 --- a/src/duckstation-qt/debuggermodels.h +++ b/src/duckstation-qt/debuggermodels.h @@ -19,6 +19,8 @@ class DebuggerCodeModel : public QAbstractTableModel virtual int columnCount(const QModelIndex& parent = QModelIndex()) const override; virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + virtual Qt::ItemFlags flags(const QModelIndex& index) const override; + virtual bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override; // Returns the row for this instruction pointer void resetCodeView(VirtualMemoryAddress start_address); diff --git a/src/duckstation-qt/qthostinterface.h b/src/duckstation-qt/qthostinterface.h index 00fafb3c28..13db1297e0 100644 --- a/src/duckstation-qt/qthostinterface.h +++ b/src/duckstation-qt/qthostinterface.h @@ -69,7 +69,6 @@ public Q_SLOTS: bool AddValueToStringList(const char* section, const char* key, const char* value); bool RemoveValueFromStringList(const char* section, const char* key, const char* value); void RemoveSettingValue(const char* section, const char* key); - void RunLater(std::function func) override; TinyString TranslateString(const char* context, const char* str, const char* disambiguation = nullptr, int n = -1) const override; From 997e290b2e4b6f7574cadcfe0c206ea535798daf Mon Sep 17 00:00:00 2001 From: mateusfavarin Date: Sat, 25 Sep 2021 20:10:39 -0300 Subject: [PATCH 32/34] Add memory size selector in the mem viewer --- src/duckstation-qt/memoryviewwidget.cpp | 35 ++++++++++++++++++------- src/duckstation-qt/memoryviewwidget.h | 1 + 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/duckstation-qt/memoryviewwidget.cpp b/src/duckstation-qt/memoryviewwidget.cpp index 86d7a849e9..9c16b50710 100644 --- a/src/duckstation-qt/memoryviewwidget.cpp +++ b/src/duckstation-qt/memoryviewwidget.cpp @@ -8,6 +8,7 @@ MemoryViewWidget::MemoryViewWidget(QWidget* parent /* = nullptr */, size_t addre : QAbstractScrollArea(parent) { m_bytes_per_line = 16; + m_display_size = 1; updateMetrics(); @@ -116,6 +117,7 @@ void MemoryViewWidget::paintEvent(QPaintEvent*) y += m_char_height; + // Painting addresses const unsigned num_rows = static_cast(m_end_offset - m_start_offset) / m_bytes_per_line; for (unsigned row = 0; row <= num_rows; row++) { @@ -139,38 +141,51 @@ void MemoryViewWidget::paintEvent(QPaintEvent*) const int HEX_CHAR_WIDTH = 4 * m_char_width; x = lx - offsetX; - for (unsigned col = 0; col < m_bytes_per_line; col++) + for (unsigned col = 0; col < m_bytes_per_line / m_display_size; col++) { if ((col % 2) != 0) - painter.fillRect(x, 0, HEX_CHAR_WIDTH, height(), viewport()->palette().color(QPalette::AlternateBase)); + painter.fillRect(x, 0, HEX_CHAR_WIDTH * m_display_size, height(), viewport()->palette().color(QPalette::AlternateBase)); - x += HEX_CHAR_WIDTH; + x += HEX_CHAR_WIDTH * m_display_size; } + // Painting addresses indexes y = m_char_height; x = lx - offsetX + m_char_width; - for (unsigned col = 0; col < m_bytes_per_line; col++) + for (unsigned col = 0; col < m_bytes_per_line; col += m_display_size) { painter.drawText(x, y, QString::asprintf("%02X", col)); - x += HEX_CHAR_WIDTH; + x += HEX_CHAR_WIDTH * m_display_size; } painter.drawLine(0, y + 3, width(), y + 3); y += m_char_height; + // Painting adresses values size_t offset = m_start_offset; for (unsigned row = 0; row <= num_rows; row++) { x = lx - offsetX + m_char_width; - for (unsigned col = 0; col < m_bytes_per_line && offset < m_data_size; col++, offset++) + for (unsigned col = 0; col < (m_bytes_per_line / m_display_size) && offset < m_data_size; col++, offset += m_display_size) { - unsigned char value; + unsigned int value; std::memcpy(&value, static_cast(m_data) + offset, sizeof(value)); if (offset >= m_highlight_start && offset < m_highlight_end) painter.fillRect(x - m_char_width, y - m_char_height + 3, HEX_CHAR_WIDTH, m_char_height, highlight_color); - painter.drawText(x, y, QString::asprintf("%02X", value)); - x += HEX_CHAR_WIDTH; + if (m_display_size == 1) + { + painter.drawText(x, y, QString::asprintf("%02X", value & 0xFF)); + } + else if (m_display_size == 2) + { + painter.drawText(x, y, QString::asprintf("%04X", value & 0xFFFF)); + } + else + { + painter.drawText(x, y, QString::asprintf("%08X", value)); + } + x += HEX_CHAR_WIDTH * m_display_size; } y += m_char_height; } @@ -180,6 +195,7 @@ void MemoryViewWidget::paintEvent(QPaintEvent*) lx += m_char_width; + // Painting address ASCII index y = m_char_height; x = (lx - offsetX); for (unsigned col = 0; col < m_bytes_per_line; col++) @@ -191,6 +207,7 @@ void MemoryViewWidget::paintEvent(QPaintEvent*) y += m_char_height; + // Painting address ASCII offset = m_start_offset; for (unsigned row = 0; row <= num_rows; row++) { diff --git a/src/duckstation-qt/memoryviewwidget.h b/src/duckstation-qt/memoryviewwidget.h index 7601249893..1de9458a85 100644 --- a/src/duckstation-qt/memoryviewwidget.h +++ b/src/duckstation-qt/memoryviewwidget.h @@ -45,6 +45,7 @@ private Q_SLOTS: size_t m_highlight_end = 0; unsigned m_bytes_per_line; + int m_display_size; int m_char_width; int m_char_height; From ed83c6fbd06fe2f98be6056da0e6aafea6a6c327 Mon Sep 17 00:00:00 2001 From: mateusfavarin Date: Sun, 26 Sep 2021 02:03:20 -0300 Subject: [PATCH 33/34] Always highlight selected address --- src/duckstation-qt/debuggermodels.cpp | 8 +++++--- src/duckstation-qt/debuggerwindow.cpp | 1 + 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/duckstation-qt/debuggermodels.cpp b/src/duckstation-qt/debuggermodels.cpp index 1552c2abc2..be06f64528 100644 --- a/src/duckstation-qt/debuggermodels.cpp +++ b/src/duckstation-qt/debuggermodels.cpp @@ -9,8 +9,6 @@ #include #include -Log_SetChannel(CPU::Core); - static constexpr int NUM_COLUMNS = 5; static constexpr int STACK_RANGE = 128; static constexpr u32 STACK_VALUE_SIZE = sizeof(u32); @@ -59,7 +57,7 @@ QVariant DebuggerCodeModel::data(const QModelIndex& index, int role /*= Qt::Disp if (index.column() < 0 || index.column() >= NUM_COLUMNS) return QVariant(); - if (role == Qt::DisplayRole) + if (role == Qt::DisplayRole || role == Qt::EditRole) { const VirtualMemoryAddress address = getAddressForRow(index.row()); switch (index.column()) @@ -137,6 +135,8 @@ QVariant DebuggerCodeModel::data(const QModelIndex& index, int role /*= Qt::Disp if (hasBreakpointAtAddress(address)) return QVariant(QColor(171, 97, 107)); + if (address == selected_address) + return QColor(76, 76, 76); // if (address == m_last_pc) // return QApplication::palette().toolTipBase(); if (address == m_last_pc) @@ -275,7 +275,9 @@ void DebuggerCodeModel::setPC(VirtualMemoryAddress pc) void DebuggerCodeModel::setCurrentSelectedAddress(VirtualMemoryAddress address) { + VirtualMemoryAddress temp = selected_address; selected_address = address; + emitDataChangedForAddress(temp); } void DebuggerCodeModel::ensureAddressVisible(VirtualMemoryAddress address) diff --git a/src/duckstation-qt/debuggerwindow.cpp b/src/duckstation-qt/debuggerwindow.cpp index 3399393ced..db5a8be6ea 100644 --- a/src/duckstation-qt/debuggerwindow.cpp +++ b/src/duckstation-qt/debuggerwindow.cpp @@ -120,6 +120,7 @@ void DebuggerWindow::onGoToAddressTriggered() if (!address.has_value()) return; + m_code_model->setCurrentSelectedAddress(address.value()); scrollToCodeAddress(address.value()); } From 1a78ff2e34fd8706c5cd1139ca93e342501138ce Mon Sep 17 00:00:00 2001 From: mateusfavarin Date: Sun, 26 Sep 2021 16:22:01 -0300 Subject: [PATCH 34/34] Add memory display UI --- src/duckstation-qt/debuggerwindow.cpp | 4 ++ src/duckstation-qt/debuggerwindow.ui | 49 ++++++++++++++++++++++--- src/duckstation-qt/memoryviewwidget.cpp | 6 +++ src/duckstation-qt/memoryviewwidget.h | 1 + 4 files changed, 54 insertions(+), 6 deletions(-) diff --git a/src/duckstation-qt/debuggerwindow.cpp b/src/duckstation-qt/debuggerwindow.cpp index db5a8be6ea..dc9044d943 100644 --- a/src/duckstation-qt/debuggerwindow.cpp +++ b/src/duckstation-qt/debuggerwindow.cpp @@ -550,6 +550,10 @@ void DebuggerWindow::connectSignals() connect(m_ui.memoryRegionScratchpad, &QRadioButton::clicked, [this]() { setMemoryViewRegion(Bus::MemoryRegion::Scratchpad); }); connect(m_ui.memoryRegionBIOS, &QRadioButton::clicked, [this]() { setMemoryViewRegion(Bus::MemoryRegion::BIOS); }); + + connect(m_ui.memoryDisplayByte, &QRadioButton::clicked, [this]() { m_ui.memoryView->setDisplaySize(sizeof(u8)); }); + connect(m_ui.memoryDisplayHalfword, &QRadioButton::clicked, [this]() { m_ui.memoryView->setDisplaySize(sizeof(u16)); }); + connect(m_ui.memoryDisplayWord, &QRadioButton::clicked, [this]() { m_ui.memoryView->setDisplaySize(sizeof(u32)); }); connect(m_ui.memorySearch, &QPushButton::clicked, this, &DebuggerWindow::onMemorySearchTriggered); connect(m_ui.memorySearchString, &QLineEdit::textChanged, this, &DebuggerWindow::onMemorySearchStringChanged); diff --git a/src/duckstation-qt/debuggerwindow.ui b/src/duckstation-qt/debuggerwindow.ui index ac04ec0e44..ef400949a3 100644 --- a/src/duckstation-qt/debuggerwindow.ui +++ b/src/duckstation-qt/debuggerwindow.ui @@ -36,15 +36,14 @@ - - + + - @@ -82,7 +81,7 @@ - + @@ -206,6 +205,43 @@ + + + + + + 1 Byte + + + true + + + buttonGroup + + + + + + + 2 Bytes + + + buttonGroup + + + + + + + 4 Bytes + + + buttonGroup + + + + + @@ -481,8 +517,6 @@ Ctrl+T - - @@ -496,4 +530,7 @@ + + + diff --git a/src/duckstation-qt/memoryviewwidget.cpp b/src/duckstation-qt/memoryviewwidget.cpp index 9c16b50710..578b7f321c 100644 --- a/src/duckstation-qt/memoryviewwidget.cpp +++ b/src/duckstation-qt/memoryviewwidget.cpp @@ -89,6 +89,12 @@ void MemoryViewWidget::setFont(const QFont& font) updateMetrics(); } +void MemoryViewWidget::setDisplaySize(int display_size) +{ + m_display_size = display_size; + adjustContent(); +} + void MemoryViewWidget::resizeEvent(QResizeEvent*) { adjustContent(); diff --git a/src/duckstation-qt/memoryviewwidget.h b/src/duckstation-qt/memoryviewwidget.h index 1de9458a85..ee4513f128 100644 --- a/src/duckstation-qt/memoryviewwidget.h +++ b/src/duckstation-qt/memoryviewwidget.h @@ -20,6 +20,7 @@ class MemoryViewWidget : public QAbstractScrollArea void scrolltoOffset(size_t offset); void scrollToAddress(size_t address); void setFont(const QFont& font); + void setDisplaySize(int display_size); protected: void paintEvent(QPaintEvent*);