all repos — mgba @ 80c46c5fd863be3c81c777d987560ad59f3248cc

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_COMMAND = 0,
 17	BATTLECHIP_STATE_UNK_0 = 1,
 18	BATTLECHIP_STATE_UNK_1 = 2,
 19	BATTLECHIP_STATE_DATA_0 = 3,
 20	BATTLECHIP_STATE_DATA_1 = 4,
 21	BATTLECHIP_STATE_ID = 5,
 22	BATTLECHIP_STATE_UNK_2 = 6,
 23	BATTLECHIP_STATE_UNK_3 = 7,
 24	BATTLECHIP_STATE_END = 8
 25};
 26
 27enum {
 28	BATTLECHIP_OK = 0xFFC6,
 29	PROGRESS_GATE_OK = 0xFFC7,
 30	BEAST_LINK_GATE_OK = 0xFFC4,
 31	BEAST_LINK_GATE_US_OK = 0xFF00,
 32	BATTLECHIP_CONTINUE = 0xFFFF,
 33};
 34
 35static bool GBASIOBattlechipGateInit(struct GBASIODriver* driver);
 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 = GBASIOBattlechipGateInit;
 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 GBASIOBattlechipGateInit(struct GBASIODriver* driver) {
 58	struct GBASIOBattlechipGate* gate = (struct GBASIOBattlechipGate*) driver;
 59	return true;
 60}
 61
 62bool GBASIOBattlechipGateLoad(struct GBASIODriver* driver) {
 63	struct GBASIOBattlechipGate* gate = (struct GBASIOBattlechipGate*) driver;
 64	gate->state = BATTLECHIP_STATE_COMMAND;
 65	gate->data[0] = 0x00FE;
 66	gate->data[1] = 0xFFFE;
 67	return true;
 68}
 69
 70uint16_t GBASIOBattlechipGateWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value) {
 71	struct GBASIOBattlechipGate* gate = (struct GBASIOBattlechipGate*) driver;
 72	switch (address) {
 73	case REG_SIOCNT:
 74		value &= ~0xC;
 75		value |= 0x8;
 76		if (value & 0x80) {
 77			_battlechipTransfer(gate);
 78		}
 79		break;
 80	case REG_SIOMLT_SEND:
 81		break;
 82	case REG_RCNT:
 83		break;
 84	default:
 85		break;
 86	}
 87	return value;
 88}
 89
 90void _battlechipTransfer(struct GBASIOBattlechipGate* gate) {
 91	int32_t cycles;
 92	if (gate->d.p->mode == SIO_NORMAL_32) {
 93		cycles = GBA_ARM7TDMI_FREQUENCY / 0x40000;
 94	} else {
 95		cycles = GBASIOCyclesPerTransfer[gate->d.p->multiplayerControl.baud][1];
 96	}
 97	mTimingDeschedule(&gate->d.p->p->timing, &gate->event);
 98	mTimingSchedule(&gate->d.p->p->timing, &gate->event, cycles);
 99}
