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}