all repos — mgba @ cb0f95b07053e63e817bd05df0a36bf917667d09

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