all repos — mgba @ cec4d48c8afb49e7e81e0e3e5eeeb7588331bc32

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