333 lines
8.0 KiB
C
333 lines
8.0 KiB
C
/* Copyright (c) 2013-2014 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 "gba.h"
|
|
|
|
#include "gba-gpio.h"
|
|
#include "gba-sensors.h"
|
|
#include "gba-serialize.h"
|
|
|
|
#include <time.h>
|
|
|
|
static void _readPins(struct GBACartridgeGPIO* gpio);
|
|
static void _outputPins(struct GBACartridgeGPIO* gpio, unsigned pins);
|
|
|
|
static void _rtcReadPins(struct GBACartridgeGPIO* gpio);
|
|
static unsigned _rtcOutput(struct GBACartridgeGPIO* gpio);
|
|
static void _rtcProcessByte(struct GBACartridgeGPIO* gpio);
|
|
static void _rtcUpdateClock(struct GBACartridgeGPIO* gpio);
|
|
static unsigned _rtcBCD(unsigned value);
|
|
|
|
static void _gyroReadPins(struct GBACartridgeGPIO* gpio);
|
|
|
|
static void _rumbleReadPins(struct GBACartridgeGPIO* gpio);
|
|
|
|
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 GBAGPIOInit(struct GBACartridgeGPIO* gpio, uint16_t* base) {
|
|
gpio->gpioDevices = GPIO_NONE;
|
|
gpio->direction = GPIO_WRITE_ONLY;
|
|
gpio->gpioBase = base;
|
|
gpio->pinState = 0;
|
|
gpio->direction = 0;
|
|
}
|
|
|
|
void GBAGPIOWrite(struct GBACartridgeGPIO* gpio, uint32_t address, uint16_t value) {
|
|
switch (address) {
|
|
case GPIO_REG_DATA:
|
|
gpio->pinState = value;
|
|
_readPins(gpio);
|
|
break;
|
|
case GPIO_REG_DIRECTION:
|
|
gpio->direction = value;
|
|
break;
|
|
case GPIO_REG_CONTROL:
|
|
gpio->readWrite = value;
|
|
break;
|
|
default:
|
|
GBALog(gpio->p, GBA_LOG_WARN, "Invalid GPIO address");
|
|
}
|
|
|
|
if (gpio->readWrite) {
|
|
uint16_t old = gpio->gpioBase[0];
|
|
old &= ~gpio->direction;
|
|
gpio->gpioBase[0] = old | (value & gpio->direction);
|
|
}
|
|
}
|
|
|
|
void GBAGPIOInitRTC(struct GBACartridgeGPIO* gpio) {
|
|
gpio->gpioDevices |= GPIO_RTC;
|
|
gpio->rtc.bytesRemaining = 0;
|
|
|
|
gpio->rtc.transferStep = 0;
|
|
|
|
gpio->rtc.bitsRead = 0;
|
|
gpio->rtc.bits = 0;
|
|
gpio->rtc.commandActive = 0;
|
|
gpio->rtc.command.packed = 0;
|
|
gpio->rtc.control.packed = 0x40;
|
|
memset(gpio->rtc.time, 0, sizeof(gpio->rtc.time));
|
|
}
|
|
|
|
void _readPins(struct GBACartridgeGPIO* gpio) {
|
|
if (gpio->gpioDevices & GPIO_RTC) {
|
|
_rtcReadPins(gpio);
|
|
}
|
|
|
|
if (gpio->gpioDevices & GPIO_GYRO) {
|
|
_gyroReadPins(gpio);
|
|
}
|
|
|
|
if (gpio->gpioDevices & GPIO_RUMBLE) {
|
|
_rumbleReadPins(gpio);
|
|
}
|
|
}
|
|
|
|
void _outputPins(struct GBACartridgeGPIO* gpio, unsigned pins) {
|
|
if (gpio->readWrite) {
|
|
uint16_t old = gpio->gpioBase[0];
|
|
old &= gpio->direction;
|
|
gpio->gpioBase[0] = old | (pins & ~gpio->direction & 0xF);
|
|
}
|
|
}
|
|
|
|
// == RTC
|
|
|
|
void _rtcReadPins(struct GBACartridgeGPIO* gpio) {
|
|
// Transfer sequence:
|
|
// P: 0 | 1 | 2 | 3
|
|
// == Initiate
|
|
// > HI | - | LO | -
|
|
// > HI | - | HI | -
|
|
// == Transfer bit (x8)
|
|
// > LO | x | HI | -
|
|
// > HI | - | HI | -
|
|
// < ?? | x | ?? | -
|
|
// == Terminate
|
|
// > - | - | LO | -
|
|
switch (gpio->rtc.transferStep) {
|
|
case 0:
|
|
if ((gpio->pinState & 5) == 1) {
|
|
gpio->rtc.transferStep = 1;
|
|
}
|
|
break;
|
|
case 1:
|
|
if ((gpio->pinState & 5) == 5) {
|
|
gpio->rtc.transferStep = 2;
|
|
}
|
|
break;
|
|
case 2:
|
|
if (!gpio->p0) {
|
|
gpio->rtc.bits &= ~(1 << gpio->rtc.bitsRead);
|
|
gpio->rtc.bits |= gpio->p1 << gpio->rtc.bitsRead;
|
|
} else {
|
|
if (gpio->p2) {
|
|
// GPIO direction should always != reading
|
|
if (gpio->dir1) {
|
|
if (gpio->rtc.command.reading) {
|
|
GBALog(gpio->p, GBA_LOG_GAME_ERROR, "Attempting to write to RTC while in read mode");
|
|
}
|
|
++gpio->rtc.bitsRead;
|
|
if (gpio->rtc.bitsRead == 8) {
|
|
_rtcProcessByte(gpio);
|
|
}
|
|
} else {
|
|
_outputPins(gpio, 5 | (_rtcOutput(gpio) << 1));
|
|
++gpio->rtc.bitsRead;
|
|
if (gpio->rtc.bitsRead == 8) {
|
|
--gpio->rtc.bytesRemaining;
|
|
if (gpio->rtc.bytesRemaining <= 0) {
|
|
gpio->rtc.commandActive = 0;
|
|
gpio->rtc.command.reading = 0;
|
|
}
|
|
gpio->rtc.bitsRead = 0;
|
|
}
|
|
}
|
|
} else {
|
|
gpio->rtc.bitsRead = 0;
|
|
gpio->rtc.bytesRemaining = 0;
|
|
gpio->rtc.commandActive = 0;
|
|
gpio->rtc.command.reading = 0;
|
|
gpio->rtc.transferStep = 0;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void _rtcProcessByte(struct GBACartridgeGPIO* gpio) {
|
|
--gpio->rtc.bytesRemaining;
|
|
if (!gpio->rtc.commandActive) {
|
|
union RTCCommandData command;
|
|
command.packed = gpio->rtc.bits;
|
|
if (command.magic == 0x06) {
|
|
gpio->rtc.command = command;
|
|
|
|
gpio->rtc.bytesRemaining = RTC_BYTES[gpio->rtc.command.command];
|
|
gpio->rtc.commandActive = gpio->rtc.bytesRemaining > 0;
|
|
switch (command.command) {
|
|
case RTC_RESET:
|
|
gpio->rtc.control.packed = 0;
|
|
break;
|
|
case RTC_DATETIME:
|
|
case RTC_TIME:
|
|
_rtcUpdateClock(gpio);
|
|
break;
|
|
case RTC_FORCE_IRQ:
|
|
case RTC_CONTROL:
|
|
break;
|
|
}
|
|
} else {
|
|
GBALog(gpio->p, GBA_LOG_WARN, "Invalid RTC command byte: %02X", gpio->rtc.bits);
|
|
}
|
|
} else {
|
|
switch (gpio->rtc.command.command) {
|
|
case RTC_CONTROL:
|
|
gpio->rtc.control.packed = gpio->rtc.bits;
|
|
break;
|
|
case RTC_FORCE_IRQ:
|
|
GBALog(gpio->p, GBA_LOG_STUB, "Unimplemented RTC command %u", gpio->rtc.command.command);
|
|
break;
|
|
case RTC_RESET:
|
|
case RTC_DATETIME:
|
|
case RTC_TIME:
|
|
break;
|
|
}
|
|
}
|
|
|
|
gpio->rtc.bits = 0;
|
|
gpio->rtc.bitsRead = 0;
|
|
if (!gpio->rtc.bytesRemaining) {
|
|
gpio->rtc.commandActive = 0;
|
|
gpio->rtc.command.reading = 0;
|
|
}
|
|
}
|
|
|
|
unsigned _rtcOutput(struct GBACartridgeGPIO* gpio) {
|
|
uint8_t outputByte = 0;
|
|
switch (gpio->rtc.command.command) {
|
|
case RTC_CONTROL:
|
|
outputByte = gpio->rtc.control.packed;
|
|
break;
|
|
case RTC_DATETIME:
|
|
case RTC_TIME:
|
|
outputByte = gpio->rtc.time[7 - gpio->rtc.bytesRemaining];
|
|
break;
|
|
case RTC_FORCE_IRQ:
|
|
case RTC_RESET:
|
|
break;
|
|
}
|
|
unsigned output = (outputByte >> gpio->rtc.bitsRead) & 1;
|
|
return output;
|
|
}
|
|
|
|
void _rtcUpdateClock(struct GBACartridgeGPIO* gpio) {
|
|
time_t t = time(0);
|
|
struct tm date;
|
|
#ifdef _WIN32
|
|
date = *localtime(&t);
|
|
#else
|
|
localtime_r(&t, &date);
|
|
#endif
|
|
gpio->rtc.time[0] = _rtcBCD(date.tm_year - 100);
|
|
gpio->rtc.time[1] = _rtcBCD(date.tm_mon + 1);
|
|
gpio->rtc.time[2] = _rtcBCD(date.tm_mday);
|
|
gpio->rtc.time[3] = _rtcBCD(date.tm_wday);
|
|
if (gpio->rtc.control.hour24) {
|
|
gpio->rtc.time[4] = _rtcBCD(date.tm_hour);
|
|
} else {
|
|
gpio->rtc.time[4] = _rtcBCD(date.tm_hour % 12);
|
|
}
|
|
gpio->rtc.time[5] = _rtcBCD(date.tm_min);
|
|
gpio->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 GBAGPIOInitGyro(struct GBACartridgeGPIO* gpio) {
|
|
gpio->gpioDevices |= GPIO_GYRO;
|
|
gpio->gyroSample = 0;
|
|
gpio->gyroEdge = 0;
|
|
}
|
|
|
|
void _gyroReadPins(struct GBACartridgeGPIO* gpio) {
|
|
struct GBARotationSource* gyro = gpio->p->rotationSource;
|
|
if (!gyro) {
|
|
return;
|
|
}
|
|
|
|
if (gpio->p0) {
|
|
if (gyro->sample) {
|
|
gyro->sample(gyro);
|
|
}
|
|
int32_t sample = gyro->readGyroZ(gyro);
|
|
|
|
// Normalize to ~12 bits, focused on 0x6C0
|
|
gpio->gyroSample = (sample >> 21) + 0x6C0; // Crop off an extra bit so that we can't go negative
|
|
}
|
|
|
|
if (gpio->gyroEdge && !gpio->p1) {
|
|
// Write bit on falling edge
|
|
unsigned bit = gpio->gyroSample >> 15;
|
|
gpio->gyroSample <<= 1;
|
|
_outputPins(gpio, bit << 2);
|
|
}
|
|
|
|
gpio->gyroEdge = gpio->p1;
|
|
}
|
|
|
|
// == Rumble
|
|
|
|
void GBAGPIOInitRumble(struct GBACartridgeGPIO* gpio) {
|
|
gpio->gpioDevices |= GPIO_RUMBLE;
|
|
}
|
|
|
|
void _rumbleReadPins(struct GBACartridgeGPIO* gpio) {
|
|
struct GBARumble* rumble = gpio->p->rumble;
|
|
if (!rumble) {
|
|
return;
|
|
}
|
|
|
|
rumble->setRumble(rumble, gpio->p3);
|
|
}
|
|
|
|
// == Serialization
|
|
|
|
void GBAGPIOSerialize(struct GBACartridgeGPIO* gpio, struct GBASerializedState* state) {
|
|
state->gpio.readWrite = gpio->readWrite;
|
|
state->gpio.pinState = gpio->pinState;
|
|
state->gpio.pinDirection = gpio->direction;
|
|
state->gpio.devices = gpio->gpioDevices;
|
|
state->gpio.rtc = gpio->rtc;
|
|
state->gpio.gyroSample = gpio->gyroSample;
|
|
state->gpio.gyroEdge = gpio->gyroEdge;
|
|
}
|
|
|
|
void GBAGPIODeserialize(struct GBACartridgeGPIO* gpio, struct GBASerializedState* state) {
|
|
gpio->readWrite = state->gpio.readWrite;
|
|
gpio->pinState = state->gpio.pinState;
|
|
gpio->direction = state->gpio.pinDirection;
|
|
// TODO: Deterministic RTC
|
|
gpio->rtc = state->gpio.rtc;
|
|
gpio->gyroSample = state->gpio.gyroSample;
|
|
gpio->gyroEdge = state->gpio.gyroEdge;
|
|
}
|