all repos — mgba @ d10ed92c562aa0c724ade1cfffeb1c49c235b86c

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		return 0xFF;
150	} else if (dscore->p->memory.slot1.spiAddress & 1) {
151		dscore->p->memory.slot1.spiAddress <<= 8;
152		dscore->p->memory.slot1.spiAddress |= datum;
153		dscore->p->memory.slot1.savedataType = DS_SAVEDATA_FLASH;
154		return 0xFF;
155	} else {
156		if (!_slot1GuaranteeSize(&dscore->p->memory.slot1)) {
157			return 0xFF;
158		}
159	}
160
161	switch (dscore->p->memory.slot1.spiCommand) {
162	case 0x03: // RD
163		return dscore->p->memory.slot1.spiData[dscore->p->memory.slot1.spiAddress++];
164	case 0x02: // WR
165		dscore->p->memory.slot1.spiData[dscore->p->memory.slot1.spiAddress] = datum;
166		++dscore->p->memory.slot1.spiAddress;
167		break;
168	}
169	return 0xFF;
170}
171
172static uint8_t _slot1SPIFlash(struct DSCommon* dscore, uint8_t datum) {
173	DSSlot1AUXSPICNT control = dscore->memory.io[DS_REG_AUXSPICNT >> 1];
174
175	if (dscore->p->memory.slot1.spiAddressingRemaining) {
176		dscore->p->memory.slot1.spiAddress <<= 8;
177		dscore->p->memory.slot1.spiAddress |= datum;
178		dscore->p->memory.slot1.spiAddressingRemaining -= 8;
179		return 0xFF;
180	} else {
181		if (!_slot1GuaranteeSize(&dscore->p->memory.slot1)) {
182			return 0xFF;
183		}
184	}
185
186	switch (dscore->p->memory.slot1.spiCommand) {
187	case 0x03: // RD
188		return dscore->p->memory.slot1.spiData[dscore->p->memory.slot1.spiAddress++];
189	case 0x02: // WR
190		dscore->p->memory.slot1.spiData[dscore->p->memory.slot1.spiAddress] = datum;
191		++dscore->p->memory.slot1.spiAddress;
192		break;
193	default:
194		mLOG(DS_SLOT1, STUB, "Unimplemented SPI Flash write: %04X:%02X:%02X", control, dscore->p->memory.slot1.spiCommand, datum);
195		break;
196	}
197	return 0xFF;
198}
199
200static void _slot1SPI(struct mTiming* timing, void* context, uint32_t cyclesLate) {
201	UNUSED(timing);
202	UNUSED(cyclesLate);
203	struct DSCommon* dscore = context;
204	DSSlot1AUXSPICNT control = dscore->memory.io[DS_REG_AUXSPICNT >> 1];
205	uint8_t oldValue = dscore->memory.io[DS_REG_AUXSPIDATA >> 1];
206	uint8_t newValue = 0xFF;
207
208	if (!dscore->p->memory.slot1.spiCommand) {
209		dscore->p->memory.slot1.spiCommand = oldValue;
210		// Probably RDHI
211		if (oldValue == 0x0B && dscore->p->memory.slot1.savedataType == DS_SAVEDATA_AUTODETECT) {
212			dscore->p->memory.slot1.savedataType = DS_SAVEDATA_EEPROM512;
213		}
214		dscore->p->memory.slot1.spiAddress = 0;
215		switch (dscore->p->memory.slot1.savedataType) {
216		case DS_SAVEDATA_FLASH:
217			dscore->p->memory.slot1.spiAddressingRemaining = 24;
218			break;
219		default:
220			dscore->p->memory.slot1.spiAddressingRemaining = 16;
221			break;
222		}
223	} else {
224		switch (dscore->p->memory.slot1.spiCommand) {
225		case 0x04: // WRDI
226			dscore->p->memory.slot1.statusReg &= ~2;
227			break;
228		case 0x05: // RDSR
229			newValue = dscore->p->memory.slot1.statusReg;
230			break;
231		case 0x06: // WREN
232			dscore->p->memory.slot1.statusReg |= 2;
233			break;
234		default:
235			switch (dscore->p->memory.slot1.savedataType) {
236			case DS_SAVEDATA_AUTODETECT:
237				newValue = _slot1SPIAutodetect(dscore, oldValue);
238				break;
239			case DS_SAVEDATA_FLASH:
240				newValue = _slot1SPIFlash(dscore, oldValue);
241				break;
242			default:
243				mLOG(DS_SLOT1, STUB, "Unimplemented SPI write: %04X:%02X", control, oldValue);
244				break;
245			}
246		}
247	}
248
249	control = DSSlot1AUXSPICNTClearBusy(control);
250	dscore->memory.io[DS_REG_AUXSPIDATA >> 1] = newValue;
251	dscore->ipc->memory.io[DS_REG_AUXSPIDATA >> 1] = newValue;
252	dscore->memory.io[DS_REG_AUXSPICNT >> 1] = control;
253	dscore->ipc->memory.io[DS_REG_AUXSPICNT >> 1] = control;
254}
255
256static bool _slot1GuaranteeSize(struct DSSlot1* slot1) {
257	if (!slot1->spiVf) {
258		return false;
259	}
260	if (slot1->spiAddress >= slot1->spiVf->size(slot1->spiVf)) {
261		size_t size = toPow2(slot1->spiAddress + 1);
262		if (slot1->spiData) {
263			slot1->spiVf->unmap(slot1->spiVf, slot1->spiData, slot1->spiVf->size(slot1->spiVf));
264			slot1->spiData = NULL;
265		}
266		slot1->spiVf->truncate(slot1->spiVf, size);
267		// TODO: Write FFs
268	}
269	if (!slot1->spiData) {
270		slot1->spiData = slot1->spiVf->map(slot1->spiVf, slot1->spiVf->size(slot1->spiVf), MAP_WRITE);
271	}
272	return slot1->spiData;
273}