all repos — mgba @ a2c9df0eb351acff95cfed93d2ca08ca5d448068

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