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 // Dummy drivers
186 switch (sio->mode) {
187 case SIO_JOYBUS:
188 switch (address) {
189 case REG_JOYCNT:
190 return (value & 0x0040) | (sio->p->memory.io[REG_JOYCNT >> 1] & ~(value & 0x7) & ~0x0040);
191 case REG_JOYSTAT:
192 return (value & 0x0030) | (sio->p->memory.io[REG_JOYSTAT >> 1] & ~0x30);
193 }
194 break;
195 default:
196 // TODO
197 break;
198 }
199 return value;
200}