all repos — mgba @ f6755a6e1b7b0cf2b944cd8ca842746f11d6bf82

mGBA Game Boy Advance Emulator

src/gba/extra/battlechip.c (view raw)

  1/* Copyright (c) 2013-2018 Jeffrey Pfau
  2 *
  3 * This Source Code Form is subject to the terms of the Mozilla Public
  4 * License, v. 2.0. If a copy of the MPL was not distributed with this
  5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  6#include <mgba/gba/interface.h>
  7
  8#include <mgba/internal/gba/gba.h>
  9#include <mgba/internal/gba/io.h>
 10#include <mgba/internal/gba/sio.h>
 11
 12mLOG_DECLARE_CATEGORY(GBA_BATTLECHIP);
 13mLOG_DEFINE_CATEGORY(GBA_BATTLECHIP, "GBA BattleChip Gate", "gba.battlechip");
 14
 15enum {
 16	BATTLECHIP_STATE_SYNC = -1,
 17	BATTLECHIP_STATE_COMMAND = 0,
 18	BATTLECHIP_STATE_UNK_0 = 1,
 19	BATTLECHIP_STATE_UNK_1 = 2,
 20	BATTLECHIP_STATE_DATA_0 = 3,
 21	BATTLECHIP_STATE_DATA_1 = 4,
 22	BATTLECHIP_STATE_ID = 5,
 23	BATTLECHIP_STATE_UNK_2 = 6,
 24	BATTLECHIP_STATE_UNK_3 = 7,
 25	BATTLECHIP_STATE_END = 8
 26};
 27
 28enum {
 29	BATTLECHIP_OK = 0xFFC6,
 30	PROGRESS_GATE_OK = 0xFFC7,
 31	BEAST_LINK_GATE_OK = 0xFFC4,
 32	BEAST_LINK_GATE_US_OK = 0xFF00,
 33	BATTLECHIP_CONTINUE = 0xFFFF,
 34};
 35
 36static bool GBASIOBattlechipGateLoad(struct GBASIODriver* driver);
 37static uint16_t GBASIOBattlechipGateWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value);
 38
 39static void _battlechipTransfer(struct GBASIOBattlechipGate* gate);
 40static void _battlechipTransferEvent(struct mTiming* timing, void* user, uint32_t cyclesLate);
 41
 42void GBASIOBattlechipGateCreate(struct GBASIOBattlechipGate* gate) {
 43	gate->d.init = NULL;
 44	gate->d.deinit = NULL;
 45	gate->d.load = GBASIOBattlechipGateLoad;
 46	gate->d.unload = NULL;
 47	gate->d.writeRegister = GBASIOBattlechipGateWriteRegister;
 48
 49	gate->event.context = gate;
 50	gate->event.callback = _battlechipTransferEvent;
 51	gate->event.priority = 0x80;
 52
 53	gate->chipId = 0;
 54	gate->flavor = GBA_FLAVOR_BATTLECHIP_GATE;
 55}
 56
 57bool GBASIOBattlechipGateLoad(struct GBASIODriver* driver) {
 58	struct GBASIOBattlechipGate* gate = (struct GBASIOBattlechipGate*) driver;
 59	gate->state = BATTLECHIP_STATE_SYNC;
 60	gate->data[0] = 0x00FE;
 61	gate->data[1] = 0xFFFE;
 62	return true;
 63}
 64
 65uint16_t GBASIOBattlechipGateWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value) {
 66	struct GBASIOBattlechipGate* gate = (struct GBASIOBattlechipGate*) driver;
 67	switch (address) {
 68	case REG_SIOCNT:
 69		value &= ~0xC;
 70		value |= 0x8;
 71		if (value & 0x80) {
 72			_battlechipTransfer(gate);
 73		}
 74		break;
 75	case REG_SIOMLT_SEND:
 76		break;
 77	case REG_RCNT:
 78		break;
 79	default:
 80		break;
 81	}
 82	return value;
 83}
 84
 85void _battlechipTransfer(struct GBASIOBattlechipGate* gate) {
 86	int32_t cycles;
 87	if (gate->d.p->mode == SIO_NORMAL_32) {
 88		cycles = GBA_ARM7TDMI_FREQUENCY / 0x40000;
 89	} else {
 90		cycles = GBASIOCyclesPerTransfer[GBASIOMultiplayerGetBaud(gate->d.p->siocnt)][1];
 91	}
 92	mTimingDeschedule(&gate->d.p->p->timing, &gate->event);
 93	mTimingSchedule(&gate->d.p->p->timing, &gate->event, cycles);
 94}
 95
 96void _battlechipTransferEvent(struct mTiming* timing, void* user, uint32_t cyclesLate) {
 97	UNUSED(timing);
 98	struct GBASIOBattlechipGate* gate = user;
 99
100	if (gate->d.p->mode == SIO_NORMAL_32) {
101		gate->d.p->p->memory.io[REG_SIODATA32_LO >> 1] = 0;
102		gate->d.p->p->memory.io[REG_SIODATA32_HI >> 1] = 0;
103		gate->d.p->siocnt = GBASIONormalClearStart(gate->d.p->siocnt);
104		if (GBASIONormalIsIrq(gate->d.p->siocnt)) {
105			GBARaiseIRQ(gate->d.p->p, IRQ_SIO, cyclesLate);
106		}
107		return;
108	}
109
110	uint16_t cmd = gate->d.p->p->memory.io[REG_SIOMLT_SEND >> 1];
111	uint16_t reply = 0xFFFF;
112	gate->d.p->p->memory.io[REG_SIOMULTI0 >> 1] = cmd;
113	gate->d.p->p->memory.io[REG_SIOMULTI2 >> 1] = 0xFFFF;
114	gate->d.p->p->memory.io[REG_SIOMULTI3 >> 1] = 0xFFFF;
115	gate->d.p->siocnt = GBASIOMultiplayerClearBusy(gate->d.p->siocnt);
116	gate->d.p->siocnt = GBASIOMultiplayerSetId(gate->d.p->siocnt, 0);
117
118	mLOG(GBA_BATTLECHIP, DEBUG, "Game: %04X (%i)", cmd, gate->state);
119
120	uint16_t ok;
121	switch (gate->flavor) {
122	case GBA_FLAVOR_BATTLECHIP_GATE:
123	default:
124		ok = BATTLECHIP_OK;
125		break;
126	case GBA_FLAVOR_PROGRESS_GATE:
127		ok = PROGRESS_GATE_OK;
128		break;
129	case GBA_FLAVOR_BEAST_LINK_GATE:
130		ok = BEAST_LINK_GATE_OK;
131		break;
132	case GBA_FLAVOR_BEAST_LINK_GATE_US:
133		ok = BEAST_LINK_GATE_US_OK;
134		break;
135	}
136
137	if (gate->state != BATTLECHIP_STATE_COMMAND) {
138		// Resync if needed
139		switch (cmd) {
140		// EXE 5, 6
141		case 0xA380:
142		case 0xA390:
143		case 0xA3A0:
144		case 0xA3B0:
145		case 0xA3C0:
146		case 0xA3D0:
147		// EXE 4
148		case 0xA6C0:
149		mLOG(GBA_BATTLECHIP, DEBUG, "Resync detected");
150			gate->state = BATTLECHIP_STATE_SYNC;
151			break;
152		}
153	}
154
155	switch (gate->state) {
156	case BATTLECHIP_STATE_SYNC:
157		if (cmd != 0x8FFF) {
158			--gate->state;
159		}
160		// Fall through
161	case BATTLECHIP_STATE_COMMAND:
162		reply = ok;
163		break;
164	case BATTLECHIP_STATE_UNK_0:
165	case BATTLECHIP_STATE_UNK_1:
166		reply = 0xFFFF;
167		break;
168	case BATTLECHIP_STATE_DATA_0:
169		reply = gate->data[0];
170		gate->data[0] += 3;
171		gate->data[0] &= 0x00FF;
172		break;
173	case BATTLECHIP_STATE_DATA_1:
174		reply = gate->data[1];
175		gate->data[1] -= 3;
176		gate->data[1] |= 0xFC00;
177		break;
178	case BATTLECHIP_STATE_ID:
179		reply = gate->chipId;
180		break;
181	case BATTLECHIP_STATE_UNK_2:
182	case BATTLECHIP_STATE_UNK_3:
183		reply = 0;
184		break;
185	case BATTLECHIP_STATE_END:
186		reply = ok;
187		gate->state = BATTLECHIP_STATE_SYNC;
188		break;
189	}
190
191	mLOG(GBA_BATTLECHIP, DEBUG, "Gate: %04X (%i)", reply, gate->state);
192	++gate->state;
193
194	gate->d.p->p->memory.io[REG_SIOMULTI1 >> 1] = reply;
195
196	if (GBASIOMultiplayerIsIrq(gate->d.p->siocnt)) {
197		GBARaiseIRQ(gate->d.p->p, IRQ_SIO, cyclesLate);
198	}
199}