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