mirror of https://github.com/mgba-emu/mgba.git
DS: Partially implement RTC
This commit is contained in:
parent
662a6cdd58
commit
2222010107
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
52
src/ds/ds.c
52
src/ds/ds.c
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in New Issue