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