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/internal/ds/dma.h>
11#include <mgba-util/math.h>
12#include <mgba-util/vfs.h>
13
14mLOG_DEFINE_CATEGORY(DS_SLOT1, "DS Slot-1", "ds.slot1");
15
16static void _slot1SPI(struct mTiming*, void* context, uint32_t cyclesLate);
17static void _transferEvent(struct mTiming* timing, void* context, uint32_t cyclesLate);
18static bool _slot1GuaranteeSize(struct DSSlot1*);
19
20void DSSlot1SPIInit(struct DS* ds, struct VFile* vf) {
21 ds->memory.slot1.spiEvent.name = "DS Slot-1 SPI";
22 ds->memory.slot1.spiEvent.priority = 0x70;
23 ds->memory.slot1.spiEvent.context = NULL;
24 ds->memory.slot1.spiEvent.callback = _slot1SPI;
25 ds->memory.slot1.transferEvent.name = "DS Slot-1 Transfer";
26 ds->memory.slot1.transferEvent.priority = 0x71;
27 ds->memory.slot1.transferEvent.context = ds;
28 ds->memory.slot1.transferEvent.callback = _transferEvent;
29 ds->memory.slot1.savedataType = DS_SAVEDATA_AUTODETECT;
30 ds->memory.slot1.spiVf = vf;
31 ds->memory.slot1.spiRealVf = vf;
32 ds->memory.slot1.spiData = NULL;
33}
34
35void DSSlot1Reset(struct DS* ds) {
36 ds->memory.slot1.statusReg = 0;
37 ds->memory.slot1.spiCommand = 0;
38 ds->memory.slot1.spiHoldEnabled = 0;
39 ds->memory.slot1.dmaSource = -1;
40 ds->memory.slot1.spiAddressingBits = 16;
41}
42
43static void _scheduleTransfer(struct DS* ds, struct mTiming* timing, uint32_t cyclesLate) {
44 DSSlot1ROMCNT romcnt = ds->memory.io7[DS_REG_ROMCNT_HI >> 1] << 16;
45 uint32_t cycles;
46 if (DSSlot1ROMCNTIsTransferRate(romcnt)) {
47 cycles = 8;
48 } else {
49 cycles = 5;
50 }
51 if (!ds->ds7.memory.slot1Access) {
52 cycles <<= 1;
53 }
54 cycles -= cyclesLate;
55 mTimingDeschedule(timing, &ds->memory.slot1.transferEvent);
56 mTimingSchedule(timing, &ds->memory.slot1.transferEvent, cycles);
57}
58
59static void _transferEvent(struct mTiming* timing, void* context, uint32_t cyclesLate) {
60 struct DS* ds = context;
61 DSSlot1ROMCNT romcnt;
62 // TODO: Big endian
63 LOAD_32(romcnt, DS_REG_ROMCNT_LO, ds->memory.io7);
64
65 struct DSCommon* dscore;
66 if (ds->ds7.memory.slot1Access) {
67 dscore = &ds->ds7;
68 } else {
69 dscore = &ds->ds9;
70 }
71
72 struct GBADMA* dma = NULL;
73 if (ds->memory.slot1.dmaSource >= 0) {
74 dma = &dscore->memory.dma[ds->memory.slot1.dmaSource];
75 }
76 bool hasDMA = false;
77 if (dma) {
78 if (ds->ds7.memory.slot1Access && GBADMARegisterGetTiming(dma->reg) == DS7_DMA_TIMING_SLOT1) {
79 hasDMA = true;
80 }
81 if (ds->ds9.memory.slot1Access && GBADMARegisterGetTiming9(dma->reg) == DS9_DMA_TIMING_SLOT1) {
82 hasDMA = true;
83 }
84 if (!GBADMARegisterIsEnable(dma->reg)) {
85 hasDMA = false;
86 }
87 }
88 if (!hasDMA) {
89 ds->memory.slot1.dmaSource = -1;
90 }
91
92 if (ds->memory.slot1.transferRemaining) {
93 ds->romVf->read(ds->romVf, ds->memory.slot1.readBuffer, 4);
94 // TODO: Error check
95 ds->memory.slot1.address += 4;
96 ds->memory.slot1.transferRemaining -= 4;
97 romcnt = DSSlot1ROMCNTFillWordReady(romcnt);
98
99 if (hasDMA) {
100 dma->when = mTimingCurrentTime(timing);
101 dma->nextCount = 1;
102 DSDMAUpdate(dscore);
103 }
104 } else {
105 DSSlot1AUXSPICNT config = ds->memory.io7[DS_REG_AUXSPICNT >> 1];
106 memset(ds->memory.slot1.readBuffer, 0, 4);
107 romcnt = DSSlot1ROMCNTClearWordReady(romcnt);
108 romcnt = DSSlot1ROMCNTClearBlockBusy(romcnt);
109 if (DSSlot1AUXSPICNTIsDoIRQ(config)) {
110 DSRaiseIRQ(dscore->cpu, dscore->memory.io, DS_IRQ_SLOT1_TRANS);
111 }
112 if (hasDMA && !GBADMARegisterIsRepeat(dma->reg)) {
113 dma->reg = GBADMARegisterClearEnable(dma->reg);
114 dscore->memory.io[(DS_REG_DMA0CNT_HI + ds->memory.slot1.dmaSource * (DS_REG_DMA1CNT_HI - DS_REG_DMA0CNT_HI)) >> 1] = dma->reg;
115 }
116 }
117 STORE_32(romcnt, DS_REG_ROMCNT_LO, ds->memory.io7);
118 STORE_32(romcnt, DS_REG_ROMCNT_LO, ds->memory.io9);
119}
120
121static void DSSlot1StartTransfer(struct DS* ds) {
122 size_t i;
123 for (i = 0; i < 8; i += 2) {
124 uint16_t bytes;
125 LOAD_16(bytes, DS_REG_ROMCMD_0 + i, ds->memory.io7);
126 ds->memory.slot1.command[i] = bytes & 0xFF;
127 ds->memory.slot1.command[i + 1] = bytes >> 8;
128 }
129 switch (ds->memory.slot1.command[0]) {
130 case 0xB7:
131 ds->memory.slot1.address = ds->memory.slot1.command[1] << 24;
132 ds->memory.slot1.address |= ds->memory.slot1.command[2] << 16;
133 ds->memory.slot1.address |= ds->memory.slot1.command[3] << 8;
134 ds->memory.slot1.address |= ds->memory.slot1.command[4];
135 if (ds->memory.slot1.address < 0x8000) {
136 mLOG(DS_SLOT1, GAME_ERROR, "Invalid read from secure area: %04X", ds->memory.slot1.address);
137 ds->memory.slot1.address = 0x8000 + (ds->memory.slot1.address & 0x1FF);
138 }
139 if (ds->romVf) {
140 ds->romVf->seek(ds->romVf, ds->memory.slot1.address, SEEK_SET);
141 }
142 ds->memory.slot1.transferRemaining = ds->memory.slot1.transferSize;
143 if (ds->ds7.memory.slot1Access) {
144 _scheduleTransfer(ds, &ds->ds7.timing, 0);
145 } else {
146 _scheduleTransfer(ds, &ds->ds9.timing, 0);
147 }
148 break;
149 case 0xB8:
150 memcpy(ds->memory.slot1.readBuffer, DS_CHIP_ID, 4);
151 ds->memory.slot1.transferRemaining = 0;
152 break;
153 default:
154 mLOG(DS_SLOT1, STUB, "Unimplemented card command: %02X%02X%02X%02X%02X%02X%02X%02X",
155 ds->memory.slot1.command[0], ds->memory.slot1.command[1],
156 ds->memory.slot1.command[2], ds->memory.slot1.command[3],
157 ds->memory.slot1.command[4], ds->memory.slot1.command[5],
158 ds->memory.slot1.command[6], ds->memory.slot1.command[7]);
159 break;
160 }
161}
162
163DSSlot1AUXSPICNT DSSlot1Configure(struct DS* ds, DSSlot1AUXSPICNT config) {
164 if (DSSlot1AUXSPICNTIsSPIMode(config)) {
165 if (!ds->memory.slot1.spiHoldEnabled) {
166 ds->memory.slot1.spiCommand = 0;
167 }
168 ds->memory.slot1.spiHoldEnabled = DSSlot1AUXSPICNTIsCSHold(config);
169 }
170 return config;
171}
172
173DSSlot1ROMCNT DSSlot1Control(struct DS* ds, DSSlot1ROMCNT control) {
174 ds->memory.slot1.transferSize = DSSlot1ROMCNTGetBlockSize(control);
175 if (ds->memory.slot1.transferSize != 0 && ds->memory.slot1.transferSize != 7) {
176 ds->memory.slot1.transferSize = 0x100 << ds->memory.slot1.transferSize;
177 }
178
179 DSSlot1AUXSPICNT config = ds->memory.io7[DS_REG_AUXSPICNT >> 1];
180 if (DSSlot1AUXSPICNTIsSPIMode(config)) {
181 mLOG(DS_SLOT1, STUB, "Bad ROMCNT?");
182 return control;
183 }
184 if (DSSlot1ROMCNTIsBlockBusy(control)) {
185 DSSlot1StartTransfer(ds);
186 // TODO: timing
187 control = DSSlot1ROMCNTFillWordReady(control);
188 }
189 return control;
190}
191
192uint32_t DSSlot1Read(struct DS* ds) {
193 uint32_t result;
194 LOAD_32(result, 0, ds->memory.slot1.readBuffer);
195 if (ds->ds7.memory.slot1Access) {
196 _scheduleTransfer(ds, &ds->ds7.timing, 0);
197 } else {
198 _scheduleTransfer(ds, &ds->ds9.timing, 0);
199 }
200 return result;
201}
202
203void DSSlot1WriteSPI(struct DSCommon* dscore, uint8_t datum) {
204 UNUSED(datum);
205 DSSlot1AUXSPICNT control = dscore->memory.io[DS_REG_AUXSPICNT >> 1];
206 if (!DSSlot1AUXSPICNTIsSPIMode(control) || !DSSlot1AUXSPICNTIsEnable(control)) {
207 return;
208 }
209 uint32_t baud = 19 - DSSlot1AUXSPICNTGetBaud(control);
210 baud = DS_ARM7TDMI_FREQUENCY >> baud; // TODO: Right frequency for ARM9
211 control = DSSlot1AUXSPICNTFillBusy(control);
212 mTimingDeschedule(&dscore->timing, &dscore->p->memory.slot1.spiEvent);
213 mTimingSchedule(&dscore->timing, &dscore->p->memory.slot1.spiEvent, baud);
214 dscore->p->memory.slot1.spiEvent.context = dscore;
215 dscore->memory.io[DS_REG_AUXSPICNT >> 1] = control;
216 dscore->ipc->memory.io[DS_REG_AUXSPICNT >> 1] = control;
217}
218
219static uint8_t _slot1SPIAutodetect(struct DSCommon* dscore, uint8_t datum) {
220 DSSlot1AUXSPICNT control = dscore->memory.io[DS_REG_AUXSPICNT >> 1];
221 mLOG(DS_SLOT1, STUB, "Unimplemented SPI write: %04X:%02X:%02X", control, dscore->p->memory.slot1.spiCommand, datum);
222
223 if (dscore->p->memory.slot1.spiAddressingRemaining) {
224 dscore->p->memory.slot1.spiAddress <<= 8;
225 dscore->p->memory.slot1.spiAddress |= datum;
226 dscore->p->memory.slot1.spiAddressingRemaining -= 8;
227 return 0xFF;
228 } else if (dscore->p->isHomebrew) {
229 if (!_slot1GuaranteeSize(&dscore->p->memory.slot1)) {
230 return 0xFF;
231 }
232 }
233 if (!dscore->p->memory.slot1.spiData) {
234 return 0xFF;
235 }
236
237 switch (dscore->p->memory.slot1.spiCommand) {
238 case 0x03: // RD
239 return dscore->p->memory.slot1.spiData[dscore->p->memory.slot1.spiAddress++];
240 case 0x02: // WR
241 dscore->p->memory.slot1.spiData[dscore->p->memory.slot1.spiAddress] = datum;
242 ++dscore->p->memory.slot1.spiAddress;
243 break;
244 }
245 return 0xFF;
246}
247
248static uint8_t _slot1SPIEEPROM(struct DSCommon* dscore, uint8_t datum) {
249 DSSlot1AUXSPICNT control = dscore->memory.io[DS_REG_AUXSPICNT >> 1];
250
251 if (dscore->p->memory.slot1.spiAddressingRemaining) {
252 dscore->p->memory.slot1.spiAddress <<= 8;
253 dscore->p->memory.slot1.spiAddress |= datum;
254 dscore->p->memory.slot1.spiAddressingRemaining -= 8;
255 return 0xFF;
256 } else if (dscore->p->isHomebrew) {
257 if (!_slot1GuaranteeSize(&dscore->p->memory.slot1)) {
258 return 0xFF;
259 }
260 }
261 if (!dscore->p->memory.slot1.spiData) {
262 return 0xFF;
263 }
264
265 switch (dscore->p->memory.slot1.spiCommand) {
266 case 0x03: // RDLO
267 case 0x0B: // RDHI
268 return dscore->p->memory.slot1.spiData[dscore->p->memory.slot1.spiAddress++];
269 case 0x02: // WRLO
270 case 0x0A: // WRHI
271 dscore->p->memory.slot1.spiData[dscore->p->memory.slot1.spiAddress] = datum;
272 ++dscore->p->memory.slot1.spiAddress;
273 break;
274 }
275 return 0xFF;
276}
277
278static uint8_t _slot1SPIFlash(struct DSCommon* dscore, uint8_t datum) {
279 DSSlot1AUXSPICNT control = dscore->memory.io[DS_REG_AUXSPICNT >> 1];
280
281 if (dscore->p->memory.slot1.spiAddressingRemaining) {
282 dscore->p->memory.slot1.spiAddress <<= 8;
283 dscore->p->memory.slot1.spiAddress |= datum;
284 dscore->p->memory.slot1.spiAddressingRemaining -= 8;
285 return 0xFF;
286 } else if (dscore->p->isHomebrew) {
287 if (!_slot1GuaranteeSize(&dscore->p->memory.slot1)) {
288 return 0xFF;
289 }
290 }
291 if (!dscore->p->memory.slot1.spiData) {
292 return 0xFF;
293 }
294
295 uint8_t oldValue;
296 switch (dscore->p->memory.slot1.spiCommand) {
297 case 0x03: // RD
298 oldValue = dscore->p->memory.slot1.spiData[dscore->p->memory.slot1.spiAddress];
299 ++dscore->p->memory.slot1.spiAddress;
300 return oldValue;
301 case 0x02: // PP
302 dscore->p->memory.slot1.spiData[dscore->p->memory.slot1.spiAddress] = datum;
303 ++dscore->p->memory.slot1.spiAddress;
304 break;
305 case 0x0A: // PW
306 oldValue = dscore->p->memory.slot1.spiData[dscore->p->memory.slot1.spiAddress];
307 dscore->p->memory.slot1.spiData[dscore->p->memory.slot1.spiAddress] = datum;
308 ++dscore->p->memory.slot1.spiAddress;
309 return oldValue;
310 default:
311 mLOG(DS_SLOT1, STUB, "Unimplemented SPI Flash write: %04X:%02X:%02X", control, dscore->p->memory.slot1.spiCommand, datum);
312 break;
313 }
314 return 0xFF;
315}
316
317static void _slot1SPI(struct mTiming* timing, void* context, uint32_t cyclesLate) {
318 UNUSED(timing);
319 UNUSED(cyclesLate);
320 struct DSCommon* dscore = context;
321 DSSlot1AUXSPICNT control = dscore->memory.io[DS_REG_AUXSPICNT >> 1];
322 uint8_t oldValue = dscore->memory.io[DS_REG_AUXSPIDATA >> 1];
323 uint8_t newValue = 0xFF;
324
325 if (!dscore->p->memory.slot1.spiCommand) {
326 dscore->p->memory.slot1.spiCommand = oldValue;
327 // Probably RDHI
328 if (oldValue == 0x0B && dscore->p->memory.slot1.savedataType == DS_SAVEDATA_AUTODETECT) {
329 dscore->p->memory.slot1.savedataType = DS_SAVEDATA_EEPROM512;
330 }
331 if ((oldValue & 0x08) && dscore->p->memory.slot1.savedataType == DS_SAVEDATA_EEPROM512) {
332 dscore->p->memory.slot1.spiAddress = 1;
333 } else {
334 dscore->p->memory.slot1.spiAddress = 0;
335 }
336 dscore->p->memory.slot1.spiAddressingRemaining = dscore->p->memory.slot1.spiAddressingBits;
337 } else {
338 switch (dscore->p->memory.slot1.spiCommand) {
339 case 0x04: // WRDI
340 dscore->p->memory.slot1.statusReg &= ~2;
341 break;
342 case 0x05: // RDSR
343 newValue = dscore->p->memory.slot1.statusReg;
344 break;
345 case 0x06: // WREN
346 dscore->p->memory.slot1.statusReg |= 2;
347 break;
348 default:
349 switch (dscore->p->memory.slot1.savedataType) {
350 case DS_SAVEDATA_AUTODETECT:
351 newValue = _slot1SPIAutodetect(dscore, oldValue);
352 break;
353 case DS_SAVEDATA_FLASH:
354 newValue = _slot1SPIFlash(dscore, oldValue);
355 break;
356 case DS_SAVEDATA_EEPROM:
357 case DS_SAVEDATA_EEPROM512:
358 newValue = _slot1SPIEEPROM(dscore, oldValue);
359 break;
360 default:
361 mLOG(DS_SLOT1, STUB, "Unimplemented SPI write: %04X:%02X", control, oldValue);
362 break;
363 }
364 }
365 }
366
367 control = DSSlot1AUXSPICNTClearBusy(control);
368 dscore->memory.io[DS_REG_AUXSPIDATA >> 1] = newValue;
369 dscore->ipc->memory.io[DS_REG_AUXSPIDATA >> 1] = newValue;
370 dscore->memory.io[DS_REG_AUXSPICNT >> 1] = control;
371 dscore->ipc->memory.io[DS_REG_AUXSPICNT >> 1] = control;
372}
373
374static bool _slot1GuaranteeSize(struct DSSlot1* slot1) {
375 if (!slot1->spiVf) {
376 return false;
377 }
378 if (slot1->spiAddress >= slot1->spiVf->size(slot1->spiVf)) {
379 size_t size = toPow2(slot1->spiAddress + 1);
380 size_t oldSize = slot1->spiVf->size(slot1->spiVf);
381 if (slot1->spiData) {
382 slot1->spiVf->unmap(slot1->spiVf, slot1->spiData, oldSize);
383 slot1->spiData = NULL;
384 }
385 slot1->spiVf->truncate(slot1->spiVf, size);
386 slot1->spiVf->seek(slot1->spiVf, oldSize, SEEK_SET);
387 while (oldSize < size) {
388 static char buffer[1024];
389 memset(buffer, 0xFF, sizeof(buffer));
390 ssize_t written;
391 if (oldSize + sizeof(buffer) <= size) {
392 written = slot1->spiVf->write(slot1->spiVf, buffer, sizeof(buffer));
393 } else {
394 written = slot1->spiVf->write(slot1->spiVf, buffer, size - oldSize);
395 }
396 if (written >= 0) {
397 oldSize += written;
398 } else {
399 break;
400 }
401 }
402 }
403 if (!slot1->spiData) {
404 slot1->spiData = slot1->spiVf->map(slot1->spiVf, slot1->spiVf->size(slot1->spiVf), MAP_WRITE);
405 }
406 return slot1->spiData;
407}
408
409void DSSlot1ConfigureSPI(struct DS* ds, uint32_t paramPtr) {
410 struct ARMCore* cpu = ds->ds7.cpu;
411 uint32_t saveParams = cpu->memory.load32(cpu, paramPtr + 4, NULL);
412 uint32_t size = 1 << ((saveParams & 0xFF00) >> 8);
413 if ((saveParams & 0xFF) == 2) {
414 ds->memory.slot1.savedataType = DS_SAVEDATA_FLASH;
415 } else {
416 ds->memory.slot1.savedataType = DS_SAVEDATA_EEPROM;
417 }
418 if (size >= 0x10000) {
419 ds->memory.slot1.spiAddressingBits = 24;
420 } else if (size <= 0x200) {
421 ds->memory.slot1.spiAddressingBits = 8;
422 ds->memory.slot1.savedataType = DS_SAVEDATA_EEPROM512;
423 } else {
424 ds->memory.slot1.spiAddressingBits = 16;
425 }
426 ds->memory.slot1.spiAddress = size - 1;
427 _slot1GuaranteeSize(&ds->memory.slot1);
428}
429
430void DSSlot1ScheduleDMA(struct DSCommon* dscore, int number, struct GBADMA* info) {
431 UNUSED(info);
432 dscore->p->memory.slot1.dmaSource = number;
433}