Qt: Configurable game overrides for Game Pak devices

This commit is contained in:
Jeffrey Pfau 2015-01-25 15:21:48 -08:00
parent f4fcdf35d4
commit f33e9c060f
11 changed files with 147 additions and 53 deletions

View File

@ -38,9 +38,13 @@ static const int RTC_BYTES[8] = {
}; };
void GBAGPIOInit(struct GBACartridgeGPIO* gpio, uint16_t* base) { void GBAGPIOInit(struct GBACartridgeGPIO* gpio, uint16_t* base) {
gpio->gpioBase = base;
GBAGPIOClear(gpio);
}
void GBAGPIOClear(struct GBACartridgeGPIO* gpio) {
gpio->gpioDevices = GPIO_NONE; gpio->gpioDevices = GPIO_NONE;
gpio->direction = GPIO_WRITE_ONLY; gpio->direction = GPIO_WRITE_ONLY;
gpio->gpioBase = base;
gpio->pinState = 0; gpio->pinState = 0;
gpio->direction = 0; gpio->direction = 0;
} }

View File

@ -11,6 +11,7 @@
#define IS_GPIO_REGISTER(reg) ((reg) == GPIO_REG_DATA || (reg) == GPIO_REG_DIRECTION || (reg) == GPIO_REG_CONTROL) #define IS_GPIO_REGISTER(reg) ((reg) == GPIO_REG_DATA || (reg) == GPIO_REG_DIRECTION || (reg) == GPIO_REG_CONTROL)
enum GPIODevice { enum GPIODevice {
GPIO_NO_OVERRIDE = 0x8000,
GPIO_NONE = 0, GPIO_NONE = 0,
GPIO_RTC = 1, GPIO_RTC = 1,
GPIO_RUMBLE = 2, GPIO_RUMBLE = 2,
@ -114,6 +115,7 @@ struct GBACartridgeGPIO {
}; };
void GBAGPIOInit(struct GBACartridgeGPIO* gpio, uint16_t* gpioBase); void GBAGPIOInit(struct GBACartridgeGPIO* gpio, uint16_t* gpioBase);
void GBAGPIOClear(struct GBACartridgeGPIO* gpio);
void GBAGPIOWrite(struct GBACartridgeGPIO* gpio, uint32_t address, uint16_t value); void GBAGPIOWrite(struct GBACartridgeGPIO* gpio, uint32_t address, uint16_t value);
void GBAGPIOInitRTC(struct GBACartridgeGPIO* gpio); void GBAGPIOInitRTC(struct GBACartridgeGPIO* gpio);

View File

@ -173,27 +173,35 @@ bool GBAOverrideFind(const struct Configuration* config, struct GBACartridgeOver
} }
void GBAOverrideApply(struct GBA* gba, const struct GBACartridgeOverride* override) { void GBAOverrideApply(struct GBA* gba, const struct GBACartridgeOverride* override) {
GBASavedataForceType(&gba->memory.savedata, override->savetype); if (override->savetype != SAVEDATA_AUTODETECT) {
GBASavedataForceType(&gba->memory.savedata, override->savetype);
if (override->hardware & GPIO_RTC) {
GBAGPIOInitRTC(&gba->memory.gpio);
} }
if (override->hardware & GPIO_GYRO) { if (override->hardware != GPIO_NO_OVERRIDE) {
GBAGPIOInitGyro(&gba->memory.gpio); GBAGPIOClear(&gba->memory.gpio);
if (override->hardware & GPIO_RTC) {
GBAGPIOInitRTC(&gba->memory.gpio);
}
if (override->hardware & GPIO_GYRO) {
GBAGPIOInitGyro(&gba->memory.gpio);
}
if (override->hardware & GPIO_RUMBLE) {
GBAGPIOInitRumble(&gba->memory.gpio);
}
if (override->hardware & GPIO_LIGHT_SENSOR) {
GBAGPIOInitLightSensor(&gba->memory.gpio);
}
if (override->hardware & GPIO_TILT) {
GBAGPIOInitTilt(&gba->memory.gpio);
}
} }
if (override->hardware & GPIO_RUMBLE) { if (override->idleLoop != 0xFFFFFFFF) {
GBAGPIOInitRumble(&gba->memory.gpio); gba->busyLoop = override->idleLoop;
} }
if (override->hardware & GPIO_LIGHT_SENSOR) {
GBAGPIOInitLightSensor(&gba->memory.gpio);
}
if (override->hardware & GPIO_TILT) {
GBAGPIOInitTilt(&gba->memory.gpio);
}
gba->busyLoop = override->idleLoop;
} }

