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