all repos — mgba @ a557fb6ca67b023be01e306a69a2f3e1c79ab79a

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
 10mLOG_DEFINE_CATEGORY(DS_SPI, "DS SPI");
 11
 12static void _tscEvent(struct mTiming*, void* context, uint32_t cyclesLate);
 13
 14void DSSPIReset(struct DS* ds) {
 15	memset(&ds->memory.spiBus, 0, sizeof(ds->memory.spiBus));
 16	ds->memory.spiBus.tscEvent.name = "DS SPI TSC";
 17	ds->memory.spiBus.tscEvent.context = ds;
 18	ds->memory.spiBus.tscEvent.callback = _tscEvent;
 19	ds->memory.spiBus.tscEvent.priority = 0x60;
 20}
 21
 22DSSPICNT DSSPIWriteControl(struct DS* ds, uint16_t control) {
 23	// TODO
 24	if (!ds->memory.spiBus.holdEnabled) {
 25		ds->memory.spiBus.tscControlByte = 0;
 26	}
 27	ds->memory.spiBus.holdEnabled = DSSPICNTIsCSHold(control);
 28	return control;
 29}
 30
 31void DSSPIWrite(struct DS* ds, uint8_t datum) {
 32	DSSPICNT control = ds->memory.io7[DS7_REG_SPICNT >> 1];
 33	if (!DSSPICNTIsEnable(control)) {
 34		return;
 35	}
 36	uint32_t baud = 19 - DSSPICNTGetBaud(control);
 37	baud = DS_ARM7TDMI_FREQUENCY >> baud;
 38	switch (DSSPICNTGetChipSelect(control)) {
 39	case DS_SPI_DEV_TSC:
 40		control = DSSPICNTFillBusy(control);
 41		mTimingDeschedule(&ds->ds7.timing, &ds->memory.spiBus.tscEvent);
 42		mTimingSchedule(&ds->ds7.timing, &ds->memory.spiBus.tscEvent, baud);
 43		break;
 44	case DS_SPI_DEV_POWERMAN:
 45	case DS_SPI_DEV_FIRMWARE:
 46	default:
 47		mLOG(DS_SPI, STUB, "Unimplemented data write: %04X:%02X", control, datum);
 48		break;
 49	}
 50	ds->memory.io7[DS7_REG_SPICNT >> 1] = control;
 51}
 52
 53static void _tscEvent(struct mTiming* timing, void* context, uint32_t cyclesLate) {
 54	UNUSED(timing);
 55	UNUSED(cyclesLate);
 56	struct DS* ds = context;
 57	uint8_t oldValue = ds->memory.io7[DS7_REG_SPIDATA >> 1];
 58	DSSPICNT control = ds->memory.io7[DS7_REG_SPICNT >> 1];
 59	uint8_t newValue = 0;
 60
 61	// TODO: /PENIRQ
 62	if (ds->memory.spiBus.tscOffset > 0) {
 63		// TODO: Make generic?
 64		if (ds->memory.spiBus.tscOffset < 12) {
 65			newValue = (ds->memory.spiBus.tscRegister & 0x1F) << 3;
 66			ds->memory.spiBus.tscOffset = 12;
 67		} else {
 68			newValue = 0;
 69		}
 70	} else if (ds->memory.spiBus.tscControlByte) {
 71		switch (DSTSCControlByteGetChannel(ds->memory.spiBus.tscControlByte)) {
 72		case DS_TSC_CHANNEL_TS_X:
 73			mLOG(DS_SPI, STUB, "Unimplemented TSC channel X");
 74			ds->memory.spiBus.tscRegister = 0;
 75			break;
 76		case DS_TSC_CHANNEL_TS_Y:
 77			mLOG(DS_SPI, STUB, "Unimplemented TSC channel Y");
 78			ds->memory.spiBus.tscRegister = 0xFFF;
 79			break;
 80		case DS_TSC_CHANNEL_TEMP_0:
 81		case DS_TSC_CHANNEL_BATTERY_V:
 82		case DS_TSC_CHANNEL_TS_Z1:
 83		case DS_TSC_CHANNEL_TS_Z2:
 84		case DS_TSC_CHANNEL_MIC:
 85		case DS_TSC_CHANNEL_TEMP_1:
 86			ds->memory.spiBus.tscRegister = 0;
 87			mLOG(DS_SPI, STUB, "Unimplemented TSC channel %i", DSTSCControlByteGetChannel(ds->memory.spiBus.tscControlByte));
 88		}
 89		newValue = (ds->memory.spiBus.tscRegister >> 5) & 0x7F;
 90		ds->memory.spiBus.tscOffset = 7;
 91	}
 92
 93	if (DSTSCControlByteIsControl(oldValue)) {
 94		ds->memory.spiBus.tscControlByte = oldValue;
 95		ds->memory.spiBus.tscOffset = 0;
 96	}
 97
 98	control = DSSPICNTClearBusy(control);
 99	ds->memory.io7[DS7_REG_SPIDATA >> 1] = newValue;
100	ds->memory.io7[DS7_REG_SPICNT >> 1] = control;
101	if (DSSPICNTIsDoIRQ(control)) {
102		DSRaiseIRQ(ds->ds7.cpu, ds->ds7.memory.io, DS_IRQ_SPI);
103	}
104}