diff --git a/CHANGES b/CHANGES index 77b196e5f..221fc0dfa 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,6 @@ 0.4.0: (Future) +Features: + - I/O viewer Bugfixes: - Qt: Windows no longer spawn in the top left on first launch - Qt: Fix install path of XDG desktop file with DESTDIR diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt index 95f0cc285..fcb164be1 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -75,6 +75,7 @@ set(SOURCE_FILES GameController.cpp GamepadAxisEvent.cpp GamepadButtonEvent.cpp + IOViewer.cpp InputController.cpp InputProfile.cpp KeyEditor.cpp @@ -101,6 +102,7 @@ qt5_wrap_ui(UI_FILES AboutScreen.ui CheatsView.ui GIFView.ui + IOViewer.ui LoadSaveState.ui LogView.ui MemoryView.ui diff --git a/src/platform/qt/IOViewer.cpp b/src/platform/qt/IOViewer.cpp new file mode 100644 index 000000000..e31ee72d1 --- /dev/null +++ b/src/platform/qt/IOViewer.cpp @@ -0,0 +1,169 @@ +/* Copyright (c) 2013-2015 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "IOViewer.h" + +#include "GameController.h" + +#include + +extern "C" { +#include "gba/io.h" +} + +using namespace QGBA; + +IOViewer::IOViewer(GameController* controller, QWidget* parent) + : QDialog(parent) + , m_controller(controller) +{ + m_ui.setupUi(this); + + for (unsigned i = 0; i < REG_MAX >> 1; ++i) { + const char* reg = GBAIORegisterNames[i]; + if (!reg) { + continue; + } + m_ui.regSelect->addItem(QString::asprintf("0x0400%04X: %s", i << 1, reg), i << 1); + } + + const QFont font = QFontDatabase::systemFont(QFontDatabase::FixedFont); + m_ui.regValue->setFont(font); + + connect(m_ui.buttonBox, SIGNAL(clicked(QAbstractButton*)), this, SLOT(buttonPressed(QAbstractButton*))); + connect(m_ui.buttonBox, SIGNAL(rejected()), this, SLOT(close())); + connect(m_ui.regSelect, SIGNAL(currentIndexChanged(int)), this, SLOT(selectRegister())); + + connect(m_ui.b0, SIGNAL(toggled(bool)), this, SLOT(bitFlipped())); + connect(m_ui.b1, SIGNAL(toggled(bool)), this, SLOT(bitFlipped())); + connect(m_ui.b2, SIGNAL(toggled(bool)), this, SLOT(bitFlipped())); + connect(m_ui.b3, SIGNAL(toggled(bool)), this, SLOT(bitFlipped())); + connect(m_ui.b4, SIGNAL(toggled(bool)), this, SLOT(bitFlipped())); + connect(m_ui.b5, SIGNAL(toggled(bool)), this, SLOT(bitFlipped())); + connect(m_ui.b6, SIGNAL(toggled(bool)), this, SLOT(bitFlipped())); + connect(m_ui.b7, SIGNAL(toggled(bool)), this, SLOT(bitFlipped())); + connect(m_ui.b8, SIGNAL(toggled(bool)), this, SLOT(bitFlipped())); + connect(m_ui.b9, SIGNAL(toggled(bool)), this, SLOT(bitFlipped())); + connect(m_ui.bA, SIGNAL(toggled(bool)), this, SLOT(bitFlipped())); + connect(m_ui.bB, SIGNAL(toggled(bool)), this, SLOT(bitFlipped())); + connect(m_ui.bC, SIGNAL(toggled(bool)), this, SLOT(bitFlipped())); + connect(m_ui.bD, SIGNAL(toggled(bool)), this, SLOT(bitFlipped())); + connect(m_ui.bE, SIGNAL(toggled(bool)), this, SLOT(bitFlipped())); + connect(m_ui.bF, SIGNAL(toggled(bool)), this, SLOT(bitFlipped())); + + selectRegister(0); +} + +void IOViewer::update() { + m_value = 0; + m_controller->threadInterrupt(); + if (m_controller->isLoaded()) { + m_value = GBAIORead(m_controller->thread()->gba, m_register); + } + m_controller->threadContinue(); + + m_ui.regValue->setText(QString::asprintf("0x%04X", m_value)); + bool signalsBlocked; + signalsBlocked = m_ui.b0->blockSignals(true); + m_ui.b0->setChecked(m_value & 0x0001 ? Qt::Checked : Qt::Unchecked); + m_ui.b0->blockSignals(signalsBlocked); + signalsBlocked = m_ui.b1->blockSignals(true); + m_ui.b1->setChecked(m_value & 0x0002 ? Qt::Checked : Qt::Unchecked); + m_ui.b1->blockSignals(signalsBlocked); + signalsBlocked = m_ui.b2->blockSignals(true); + m_ui.b2->setChecked(m_value & 0x0004 ? Qt::Checked : Qt::Unchecked); + m_ui.b2->blockSignals(signalsBlocked); + signalsBlocked = m_ui.b3->blockSignals(true); + m_ui.b3->setChecked(m_value & 0x0008 ? Qt::Checked : Qt::Unchecked); + m_ui.b3->blockSignals(signalsBlocked); + signalsBlocked = m_ui.b4->blockSignals(true); + m_ui.b4->setChecked(m_value & 0x0010 ? Qt::Checked : Qt::Unchecked); + m_ui.b4->blockSignals(signalsBlocked); + signalsBlocked = m_ui.b5->blockSignals(true); + m_ui.b5->setChecked(m_value & 0x0020 ? Qt::Checked : Qt::Unchecked); + m_ui.b5->blockSignals(signalsBlocked); + signalsBlocked = m_ui.b6->blockSignals(true); + m_ui.b6->setChecked(m_value & 0x0040 ? Qt::Checked : Qt::Unchecked); + m_ui.b6->blockSignals(signalsBlocked); + signalsBlocked = m_ui.b7->blockSignals(true); + m_ui.b7->setChecked(m_value & 0x0080 ? Qt::Checked : Qt::Unchecked); + m_ui.b7->blockSignals(signalsBlocked); + signalsBlocked = m_ui.b8->blockSignals(true); + m_ui.b8->setChecked(m_value & 0x0100 ? Qt::Checked : Qt::Unchecked); + m_ui.b8->blockSignals(signalsBlocked); + signalsBlocked = m_ui.b9->blockSignals(true); + m_ui.b9->setChecked(m_value & 0x0200 ? Qt::Checked : Qt::Unchecked); + m_ui.b9->blockSignals(signalsBlocked); + signalsBlocked = m_ui.bA->blockSignals(true); + m_ui.bA->setChecked(m_value & 0x0400 ? Qt::Checked : Qt::Unchecked); + m_ui.bA->blockSignals(signalsBlocked); + signalsBlocked = m_ui.bB->blockSignals(true); + m_ui.bB->setChecked(m_value & 0x0800 ? Qt::Checked : Qt::Unchecked); + m_ui.bB->blockSignals(signalsBlocked); + signalsBlocked = m_ui.bC->blockSignals(true); + m_ui.bC->setChecked(m_value & 0x1000 ? Qt::Checked : Qt::Unchecked); + m_ui.bC->blockSignals(signalsBlocked); + signalsBlocked = m_ui.bD->blockSignals(true); + m_ui.bD->setChecked(m_value & 0x2000 ? Qt::Checked : Qt::Unchecked); + m_ui.bD->blockSignals(signalsBlocked); + signalsBlocked = m_ui.bE->blockSignals(true); + m_ui.bE->setChecked(m_value & 0x4000 ? Qt::Checked : Qt::Unchecked); + m_ui.bE->blockSignals(signalsBlocked); + signalsBlocked = m_ui.bF->blockSignals(true); + m_ui.bF->setChecked(m_value & 0x8000 ? Qt::Checked : Qt::Unchecked); + m_ui.bF->blockSignals(signalsBlocked); +} + +void IOViewer::bitFlipped() { + m_value = 0; + m_value |= m_ui.b0->isChecked() << 0x0; + m_value |= m_ui.b1->isChecked() << 0x1; + m_value |= m_ui.b2->isChecked() << 0x2; + m_value |= m_ui.b3->isChecked() << 0x3; + m_value |= m_ui.b4->isChecked() << 0x4; + m_value |= m_ui.b5->isChecked() << 0x5; + m_value |= m_ui.b6->isChecked() << 0x6; + m_value |= m_ui.b7->isChecked() << 0x7; + m_value |= m_ui.b8->isChecked() << 0x8; + m_value |= m_ui.b9->isChecked() << 0x9; + m_value |= m_ui.bA->isChecked() << 0xA; + m_value |= m_ui.bB->isChecked() << 0xB; + m_value |= m_ui.bC->isChecked() << 0xC; + m_value |= m_ui.bD->isChecked() << 0xD; + m_value |= m_ui.bE->isChecked() << 0xE; + m_value |= m_ui.bF->isChecked() << 0xF; + m_ui.regValue->setText(QString::asprintf("0x%04X", m_value)); +} + +void IOViewer::writeback() { + m_controller->threadInterrupt(); + if (m_controller->isLoaded()) { + GBAIOWrite(m_controller->thread()->gba, m_register, m_value); + } + m_controller->threadContinue(); + update(); +} + +void IOViewer::selectRegister(unsigned address) { + m_register = address; + update(); +} + +void IOViewer::selectRegister() { + selectRegister(m_ui.regSelect->currentData().toUInt()); +} + +void IOViewer::buttonPressed(QAbstractButton* button) { + switch (m_ui.buttonBox->standardButton(button)) { + case QDialogButtonBox::Reset: + update(); + break; + case QDialogButtonBox::Apply: + writeback(); + break; + default: + break; + } +} diff --git a/src/platform/qt/IOViewer.h b/src/platform/qt/IOViewer.h new file mode 100644 index 000000000..52557f58f --- /dev/null +++ b/src/platform/qt/IOViewer.h @@ -0,0 +1,44 @@ +/* Copyright (c) 2013-2015 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef QGBA_IOVIEWER +#define QGBA_IOVIEWER + +#include + +#include "ui_IOViewer.h" + +namespace QGBA { + +class GameController; + +class IOViewer : public QDialog { +Q_OBJECT + +public: + IOViewer(GameController* controller, QWidget* parent = nullptr); + +public slots: + void update(); + void selectRegister(unsigned address); + +private slots: + void buttonPressed(QAbstractButton* button); + void bitFlipped(); + void writeback(); + void selectRegister(); + +private: + Ui::IOViewer m_ui; + + unsigned m_register; + uint16_t m_value; + + GameController* m_controller; +}; + +} + +#endif diff --git a/src/platform/qt/IOViewer.ui b/src/platform/qt/IOViewer.ui new file mode 100644 index 000000000..9a7783553 --- /dev/null +++ b/src/platform/qt/IOViewer.ui @@ -0,0 +1,414 @@ + + + IOViewer + + + + 0 + 0 + 343 + 342 + + + + I/O Viewer + + + + + + + + + 0x0000 + + + + + + + + + + + + + + + + + + + + + + 16777215 + 80 + + + + + 10 + + + + 2 + + + + + + + + + + + + + + 16777215 + 80 + + + + + 10 + + + + 5 + + + + + + + + 16777215 + 80 + + + + + 10 + + + + 4 + + + + + + + + 16777215 + 80 + + + + + 10 + + + + 7 + + + + + + + + 16777215 + 80 + + + + + 10 + + + + 0 + + + + + + + + + + + 16777215 + 80 + + + + + 10 + + + + 9 + + + + + + + + + + + 16777215 + 80 + + + + + 10 + + + + 1 + + + + + + + + 16777215 + 80 + + + + + 10 + + + + 3 + + + + + + + + + + + 16777215 + 80 + + + + + 10 + + + + 8 + + + + + + + + + + + + + + 16777215 + 80 + + + + + 10 + + + + C + + + + + + + + 16777215 + 80 + + + + + 10 + + + + E + + + + + + + + + + + + + + 16777215 + 80 + + + + + 10 + + + + 6 + + + + + + + + + + + + + + + + + 16777215 + 80 + + + + + 10 + + + + D + + + + + + + + 16777215 + 80 + + + + + 10 + + + + F + + + + + + + + 16777215 + 80 + + + + + 10 + + + + A + + + + + + + + 16777215 + 80 + + + + + 10 + + + + B + + + + + + + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + + + + + QDialogButtonBox::Apply|QDialogButtonBox::Close|QDialogButtonBox::Reset + + + + + + + regSelect + b0 + b1 + b2 + b3 + b4 + b5 + b6 + b7 + bE + b8 + b9 + bA + bB + bC + bD + bF + + + + diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 4b4370d40..c514cbd7b 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -24,6 +24,7 @@ #include "GDBController.h" #include "GDBWindow.h" #include "GIFView.h" +#include "IOViewer.h" #include "LoadSaveState.h" #include "LogView.h" #include "MultiplayerController.h" @@ -374,6 +375,11 @@ void Window::openMemoryWindow() { openView(memoryWindow); } +void Window::openIOViewer() { + IOViewer* ioViewer = new IOViewer(m_controller); + openView(ioViewer); +} + void Window::openAboutScreen() { AboutScreen* about = new AboutScreen(); openView(about); @@ -1164,6 +1170,11 @@ void Window::setupMenu(QMenuBar* menubar) { m_gameActions.append(memoryView); addControlledAction(toolsMenu, memoryView, "memoryView"); + QAction* ioViewer = new QAction(tr("View &I/O registers..."), toolsMenu); + connect(ioViewer, SIGNAL(triggered()), this, SLOT(openIOViewer())); + m_gameActions.append(ioViewer); + addControlledAction(toolsMenu, ioViewer, "ioViewer"); + ConfigOption* skipBios = m_config->addOption("skipBios"); skipBios->connect([this](const QVariant& value) { m_controller->setSkipBIOS(value.toBool()); diff --git a/src/platform/qt/Window.h b/src/platform/qt/Window.h index 1a9ef24f0..9e8afec42 100644 --- a/src/platform/qt/Window.h +++ b/src/platform/qt/Window.h @@ -84,6 +84,7 @@ public slots: void openPaletteWindow(); void openMemoryWindow(); + void openIOViewer(); void openAboutScreen();