all repos — mgba @ d157fd6037d00d14e27c855b2d7bb29c2b3ea2d5

mGBA Game Boy Advance Emulator

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}