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_INDEX_HANDSHAKE_0 = 0,
17 BATTLECHIP_INDEX_HANDSHAKE_1 = 1,
18 BATTLECHIP_INDEX_ID = 2,
19 BATTLECHIP_INDEX_END = 6
20};
21
22enum {
23 BATTLECHIP_OK = 0xFFC6,
24 BATTLECHIP_CONTINUE = 0xFFFF,
25};
26
27static bool GBASIOBattlechipGateInit(struct GBASIODriver* driver);
28static bool GBASIOBattlechipGateLoad(struct GBASIODriver* driver);
29static uint16_t GBASIOBattlechipGateWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value);
30
31static void _battlechipTransfer(struct GBASIOBattlechipGate* gate);
32static void _battlechipTransferEvent(struct mTiming* timing, void* user, uint32_t cyclesLate);
33
34void GBASIOBattlechipGateCreate(struct GBASIOBattlechipGate* gate) {
35 gate->d.init = GBASIOBattlechipGateInit;
36 gate->d.deinit = NULL;
37 gate->d.load = GBASIOBattlechipGateLoad;
38 gate->d.unload = NULL;
39 gate->d.writeRegister = GBASIOBattlechipGateWriteRegister;
40
41 gate->event.context = gate;
42 gate->event.callback = _battlechipTransferEvent;
43 gate->event.priority = 0x80;
44}
45
46bool GBASIOBattlechipGateInit(struct GBASIODriver* driver) {
47 struct GBASIOBattlechipGate* gate = (struct GBASIOBattlechipGate*) driver;
48 gate->chipId = 0;
49 return true;
50}
51
52bool GBASIOBattlechipGateLoad(struct GBASIODriver* driver) {
53 struct GBASIOBattlechipGate* gate = (struct GBASIOBattlechipGate*) driver;
54 gate->index = BATTLECHIP_INDEX_END;
55 return true;
56}
57
58uint16_t GBASIOBattlechipGateWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value) {
59 struct GBASIOBattlechipGate* gate = (struct GBASIOBattlechipGate*) driver;
60 switch (address) {
61 case REG_SIOCNT:
62 value &= ~0xC;
63 value |= 0x8;
64 if (value & 0x80) {
65 _battlechipTransfer(gate);
66 }
67 break;
68 case REG_SIOMLT_SEND:
69 break;
70 case REG_RCNT:
71 break;
72 default:
73 break;
74 }
75 return value;
76}
77
78void _battlechipTransfer(struct GBASIOBattlechipGate* gate) {
79 int32_t cycles = GBASIOCyclesPerTransfer[gate->d.p->multiplayerControl.baud][1];
80 mTimingSchedule(&gate->d.p->p->timing, &gate->event, cycles);
81}
82
83void _battlechipTransferEvent(struct mTiming* timing, void* user, uint32_t cyclesLate) {
84 struct GBASIOBattlechipGate* gate = user;
85
86 uint16_t cmd = gate->d.p->p->memory.io[REG_SIOMLT_SEND >> 1];
87 uint16_t reply = 0xFFFF;
88 gate->d.p->p->memory.io[REG_SIOMULTI0 >> 1] = cmd;
89 gate->d.p->p->memory.io[REG_SIOMULTI2 >> 1] = 0xFFFF;
90 gate->d.p->p->memory.io[REG_SIOMULTI3 >> 1] = 0xFFFF;
91 gate->d.p->multiplayerControl.busy = 0;
92 gate->d.p->multiplayerControl.id = 0;
93
94 mLOG(GBA_BATTLECHIP, DEBUG, "> %04x", cmd);
95
96 switch (cmd) {
97 case 0x4000:
98 gate->index = 0;
99 // Fall through
100 case 0:
101 switch (gate->index) {
102 case BATTLECHIP_INDEX_HANDSHAKE_0:
103 reply = 0x00FE;
104 break;
105 case BATTLECHIP_INDEX_HANDSHAKE_1:
106 reply = 0xFFFE;
107 break;
108 case BATTLECHIP_INDEX_ID:
109 reply = gate->chipId;
110 break;
111 default:
112 if (gate->index >= BATTLECHIP_INDEX_END) {
113 reply = BATTLECHIP_OK;
114 } else if (gate->index < 0) {
115 reply = BATTLECHIP_CONTINUE;
116 } else {
117 reply = 0;
118 }
119 break;
120 }
121 ++gate->index;
122 break;
123 case 0x8FFF:
124 gate->index = -2;
125 // Fall through
126 default:
127 case 0xA3D0:
128 reply = BATTLECHIP_OK;
129 break;
130 case 0x4234:
131 case 0x574A:
132 reply = BATTLECHIP_CONTINUE;
133 break;
134 }
135
136 mLOG(GBA_BATTLECHIP, DEBUG, "< %04x", reply);
137
138 gate->d.p->p->memory.io[REG_SIOMULTI1 >> 1] = reply;
139
140 if (gate->d.p->multiplayerControl.irq) {
141 GBARaiseIRQ(gate->d.p->p, IRQ_SIO);
142 }
143}