Implement RTC

This commit is contained in:
Jeffrey Pfau 2013-10-20 21:39:47 -07:00
parent 7e5eada69b
commit fcaa0eb066
3 changed files with 236 additions and 9 deletions

View File

@ -2,16 +2,34 @@
#include "gba-gpio.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 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->pinDirection = 0;
gpio->direction = 0;
}
void GBAGPIOWrite(struct GBACartridgeGPIO* gpio, uint32_t address, uint16_t value) {
@ -21,18 +39,34 @@ void GBAGPIOWrite(struct GBACartridgeGPIO* gpio, uint32_t address, uint16_t valu
_readPins(gpio);
break;
case GPIO_REG_DIRECTION:
gpio->pinDirection = value;
gpio->direction = value;
break;
case GPIO_REG_CONTROL:
gpio->direction = value;
gpio->readWrite = value;
break;
default:
GBALog(0, 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) {
// TODO
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) {
@ -41,6 +75,162 @@ void _readPins(struct GBACartridgeGPIO* 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);
}
};
void _rtcReadPins(struct GBACartridgeGPIO* gpio) {
// TODO
// 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(0, 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(0, 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(0, 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;
localtime_r(&t, &date);
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;
}

View File

@ -24,13 +24,48 @@ enum GPIODirection {
GPIO_READ_WRITE = 1
};
union RTCControl {
struct {
unsigned : 3;
unsigned minIRQ : 1;
unsigned : 2;
unsigned hour24 : 1;
unsigned poweroff : 1;
};
uint8_t packed;
};
enum RTCCommand {
RTC_RESET = 0,
RTC_DATETIME = 2,
RTC_FORCE_IRQ = 3,
RTC_CONTROL = 4,
RTC_TIME = 6
};
union RTCCommandData {
struct {
unsigned magic : 4;
enum RTCCommand command : 3;
unsigned reading : 1;
};
uint8_t packed;
};
struct GBARTC {
// TODO
int bytesRemaining;
int transferStep;
int bitsRead;
int bits;
int commandActive;
union RTCCommandData command;
union RTCControl control;
uint8_t time[7];
};
struct GBACartridgeGPIO {
int gpioDevices;
enum GPIODirection direction;
enum GPIODirection readWrite;
uint16_t* gpioBase;
union {
@ -50,7 +85,7 @@ struct GBACartridgeGPIO {
unsigned dir2 : 1;
unsigned dir3 : 1;
};
uint16_t pinDirection;
uint16_t direction;
};
struct GBARTC rtc;

View File

@ -29,6 +29,8 @@ struct GBACartridgeOverride {
};
static const struct GBACartridgeOverride _overrides[] = {
{ 'EPXA', SAVEDATA_FLASH1M, GPIO_RTC },
{ 'EVXA', SAVEDATA_FLASH1M, GPIO_RTC },
{ 'E4XA', SAVEDATA_FLASH1M, GPIO_NONE },
{ 'EEPB', SAVEDATA_FLASH1M, GPIO_RTC },
{ 0, 0, 0 }
@ -479,11 +481,11 @@ void _checkOverrides(struct GBA* gba, uint32_t id) {
case SAVEDATA_NONE:
break;
}
return;
if (_overrides[i].gpio & GPIO_RTC) {
GBAGPIOInitRTC(&gba->memory.gpio);
}
return;
}
}
}