GBA: Gigantic refactor and add preliminary Game Boy Player support
Jeffrey Pfau jeffrey@endrift.com
Mon, 13 Jul 2015 20:46:41 -0700
7 files changed,
257 insertions(+),
86 deletions(-)
M
src/gba/gba.c
→
src/gba/gba.c
@@ -81,6 +81,7 @@
gba->logHandler = 0; gba->logLevel = GBA_LOG_WARN | GBA_LOG_ERROR | GBA_LOG_FATAL; gba->stream = 0; + gba->keyCallback = 0; gba->biosChecksum = GBAChecksum(gba->memory.bios, SIZE_BIOS);@@ -776,6 +777,8 @@
if (gba->stream) { gba->stream->postVideoFrame(gba->stream, gba->video.renderer); } + + GBAHardwarePlayerUpdate(gba); struct GBAThread* thread = GBAThreadGetContext(); if (!thread) {
M
src/gba/gba.h
→
src/gba/gba.h
@@ -11,6 +11,7 @@
#include "arm.h" #include "debugger/debugger.h" +#include "gba/interface.h" #include "gba/memory.h" #include "gba/video.h" #include "gba/audio.h"@@ -35,37 +36,6 @@ IRQ_KEYPAD = 0xC,
IRQ_GAMEPAK = 0xD }; -enum GBALogLevel { - GBA_LOG_FATAL = 0x01, - GBA_LOG_ERROR = 0x02, - GBA_LOG_WARN = 0x04, - GBA_LOG_INFO = 0x08, - GBA_LOG_DEBUG = 0x10, - GBA_LOG_STUB = 0x20, - - GBA_LOG_GAME_ERROR = 0x100, - GBA_LOG_SWI = 0x200, - GBA_LOG_STATUS = 0x400, - GBA_LOG_SIO = 0x800, - - GBA_LOG_ALL = 0xF3F, -}; - -enum GBAKey { - GBA_KEY_A = 0, - GBA_KEY_B = 1, - GBA_KEY_SELECT = 2, - GBA_KEY_START = 3, - GBA_KEY_RIGHT = 4, - GBA_KEY_LEFT = 5, - GBA_KEY_UP = 6, - GBA_KEY_DOWN = 7, - GBA_KEY_R = 8, - GBA_KEY_L = 9, - GBA_KEY_MAX, - GBA_KEY_NONE = -1 -}; - enum GBAComponent { GBA_COMPONENT_DEBUGGER, GBA_COMPONENT_CHEAT_DEVICE,@@ -85,18 +55,9 @@ SP_BASE_SUPERVISOR = 0x03007FE0
}; struct GBA; -struct GBARotationSource; struct GBAThread; struct Patch; struct VFile; - -typedef void (*GBALogHandler)(struct GBAThread*, enum GBALogLevel, const char* format, va_list args); - -struct GBAAVStream { - void (*postVideoFrame)(struct GBAAVStream*, struct GBAVideoRenderer* renderer); - void (*postAudioFrame)(struct GBAAVStream*, int16_t left, int16_t right); - void (*postAudioBuffer)(struct GBAAVStream*, struct GBAAudio*); -}; struct GBATimer { uint16_t reload;@@ -150,6 +111,7 @@
GBALogHandler logHandler; enum GBALogLevel logLevel; struct GBAAVStream* stream; + struct GBAKeyCallback* keyCallback; enum GBAIdleLoopOptimization idleOptimization; uint32_t idleLoop;
M
src/gba/hardware.c
→
src/gba/hardware.c
@@ -5,6 +5,7 @@ * 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 "hardware.h" +#include "gba/io.h" #include "gba/serialize.h" #include "util/hash.h"@@ -25,6 +26,11 @@ static void _rumbleReadPins(struct GBACartridgeHardware* hw);
static void _lightReadPins(struct GBACartridgeHardware* hw); +static uint16_t _gbpRead(struct GBAKeyCallback*); +static bool _gbpSioLoad(struct GBASIODriver* driver); +static uint16_t _gbpSioWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value); +static int32_t _gbpSioProcessEvents(struct GBASIODriver* driver, int32_t cycles); + static const int RTC_BYTES[8] = { 0, // Force reset 0, // Empty@@ -46,6 +52,7 @@ hw->devices = HW_NONE;
hw->direction = GPIO_WRITE_ONLY; hw->pinState = 0; hw->direction = 0; + hw->gbpRunning = false; } void GBAHardwareGPIOWrite(struct GBACartridgeHardware* hw, uint32_t address, uint16_t value) {@@ -461,12 +468,128 @@ };
static const uint32_t _logoHash = 0xEEDA6963; +static const uint32_t _gbpTxData[] = { + 0x0000494E, 0x0000494E, + 0xB6B1494E, 0xB6B1544E, + 0xABB1544E, 0xABB14E45, + 0xB1BA4E45, 0xB1BA4F44, + 0xB0BB4F44, 0xB0BB8002, + 0x10000010, 0x20000013, + 0x30000003, 0x30000003, + 0x30000003, 0x30000003, + 0x30000003, 0x00000000, +}; + +static const uint32_t _gbpRxData[] = { + 0x00000000, 0x494EB6B1, + 0x494EB6B1, 0x544EB6B1, + 0x544EABB1, 0x4E45ABB1, + 0x4E45B1BA, 0x4F44B1BA, + 0x4F44B0BB, 0x8000B0BB, + 0x10000010, 0x20000013, + 0x40000004, 0x40000004, + 0x40000004, 0x40000004, + 0x40000004, 0x40000004 +}; + bool GBAHardwarePlayerCheckScreen(const struct GBAVideo* video) { if (memcmp(video->palette, _logoPalette, sizeof(_logoPalette)) != 0) { return false; } uint32_t hash = hash32(&video->renderer->vram[0x4000], 0x4000, 0); return hash == _logoHash; +} + +void GBAHardwarePlayerUpdate(struct GBA* gba) { + if (gba->memory.hw.gbpRunning) { + if (GBAHardwarePlayerCheckScreen(&gba->video)) { + ++gba->memory.hw.gbpInputsPosted; + gba->memory.hw.gbpInputsPosted %= 3; + gba->keyCallback = &gba->memory.hw.gbpCallback.d; + } else { + // TODO: Save and restore + gba->keyCallback = 0; + } + gba->memory.hw.gbpTxPosition = 0; + return; + } + if (gba->keyCallback || gba->sio.drivers.normal) { + return; + } + if (GBAHardwarePlayerCheckScreen(&gba->video)) { + gba->memory.hw.gbpRunning = true; + gba->memory.hw.gbpCallback.d.readKeys = _gbpRead; + gba->memory.hw.gbpCallback.p = &gba->memory.hw; + gba->memory.hw.gbpDriver.d.init = 0; + gba->memory.hw.gbpDriver.d.deinit = 0; + gba->memory.hw.gbpDriver.d.load = _gbpSioLoad; + gba->memory.hw.gbpDriver.d.unload = 0; + gba->memory.hw.gbpDriver.d.writeRegister = _gbpSioWriteRegister; + gba->memory.hw.gbpDriver.d.processEvents = _gbpSioProcessEvents; + gba->memory.hw.gbpDriver.p = &gba->memory.hw; + gba->memory.hw.gbpInputsPosted = 0; + gba->keyCallback = &gba->memory.hw.gbpCallback.d; + GBASIOSetDriver(&gba->sio, &gba->memory.hw.gbpDriver.d, SIO_NORMAL_32); + } +} + +uint16_t _gbpRead(struct GBAKeyCallback* callback) { + struct GBAGBPKeyCallback* gbpCallback = (struct GBAGBPKeyCallback*) callback; + if (gbpCallback->p->gbpInputsPosted == 2) { + return 0x30F; + } + return 0x3FF; +} + +bool _gbpSioLoad(struct GBASIODriver* driver) { + struct GBAGBPSIODriver* gbp = (struct GBAGBPSIODriver*) driver; + gbp->p->gbpTxPosition = 0; + gbp->p->gbpNextEvent = INT_MAX; + return true; +} + +uint16_t _gbpSioWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value) { + struct GBAGBPSIODriver* gbp = (struct GBAGBPSIODriver*) driver; + if (address == REG_SIOCNT) { + if (value & 0x0080) { + if (gbp->p->gbpTxPosition <= 16 && gbp->p->gbpTxPosition > 0) { + uint32_t rx = gbp->p->p->memory.io[REG_SIODATA32_LO >> 1] | (gbp->p->p->memory.io[REG_SIODATA32_HI >> 1] << 16); + uint32_t expected = _gbpRxData[gbp->p->gbpTxPosition]; + // TODO: Check expected + uint32_t mask = 0; + if (gbp->p->gbpTxPosition == 15) { + mask = 0x22; + if (gbp->p->p->rumble) { + gbp->p->p->rumble->setRumble(gbp->p->p->rumble, (rx & mask) == mask); + } + } + } + gbp->p->gbpNextEvent = 2048; + } + value &= 0x78FB; + } + return value; +} + +int32_t _gbpSioProcessEvents(struct GBASIODriver* driver, int32_t cycles) { + struct GBAGBPSIODriver* gbp = (struct GBAGBPSIODriver*) driver; + gbp->p->gbpNextEvent -= cycles; + if (gbp->p->gbpNextEvent <= 0) { + uint32_t tx = 0; + if (gbp->p->gbpTxPosition <= 16) { + tx = _gbpTxData[gbp->p->gbpTxPosition]; + ++gbp->p->gbpTxPosition; + } + gbp->p->p->memory.io[REG_SIODATA32_LO >> 1] = tx; + gbp->p->p->memory.io[REG_SIODATA32_HI >> 1] = tx >> 16; + if (gbp->d.p->normalControl.irq) { + GBARaiseIRQ(gbp->p->p, IRQ_SIO); + } + gbp->d.p->normalControl.start = 0; + gbp->p->p->memory.io[REG_SIOCNT >> 1] = gbp->d.p->siocnt; + gbp->p->gbpNextEvent = INT_MAX; + } + return gbp->p->gbpNextEvent; } // == Serialization
M
src/gba/hardware.h
→
src/gba/hardware.h
@@ -7,33 +7,13 @@ #ifndef GBA_HARDWARE_H
#define GBA_HARDWARE_H #include "util/common.h" +#include "gba/interface.h" #include "macros.h" #include <time.h> #define IS_GPIO_REGISTER(reg) ((reg) == GPIO_REG_DATA || (reg) == GPIO_REG_DIRECTION || (reg) == GPIO_REG_CONTROL) - -struct GBARotationSource { - void (*sample)(struct GBARotationSource*); - - int32_t (*readTiltX)(struct GBARotationSource*); - int32_t (*readTiltY)(struct GBARotationSource*); - - int32_t (*readGyroZ)(struct GBARotationSource*); -}; - -struct GBALuminanceSource { - void (*sample)(struct GBALuminanceSource*); - - uint8_t (*readLuminance)(struct GBALuminanceSource*); -}; - -struct GBARTCSource { - void (*sample)(struct GBARTCSource*); - - time_t (*unixTime)(struct GBARTCSource*); -}; struct GBARTCGenericSource { struct GBARTCSource d;@@ -102,6 +82,16 @@ struct GBARumble {
void (*setRumble)(struct GBARumble*, int enable); }; +struct GBAGBPKeyCallback { + struct GBAKeyCallback d; + struct GBACartridgeHardware* p; +}; + +struct GBAGBPSIODriver { + struct GBASIODriver d; + struct GBACartridgeHardware* p; +}; + DECL_BITFIELD(GPIOPin, uint16_t); struct GBACartridgeHardware {@@ -125,6 +115,13 @@
uint16_t tiltX; uint16_t tiltY; int tiltState; + + bool gbpRunning; + int gbpInputsPosted; + int gbpTxPosition; + struct GBAGBPKeyCallback gbpCallback; + struct GBAGBPSIODriver gbpDriver; + int32_t gbpNextEvent; }; void GBAHardwareInit(struct GBACartridgeHardware* gpio, uint16_t* gpioBase);@@ -141,6 +138,7 @@ void GBAHardwareTiltWrite(struct GBACartridgeHardware* gpio, uint32_t address, uint8_t value);
uint8_t GBAHardwareTiltRead(struct GBACartridgeHardware* gpio, uint32_t address); struct GBAVideo; +void GBAHardwarePlayerUpdate(struct GBA* gba); bool GBAHardwarePlayerCheckScreen(const struct GBAVideo* video); void GBARTCGenericSourceInit(struct GBARTCGenericSource* rtc, struct GBA* gba);
A
src/gba/interface.h
@@ -0,0 +1,101 @@
+/* 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 INTERFACE_H +#define INTERFACE_H + +#include "util/common.h" + +enum GBALogLevel { + GBA_LOG_FATAL = 0x01, + GBA_LOG_ERROR = 0x02, + GBA_LOG_WARN = 0x04, + GBA_LOG_INFO = 0x08, + GBA_LOG_DEBUG = 0x10, + GBA_LOG_STUB = 0x20, + + GBA_LOG_GAME_ERROR = 0x100, + GBA_LOG_SWI = 0x200, + GBA_LOG_STATUS = 0x400, + GBA_LOG_SIO = 0x800, + + GBA_LOG_ALL = 0xF3F, +}; + +enum GBAKey { + GBA_KEY_A = 0, + GBA_KEY_B = 1, + GBA_KEY_SELECT = 2, + GBA_KEY_START = 3, + GBA_KEY_RIGHT = 4, + GBA_KEY_LEFT = 5, + GBA_KEY_UP = 6, + GBA_KEY_DOWN = 7, + GBA_KEY_R = 8, + GBA_KEY_L = 9, + GBA_KEY_MAX, + GBA_KEY_NONE = -1 +}; + +enum GBASIOMode { + SIO_NORMAL_8 = 0, + SIO_NORMAL_32 = 1, + SIO_MULTI = 2, + SIO_UART = 3, + SIO_GPIO = 8, + SIO_JOYBUS = 12 +}; + +struct GBA; +struct GBAAudio; +struct GBASIO; +struct GBAThread; +struct GBAVideoRenderer; + +typedef void (*GBALogHandler)(struct GBAThread*, enum GBALogLevel, const char* format, va_list args); + +struct GBAAVStream { + void (*postVideoFrame)(struct GBAAVStream*, struct GBAVideoRenderer* renderer); + void (*postAudioFrame)(struct GBAAVStream*, int16_t left, int16_t right); + void (*postAudioBuffer)(struct GBAAVStream*, struct GBAAudio*); +}; + +struct GBAKeyCallback { + uint16_t (*readKeys)(struct GBAKeyCallback*); +}; + +struct GBARotationSource { + void (*sample)(struct GBARotationSource*); + + int32_t (*readTiltX)(struct GBARotationSource*); + int32_t (*readTiltY)(struct GBARotationSource*); + + int32_t (*readGyroZ)(struct GBARotationSource*); +}; + +struct GBALuminanceSource { + void (*sample)(struct GBALuminanceSource*); + + uint8_t (*readLuminance)(struct GBALuminanceSource*); +}; + +struct GBARTCSource { + void (*sample)(struct GBARTCSource*); + + time_t (*unixTime)(struct GBARTCSource*); +}; + +struct GBASIODriver { + struct GBASIO* p; + + bool (*init)(struct GBASIODriver* driver); + void (*deinit)(struct GBASIODriver* driver); + bool (*load)(struct GBASIODriver* driver); + bool (*unload)(struct GBASIODriver* driver); + uint16_t (*writeRegister)(struct GBASIODriver* driver, uint32_t address, uint16_t value); + int32_t (*processEvents)(struct GBASIODriver* driver, int32_t cycles); +}; + +#endif
M
src/gba/io.c
→
src/gba/io.c
@@ -584,14 +584,18 @@
case REG_KEYINPUT: if (gba->rr && gba->rr->isPlaying(gba->rr)) { return 0x3FF ^ gba->rr->queryInput(gba->rr); - } else if (gba->keySource) { - uint16_t input = *gba->keySource; + } else { + uint16_t input = 0x3FF; + if (gba->keyCallback) { + input = gba->keyCallback->readKeys(gba->keyCallback); + } else if (gba->keySource) { + input = *gba->keySource; + } if (gba->rr && gba->rr->isRecording(gba->rr)) { gba->rr->logInput(gba->rr, input); } return 0x3FF ^ input; } - break; case REG_SIOCNT: return gba->sio.siocnt;
M
src/gba/sio.h
→
src/gba/sio.h
@@ -8,34 +8,14 @@ #define GBA_SIO_H
#include "util/common.h" +#include "gba/interface.h" + #define MAX_GBAS 4 extern const int GBASIOCyclesPerTransfer[4][MAX_GBAS]; -enum GBASIOMode { - SIO_NORMAL_8 = 0, - SIO_NORMAL_32 = 1, - SIO_MULTI = 2, - SIO_UART = 3, - SIO_GPIO = 8, - SIO_JOYBUS = 12 -}; - enum { RCNT_INITIAL = 0x8000 -}; - -struct GBASIO; - -struct GBASIODriver { - struct GBASIO* p; - - bool (*init)(struct GBASIODriver* driver); - void (*deinit)(struct GBASIODriver* driver); - bool (*load)(struct GBASIODriver* driver); - bool (*unload)(struct GBASIODriver* driver); - uint16_t (*writeRegister)(struct GBASIODriver* driver, uint32_t address, uint16_t value); - int32_t (*processEvents)(struct GBASIODriver* driver, int32_t cycles); }; struct GBASIODriverSet {