100
101void _battlechipTransferEvent(struct mTiming* timing, void* user, uint32_t cyclesLate) {
102	struct GBASIOBattlechipGate* gate = user;
103
104	if (gate->d.p->mode == SIO_NORMAL_32) {
105		gate->d.p->p->memory.io[REG_SIODATA32_LO >> 1] = 0;
106		gate->d.p->p->memory.io[REG_SIODATA32_HI >> 1] = 0;
107		gate->d.p->normalControl.start = 0;
108		if (gate->d.p->normalControl.irq) {
109			GBARaiseIRQ(gate->d.p->p, IRQ_SIO);
110		}
111		return;
112	}
113
114	uint16_t cmd = gate->d.p->p->memory.io[REG_SIOMLT_SEND >> 1];
115	uint16_t reply = 0xFFFF;
116	gate->d.p->p->memory.io[REG_SIOMULTI0 >> 1] = cmd;
117	gate->d.p->p->memory.io[REG_SIOMULTI2 >> 1] = 0xFFFF;
118	gate->d.p->p->memory.io[REG_SIOMULTI3 >> 1] = 0xFFFF;
119	gate->d.p->multiplayerControl.busy = 0;
120	gate->d.p->multiplayerControl.id = 0;
121
122	mLOG(GBA_BATTLECHIP, DEBUG, "> %04X", cmd);
123
124	uint16_t ok;
125	switch (gate->flavor) {
126	case GBA_FLAVOR_BATTLECHIP_GATE:
127	default:
128		ok = BATTLECHIP_OK;
129		break;
130	case GBA_FLAVOR_PROGRESS_GATE:
131		ok = PROGRESS_GATE_OK;
132		break;
133	case GBA_FLAVOR_BEAST_LINK_GATE:
134		ok = BEAST_LINK_GATE_OK;
135		break;
136	case GBA_FLAVOR_BEAST_LINK_GATE_US:
137		ok = BEAST_LINK_GATE_US_OK;
138		break;
139	}
140
141	switch (gate->state) {
142	case BATTLECHIP_STATE_COMMAND:
143		mLOG(GBA_BATTLECHIP, DEBUG, "C %04X", cmd);
144		switch (cmd) {
145		case 0x0000:
146		case 0x8FFF:
147		// EXE 5, 6
148		case 0xA380:
149		case 0xA390:
150		case 0xA3A0:
151		case 0xA3B0:
152		case 0xA3C0:
153		case 0xA3D0:
154		// EXE 4
155		case 0xA6C0:
156			gate->state = -1;
157		// Fall through
158		//
159		case 0x5379:
160		case 0x537A:
161		case 0x537B:
162		case 0x537C:
163		case 0x537D:
164		case 0x537E:
165		// EXE 6
166		case 0x65AF:
167		//
168		case 0x6E8F:
169		//
170		case 0x87D0:
171		case 0x87D1:
172		case 0x87D2:
173		case 0x87D3:
174		case 0x87D4:
175		case 0x87D5:
176		case 0x87DB:
177		case 0xB7D3:
178		case 0xB7D4:
179		case 0xB7D5:
180		case 0xB7D6:
181		case 0xB7D7:
182		case 0xB7D8:
183		//
184		case 0xC4D3:
185		case 0xC4D4:
186		case 0xC4D5:
187		case 0xC4D6:
188		case 0xC4D7:
189		case 0xC4D8:
190		case 0xC4D9:
191		case 0xC4DA:
192		case 0xC4DB:
193		case 0xC4DC:
194		case 0xC4DD:
195		// EXE 4
196		case 0xD979:
197		case 0xD97A:
198		case 0xD97B:
199		case 0xD97C:
200		case 0xD97D:
201		case 0xD97E:
202		case 0xD97F:
203		case 0xD980:
204		case 0xD981:
205		case 0xD982:
206		case 0xD983:
207		case 0xD984:
208		// EXE 5
209		case 0xE49A:
210		case 0xE49B:
211		case 0xE49C:
212			reply = ok;
213			break;
214		//
215		case 0x3545:
216		case 0x3546:
217		case 0x3547:
218		case 0x3548:
219		case 0x3549:
220		case 0x354A:
221		//
222		case 0x424A:
223		case 0x424B:
224		case 0x424C:
225		case 0x424D:
226		case 0x424E:
227		case 0x424F:
228		case 0x4250:
229		//
230		case 0x5745:
231		case 0x5746:
232		case 0x5747:
233		case 0x5748:
234		case 0x5749:
235		case 0x574A:
236			// Resync
237			gate->state = BATTLECHIP_STATE_UNK_0;
238			break;
239		default:
240			mLOG(GBA_BATTLECHIP, STUB, "? %04X", cmd);
241			gate->state = -1;
242			break;
243		}
244		break;
245	case BATTLECHIP_STATE_UNK_0:
246	case BATTLECHIP_STATE_UNK_1:
247		reply = 0xFFFF;
248		break;
249	case BATTLECHIP_STATE_DATA_0:
250		reply = gate->data[0];
251		gate->data[0] += 3;
252		gate->data[0] &= 0x00FF;
253		break;
254	case BATTLECHIP_STATE_DATA_1:
255		reply = gate->data[1];
256		gate->data[1] -= 3;
257		gate->data[1] |= 0xFC00;
258		break;
259	case BATTLECHIP_STATE_ID:
260		reply = gate->chipId;
261		break;
262	case BATTLECHIP_STATE_UNK_2:
263	case BATTLECHIP_STATE_UNK_3:
264		reply = 0;
265		break;
266	case BATTLECHIP_STATE_END:
267		reply = ok;
268		gate->state = -1;
269		break;
270	}
271	++gate->state;
272
273	mLOG(GBA_BATTLECHIP, DEBUG, "< %04X", reply);
274
275	gate->d.p->p->memory.io[REG_SIOMULTI1 >> 1] = reply;
276
277	if (gate->d.p->multiplayerControl.irq) {
278		GBARaiseIRQ(gate->d.p->p, IRQ_SIO);
279	}
280}