all repos — mgba @ 9639d7ad49ee24ca045c6db170bee825d615538b

mGBA Game Boy Advance Emulator

src/ds/spi.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/spi.h>
  7
  8#include <mgba/internal/ds/ds.h>
  9#include <mgba-util/vfs.h>
 10
 11mLOG_DEFINE_CATEGORY(DS_SPI, "DS SPI");
 12
 13static void _tscEvent(struct mTiming*, void* context, uint32_t cyclesLate);
 14static void _firmwareEvent(struct mTiming* timing, void* context, uint32_t cyclesLate);
 15
 16void DSSPIReset(struct DS* ds) {
 17	memset(&ds->memory.spiBus, 0, sizeof(ds->memory.spiBus));
 18	ds->memory.spiBus.event.name = "DS SPI Event";
 19	ds->memory.spiBus.event.context = ds;
 20	ds->memory.spiBus.event.callback = _tscEvent;
 21	ds->memory.spiBus.event.priority = 0x60;
 22}
 23
 24DSSPICNT DSSPIWriteControl(struct DS* ds, uint16_t control) {
 25	// TODO
 26	if (!ds->memory.spiBus.holdEnabled) {
 27		ds->memory.spiBus.tscControlByte = 0;
 28		ds->memory.spiBus.firmCommand = 0;
 29	}
 30	ds->memory.spiBus.holdEnabled = DSSPICNTIsCSHold(control);
 31	return control;
 32}
 33
 34void DSSPIWrite(struct DS* ds, uint8_t datum) {
 35	DSSPICNT control = ds->memory.io7[DS7_REG_SPICNT >> 1];
 36	if (!DSSPICNTIsEnable(control)) {
 37		return;
 38	}
 39	uint32_t baud = 19 - DSSPICNTGetBaud(control);
 40	baud = DS_ARM7TDMI_FREQUENCY >> baud;
 41	switch (DSSPICNTGetChipSelect(control)) {
 42	case DS_SPI_DEV_TSC:
 43		ds->memory.spiBus.event.callback = _tscEvent;
 44		break;
 45	case DS_SPI_DEV_FIRMWARE:
 46		ds->memory.spiBus.event.callback = _firmwareEvent;
 47		break;
 48	case DS_SPI_DEV_POWERMAN:
 49	default:
 50		mLOG(DS_SPI, STUB, "Unimplemented data write: %04X:%02X", control, datum);
 51		break;
 52	}
 53	control = DSSPICNTFillBusy(control);
 54	mTimingDeschedule(&ds->ds7.timing, &ds->memory.spiBus.event);
 55	mTimingSchedule(&ds->ds7.timing, &ds->memory.spiBus.event, baud);
 56	ds->memory.io7[DS7_REG_SPICNT >> 1] = control;
 57}
 58
 59static void _tscEvent(struct mTiming* timing, void* context, uint32_t cyclesLate) {
 60	UNUSED(timing);
 61	UNUSED(cyclesLate);
 62	struct DS* ds = context;
 63	uint8_t oldValue = ds->memory.io7[DS7_REG_SPIDATA >> 1];
 64	DSSPICNT control = ds->memory.io7[DS7_REG_SPICNT >> 1];
 65	uint8_t newValue = 0;
 66
 67	// TODO: /PENIRQ
 68	if (ds->memory.spiBus.tscOffset > 0) {
 69		// TODO: Make generic?
 70		if (ds->memory.spiBus.tscOffset < 12) {
 71			newValue = (ds->memory.spiBus.tscRegister & 0x1F) << 3;
 72			ds->memory.spiBus.tscOffset = 12;
 73		} else {
 74			newValue = 0;
 75		}
 76	} else if (ds->memory.spiBus.tscControlByte) {
 77		switch (DSTSCControlByteGetChannel(ds->memory.spiBus.tscControlByte)) {
 78		case DS_TSC_CHANNEL_TS_X:
 79			mLOG(DS_SPI, STUB, "Unimplemented TSC channel X");
 80			ds->memory.spiBus.tscRegister = 0;
 81			break;
 82		case DS_TSC_CHANNEL_TS_Y:
 83			mLOG(DS_SPI, STUB, "Unimplemented TSC channel Y");
 84			ds->memory.spiBus.tscRegister = 0xFFF;
 85			break;
 86		case DS_TSC_CHANNEL_TEMP_0:
 87		case DS_TSC_CHANNEL_BATTERY_V:
 88		case DS_TSC_CHANNEL_TS_Z1:
 89		case DS_TSC_CHANNEL_TS_Z2:
 90		case DS_TSC_CHANNEL_MIC:
 91		case DS_TSC_CHANNEL_TEMP_1:
 92			ds->memory.spiBus.tscRegister = 0;
 93			mLOG(DS_SPI, STUB, "Unimplemented TSC channel %i", DSTSCControlByteGetChannel(ds->memory.spiBus.tscControlByte));
 94		}
 95		newValue = (ds->memory.spiBus.tscRegister >> 5) & 0x7F;
 96		ds->memory.spiBus.tscOffset = 7;
 97	}
 98
 99	if (DSTSCControlByteIsControl(oldValue)) {
100		ds->memory.spiBus.tscControlByte = oldValue;
101		ds->memory.spiBus.tscOffset = 0;
102	}
103
104	control = DSSPICNTClearBusy(control);
105	ds->memory.io7[DS7_REG_SPIDATA >> 1] = newValue;
106	ds->memory.io7[DS7_REG_SPICNT >> 1] = control;
107	if (DSSPICNTIsDoIRQ(control)) {
108		DSRaiseIRQ(ds->ds7.cpu, ds->ds7.memory.io, DS_IRQ_SPI);
109	}
110}
111
112static void _firmwareEvent(struct mTiming* timing, void* context, uint32_t cyclesLate) {
113	UNUSED(timing);
114	UNUSED(cyclesLate);
115	struct DS* ds = context;
116	uint8_t oldValue = ds->memory.io7[DS7_REG_SPIDATA >> 1];
117	DSSPICNT control = ds->memory.io7[DS7_REG_SPICNT >> 1];
118	uint8_t newValue = 0;
119
120	if (!ds->memory.spiBus.firmCommand) {
121		ds->memory.spiBus.firmCommand = oldValue;
122		ds->memory.spiBus.firmAddress = 0;
123		ds->memory.spiBus.firmAddressingRemaining = 24;
124	} else if (ds->memory.spiBus.firmAddressingRemaining) {
125		ds->memory.spiBus.firmAddress <<= 8;
126		ds->memory.spiBus.firmAddress |= oldValue;
127		ds->memory.spiBus.firmAddressingRemaining -= 8;
128		ds->firmwareVf->seek(ds->firmwareVf, ds->memory.spiBus.firmAddress, SEEK_SET);
129	} else {
130		switch (ds->memory.spiBus.firmCommand) {
131		case 0x02: // WR
132			ds->firmwareVf->write(ds->firmwareVf, &oldValue, 1);
133			++ds->memory.spiBus.firmAddress;
134			break;
135		case 0x03: // RD
136			ds->firmwareVf->read(ds->firmwareVf, &newValue, 1);
137			++ds->memory.spiBus.firmAddress;
138		case 0x04: // WRDI
139			ds->memory.spiBus.firmStatusReg &= ~2;
140			break;
141		case 0x05: // RDSR
142			newValue = ds->memory.spiBus.firmStatusReg;
143			break;
144		case 0x06: // WREN
145			ds->memory.spiBus.firmStatusReg |= 2;
146			break;
147		default:
148			mLOG(DS_SPI, STUB, "Unimplemented Firmware write: %04X:%02X:%02X", control, ds->memory.spiBus.firmCommand, newValue);
149			break;
150		}
151	}
152
153	control = DSSPICNTClearBusy(control);
154	ds->memory.io7[DS7_REG_SPIDATA >> 1] = newValue;
155	ds->memory.io7[DS7_REG_SPICNT >> 1] = control;
156	if (DSSPICNTIsDoIRQ(control)) {
157		DSRaiseIRQ(ds->ds7.cpu, ds->ds7.memory.io, DS_IRQ_SPI);
158	}
159}