From 43d9c8b754b4ccc2376ac319d56da25a7ced8eda Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Mon, 13 Jul 2015 20:46:41 -0700 Subject: [PATCH] GBA: Gigantic refactor and add preliminary Game Boy Player support --- src/gba/gba.c | 3 ++ src/gba/gba.h | 42 +-------------- src/gba/hardware.c | 123 ++++++++++++++++++++++++++++++++++++++++++++ src/gba/hardware.h | 40 +++++++------- src/gba/interface.h | 101 ++++++++++++++++++++++++++++++++++++ src/gba/io.c | 10 ++-- src/gba/sio.h | 24 +-------- 7 files changed, 257 insertions(+), 86 deletions(-) create mode 100644 src/gba/interface.h diff --git a/src/gba/gba.c b/src/gba/gba.c index 4e126e543..9cf50a3ef 100644 --- a/src/gba/gba.c +++ b/src/gba/gba.c @@ -81,6 +81,7 @@ static void GBAInit(struct ARMCore* cpu, struct ARMComponent* component) { 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); @@ -777,6 +778,8 @@ void GBAFrameEnded(struct GBA* gba) { gba->stream->postVideoFrame(gba->stream, gba->video.renderer); } + GBAHardwarePlayerUpdate(gba); + struct GBAThread* thread = GBAThreadGetContext(); if (!thread) { return; diff --git a/src/gba/gba.h b/src/gba/gba.h index 92847c84d..0e5d92a42 100644 --- a/src/gba/gba.h +++ b/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 @@ enum GBAIRQ { 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,19 +55,10 @@ enum { }; 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; uint16_t oldReload; @@ -150,6 +111,7 @@ struct GBA { GBALogHandler logHandler; enum GBALogLevel logLevel; struct GBAAVStream* stream; + struct GBAKeyCallback* keyCallback; enum GBAIdleLoopOptimization idleOptimization; uint32_t idleLoop; diff --git a/src/gba/hardware.c b/src/gba/hardware.c index e0c341e26..a2e61c927 100644 --- a/src/gba/hardware.c +++ b/src/gba/hardware.c @@ -5,6 +5,7 @@ * 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 @@ void GBAHardwareClear(struct GBACartridgeHardware* hw) { 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,6 +468,30 @@ static const uint16_t _logoPalette[] = { 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; @@ -469,6 +500,98 @@ bool GBAHardwarePlayerCheckScreen(const struct GBAVideo* video) { 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 void GBAHardwareSerialize(const struct GBACartridgeHardware* hw, struct GBASerializedState* state) { diff --git a/src/gba/hardware.h b/src/gba/hardware.h index 5ba17c221..8a1930685 100644 --- a/src/gba/hardware.h +++ b/src/gba/hardware.h @@ -7,6 +7,7 @@ #define GBA_HARDWARE_H #include "util/common.h" +#include "gba/interface.h" #include "macros.h" @@ -14,27 +15,6 @@ #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; struct GBA* p; @@ -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 @@ struct GBACartridgeHardware { 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, u 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); diff --git a/src/gba/interface.h b/src/gba/interface.h new file mode 100644 index 000000000..7b5e1c9f5 --- /dev/null +++ b/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 diff --git a/src/gba/io.c b/src/gba/io.c index a93227669..f5e383713 100644 --- a/src/gba/io.c +++ b/src/gba/io.c @@ -584,14 +584,18 @@ uint16_t GBAIORead(struct GBA* gba, uint32_t address) { 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; diff --git a/src/gba/sio.h b/src/gba/sio.h index 61e979385..5963698d9 100644 --- a/src/gba/sio.h +++ b/src/gba/sio.h @@ -8,36 +8,16 @@ #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 { struct GBASIODriver* normal; struct GBASIODriver* multiplayer;