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_SYNC = -1,
17 BATTLECHIP_STATE_COMMAND = 0,
18 BATTLECHIP_STATE_UNK_0 = 1,
19 BATTLECHIP_STATE_UNK_1 = 2,
20 BATTLECHIP_STATE_DATA_0 = 3,
21 BATTLECHIP_STATE_DATA_1 = 4,
22 BATTLECHIP_STATE_ID = 5,
23 BATTLECHIP_STATE_UNK_2 = 6,
24 BATTLECHIP_STATE_UNK_3 = 7,
25 BATTLECHIP_STATE_END = 8
26};
27
28enum {
29 BATTLECHIP_OK = 0xFFC6,
30 PROGRESS_GATE_OK = 0xFFC7,
31 BEAST_LINK_GATE_OK = 0xFFC4,
32 BEAST_LINK_GATE_US_OK = 0xFF00,
33 BATTLECHIP_CONTINUE = 0xFFFF,
34};
35
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 = NULL;
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 GBASIOBattlechipGateLoad(struct GBASIODriver* driver) {
58 struct GBASIOBattlechipGate* gate = (struct GBASIOBattlechipGate*) driver;
59 gate->state = BATTLECHIP_STATE_SYNC;
60 gate->data[0] = 0x00FE;
61 gate->data[1] = 0xFFFE;
62 return true;
63}
64
65uint16_t GBASIOBattlechipGateWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value) {
66 struct GBASIOBattlechipGate* gate = (struct GBASIOBattlechipGate*) driver;
67 switch (address) {
68 case REG_SIOCNT:
69 value &= ~0xC;
70 value |= 0x8;
71 if (value & 0x80) {
72 _battlechipTransfer(gate);
73 }
74 break;
75 case REG_SIOMLT_SEND:
76 break;
77 case REG_RCNT:
78 break;
79 default:
80 break;
81 }
82 return value;
83}
84
85void _battlechipTransfer(struct GBASIOBattlechipGate* gate) {
86 int32_t cycles;
87 if (gate->d.p->mode == SIO_NORMAL_32) {
88 cycles = GBA_ARM7TDMI_FREQUENCY / 0x40000;
89 } else {
90 cycles = GBASIOCyclesPerTransfer[GBASIOMultiplayerGetBaud(gate->d.p->siocnt)][1];
91 }
92 mTimingDeschedule(&gate->d.p->p->timing, &gate->event);
93 mTimingSchedule(&gate->d.p->p->timing, &gate->event, cycles);
94}
95
96void _battlechipTransferEvent(struct mTiming* timing, void* user, uint32_t cyclesLate) {
97 UNUSED(timing);
98 struct GBASIOBattlechipGate* gate = user;
99
100 if (gate->d.p->mode == SIO_NORMAL_32) {
101 gate->d.p->p->memory.io[REG_SIODATA32_LO >> 1] = 0;
102 gate->d.p->p->memory.io[REG_SIODATA32_HI >> 1] = 0;
103 gate->d.p->siocnt = GBASIONormalClearStart(gate->d.p->siocnt);
104 if (GBASIONormalIsIrq(gate->d.p->siocnt)) {
105 GBARaiseIRQ(gate->d.p->p, IRQ_SIO, cyclesLate);
106 }
107 return;
108 }
109
110 uint16_t cmd = gate->d.p->p->memory.io[REG_SIOMLT_SEND >> 1];
111 uint16_t reply = 0xFFFF;
112 gate->d.p->p->memory.io[REG_SIOMULTI0 >> 1] = cmd;
113 gate->d.p->p->memory.io[REG_SIOMULTI2 >> 1] = 0xFFFF;
114 gate->d.p->p->memory.io[REG_SIOMULTI3 >> 1] = 0xFFFF;
115 gate->d.p->siocnt = GBASIOMultiplayerClearBusy(gate->d.p->siocnt);
116 gate->d.p->siocnt = GBASIOMultiplayerSetId(gate->d.p->siocnt, 0);
117
118 mLOG(GBA_BATTLECHIP, DEBUG, "Game: %04X (%i)", cmd, gate->state);
119
120 uint16_t ok;
121 switch (gate->flavor) {
122 case GBA_FLAVOR_BATTLECHIP_GATE:
123 default:
124 ok = BATTLECHIP_OK;
125 break;
126 case GBA_FLAVOR_PROGRESS_GATE:
127 ok = PROGRESS_GATE_OK;
128 break;
129 case GBA_FLAVOR_BEAST_LINK_GATE:
130 ok = BEAST_LINK_GATE_OK;
131 break;
132 case GBA_FLAVOR_BEAST_LINK_GATE_US:
133 ok = BEAST_LINK_GATE_US_OK;
134 break;
135 }
136
137 if (gate->state != BATTLECHIP_STATE_COMMAND) {
138 // Resync if needed
139 switch (cmd) {
140 // EXE 5, 6
141 case 0xA380:
142 case 0xA390:
143 case 0xA3A0:
144 case 0xA3B0:
145 case 0xA3C0:
146 case 0xA3D0:
147 // EXE 4
148 case 0xA6C0:
149 mLOG(GBA_BATTLECHIP, DEBUG, "Resync detected");
150 gate->state = BATTLECHIP_STATE_SYNC;
151 break;
152 }
153 }
154
155 switch (gate->state) {
156 case BATTLECHIP_STATE_SYNC:
157 if (cmd != 0x8FFF) {
158 --gate->state;
159 }
160 // Fall through
161 case BATTLECHIP_STATE_COMMAND:
162 reply = ok;
163 break;
164 case BATTLECHIP_STATE_UNK_0:
165 case BATTLECHIP_STATE_UNK_1:
166 reply = 0xFFFF;
167 break;
168 case BATTLECHIP_STATE_DATA_0:
169 reply = gate->data[0];
170 gate->data[0] += 3;
171 gate->data[0] &= 0x00FF;
172 break;
173 case BATTLECHIP_STATE_DATA_1:
174 reply = gate->data[1];
175 gate->data[1] -= 3;
176 gate->data[1] |= 0xFC00;
177 break;
178 case BATTLECHIP_STATE_ID:
179 reply = gate->chipId;
180 break;
181 case BATTLECHIP_STATE_UNK_2:
182 case BATTLECHIP_STATE_UNK_3:
183 reply = 0;
184 break;
185 case BATTLECHIP_STATE_END:
186 reply = ok;
187 gate->state = BATTLECHIP_STATE_SYNC;
188 break;
189 }
190
191 mLOG(GBA_BATTLECHIP, DEBUG, "Gate: %04X (%i)", reply, gate->state);
192 ++gate->state;
193
194 gate->d.p->p->memory.io[REG_SIOMULTI1 >> 1] = reply;
195
196 if (GBASIOMultiplayerIsIrq(gate->d.p->siocnt)) {
197 GBARaiseIRQ(gate->d.p->p, IRQ_SIO, cyclesLate);
198 }
199}