mgba-ps3/src/gba/cart/gpio.c

563 lines
15 KiB
C

/* Copyright (c) 2013-2021 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <mgba/internal/gba/cart/gpio.h>
#include <mgba/internal/arm/macros.h>
#include <mgba/internal/gba/io.h>
#include <mgba/internal/gba/serialize.h>
#include <mgba-util/formatting.h>
#include <mgba-util/hash.h>
#include <mgba-util/memory.h>
mLOG_DEFINE_CATEGORY(GBA_HW, "GBA Pak Hardware", "gba.hardware");
MGBA_EXPORT const int GBA_LUX_LEVELS[10] = { 5, 11, 18, 27, 42, 62, 84, 109, 139, 183 };
static void _readPins(struct GBACartridgeHardware* hw);
static void _outputPins(struct GBACartridgeHardware* hw, unsigned pins);
static void _rtcReadPins(struct GBACartridgeHardware* hw);
static unsigned _rtcOutput(struct GBACartridgeHardware* hw);
static void _rtcProcessByte(struct GBACartridgeHardware* hw);
static void _rtcUpdateClock(struct GBACartridgeHardware* hw);
static unsigned _rtcBCD(unsigned value);
static void _gyroReadPins(struct GBACartridgeHardware* hw);
static void _rumbleReadPins(struct GBACartridgeHardware* hw);
static void _lightReadPins(struct GBACartridgeHardware* hw);
static const int RTC_BYTES[8] = {
0, // Force reset
0, // Empty
7, // Date/Time
0, // Force IRQ
1, // Control register
0, // Empty
3, // Time
0 // Empty
};
void GBAHardwareInit(struct GBACartridgeHardware* hw, uint16_t* base) {
hw->gpioBase = base;
GBAHardwareClear(hw);
}
void GBAHardwareReset(struct GBACartridgeHardware* hw) {
hw->readWrite = GPIO_WRITE_ONLY;
hw->pinState = 0;
hw->direction = 0;
hw->lightCounter = 0;
hw->lightEdge = false;
hw->lightSample = 0xFF;
hw->gyroSample = 0;
hw->gyroEdge = 0;
hw->tiltX = 0xFFF;
hw->tiltY = 0xFFF;
hw->tiltState = 0;
}
void GBAHardwareClear(struct GBACartridgeHardware* hw) {
hw->devices = HW_NONE | (hw->devices & HW_GB_PLAYER_DETECTION);
hw->readWrite = GPIO_WRITE_ONLY;
hw->pinState = 0;
hw->direction = 0;
}
void GBAHardwareGPIOWrite(struct GBACartridgeHardware* hw, uint32_t address, uint16_t value) {
if (!hw->gpioBase) {
return;
}
switch (address) {
case GPIO_REG_DATA:
if (!hw->p->vbaBugCompat) {
hw->pinState &= ~hw->direction;
hw->pinState |= value & hw->direction;
} else {
hw->pinState = value;
}
_readPins(hw);
break;
case GPIO_REG_DIRECTION:
hw->direction = value;
break;
case GPIO_REG_CONTROL:
hw->readWrite = value;
break;
default:
mLOG(GBA_HW, WARN, "Invalid GPIO address");
}
if (hw->readWrite) {
STORE_16(hw->pinState, 0, hw->gpioBase);
STORE_16(hw->direction, 2, hw->gpioBase);
STORE_16(hw->readWrite, 4, hw->gpioBase);
} else {
hw->gpioBase[0] = 0;
hw->gpioBase[1] = 0;
hw->gpioBase[2] = 0;
}
}
void GBAHardwareInitRTC(struct GBACartridgeHardware* hw) {
hw->devices |= HW_RTC;
hw->rtc.bytesRemaining = 0;
hw->rtc.transferStep = 0;
hw->rtc.bitsRead = 0;
hw->rtc.bits = 0;
hw->rtc.commandActive = 0;
hw->rtc.command = 0;
hw->rtc.control = 0x40;
memset(hw->rtc.time, 0, sizeof(hw->rtc.time));
hw->rtc.lastLatch = 0;
hw->rtc.offset = 0;
}
void _readPins(struct GBACartridgeHardware* hw) {
if (hw->devices & HW_RTC) {
_rtcReadPins(hw);
}
if (hw->devices & HW_GYRO) {
_gyroReadPins(hw);
}
if (hw->devices & HW_RUMBLE) {
_rumbleReadPins(hw);
}
if (hw->devices & HW_LIGHT_SENSOR) {
_lightReadPins(hw);
}
}
void _outputPins(struct GBACartridgeHardware* hw, unsigned pins) {
if (hw->readWrite) {
uint16_t old;
LOAD_16(old, 0, hw->gpioBase);
old &= hw->direction;
hw->pinState = old | (pins & ~hw->direction & 0xF);
STORE_16(hw->pinState, 0, hw->gpioBase);
}
}
// == RTC
void _rtcReadPins(struct GBACartridgeHardware* hw) {
// Transfer sequence:
// P: 0 | 1 | 2 | 3
// == Initiate
// > HI | - | LO | -
// > HI | - | HI | -
// == Transfer bit (x8)
// > LO | x | HI | -
// > HI | - | HI | -
// < ?? | x | ?? | -
// == Terminate
// > - | - | LO | -
switch (hw->rtc.transferStep) {
case 0:
if ((hw->pinState & 5) == 1) {
hw->rtc.transferStep = 1;
}
break;
case 1:
if ((hw->pinState & 5) == 5) {
hw->rtc.transferStep = 2;
} else if ((hw->pinState & 5) != 1) {
hw->rtc.transferStep = 0;
}
break;
case 2:
if (!(hw->pinState & 1)) {
hw->rtc.bits &= ~(1 << hw->rtc.bitsRead);
hw->rtc.bits |= ((hw->pinState & 2) >> 1) << hw->rtc.bitsRead;
} else {
if (hw->pinState & 4) {
if (!RTCCommandDataIsReading(hw->rtc.command)) {
++hw->rtc.bitsRead;
if (hw->rtc.bitsRead == 8) {
_rtcProcessByte(hw);
}
} else {
_outputPins(hw, 5 | (_rtcOutput(hw) << 1));
++hw->rtc.bitsRead;
if (hw->rtc.bitsRead == 8) {
--hw->rtc.bytesRemaining;
if (hw->rtc.bytesRemaining <= 0) {
hw->rtc.commandActive = 0;
hw->rtc.command = 0;
}
hw->rtc.bitsRead = 0;
}
}
} else {
hw->rtc.bitsRead = 0;
hw->rtc.bytesRemaining = 0;
hw->rtc.commandActive = 0;
hw->rtc.command = 0;
hw->rtc.transferStep = hw->pinState & 1;
_outputPins(hw, 1);
}
}
break;
}
}
void _rtcProcessByte(struct GBACartridgeHardware* hw) {
--hw->rtc.bytesRemaining;
if (!hw->rtc.commandActive) {
RTCCommandData command;
command = hw->rtc.bits;
if (RTCCommandDataGetMagic(command) == 0x06) {
hw->rtc.command = command;
hw->rtc.bytesRemaining = RTC_BYTES[RTCCommandDataGetCommand(command)];
hw->rtc.commandActive = hw->rtc.bytesRemaining > 0;
mLOG(GBA_HW, DEBUG, "Got RTC command %x", RTCCommandDataGetCommand(command));
switch (RTCCommandDataGetCommand(command)) {
case RTC_RESET:
hw->rtc.control = 0;
break;
case RTC_DATETIME:
case RTC_TIME:
_rtcUpdateClock(hw);
break;
case RTC_FORCE_IRQ:
case RTC_CONTROL:
break;
}
} else {
mLOG(GBA_HW, WARN, "Invalid RTC command byte: %02X", hw->rtc.bits);
}
} else {
switch (RTCCommandDataGetCommand(hw->rtc.command)) {
case RTC_CONTROL:
hw->rtc.control = hw->rtc.bits;
break;
case RTC_FORCE_IRQ:
mLOG(GBA_HW, STUB, "Unimplemented RTC command %u", RTCCommandDataGetCommand(hw->rtc.command));
break;
case RTC_RESET:
case RTC_DATETIME:
case RTC_TIME:
break;
}
}
hw->rtc.bits = 0;
hw->rtc.bitsRead = 0;
if (!hw->rtc.bytesRemaining) {
hw->rtc.commandActive = 0;
hw->rtc.command = 0;
}
}
unsigned _rtcOutput(struct GBACartridgeHardware* hw) {
uint8_t outputByte = 0;
if (!hw->rtc.commandActive) {
mLOG(GBA_HW, GAME_ERROR, "Attempting to use RTC without an active command");
return 0;
}
switch (RTCCommandDataGetCommand(hw->rtc.command)) {
case RTC_CONTROL:
outputByte = hw->rtc.control;
break;
case RTC_DATETIME:
case RTC_TIME:
outputByte = hw->rtc.time[7 - hw->rtc.bytesRemaining];
break;
case RTC_FORCE_IRQ:
case RTC_RESET:
break;
}
unsigned output = (outputByte >> hw->rtc.bitsRead) & 1;
if (hw->rtc.bitsRead == 0) {
mLOG(GBA_HW, DEBUG, "RTC output byte %02X", outputByte);
}
return output;
}
void _rtcUpdateClock(struct GBACartridgeHardware* hw) {
time_t t;
struct mRTCSource* rtc = hw->p->rtcSource;
if (rtc) {
if (rtc->sample) {
rtc->sample(rtc);
}
t = rtc->unixTime(rtc);
} else {
t = time(0);
}
hw->rtc.lastLatch = t;
t -= hw->rtc.offset;
struct tm date;
localtime_r(&t, &date);
hw->rtc.time[0] = _rtcBCD(date.tm_year - 100);
hw->rtc.time[1] = _rtcBCD(date.tm_mon + 1);
hw->rtc.time[2] = _rtcBCD(date.tm_mday);
hw->rtc.time[3] = _rtcBCD(date.tm_wday);
if (RTCControlIsHour24(hw->rtc.control)) {
hw->rtc.time[4] = _rtcBCD(date.tm_hour);
} else {
hw->rtc.time[4] = _rtcBCD(date.tm_hour % 12);
}
hw->rtc.time[5] = _rtcBCD(date.tm_min);
hw->rtc.time[6] = _rtcBCD(date.tm_sec);
}
unsigned _rtcBCD(unsigned value) {
int counter = value % 10;
value /= 10;
counter += (value % 10) << 4;
return counter;
}
// == Gyro
void GBAHardwareInitGyro(struct GBACartridgeHardware* hw) {
hw->devices |= HW_GYRO;
hw->gyroSample = 0;
hw->gyroEdge = 0;
}
void _gyroReadPins(struct GBACartridgeHardware* hw) {
struct mRotationSource* gyro = hw->p->rotationSource;
if (!gyro || !gyro->readGyroZ) {
return;
}
// Write bit on falling edge
bool doOutput = hw->gyroEdge && !(hw->pinState & 2);
if (hw->pinState & 1) {
if (gyro->sample) {
gyro->sample(gyro);
}
int32_t sample = gyro->readGyroZ(gyro);
// Normalize to ~12 bits, focused on 0x700
hw->gyroSample = (sample >> 21) + 0x700; // Crop off an extra bit so that we can't go negative
doOutput = true;
}
if (doOutput) {
unsigned bit = hw->gyroSample >> 15;
hw->gyroSample <<= 1;
_outputPins(hw, bit << 2);
}
hw->gyroEdge = !!(hw->pinState & 2);
}
// == Rumble
void GBAHardwareInitRumble(struct GBACartridgeHardware* hw) {
hw->devices |= HW_RUMBLE;
}
void _rumbleReadPins(struct GBACartridgeHardware* hw) {
struct mRumble* rumble = hw->p->rumble;
if (!rumble) {
return;
}
int32_t currentTime = mTimingCurrentTime(&hw->p->timing);
rumble->setRumble(rumble, !!(hw->pinState & 8), currentTime - hw->p->lastRumble);
hw->p->lastRumble = currentTime;
}
// == Light sensor
void GBAHardwareInitLight(struct GBACartridgeHardware* hw) {
hw->devices |= HW_LIGHT_SENSOR;
hw->lightCounter = 0;
hw->lightEdge = false;
hw->lightSample = 0xFF;
}
void _lightReadPins(struct GBACartridgeHardware* hw) {
if (hw->pinState & 4) {
// Boktai chip select
return;
}
if (hw->pinState & 2) {
struct GBALuminanceSource* lux = hw->p->luminanceSource;
mLOG(GBA_HW, DEBUG, "[SOLAR] Got reset");
hw->lightCounter = 0;
if (lux) {
if (lux->sample) {
lux->sample(lux);
}
hw->lightSample = lux->readLuminance(lux);
} else {
hw->lightSample = 0xFF;
}
}
if ((hw->pinState & 1) && hw->lightEdge) {
++hw->lightCounter;
}
hw->lightEdge = !(hw->pinState & 1);
bool sendBit = hw->lightCounter >= hw->lightSample;
_outputPins(hw, sendBit << 3);
mLOG(GBA_HW, DEBUG, "[SOLAR] Output %u with pins %u", hw->lightCounter, hw->pinState);
}
// == Tilt
void GBAHardwareInitTilt(struct GBACartridgeHardware* hw) {
hw->devices |= HW_TILT;
hw->tiltX = 0xFFF;
hw->tiltY = 0xFFF;
hw->tiltState = 0;
}
void GBAHardwareTiltWrite(struct GBACartridgeHardware* hw, uint32_t address, uint8_t value) {
switch (address) {
case 0x8000:
if (value == 0x55) {
hw->tiltState = 1;
} else {
mLOG(GBA_HW, GAME_ERROR, "Tilt sensor wrote wrong byte to %04x: %02x", address, value);
}
break;
case 0x8100:
if (value == 0xAA && hw->tiltState == 1) {
hw->tiltState = 0;
struct mRotationSource* rotationSource = hw->p->rotationSource;
if (!rotationSource || !rotationSource->readTiltX || !rotationSource->readTiltY) {
return;
}
if (rotationSource->sample) {
rotationSource->sample(rotationSource);
}
int32_t x = rotationSource->readTiltX(rotationSource);
int32_t y = rotationSource->readTiltY(rotationSource);
// Normalize to ~12 bits, focused on 0x3A0
hw->tiltX = 0x3A0 - (x >> 22);
hw->tiltY = 0x3A0 - (y >> 22);
} else {
mLOG(GBA_HW, GAME_ERROR, "Tilt sensor wrote wrong byte to %04x: %02x", address, value);
}
break;
default:
mLOG(GBA_HW, GAME_ERROR, "Invalid tilt sensor write to %04x: %02x", address, value);
break;
}
}
uint8_t GBAHardwareTiltRead(struct GBACartridgeHardware* hw, uint32_t address) {
switch (address) {
case 0x8200:
return hw->tiltX & 0xFF;
case 0x8300:
return ((hw->tiltX >> 8) & 0xF) | 0x80;
case 0x8400:
return hw->tiltY & 0xFF;
case 0x8500:
return (hw->tiltY >> 8) & 0xF;
default:
mLOG(GBA_HW, GAME_ERROR, "Invalid tilt sensor read from %04x", address);
break;
}
return 0xFF;
}
// == Serialization
void GBAHardwareSerialize(const struct GBACartridgeHardware* hw, struct GBASerializedState* state) {
GBASerializedHWFlags1 flags1 = 0;
flags1 = GBASerializedHWFlags1SetReadWrite(flags1, hw->readWrite);
STORE_16(hw->pinState, 0, &state->hw.pinState);
STORE_16(hw->direction, 0, &state->hw.pinDirection);
state->hw.devices = hw->devices;
STORE_32(hw->rtc.bytesRemaining, 0, &state->hw.rtcBytesRemaining);
STORE_32(hw->rtc.transferStep, 0, &state->hw.rtcTransferStep);
STORE_32(hw->rtc.bitsRead, 0, &state->hw.rtcBitsRead);
STORE_32(hw->rtc.bits, 0, &state->hw.rtcBits);
STORE_32(hw->rtc.commandActive, 0, &state->hw.rtcCommandActive);
STORE_32(hw->rtc.command, 0, &state->hw.rtcCommand);
STORE_32(hw->rtc.control, 0, &state->hw.rtcControl);
memcpy(state->hw.time, hw->rtc.time, sizeof(state->hw.time));
STORE_16(hw->gyroSample, 0, &state->hw.gyroSample);
flags1 = GBASerializedHWFlags1SetGyroEdge(flags1, hw->gyroEdge);
STORE_16(hw->tiltX, 0, &state->hw.tiltSampleX);
STORE_16(hw->tiltY, 0, &state->hw.tiltSampleY);
state->hw.lightSample = hw->lightSample;
flags1 = GBASerializedHWFlags1SetLightEdge(flags1, hw->lightEdge);
flags1 = GBASerializedHWFlags1SetLightCounter(flags1, hw->lightCounter);
STORE_16(flags1, 0, &state->hw.flags1);
GBASerializedHWFlags2 flags2 = 0;
flags2 = GBASerializedHWFlags2SetTiltState(flags2, hw->tiltState);
// GBP/SIO stuff is only here for legacy reasons
flags2 = GBASerializedHWFlags2SetGbpInputsPosted(flags2, hw->p->sio.gbp.inputsPosted);
flags2 = GBASerializedHWFlags2SetGbpTxPosition(flags2, hw->p->sio.gbp.txPosition);
STORE_32(hw->p->sio.completeEvent.when - mTimingCurrentTime(&hw->p->timing), 0, &state->hw.sioNextEvent);
state->hw.flags2 = flags2;
}
void GBAHardwareDeserialize(struct GBACartridgeHardware* hw, const struct GBASerializedState* state) {
GBASerializedHWFlags1 flags1;
LOAD_16(flags1, 0, &state->hw.flags1);
hw->readWrite = GBASerializedHWFlags1GetReadWrite(flags1);
LOAD_16(hw->pinState, 0, &state->hw.pinState);
LOAD_16(hw->direction, 0, &state->hw.pinDirection);
hw->devices = state->hw.devices;
if ((hw->devices & HW_GPIO) && hw->gpioBase) {
// TODO: This needs to update the pristine state somehow
if (hw->readWrite) {
STORE_16(hw->pinState, 0, hw->gpioBase);
STORE_16(hw->direction, 2, hw->gpioBase);
STORE_16(hw->readWrite, 4, hw->gpioBase);
} else {
hw->gpioBase[0] = 0;
hw->gpioBase[1] = 0;
hw->gpioBase[2] = 0;
}
}
LOAD_32(hw->rtc.bytesRemaining, 0, &state->hw.rtcBytesRemaining);
LOAD_32(hw->rtc.transferStep, 0, &state->hw.rtcTransferStep);
LOAD_32(hw->rtc.bitsRead, 0, &state->hw.rtcBitsRead);
LOAD_32(hw->rtc.bits, 0, &state->hw.rtcBits);
LOAD_32(hw->rtc.commandActive, 0, &state->hw.rtcCommandActive);
LOAD_32(hw->rtc.command, 0, &state->hw.rtcCommand);
LOAD_32(hw->rtc.control, 0, &state->hw.rtcControl);
memcpy(hw->rtc.time, state->hw.time, sizeof(hw->rtc.time));
LOAD_16(hw->gyroSample, 0, &state->hw.gyroSample);
hw->gyroEdge = GBASerializedHWFlags1GetGyroEdge(flags1);
LOAD_16(hw->tiltX, 0, &state->hw.tiltSampleX);
LOAD_16(hw->tiltY, 0, &state->hw.tiltSampleY);
hw->tiltState = GBASerializedHWFlags2GetTiltState(state->hw.flags2);
hw->lightCounter = GBASerializedHWFlags1GetLightCounter(flags1);
hw->lightSample = state->hw.lightSample;
hw->lightEdge = GBASerializedHWFlags1GetLightEdge(flags1);
// GBP/SIO stuff is only here for legacy reasons
hw->p->sio.gbp.inputsPosted = GBASerializedHWFlags2GetGbpInputsPosted(state->hw.flags2);
hw->p->sio.gbp.txPosition = GBASerializedHWFlags2GetGbpTxPosition(state->hw.flags2);
uint32_t when;
LOAD_32(when, 0, &state->hw.sioNextEvent);
if (hw->devices & HW_GB_PLAYER) {
GBASIOSetDriver(&hw->p->sio, &hw->p->sio.gbp.d);
}
if ((hw->p->memory.io[GBA_REG(SIOCNT)] & 0x0080) && when < 0x20000) {
mTimingSchedule(&hw->p->timing, &hw->p->sio.completeEvent, when);
}
}