summaryrefslogtreecommitdiff
path: root/src/Dolphin/os/OSTime.c
blob: 42507980bcd7f03f1a1f3bbf526484f933c08282 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#include <dolphin/os.h>

#define OS_TIME_MONTH_MAX 12
#define OS_TIME_WEEK_DAY_MAX 7
#define OS_TIME_YEAR_DAY_MAX 365

// End of each month in standard year
static s32 YearDays[OS_TIME_MONTH_MAX] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
// End of each month in leap year
static s32 LeapYearDays[OS_TIME_MONTH_MAX] = {0,   31,  60,  91,  121, 152,
                                              182, 213, 244, 274, 305, 335};

asm OSTime OSGetTime(void) {
  // clang-format off
    nofralloc

    mftbu r3
    mftb r4

    // Check for possible carry from TBL to TBU
    mftbu r5
    cmpw r3, r5
    bne OSGetTime

    blr
  // clang-format on
}

asm OSTick OSGetTick(void){
    // clang-format off
    nofralloc

    mftb r3
    blr
    // clang-format on
}

#define OS_SYSTEMTIME_BASE 0x30D8

OSTime __OSGetSystemTime(void) {
  BOOL enabled;
  OSTime* timeAdjustAddr = (OSTime*)(OS_BASE_CACHED + OS_SYSTEMTIME_BASE);
  OSTime result;

  enabled = OSDisableInterrupts();
  result = *timeAdjustAddr + OSGetTime();
  OSRestoreInterrupts(enabled);

  return result;
}

OSTime __OSTimeToSystemTime(OSTime time) {
  BOOL enabled;
  OSTime* timeAdjustAddr = (OSTime*)(OS_BASE_CACHED + OS_SYSTEMTIME_BASE);
  OSTime result;

  enabled = OSDisableInterrupts();
  result = *timeAdjustAddr + time;
  OSRestoreInterrupts(enabled);

  return result;
}

static BOOL IsLeapYear(s32 year) { return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0); }

static s32 GetYearDays(s32 year, s32 mon) {
  return (IsLeapYear(year) ? LeapYearDays : YearDays)[mon];
}

static s32 GetLeapDays(s32 year) {
  if (year < 1) {
    return 0;
  }
  return (year + 3) / 4 - (year - 1) / 100 + (year - 1) / 400;
}

static void GetDates(s32 days, OSCalendarTime* cal) {
  s32 year;
  s32 totalDays;
  s32* p_days;
  s32 month;
  cal->wday = (days + 6) % OS_TIME_WEEK_DAY_MAX;

  for (year = days / OS_TIME_YEAR_DAY_MAX;
       days < (totalDays = year * OS_TIME_YEAR_DAY_MAX + GetLeapDays(year));) {
    year--;
  }

  days -= totalDays;
  cal->year = year;
  cal->yday = days;

  p_days = IsLeapYear(year) ? LeapYearDays : YearDays;
  month = OS_TIME_MONTH_MAX;
  while (days < p_days[--month]) {
    ;
  }
  cal->mon = month;
  cal->mday = days - p_days[month] + 1;
}

#define BIAS (2000 * 365 + (2000 + 3) / 4 - (2000 - 1) / 100 + (2000 - 1) / 400)

#pragma push
#pragma dont_inline on
void OSTicksToCalendarTime(OSTime ticks, OSCalendarTime* td) {
  int days;
  int secs;
  OSTime d;

  d = ticks % OSSecondsToTicks(1);
  if (d < 0) {
    d += OSSecondsToTicks(1);
  }
  td->usec = (int)(OSTicksToMicroseconds(d) % 1000);
  td->msec = (int)(OSTicksToMilliseconds(d) % 1000);

  ticks -= d;
  days = (int)(OSTicksToSeconds(ticks) / 86400 + BIAS);
  secs = (int)(OSTicksToSeconds(ticks) % 86400);
  if (secs < 0) {
    days -= 1;
    secs += 24 * 60 * 60;
  }

  GetDates(days, td);

  td->hour = secs / 60 / 60;
  td->min = (secs / 60) % 60;
  td->sec = secs % 60;
}
#pragma dont_inline reset

OSTime OSCalendarTimeToTicks(OSCalendarTime* time) {
  ;
  ;
}