rlm@1: #include rlm@1: rlm@1: #include "../Port.h" rlm@1: #include "../NLS.h" rlm@1: #include "../common/System.h" // systemMessage rlm@1: #include "../common/Util.h" rlm@1: #include "../common/movie.h" rlm@1: #include "GBAGlobals.h" rlm@1: rlm@1: enum RTCSTATE { IDLE, COMMAND, DATA, READDATA }; rlm@1: rlm@1: typedef struct rlm@1: { rlm@1: u8 byte0; rlm@1: u8 byte1; rlm@1: u8 byte2; rlm@1: u8 command; rlm@1: int dataLen; rlm@1: int bits; rlm@1: RTCSTATE state; rlm@1: u8 data[12]; rlm@1: // reserved variables for future rlm@1: u8 reserved[12]; rlm@1: bool reserved2; rlm@1: u32 reserved3; rlm@1: } RTCCLOCKDATA; rlm@1: rlm@1: static RTCCLOCKDATA rtcClockData; rlm@1: static bool rtcEnabled = false; rlm@1: rlm@1: void rtcEnable(bool enable) rlm@1: { rlm@1: rtcEnabled = enable; rlm@1: } rlm@1: rlm@1: bool rtcIsEnabled() rlm@1: { rlm@1: return rtcEnabled; rlm@1: } rlm@1: rlm@1: u16 rtcRead(u32 address) rlm@1: { rlm@1: if (rtcEnabled) rlm@1: { rlm@1: if (address == 0x80000c8) rlm@1: return rtcClockData.byte2; rlm@1: else if (address == 0x80000c6) rlm@1: return rtcClockData.byte1; rlm@1: else if (address == 0x80000c4) rlm@1: { rlm@1: return rtcClockData.byte0; rlm@1: } rlm@1: } rlm@1: rlm@1: return READ16LE((&rom[address & 0x1FFFFFE])); rlm@1: } rlm@1: rlm@1: static u8 toBCD(u8 value) rlm@1: { rlm@1: value = value % 100; rlm@1: int l = value % 10; rlm@1: int h = value / 10; rlm@1: return h * 16 + l; rlm@1: } rlm@1: rlm@1: bool rtcWrite(u32 address, u16 value) rlm@1: { rlm@1: if (!rtcEnabled) rlm@1: return false; rlm@1: rlm@1: if (address == 0x80000c8) rlm@1: { rlm@1: rtcClockData.byte2 = (u8)value; // enable ? rlm@1: } rlm@1: else if (address == 0x80000c6) rlm@1: { rlm@1: rtcClockData.byte1 = (u8)value; // read/write rlm@1: } rlm@1: else if (address == 0x80000c4) rlm@1: { rlm@1: if (rtcClockData.byte2 & 1) rlm@1: { rlm@1: if (rtcClockData.state == IDLE && rtcClockData.byte0 == 1 && value == 5) rlm@1: { rlm@1: rtcClockData.state = COMMAND; rlm@1: rtcClockData.bits = 0; rlm@1: rtcClockData.command = 0; rlm@1: } rlm@1: else if (!(rtcClockData.byte0 & 1) && (value & 1)) // bit transfer rlm@1: { rlm@1: rtcClockData.byte0 = (u8)value; rlm@1: switch (rtcClockData.state) rlm@1: { rlm@1: case COMMAND: rlm@1: rtcClockData.command |= ((value & 2) >> 1) << (7-rtcClockData.bits); rlm@1: rtcClockData.bits++; rlm@1: if (rtcClockData.bits == 8) rlm@1: { rlm@1: rtcClockData.bits = 0; rlm@1: switch (rtcClockData.command) rlm@1: { rlm@1: case 0x60: rlm@1: // not sure what this command does but it doesn't take parameters rlm@1: // maybe it is a reset or stop rlm@1: rtcClockData.state = IDLE; rlm@1: rtcClockData.bits = 0; rlm@1: break; rlm@1: case 0x62: rlm@1: // this sets the control state but not sure what those values are rlm@1: rtcClockData.state = READDATA; rlm@1: rtcClockData.dataLen = 1; rlm@1: break; rlm@1: case 0x63: rlm@1: rtcClockData.dataLen = 1; rlm@1: rtcClockData.data[0] = 0x40; rlm@1: rtcClockData.state = DATA; rlm@1: break; rlm@1: case 0x65: rlm@1: { rlm@1: struct tm *newtime; rlm@1: time_t long_time; rlm@1: rlm@1: if (VBAMovieActive() || VBAMovieLoading()) rlm@1: { rlm@1: long_time = VBAMovieGetId() + VBAMovieGetFrameCounter()/60; rlm@1: newtime = gmtime(&long_time); rlm@1: } rlm@1: else rlm@1: { rlm@1: time(&long_time); /* Get time as long integer. */ rlm@1: newtime = localtime(&long_time); /* Convert to local time. */ rlm@1: } rlm@1: rlm@1: rtcClockData.dataLen = 7; rlm@1: rtcClockData.data[0] = toBCD(newtime->tm_year); rlm@1: rtcClockData.data[1] = toBCD(newtime->tm_mon+1); rlm@1: rtcClockData.data[2] = toBCD(newtime->tm_mday); rlm@1: rtcClockData.data[3] = 0; rlm@1: rtcClockData.data[4] = toBCD(newtime->tm_hour); rlm@1: rtcClockData.data[5] = toBCD(newtime->tm_min); rlm@1: rtcClockData.data[6] = toBCD(newtime->tm_sec); rlm@1: rtcClockData.state = DATA; rlm@1: break; rlm@1: } rlm@1: case 0x67: rlm@1: { rlm@1: struct tm *newtime; rlm@1: time_t long_time; rlm@1: rlm@1: if (VBAMovieActive() || VBAMovieLoading()) rlm@1: { rlm@1: long_time = VBAMovieGetId() + VBAMovieGetFrameCounter()/60; rlm@1: newtime = gmtime(&long_time); rlm@1: } rlm@1: else rlm@1: { rlm@1: time(&long_time); /* Get time as long integer. */ rlm@1: newtime = localtime(&long_time); /* Convert to local time. */ rlm@1: } rlm@1: rlm@1: rtcClockData.dataLen = 3; rlm@1: rtcClockData.data[0] = toBCD(newtime->tm_hour); rlm@1: rtcClockData.data[1] = toBCD(newtime->tm_min); rlm@1: rtcClockData.data[2] = toBCD(newtime->tm_sec); rlm@1: rtcClockData.state = DATA; rlm@1: break; rlm@1: } rlm@1: default: rlm@1: systemMessage(0, N_("Unknown RTC command %02x"), rtcClockData.command); rlm@1: rtcClockData.state = IDLE; rlm@1: break; rlm@1: } rlm@1: } rlm@1: break; rlm@1: case DATA: rlm@1: if (rtcClockData.byte1 & 2) rlm@1: {} rlm@1: else rlm@1: { rlm@1: rtcClockData.byte0 = (rtcClockData.byte0 & ~2) | rlm@1: ((rtcClockData.data[rtcClockData.bits >> 3] >> rlm@1: (rtcClockData.bits & 7)) & 1)*2; rlm@1: rtcClockData.bits++; rlm@1: if (rtcClockData.bits == 8*rtcClockData.dataLen) rlm@1: { rlm@1: rtcClockData.bits = 0; rlm@1: rtcClockData.state = IDLE; rlm@1: } rlm@1: } rlm@1: break; rlm@1: case READDATA: rlm@1: if (!(rtcClockData.byte1 & 2)) rlm@1: {} rlm@1: else rlm@1: { rlm@1: rtcClockData.data[rtcClockData.bits >> 3] = rlm@1: (rtcClockData.data[rtcClockData.bits >> 3] >> 1) | rlm@1: ((value << 6) & 128); rlm@1: rtcClockData.bits++; rlm@1: if (rtcClockData.bits == 8*rtcClockData.dataLen) rlm@1: { rlm@1: rtcClockData.bits = 0; rlm@1: rtcClockData.state = IDLE; rlm@1: } rlm@1: } rlm@1: break; rlm@1: default: rlm@1: break; rlm@1: } rlm@1: } rlm@1: else rlm@1: rtcClockData.byte0 = (u8)value; rlm@1: } rlm@1: } rlm@1: return true; rlm@1: } rlm@1: rlm@1: void rtcReset() rlm@1: { rlm@1: memset(&rtcClockData, 0, sizeof(rtcClockData)); rlm@1: rlm@1: rtcClockData.byte0 = 0; rlm@1: rtcClockData.byte1 = 0; rlm@1: rtcClockData.byte2 = 0; rlm@1: rtcClockData.command = 0; rlm@1: rtcClockData.dataLen = 0; rlm@1: rtcClockData.bits = 0; rlm@1: rtcClockData.state = IDLE; rlm@1: } rlm@1: rlm@1: void rtcSaveGame(gzFile gzFile) rlm@1: { rlm@1: utilGzWrite(gzFile, &rtcClockData, sizeof(rtcClockData)); rlm@1: } rlm@1: rlm@1: void rtcReadGame(gzFile gzFile) rlm@1: { rlm@1: utilGzRead(gzFile, &rtcClockData, sizeof(rtcClockData)); rlm@1: } rlm@1: