all repos — mgba @ medusa-a1

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