all repos — mgba @ e5ae0209a6974f495b4f7f18629e1113ed6c6aa0

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 {
151		if (!_slot1GuaranteeSize(&dscore->p->memory.slot1)) {
152			return 0xFF;
153		}
154	}
155
156	switch (dscore->p->memory.slot1.spiCommand) {
157	case 0x03: // RD
158		return dscore->p->memory.slot1.spiData[dscore->p->memory.slot1.spiAddress++];
159	case 0x02: // WR
160		dscore->p->memory.slot1.spiData[dscore->p->memory.slot1.spiAddress] = datum;
161		++dscore->p->memory.slot1.spiAddress;
162		break;
163	}
164	return 0xFF;
165}
166
167static void _slot1SPI(struct mTiming* timing, void* context, uint32_t cyclesLate) {
168	UNUSED(timing);
169	UNUSED(cyclesLate);
170	struct DSCommon* dscore = context;
171	DSSlot1AUXSPICNT control = dscore->memory.io[DS_REG_AUXSPICNT >> 1];
172	uint8_t oldValue = dscore->memory.io[DS_REG_AUXSPIDATA >> 1];
173	uint8_t newValue = 0xFF;
174
175	if (!dscore->p->memory.slot1.spiCommand) {
176		dscore->p->memory.slot1.spiCommand = oldValue;
177		// Probably RDHI
178		if (oldValue == 0x0B && dscore->p->memory.slot1.savedataType == DS_SAVEDATA_AUTODETECT) {
179			dscore->p->memory.slot1.savedataType = DS_SAVEDATA_EEPROM512;
180		}
181		dscore->p->memory.slot1.spiAddress = 0;
182		dscore->p->memory.slot1.spiAddressingRemaining = 16;
183	} else {
184		switch (dscore->p->memory.slot1.spiCommand) {
185		case 0x04: // WRDI
186			dscore->p->memory.slot1.statusReg &= ~2;
187			break;
188		case 0x05: // RDSR
189			newValue = dscore->p->memory.slot1.statusReg;
190			break;
191		case 0x06: // WREN
192			dscore->p->memory.slot1.statusReg |= 2;
193			break;
194		default:
195			switch (dscore->p->memory.slot1.savedataType) {
196			case DS_SAVEDATA_AUTODETECT:
197				newValue = _slot1SPIAutodetect(dscore, oldValue);
198				break;
199			default:
200				mLOG(DS_SLOT1, STUB, "Unimplemented SPI write: %04X:%02X", control, oldValue);
201				break;
202			}
203		}
204	}
205
206	control = DSSlot1AUXSPICNTClearBusy(control);
207	dscore->memory.io[DS_REG_AUXSPIDATA >> 1] = newValue;
208	dscore->ipc->memory.io[DS_REG_AUXSPIDATA >> 1] = newValue;
209	dscore->memory.io[DS_REG_AUXSPICNT >> 1] = control;
210	dscore->ipc->memory.io[DS_REG_AUXSPICNT >> 1] = control;
211}
212
213static bool _slot1GuaranteeSize(struct DSSlot1* slot1) {
214	if (!slot1->spiVf) {
215		return false;
216	}
217	if (slot1->spiAddress >= slot1->spiVf->size(slot1->spiVf)) {
218		size_t size = toPow2(slot1->spiAddress + 1);
219		if (slot1->spiData) {
220			slot1->spiVf->unmap(slot1->spiVf, slot1->spiData, slot1->spiVf->size(slot1->spiVf));
221			slot1->spiData = NULL;
222		}
223		slot1->spiVf->truncate(slot1->spiVf, size);
224		// TODO: Write FFs
225	}
226	if (!slot1->spiData) {
227		slot1->spiData = slot1->spiVf->map(slot1->spiVf, slot1->spiVf->size(slot1->spiVf), MAP_WRITE);
228	}
229	return slot1->spiData;
230}