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