all repos — mgba @ 85f663fcccc72fb3e11d4a9e5ef9e046826e8411

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 void _switchMode(struct GBASIO* sio) {
 35	unsigned mode = ((sio->rcnt & 0xC000) | (sio->siocnt & 0x3000)) >> 12;
 36	enum GBASIOMode newMode;
 37	if (mode < 8) {
 38		newMode = (enum GBASIOMode) (mode & 0x3);
 39	} else {
 40		newMode = (enum GBASIOMode) (mode & 0xC);
 41	}
 42	if (newMode != sio->mode) {
 43		if (sio->activeDriver && sio->activeDriver->unload) {
 44			sio->activeDriver->unload(sio->activeDriver);
 45		}
 46		sio->mode = newMode;
 47		sio->activeDriver = _lookupDriver(sio, sio->mode);
 48		if (sio->activeDriver && sio->activeDriver->load) {
 49			sio->activeDriver->load(sio->activeDriver);
 50		}
 51	}
 52}
 53
 54void GBASIOInit(struct GBASIO* sio) {
 55	sio->drivers.normal = 0;
 56	sio->drivers.multiplayer = 0;
 57	sio->drivers.joybus = 0;
 58	sio->activeDriver = 0;
 59	GBASIOReset(sio);
 60}
 61
 62void GBASIODeinit(struct GBASIO* sio) {
 63	if (sio->activeDriver && sio->activeDriver->unload) {
 64		sio->activeDriver->unload(sio->activeDriver);
 65	}
 66	if (sio->drivers.multiplayer && sio->drivers.multiplayer->deinit) {
 67		sio->drivers.multiplayer->deinit(sio->drivers.multiplayer);
 68	}
 69	if (sio->drivers.joybus && sio->drivers.joybus->deinit) {
 70		sio->drivers.joybus->deinit(sio->drivers.joybus);
 71	}
 72	if (sio->drivers.normal && sio->drivers.normal->deinit) {
 73		sio->drivers.normal->deinit(sio->drivers.normal);
 74	}
 75}
 76
 77void GBASIOReset(struct GBASIO* sio) {
 78	if (sio->activeDriver && sio->activeDriver->unload) {
 79		sio->activeDriver->unload(sio->activeDriver);
 80	}
 81	sio->rcnt = RCNT_INITIAL;
 82	sio->siocnt = 0;
 83	sio->mode = -1;
 84	sio->activeDriver = NULL;
 85	_switchMode(sio);
 86}
 87
 88void GBASIOSetDriverSet(struct GBASIO* sio, struct GBASIODriverSet* drivers) {
 89	GBASIOSetDriver(sio, drivers->normal, SIO_NORMAL_8);
 90	GBASIOSetDriver(sio, drivers->multiplayer, SIO_MULTI);
 91	GBASIOSetDriver(sio, drivers->joybus, SIO_JOYBUS);
 92}
 93
 94void GBASIOSetDriver(struct GBASIO* sio, struct GBASIODriver* driver, enum GBASIOMode mode) {
 95	struct GBASIODriver** driverLoc;
 96	switch (mode) {
 97	case SIO_NORMAL_8:
 98	case SIO_NORMAL_32:
 99		driverLoc = &sio->drivers.normal;
100		break;
101	case SIO_MULTI:
102		driverLoc = &sio->drivers.multiplayer;
103		break;
104	case SIO_JOYBUS:
105		driverLoc = &sio->drivers.joybus;
106		break;
107	default:
108		mLOG(GBA_SIO, ERROR, "Setting an unsupported SIO driver: %x", mode);
109		return;
110	}
111	if (*driverLoc) {
112		if ((*driverLoc)->unload) {
113			(*driverLoc)->unload(*driverLoc);
114		}
115		if ((*driverLoc)->deinit) {
116			(*driverLoc)->deinit(*driverLoc);
117		}
118	}
119	if (driver) {
120		driver->p = sio;
121
122		if (driver->init) {
123			if (!driver->init(driver)) {
124				driver->deinit(driver);
125				mLOG(GBA_SIO, ERROR, "Could not initialize SIO driver");
126				return;
127			}
128		}
129	}
130	if (sio->activeDriver == *driverLoc) {
131		sio->activeDriver = driver;
132		if (driver && driver->load) {
133			driver->load(driver);
134		}
135	}
136	*driverLoc = driver;
137}
138
139void GBASIOWriteRCNT(struct GBASIO* sio, uint16_t value) {
140	sio->rcnt &= 0xF;
141	sio->rcnt |= value & ~0xF;
142	_switchMode(sio);
143	if (sio->activeDriver && sio->activeDriver->writeRegister) {
144		sio->activeDriver->writeRegister(sio->activeDriver, REG_RCNT, value);
145	}
146}
147
148void GBASIOWriteSIOCNT(struct GBASIO* sio, uint16_t value) {
149	if ((value ^ sio->siocnt) & 0x3000) {
150		sio->siocnt = value & 0x3000;
151		_switchMode(sio);
152	}
153	if (sio->activeDriver && sio->activeDriver->writeRegister) {
154		value = sio->activeDriver->writeRegister(sio->activeDriver, REG_SIOCNT, value);
155	} else {
156		// Dummy drivers
157		switch (sio->mode) {
158		case SIO_NORMAL_8:
159		case SIO_NORMAL_32:
160			value |= 0x0004;
161			if ((value & 0x0081) == 0x0081) {
162				if (value & 0x4000) {
163					// TODO: Test this on hardware to see if this is correct
164					GBARaiseIRQ(sio->p, IRQ_SIO, 0);
165				}
166				value &= ~0x0080;
167			}
168			break;
169		case SIO_MULTI:
170			value &= 0xFF83;
171			value |= 0xC;
172			break;
173		default:
174			// TODO
175			break;
176		}
177	}
178	sio->siocnt = value;
179}
180
181uint16_t GBASIOWriteRegister(struct GBASIO* sio, uint32_t address, uint16_t value) {
182	if (sio->activeDriver && sio->activeDriver->writeRegister) {
183		return sio->activeDriver->writeRegister(sio->activeDriver, address, value);
184	}
185	return value;
186}