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-util/math.h>
11#include <mgba-util/vfs.h>
12
13mLOG_DEFINE_CATEGORY(DS_SLOT1, "DS Slot-1");
14
15static void _slot1SPI(struct mTiming*, void* context, uint32_t cyclesLate);
16static void _transferEvent(struct mTiming* timing, void* context, uint32_t cyclesLate);
17static bool _slot1GuaranteeSize(struct DSSlot1*);
18
19void DSSlot1SPIInit(struct DS* ds, struct VFile* vf) {
20 ds->memory.slot1.spiEvent.name = "DS Slot-1 SPI";
21 ds->memory.slot1.spiEvent.priority = 0x70;
22 ds->memory.slot1.spiEvent.context = NULL;
23 ds->memory.slot1.spiEvent.callback = _slot1SPI;
24 ds->memory.slot1.transferEvent.name = "DS Slot-1 Transfer";
25 ds->memory.slot1.transferEvent.priority = 0x71;
26 ds->memory.slot1.transferEvent.context = ds;
27 ds->memory.slot1.transferEvent.callback = _transferEvent;
28 ds->memory.slot1.savedataType = DS_SAVEDATA_AUTODETECT;
29 ds->memory.slot1.spiVf = vf;
30 ds->memory.slot1.spiRealVf = vf;
31 ds->memory.slot1.spiData = NULL;
32}
33
34void DSSlot1Reset(struct DS* ds) {
35 ds->memory.slot1.statusReg = 0;
36 ds->memory.slot1.spiCommand = 0;
37 ds->memory.slot1.spiHoldEnabled = 0;
38}
39
40static void _scheduleTransfer(struct DS* ds, struct mTiming* timing, uint32_t cyclesLate) {
41 DSSlot1ROMCNT romcnt = ds->memory.io7[DS_REG_ROMCNT_HI >> 1] << 16;
42 uint32_t cycles;
43 if (DSSlot1ROMCNTIsTransferRate(romcnt)) {
44 cycles = 8;
45 } else {
46 cycles = 5;
47 }
48 if (!ds->ds7.memory.slot1Access) {
49 cycles << 1;
50 }
51 cycles -= cyclesLate;
52 mTimingDeschedule(timing, &ds->memory.slot1.transferEvent);
53 mTimingSchedule(timing, &ds->memory.slot1.transferEvent, cycles);
54}
55
56static void _transferEvent(struct mTiming* timing, void* context, uint32_t cyclesLate) {
57 struct DS* ds = context;
58 DSSlot1ROMCNT romcnt;
59 // TODO: Big endian
60 LOAD_32(romcnt, DS_REG_ROMCNT_LO, ds->memory.io7);
61 if (ds->memory.slot1.transferRemaining) {
62 ds->romVf->read(ds->romVf, ds->memory.slot1.readBuffer, 4);
63 // TODO: Error check
64 ds->memory.slot1.address += 4;
65 ds->memory.slot1.transferRemaining -= 4;
66 romcnt = DSSlot1ROMCNTFillWordReady(romcnt);
67 } else {
68 DSSlot1AUXSPICNT config = ds->memory.io7[DS_REG_AUXSPICNT >> 1];
69 memset(ds->memory.slot1.readBuffer, 0, 4);
70 romcnt = DSSlot1ROMCNTClearWordReady(romcnt);
71 romcnt = DSSlot1ROMCNTClearBlockBusy(romcnt);
72 if (DSSlot1AUXSPICNTIsDoIRQ(config)) {
73 if (ds->ds7.memory.slot1Access) {
74 DSRaiseIRQ(ds->ds7.cpu, ds->ds7.memory.io, DS_IRQ_SLOT1_TRANS);
75 } else {
76 DSRaiseIRQ(ds->ds9.cpu, ds->ds9.memory.io, DS_IRQ_SLOT1_TRANS);
77 }
78 }
79 }
80 STORE_32(romcnt, DS_REG_ROMCNT_LO, ds->memory.io7);
81 STORE_32(romcnt, DS_REG_ROMCNT_LO, ds->memory.io9);
82}
83
84static void DSSlot1StartTransfer(struct DS* ds) {
85 size_t i;
86 for (i = 0; i < 8; i += 2) {
87 uint16_t bytes;
88 LOAD_16(bytes, DS_REG_ROMCMD_0 + i, ds->memory.io7);
89 ds->memory.slot1.command[i] = bytes & 0xFF;
90 ds->memory.slot1.command[i + 1] = bytes >> 8;
91 }
92 switch (ds->memory.slot1.command[0]) {
93 case 0xB7:
94 ds->memory.slot1.address = ds->memory.slot1.command[1] << 24;
95 ds->memory.slot1.address |= ds->memory.slot1.command[2] << 16;
96 ds->memory.slot1.address |= ds->memory.slot1.command[3] << 8;
97 ds->memory.slot1.address |= ds->memory.slot1.command[4];
98 if (ds->romVf) {
99 ds->romVf->seek(ds->romVf, ds->memory.slot1.address, SEEK_SET);
100 }
101 ds->memory.slot1.transferRemaining = ds->memory.slot1.transferSize;
102 if (ds->ds7.memory.slot1Access) {
103 _scheduleTransfer(ds, &ds->ds7.timing, 0);
104 } else {
105 _scheduleTransfer(ds, &ds->ds9.timing, 0);
106 }
107 break;
108 case 0xB8:
109 memcpy(ds->memory.slot1.readBuffer, DS_CHIP_ID, 4);
110 ds->memory.slot1.transferRemaining = 0;
111 break;
112 default:
113 mLOG(DS_SLOT1, STUB, "Unimplemented card command: %02X%02X%02X%02X%02X%02X%02X%02X",
114 ds->memory.slot1.command[0], ds->memory.slot1.command[1],
115 ds->memory.slot1.command[2], ds->memory.slot1.command[3],
116 ds->memory.slot1.command[4], ds->memory.slot1.command[5],
117 ds->memory.slot1.command[6], ds->memory.slot1.command[7]);
118 break;
119 }
120}
121
122DSSlot1AUXSPICNT DSSlot1Configure(struct DS* ds, DSSlot1AUXSPICNT config) {
123 if (DSSlot1AUXSPICNTIsSPIMode(config)) {
124 if (!ds->memory.slot1.spiHoldEnabled) {
125 ds->memory.slot1.spiCommand = 0;
126 }
127 ds->memory.slot1.spiHoldEnabled = DSSlot1AUXSPICNTIsCSHold(config);
128 }
129 return config;
130}
131
132DSSlot1ROMCNT DSSlot1Control(struct DS* ds, DSSlot1ROMCNT control) {
133 ds->memory.slot1.transferSize = DSSlot1ROMCNTGetBlockSize(control);
134 if (ds->memory.slot1.transferSize != 0 && ds->memory.slot1.transferSize != 7) {
135 ds->memory.slot1.transferSize = 0x100 << ds->memory.slot1.transferSize;
136 }
137
138 DSSlot1AUXSPICNT config = ds->memory.io7[DS_REG_AUXSPICNT >> 1];
139 if (DSSlot1AUXSPICNTIsSPIMode(config)) {
140 mLOG(DS_SLOT1, STUB, "Bad ROMCNT?");
141 return control;
142 }
143 if (DSSlot1ROMCNTIsBlockBusy(control)) {
144 DSSlot1StartTransfer(ds);
145 // TODO: timing
146 control = DSSlot1ROMCNTFillWordReady(control);
147 }
148 return control;
149}
150
151uint32_t DSSlot1Read(struct DS* ds) {
152 uint32_t result;
153 LOAD_32(result, 0, ds->memory.slot1.readBuffer);
154 if (ds->ds7.memory.slot1Access) {
155 _scheduleTransfer(ds, &ds->ds7.timing, 0);
156 } else {
157 _scheduleTransfer(ds, &ds->ds9.timing, 0);
158 }
159 return result;
160}
161
162void DSSlot1WriteSPI(struct DSCommon* dscore, uint8_t datum) {
163 UNUSED(datum);
164 DSSlot1AUXSPICNT control = dscore->memory.io[DS_REG_AUXSPICNT >> 1];
165 if (!DSSlot1AUXSPICNTIsSPIMode(control) || !DSSlot1AUXSPICNTIsEnable(control)) {
166 return;
167 }
168 uint32_t baud = 19 - DSSlot1AUXSPICNTGetBaud(control);
169 baud = DS_ARM7TDMI_FREQUENCY >> baud; // TODO: Right frequency for ARM9
170 control = DSSlot1AUXSPICNTFillBusy(control);
171 mTimingDeschedule(&dscore->timing, &dscore->p->memory.slot1.spiEvent);
172 mTimingSchedule(&dscore->timing, &dscore->p->memory.slot1.spiEvent, baud);
173 dscore->p->memory.slot1.spiEvent.context = dscore;
174 dscore->memory.io[DS_REG_AUXSPICNT >> 1] = control;
175 dscore->ipc->memory.io[DS_REG_AUXSPICNT >> 1] = control;
176}
177
178static uint8_t _slot1SPIAutodetect(struct DSCommon* dscore, uint8_t datum) {
179 DSSlot1AUXSPICNT control = dscore->memory.io[DS_REG_AUXSPICNT >> 1];
180 mLOG(DS_SLOT1, STUB, "Unimplemented SPI write: %04X:%02X:%02X", control, dscore->p->memory.slot1.spiCommand, datum);
181
182 if (dscore->p->memory.slot1.spiAddressingRemaining) {
183 dscore->p->memory.slot1.spiAddress <<= 8;
184 dscore->p->memory.slot1.spiAddress |= datum;
185 dscore->p->memory.slot1.spiAddressingRemaining -= 8;
186 if (dscore->p->memory.slot1.spiAddressingPc >= 0) {
187 dscore->p->memory.slot1.spiAddressingPc = dscore->cpu->gprs[ARM_PC];
188 }
189 return 0xFF;
190 } else if (dscore->cpu->gprs[ARM_PC] == dscore->p->memory.slot1.spiAddressingPc) {
191 dscore->p->memory.slot1.spiAddress <<= 8;
192 dscore->p->memory.slot1.spiAddress |= datum;
193 dscore->p->memory.slot1.savedataType = DS_SAVEDATA_FLASH;
194 return 0xFF;
195 } else {
196 if (dscore->p->memory.slot1.spiAddress) {
197 // Cease autodetection
198 dscore->p->memory.slot1.spiAddressingPc = -1;
199 }
200 if (!_slot1GuaranteeSize(&dscore->p->memory.slot1)) {
201 return 0xFF;
202 }
203 }
204
205 switch (dscore->p->memory.slot1.spiCommand) {
206 case 0x03: // RD
207 return dscore->p->memory.slot1.spiData[dscore->p->memory.slot1.spiAddress++];
208 case 0x02: // WR
209 dscore->p->memory.slot1.spiData[dscore->p->memory.slot1.spiAddress] = datum;
210 ++dscore->p->memory.slot1.spiAddress;
211 break;
212 }
213 return 0xFF;
214}
215
216static uint8_t _slot1SPIFlash(struct DSCommon* dscore, uint8_t datum) {
217 DSSlot1AUXSPICNT control = dscore->memory.io[DS_REG_AUXSPICNT >> 1];
218
219 if (dscore->p->memory.slot1.spiAddressingRemaining) {
220 dscore->p->memory.slot1.spiAddress <<= 8;
221 dscore->p->memory.slot1.spiAddress |= datum;
222 dscore->p->memory.slot1.spiAddressingRemaining -= 8;
223 return 0xFF;
224 } else {
225 if (!_slot1GuaranteeSize(&dscore->p->memory.slot1)) {
226 return 0xFF;
227 }
228 }
229
230 switch (dscore->p->memory.slot1.spiCommand) {
231 case 0x03: // RD
232 return dscore->p->memory.slot1.spiData[dscore->p->memory.slot1.spiAddress++];
233 case 0x02: // WR
234 dscore->p->memory.slot1.spiData[dscore->p->memory.slot1.spiAddress] = datum;
235 ++dscore->p->memory.slot1.spiAddress;
236 break;
237 default:
238 mLOG(DS_SLOT1, STUB, "Unimplemented SPI Flash write: %04X:%02X:%02X", control, dscore->p->memory.slot1.spiCommand, datum);
239 break;
240 }
241 return 0xFF;
242}
243
244static void _slot1SPI(struct mTiming* timing, void* context, uint32_t cyclesLate) {
245 UNUSED(timing);
246 UNUSED(cyclesLate);
247 struct DSCommon* dscore = context;
248 DSSlot1AUXSPICNT control = dscore->memory.io[DS_REG_AUXSPICNT >> 1];
249 uint8_t oldValue = dscore->memory.io[DS_REG_AUXSPIDATA >> 1];
250 uint8_t newValue = 0xFF;
251
252 if (!dscore->p->memory.slot1.spiCommand) {
253 dscore->p->memory.slot1.spiCommand = oldValue;
254 // Probably RDHI
255 if (oldValue == 0x0B && dscore->p->memory.slot1.savedataType == DS_SAVEDATA_AUTODETECT) {
256 dscore->p->memory.slot1.savedataType = DS_SAVEDATA_EEPROM512;
257 }
258 dscore->p->memory.slot1.spiAddress = 0;
259 switch (dscore->p->memory.slot1.savedataType) {
260 case DS_SAVEDATA_FLASH:
261 dscore->p->memory.slot1.spiAddressingRemaining = 24;
262 break;
263 default:
264 dscore->p->memory.slot1.spiAddressingRemaining = 16;
265 break;
266 }
267 } else {
268 switch (dscore->p->memory.slot1.spiCommand) {
269 case 0x04: // WRDI
270 dscore->p->memory.slot1.statusReg &= ~2;
271 break;
272 case 0x05: // RDSR
273 newValue = dscore->p->memory.slot1.statusReg;
274 break;
275 case 0x06: // WREN
276 dscore->p->memory.slot1.statusReg |= 2;
277 break;
278 default:
279 switch (dscore->p->memory.slot1.savedataType) {
280 case DS_SAVEDATA_AUTODETECT:
281 newValue = _slot1SPIAutodetect(dscore, oldValue);
282 break;
283 case DS_SAVEDATA_FLASH:
284 newValue = _slot1SPIFlash(dscore, oldValue);
285 break;
286 default:
287 mLOG(DS_SLOT1, STUB, "Unimplemented SPI write: %04X:%02X", control, oldValue);
288 break;
289 }
290 }
291 }
292
293 control = DSSlot1AUXSPICNTClearBusy(control);
294 dscore->memory.io[DS_REG_AUXSPIDATA >> 1] = newValue;
295 dscore->ipc->memory.io[DS_REG_AUXSPIDATA >> 1] = newValue;
296 dscore->memory.io[DS_REG_AUXSPICNT >> 1] = control;
297 dscore->ipc->memory.io[DS_REG_AUXSPICNT >> 1] = control;
298}
299
300static bool _slot1GuaranteeSize(struct DSSlot1* slot1) {
301 if (!slot1->spiVf) {
302 return false;
303 }
304 if (slot1->spiAddress >= slot1->spiVf->size(slot1->spiVf)) {
305 size_t size = toPow2(slot1->spiAddress + 1);
306 size_t oldSize = slot1->spiVf->size(slot1->spiVf);
307 if (slot1->spiData) {
308 slot1->spiVf->unmap(slot1->spiVf, slot1->spiData, oldSize);
309 slot1->spiData = NULL;
310 }
311 slot1->spiVf->truncate(slot1->spiVf, size);
312 slot1->spiVf->seek(slot1->spiVf, oldSize, SEEK_SET);
313 while (oldSize < size) {
314 static char buffer[1024];
315 memset(buffer, 0xFF, sizeof(buffer));
316 ssize_t written;
317 if (oldSize + sizeof(buffer) <= size) {
318 written = slot1->spiVf->write(slot1->spiVf, buffer, sizeof(buffer));
319 } else {
320 written = slot1->spiVf->write(slot1->spiVf, buffer, size - oldSize);
321 }
322 if (written >= 0) {
323 oldSize += written;
324 } else {
325 break;
326 }
327 }
328 }
329 if (!slot1->spiData) {
330 slot1->spiData = slot1->spiVf->map(slot1->spiVf, slot1->spiVf->size(slot1->spiVf), MAP_WRITE);
331 }
332 return slot1->spiData;
333}