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