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 BATTLECHIP_CONTINUE = 0xFFFF,
30};
31
32static bool GBASIOBattlechipGateInit(struct GBASIODriver* driver);
33static bool GBASIOBattlechipGateLoad(struct GBASIODriver* driver);
34static uint16_t GBASIOBattlechipGateWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value);
35
36static void _battlechipTransfer(struct GBASIOBattlechipGate* gate);
37static void _battlechipTransferEvent(struct mTiming* timing, void* user, uint32_t cyclesLate);
38
39void GBASIOBattlechipGateCreate(struct GBASIOBattlechipGate* gate) {
40 gate->d.init = GBASIOBattlechipGateInit;
41 gate->d.deinit = NULL;
42 gate->d.load = GBASIOBattlechipGateLoad;
43 gate->d.unload = NULL;
44 gate->d.writeRegister = GBASIOBattlechipGateWriteRegister;
45
46 gate->event.context = gate;
47 gate->event.callback = _battlechipTransferEvent;
48 gate->event.priority = 0x80;
49}
50
51bool GBASIOBattlechipGateInit(struct GBASIODriver* driver) {
52 struct GBASIOBattlechipGate* gate = (struct GBASIOBattlechipGate*) driver;
53 gate->chipId = 0;
54 return true;
55}
56
57bool GBASIOBattlechipGateLoad(struct GBASIODriver* driver) {
58 struct GBASIOBattlechipGate* gate = (struct GBASIOBattlechipGate*) driver;
59 gate->state = BATTLECHIP_STATE_COMMAND;
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 = GBASIOCyclesPerTransfer[gate->d.p->multiplayerControl.baud][1];
87 mTimingSchedule(&gate->d.p->p->timing, &gate->event, cycles);
88}
89
90void _battlechipTransferEvent(struct mTiming* timing, void* user, uint32_t cyclesLate) {
91 struct GBASIOBattlechipGate* gate = user;
92
93 uint16_t cmd = gate->d.p->p->memory.io[REG_SIOMLT_SEND >> 1];
94 uint16_t reply = 0xFFFF;
95 gate->d.p->p->memory.io[REG_SIOMULTI0 >> 1] = cmd;
96 gate->d.p->p->memory.io[REG_SIOMULTI2 >> 1] = 0xFFFF;
97 gate->d.p->p->memory.io[REG_SIOMULTI3 >> 1] = 0xFFFF;
98 gate->d.p->multiplayerControl.busy = 0;
99 gate->d.p->multiplayerControl.id = 0;
100
101 mLOG(GBA_BATTLECHIP, DEBUG, "> %04X", cmd);
102
103 switch (gate->state) {
104 case BATTLECHIP_STATE_COMMAND:
105 mLOG(GBA_BATTLECHIP, DEBUG, "C %04X", cmd);
106 switch (cmd) {
107 case 0x0000:
108 case 0x8FFF:
109 case 0xA380:
110 case 0xA390:
111 case 0xA3A0:
112 case 0xA3B0:
113 case 0xA3C0:
114 case 0xA3D0:
115 gate->state = -1;
116 // Fall through
117 case 0x5379:
118 case 0x537A:
119 case 0x537B:
120 case 0x537C:
121 case 0x537D:
122 case 0x537E:
123 case 0xD979:
124 case 0xD97A:
125 case 0xD97B:
126 case 0xD97C:
127 case 0xD97D:
128 case 0xD97E:
129 reply = BATTLECHIP_OK;
130 break;
131 case 0x5745:
132 case 0x5746:
133 case 0x5747:
134 case 0x5748:
135 case 0x5749:
136 case 0x574A:
137 case 0xFC00:
138 // Resync
139 gate->state = BATTLECHIP_STATE_UNK_0;
140 break;
141 default:
142 mLOG(GBA_BATTLECHIP, STUB, "? %04X", cmd);
143 break;
144 }
145 break;
146 case BATTLECHIP_STATE_UNK_0:
147 case BATTLECHIP_STATE_UNK_1:
148 reply = 0xFFFF;
149 break;
150 case BATTLECHIP_STATE_DATA_0:
151 reply = gate->data[0];
152 gate->data[0] += 3;
153 gate->data[0] &= 0xF0FF;
154 break;
155 case BATTLECHIP_STATE_DATA_1:
156 reply = gate->data[1];
157 gate->data[1] -= 3;
158 gate->data[1] |= 0xFE00;
159 break;
160 case BATTLECHIP_STATE_ID:
161 reply = gate->chipId;
162 break;
163 case BATTLECHIP_STATE_UNK_2:
164 case BATTLECHIP_STATE_UNK_3:
165 reply = 0;
166 break;
167 case BATTLECHIP_STATE_END:
168 reply = BATTLECHIP_OK;
169 gate->state = -1;
170 break;
171 }
172 ++gate->state;
173
174 mLOG(GBA_BATTLECHIP, DEBUG, "< %04X", reply);
175
176 gate->d.p->p->memory.io[REG_SIOMULTI1 >> 1] = reply;
177
178 if (gate->d.p->multiplayerControl.irq) {
179 GBARaiseIRQ(gate->d.p->p, IRQ_SIO);
180 }
181}