all repos — mgba @ fc74ad9ceb102a046db417dc7382b8954a7cc85e

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(cyclesLate);
 55	struct DS* ds = context;
 56	uint8_t oldValue = ds->memory.io7[DS7_REG_SPIDATA >> 1];
 57	DSSPICNT control = ds->memory.io7[DS7_REG_SPICNT >> 1];
 58	uint8_t newValue = 0;
 59
 60	// TODO: /PENIRQ
 61	if (ds->memory.spiBus.tscOffset > 0) {
 62		// TODO: Make generic?
 63		if (ds->memory.spiBus.tscOffset < 12) {
 64			newValue = (ds->memory.spiBus.tscRegister & 0x1F) << 3;
 65			ds->memory.spiBus.tscOffset = 12;
 66		} else {
 67			newValue = 0;
 68		}
 69	} else if (ds->memory.spiBus.tscControlByte) {
 70		switch (DSTSCControlByteGetChannel(ds->memory.spiBus.tscControlByte)) {
 71		case DS_TSC_CHANNEL_TS_X:
 72			mLOG(DS_SPI, STUB, "Unimplemented TSC channel X");
 73			ds->memory.spiBus.tscRegister = 0;
 74			break;
 75		case DS_TSC_CHANNEL_TS_Y:
 76			mLOG(DS_SPI, STUB, "Unimplemented TSC channel Y");
 77			ds->memory.spiBus.tscRegister = 0xFFF;
 78			break;
 79		case DS_TSC_CHANNEL_TEMP_0:
 80		case DS_TSC_CHANNEL_BATTERY_V:
 81		case DS_TSC_CHANNEL_TS_Z1:
 82		case DS_TSC_CHANNEL_TS_Z2:
 83		case DS_TSC_CHANNEL_MIC:
 84		case DS_TSC_CHANNEL_TEMP_1:
 85			ds->memory.spiBus.tscRegister = 0;
 86			mLOG(DS_SPI, STUB, "Unimplemented TSC channel %i", DSTSCControlByteGetChannel(ds->memory.spiBus.tscControlByte));
 87		}
 88		newValue = (ds->memory.spiBus.tscRegister >> 5) & 0x7F;
 89		ds->memory.spiBus.tscOffset = 7;
 90	}
 91
 92	if (DSTSCControlByteIsControl(oldValue)) {
 93		ds->memory.spiBus.tscControlByte = oldValue;
 94		ds->memory.spiBus.tscOffset = 0;
 95	}
 96
 97	control = DSSPICNTClearBusy(control);
 98	ds->memory.io7[DS7_REG_SPIDATA >> 1] = newValue;
 99	ds->memory.io7[DS7_REG_SPICNT >> 1] = control;
100	if (DSSPICNTIsDoIRQ(control)) {
101		DSRaiseIRQ(ds->ds7.cpu, ds->ds7.memory.io, DS_IRQ_SPI);
102	}
103}