From f7f8e38dc17ec7d1daeb1c570cfb124490cf2990 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 10 Feb 2019 16:29:51 -0800 Subject: [PATCH] GBA Peripherals: Start implementing BattleChip Gate --- include/mgba/gba/interface.h | 12 +++- src/gba/core.c | 3 + src/gba/extra/battlechip.c | 136 +++++++++++++++++++++++++++++++++++ 3 files changed, 150 insertions(+), 1 deletion(-) create mode 100644 src/gba/extra/battlechip.c diff --git a/include/mgba/gba/interface.h b/include/mgba/gba/interface.h index 0d85982f5..440a6e567 100644 --- a/include/mgba/gba/interface.h +++ b/include/mgba/gba/interface.h @@ -11,6 +11,7 @@ CXX_GUARD_START #include +#include enum GBASIOMode { SIO_NORMAL_8 = 0, @@ -36,7 +37,8 @@ struct GBAVideoRenderer; extern const int GBA_LUX_LEVELS[10]; enum { - mPERIPH_GBA_LUMINANCE = 0x1000 + mPERIPH_GBA_LUMINANCE = 0x1000, + mPERIPH_GBA_BATTLECHIP_GATE, }; struct GBALuminanceSource { @@ -58,6 +60,14 @@ struct GBASIODriver { void GBASIOJOYCreate(struct GBASIODriver* sio); int GBASIOJOYSendCommand(struct GBASIODriver* sio, enum GBASIOJOYCommand command, uint8_t* data); +struct GBASIOBattlechipGate { + struct GBASIODriver d; + struct mTimingEvent event; + uint16_t chipId; + int32_t index; +}; + +void GBASIOBattlechipGateCreate(struct GBASIOBattlechipGate*); CXX_GUARD_END diff --git a/src/gba/core.c b/src/gba/core.c index 93dc98d84..193d6970a 100644 --- a/src/gba/core.c +++ b/src/gba/core.c @@ -533,6 +533,9 @@ static void _GBACoreSetPeripheral(struct mCore* core, int type, void* periph) { case mPERIPH_GBA_LUMINANCE: gba->luminanceSource = periph; break; + case mPERIPH_GBA_BATTLECHIP_GATE: + GBASIOSetDriver(&gba->sio, periph, SIO_MULTI); + break; default: return; } diff --git a/src/gba/extra/battlechip.c b/src/gba/extra/battlechip.c new file mode 100644 index 000000000..3af871432 --- /dev/null +++ b/src/gba/extra/battlechip.c @@ -0,0 +1,136 @@ +/* Copyright (c) 2013-2018 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 + +#include +#include +#include + +mLOG_DECLARE_CATEGORY(GBA_BATTLECHIP); +mLOG_DEFINE_CATEGORY(GBA_BATTLECHIP, "GBA BattleChip Gate", "gba.battlechip"); + +enum { + BATTLECHIP_INDEX_HANDSHAKE_0 = 0, + BATTLECHIP_INDEX_HANDSHAKE_1 = 1, + BATTLECHIP_INDEX_ID = 2, + BATTLECHIP_INDEX_END = 6 +}; + +enum { + BATTLECHIP_OK = 0xFFC6, + BATTLECHIP_CONTINUE = 0xFFFF, +}; + +static bool GBASIOBattlechipGateLoad(struct GBASIODriver* driver); +static uint16_t GBASIOBattlechipGateWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value); + +static void _battlechipTransfer(struct GBASIOBattlechipGate* gate); +static void _battlechipTransferEvent(struct mTiming* timing, void* user, uint32_t cyclesLate); + +void GBASIOBattlechipGateCreate(struct GBASIOBattlechipGate* gate) { + gate->d.init = NULL; + gate->d.deinit = NULL; + gate->d.load = GBASIOBattlechipGateLoad; + gate->d.unload = NULL; + gate->d.writeRegister = GBASIOBattlechipGateWriteRegister; + + gate->event.context = gate; + gate->event.callback = _battlechipTransferEvent; + gate->event.priority = 0x80; +} + +bool GBASIOBattlechipGateLoad(struct GBASIODriver* driver) { + struct GBASIOBattlechipGate* gate = (struct GBASIOBattlechipGate*) driver; + gate->index = BATTLECHIP_INDEX_END; + return true; +} + +uint16_t GBASIOBattlechipGateWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value) { + struct GBASIOBattlechipGate* gate = (struct GBASIOBattlechipGate*) driver; + switch (address) { + case REG_SIOCNT: + value &= ~0xC; + value |= 0x8; + if (value & 0x80) { + _battlechipTransfer(gate); + } + break; + case REG_SIOMLT_SEND: + break; + case REG_RCNT: + break; + default: + break; + } + return value; +} + +void _battlechipTransfer(struct GBASIOBattlechipGate* gate) { + int32_t cycles = GBASIOCyclesPerTransfer[gate->d.p->multiplayerControl.baud][1]; + mTimingSchedule(&gate->d.p->p->timing, &gate->event, cycles); +} + +void _battlechipTransferEvent(struct mTiming* timing, void* user, uint32_t cyclesLate) { + struct GBASIOBattlechipGate* gate = user; + + uint16_t cmd = gate->d.p->p->memory.io[REG_SIOMLT_SEND >> 1]; + uint16_t reply = 0xFFFF; + gate->d.p->p->memory.io[REG_SIOMULTI0 >> 1] = cmd; + gate->d.p->p->memory.io[REG_SIOMULTI2 >> 1] = 0xFFFF; + gate->d.p->p->memory.io[REG_SIOMULTI3 >> 1] = 0xFFFF; + gate->d.p->multiplayerControl.busy = 0; + gate->d.p->multiplayerControl.id = 0; + + mLOG(GBA_BATTLECHIP, DEBUG, "> %04x", cmd); + + switch (cmd) { + case 0x4000: + gate->index = 0; + // Fall through + case 0: + switch (gate->index) { + case BATTLECHIP_INDEX_HANDSHAKE_0: + reply = 0x00FE; + break; + case BATTLECHIP_INDEX_HANDSHAKE_1: + reply = 0xFFFE; + break; + case BATTLECHIP_INDEX_ID: + reply = gate->chipId; + break; + default: + if (gate->index >= BATTLECHIP_INDEX_END) { + reply = BATTLECHIP_OK; + } else if (gate->index < 0) { + reply = BATTLECHIP_CONTINUE; + } else { + reply = 0; + } + break; + } + ++gate->index; + break; + case 0x8FFF: + gate->index = -2; + // Fall through + default: + case 0xA3D0: + reply = BATTLECHIP_OK; + break; + case 0x4234: + case 0x574A: + reply = BATTLECHIP_CONTINUE; + break; + } + + mLOG(GBA_BATTLECHIP, DEBUG, "< %04x", reply); + + gate->d.p->p->memory.io[REG_SIOMULTI1 >> 1] = reply; + + if (gate->d.p->multiplayerControl.irq) { + GBARaiseIRQ(gate->d.p->p, IRQ_SIO); + } +}