mirror of https://github.com/mgba-emu/mgba.git
Qt: Start adding I/O register info
This commit is contained in:
parent
d31326a024
commit
d9a60540f7
|
@ -8,6 +8,7 @@
|
||||||
#include "GameController.h"
|
#include "GameController.h"
|
||||||
|
|
||||||
#include <QFontDatabase>
|
#include <QFontDatabase>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include "gba/io.h"
|
#include "gba/io.h"
|
||||||
|
@ -15,6 +16,119 @@ extern "C" {
|
||||||
|
|
||||||
using namespace QGBA;
|
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)
|
IOViewer::IOViewer(GameController* controller, QWidget* parent)
|
||||||
: QDialog(parent)
|
: QDialog(parent)
|
||||||
, m_controller(controller)
|
, m_controller(controller)
|
||||||
|
@ -36,104 +150,50 @@ IOViewer::IOViewer(GameController* controller, QWidget* parent)
|
||||||
connect(m_ui.buttonBox, SIGNAL(rejected()), this, SLOT(close()));
|
connect(m_ui.buttonBox, SIGNAL(rejected()), this, SLOT(close()));
|
||||||
connect(m_ui.regSelect, SIGNAL(currentIndexChanged(int)), this, SLOT(selectRegister()));
|
connect(m_ui.regSelect, SIGNAL(currentIndexChanged(int)), this, SLOT(selectRegister()));
|
||||||
|
|
||||||
connect(m_ui.b0, SIGNAL(toggled(bool)), this, SLOT(bitFlipped()));
|
m_b[0] = m_ui.b0;
|
||||||
connect(m_ui.b1, SIGNAL(toggled(bool)), this, SLOT(bitFlipped()));
|
m_b[1] = m_ui.b1;
|
||||||
connect(m_ui.b2, SIGNAL(toggled(bool)), this, SLOT(bitFlipped()));
|
m_b[2] = m_ui.b2;
|
||||||
connect(m_ui.b3, SIGNAL(toggled(bool)), this, SLOT(bitFlipped()));
|
m_b[3] = m_ui.b3;
|
||||||
connect(m_ui.b4, SIGNAL(toggled(bool)), this, SLOT(bitFlipped()));
|
m_b[4] = m_ui.b4;
|
||||||
connect(m_ui.b5, SIGNAL(toggled(bool)), this, SLOT(bitFlipped()));
|
m_b[5] = m_ui.b5;
|
||||||
connect(m_ui.b6, SIGNAL(toggled(bool)), this, SLOT(bitFlipped()));
|
m_b[6] = m_ui.b6;
|
||||||
connect(m_ui.b7, SIGNAL(toggled(bool)), this, SLOT(bitFlipped()));
|
m_b[7] = m_ui.b7;
|
||||||
connect(m_ui.b8, SIGNAL(toggled(bool)), this, SLOT(bitFlipped()));
|
m_b[8] = m_ui.b8;
|
||||||
connect(m_ui.b9, SIGNAL(toggled(bool)), this, SLOT(bitFlipped()));
|
m_b[9] = m_ui.b9;
|
||||||
connect(m_ui.bA, SIGNAL(toggled(bool)), this, SLOT(bitFlipped()));
|
m_b[10] = m_ui.bA;
|
||||||
connect(m_ui.bB, SIGNAL(toggled(bool)), this, SLOT(bitFlipped()));
|
m_b[11] = m_ui.bB;
|
||||||
connect(m_ui.bC, SIGNAL(toggled(bool)), this, SLOT(bitFlipped()));
|
m_b[12] = m_ui.bC;
|
||||||
connect(m_ui.bD, SIGNAL(toggled(bool)), this, SLOT(bitFlipped()));
|
m_b[13] = m_ui.bD;
|
||||||
connect(m_ui.bE, SIGNAL(toggled(bool)), this, SLOT(bitFlipped()));
|
m_b[14] = m_ui.bE;
|
||||||
connect(m_ui.bF, SIGNAL(toggled(bool)), this, SLOT(bitFlipped()));
|
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);
|
selectRegister(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IOViewer::update() {
|
void IOViewer::updateRegister() {
|
||||||
m_value = 0;
|
m_value = 0;
|
||||||
|
uint16_t value = 0;
|
||||||
m_controller->threadInterrupt();
|
m_controller->threadInterrupt();
|
||||||
if (m_controller->isLoaded()) {
|
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_controller->threadContinue();
|
||||||
|
|
||||||
m_ui.regValue->setText("0x" + QString("%1").arg(m_value, 4, 16, QChar('0')).toUpper());
|
for (int i = 0; i < 16; ++i) {
|
||||||
bool signalsBlocked;
|
m_b[i]->setChecked(value & (1 << i) ? Qt::Checked : Qt::Unchecked);
|
||||||
signalsBlocked = m_ui.b0->blockSignals(true);
|
}
|
||||||
m_ui.b0->setChecked(m_value & 0x0001 ? Qt::Checked : Qt::Unchecked);
|
m_value = value;
|
||||||
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() {
|
void IOViewer::bitFlipped() {
|
||||||
m_value = 0;
|
m_value = 0;
|
||||||
m_value |= m_ui.b0->isChecked() << 0x0;
|
for (int i = 0; i < 16; ++i) {
|
||||||
m_value |= m_ui.b1->isChecked() << 0x1;
|
m_value |= m_b[i]->isChecked() << i;
|
||||||
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("0x" + QString("%1").arg(m_value, 4, 16, QChar('0')).toUpper());
|
m_ui.regValue->setText("0x" + QString("%1").arg(m_value, 4, 16, QChar('0')).toUpper());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,12 +203,38 @@ void IOViewer::writeback() {
|
||||||
GBAIOWrite(m_controller->thread()->gba, m_register, m_value);
|
GBAIOWrite(m_controller->thread()->gba, m_register, m_value);
|
||||||
}
|
}
|
||||||
m_controller->threadContinue();
|
m_controller->threadContinue();
|
||||||
update();
|
updateRegister();
|
||||||
}
|
}
|
||||||
|
|
||||||
void IOViewer::selectRegister(unsigned address) {
|
void IOViewer::selectRegister(unsigned address) {
|
||||||
m_register = 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() {
|
void IOViewer::selectRegister() {
|
||||||
|
@ -158,7 +244,7 @@ void IOViewer::selectRegister() {
|
||||||
void IOViewer::buttonPressed(QAbstractButton* button) {
|
void IOViewer::buttonPressed(QAbstractButton* button) {
|
||||||
switch (m_ui.buttonBox->standardButton(button)) {
|
switch (m_ui.buttonBox->standardButton(button)) {
|
||||||
case QDialogButtonBox::Reset:
|
case QDialogButtonBox::Reset:
|
||||||
update();
|
updateRegister();
|
||||||
break;
|
break;
|
||||||
case QDialogButtonBox::Apply:
|
case QDialogButtonBox::Apply:
|
||||||
writeback();
|
writeback();
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#define QGBA_IOVIEWER
|
#define QGBA_IOVIEWER
|
||||||
|
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
|
#include <QList>
|
||||||
|
|
||||||
#include "ui_IOViewer.h"
|
#include "ui_IOViewer.h"
|
||||||
|
|
||||||
|
@ -18,10 +19,25 @@ class IOViewer : public QDialog {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
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);
|
IOViewer(GameController* controller, QWidget* parent = nullptr);
|
||||||
|
|
||||||
|
static const QList<RegisterDescription>& registerDescriptions();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void update();
|
void updateRegister();
|
||||||
void selectRegister(unsigned address);
|
void selectRegister(unsigned address);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
@ -31,11 +47,14 @@ private slots:
|
||||||
void selectRegister();
|
void selectRegister();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
static QList<RegisterDescription> s_registers;
|
||||||
Ui::IOViewer m_ui;
|
Ui::IOViewer m_ui;
|
||||||
|
|
||||||
unsigned m_register;
|
unsigned m_register;
|
||||||
uint16_t m_value;
|
uint16_t m_value;
|
||||||
|
|
||||||
|
QCheckBox* m_b[16];
|
||||||
|
|
||||||
GameController* m_controller;
|
GameController* m_controller;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -372,7 +372,7 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QWidget" name="widget" native="true">
|
<widget class="QWidget" name="regDescription" native="true">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
|
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
|
||||||
<horstretch>0</horstretch>
|
<horstretch>0</horstretch>
|
||||||
|
|
Loading…
Reference in New Issue