all repos — mgba @ a5890bfea5dfdb956d3464d0b6c4076e963dffab

mGBA Game Boy Advance Emulator

src/gba/sio.c (view raw)

  1/* Copyright (c) 2013-2015 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/internal/gba/sio.h>
  7
  8#include <mgba/internal/gba/gba.h>
  9#include <mgba/internal/gba/io.h>
 10
 11mLOG_DEFINE_CATEGORY(GBA_SIO, "GBA Serial I/O", "gba.sio");
 12
 13const int GBASIOCyclesPerTransfer[4][MAX_GBAS] = {
 14	{ 38326, 73003, 107680, 142356 },
 15	{ 9582, 18251, 26920, 35589 },
 16	{ 6388, 12167, 17947, 23726 },
 17	{ 3194, 6075, 8973, 11863 }
 18};
 19
 20static struct GBASIODriver* _lookupDriver(struct GBASIO* sio, enum GBASIOMode mode) {
 21	switch (mode) {
 22	case SIO_NORMAL_8:
 23	case SIO_NORMAL_32:
 24		return sio->drivers.normal;
 25	case SIO_MULTI:
 26		return sio->drivers.multiplayer;
 27	case SIO_JOYBUS:
 28		return sio->drivers.joybus;
 29	default:
 30		return 0;
 31	}
 32}
 33
 34static const char* _modeName(enum GBASIOMode mode) {
 35	switch (mode) {
 36	case SIO_NORMAL_8:
 37		return "NORMAL8";
 38	case SIO_NORMAL_32:
 39		return "NORMAL32";
 40	case SIO_MULTI:
 41		return "MULTI";
 42	case SIO_JOYBUS:
 43		return "JOYBUS";
 44	case SIO_GPIO:
 45		return "GPIO";
 46	default:
 47		return "(unknown)";
 48	}
 49}
 50
 51static void _switchMode(struct GBASIO* sio) {
 52	unsigned mode = ((sio->rcnt & 0xC000) | (sio->siocnt & 0x3000)) >> 12;
 53	enum GBASIOMode newMode;
 54	if (mode < 8) {
 55		newMode = (enum GBASIOMode) (mode & 0x3);
 56	} else {
 57		newMode = (enum GBASIOMode) (mode & 0xC);
 58	}
 59	if (newMode != sio->mode) {
 60		if (sio->activeDriver && sio->activeDriver->unload) {
 61			sio->activeDriver->unload(sio->activeDriver);
 62		}
 63		mLOG(GBA_SIO, DEBUG, "Switching mode from %s to %s", _modeName(sio->mode), _modeName(newMode));
 64		sio->mode = newMode;
 65		sio->activeDriver = _lookupDriver(sio, sio->mode);
 66		if (sio->activeDriver && sio->activeDriver->load) {
 67			sio->activeDriver->load(sio->activeDriver);
 68		}
 69	}
 70}
 71
 72void GBASIOInit(struct GBASIO* sio) {
 73	sio->drivers.normal = 0;
 74	sio->drivers.multiplayer = 0;
 75	sio->drivers.joybus = 0;
 76	sio->activeDriver = 0;
 77	GBASIOReset(sio);
 78}
 79
 80void GBASIODeinit(struct GBASIO* sio) {
 81	if (sio->activeDriver && sio->activeDriver->unload) {
 82		sio->activeDriver->unload(sio->activeDriver);
 83	}
 84	if (sio->drivers.multiplayer && sio->drivers.multiplayer->deinit) {
 85		sio->drivers.multiplayer->deinit(sio->drivers.multiplayer);
 86	}
 87	if (sio->drivers.joybus && sio->drivers.joybus->deinit) {
 88		sio->drivers.joybus->deinit(sio->drivers.joybus);
 89	}
 90	if (sio->drivers.normal && sio->drivers.normal->deinit) {
 91		sio->drivers.normal->deinit(sio->drivers.normal);
 92	}
 93}
 94
 95void GBASIOReset(struct GBASIO* sio) {
 96	if (sio->activeDriver && sio->activeDriver->unload) {
 97		sio->activeDriver->unload(sio->activeDriver);
 98	}
 99	sio->rcnt = RCNT_INITIAL;
100	sio->siocnt = 0;
101	sio->mode = -1;
102	sio->activeDriver = NULL;
103	_switchMode(sio);
104}
105
106void GBASIOSetDriverSet(struct GBASIO* sio, struct GBASIODriverSet* drivers) {
107	GBASIOSetDriver(sio, drivers->normal, SIO_NORMAL_8);
108	GBASIOSetDriver(sio, drivers->multiplayer, SIO_MULTI);
109	GBASIOSetDriver(sio, drivers->joybus, SIO_JOYBUS);
110}
111
112void GBASIOSetDriver(struct GBASIO* sio, struct GBASIODriver* driver, enum GBASIOMode mode) {
113	struct GBASIODriver** driverLoc;
114	switch (mode) {
115	case SIO_NORMAL_8:
116	case SIO_NORMAL_32:
117		driverLoc = &sio->drivers.normal;
118		break;
119	case SIO_MULTI:
120		driverLoc = &sio->drivers.multiplayer;
121		break;
122	case SIO_JOYBUS:
123		driverLoc = &sio->drivers.joybus;
124		break;
125	default:
126		mLOG(GBA_SIO, ERROR, "Setting an unsupported SIO driver: %x", mode);
127		return;
128	}
129	if (*driverLoc) {
130		if ((*driverLoc)->unload) {
131			(*driverLoc)->unload(*driverLoc);
132		}
133		if ((*driverLoc)->deinit) {
134			(*driverLoc)->deinit(*driverLoc);
135		}
136	}
137	if (driver) {
138		driver->p = sio;
139
140		if (driver->init) {
141			if (!driver->init(driver)) {
142				driver->deinit(driver);
143				mLOG(GBA_SIO, ERROR, "Could not initialize SIO driver");
144				return;
145			}
146		}
147	}
148	if (sio->activeDriver == *driverLoc) {
149		sio->activeDriver = driver;
150		if (driver && driver->load) {
151			driver->load(driver);
152		}
153	}
154	*driverLoc = driver;
155}
156
157void GBASIOWriteRCNT(struct GBASIO* sio, uint16_t value) {
158	sio->rcnt &= 0xF;
159	sio->rcnt |= value & ~0xF;
160	_switchMode(sio);
161	if (sio->activeDriver && sio->activeDriver->writeRegister) {
162		sio->activeDriver->writeRegister(sio->activeDriver, REG_RCNT, value);
163	}
164}
165
166void GBASIOWriteSIOCNT(struct GBASIO* sio, uint16_t value) {
167	if ((value ^ sio->siocnt) & 0x3000) {
168		sio->siocnt = value & 0x3000;
169		_switchMode(sio);
170	}
171	if (sio->activeDriver && sio->activeDriver->writeRegister) {
172		value = sio->activeDriver->writeRegister(sio->activeDriver, REG_SIOCNT, value);
173	} else {
174		// Dummy drivers
175		switch (sio->mode) {
176		case SIO_NORMAL_8:
177		case SIO_NORMAL_32:
178			value |= 0x0004;
179			if ((value & 0x0081) == 0x0081) {
180				if (value & 0x4000) {
181					// TODO: Test this on hardware to see if this is correct
182					GBARaiseIRQ(sio->p, IRQ_SIO, 0);
183				}
184				value &= ~0x0080;
185			}
186			break;
187		case SIO_MULTI:
188			value &= 0xFF83;
189			value |= 0xC;
190			break;
191		default:
192			// TODO
193			break;
194		}
195	}
196	sio->siocnt = value;
197}
198
199uint16_t GBASIOWriteRegister(struct GBASIO* sio, uint32_t address, uint16_t value) {
200	if (sio->activeDriver && sio->activeDriver->writeRegister) {
201		return sio->activeDriver->writeRegister(sio->activeDriver, address, value);
202	}
203	// Dummy drivers
204	switch (sio->mode) {
205	case SIO_JOYBUS:
206		switch (address) {
207		case REG_JOYCNT:
208			return (value & 0x0040) | (sio->p->memory.io[REG_JOYCNT >> 1] & ~(value & 0x7) & ~0x0040);
209		case REG_JOYSTAT:
210			return (value & 0x0030) | (sio->p->memory.io[REG_JOYSTAT >> 1] & ~0x30);
211		}
212		break;
213	default:
214		// TODO
215		break;
216	}
217	return value;
218}