GBA Cart: Refactor hardware.c into cart/gpio.c etc

This commit is contained in:
Vicki Pfau 2021-04-03 01:18:23 -07:00
parent 9a192d9ab1
commit bc16a1bfe3
15 changed files with 230 additions and 231 deletions

View File

@ -13,13 +13,10 @@ CXX_GUARD_START
#include <mgba/core/log.h>
#include <mgba/core/timing.h>
#include <mgba/gba/interface.h>
#include <mgba/internal/gba/cart/ereader.h>
mLOG_DECLARE_CATEGORY(GBA_HW);
#define EREADER_DOTCODE_STRIDE 1420
#define EREADER_DOTCODE_SIZE (EREADER_DOTCODE_STRIDE * 40)
#define EREADER_CARDS_MAX 16
#define IS_GPIO_REGISTER(reg) ((reg) == GPIO_REG_DATA || (reg) == GPIO_REG_DIRECTION || (reg) == GPIO_REG_CONTROL)
struct GBARTCGenericSource {
@ -82,57 +79,8 @@ struct GBARTC {
uint8_t time[7];
};
struct GBAGBPKeyCallback {
struct mKeyCallback d;
struct GBACartridgeHardware* p;
};
struct GBAGBPSIODriver {
struct GBASIODriver d;
struct GBACartridgeHardware* p;
};
DECL_BITFIELD(GPIOPin, uint16_t);
DECL_BITFIELD(EReaderControl0, uint8_t);
DECL_BIT(EReaderControl0, Data, 0);
DECL_BIT(EReaderControl0, Clock, 1);
DECL_BIT(EReaderControl0, Direction, 2);
DECL_BIT(EReaderControl0, LedEnable, 3);
DECL_BIT(EReaderControl0, Scan, 4);
DECL_BIT(EReaderControl0, Phi, 5);
DECL_BIT(EReaderControl0, PowerEnable, 6);
DECL_BITFIELD(EReaderControl1, uint8_t);
DECL_BIT(EReaderControl1, Scanline, 1);
DECL_BIT(EReaderControl1, Unk1, 4);
DECL_BIT(EReaderControl1, Voltage, 5);
enum EReaderStateMachine {
EREADER_SERIAL_INACTIVE = 0,
EREADER_SERIAL_STARTING,
EREADER_SERIAL_BIT_0,
EREADER_SERIAL_BIT_1,
EREADER_SERIAL_BIT_2,
EREADER_SERIAL_BIT_3,
EREADER_SERIAL_BIT_4,
EREADER_SERIAL_BIT_5,
EREADER_SERIAL_BIT_6,
EREADER_SERIAL_BIT_7,
EREADER_SERIAL_END_BIT,
};
enum EReaderCommand {
EREADER_COMMAND_IDLE = 0, // TODO: Verify on hardware
EREADER_COMMAND_WRITE_DATA = 1,
EREADER_COMMAND_SET_INDEX = 0x22,
EREADER_COMMAND_READ_DATA = 0x23,
};
struct EReaderCard {
void* data;
size_t size;
};
struct GBACartridgeHardware {
struct GBA* p;
uint32_t devices;
@ -155,12 +103,6 @@ struct GBACartridgeHardware {
uint16_t tiltY;
int tiltState;
unsigned gbpInputsPosted;
int gbpTxPosition;
struct mTimingEvent gbpNextEvent;
struct GBAGBPKeyCallback gbpCallback;
struct GBAGBPSIODriver gbpDriver;
uint16_t eReaderData[44];
uint8_t eReaderSerial[92];
uint16_t eReaderRegisterUnk;
@ -193,17 +135,6 @@ void GBAHardwareGPIOWrite(struct GBACartridgeHardware* gpio, uint32_t address, u
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 GBAHardwareInitEReader(struct GBACartridgeHardware* hw);
void GBAHardwareEReaderWrite(struct GBACartridgeHardware* hw, uint32_t address, uint16_t value);
void GBAHardwareEReaderWriteFlash(struct GBACartridgeHardware* hw, uint32_t address, uint8_t value);
uint16_t GBAHardwareEReaderRead(struct GBACartridgeHardware* hw, uint32_t address);
uint8_t GBAHardwareEReaderReadFlash(struct GBACartridgeHardware* hw, uint32_t address);
void GBAHardwareEReaderScan(struct GBACartridgeHardware* hw, const void* data, size_t size);
void GBARTCGenericSourceInit(struct GBARTCGenericSource* rtc, struct GBA* gba);
struct GBASerializedState;

View File

@ -14,10 +14,10 @@ CXX_GUARD_START
#include <mgba/internal/arm/arm.h>
#include <mgba/internal/gba/dma.h>
#include <mgba/internal/gba/hardware.h>
#include <mgba/internal/gba/savedata.h>
#include <mgba/internal/gba/vfame.h>
#include <mgba/internal/gba/matrix.h>
#include <mgba/internal/gba/cart/gpio.h>
#include <mgba/internal/gba/cart/matrix.h>
#include <mgba/internal/gba/cart/vfame.h>
enum GBAMemoryRegion {
REGION_BIOS = 0x0,

View File

@ -10,8 +10,9 @@
CXX_GUARD_START
#include <mgba/gba/interface.h>
#include <mgba/core/log.h>
#include <mgba/gba/interface.h>
#include <mgba/internal/gba/sio/gbp.h>
#define MAX_GBAS 4
@ -69,6 +70,8 @@ struct GBASIO {
uint16_t rcnt;
uint16_t siocnt;
struct GBASIOPlayer gbp;
};
void GBASIOInit(struct GBASIO* sio);

View File

@ -0,0 +1,37 @@
/* Copyright (c) 2013-2021 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 GBA_GBP_H
#define GBA_GBP_H
#include <mgba-util/common.h>
CXX_GUARD_START
struct GBASIOPlayer;
struct GBASIOPlayerKeyCallback {
struct mKeyCallback d;
struct GBASIOPlayer* p;
};
struct GBASIOPlayer {
struct GBASIODriver d;
struct GBA* p;
unsigned inputsPosted;
int txPosition;
struct mTimingEvent event;
struct GBASIOPlayerKeyCallback callback;
};
void GBASIOPlayerInit(struct GBASIOPlayer* gbp);
void GBASIOPlayerReset(struct GBASIOPlayer* gbp);
struct GBAVideo;
void GBASIOPlayerUpdate(struct GBA* gba);
bool GBASIOPlayerCheckScreen(const struct GBAVideo* video);
CXX_GUARD_END
#endif

View File

@ -3,19 +3,20 @@ set(SOURCE_FILES
../gb/audio.c
audio.c
bios.c
cart/ereader.c
cart/gpio.c
cart/matrix.c
cart/vfame.c
cheats.c
cheats/codebreaker.c
cheats/gameshark.c
cheats/parv3.c
core.c
dma.c
ereader.c
gba.c
hardware.c
hle-bios.c
input.c
io.c
matrix.c
memory.c
overrides.c
renderers/cache-set.c
@ -29,13 +30,13 @@ set(SOURCE_FILES
serialize.c
sharkport.c
sio.c
sio/gbp.c
sio/joybus.c
timer.c
vfame.c
video.c)
set(SIO_FILES
sio/dolphin.c
sio/joybus.c
sio/lockstep.c)
set(EXTRA_FILES

View File

@ -4,7 +4,7 @@
* 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/. */
#include <mgba/internal/gba/hardware.h>
#include <mgba/internal/gba/cart/ereader.h>
#include <mgba/internal/arm/macros.h>
#include <mgba/internal/gba/gba.h>

View File

@ -1,9 +1,9 @@
/* Copyright (c) 2013-2015 Jeffrey Pfau
/* Copyright (c) 2013-2021 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/. */
#include <mgba/internal/gba/hardware.h>
#include <mgba/internal/gba/cart/gpio.h>
#include <mgba/internal/arm/macros.h>
#include <mgba/internal/gba/io.h>
@ -33,10 +33,6 @@ static void _rumbleReadPins(struct GBACartridgeHardware* hw);
static void _lightReadPins(struct GBACartridgeHardware* hw);
static uint16_t _gbpRead(struct mKeyCallback*);
static uint16_t _gbpSioWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value);
static void _gbpSioProcessEvents(struct mTiming* timing, void* user, uint32_t cyclesLate);
static const int RTC_BYTES[8] = {
0, // Force reset
0, // Empty
@ -53,19 +49,6 @@ void GBAHardwareInit(struct GBACartridgeHardware* hw, uint16_t* base) {
hw->eReaderDots = NULL;
memset(hw->eReaderCards, 0, sizeof(hw->eReaderCards));
GBAHardwareClear(hw);
hw->gbpCallback.d.readKeys = _gbpRead;
hw->gbpCallback.p = hw;
hw->gbpDriver.d.init = 0;
hw->gbpDriver.d.deinit = 0;
hw->gbpDriver.d.load = 0;
hw->gbpDriver.d.unload = 0;
hw->gbpDriver.d.writeRegister = _gbpSioWriteRegister;
hw->gbpDriver.p = hw;
hw->gbpNextEvent.context = &hw->gbpDriver;
hw->gbpNextEvent.name = "GBA SIO Game Boy Player";
hw->gbpNextEvent.callback = _gbpSioProcessEvents;
hw->gbpNextEvent.priority = 0x80;
}
void GBAHardwareClear(struct GBACartridgeHardware* hw) {
@ -87,10 +70,6 @@ void GBAHardwareClear(struct GBACartridgeHardware* hw) {
hw->eReaderCards[i].data = NULL;
hw->eReaderCards[i].size = 0;
}
if (hw->p->sio.drivers.normal == &hw->gbpDriver.d) {
GBASIOSetDriver(&hw->p->sio, 0, SIO_NORMAL_32);
}
}
void GBAHardwareGPIOWrite(struct GBACartridgeHardware* hw, uint32_t address, uint16_t value) {
@ -504,124 +483,10 @@ uint8_t GBAHardwareTiltRead(struct GBACartridgeHardware* hw, uint32_t address) {
return 0xFF;
}
// == Game Boy Player
static const uint8_t _logoPalette[] = {
0xDF, 0xFF, 0x0C, 0x64, 0x0C, 0xE4, 0x2D, 0xE4, 0x4E, 0x64, 0x4E, 0xE4, 0x6E, 0xE4, 0xAF, 0x68,
0xB0, 0xE8, 0xD0, 0x68, 0xF0, 0x68, 0x11, 0x69, 0x11, 0xE9, 0x32, 0x6D, 0x32, 0xED, 0x73, 0xED,
0x93, 0x6D, 0x94, 0xED, 0xB4, 0x6D, 0xD5, 0xF1, 0xF5, 0x71, 0xF6, 0xF1, 0x16, 0x72, 0x57, 0x72,
0x57, 0xF6, 0x78, 0x76, 0x78, 0xF6, 0x99, 0xF6, 0xB9, 0xF6, 0xD9, 0x76, 0xDA, 0xF6, 0x1B, 0x7B,
0x1B, 0xFB, 0x3C, 0xFB, 0x5C, 0x7B, 0x7D, 0x7B, 0x7D, 0xFF, 0x9D, 0x7F, 0xBE, 0x7F, 0xFF, 0x7F,
0x2D, 0x64, 0x8E, 0x64, 0x8F, 0xE8, 0xF1, 0xE8, 0x52, 0x6D, 0x73, 0x6D, 0xB4, 0xF1, 0x16, 0xF2,
0x37, 0x72, 0x98, 0x76, 0xFA, 0x7A, 0xFA, 0xFA, 0x5C, 0xFB, 0xBE, 0xFF, 0xDE, 0x7F, 0xFF, 0xFF,
0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
static const uint32_t _logoHash = 0xEEDA6963;
static const uint32_t _gbpTxData[] = {
0x0000494E, 0x0000494E,
0xB6B1494E, 0xB6B1544E,
0xABB1544E, 0xABB14E45,
0xB1BA4E45, 0xB1BA4F44,
0xB0BB4F44, 0xB0BB8002,
0x10000010, 0x20000013,
0x30000003
};
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.devices & HW_GB_PLAYER) {
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) {
return;
}
if (GBAHardwarePlayerCheckScreen(&gba->video)) {
gba->memory.hw.devices |= HW_GB_PLAYER;
gba->memory.hw.gbpInputsPosted = 0;
gba->keyCallback = &gba->memory.hw.gbpCallback.d;
// TODO: Check if the SIO driver is actually used first
GBASIOSetDriver(&gba->sio, &gba->memory.hw.gbpDriver.d, SIO_NORMAL_32);
}
}
uint16_t _gbpRead(struct mKeyCallback* callback) {
struct GBAGBPKeyCallback* gbpCallback = (struct GBAGBPKeyCallback*) callback;
if (gbpCallback->p->gbpInputsPosted == 2) {
return 0xF0;
}
return 0;
}
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) {
uint32_t rx = gbp->p->p->memory.io[REG_SIODATA32_LO >> 1] | (gbp->p->p->memory.io[REG_SIODATA32_HI >> 1] << 16);
if (gbp->p->gbpTxPosition < 12 && gbp->p->gbpTxPosition > 0) {
// TODO: Check expected
} else if (gbp->p->gbpTxPosition >= 12) {
uint32_t mask = 0x33;
// 0x00 = Stop
// 0x11 = Hard Stop
// 0x22 = Start
if (gbp->p->p->rumble) {
gbp->p->p->rumble->setRumble(gbp->p->p->rumble, (rx & mask) == 0x22);
}
}
mTimingDeschedule(&gbp->p->p->timing, &gbp->p->gbpNextEvent);
mTimingSchedule(&gbp->p->p->timing, &gbp->p->gbpNextEvent, 2048);
}
value &= 0x78FB;
}
return value;
}
void _gbpSioProcessEvents(struct mTiming* timing, void* user, uint32_t cyclesLate) {
UNUSED(timing);
UNUSED(cyclesLate);
struct GBAGBPSIODriver* gbp = user;
uint32_t tx = 0;
int txPosition = gbp->p->gbpTxPosition;
if (txPosition > 16) {
gbp->p->gbpTxPosition = 0;
txPosition = 0;
} else if (txPosition > 12) {
txPosition = 12;
}
tx = _gbpTxData[txPosition];
++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 (GBASIONormalIsIrq(gbp->d.p->siocnt)) {
GBARaiseIRQ(gbp->p->p, IRQ_SIO, cyclesLate);
}
gbp->d.p->siocnt = GBASIONormalClearStart(gbp->d.p->siocnt);
gbp->p->p->memory.io[REG_SIOCNT >> 1] = gbp->d.p->siocnt & ~0x0080;
}
// == Serialization
void GBAHardwareSerialize(const struct GBACartridgeHardware* hw, struct GBASerializedState* state) {
GBASerializedHWFlags1 flags1 = 0;
GBASerializedHWFlags2 flags2 = 0;
flags1 = GBASerializedHWFlags1SetReadWrite(flags1, hw->readWrite);
STORE_16(hw->pinState, 0, &state->hw.pinState);
STORE_16(hw->direction, 0, &state->hw.pinDirection);
@ -640,14 +505,19 @@ void GBAHardwareSerialize(const struct GBACartridgeHardware* hw, struct GBASeria
flags1 = GBASerializedHWFlags1SetGyroEdge(flags1, hw->gyroEdge);
STORE_16(hw->tiltX, 0, &state->hw.tiltSampleX);
STORE_16(hw->tiltY, 0, &state->hw.tiltSampleY);
flags2 = GBASerializedHWFlags2SetTiltState(flags2, hw->tiltState);
flags2 = GBASerializedHWFlags1SetLightCounter(flags2, hw->lightCounter);
state->hw.lightSample = hw->lightSample;
flags1 = GBASerializedHWFlags1SetLightEdge(flags1, hw->lightEdge);
flags2 = GBASerializedHWFlags2SetGbpInputsPosted(flags2, hw->gbpInputsPosted);
flags2 = GBASerializedHWFlags2SetGbpTxPosition(flags2, hw->gbpTxPosition);
STORE_32(hw->gbpNextEvent.when - mTimingCurrentTime(&hw->p->timing), 0, &state->hw.gbpNextEvent);
STORE_16(flags1, 0, &state->hw.flags1);
GBASerializedHWFlags2 flags2 = 0;
flags2 = GBASerializedHWFlags2SetTiltState(flags2, hw->tiltState);
flags2 = GBASerializedHWFlags1SetLightCounter(flags2, hw->lightCounter);
// GBP stuff is only here for legacy reasons
flags2 = GBASerializedHWFlags2SetGbpInputsPosted(flags2, hw->p->sio.gbp.inputsPosted);
flags2 = GBASerializedHWFlags2SetGbpTxPosition(flags2, hw->p->sio.gbp.txPosition);
STORE_32(hw->p->sio.gbp.event.when - mTimingCurrentTime(&hw->p->timing), 0, &state->hw.gbpNextEvent);
state->hw.flags2 = flags2;
}
@ -676,15 +546,17 @@ void GBAHardwareDeserialize(struct GBACartridgeHardware* hw, const struct GBASer
hw->lightCounter = GBASerializedHWFlags1GetLightCounter(flags1);
hw->lightSample = state->hw.lightSample;
hw->lightEdge = GBASerializedHWFlags1GetLightEdge(flags1);
hw->gbpInputsPosted = GBASerializedHWFlags2GetGbpInputsPosted(state->hw.flags2);
hw->gbpTxPosition = GBASerializedHWFlags2GetGbpTxPosition(state->hw.flags2);
// GBP stuff is only here for legacy reasons
hw->p->sio.gbp.inputsPosted = GBASerializedHWFlags2GetGbpInputsPosted(state->hw.flags2);
hw->p->sio.gbp.txPosition = GBASerializedHWFlags2GetGbpTxPosition(state->hw.flags2);
uint32_t when;
LOAD_32(when, 0, &state->hw.gbpNextEvent);
if (hw->devices & HW_GB_PLAYER) {
GBASIOSetDriver(&hw->p->sio, &hw->gbpDriver.d, SIO_NORMAL_32);
GBASIOSetDriver(&hw->p->sio, &hw->p->sio.gbp.d, SIO_NORMAL_32);
if (hw->p->memory.io[REG_SIOCNT >> 1] & 0x0080) {
mTimingSchedule(&hw->p->timing, &hw->gbpNextEvent, when);
mTimingSchedule(&hw->p->timing, &hw->p->sio.gbp.event, when);
}
}
}

View File

@ -3,7 +3,7 @@
* 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/. */
#include <mgba/internal/gba/matrix.h>
#include <mgba/internal/gba/cart/matrix.h>
#include <mgba/internal/arm/macros.h>
#include <mgba/internal/gba/gba.h>

View File

@ -3,7 +3,7 @@
* 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/. */
#include <mgba/internal/gba/vfame.h>
#include <mgba/internal/gba/cart/vfame.h>
#include <mgba/internal/gba/gba.h>
#include <mgba/internal/gba/memory.h>

View File

@ -219,7 +219,7 @@ void GBAReset(struct ARMCore* cpu) {
// GB Player SIO control should not be engaged before detection, even if we already know it's GBP
gba->memory.hw.devices &= ~HW_GB_PLAYER;
if (gba->sio.drivers.normal == &gba->memory.hw.gbpDriver.d) {
if (gba->sio.drivers.normal == &gba->sio.gbp.d) {
GBASIOSetDriver(&gba->sio, NULL, SIO_NORMAL_32);
}
@ -855,7 +855,7 @@ void GBAFrameEnded(struct GBA* gba) {
}
if (gba->memory.hw.devices & (HW_GB_PLAYER | HW_GB_PLAYER_DETECTION)) {
GBAHardwarePlayerUpdate(gba);
GBASIOPlayerUpdate(gba);
}
size_t c;

View File

@ -6,7 +6,7 @@
#include <mgba/internal/gba/overrides.h>
#include <mgba/internal/gba/gba.h>
#include <mgba/internal/gba/hardware.h>
#include <mgba/internal/gba/cart/gpio.h>
#include <mgba-util/configuration.h>

View File

@ -7,6 +7,7 @@
#include <mgba/internal/gba/gba.h>
#include <mgba/internal/gba/io.h>
#include <mgba/internal/gba/sio/gbp.h>
mLOG_DEFINE_CATEGORY(GBA_SIO, "GBA Serial I/O", "gba.sio");
@ -76,6 +77,10 @@ void GBASIOInit(struct GBASIO* sio) {
sio->drivers.multiplayer = 0;
sio->drivers.joybus = 0;
sio->activeDriver = 0;
sio->gbp.p = sio->p;
GBASIOPlayerInit(&sio->gbp);
GBASIOReset(sio);
}
@ -103,6 +108,8 @@ void GBASIOReset(struct GBASIO* sio) {
sio->mode = -1;
sio->activeDriver = NULL;
_switchMode(sio);
GBASIOPlayerReset(&sio->gbp);
}
void GBASIOSetDriverSet(struct GBASIO* sio, struct GBASIODriverSet* drivers) {

148
src/gba/sio/gbp.c Normal file
View File

@ -0,0 +1,148 @@
/* Copyright (c) 2013-2021 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/. */
#include <mgba/internal/gba/sio.h>
#include <mgba/internal/arm/macros.h>
#include <mgba/internal/gba/io.h>
#include <mgba/internal/gba/serialize.h>
#include <mgba-util/formatting.h>
#include <mgba-util/hash.h>
#include <mgba-util/memory.h>
static uint16_t _gbpRead(struct mKeyCallback*);
static uint16_t _gbpSioWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value);
static void _gbpSioProcessEvents(struct mTiming* timing, void* user, uint32_t cyclesLate);
static const uint8_t _logoPalette[] = {
0xDF, 0xFF, 0x0C, 0x64, 0x0C, 0xE4, 0x2D, 0xE4, 0x4E, 0x64, 0x4E, 0xE4, 0x6E, 0xE4, 0xAF, 0x68,
0xB0, 0xE8, 0xD0, 0x68, 0xF0, 0x68, 0x11, 0x69, 0x11, 0xE9, 0x32, 0x6D, 0x32, 0xED, 0x73, 0xED,
0x93, 0x6D, 0x94, 0xED, 0xB4, 0x6D, 0xD5, 0xF1, 0xF5, 0x71, 0xF6, 0xF1, 0x16, 0x72, 0x57, 0x72,
0x57, 0xF6, 0x78, 0x76, 0x78, 0xF6, 0x99, 0xF6, 0xB9, 0xF6, 0xD9, 0x76, 0xDA, 0xF6, 0x1B, 0x7B,
0x1B, 0xFB, 0x3C, 0xFB, 0x5C, 0x7B, 0x7D, 0x7B, 0x7D, 0xFF, 0x9D, 0x7F, 0xBE, 0x7F, 0xFF, 0x7F,
0x2D, 0x64, 0x8E, 0x64, 0x8F, 0xE8, 0xF1, 0xE8, 0x52, 0x6D, 0x73, 0x6D, 0xB4, 0xF1, 0x16, 0xF2,
0x37, 0x72, 0x98, 0x76, 0xFA, 0x7A, 0xFA, 0xFA, 0x5C, 0xFB, 0xBE, 0xFF, 0xDE, 0x7F, 0xFF, 0xFF,
0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
static const uint32_t _logoHash = 0xEEDA6963;
static const uint32_t _gbpTxData[] = {
0x0000494E, 0x0000494E,
0xB6B1494E, 0xB6B1544E,
0xABB1544E, 0xABB14E45,
0xB1BA4E45, 0xB1BA4F44,
0xB0BB4F44, 0xB0BB8002,
0x10000010, 0x20000013,
0x30000003
};
void GBASIOPlayerInit(struct GBASIOPlayer* gbp) {
gbp->callback.d.readKeys = _gbpRead;
gbp->callback.p = gbp;
gbp->d.init = 0;
gbp->d.deinit = 0;
gbp->d.load = 0;
gbp->d.unload = 0;
gbp->d.writeRegister = _gbpSioWriteRegister;
gbp->event.context = gbp;
gbp->event.name = "GBA SIO Game Boy Player";
gbp->event.callback = _gbpSioProcessEvents;
gbp->event.priority = 0x80;
}
void GBASIOPlayerReset(struct GBASIOPlayer* gbp) {
if (gbp->p->sio.drivers.normal == &gbp->d) {
GBASIOSetDriver(&gbp->p->sio, NULL, SIO_NORMAL_32);
}
}
bool GBASIOPlayerCheckScreen(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 GBASIOPlayerUpdate(struct GBA* gba) {
if (gba->memory.hw.devices & HW_GB_PLAYER) {
if (GBASIOPlayerCheckScreen(&gba->video)) {
++gba->sio.gbp.inputsPosted;
gba->sio.gbp.inputsPosted %= 3;
gba->keyCallback = &gba->sio.gbp.callback.d;
} else {
// TODO: Save and restore
gba->keyCallback = 0;
}
gba->sio.gbp.txPosition = 0;
return;
}
if (gba->keyCallback) {
return;
}
if (GBASIOPlayerCheckScreen(&gba->video)) {
gba->memory.hw.devices |= HW_GB_PLAYER;
gba->sio.gbp.inputsPosted = 0;
gba->keyCallback = &gba->sio.gbp.callback.d;
// TODO: Check if the SIO driver is actually used first
GBASIOSetDriver(&gba->sio, &gba->sio.gbp.d, SIO_NORMAL_32);
}
}
uint16_t _gbpRead(struct mKeyCallback* callback) {
struct GBASIOPlayerKeyCallback* gbpCallback = (struct GBASIOPlayerKeyCallback*) callback;
if (gbpCallback->p->inputsPosted == 2) {
return 0xF0;
}
return 0;
}
uint16_t _gbpSioWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value) {
struct GBASIOPlayer* gbp = (struct GBASIOPlayer*) driver;
if (address == REG_SIOCNT) {
if (value & 0x0080) {
uint32_t rx = gbp->p->memory.io[REG_SIODATA32_LO >> 1] | (gbp->p->memory.io[REG_SIODATA32_HI >> 1] << 16);
if (gbp->txPosition < 12 && gbp->txPosition > 0) {
// TODO: Check expected
} else if (gbp->txPosition >= 12) {
uint32_t mask = 0x33;
// 0x00 = Stop
// 0x11 = Hard Stop
// 0x22 = Start
if (gbp->p->rumble) {
gbp->p->rumble->setRumble(gbp->p->rumble, (rx & mask) == 0x22);
}
}
mTimingDeschedule(&gbp->p->timing, &gbp->event);
mTimingSchedule(&gbp->p->timing, &gbp->event, 2048);
}
value &= 0x78FB;
}
return value;
}
void _gbpSioProcessEvents(struct mTiming* timing, void* user, uint32_t cyclesLate) {
UNUSED(timing);
UNUSED(cyclesLate);
struct GBASIOPlayer* gbp = user;
uint32_t tx = 0;
int txPosition = gbp->txPosition;
if (txPosition > 16) {
gbp->txPosition = 0;
txPosition = 0;
} else if (txPosition > 12) {
txPosition = 12;
}
tx = _gbpTxData[txPosition];
++gbp->txPosition;
gbp->p->memory.io[REG_SIODATA32_LO >> 1] = tx;
gbp->p->memory.io[REG_SIODATA32_HI >> 1] = tx >> 16;
if (GBASIONormalIsIrq(gbp->d.p->siocnt)) {
GBARaiseIRQ(gbp->p, IRQ_SIO, cyclesLate);
}
gbp->d.p->siocnt = GBASIONormalClearStart(gbp->d.p->siocnt);
gbp->p->memory.io[REG_SIOCNT >> 1] = gbp->d.p->siocnt & ~0x0080;
}