Implement RTC
Jeffrey Pfau jeffrey@endrift.com
Sun, 20 Oct 2013 21:39:47 -0700
3 files changed,
236 insertions(+),
9 deletions(-)
M
src/gba/gba-gpio.c
→
src/gba/gba-gpio.c
@@ -2,16 +2,34 @@ #include "gba.h"
#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 @@ gpio->pinState = value;
_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) {@@ -40,7 +74,163 @@ if (gpio->gpioDevices & GPIO_RTC) {
_rtcReadPins(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; }
M
src/gba/gba-gpio.h
→
src/gba/gba-gpio.h
@@ -24,13 +24,48 @@ GPIO_WRITE_ONLY = 0,
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 @@ unsigned dir1 : 1;
unsigned dir2 : 1; unsigned dir3 : 1; }; - uint16_t pinDirection; + uint16_t direction; }; struct GBARTC rtc;
M
src/gba/gba.c
→
src/gba/gba.c
@@ -29,6 +29,8 @@ int gpio;
}; 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 @@ break;
case SAVEDATA_NONE: break; } - return; if (_overrides[i].gpio & GPIO_RTC) { GBAGPIOInitRTC(&gba->memory.gpio); } + return; } } }