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