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 != mode) {
40 if (sio->activeDriver && sio->activeDriver->unload) {
41 sio->activeDriver->unload(sio->activeDriver);
42 }
43 sio->activeDriver = _lookupDriver(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->rcnt = RCNT_INITIAL;
52 sio->siocnt = 0;
53 sio->mode = -1;
54 sio->activeDriver = 0;
55 sio->drivers.normal = 0;
56 sio->drivers.multiplayer = 0;
57 sio->drivers.joybus = 0;
58 _switchMode(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}
72
73void GBASIOSetDriverSet(struct GBASIO* sio, struct GBASIODriverSet* drivers) {
74 GBASIOSetDriver(sio, drivers->normal, SIO_NORMAL_8);
75 GBASIOSetDriver(sio, drivers->multiplayer, SIO_MULTI);
76 GBASIOSetDriver(sio, drivers->joybus, SIO_JOYBUS);
77}
78
79void GBASIOSetDriver(struct GBASIO* sio, struct GBASIODriver* driver, enum GBASIOMode mode) {
80 struct GBASIODriver** driverLoc;
81 switch (mode) {
82 case SIO_NORMAL_8:
83 case SIO_NORMAL_32:
84 driverLoc = &sio->drivers.normal;
85 break;
86 case SIO_MULTI:
87 driverLoc = &sio->drivers.multiplayer;
88 break;
89 case SIO_JOYBUS:
90 driverLoc = &sio->drivers.joybus;
91 break;
92 default:
93 GBALog(sio->p, GBA_LOG_ERROR, "Setting an unsupported SIO driver: %x", mode);
94 return;
95 }
96 if (*driverLoc) {
97 if ((*driverLoc)->unload) {
98 (*driverLoc)->unload(*driverLoc);
99 }
100 if ((*driverLoc)->deinit) {
101 (*driverLoc)->deinit(*driverLoc);
102 }
103 }
104 if (driver) {
105 driver->p = sio;
106
107 if (driver->init) {
108 if (!driver->init(driver)) {
109 driver->deinit(driver);
110 GBALog(sio->p, GBA_LOG_ERROR, "Could not initialize SIO driver");
111 return;
112 }
113 }
114 if (sio->mode == mode) {
115 sio->activeDriver = driver;
116 if (driver->load) {
117 driver->load(driver);
118 }
119 }
120 }
121 *driverLoc = driver;
122}
123
124void GBASIOWriteRCNT(struct GBASIO* sio, uint16_t value) {
125 sio->rcnt = value;
126 _switchMode(sio);
127 if (sio->activeDriver && sio->activeDriver->writeRegister) {
128 sio->activeDriver->writeRegister(sio->activeDriver, REG_RCNT, value);
129 }
130}
131
132void GBASIOWriteSIOCNT(struct GBASIO* sio, uint16_t value) {
133 if (sio->activeDriver && sio->activeDriver->writeRegister) {
134 value = sio->activeDriver->writeRegister(sio->activeDriver, REG_SIOCNT, value);
135 }
136 sio->siocnt = value;
137 _switchMode(sio);
138}
139
140void GBASIOWriteSIOMLT_SEND(struct GBASIO* sio, uint16_t value) {
141 if (sio->activeDriver && sio->activeDriver->writeRegister) {
142 sio->activeDriver->writeRegister(sio->activeDriver, REG_SIOMLT_SEND, value);
143 }
144}
145
146int32_t GBASIOProcessEvents(struct GBASIO* sio, int32_t cycles) {
147 if (sio->activeDriver && sio->activeDriver->processEvents) {
148 return sio->activeDriver->processEvents(sio->activeDriver, cycles);
149 }
150 return INT_MAX;
151}