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