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}