summaryrefslogtreecommitdiff
path: root/src/Dolphin/os/OSAlarm.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/Dolphin/os/OSAlarm.c')
-rw-r--r--src/Dolphin/os/OSAlarm.c183
1 files changed, 183 insertions, 0 deletions
diff --git a/src/Dolphin/os/OSAlarm.c b/src/Dolphin/os/OSAlarm.c
new file mode 100644
index 0000000..d362ceb
--- /dev/null
+++ b/src/Dolphin/os/OSAlarm.c
@@ -0,0 +1,183 @@
+#include "dolphin/PPCArch.h"
+#include "dolphin/os/OSPriv.h"
+
+static struct OSAlarmQueue {
+ OSAlarm* head;
+ OSAlarm* tail;
+} AlarmQueue;
+
+static void DecrementerExceptionHandler(__OSException exception, OSContext* context);
+static BOOL OnReset(BOOL final);
+
+void OSInitAlarm(void) {
+ if (__OSGetExceptionHandler(8) != DecrementerExceptionHandler) {
+ AlarmQueue.head = AlarmQueue.tail = NULL;
+ __OSSetExceptionHandler(8, DecrementerExceptionHandler);
+ }
+}
+
+void OSCreateAlarm(OSAlarm* alarm) {
+ alarm->handler = 0;
+ alarm->tag = 0;
+}
+
+static void SetTimer(OSAlarm* alarm) {
+ OSTime delta;
+
+ delta = alarm->fire - __OSGetSystemTime();
+ if (delta < 0) {
+ PPCMtdec(0);
+ } else if (delta < 0x80000000) {
+ PPCMtdec((u32)delta);
+ } else {
+ PPCMtdec(0x7fffffff);
+ }
+}
+
+static void InsertAlarm(OSAlarm* alarm, OSTime fire, OSAlarmHandler handler) {
+ OSAlarm* next;
+ OSAlarm* prev;
+
+ if (0 < alarm->period) {
+ OSTime time = __OSGetSystemTime();
+
+ fire = alarm->start;
+ if (alarm->start < time) {
+ fire += alarm->period * ((time - alarm->start) / alarm->period + 1);
+ }
+ }
+
+ alarm->handler = handler;
+ alarm->fire = fire;
+
+ for (next = AlarmQueue.head; next; next = next->next) {
+ if (next->fire <= fire) {
+ continue;
+ }
+
+ alarm->prev = next->prev;
+ next->prev = alarm;
+ alarm->next = next;
+ prev = alarm->prev;
+ if (prev) {
+ prev->next = alarm;
+ } else {
+ AlarmQueue.head = alarm;
+ SetTimer(alarm);
+ }
+ return;
+ }
+ alarm->next = 0;
+ prev = AlarmQueue.tail;
+ AlarmQueue.tail = alarm;
+ alarm->prev = prev;
+ if (prev) {
+ prev->next = alarm;
+ } else {
+ AlarmQueue.head = AlarmQueue.tail = alarm;
+ SetTimer(alarm);
+ }
+}
+
+void OSSetAlarm(OSAlarm* alarm, OSTime tick, OSAlarmHandler handler) {
+ BOOL enabled;
+ enabled = OSDisableInterrupts();
+ alarm->period = 0;
+ InsertAlarm(alarm, __OSGetSystemTime() + tick, handler);
+ OSRestoreInterrupts(enabled);
+}
+
+void OSSetPeriodicAlarm(OSAlarm* alarm, OSTime start, OSTime period, OSAlarmHandler handler) {
+ BOOL enabled;
+ enabled = OSDisableInterrupts();
+ alarm->period = period;
+ alarm->start = __OSTimeToSystemTime(start);
+ InsertAlarm(alarm, 0, handler);
+ OSRestoreInterrupts(enabled);
+}
+
+void OSCancelAlarm(OSAlarm* alarm) {
+ OSAlarm* next;
+ BOOL enabled;
+
+ enabled = OSDisableInterrupts();
+
+ if (alarm->handler == 0) {
+ OSRestoreInterrupts(enabled);
+ return;
+ }
+
+ next = alarm->next;
+ if (next == 0) {
+ AlarmQueue.tail = alarm->prev;
+ } else {
+ next->prev = alarm->prev;
+ }
+ if (alarm->prev) {
+ alarm->prev->next = next;
+ } else {
+ AlarmQueue.head = next;
+ if (next) {
+ SetTimer(next);
+ }
+ }
+ alarm->handler = 0;
+
+ OSRestoreInterrupts(enabled);
+}
+
+static void DecrementerExceptionCallback(register __OSException exception,
+ register OSContext* context) {
+ OSAlarm* alarm;
+ OSAlarm* next;
+ OSAlarmHandler handler;
+ OSTime time;
+ OSContext exceptionContext;
+ time = __OSGetSystemTime();
+ alarm = AlarmQueue.head;
+ if (alarm == 0) {
+ OSLoadContext(context);
+ }
+
+ if (time < alarm->fire) {
+ SetTimer(alarm);
+ OSLoadContext(context);
+ }
+
+ next = alarm->next;
+ AlarmQueue.head = next;
+ if (next == 0) {
+ AlarmQueue.tail = 0;
+ } else {
+ next->prev = 0;
+ }
+
+ handler = alarm->handler;
+ alarm->handler = 0;
+ if (0 < alarm->period) {
+ InsertAlarm(alarm, 0, handler);
+ }
+
+ if (AlarmQueue.head) {
+ SetTimer(AlarmQueue.head);
+ }
+
+ OSDisableScheduler();
+ OSClearContext(&exceptionContext);
+ OSSetCurrentContext(&exceptionContext);
+ handler(alarm, context);
+ OSClearContext(&exceptionContext);
+ OSSetCurrentContext(context);
+ OSEnableScheduler();
+ __OSReschedule();
+ OSLoadContext(context);
+}
+
+static asm void DecrementerExceptionHandler(register __OSException exception,
+ register OSContext* context) {
+
+ nofralloc
+ OS_EXCEPTION_SAVE_GPRS(context)
+ stwu r1, -8(r1)
+ b DecrementerExceptionCallback
+}