mirror of https://github.com/mgba-emu/mgba.git
Implement RTC
This commit is contained in:
parent
7e5eada69b
commit
fcaa0eb066
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue