DS: Partially implement RTC

This commit is contained in:
Vicki Pfau 2017-03-09 22:51:09 -05:00
parent 662a6cdd58
commit 2222010107
6 changed files with 110 additions and 68 deletions

View File

@ -20,6 +20,7 @@ CXX_GUARD_START
#include <mgba/internal/ds/timer.h>
#include <mgba/internal/ds/video.h>
#include <mgba/internal/ds/wifi.h>
#include <mgba/internal/gba/hardware.h>
extern const uint32_t DS_ARM946ES_FREQUENCY;
extern const uint32_t DS_ARM7TDMI_FREQUENCY;
@ -89,6 +90,7 @@ struct DS {
struct DSAudio audio;
struct DSGX gx;
struct DSWifi wifi;
struct GBARTC rtc;
struct mCoreSync* sync;
struct mTimingEvent slice;
@ -193,6 +195,8 @@ void DSRaiseIRQ(struct ARMCore* cpu, uint16_t* io, enum DSIRQ irq);
void DSFrameStarted(struct DS* ds);
void DSFrameEnded(struct DS* ds);
uint16_t DSWriteRTC(struct DS* ds, DSRegisterRTC value);
CXX_GUARD_END
#endif

View File

@ -567,6 +567,14 @@ mLOG_DECLARE_CATEGORY(DS_IO);
extern const char* const DS7IORegisterNames[];
extern const char* const DS9IORegisterNames[];
DECL_BITFIELD(DSRegisterRTC, uint16_t);
DECL_BIT(DSRegisterRTC, Data, 0);
DECL_BIT(DSRegisterRTC, Clock, 1);
DECL_BIT(DSRegisterRTC, Select, 2);
DECL_BIT(DSRegisterRTC, DataDirection, 4);
DECL_BIT(DSRegisterRTC, ClockDirection, 5);
DECL_BIT(DSRegisterRTC, SelectDirection, 6);
struct DS;
void DS7IOInit(struct DS* ds);
void DS7IOWrite(struct DS* ds, uint32_t address, uint16_t value);

View File

@ -123,6 +123,9 @@ struct GBACartridgeHardware {
void GBAHardwareInit(struct GBACartridgeHardware* gpio, uint16_t* gpioBase);
void GBAHardwareClear(struct GBACartridgeHardware* gpio);
void GBARTCProcessByte(struct GBARTC* rtc, struct mRTCSource* source);
unsigned GBARTCOutput(struct GBARTC* rtc);
void GBAHardwareInitRTC(struct GBACartridgeHardware* gpio);
void GBAHardwareInitGyro(struct GBACartridgeHardware* gpio);
void GBAHardwareInitRumble(struct GBACartridgeHardware* gpio);
@ -137,8 +140,6 @@ struct GBAVideo;
void GBAHardwarePlayerUpdate(struct GBA* gba);
bool GBAHardwarePlayerCheckScreen(const struct GBAVideo* video);
void GBARTCGenericSourceInit(struct GBARTCGenericSource* rtc, struct GBA* gba);
struct GBASerializedState;
void GBAHardwareSerialize(const struct GBACartridgeHardware* gpio, struct GBASerializedState* state);
void GBAHardwareDeserialize(struct GBACartridgeHardware* gpio, const struct GBASerializedState* state);

View File

@ -870,3 +870,55 @@ void DSFrameEnded(struct DS* ds) {
ds->stream->postVideoFrame(ds->stream, pixels, stride);
}
}
uint16_t DSWriteRTC(struct DS* ds, DSRegisterRTC value) {
switch (ds->rtc.transferStep) {
case 0:
if ((value & 6) == 2) {
ds->rtc.transferStep = 1;
}
break;
case 1:
if ((value & 6) == 6) {
ds->rtc.transferStep = 2;
}
break;
case 2:
if (!DSRegisterRTCIsClock(value)) {
ds->rtc.bits &= ~(1 << ds->rtc.bitsRead);
ds->rtc.bits |= DSRegisterRTCGetData(value) << ds->rtc.bitsRead;
} else {
if (DSRegisterRTCIsSelect(value)) {
// GPIO direction should always != reading
if (DSRegisterRTCIsDataDirection(value)) {
if (RTCCommandDataIsReading(ds->rtc.command)) {
mLOG(DS, GAME_ERROR, "Attempting to write to RTC while in read mode");
}
++ds->rtc.bitsRead;
if (ds->rtc.bitsRead == 8) {
GBARTCProcessByte(&ds->rtc, ds->rtcSource);
}
} else {
value = DSRegisterRTCSetData(value, GBARTCOutput(&ds->rtc));
++ds->rtc.bitsRead;
if (ds->rtc.bitsRead == 8) {
--ds->rtc.bytesRemaining;
if (ds->rtc.bytesRemaining <= 0) {
ds->rtc.commandActive = 0;
ds->rtc.command = RTCCommandDataClearReading(ds->rtc.command);
}
ds->rtc.bitsRead = 0;
}
}
} else {
ds->rtc.bitsRead = 0;
ds->rtc.bytesRemaining = 0;
ds->rtc.commandActive = 0;
ds->rtc.command = RTCCommandDataClearReading(ds->rtc.command);
ds->rtc.transferStep = 0;
}
}
break;
}
return value;
}

View File

@ -273,6 +273,9 @@ void DS7IOWrite(struct DS* ds, uint32_t address, uint16_t value) {
case DS7_REG_SPIDATA:
DSSPIWrite(ds, value);
break;
case DS7_REG_RTC:
value = DSWriteRTC(ds, value);
break;
case DS7_REG_SOUND0CNT_LO:
case DS7_REG_SOUND1CNT_LO:
case DS7_REG_SOUND2CNT_LO:

View File

@ -19,13 +19,9 @@ 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 void _rtcUpdateClock(struct GBARTC* rtc, struct mRTCSource*);
static unsigned _rtcBCD(unsigned value);
static time_t _rtcGenericCallback(struct mRTCSource* source);
static void _gyroReadPins(struct GBACartridgeHardware* hw);
static void _rumbleReadPins(struct GBACartridgeHardware* hw);
@ -183,10 +179,10 @@ void _rtcReadPins(struct GBACartridgeHardware* hw) {
}
++hw->rtc.bitsRead;
if (hw->rtc.bitsRead == 8) {
_rtcProcessByte(hw);
GBARTCProcessByte(&hw->rtc, hw->p->rtcSource);
}
} else {
_outputPins(hw, 5 | (_rtcOutput(hw) << 1));
_outputPins(hw, 5 | (GBARTCOutput(&hw->rtc) << 1));
++hw->rtc.bitsRead;
if (hw->rtc.bitsRead == 8) {
--hw->rtc.bytesRemaining;
@ -209,38 +205,38 @@ void _rtcReadPins(struct GBACartridgeHardware* hw) {
}
}
void _rtcProcessByte(struct GBACartridgeHardware* hw) {
--hw->rtc.bytesRemaining;
if (!hw->rtc.commandActive) {
void GBARTCProcessByte(struct GBARTC* rtc, struct mRTCSource* source) {
--rtc->bytesRemaining;
if (!rtc->commandActive) {
RTCCommandData command;
command = hw->rtc.bits;
command = rtc->bits;
if (RTCCommandDataGetMagic(command) == 0x06) {
hw->rtc.command = command;
rtc->command = command;
hw->rtc.bytesRemaining = RTC_BYTES[RTCCommandDataGetCommand(command)];
hw->rtc.commandActive = hw->rtc.bytesRemaining > 0;
rtc->bytesRemaining = RTC_BYTES[RTCCommandDataGetCommand(command)];
rtc->commandActive = rtc->bytesRemaining > 0;
switch (RTCCommandDataGetCommand(command)) {
case RTC_RESET:
hw->rtc.control = 0;
rtc->control = 0;
break;
case RTC_DATETIME:
case RTC_TIME:
_rtcUpdateClock(hw);
_rtcUpdateClock(rtc, source);
break;
case RTC_FORCE_IRQ:
case RTC_CONTROL:
break;
}
} else {
mLOG(GBA_HW, WARN, "Invalid RTC command byte: %02X", hw->rtc.bits);
mLOG(GBA_HW, WARN, "Invalid RTC command byte: %02X", rtc->bits);
}
} else {
switch (RTCCommandDataGetCommand(hw->rtc.command)) {
switch (RTCCommandDataGetCommand(rtc->command)) {
case RTC_CONTROL:
hw->rtc.control = hw->rtc.bits;
rtc->control = rtc->bits;
break;
case RTC_FORCE_IRQ:
mLOG(GBA_HW, STUB, "Unimplemented RTC command %u", RTCCommandDataGetCommand(hw->rtc.command));
mLOG(GBA_HW, STUB, "Unimplemented RTC command %u", RTCCommandDataGetCommand(rtc->command));
break;
case RTC_RESET:
case RTC_DATETIME:
@ -249,56 +245,55 @@ void _rtcProcessByte(struct GBACartridgeHardware* hw) {
}
}
hw->rtc.bits = 0;
hw->rtc.bitsRead = 0;
if (!hw->rtc.bytesRemaining) {
hw->rtc.commandActive = 0;
hw->rtc.command = RTCCommandDataClearReading(hw->rtc.command);
rtc->bits = 0;
rtc->bitsRead = 0;
if (!rtc->bytesRemaining) {
rtc->commandActive = 0;
rtc->command = RTCCommandDataClearReading(rtc->command);
}
}
unsigned _rtcOutput(struct GBACartridgeHardware* hw) {
unsigned GBARTCOutput(struct GBARTC* rtc) {
uint8_t outputByte = 0;
switch (RTCCommandDataGetCommand(hw->rtc.command)) {
switch (RTCCommandDataGetCommand(rtc->command)) {
case RTC_CONTROL:
outputByte = hw->rtc.control;
outputByte = rtc->control;
break;
case RTC_DATETIME:
case RTC_TIME:
outputByte = hw->rtc.time[7 - hw->rtc.bytesRemaining];
outputByte = rtc->time[7 - rtc->bytesRemaining];
break;
case RTC_FORCE_IRQ:
case RTC_RESET:
break;
}
unsigned output = (outputByte >> hw->rtc.bitsRead) & 1;
unsigned output = (outputByte >> rtc->bitsRead) & 1;
return output;
}
void _rtcUpdateClock(struct GBACartridgeHardware* hw) {
void _rtcUpdateClock(struct GBARTC* rtc, struct mRTCSource* source) {
time_t t;
struct mRTCSource* rtc = hw->p->rtcSource;
if (rtc) {
if (rtc->sample) {
rtc->sample(rtc);
if (source) {
if (source->sample) {
source->sample(source);
}
t = rtc->unixTime(rtc);
t = source->unixTime(source);
} else {
t = time(0);
}
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);
rtc->time[0] = _rtcBCD(date.tm_year - 100);
rtc->time[1] = _rtcBCD(date.tm_mon + 1);
rtc->time[2] = _rtcBCD(date.tm_mday);
rtc->time[3] = _rtcBCD(date.tm_wday);
if (RTCControlIsHour24(rtc->control)) {
rtc->time[4] = _rtcBCD(date.tm_hour);
} else {
hw->rtc.time[4] = _rtcBCD(date.tm_hour % 12);
rtc->time[4] = _rtcBCD(date.tm_hour % 12);
}
hw->rtc.time[5] = _rtcBCD(date.tm_min);
hw->rtc.time[6] = _rtcBCD(date.tm_sec);
rtc->time[5] = _rtcBCD(date.tm_min);
rtc->time[6] = _rtcBCD(date.tm_sec);
}
unsigned _rtcBCD(unsigned value) {
@ -308,27 +303,6 @@ unsigned _rtcBCD(unsigned value) {
return counter;
}
time_t _rtcGenericCallback(struct mRTCSource* source) {
struct GBARTCGenericSource* rtc = (struct GBARTCGenericSource*) source;
switch (rtc->override) {
case RTC_NO_OVERRIDE:
default:
return time(0);
case RTC_FIXED:
return rtc->value;
case RTC_FAKE_EPOCH:
return rtc->value + rtc->p->video.frameCounter * (int64_t) VIDEO_TOTAL_LENGTH / GBA_ARM7TDMI_FREQUENCY;
}
}
void GBARTCGenericSourceInit(struct GBARTCGenericSource* rtc, struct GBA* gba) {
rtc->p = gba;
rtc->override = RTC_NO_OVERRIDE;
rtc->value = 0;
rtc->d.sample = 0;
rtc->d.unixTime = _rtcGenericCallback;
}
// == Gyro
void GBAHardwareInitGyro(struct GBACartridgeHardware* hw) {