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