src/ds/slot1.c (view raw)
1/* Copyright (c) 2013-2017 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/internal/ds/slot1.h>
7
8#include <mgba/internal/arm/macros.h>
9#include <mgba/internal/ds/ds.h>
10#include <mgba-util/math.h>
11#include <mgba-util/vfs.h>
12
13mLOG_DEFINE_CATEGORY(DS_SLOT1, "DS Slot-1");
14
15static void _slot1SPI(struct mTiming*, void* context, uint32_t cyclesLate);
16static bool _slot1GuaranteeSize(struct DSSlot1*);
17
18void DSSlot1SPIInit(struct DS* ds, struct VFile* vf) {
19 ds->memory.slot1.spiEvent.name = "DS Slot-1 SPI";
20 ds->memory.slot1.spiEvent.priority = 0x70;
21 ds->memory.slot1.spiEvent.context = NULL;
22 ds->memory.slot1.spiEvent.callback = _slot1SPI;
23 ds->memory.slot1.savedataType = DS_SAVEDATA_AUTODETECT;
24 ds->memory.slot1.spiVf = vf;
25 ds->memory.slot1.spiRealVf = vf;
26 ds->memory.slot1.spiData = NULL;
27}
28
29void DSSlot1Reset(struct DS* ds) {
30 ds->memory.slot1.statusReg = 0;
31 ds->memory.slot1.spiCommand = 0;
32 ds->memory.slot1.spiHoldEnabled = 0;
33}
34
35static void DSSlot1StepTransfer(struct DS* ds) {
36 DSSlot1ROMCNT romcnt;
37 // TODO: Big endian
38 LOAD_32(romcnt, DS_REG_ROMCNT_LO, ds->memory.io7);
39 if (ds->memory.slot1.transferRemaining) {
40 ds->romVf->read(ds->romVf, ds->memory.slot1.readBuffer, 4);
41 // TODO: Error check
42 ds->memory.slot1.address += 4;
43 ds->memory.slot1.transferRemaining -= 4;
44 romcnt = DSSlot1ROMCNTFillWordReady(romcnt);
45 } else {
46 memset(ds->memory.slot1.readBuffer, 0, 4);
47 romcnt = DSSlot1ROMCNTClearWordReady(romcnt);
48 // TODO: IRQ
49 romcnt = DSSlot1ROMCNTClearBlockBusy(romcnt);
50 }
51 STORE_32(romcnt, DS_REG_ROMCNT_LO, ds->memory.io7);
52 STORE_32(romcnt, DS_REG_ROMCNT_LO, ds->memory.io9);
53}
54
55static void DSSlot1StartTransfer(struct DS* ds) {
56 size_t i;
57 for (i = 0; i < 8; i += 2) {
58 uint16_t bytes;
59 LOAD_16(bytes, DS_REG_ROMCMD_0 + i, ds->memory.io7);
60 ds->memory.slot1.command[i] = bytes & 0xFF;
61 ds->memory.slot1.command[i + 1] = bytes >> 8;
62 }
63 switch (ds->memory.slot1.command[0]) {
64 case 0xB7:
65 ds->memory.slot1.address = ds->memory.slot1.command[1] << 24;
66 ds->memory.slot1.address |= ds->memory.slot1.command[2] << 16;
67 ds->memory.slot1.address |= ds->memory.slot1.command[3] << 8;
68 ds->memory.slot1.address |= ds->memory.slot1.command[4];
69 if (ds->romVf) {
70 ds->romVf->seek(ds->romVf, ds->memory.slot1.address, SEEK_SET);
71 }
72 ds->memory.slot1.transferRemaining = ds->memory.slot1.transferSize;
73 DSSlot1StepTransfer(ds);
74 break;
75 case 0xB8:
76 memcpy(ds->memory.slot1.readBuffer, DS_CHIP_ID, 4);
77 ds->memory.slot1.transferRemaining = 0;
78 break;
79 default:
80 mLOG(DS_SLOT1, STUB, "Unimplemented card command: %02X%02X%02X%02X%02X%02X%02X%02X",
81 ds->memory.slot1.command[0], ds->memory.slot1.command[1],
82 ds->memory.slot1.command[2], ds->memory.slot1.command[3],
83 ds->memory.slot1.command[4], ds->memory.slot1.command[5],
84 ds->memory.slot1.command[6], ds->memory.slot1.command[7]);
85 break;
86 }
87}
88
89DSSlot1AUXSPICNT DSSlot1Configure(struct DS* ds, DSSlot1AUXSPICNT config) {
90 if (DSSlot1AUXSPICNTIsSPIMode(config)) {
91 if (!ds->memory.slot1.spiHoldEnabled) {
92 ds->memory.slot1.spiCommand = 0;
93 }
94 ds->memory.slot1.spiHoldEnabled = DSSlot1AUXSPICNTIsCSHold(config);
95 }
96 return config;
97}
98
99DSSlot1ROMCNT DSSlot1Control(struct DS* ds, DSSlot1ROMCNT control) {
100 ds->memory.slot1.transferSize = DSSlot1ROMCNTGetBlockSize(control);
101 if (ds->memory.slot1.transferSize != 0 && ds->memory.slot1.transferSize != 7) {
102 ds->memory.slot1.transferSize = 0x100 << ds->memory.slot1.transferSize;
103 }
104
105 DSSlot1AUXSPICNT config = ds->memory.io7[DS_REG_AUXSPICNT >> 1];
106 if (DSSlot1AUXSPICNTIsSPIMode(config)) {
107 mLOG(DS_SLOT1, STUB, "Bad ROMCNT?");
108 return control;
109 }
110 if (DSSlot1ROMCNTIsBlockBusy(control)) {
111 DSSlot1StartTransfer(ds);
112 // TODO: timing
113 control = DSSlot1ROMCNTFillWordReady(control);
114 }
115 return control;
116}
117
118uint32_t DSSlot1Read(struct DS* ds) {
119 uint32_t result;
120 LOAD_32(result, 0, ds->memory.slot1.readBuffer);
121 DSSlot1StepTransfer(ds);
122 return result;
123}
124
125void DSSlot1WriteSPI(struct DSCommon* dscore, uint8_t datum) {
126 UNUSED(datum);
127 DSSlot1AUXSPICNT control = dscore->memory.io[DS_REG_AUXSPICNT >> 1];
128 if (!DSSlot1AUXSPICNTIsSPIMode(control) || !DSSlot1AUXSPICNTIsEnable(control)) {
129 return;
130 }
131 uint32_t baud = 19 - DSSlot1AUXSPICNTGetBaud(control);
132 baud = DS_ARM7TDMI_FREQUENCY >> baud; // TODO: Right frequency for ARM9
133 control = DSSlot1AUXSPICNTFillBusy(control);
134 mTimingDeschedule(&dscore->timing, &dscore->p->memory.slot1.spiEvent);
135 mTimingSchedule(&dscore->timing, &dscore->p->memory.slot1.spiEvent, baud);
136 dscore->p->memory.slot1.spiEvent.context = dscore;
137 dscore->memory.io[DS_REG_AUXSPICNT >> 1] = control;
138 dscore->ipc->memory.io[DS_REG_AUXSPICNT >> 1] = control;
139}
140
141static uint8_t _slot1SPIAutodetect(struct DSCommon* dscore, uint8_t datum) {
142 DSSlot1AUXSPICNT control = dscore->memory.io[DS_REG_AUXSPICNT >> 1];
143 mLOG(DS_SLOT1, STUB, "Unimplemented SPI write: %04X:%02X:%02X", control, dscore->p->memory.slot1.spiCommand, datum);
144
145 if (dscore->p->memory.slot1.spiAddressingRemaining) {
146 dscore->p->memory.slot1.spiAddress <<= 8;
147 dscore->p->memory.slot1.spiAddress |= datum;
148 dscore->p->memory.slot1.spiAddressingRemaining -= 8;
149 return 0xFF;
150 } else if (dscore->p->memory.slot1.spiAddress & 1) {
151 dscore->p->memory.slot1.spiAddress <<= 8;
152 dscore->p->memory.slot1.spiAddress |= datum;
153 dscore->p->memory.slot1.savedataType = DS_SAVEDATA_FLASH;
154 return 0xFF;
155 } else {
156 if (!_slot1GuaranteeSize(&dscore->p->memory.slot1)) {
157 return 0xFF;
158 }
159 }
160
161 switch (dscore->p->memory.slot1.spiCommand) {
162 case 0x03: // RD
163 return dscore->p->memory.slot1.spiData[dscore->p->memory.slot1.spiAddress++];
164 case 0x02: // WR
165 dscore->p->memory.slot1.spiData[dscore->p->memory.slot1.spiAddress] = datum;
166 ++dscore->p->memory.slot1.spiAddress;
167 break;
168 }
169 return 0xFF;
170}
171
172static uint8_t _slot1SPIFlash(struct DSCommon* dscore, uint8_t datum) {
173 DSSlot1AUXSPICNT control = dscore->memory.io[DS_REG_AUXSPICNT >> 1];
174
175 if (dscore->p->memory.slot1.spiAddressingRemaining) {
176 dscore->p->memory.slot1.spiAddress <<= 8;
177 dscore->p->memory.slot1.spiAddress |= datum;
178 dscore->p->memory.slot1.spiAddressingRemaining -= 8;
179 return 0xFF;
180 } else {
181 if (!_slot1GuaranteeSize(&dscore->p->memory.slot1)) {
182 return 0xFF;
183 }
184 }
185
186 switch (dscore->p->memory.slot1.spiCommand) {
187 case 0x03: // RD
188 return dscore->p->memory.slot1.spiData[dscore->p->memory.slot1.spiAddress++];
189 case 0x02: // WR
190 dscore->p->memory.slot1.spiData[dscore->p->memory.slot1.spiAddress] = datum;
191 ++dscore->p->memory.slot1.spiAddress;
192 break;
193 default:
194 mLOG(DS_SLOT1, STUB, "Unimplemented SPI Flash write: %04X:%02X:%02X", control, dscore->p->memory.slot1.spiCommand, datum);
195 break;
196 }
197 return 0xFF;
198}
199
200static void _slot1SPI(struct mTiming* timing, void* context, uint32_t cyclesLate) {
201 UNUSED(timing);
202 UNUSED(cyclesLate);
203 struct DSCommon* dscore = context;
204 DSSlot1AUXSPICNT control = dscore->memory.io[DS_REG_AUXSPICNT >> 1];
205 uint8_t oldValue = dscore->memory.io[DS_REG_AUXSPIDATA >> 1];
206 uint8_t newValue = 0xFF;
207
208 if (!dscore->p->memory.slot1.spiCommand) {
209 dscore->p->memory.slot1.spiCommand = oldValue;
210 // Probably RDHI
211 if (oldValue == 0x0B && dscore->p->memory.slot1.savedataType == DS_SAVEDATA_AUTODETECT) {
212 dscore->p->memory.slot1.savedataType = DS_SAVEDATA_EEPROM512;
213 }
214 dscore->p->memory.slot1.spiAddress = 0;
215 switch (dscore->p->memory.slot1.savedataType) {
216 case DS_SAVEDATA_FLASH:
217 dscore->p->memory.slot1.spiAddressingRemaining = 24;
218 break;
219 default:
220 dscore->p->memory.slot1.spiAddressingRemaining = 16;
221 break;
222 }
223 } else {
224 switch (dscore->p->memory.slot1.spiCommand) {
225 case 0x04: // WRDI
226 dscore->p->memory.slot1.statusReg &= ~2;
227 break;
228 case 0x05: // RDSR
229 newValue = dscore->p->memory.slot1.statusReg;
230 break;
231 case 0x06: // WREN
232 dscore->p->memory.slot1.statusReg |= 2;
233 break;
234 default:
235 switch (dscore->p->memory.slot1.savedataType) {
236 case DS_SAVEDATA_AUTODETECT:
237 newValue = _slot1SPIAutodetect(dscore, oldValue);
238 break;
239 case DS_SAVEDATA_FLASH:
240 newValue = _slot1SPIFlash(dscore, oldValue);
241 break;
242 default:
243 mLOG(DS_SLOT1, STUB, "Unimplemented SPI write: %04X:%02X", control, oldValue);
244 break;
245 }
246 }
247 }
248
249 control = DSSlot1AUXSPICNTClearBusy(control);
250 dscore->memory.io[DS_REG_AUXSPIDATA >> 1] = newValue;
251 dscore->ipc->memory.io[DS_REG_AUXSPIDATA >> 1] = newValue;
252 dscore->memory.io[DS_REG_AUXSPICNT >> 1] = control;
253 dscore->ipc->memory.io[DS_REG_AUXSPICNT >> 1] = control;
254}
255
256static bool _slot1GuaranteeSize(struct DSSlot1* slot1) {
257 if (!slot1->spiVf) {
258 return false;
259 }
260 if (slot1->spiAddress >= slot1->spiVf->size(slot1->spiVf)) {
261 size_t size = toPow2(slot1->spiAddress + 1);
262 if (slot1->spiData) {
263 slot1->spiVf->unmap(slot1->spiVf, slot1->spiData, slot1->spiVf->size(slot1->spiVf));
264 slot1->spiData = NULL;
265 }
266 slot1->spiVf->truncate(slot1->spiVf, size);
267 // TODO: Write FFs
268 }
269 if (!slot1->spiData) {
270 slot1->spiData = slot1->spiVf->map(slot1->spiVf, slot1->spiVf->size(slot1->spiVf), MAP_WRITE);
271 }
272 return slot1->spiData;
273}