all repos — mgba @ a9ca1221f2eb445dc71149682bb7cae040ff6a57

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-util/math.h>
 11#include <mgba-util/vfs.h>
 12
 13mLOG_DEFINE_CATEGORY(DS_SLOT1, "DS Slot-1");
 14
 15static void _slot1SPI(struct mTiming*, void* context, uint32_t cyclesLate);
 16static bool _slot1GuaranteeSize(struct DSSlot1*);
 17
 18void DSSlot1SPIInit(struct DS* ds, struct VFile* vf) {
 19	ds->memory.slot1.spiEvent.name = "DS Slot-1 SPI";
 20	ds->memory.slot1.spiEvent.priority = 0x70;
 21	ds->memory.slot1.spiEvent.context = NULL;
 22	ds->memory.slot1.spiEvent.callback = _slot1SPI;
 23	ds->memory.slot1.savedataType = DS_SAVEDATA_AUTODETECT;
 24	ds->memory.slot1.spiVf = vf;
 25	ds->memory.slot1.spiRealVf = vf;
 26	ds->memory.slot1.spiData = NULL;
 27}
 28
 29void DSSlot1Reset(struct DS* ds) {
 30	ds->memory.slot1.statusReg = 0;
 31	ds->memory.slot1.spiCommand = 0;
 32	ds->memory.slot1.spiHoldEnabled = 0;
 33}
 34
 35static void DSSlot1StepTransfer(struct DS* ds) {
 36	DSSlot1ROMCNT romcnt;
 37	// TODO: Big endian
 38	LOAD_32(romcnt, DS_REG_ROMCNT_LO, ds->memory.io7);
 39	if (ds->memory.slot1.transferRemaining) {
 40		ds->romVf->read(ds->romVf, ds->memory.slot1.readBuffer, 4);
 41		// TODO: Error check
 42		ds->memory.slot1.address += 4;
 43		ds->memory.slot1.transferRemaining -= 4;
 44		romcnt = DSSlot1ROMCNTFillWordReady(romcnt);
 45	} else {
 46		memset(ds->memory.slot1.readBuffer, 0, 4);
 47		romcnt = DSSlot1ROMCNTClearWordReady(romcnt);
 48		// TODO: IRQ
 49		romcnt = DSSlot1ROMCNTClearBlockBusy(romcnt);
 50	}
 51	STORE_32(romcnt, DS_REG_ROMCNT_LO, ds->memory.io7);
 52	STORE_32(romcnt, DS_REG_ROMCNT_LO, ds->memory.io9);
 53}
 54
 55static void DSSlot1StartTransfer(struct DS* ds) {
 56	size_t i;
 57	for (i = 0; i < 8; i += 2) {
 58		uint16_t bytes;
 59		LOAD_16(bytes, DS_REG_ROMCMD_0 + i, ds->memory.io7);
 60		ds->memory.slot1.command[i] = bytes & 0xFF;
 61		ds->memory.slot1.command[i + 1] = bytes >> 8;
 62	}
 63	switch (ds->memory.slot1.command[0]) {
 64	case 0xB7:
 65		ds->memory.slot1.address = ds->memory.slot1.command[1] << 24;
 66		ds->memory.slot1.address |= ds->memory.slot1.command[2] << 16;
 67		ds->memory.slot1.address |= ds->memory.slot1.command[3] << 8;
 68		ds->memory.slot1.address |= ds->memory.slot1.command[4];
 69		if (ds->romVf) {
 70			ds->romVf->seek(ds->romVf, ds->memory.slot1.address, SEEK_SET);
 71		}
 72		ds->memory.slot1.transferRemaining = ds->memory.slot1.transferSize;
 73		DSSlot1StepTransfer(ds);
 74		break;
 75	case 0xB8:
 76		memcpy(ds->memory.slot1.readBuffer, DS_CHIP_ID, 4);
 77		ds->memory.slot1.transferRemaining = 0;
 78		break;
 79	default:
 80		mLOG(DS_SLOT1, STUB, "Unimplemented card command: %02X%02X%02X%02X%02X%02X%02X%02X",
 81		     ds->memory.slot1.command[0], ds->memory.slot1.command[1],
 82		     ds->memory.slot1.command[2], ds->memory.slot1.command[3],
 83		     ds->memory.slot1.command[4], ds->memory.slot1.command[5],
 84		     ds->memory.slot1.command[6], ds->memory.slot1.command[7]);
 85		break;
 86	}
 87}
 88
 89DSSlot1AUXSPICNT DSSlot1Configure(struct DS* ds, DSSlot1AUXSPICNT config) {
 90	if (DSSlot1AUXSPICNTIsSPIMode(config)) {
 91		if (!ds->memory.slot1.spiHoldEnabled) {
 92			ds->memory.slot1.spiCommand = 0;
 93		}
 94		ds->memory.slot1.spiHoldEnabled = DSSlot1AUXSPICNTIsCSHold(config);
 95	}
 96	return config;
 97}
 98
 99DSSlot1ROMCNT DSSlot1Control(struct DS* ds, DSSlot1ROMCNT control) {
100	ds->memory.slot1.transferSize = DSSlot1ROMCNTGetBlockSize(control);
101	if (ds->memory.slot1.transferSize != 0 && ds->memory.slot1.transferSize != 7) {
102		ds->memory.slot1.transferSize = 0x100 << ds->memory.slot1.transferSize;
103	}
104
105	DSSlot1AUXSPICNT config = ds->memory.io7[DS_REG_AUXSPICNT >> 1];
106	if (DSSlot1AUXSPICNTIsSPIMode(config)) {
107		mLOG(DS_SLOT1, STUB, "Bad ROMCNT?");
108		return control;
109	}
110	if (DSSlot1ROMCNTIsBlockBusy(control)) {
111		DSSlot1StartTransfer(ds);
112		// TODO: timing
113		control = DSSlot1ROMCNTFillWordReady(control);
114	}
115	return control;
116}
117
118uint32_t DSSlot1Read(struct DS* ds) {
119	uint32_t result;
120	LOAD_32(result, 0, ds->memory.slot1.readBuffer);
121	DSSlot1StepTransfer(ds);
122	return result;
123}
124
125void DSSlot1WriteSPI(struct DSCommon* dscore, uint8_t datum) {
126	UNUSED(datum);
127	DSSlot1AUXSPICNT control = dscore->memory.io[DS_REG_AUXSPICNT >> 1];
128	if (!DSSlot1AUXSPICNTIsSPIMode(control) || !DSSlot1AUXSPICNTIsEnable(control)) {
129		return;
130	}
131	uint32_t baud = 19 - DSSlot1AUXSPICNTGetBaud(control);
132	baud = DS_ARM7TDMI_FREQUENCY >> baud; // TODO: Right frequency for ARM9
133	control = DSSlot1AUXSPICNTFillBusy(control);
134	mTimingDeschedule(&dscore->timing, &dscore->p->memory.slot1.spiEvent);
135	mTimingSchedule(&dscore->timing, &dscore->p->memory.slot1.spiEvent, baud);
136	dscore->p->memory.slot1.spiEvent.context = dscore;
137	dscore->memory.io[DS_REG_AUXSPICNT >> 1] = control;
138	dscore->ipc->memory.io[DS_REG_AUXSPICNT >> 1] = control;
139}
140
141static uint8_t _slot1SPIAutodetect(struct DSCommon* dscore, uint8_t datum) {
142	DSSlot1AUXSPICNT control = dscore->memory.io[DS_REG_AUXSPICNT >> 1];
143	mLOG(DS_SLOT1, STUB, "Unimplemented SPI write: %04X:%02X:%02X", control, dscore->p->memory.slot1.spiCommand, datum);
144
145	if (dscore->p->memory.slot1.spiAddressingRemaining) {
146		dscore->p->memory.slot1.spiAddress <<= 8;
147		dscore->p->memory.slot1.spiAddress |= datum;
148		dscore->p->memory.slot1.spiAddressingRemaining -= 8;
149		if (dscore->p->memory.slot1.spiAddressingPc >= 0) {
150			dscore->p->memory.slot1.spiAddressingPc = dscore->cpu->gprs[ARM_PC];
151		}
152		return 0xFF;
153	} else if (dscore->cpu->gprs[ARM_PC] == dscore->p->memory.slot1.spiAddressingPc) {
154		dscore->p->memory.slot1.spiAddress <<= 8;
155		dscore->p->memory.slot1.spiAddress |= datum;
156		dscore->p->memory.slot1.savedataType = DS_SAVEDATA_FLASH;
157		return 0xFF;
158	} else {
159		if (dscore->p->memory.slot1.spiAddress) {
160			// Cease autodetection
161			dscore->p->memory.slot1.spiAddressingPc = -1;
162		}
163		if (!_slot1GuaranteeSize(&dscore->p->memory.slot1)) {
164			return 0xFF;
165		}
166	}
167
168	switch (dscore->p->memory.slot1.spiCommand) {
169	case 0x03: // RD
170		return dscore->p->memory.slot1.spiData[dscore->p->memory.slot1.spiAddress++];
171	case 0x02: // WR
172		dscore->p->memory.slot1.spiData[dscore->p->memory.slot1.spiAddress] = datum;
173		++dscore->p->memory.slot1.spiAddress;
174		break;
175	}
176	return 0xFF;
177}
178
179static uint8_t _slot1SPIFlash(struct DSCommon* dscore, uint8_t datum) {
180	DSSlot1AUXSPICNT control = dscore->memory.io[DS_REG_AUXSPICNT >> 1];
181
182	if (dscore->p->memory.slot1.spiAddressingRemaining) {
183		dscore->p->memory.slot1.spiAddress <<= 8;
184		dscore->p->memory.slot1.spiAddress |= datum;
185		dscore->p->memory.slot1.spiAddressingRemaining -= 8;
186		return 0xFF;
187	} else {
188		if (!_slot1GuaranteeSize(&dscore->p->memory.slot1)) {
189			return 0xFF;
190		}
191	}
192
193	switch (dscore->p->memory.slot1.spiCommand) {
194	case 0x03: // RD
195		return dscore->p->memory.slot1.spiData[dscore->p->memory.slot1.spiAddress++];
196	case 0x02: // WR
197		dscore->p->memory.slot1.spiData[dscore->p->memory.slot1.spiAddress] = datum;
198		++dscore->p->memory.slot1.spiAddress;
199		break;
200	default:
201		mLOG(DS_SLOT1, STUB, "Unimplemented SPI Flash write: %04X:%02X:%02X", control, dscore->p->memory.slot1.spiCommand, datum);
202		break;
203	}
204	return 0xFF;
205}
206
207static void _slot1SPI(struct mTiming* timing, void* context, uint32_t cyclesLate) {
208	UNUSED(timing);
209	UNUSED(cyclesLate);
210	struct DSCommon* dscore = context;
211	DSSlot1AUXSPICNT control = dscore->memory.io[DS_REG_AUXSPICNT >> 1];
212	uint8_t oldValue = dscore->memory.io[DS_REG_AUXSPIDATA >> 1];
213	uint8_t newValue = 0xFF;
214
215	if (!dscore->p->memory.slot1.spiCommand) {
216		dscore->p->memory.slot1.spiCommand = oldValue;
217		// Probably RDHI
218		if (oldValue == 0x0B && dscore->p->memory.slot1.savedataType == DS_SAVEDATA_AUTODETECT) {
219			dscore->p->memory.slot1.savedataType = DS_SAVEDATA_EEPROM512;
220		}
221		dscore->p->memory.slot1.spiAddress = 0;
222		switch (dscore->p->memory.slot1.savedataType) {
223		case DS_SAVEDATA_FLASH:
224			dscore->p->memory.slot1.spiAddressingRemaining = 24;
225			break;
226		default:
227			dscore->p->memory.slot1.spiAddressingRemaining = 16;
228			break;
229		}
230	} else {
231		switch (dscore->p->memory.slot1.spiCommand) {
232		case 0x04: // WRDI
233			dscore->p->memory.slot1.statusReg &= ~2;
234			break;
235		case 0x05: // RDSR
236			newValue = dscore->p->memory.slot1.statusReg;
237			break;
238		case 0x06: // WREN
239			dscore->p->memory.slot1.statusReg |= 2;
240			break;
241		default:
242			switch (dscore->p->memory.slot1.savedataType) {
243			case DS_SAVEDATA_AUTODETECT:
244				newValue = _slot1SPIAutodetect(dscore, oldValue);
245				break;
246			case DS_SAVEDATA_FLASH:
247				newValue = _slot1SPIFlash(dscore, oldValue);
248				break;
249			default:
250				mLOG(DS_SLOT1, STUB, "Unimplemented SPI write: %04X:%02X", control, oldValue);
251				break;
252			}
253		}
254	}
255
256	control = DSSlot1AUXSPICNTClearBusy(control);
257	dscore->memory.io[DS_REG_AUXSPIDATA >> 1] = newValue;
258	dscore->ipc->memory.io[DS_REG_AUXSPIDATA >> 1] = newValue;
259	dscore->memory.io[DS_REG_AUXSPICNT >> 1] = control;
260	dscore->ipc->memory.io[DS_REG_AUXSPICNT >> 1] = control;
261}
262
263static bool _slot1GuaranteeSize(struct DSSlot1* slot1) {
264	if (!slot1->spiVf) {
265		return false;
266	}
267	if (slot1->spiAddress >= slot1->spiVf->size(slot1->spiVf)) {
268		size_t size = toPow2(slot1->spiAddress + 1);
269		size_t oldSize = slot1->spiVf->size(slot1->spiVf);
270		if (slot1->spiData) {
271			slot1->spiVf->unmap(slot1->spiVf, slot1->spiData, oldSize);
272			slot1->spiData = NULL;
273		}
274		slot1->spiVf->truncate(slot1->spiVf, size);
275		slot1->spiVf->seek(slot1->spiVf, oldSize, SEEK_SET);
276		while (oldSize < size) {
277			static char buffer[1024];
278			memset(buffer, 0xFF, sizeof(buffer));
279			ssize_t written;
280			if (oldSize + sizeof(buffer) <= size) {
281				written = slot1->spiVf->write(slot1->spiVf, buffer, sizeof(buffer));
282			} else {
283				written = slot1->spiVf->write(slot1->spiVf, buffer, size - oldSize);
284			}
285			if (written >= 0) {
286				oldSize += written;
287			} else {
288				break;
289			}
290		}
291	}
292	if (!slot1->spiData) {
293		slot1->spiData = slot1->spiVf->map(slot1->spiVf, slot1->spiVf->size(slot1->spiVf), MAP_WRITE);
294	}
295	return slot1->spiData;
296}