all repos — mgba @ 498aa541fc660c8755cda99cf8889171b3bd5381

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", "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	if (ds->memory.spiBus.tscOffset > 0) {
 68		// TODO: Make generic?
 69		if (ds->memory.spiBus.tscOffset < 12) {
 70			newValue = (ds->memory.spiBus.tscRegister & 0x1F) << 3;
 71			ds->memory.spiBus.tscOffset = 12;
 72		} else {
 73			newValue = 0;
 74		}
 75	} else if (ds->memory.spiBus.tscControlByte) {
 76		switch (DSTSCControlByteGetChannel(ds->memory.spiBus.tscControlByte)) {
 77		// TODO: Calibrate from firmware
 78		case DS_TSC_CHANNEL_TS_X:
 79			if (*ds->touchSource) {
 80				ds->memory.spiBus.tscRegister = (*ds->cursorSourceX * 0xDD0 / DS_VIDEO_HORIZONTAL_PIXELS) + 0x100;
 81			} else {
 82				ds->memory.spiBus.tscRegister = 0;
 83			}
 84			break;
 85		case DS_TSC_CHANNEL_TS_Y:
 86			if (*ds->touchSource) {
 87				ds->memory.spiBus.tscRegister = (*ds->cursorSourceY * 0xE70 / DS_VIDEO_VERTICAL_PIXELS) + 0x0B0;
 88			} else {
 89				ds->memory.spiBus.tscRegister = 0xFFF;
 90			}
 91			break;
 92		case DS_TSC_CHANNEL_TEMP_0:
 93			if (*ds->touchSource) {
 94				ds->memory.io7[DS7_REG_EXTKEYIN >> 1] &= ~0x040;
 95			} else {
 96				ds->memory.io7[DS7_REG_EXTKEYIN >> 1] |= 0x040;
 97			}
 98			break;
 99		case DS_TSC_CHANNEL_BATTERY_V:
100		case DS_TSC_CHANNEL_TS_Z1:
101		case DS_TSC_CHANNEL_TS_Z2:
102		case DS_TSC_CHANNEL_MIC:
103		case DS_TSC_CHANNEL_TEMP_1:
104			ds->memory.spiBus.tscRegister = 0;
105			mLOG(DS_SPI, STUB, "Unimplemented TSC channel %i", DSTSCControlByteGetChannel(ds->memory.spiBus.tscControlByte));
106		}
107		newValue = (ds->memory.spiBus.tscRegister >> 5) & 0x7F;
108		ds->memory.spiBus.tscOffset = 7;
109	}
110
111	if (DSTSCControlByteIsControl(oldValue)) {
112		ds->memory.spiBus.tscControlByte = oldValue;
113		ds->memory.spiBus.tscOffset = 0;
114	}
115
116	control = DSSPICNTClearBusy(control);
117	ds->memory.io7[DS7_REG_SPIDATA >> 1] = newValue;
118	ds->memory.io7[DS7_REG_SPICNT >> 1] = control;
119	if (DSSPICNTIsDoIRQ(control)) {
120		DSRaiseIRQ(ds->ds7.cpu, ds->ds7.memory.io, DS_IRQ_SPI);
121	}
122}
123
124static void _firmwareEvent(struct mTiming* timing, void* context, uint32_t cyclesLate) {
125	UNUSED(timing);
126	UNUSED(cyclesLate);
127	struct DS* ds = context;
128	uint8_t oldValue = ds->memory.io7[DS7_REG_SPIDATA >> 1];
129	DSSPICNT control = ds->memory.io7[DS7_REG_SPICNT >> 1];
130	uint8_t newValue = 0;
131
132	if (!ds->memory.spiBus.firmCommand) {
133		ds->memory.spiBus.firmCommand = oldValue;
134		ds->memory.spiBus.firmAddress = 0;
135		ds->memory.spiBus.firmAddressingRemaining = 24;
136	} else if (ds->memory.spiBus.firmAddressingRemaining) {
137		ds->memory.spiBus.firmAddress <<= 8;
138		ds->memory.spiBus.firmAddress |= oldValue;
139		ds->memory.spiBus.firmAddressingRemaining -= 8;
140		ds->firmwareVf->seek(ds->firmwareVf, ds->memory.spiBus.firmAddress, SEEK_SET);
141	} else {
142		switch (ds->memory.spiBus.firmCommand) {
143		case 0x02: // WR
144			ds->firmwareVf->write(ds->firmwareVf, &oldValue, 1);
145			++ds->memory.spiBus.firmAddress;
146			break;
147		case 0x03: // RD
148			ds->firmwareVf->read(ds->firmwareVf, &newValue, 1);
149			++ds->memory.spiBus.firmAddress;
150		case 0x04: // WRDI
151			ds->memory.spiBus.firmStatusReg &= ~2;
152			break;
153		case 0x05: // RDSR
154			newValue = ds->memory.spiBus.firmStatusReg;
155			break;
156		case 0x06: // WREN
157			ds->memory.spiBus.firmStatusReg |= 2;
158			break;
159		default:
160			mLOG(DS_SPI, STUB, "Unimplemented Firmware write: %04X:%02X:%02X", control, ds->memory.spiBus.firmCommand, newValue);
161			break;
162		}
163	}
164
165	control = DSSPICNTClearBusy(control);
166	ds->memory.io7[DS7_REG_SPIDATA >> 1] = newValue;
167	ds->memory.io7[DS7_REG_SPICNT >> 1] = control;
168	if (DSSPICNTIsDoIRQ(control)) {
169		DSRaiseIRQ(ds->ds7.cpu, ds->ds7.memory.io, DS_IRQ_SPI);
170	}
171}