src/gba/extra/battlechip.c (view raw)
1/* Copyright (c) 2013-2018 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 <mgba/gba/interface.h>
7
8#include <mgba/internal/gba/gba.h>
9#include <mgba/internal/gba/io.h>
10#include <mgba/internal/gba/sio.h>
11
12mLOG_DECLARE_CATEGORY(GBA_BATTLECHIP);
13mLOG_DEFINE_CATEGORY(GBA_BATTLECHIP, "GBA BattleChip Gate", "gba.battlechip");
14
15enum {
16 BATTLECHIP_STATE_COMMAND = 0,
17 BATTLECHIP_STATE_UNK_0 = 1,
18 BATTLECHIP_STATE_UNK_1 = 2,
19 BATTLECHIP_STATE_DATA_0 = 3,
20 BATTLECHIP_STATE_DATA_1 = 4,
21 BATTLECHIP_STATE_ID = 5,
22 BATTLECHIP_STATE_UNK_2 = 6,
23 BATTLECHIP_STATE_UNK_3 = 7,
24 BATTLECHIP_STATE_END = 8
25};
26
27enum {
28 BATTLECHIP_OK = 0xFFC6,
29 PROGRESS_GATE_OK = 0xFFC7,
30 BEAST_LINK_GATE_OK = 0xFFC4,
31 BEAST_LINK_GATE_US_OK = 0xFF00,
32 BATTLECHIP_CONTINUE = 0xFFFF,
33};
34
35static bool GBASIOBattlechipGateInit(struct GBASIODriver* driver);
36static bool GBASIOBattlechipGateLoad(struct GBASIODriver* driver);
37static uint16_t GBASIOBattlechipGateWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value);
38
39static void _battlechipTransfer(struct GBASIOBattlechipGate* gate);
40static void _battlechipTransferEvent(struct mTiming* timing, void* user, uint32_t cyclesLate);
41
42void GBASIOBattlechipGateCreate(struct GBASIOBattlechipGate* gate) {
43 gate->d.init = GBASIOBattlechipGateInit;
44 gate->d.deinit = NULL;
45 gate->d.load = GBASIOBattlechipGateLoad;
46 gate->d.unload = NULL;
47 gate->d.writeRegister = GBASIOBattlechipGateWriteRegister;
48
49 gate->event.context = gate;
50 gate->event.callback = _battlechipTransferEvent;
51 gate->event.priority = 0x80;
52
53 gate->chipId = 0;
54 gate->flavor = GBA_FLAVOR_BATTLECHIP_GATE;
55}
56
57bool GBASIOBattlechipGateInit(struct GBASIODriver* driver) {
58 struct GBASIOBattlechipGate* gate = (struct GBASIOBattlechipGate*) driver;
59 return true;
60}
61
62bool GBASIOBattlechipGateLoad(struct GBASIODriver* driver) {
63 struct GBASIOBattlechipGate* gate = (struct GBASIOBattlechipGate*) driver;
64 gate->state = BATTLECHIP_STATE_COMMAND;
65 gate->data[0] = 0x00FE;
66 gate->data[1] = 0xFFFE;
67 return true;
68}
69
70uint16_t GBASIOBattlechipGateWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value) {
71 struct GBASIOBattlechipGate* gate = (struct GBASIOBattlechipGate*) driver;
72 switch (address) {
73 case REG_SIOCNT:
74 value &= ~0xC;
75 value |= 0x8;
76 if (value & 0x80) {
77 _battlechipTransfer(gate);
78 }
79 break;
80 case REG_SIOMLT_SEND:
81 break;
82 case REG_RCNT:
83 break;
84 default:
85 break;
86 }
87 return value;
88}
89
90void _battlechipTransfer(struct GBASIOBattlechipGate* gate) {
91 int32_t cycles;
92 if (gate->d.p->mode == SIO_NORMAL_32) {
93 cycles = GBA_ARM7TDMI_FREQUENCY / 0x40000;
94 } else {
95 cycles = GBASIOCyclesPerTransfer[gate->d.p->multiplayerControl.baud][1];
96 }
97 mTimingDeschedule(&gate->d.p->p->timing, &gate->event);
98 mTimingSchedule(&gate->d.p->p->timing, &gate->event, cycles);
99}
100
101void _battlechipTransferEvent(struct mTiming* timing, void* user, uint32_t cyclesLate) {
102 struct GBASIOBattlechipGate* gate = user;
103
104 if (gate->d.p->mode == SIO_NORMAL_32) {
105 gate->d.p->p->memory.io[REG_SIODATA32_LO >> 1] = 0;
106 gate->d.p->p->memory.io[REG_SIODATA32_HI >> 1] = 0;
107 gate->d.p->normalControl.start = 0;
108 if (gate->d.p->normalControl.irq) {
109 GBARaiseIRQ(gate->d.p->p, IRQ_SIO);
110 }
111 return;
112 }
113
114 uint16_t cmd = gate->d.p->p->memory.io[REG_SIOMLT_SEND >> 1];
115 uint16_t reply = 0xFFFF;
116 gate->d.p->p->memory.io[REG_SIOMULTI0 >> 1] = cmd;
117 gate->d.p->p->memory.io[REG_SIOMULTI2 >> 1] = 0xFFFF;
118 gate->d.p->p->memory.io[REG_SIOMULTI3 >> 1] = 0xFFFF;
119 gate->d.p->multiplayerControl.busy = 0;
120 gate->d.p->multiplayerControl.id = 0;
121
122 mLOG(GBA_BATTLECHIP, DEBUG, "> %04X", cmd);
123
124 uint16_t ok;
125 switch (gate->flavor) {
126 case GBA_FLAVOR_BATTLECHIP_GATE:
127 default:
128 ok = BATTLECHIP_OK;
129 break;
130 case GBA_FLAVOR_PROGRESS_GATE:
131 ok = PROGRESS_GATE_OK;
132 break;
133 case GBA_FLAVOR_BEAST_LINK_GATE:
134 ok = BEAST_LINK_GATE_OK;
135 break;
136 case GBA_FLAVOR_BEAST_LINK_GATE_US:
137 ok = BEAST_LINK_GATE_US_OK;
138 break;
139 }
140
141 switch (gate->state) {
142 case BATTLECHIP_STATE_COMMAND:
143 mLOG(GBA_BATTLECHIP, DEBUG, "C %04X", cmd);
144 switch (cmd) {
145 case 0x0000:
146 case 0x8FFF:
147 // EXE 5, 6
148 case 0xA380:
149 case 0xA390:
150 case 0xA3A0:
151 case 0xA3B0:
152 case 0xA3C0:
153 case 0xA3D0:
154 // EXE 4
155 case 0xA6C0:
156 gate->state = -1;
157 // Fall through
158 //
159 case 0x5379:
160 case 0x537A:
161 case 0x537B:
162 case 0x537C:
163 case 0x537D:
164 case 0x537E:
165 // EXE 6
166 case 0x65AF:
167 //
168 case 0x6E8F:
169 //
170 case 0x87D0:
171 case 0x87D1:
172 case 0x87D2:
173 case 0x87D3:
174 case 0x87D4:
175 case 0x87D5:
176 case 0x87DB:
177 case 0xB7D3:
178 case 0xB7D4:
179 case 0xB7D5:
180 case 0xB7D6:
181 case 0xB7D7:
182 case 0xB7D8:
183 //
184 case 0xC4D3:
185 case 0xC4D4:
186 case 0xC4D5:
187 case 0xC4D6:
188 case 0xC4D7:
189 case 0xC4D8:
190 case 0xC4D9:
191 case 0xC4DA:
192 case 0xC4DB:
193 case 0xC4DC:
194 case 0xC4DD:
195 // EXE 4
196 case 0xD979:
197 case 0xD97A:
198 case 0xD97B:
199 case 0xD97C:
200 case 0xD97D:
201 case 0xD97E:
202 case 0xD97F:
203 case 0xD980:
204 case 0xD981:
205 case 0xD982:
206 case 0xD983:
207 case 0xD984:
208 // EXE 5
209 case 0xE49A:
210 case 0xE49B:
211 case 0xE49C:
212 reply = ok;
213 break;
214 //
215 case 0x3545:
216 case 0x3546:
217 case 0x3547:
218 case 0x3548:
219 case 0x3549:
220 case 0x354A:
221 //
222 case 0x424A:
223 case 0x424B:
224 case 0x424C:
225 case 0x424D:
226 case 0x424E:
227 case 0x424F:
228 case 0x4250:
229 //
230 case 0x5745:
231 case 0x5746:
232 case 0x5747:
233 case 0x5748:
234 case 0x5749:
235 case 0x574A:
236 // Resync
237 gate->state = BATTLECHIP_STATE_UNK_0;
238 break;
239 default:
240 mLOG(GBA_BATTLECHIP, STUB, "? %04X", cmd);
241 gate->state = -1;
242 break;
243 }
244 break;
245 case BATTLECHIP_STATE_UNK_0:
246 case BATTLECHIP_STATE_UNK_1:
247 reply = 0xFFFF;
248 break;
249 case BATTLECHIP_STATE_DATA_0:
250 reply = gate->data[0];
251 gate->data[0] += 3;
252 gate->data[0] &= 0x00FF;
253 break;
254 case BATTLECHIP_STATE_DATA_1:
255 reply = gate->data[1];
256 gate->data[1] -= 3;
257 gate->data[1] |= 0xFC00;
258 break;
259 case BATTLECHIP_STATE_ID:
260 reply = gate->chipId;
261 break;
262 case BATTLECHIP_STATE_UNK_2:
263 case BATTLECHIP_STATE_UNK_3:
264 reply = 0;
265 break;
266 case BATTLECHIP_STATE_END:
267 reply = ok;
268 gate->state = -1;
269 break;
270 }
271 ++gate->state;
272
273 mLOG(GBA_BATTLECHIP, DEBUG, "< %04X", reply);
274
275 gate->d.p->p->memory.io[REG_SIOMULTI1 >> 1] = reply;
276
277 if (gate->d.p->multiplayerControl.irq) {
278 GBARaiseIRQ(gate->d.p->p, IRQ_SIO);
279 }
280}