all repos — mgba @ 38e3858852d8df4ecdaec3386f4521f97e8b5191

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