all repos — mgba @ 9f36284fd1b62a62bdd0bee765895bd8cac8eb72

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