diff --git a/src/platform/qt/GDBController.cpp b/src/platform/qt/GDBController.cpp new file mode 100644 index 000000000..3ee878798 --- /dev/null +++ b/src/platform/qt/GDBController.cpp @@ -0,0 +1,71 @@ +#include "GDBController.h" + +#include "GameController.h" + +using namespace QGBA; + +GDBController::GDBController(GameController* controller, QObject* parent) + : QObject(parent) + , m_gameController(controller) + , m_port(2345) + , m_bindAddress(0) +{ + GDBStubCreate(&m_gdbStub); +} + +ushort GDBController::port() { + return m_port; +} + +uint32_t GDBController::bindAddress() { + return m_bindAddress; +} + +bool GDBController::isAttached() { + return m_gameController->debugger() == &m_gdbStub.d; +} + +void GDBController::setPort(ushort port) { + m_port = port; +} + +void GDBController::setBindAddress(uint32_t bindAddress) { + m_bindAddress = bindAddress; +} + +void GDBController::attach() { + if (isAttached()) { + return; + } + m_gameController->setDebugger(&m_gdbStub.d); +} + +void GDBController::detach() { + if (!isAttached()) { + return; + } + bool wasPaused = m_gameController->isPaused(); + disconnect(m_gameController, SIGNAL(frameAvailable(const uint32_t*)), this, SLOT(updateGDB())); + m_gameController->setPaused(true); + GDBStubShutdown(&m_gdbStub); + m_gameController->setDebugger(nullptr); + m_gameController->setPaused(wasPaused); +} + +void GDBController::listen() { + if (!isAttached()) { + attach(); + } + bool wasPaused = m_gameController->isPaused(); + connect(m_gameController, SIGNAL(frameAvailable(const uint32_t*)), this, SLOT(updateGDB())); + m_gameController->setPaused(true); + GDBStubListen(&m_gdbStub, m_port, m_bindAddress); + m_gameController->setPaused(wasPaused); +} + +void GDBController::updateGDB() { + bool wasPaused = m_gameController->isPaused(); + m_gameController->setPaused(true); + GDBStubUpdate(&m_gdbStub); + m_gameController->setPaused(wasPaused); +} diff --git a/src/platform/qt/GDBController.h b/src/platform/qt/GDBController.h new file mode 100644 index 000000000..5f65469e7 --- /dev/null +++ b/src/platform/qt/GDBController.h @@ -0,0 +1,44 @@ +#ifndef QGBA_GDB_CONTROLLER +#define QGBA_GDB_CONTROLLER + +#include + +extern "C" { +#include "gdb-stub.h" +} + +namespace QGBA { + +class GameController; + +class GDBController : public QObject { +Q_OBJECT + +public: + GDBController(GameController* controller, QObject* parent = nullptr); + +public: + ushort port(); + uint32_t bindAddress(); + bool isAttached(); + +public slots: + void setPort(ushort port); + void setBindAddress(uint32_t bindAddress); + void attach(); + void detach(); + void listen(); + +private slots: + void updateGDB(); + +private: + GDBStub m_gdbStub; + GameController* m_gameController; + + ushort m_port; + uint32_t m_bindAddress; +}; + +} +#endif diff --git a/src/platform/qt/GDBWindow.cpp b/src/platform/qt/GDBWindow.cpp new file mode 100644 index 000000000..a3f79bc1e --- /dev/null +++ b/src/platform/qt/GDBWindow.cpp @@ -0,0 +1,96 @@ +#include "GDBWindow.h" + +#include +#include +#include +#include +#include +#include + +#include "GDBController.h" + +using namespace QGBA; + +GDBWindow::GDBWindow(GDBController* controller, QWidget* parent) + : QWidget(parent) + , m_gdbController(controller) +{ + setWindowFlags(windowFlags() & ~Qt::WindowFullscreenButtonHint); + QVBoxLayout* mainSegment = new QVBoxLayout; + setLayout(mainSegment); + QGroupBox* settings = new QGroupBox(tr("Server settings")); + mainSegment->addWidget(settings); + + QGridLayout* settingsGrid = new QGridLayout; + settings->setLayout(settingsGrid); + + QLabel* portLabel = new QLabel(tr("Local port")); + settingsGrid->addWidget(portLabel, 0, 0, Qt::AlignRight); + QLabel* bindAddressLabel = new QLabel(tr("Bind address")); + settingsGrid->addWidget(bindAddressLabel, 1, 0, Qt::AlignRight); + + m_portEdit = new QLineEdit("2345"); + m_portEdit->setMaxLength(5); + connect(m_portEdit, SIGNAL(textChanged(const QString&)), this, SLOT(portChanged(const QString&))); + settingsGrid->addWidget(m_portEdit, 0, 1, Qt::AlignLeft); + + m_bindAddressEdit = new QLineEdit("0.0.0.0"); + m_bindAddressEdit->setMaxLength(15); + connect(m_bindAddressEdit, SIGNAL(textChanged(const QString&)), this, SLOT(bindAddressChanged(const QString&))); + settingsGrid->addWidget(m_bindAddressEdit, 1, 1, Qt::AlignLeft); + + m_startStopButton = new QPushButton(tr("Start")); + connect(m_startStopButton, SIGNAL(clicked()), m_gdbController, SLOT(listen())); + connect(m_startStopButton, SIGNAL(clicked()), this, SLOT(started())); + mainSegment->addWidget(m_startStopButton); +} + +void GDBWindow::portChanged(const QString& portString) { + bool ok = false; + ushort port = portString.toUShort(&ok); + if (ok) { + m_gdbController->setPort(port); + } +} + +void GDBWindow::bindAddressChanged(const QString& bindAddressString) { + bool ok = false; + QStringList parts = bindAddressString.split('.'); + if (parts.length() != 4) { + return; + } + int i; + uint32_t address = 0; + for (i = 0; i < 4; ++i) { + ushort octet = parts[i].toUShort(&ok); + if (!ok) { + return; + } + if (octet > 0xFF) { + return; + } + address <<= 8; + address += octet; + } + m_gdbController->setBindAddress(address); +} + +void GDBWindow::started() { + m_portEdit->setEnabled(false); + m_bindAddressEdit->setEnabled(false); + m_startStopButton->setText(tr("Stop")); + disconnect(m_startStopButton, SIGNAL(clicked()), m_gdbController, SLOT(listen())); + disconnect(m_startStopButton, SIGNAL(clicked()), this, SLOT(started())); + connect(m_startStopButton, SIGNAL(clicked()), m_gdbController, SLOT(detach())); + connect(m_startStopButton, SIGNAL(clicked()), this, SLOT(stopped())); +} + +void GDBWindow::stopped() { + m_portEdit->setEnabled(true); + m_bindAddressEdit->setEnabled(true); + m_startStopButton->setText(tr("Start")); + disconnect(m_startStopButton, SIGNAL(clicked()), m_gdbController, SLOT(detach())); + disconnect(m_startStopButton, SIGNAL(clicked()), this, SLOT(stopped())); + connect(m_startStopButton, SIGNAL(clicked()), m_gdbController, SLOT(listen())); + connect(m_startStopButton, SIGNAL(clicked()), this, SLOT(started())); +} diff --git a/src/platform/qt/GDBWindow.h b/src/platform/qt/GDBWindow.h new file mode 100644 index 000000000..1e97c66ee --- /dev/null +++ b/src/platform/qt/GDBWindow.h @@ -0,0 +1,36 @@ +#ifndef QGBA_GDB_WINDOW +#define QGBA_GDB_WINDOW + +#include + +class QLineEdit; +class QPushButton; + +namespace QGBA { + +class GDBController; + +class GDBWindow : public QWidget { +Q_OBJECT + +public: + GDBWindow(GDBController* controller, QWidget* parent = nullptr); + +private slots: + void portChanged(const QString&); + void bindAddressChanged(const QString&); + + void started(); + void stopped(); + +private: + GDBController* m_gdbController; + + QLineEdit* m_portEdit; + QLineEdit* m_bindAddressEdit; + QPushButton* m_startStopButton; +}; + +} + +#endif