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