all repos — mgba @ ec626d723fbb4716a697ad3cac0b0b29943dba00

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