Qt: Start adding I/O register info
Jeffrey Pfau jeffrey@endrift.com
Sun, 11 Oct 2015 19:11:15 -0700
3 files changed,
194 insertions(+),
89 deletions(-)
M
src/platform/qt/IOViewer.cpp
→
src/platform/qt/IOViewer.cpp
@@ -8,6 +8,7 @@
#include "GameController.h" #include <QFontDatabase> +#include <QVBoxLayout> extern "C" { #include "gba/io.h"@@ -15,6 +16,119 @@ }
using namespace QGBA; + +QList<IOViewer::RegisterDescription> IOViewer::s_registers; + +const QList<IOViewer::RegisterDescription>& IOViewer::registerDescriptions() { + if (!s_registers.isEmpty()) { + return s_registers; + } + // 0x04000000: DISPCNT + s_registers.append({ + { tr("Background mode"), 0, 3 }, + { tr("CGB Mode"), 3, 1, true }, + { tr("Frame select"), 4 }, + { tr("Unlocked HBlank"), 5 }, + { tr("Linear OBJ tile mapping"), 6 }, + { tr("Force blank screen"), 7 }, + { tr("Enable background 0"), 8 }, + { tr("Enable background 1"), 9 }, + { tr("Enable background 2"), 10 }, + { tr("Enable background 3"), 11 }, + { tr("Enable OBJ"), 12 }, + { tr("Enable Window 0"), 13 }, + { tr("Enable Window 1"), 14 }, + { tr("Enable OBJ Window"), 15 }, + }); + // 0x04000002: Green swap (undocumented and unimplemented) + s_registers.append(RegisterDescription()); + // 0x04000004: DISPSTAT + s_registers.append({ + { tr("Currently in VBlank"), 0, 1, true }, + { tr("Currently in HBlank"), 1, 1, true }, + { tr("Currently in VCounter"), 2, 1, true }, + { tr("Enable VBlank IRQ generation"), 3 }, + { tr("Enable HBlank IRQ generation"), 4 }, + { tr("Enable VCounter IRQ generation"), 5 }, + { tr("VCounter scanline"), 8, 8 }, + }); + // 0x04000006: VCOUNT + s_registers.append({ + { tr("Current scanline"), 0, 8, true }, + }); + // 0x04000008: BG0CNT + s_registers.append({ + { tr("Priority"), 0, 2 }, + { tr("Tile data base (* 16kB)"), 2, 2 }, + { tr("Enable mosaic"), 3 }, + { tr("Enable 256-color"), 3 }, + { tr("Tile map base (* 2kB)"), 8, 5 }, + { tr("Background dimensions"), 14, 2 }, + }); + // 0x0400000A: BG1CNT + s_registers.append({ + { tr("Priority"), 0, 2 }, + { tr("Tile data base (* 16kB)"), 2, 2 }, + { tr("Enable mosaic"), 3 }, + { tr("Enable 256-color"), 3 }, + { tr("Tile map base (* 2kB)"), 8, 5 }, + { tr("Background dimensions"), 14, 2 }, + }); + // 0x0400000C: BG2CNT + s_registers.append({ + { tr("Priority"), 0, 2 }, + { tr("Tile data base (* 16kB)"), 2, 2 }, + { tr("Enable mosaic"), 3 }, + { tr("Enable 256-color"), 3 }, + { tr("Tile map base (* 2kB)"), 8, 5 }, + { tr("Overflow wraps"), 9 }, + { tr("Background dimensions"), 14, 2 }, + }); + // 0x0400000E: BG3CNT + s_registers.append({ + { tr("Priority"), 0, 2 }, + { tr("Tile data base (* 16kB)"), 2, 2 }, + { tr("Enable mosaic"), 3 }, + { tr("Enable 256-color"), 3 }, + { tr("Tile map base (* 2kB)"), 8, 5 }, + { tr("Overflow wraps"), 9 }, + { tr("Background dimensions"), 14, 2 }, + }); + // 0x04000010: BG0HOFS + s_registers.append({ + { tr("Horizontal offset"), 0, 9 }, + }); + // 0x04000012: BG0VOFS + s_registers.append({ + { tr("Vertical offset"), 0, 9 }, + }); + // 0x04000014: BG1HOFS + s_registers.append({ + { tr("Horizontal offset"), 0, 9 }, + }); + // 0x04000016: BG1VOFS + s_registers.append({ + { tr("Vertical offset"), 0, 9 }, + }); + // 0x04000018: BG2HOFS + s_registers.append({ + { tr("Horizontal offset"), 0, 9 }, + }); + // 0x0400001A: BG2VOFS + s_registers.append({ + { tr("Vertical offset"), 0, 9 }, + }); + // 0x0400001C: BG3HOFS + s_registers.append({ + { tr("Horizontal offset"), 0, 9 }, + }); + // 0x0400001E: BG3VOFS + s_registers.append({ + { tr("Vertical offset"), 0, 9 }, + }); + return s_registers; +} + IOViewer::IOViewer(GameController* controller, QWidget* parent) : QDialog(parent) , m_controller(controller)@@ -36,104 +150,50 @@ 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())); + m_b[0] = m_ui.b0; + m_b[1] = m_ui.b1; + m_b[2] = m_ui.b2; + m_b[3] = m_ui.b3; + m_b[4] = m_ui.b4; + m_b[5] = m_ui.b5; + m_b[6] = m_ui.b6; + m_b[7] = m_ui.b7; + m_b[8] = m_ui.b8; + m_b[9] = m_ui.b9; + m_b[10] = m_ui.bA; + m_b[11] = m_ui.bB; + m_b[12] = m_ui.bC; + m_b[13] = m_ui.bD; + m_b[14] = m_ui.bE; + m_b[15] = m_ui.bF; + + for (int i = 0; i < 16; ++i) { + connect(m_b[i], SIGNAL(toggled(bool)), this, SLOT(bitFlipped())); + } selectRegister(0); } -void IOViewer::update() { +void IOViewer::updateRegister() { m_value = 0; + uint16_t value = 0; m_controller->threadInterrupt(); if (m_controller->isLoaded()) { - m_value = GBAIORead(m_controller->thread()->gba, m_register); + value = GBAIORead(m_controller->thread()->gba, m_register); } m_controller->threadContinue(); - m_ui.regValue->setText("0x" + QString("%1").arg(m_value, 4, 16, QChar('0')).toUpper()); - 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); + for (int i = 0; i < 16; ++i) { + m_b[i]->setChecked(value & (1 << i) ? Qt::Checked : Qt::Unchecked); + } + m_value = value; } 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; + for (int i = 0; i < 16; ++i) { + m_value |= m_b[i]->isChecked() << i; + } m_ui.regValue->setText("0x" + QString("%1").arg(m_value, 4, 16, QChar('0')).toUpper()); }@@ -143,12 +203,38 @@ if (m_controller->isLoaded()) {
GBAIOWrite(m_controller->thread()->gba, m_register, m_value); } m_controller->threadContinue(); - update(); + updateRegister(); } void IOViewer::selectRegister(unsigned address) { m_register = address; - update(); + QLayout* box = m_ui.regDescription->layout(); + if (box) { + // I can't believe there isn't a real way to do this... + while (!box->isEmpty()) { + QLayoutItem* item = box->takeAt(0); + if (item->widget()) { + delete item->widget(); + } + delete item; + } + } else { + box = new QVBoxLayout; + } + if (registerDescriptions().count() > address >> 1) { + // TODO: Remove the check when done filling in register information + const RegisterDescription& description = registerDescriptions().at(address >> 1); + for (const RegisterItem& ri : description) { + QCheckBox* check = new QCheckBox; + check->setText(ri.description); + check->setEnabled(!ri.readonly); + box->addWidget(check); + connect(m_b[ri.start], SIGNAL(toggled(bool)), check, SLOT(setChecked(bool))); + connect(check, SIGNAL(toggled(bool)), m_b[ri.start], SLOT(setChecked(bool))); + } + } + m_ui.regDescription->setLayout(box); + updateRegister(); } void IOViewer::selectRegister() {@@ -158,7 +244,7 @@
void IOViewer::buttonPressed(QAbstractButton* button) { switch (m_ui.buttonBox->standardButton(button)) { case QDialogButtonBox::Reset: - update(); + updateRegister(); break; case QDialogButtonBox::Apply: writeback();
M
src/platform/qt/IOViewer.h
→
src/platform/qt/IOViewer.h
@@ -7,6 +7,7 @@ #ifndef QGBA_IOVIEWER
#define QGBA_IOVIEWER #include <QDialog> +#include <QList> #include "ui_IOViewer.h"@@ -18,10 +19,25 @@ class IOViewer : public QDialog {
Q_OBJECT public: + struct RegisterItem { + RegisterItem(const QString& description, uint start, uint size = 1, bool readonly = false) + : description(description) + , start(start) + , size(size) + , readonly(readonly) {} + uint start; + uint size; + bool readonly; + QString description; + }; + typedef QList<RegisterItem> RegisterDescription; + IOViewer(GameController* controller, QWidget* parent = nullptr); + + static const QList<RegisterDescription>& registerDescriptions(); public slots: - void update(); + void updateRegister(); void selectRegister(unsigned address); private slots:@@ -31,10 +47,13 @@ void writeback();
void selectRegister(); private: + static QList<RegisterDescription> s_registers; Ui::IOViewer m_ui; unsigned m_register; uint16_t m_value; + + QCheckBox* m_b[16]; GameController* m_controller; };
M
src/platform/qt/IOViewer.ui
→
src/platform/qt/IOViewer.ui
@@ -372,7 +372,7 @@ </property>
</widget> </item> <item> - <widget class="QWidget" name="widget" native="true"> + <widget class="QWidget" name="regDescription" native="true"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding"> <horstretch>0</horstretch>