all repos — mgba @ 7899e02db00c15e6f33c958d7c07f64f1047af96

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	BATTLECHIP_CONTINUE = 0xFFFF,
 30};
 31
 32static bool GBASIOBattlechipGateInit(struct GBASIODriver* driver);
 33static bool GBASIOBattlechipGateLoad(struct GBASIODriver* driver);
 34static uint16_t GBASIOBattlechipGateWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value);
 35
 36static void _battlechipTransfer(struct GBASIOBattlechipGate* gate);
 37static void _battlechipTransferEvent(struct mTiming* timing, void* user, uint32_t cyclesLate);
 38
 39void GBASIOBattlechipGateCreate(struct GBASIOBattlechipGate* gate) {
 40	gate->d.init = GBASIOBattlechipGateInit;
 41	gate->d.deinit = NULL;
 42	gate->d.load = GBASIOBattlechipGateLoad;
 43	gate->d.unload = NULL;
 44	gate->d.writeRegister = GBASIOBattlechipGateWriteRegister;
 45
 46	gate->event.context = gate;
 47	gate->event.callback = _battlechipTransferEvent;
 48	gate->event.priority = 0x80;
 49}
 50
 51bool GBASIOBattlechipGateInit(struct GBASIODriver* driver) {
 52	struct GBASIOBattlechipGate* gate = (struct GBASIOBattlechipGate*) driver;
 53	gate->chipId = 0;
 54	return true;
 55}
 56
 57bool GBASIOBattlechipGateLoad(struct GBASIODriver* driver) {
 58	struct GBASIOBattlechipGate* gate = (struct GBASIOBattlechipGate*) driver;
 59	gate->state = BATTLECHIP_STATE_COMMAND;
 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 = GBASIOCyclesPerTransfer[gate->d.p->multiplayerControl.baud][1];
 87	mTimingSchedule(&gate->d.p->p->timing, &gate->event, cycles);
 88}
 89
 90void _battlechipTransferEvent(struct mTiming* timing, void* user, uint32_t cyclesLate) {
 91	struct GBASIOBattlechipGate* gate = user;
 92
 93	uint16_t cmd = gate->d.p->p->memory.io[REG_SIOMLT_SEND >> 1];
 94	uint16_t reply = 0xFFFF;
 95	gate->d.p->p->memory.io[REG_SIOMULTI0 >> 1] = cmd;
 96	gate->d.p->p->memory.io[REG_SIOMULTI2 >> 1] = 0xFFFF;
 97	gate->d.p->p->memory.io[REG_SIOMULTI3 >> 1] = 0xFFFF;
 98	gate->d.p->multiplayerControl.busy = 0;
 99	gate->d.p->multiplayerControl.id = 0;
100
101	mLOG(GBA_BATTLECHIP, DEBUG, "> %04X", cmd);
102
103	switch (gate->state) {
104	case BATTLECHIP_STATE_COMMAND:
105		mLOG(GBA_BATTLECHIP, DEBUG, "C %04X", cmd);
106		switch (cmd) {
107		case 0x0000:
108		case 0x8FFF:
109		case 0xA380:
110		case 0xA390:
111		case 0xA3A0:
112		case 0xA3B0:
113		case 0xA3C0:
114		case 0xA3D0:
115			gate->state = -1;
116		// Fall through
117		case 0x5379:
118		case 0x537A:
119		case 0x537B:
120		case 0x537C:
121		case 0x537D:
122		case 0x537E:
123		case 0xD979:
124		case 0xD97A:
125		case 0xD97B:
126		case 0xD97C:
127		case 0xD97D:
128		case 0xD97E:
129			reply = BATTLECHIP_OK;
130			break;
131		case 0x5745:
132		case 0x5746:
133		case 0x5747:
134		case 0x5748:
135		case 0x5749:
136		case 0x574A:
137		case 0xFC00:
138			// Resync
139			gate->state = BATTLECHIP_STATE_UNK_0;
140			break;
141		default:
142			mLOG(GBA_BATTLECHIP, STUB, "? %04X", cmd);
143			break;
144		}
145		break;
146	case BATTLECHIP_STATE_UNK_0:
147	case BATTLECHIP_STATE_UNK_1:
148		reply = 0xFFFF;
149		break;
150	case BATTLECHIP_STATE_DATA_0:
151		reply = gate->data[0];
152		gate->data[0] += 3;
153		gate->data[0] &= 0xF0FF;
154		break;
155	case BATTLECHIP_STATE_DATA_1:
156		reply = gate->data[1];
157		gate->data[1] -= 3;
158		gate->data[1] |= 0xFE00;
159		break;
160	case BATTLECHIP_STATE_ID:
161		reply = gate->chipId;
162		break;
163	case BATTLECHIP_STATE_UNK_2:
164	case BATTLECHIP_STATE_UNK_3:
165		reply = 0;
166		break;
167	case BATTLECHIP_STATE_END:
168		reply = BATTLECHIP_OK;
169		gate->state = -1;
170		break;
171	}
172	++gate->state;
173
174	mLOG(GBA_BATTLECHIP, DEBUG, "< %04X", reply);
175
176	gate->d.p->p->memory.io[REG_SIOMULTI1 >> 1] = reply;
177
178	if (gate->d.p->multiplayerControl.irq) {
179		GBARaiseIRQ(gate->d.p->p, IRQ_SIO);
180	}
181}