tumbledemerald-legacy/berry_fix/payload/src/rtc.c

347 lines
7.6 KiB
C

#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);
}
}
}