View File

@ -113,6 +113,11 @@ bool GBASavedataClone(struct GBASavedata* savedata, struct VFile* out) {
} }
void GBASavedataForceType(struct GBASavedata* savedata, enum SavedataType type) { void GBASavedataForceType(struct GBASavedata* savedata, enum SavedataType type) {
if (savedata->type != SAVEDATA_AUTODETECT) {
struct VFile* vf = savedata->vf;
GBASavedataDeinit(savedata);
GBASavedataInit(savedata, vf);
}
switch (type) { switch (type) {
case SAVEDATA_FLASH512: case SAVEDATA_FLASH512:
case SAVEDATA_FLASH1M: case SAVEDATA_FLASH1M:

View File

@ -8,7 +8,6 @@
#include "arm.h" #include "arm.h"
#include "gba.h" #include "gba.h"
#include "gba-config.h" #include "gba-config.h"
#include "gba-overrides.h"
#include "gba-serialize.h" #include "gba-serialize.h"
#include "debugger/debugger.h" #include "debugger/debugger.h"
@ -146,6 +145,9 @@ static THREAD_ENTRY _GBAThreadRun(void* context) {
if (GBAOverrideFind(threadContext->overrides, &override)) { if (GBAOverrideFind(threadContext->overrides, &override)) {
GBAOverrideApply(&gba, &override); GBAOverrideApply(&gba, &override);
} }
if (threadContext->hasOverride) {
GBAOverrideApply(&gba, &threadContext->override);
}
if (threadContext->bios && GBAIsBIOS(threadContext->bios)) { if (threadContext->bios && GBAIsBIOS(threadContext->bios)) {
GBALoadBIOS(&gba, threadContext->bios); GBALoadBIOS(&gba, threadContext->bios);

View File

@ -10,6 +10,7 @@
#include "gba.h" #include "gba.h"
#include "gba-input.h" #include "gba-input.h"
#include "gba-overrides.h"
#include "util/threading.h" #include "util/threading.h"
@ -72,6 +73,9 @@ struct GBAThread {
struct GBAAVStream* stream; struct GBAAVStream* stream;
struct Configuration* overrides; struct Configuration* overrides;
bool hasOverride;
struct GBACartridgeOverride override;
// Run-time options // Run-time options
int frameskip; int frameskip;
float fpsTarget; float fpsTarget;

View File

@ -140,6 +140,11 @@ GameController::~GameController() {
delete[] m_drawContext; delete[] m_drawContext;
} }
void GameController::setOverride(const GBACartridgeOverride& override) {
m_threadContext.override = override;
m_threadContext.hasOverride = true;
}
#ifdef USE_GDB_STUB #ifdef USE_GDB_STUB
ARMDebugger* GameController::debugger() { ARMDebugger* GameController::debugger() {
return m_threadContext.debugger; return m_threadContext.debugger;

View File

@ -56,6 +56,9 @@ public:
void setInputController(InputController* controller) { m_inputController = controller; } void setInputController(InputController* controller) { m_inputController = controller; }
void setOverrides(Configuration* overrides) { m_threadContext.overrides = overrides; } void setOverrides(Configuration* overrides) { m_threadContext.overrides = overrides; }
void setOverride(const GBACartridgeOverride& override);
void clearOverride() { m_threadContext.hasOverride = false; }
#ifdef USE_GDB_STUB #ifdef USE_GDB_STUB
ARMDebugger* debugger(); ARMDebugger* debugger();
void setDebugger(ARMDebugger*); void setDebugger(ARMDebugger*);

View File

@ -38,46 +38,99 @@ GamePakView::GamePakView(GameController* controller, QWidget* parent)
m_ui.time->setDateTime(QDateTime::currentDateTime()); m_ui.time->setDateTime(QDateTime::currentDateTime());
}); });
connect(m_ui.hwAutodetect, &QAbstractButton::toggled, [this] (bool enabled) {
m_ui.hwRTC->setEnabled(!enabled);
m_ui.hwGyro->setEnabled(!enabled);
m_ui.hwLight->setEnabled(!enabled);
m_ui.hwTilt->setEnabled(!enabled);
m_ui.hwRumble->setEnabled(!enabled);
});
connect(m_ui.savetype, SIGNAL(currentIndexChanged(int)), this, SLOT(updateOverrides()));
connect(m_ui.hwAutodetect, SIGNAL(clicked()), this, SLOT(updateOverrides()));
connect(m_ui.hwRTC, SIGNAL(clicked()), this, SLOT(updateOverrides()));
connect(m_ui.hwGyro, SIGNAL(clicked()), this, SLOT(updateOverrides()));
connect(m_ui.hwLight, SIGNAL(clicked()), this, SLOT(updateOverrides()));
connect(m_ui.hwTilt, SIGNAL(clicked()), this, SLOT(updateOverrides()));
connect(m_ui.hwRumble, SIGNAL(clicked()), this, SLOT(updateOverrides()));
if (controller->isLoaded()) { if (controller->isLoaded()) {
gameStarted(controller->thread()); gameStarted(controller->thread());
} }
} }
void GamePakView::updateOverrides() {
GBACartridgeOverride override = {
"",
static_cast<SavedataType>(m_ui.savetype->currentIndex() - 1),
GPIO_NO_OVERRIDE,
0xFFFFFFFF
};
if (!m_ui.hwAutodetect->isChecked()) {
override.hardware = GPIO_NONE;
if (m_ui.hwRTC->isChecked()) {
override.hardware |= GPIO_RTC;
}
if (m_ui.hwGyro->isChecked()) {
override.hardware |= GPIO_GYRO;
}
if (m_ui.hwLight->isChecked()) {
override.hardware |= GPIO_LIGHT_SENSOR;
}
if (m_ui.hwTilt->isChecked()) {
override.hardware |= GPIO_TILT;
}
if (m_ui.hwRumble->isChecked()) {
override.hardware |= GPIO_RUMBLE;
}
}
if (override.savetype != SAVEDATA_AUTODETECT || override.hardware != GPIO_NO_OVERRIDE) {
m_controller->setOverride(override);
} else {
m_controller->clearOverride();
}
}
void GamePakView::gameStarted(GBAThread* thread) { void GamePakView::gameStarted(GBAThread* thread) {
if (!thread->gba) { if (!thread->gba) {
gameStopped(); gameStopped();
return; return;
} }
SavedataType savetype = thread->gba->memory.savedata.type; m_ui.savetype->setCurrentIndex(thread->gba->memory.savedata.type + 1);
if (m_ui.savetype->currentIndex() > 0) {
if (savetype > SAVEDATA_FORCE_NONE) {
VFile* vf = thread->gba->memory.savedata.vf;
GBASavedataDeinit(&thread->gba->memory.savedata);
GBASavedataInit(&thread->gba->memory.savedata, vf);
}
savetype = static_cast<SavedataType>(m_ui.savetype->currentIndex() - 1);
GBASavedataForceType(&thread->gba->memory.savedata, savetype);
}
if (savetype > SAVEDATA_AUTODETECT) {
m_ui.savetype->setCurrentIndex(savetype + 1);
}
m_ui.savetype->setEnabled(false); m_ui.savetype->setEnabled(false);
m_ui.sensorRTC->setChecked(thread->gba->memory.gpio.gpioDevices & GPIO_RTC); m_ui.hwAutodetect->setEnabled(false);
m_ui.sensorGyro->setChecked(thread->gba->memory.gpio.gpioDevices & GPIO_GYRO); m_ui.hwRTC->setEnabled(false);
m_ui.sensorLight->setChecked(thread->gba->memory.gpio.gpioDevices & GPIO_LIGHT_SENSOR); m_ui.hwGyro->setEnabled(false);
m_ui.sensorTilt->setChecked(thread->gba->memory.gpio.gpioDevices & GPIO_TILT); m_ui.hwLight->setEnabled(false);
m_ui.hwTilt->setEnabled(false);
m_ui.hwRumble->setEnabled(false);
m_ui.hwRTC->setChecked(thread->gba->memory.gpio.gpioDevices & GPIO_RTC);
m_ui.hwGyro->setChecked(thread->gba->memory.gpio.gpioDevices & GPIO_GYRO);
m_ui.hwLight->setChecked(thread->gba->memory.gpio.gpioDevices & GPIO_LIGHT_SENSOR);
m_ui.hwTilt->setChecked(thread->gba->memory.gpio.gpioDevices & GPIO_TILT);
m_ui.hwRumble->setChecked(thread->gba->memory.gpio.gpioDevices & GPIO_RUMBLE);
} }
void GamePakView::gameStopped() { void GamePakView::gameStopped() {
m_ui.savetype->setCurrentIndex(0); m_ui.savetype->setCurrentIndex(0);
m_ui.savetype->setEnabled(true); m_ui.savetype->setEnabled(true);
m_ui.sensorRTC->setChecked(false); m_ui.hwAutodetect->setEnabled(true);
m_ui.sensorGyro->setChecked(false); m_ui.hwRTC->setEnabled(!m_ui.hwAutodetect->isChecked());
m_ui.sensorLight->setChecked(false); m_ui.hwGyro->setEnabled(!m_ui.hwAutodetect->isChecked());
m_ui.sensorTilt->setChecked(false); m_ui.hwLight->setEnabled(!m_ui.hwAutodetect->isChecked());
m_ui.hwTilt->setEnabled(!m_ui.hwAutodetect->isChecked());
m_ui.hwRumble->setEnabled(!m_ui.hwAutodetect->isChecked());
m_ui.hwRTC->setChecked(false);
m_ui.hwGyro->setChecked(false);
m_ui.hwLight->setChecked(false);
m_ui.hwTilt->setChecked(false);
m_ui.hwRumble->setChecked(false);
} }
void GamePakView::setLuminanceValue(int value) { void GamePakView::setLuminanceValue(int value) {

View File

@ -23,6 +23,7 @@ public:
GamePakView(GameController* controller, QWidget* parent = nullptr); GamePakView(GameController* controller, QWidget* parent = nullptr);
private slots: private slots:
void updateOverrides();
void gameStarted(GBAThread*); void gameStarted(GBAThread*);
void gameStopped(); void gameStopped();
void setLuminanceValue(int); void setLuminanceValue(int);

View File

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>259</width> <width>251</width>
<height>373</height> <height>391</height>
</rect> </rect>
</property> </property>
<property name="sizePolicy"> <property name="sizePolicy">
@ -39,15 +39,12 @@
<item row="2" column="0"> <item row="2" column="0">
<widget class="QLabel" name="label_2"> <widget class="QLabel" name="label_2">
<property name="text"> <property name="text">
<string>Sensors</string> <string>Hardware</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="1"> <item row="2" column="1">
<widget class="QCheckBox" name="sensorAutodetect"> <widget class="QCheckBox" name="hwAutodetect">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text"> <property name="text">
<string>Autodetect</string> <string>Autodetect</string>
</property> </property>
@ -57,7 +54,7 @@
</widget> </widget>
</item> </item>
<item row="3" column="1"> <item row="3" column="1">
<widget class="QCheckBox" name="sensorRTC"> <widget class="QCheckBox" name="hwRTC">
<property name="enabled"> <property name="enabled">
<bool>false</bool> <bool>false</bool>
</property> </property>
@ -67,7 +64,7 @@
</widget> </widget>
</item> </item>
<item row="4" column="1"> <item row="4" column="1">
<widget class="QCheckBox" name="sensorGyro"> <widget class="QCheckBox" name="hwGyro">
<property name="enabled"> <property name="enabled">
<bool>false</bool> <bool>false</bool>
</property> </property>
@ -77,7 +74,7 @@
</widget> </widget>
</item> </item>
<item row="5" column="1"> <item row="5" column="1">
<widget class="QCheckBox" name="sensorTilt"> <widget class="QCheckBox" name="hwTilt">
<property name="enabled"> <property name="enabled">
<bool>false</bool> <bool>false</bool>
</property> </property>
@ -87,7 +84,7 @@
</widget> </widget>
</item> </item>
<item row="6" column="1"> <item row="6" column="1">
<widget class="QCheckBox" name="sensorLight"> <widget class="QCheckBox" name="hwLight">
<property name="enabled"> <property name="enabled">
<bool>false</bool> <bool>false</bool>
</property> </property>
@ -137,6 +134,16 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="7" column="1">
<widget class="QCheckBox" name="hwRumble">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Rumble</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>