all repos — mgba @ aafac329fbb27d441c3915ceccc12ea15ac3556f

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