summaryrefslogtreecommitdiff
path: root/berry_fix/payload/src/rtc.c
diff options
context:
space:
mode:
Diffstat (limited to 'berry_fix/payload/src/rtc.c')
-rw-r--r--berry_fix/payload/src/rtc.c346
1 files changed, 346 insertions, 0 deletions
diff --git a/berry_fix/payload/src/rtc.c b/berry_fix/payload/src/rtc.c
new file mode 100644
index 000000000..97692e205
--- /dev/null
+++ b/berry_fix/payload/src/rtc.c
@@ -0,0 +1,346 @@
+#include <gba/gba.h>
+#include <siirtc.h>
+#include "global.h"
+#include "main.h"
+
+struct Time gTimeSinceBerryUpdate;
+struct Time gRtcUTCTime;
+
+static u16 sRtcProbeStatus;
+static struct SiiRtcInfo sRtcInfoBuffer;
+static u8 sRtcProbeCode;
+static u16 sImeBak;
+static struct SiiRtcInfo sRtcInfoWork;
+
+const struct SiiRtcInfo sDefaultRTC = {
+ .year = 0, // 2000
+ .month = 1, // January
+ .day = 1, // 01
+ .dayOfWeek = 0,
+ .hour = 0,
+ .minute = 0,
+ .second = 0,
+ .status = 0,
+ .alarmHour = 0,
+ .alarmMinute = 0
+};
+const s32 sDaysPerMonth[] = {
+ 31,
+ 28,
+ 31,
+ 30,
+ 31,
+ 30,
+ 31,
+ 31,
+ 30,
+ 31,
+ 30,
+ 31
+};
+
+void rtc_get_status_and_datetime(struct SiiRtcInfo *);
+u16 rtc_validate_datetime(struct SiiRtcInfo *);
+
+
+void rtc_intr_disable(void)
+{
+ sImeBak = REG_IME;
+ REG_IME = 0;
+}
+
+void rtc_intr_enable(void)
+{
+ REG_IME = sImeBak;
+}
+
+s32 bcd_to_hex(u8 a0)
+{
+ if (a0 >= 0xa0 || (a0 & 0xF) >= 10)
+ return 0xFF;
+ return ((a0 >> 4) & 0xF) * 10 + (a0 & 0xF);
+}
+
+bool8 is_leap_year(u8 year)
+{
+ if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)
+ return TRUE;
+ return FALSE;
+}
+
+u16 rtc_count_days_parameterized(u8 year, u8 month, u8 day)
+{
+ u16 numDays = 0;
+ s32 i;
+ for (i = year - 1; i > 0; i--)
+ {
+ numDays += 365;
+ if (is_leap_year(i) == TRUE)
+ numDays++;
+ }
+ for (i = 0; i < month - 1; i++)
+ numDays += sDaysPerMonth[i];
+ if (month > MONTH_FEB && is_leap_year(year) == TRUE)
+ numDays++;
+ numDays += day;
+ return numDays;
+}
+
+u16 rtc_count_days_from_info(struct SiiRtcInfo *info)
+{
+ return rtc_count_days_parameterized(bcd_to_hex(info->year), bcd_to_hex(info->month), bcd_to_hex(info->day));
+}
+
+static void rtc_probe_status(void)
+{
+ sRtcProbeStatus = 0;
+ rtc_intr_disable();
+ SiiRtcUnprotect();
+ sRtcProbeCode = SiiRtcProbe();
+ rtc_intr_enable();
+ if ((sRtcProbeCode & 0xF) != 1)
+ sRtcProbeStatus = 1;
+ else
+ {
+ if (sRtcProbeCode & 0xF0)
+ sRtcProbeStatus = 2;
+ else
+ sRtcProbeStatus = 0;
+ rtc_get_status_and_datetime(&sRtcInfoBuffer);
+ sRtcProbeStatus = rtc_validate_datetime(&sRtcInfoBuffer);
+ }
+}
+
+u16 rtc_get_probe_status(void)
+{
+ return sRtcProbeStatus;
+}
+
+void sub_020106EC(struct SiiRtcInfo * info)
+{
+ if (sRtcProbeStatus & 0xFF0)
+ *info = sDefaultRTC;
+ else
+ rtc_get_status_and_datetime(info);
+}
+
+void rtc_get_datetime(struct SiiRtcInfo * info)
+{
+ rtc_intr_disable();
+ SiiRtcGetDateTime(info);
+ rtc_intr_enable();
+}
+
+void rtc_get_status(struct SiiRtcInfo * info)
+{
+ rtc_intr_disable();
+ SiiRtcGetStatus(info);
+ rtc_intr_enable();
+}
+
+void rtc_get_status_and_datetime(struct SiiRtcInfo * info)
+{
+ rtc_get_status(info);
+ rtc_get_datetime(info);
+}
+
+u16 rtc_validate_datetime(struct SiiRtcInfo * info)
+{
+ s32 year, month, day;
+ u16 r4 = (info->status & SIIRTCINFO_POWER) ? 0x20 : 0;
+ if (!(info->status & SIIRTCINFO_24HOUR))
+ r4 |= 0x10;
+ year = bcd_to_hex(info->year);
+ if (year == 0xFF)
+ r4 |= 0x40;
+ month = bcd_to_hex(info->month);
+ if (month == 0xFF || month == 0 || month > 12)
+ r4 |= 0x80;
+ day = bcd_to_hex(info->day);
+ if (day == 0xFF)
+ r4 |= 0x100;
+ if (month == MONTH_FEB)
+ {
+ if (day > is_leap_year(year) + sDaysPerMonth[1])
+ r4 |= 0x100;
+ }
+ else
+ {
+ if (day > sDaysPerMonth[month - 1])
+ r4 |= 0x100;
+ }
+ day = bcd_to_hex(info->hour);
+ if (day > 24)
+ r4 |= 0x200;
+ day = bcd_to_hex(info->minute);
+ if (day > 60)
+ r4 |= 0x400;
+ day = bcd_to_hex(info->second);
+ if (day > 60)
+ r4 |= 0x800;
+ return r4;
+}
+
+void rtc_reset(void)
+{
+ rtc_intr_disable();
+ SiiRtcReset();
+ rtc_intr_enable();
+}
+
+void rtc_sub_time_from_datetime(struct SiiRtcInfo * datetime, struct Time * dest, struct Time * timediff)
+{
+ u16 r4 = rtc_count_days_from_info(datetime);
+ dest->seconds = bcd_to_hex(datetime->second) - timediff->seconds;
+ dest->minutes = bcd_to_hex(datetime->minute) - timediff->minutes;
+ dest->hours = bcd_to_hex(datetime->hour) - timediff->hours;
+ dest->days = r4 - timediff->days;
+ if (dest->seconds < 0)
+ {
+ dest->seconds += 60;
+ dest->minutes--;
+ }
+ if (dest->minutes < 0)
+ {
+ dest->minutes += 60;
+ dest->hours--;
+ }
+ if (dest->hours < 0)
+ {
+ dest->hours += 24;
+ dest->days--;
+ }
+}
+
+void rtc_sub_time_from_time(struct Time * dest, struct Time * diff, struct Time * src)
+{
+ dest->seconds = src->seconds - diff->seconds;
+ dest->minutes = src->minutes - diff->minutes;
+ dest->hours = src->hours - diff->hours;
+ dest->days = src->days - diff->days;
+ if (dest->seconds < 0)
+ {
+ dest->seconds += 60;
+ dest->minutes--;
+ }
+ if (dest->minutes < 0)
+ {
+ dest->minutes += 60;
+ dest->hours--;
+ }
+ if (dest->hours < 0)
+ {
+ dest->hours += 24;
+ dest->days--;
+ }
+}
+
+bool32 rtc_maincb_is_rtc_working(void)
+{
+ rtc_probe_status();
+ if (rtc_get_probe_status() & 0xFF0)
+ return FALSE;
+ return TRUE;
+}
+
+void rtc_set_datetime(struct SiiRtcInfo * info)
+{
+ vu16 imeBak = REG_IME;
+ REG_IME = 0;
+ SiiRtcSetDateTime(info);
+ REG_IME = imeBak;
+}
+
+bool32 rtc_maincb_is_time_since_last_berry_update_positive(u8 * a0)
+{
+ rtc_get_status_and_datetime(&sRtcInfoWork);
+ *a0 = bcd_to_hex(sRtcInfoWork.year);
+ rtc_sub_time_from_datetime(&sRtcInfoWork, &gRtcUTCTime, LocalTimeOffset);
+ rtc_sub_time_from_time(&gTimeSinceBerryUpdate, LastBerryTreeUpdate, &gRtcUTCTime);
+ if (gTimeSinceBerryUpdate.days * 1440 + gTimeSinceBerryUpdate.hours * 60 + gTimeSinceBerryUpdate.minutes >= 0)
+ return TRUE;
+ return FALSE;
+}
+
+u32 hex_to_bcd(u8 a0)
+{
+ u32 r4;
+ if (a0 > 99)
+ return 0xFF;
+ r4 = Div(a0, 10) << 4;
+ r4 |= Mod(a0, 10);
+ return r4;
+}
+
+void sii_rtc_inc(u8 * a0)
+{
+ *a0 = hex_to_bcd(bcd_to_hex(*a0) + 1);
+}
+
+void sii_rtc_inc_month(struct SiiRtcInfo * a0)
+{
+ sii_rtc_inc(&a0->month);
+ if (bcd_to_hex(a0->month) > 12)
+ {
+ sii_rtc_inc(&a0->year);
+ a0->month = MONTH_JAN;
+ }
+}
+
+void sii_rtc_inc_day(struct SiiRtcInfo * a0)
+{
+ sii_rtc_inc(&a0->day);
+ if (bcd_to_hex(a0->day) > sDaysPerMonth[bcd_to_hex(a0->month) - 1])
+ {
+ if (!is_leap_year(bcd_to_hex(a0->year)) || bcd_to_hex(a0->month) != MONTH_FEB || bcd_to_hex(a0->day) != 29)
+ {
+ a0->day = 1;
+ sii_rtc_inc_month(a0);
+ }
+ }
+}
+
+bool32 rtc_is_past_feb_28_2000(struct SiiRtcInfo * a0)
+{
+ if (bcd_to_hex(a0->year) == 0)
+ {
+ if (bcd_to_hex(a0->month) == MONTH_JAN)
+ return FALSE;
+ if (bcd_to_hex(a0->month) > MONTH_FEB)
+ return TRUE;
+ if (bcd_to_hex(a0->day) == 29)
+ return TRUE;
+ return FALSE;
+ }
+ if (bcd_to_hex(a0->year) == 1)
+ return TRUE;
+ return FALSE;
+}
+
+void rtc_maincb_fix_date(void)
+{
+ rtc_get_status_and_datetime(&sRtcInfoWork);
+ if (bcd_to_hex(sRtcInfoWork.year) == 0 || bcd_to_hex(sRtcInfoWork.year) == 1)
+ {
+ if (bcd_to_hex(sRtcInfoWork.year) == 1)
+ {
+ sRtcInfoWork.year = 2;
+ sRtcInfoWork.month = MONTH_JAN;
+ sRtcInfoWork.day = 2;
+ rtc_set_datetime(&sRtcInfoWork);
+ }
+ else
+ {
+ if (rtc_is_past_feb_28_2000(&sRtcInfoWork) == TRUE)
+ {
+ sii_rtc_inc_day(&sRtcInfoWork);
+ sii_rtc_inc(&sRtcInfoWork.year);
+ }
+ else
+ {
+ sii_rtc_inc(&sRtcInfoWork.year);
+ }
+ rtc_set_datetime(&sRtcInfoWork);
+ }
+ }
+}