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