mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-07-09 00:28:42 +12:00
More CPU debugger work
Co-Authored-By: liuk707 <62625900+liuk7071@users.noreply.github.com>
This commit is contained in:
parent
9dc52577ea
commit
37f38509db
2 changed files with 61 additions and 20 deletions
|
@ -1,4 +1,5 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include <QLineEdit>
|
||||||
#include <QListWidget>
|
#include <QListWidget>
|
||||||
#include <QPlainTextEdit>
|
#include <QPlainTextEdit>
|
||||||
#include <QScrollBar>
|
#include <QScrollBar>
|
||||||
|
@ -14,10 +15,13 @@ class CPUDebugger : public QWidget {
|
||||||
QListWidget* disasmListWidget;
|
QListWidget* disasmListWidget;
|
||||||
QScrollBar* verticalScrollBar;
|
QScrollBar* verticalScrollBar;
|
||||||
QPlainTextEdit* registerTextEdit;
|
QPlainTextEdit* registerTextEdit;
|
||||||
|
QTimer* updateTimer;
|
||||||
|
QLineEdit* addressInput;
|
||||||
|
|
||||||
DisabledWidgetOverlay* disabledOverlay;
|
DisabledWidgetOverlay* disabledOverlay;
|
||||||
|
|
||||||
bool enabled = false;
|
bool enabled = false;
|
||||||
|
bool followPC = false;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CPUDebugger(Emulator* emulator, QWidget* parent = nullptr);
|
CPUDebugger(Emulator* emulator, QWidget* parent = nullptr);
|
||||||
|
@ -30,6 +34,7 @@ class CPUDebugger : public QWidget {
|
||||||
void update();
|
void update();
|
||||||
void updateDisasm();
|
void updateDisasm();
|
||||||
void updateRegisters();
|
void updateRegisters();
|
||||||
|
void scrollToPC();
|
||||||
|
|
||||||
bool eventFilter(QObject* obj, QEvent* event) override;
|
bool eventFilter(QObject* obj, QEvent* event) override;
|
||||||
void showEvent(QShowEvent* event) override;
|
void showEvent(QShowEvent* event) override;
|
||||||
|
|
|
@ -2,16 +2,19 @@
|
||||||
|
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
|
|
||||||
|
#include <QCheckBox>
|
||||||
#include <QGridLayout>
|
#include <QGridLayout>
|
||||||
#include <QHBoxLayout>
|
#include <QHBoxLayout>
|
||||||
#include <QListWidget>
|
#include <QListWidget>
|
||||||
#include <QPlainTextEdit>
|
#include <QPlainTextEdit>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
|
#include <limits>
|
||||||
#include <span>
|
#include <span>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "capstone.hpp"
|
#include "capstone.hpp"
|
||||||
|
|
||||||
|
// TODO: Make this actually thread-safe by having it only work when paused
|
||||||
static int getLinesInViewport(QListWidget* listWidget) {
|
static int getLinesInViewport(QListWidget* listWidget) {
|
||||||
auto viewportHeight = listWidget->viewport()->height();
|
auto viewportHeight = listWidget->viewport()->height();
|
||||||
QFontMetrics fm = QFontMetrics(listWidget->font());
|
QFontMetrics fm = QFontMetrics(listWidget->font());
|
||||||
|
@ -31,62 +34,83 @@ CPUDebugger::CPUDebugger(Emulator* emulator, QWidget* parent) : emu(emulator), Q
|
||||||
setWindowTitle(tr("CPU debugger"));
|
setWindowTitle(tr("CPU debugger"));
|
||||||
resize(1000, 600);
|
resize(1000, 600);
|
||||||
|
|
||||||
// Main grid layout
|
|
||||||
QGridLayout* gridLayout = new QGridLayout(this);
|
QGridLayout* gridLayout = new QGridLayout(this);
|
||||||
|
|
||||||
// Top row: buttons in a horizontal layout
|
|
||||||
QHBoxLayout* horizontalLayout = new QHBoxLayout();
|
QHBoxLayout* horizontalLayout = new QHBoxLayout();
|
||||||
QPushButton* stepButton = new QPushButton(tr("Step"), this);
|
|
||||||
|
// Set up the top line widgets
|
||||||
QPushButton* goToAddressButton = new QPushButton(tr("Go to address"), this);
|
QPushButton* goToAddressButton = new QPushButton(tr("Go to address"), this);
|
||||||
QPushButton* goToPCButton = new QPushButton(tr("Go to PC"), this);
|
QPushButton* goToPCButton = new QPushButton(tr("Go to PC"), this);
|
||||||
|
QCheckBox* followPCCheckBox = new QCheckBox(tr("Follow PC"), this);
|
||||||
|
addressInput = new QLineEdit(this);
|
||||||
|
|
||||||
horizontalLayout->addWidget(stepButton);
|
|
||||||
horizontalLayout->addWidget(goToAddressButton);
|
horizontalLayout->addWidget(goToAddressButton);
|
||||||
horizontalLayout->addWidget(goToPCButton);
|
horizontalLayout->addWidget(goToPCButton);
|
||||||
|
horizontalLayout->addWidget(followPCCheckBox);
|
||||||
|
horizontalLayout->addWidget(addressInput);
|
||||||
|
|
||||||
|
followPCCheckBox->setChecked(followPC);
|
||||||
|
connect(followPCCheckBox, &QCheckBox::toggled, this, [&](bool checked) { followPC = checked; });
|
||||||
|
|
||||||
|
addressInput->setPlaceholderText(tr("Address to jump to"));
|
||||||
|
addressInput->setMaximumWidth(100);
|
||||||
|
|
||||||
gridLayout->addLayout(horizontalLayout, 0, 0);
|
gridLayout->addLayout(horizontalLayout, 0, 0);
|
||||||
|
|
||||||
// Disassembly list on the left
|
// Disassembly list on the left, scrollbar in the middle, register view on the right
|
||||||
disasmListWidget = new QListWidget(this);
|
disasmListWidget = new QListWidget(this);
|
||||||
gridLayout->addWidget(disasmListWidget, 1, 0);
|
gridLayout->addWidget(disasmListWidget, 1, 0);
|
||||||
|
|
||||||
// Vertical scroll bar in the middle
|
|
||||||
verticalScrollBar = new QScrollBar(Qt::Vertical, this);
|
verticalScrollBar = new QScrollBar(Qt::Vertical, this);
|
||||||
gridLayout->addWidget(verticalScrollBar, 1, 1);
|
gridLayout->addWidget(verticalScrollBar, 1, 1);
|
||||||
|
|
||||||
// Register view on the right
|
|
||||||
registerTextEdit = new QPlainTextEdit(this);
|
registerTextEdit = new QPlainTextEdit(this);
|
||||||
registerTextEdit->setEnabled(true);
|
registerTextEdit->setEnabled(true);
|
||||||
registerTextEdit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
registerTextEdit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||||
registerTextEdit->setMaximumWidth(800);
|
registerTextEdit->setMaximumWidth(800);
|
||||||
gridLayout->addWidget(registerTextEdit, 1, 2);
|
gridLayout->addWidget(registerTextEdit, 1, 2);
|
||||||
|
|
||||||
// Setup disabled widget overlay
|
// Setup overlay for when the widget is disabled
|
||||||
disabledOverlay = new DisabledWidgetOverlay(this, tr("Pause the emulator to use the CPU Debugger"));
|
disabledOverlay = new DisabledWidgetOverlay(this, tr("Pause the emulator to use the CPU Debugger"));
|
||||||
disabledOverlay->resize(size()); // Fill the whole screen
|
disabledOverlay->resize(size()); // Fill the whole screen
|
||||||
disabledOverlay->raise();
|
disabledOverlay->raise();
|
||||||
disabledOverlay->hide();
|
disabledOverlay->hide();
|
||||||
|
|
||||||
// Monospace font
|
// Use a monospace font for the disassembly to align it
|
||||||
QFont mono_font = QFont("Courier New");
|
QFont mono_font = QFont("Courier New");
|
||||||
mono_font.setStyleHint(QFont::Monospace);
|
mono_font.setStyleHint(QFont::Monospace);
|
||||||
disasmListWidget->setFont(mono_font);
|
disasmListWidget->setFont(mono_font);
|
||||||
registerTextEdit->setFont(mono_font);
|
registerTextEdit->setFont(mono_font);
|
||||||
|
|
||||||
// To forward scrolling from the list widget to the external scrollbar
|
// Forward scrolling from the list widget to our scrollbar
|
||||||
disasmListWidget->installEventFilter(this);
|
disasmListWidget->installEventFilter(this);
|
||||||
|
|
||||||
// Setup scroll bar
|
// Annoyingly, due to a Qt limitation we can't set it to U32_MAX
|
||||||
verticalScrollBar->setRange(0, INT32_MAX);
|
verticalScrollBar->setRange(0, std::numeric_limits<s32>::max());
|
||||||
verticalScrollBar->setSingleStep(8);
|
verticalScrollBar->setSingleStep(8);
|
||||||
verticalScrollBar->setPageStep(getLinesInViewport(disasmListWidget));
|
verticalScrollBar->setPageStep(getLinesInViewport(disasmListWidget));
|
||||||
verticalScrollBar->show();
|
verticalScrollBar->show();
|
||||||
connect(verticalScrollBar, &QScrollBar::valueChanged, this, &CPUDebugger::updateDisasm);
|
connect(verticalScrollBar, &QScrollBar::valueChanged, this, &CPUDebugger::updateDisasm);
|
||||||
registerTextEdit->setReadOnly(true);
|
registerTextEdit->setReadOnly(true);
|
||||||
|
|
||||||
connect(goToPCButton, &QPushButton::clicked, this, [&]() {
|
connect(goToPCButton, &QPushButton::clicked, this, [&]() { scrollToPC(); });
|
||||||
u32 pc = emu->getCPU().getReg(15);
|
|
||||||
verticalScrollBar->setValue(pc);
|
// We have a QTimer that triggers every 500ms to update our widget when it's active
|
||||||
|
updateTimer = new QTimer(this);
|
||||||
|
connect(updateTimer, &QTimer::timeout, this, &CPUDebugger::update);
|
||||||
|
|
||||||
|
// Go to address when the "Go to address" button is pressed, or when we press enter inside the address input box
|
||||||
|
connect(goToAddressButton, &QPushButton::clicked, this, [&]() {
|
||||||
|
QString text = addressInput->text().trimmed();
|
||||||
|
|
||||||
|
bool validAddr = false;
|
||||||
|
u32 addr = text.toUInt(&validAddr, 16); // Parse address as hex
|
||||||
|
if (validAddr) {
|
||||||
|
verticalScrollBar->setValue(addr);
|
||||||
|
} else {
|
||||||
|
addressInput->setText(tr("Invalid hexadecimal address"));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
connect(addressInput, &QLineEdit::returnPressed, goToAddressButton, &QPushButton::click);
|
||||||
|
|
||||||
disable();
|
disable();
|
||||||
hide();
|
hide();
|
||||||
|
@ -94,22 +118,28 @@ CPUDebugger::CPUDebugger(Emulator* emulator, QWidget* parent) : emu(emulator), Q
|
||||||
|
|
||||||
void CPUDebugger::enable() {
|
void CPUDebugger::enable() {
|
||||||
enabled = true;
|
enabled = true;
|
||||||
auto pc = emu->getCPU().getReg(15);
|
|
||||||
|
|
||||||
disabledOverlay->hide();
|
disabledOverlay->hide();
|
||||||
verticalScrollBar->setValue(pc);
|
scrollToPC();
|
||||||
|
|
||||||
|
// Update the widget every 500ms
|
||||||
|
updateTimer->start(500);
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPUDebugger::disable() {
|
void CPUDebugger::disable() {
|
||||||
enabled = false;
|
enabled = false;
|
||||||
|
|
||||||
|
updateTimer->stop();
|
||||||
disabledOverlay->show();
|
disabledOverlay->show();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPUDebugger::update() {
|
void CPUDebugger::update() {
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
|
if (followPC) {
|
||||||
|
scrollToPC();
|
||||||
|
}
|
||||||
|
|
||||||
updateDisasm();
|
updateDisasm();
|
||||||
updateRegisters();
|
updateRegisters();
|
||||||
}
|
}
|
||||||
|
@ -154,7 +184,12 @@ void CPUDebugger::updateDisasm() {
|
||||||
disasmListWidget->addItem(QString::fromStdString(fmt::format("{:08X} | ???", addr)));
|
disasmListWidget->addItem(QString::fromStdString(fmt::format("{:08X} | ???", addr)));
|
||||||
}
|
}
|
||||||
|
|
||||||
disasmListWidget->setCurrentRow(currentRow);
|
disasmListWidget->setCurrentRow(currentRow);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPUDebugger::scrollToPC() {
|
||||||
|
u32 pc = emu->getCPU().getReg(15);
|
||||||
|
verticalScrollBar->setValue(pc);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPUDebugger::updateRegisters() {
|
void CPUDebugger::updateRegisters() {
|
||||||
|
@ -211,8 +246,9 @@ void CPUDebugger::showEvent(QShowEvent* event) {
|
||||||
enable();
|
enable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Scroll 1 instruction up or down when the arrow keys are pressed and we're at the edge of the disassembly list
|
||||||
void CPUDebugger::keyPressEvent(QKeyEvent* event) {
|
void CPUDebugger::keyPressEvent(QKeyEvent* event) {
|
||||||
constexpr usize instructionSize = sizeof(u32);
|
constexpr usize instructionSize = sizeof(u32);
|
||||||
|
|
||||||
if (event->key() == Qt::Key_Up) {
|
if (event->key() == Qt::Key_Up) {
|
||||||
verticalScrollBar->setValue(verticalScrollBar->value() - instructionSize);
|
verticalScrollBar->setValue(verticalScrollBar->value() - instructionSize);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue