summaryrefslogtreecommitdiff
path: root/kernel/source/timer.cpp
diff options
context:
space:
mode:
authorBenji Dial <benji@benjidial.net>2024-07-31 13:36:53 -0400
committerBenji Dial <benji@benjidial.net>2024-07-31 13:36:53 -0400
commitb1cf9e5dfbc8967bd7cb2a22ec1e5e521f4e0e6e (patch)
tree00837891f9b9bf232e540a6f9b3e16f2438865c3 /kernel/source/timer.cpp
parent86b343f17175ef3e1fad2197636f75770466aa7c (diff)
downloadhilbert-os-b1cf9e5dfbc8967bd7cb2a22ec1e5e521f4e0e6e.tar.gz
add clock
Diffstat (limited to 'kernel/source/timer.cpp')
-rw-r--r--kernel/source/timer.cpp117
1 files changed, 117 insertions, 0 deletions
diff --git a/kernel/source/timer.cpp b/kernel/source/timer.cpp
new file mode 100644
index 0000000..d6d37d7
--- /dev/null
+++ b/kernel/source/timer.cpp
@@ -0,0 +1,117 @@
+#include <hilbert/kernel/timer.hpp>
+
+namespace hilbert::kernel::timer {
+
+ struct sleeping_thread {
+ uint64_t sleeping_until;
+ application::thread *thread;
+ };
+
+ //sorted ascending by sleeping_until
+ static utility::list<sleeping_thread> *sleeping_threads;
+
+ uint64_t current_time;
+
+ //output is
+ // seconds | (minutes << 8) | (hours << 16) |
+ // (days << 24) | ( months << 32) | (years << 40)
+ extern "C" uint64_t get_time_from_rtc();
+
+ //index is (year % 4) * 12 + month - 1;
+ //output is days from january 1st 2000 to [month] 1st [year]
+ static uint16_t month_table[] {
+ 0, 31, 60, 91, 121, 152,
+ 182, 213, 244, 274, 305, 335,
+ 366, 397, 425, 456, 486, 517,
+ 547, 578, 609, 639, 670, 700,
+ 731, 762, 790, 821, 851, 882,
+ 912, 943, 974, 1004, 1035, 1065,
+ 1096, 1127, 1155, 1186, 1216, 1247,
+ 1277, 1308, 1339, 1369, 1400, 1430
+ };
+
+ //index is (year % 4) * 12 + month - 1;
+ //output is days in that month
+ static uint8_t month_table_2[] = {
+ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
+ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
+ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
+ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+ };
+
+ void clamp(uint8_t &value, uint8_t min, uint8_t max) {
+ if (value < min)
+ value = min;
+ else if (value > max)
+ value = max;
+ }
+
+ extern "C" void enable_rtc_interrupts();
+
+ void init_timer() {
+
+ sleeping_threads = new utility::list<sleeping_thread>();
+
+ uint64_t rtc_time = get_time_from_rtc();
+
+ uint8_t years = rtc_time >> 40;
+ uint8_t months = (rtc_time >> 32) & 0xff;
+ uint8_t days = (rtc_time >> 24) & 0xff;
+ uint8_t hours = (rtc_time >> 16) & 0xff;
+ uint8_t minutes = (rtc_time >> 8) & 0xff;
+ uint8_t seconds = rtc_time & 0xff;
+
+ uint8_t month_table_index =
+ (years % 4) * 12 + months - 1;
+
+ clamp( years, 0, 99);
+ clamp( months, 1, 12);
+ clamp( days, 1, month_table_2[month_table_index]);
+ clamp( hours, 0, 23);
+ clamp(minutes, 0, 59);
+ clamp(seconds, 0, 59);
+
+ current_time = 1461 * (years / 4);
+ current_time += month_table[month_table_index];
+ current_time += days - 1;
+ current_time *= 86400;
+ current_time += hours * 3600;
+ current_time += minutes * 60;
+ current_time += seconds;
+ current_time *= 1024;
+
+ enable_rtc_interrupts();
+
+ }
+
+ void register_sleeping_thread(
+ uint64_t sleeping_until, application::thread *thread) {
+
+ auto *after = sleeping_threads->first;
+ while (after && after->value.sleeping_until < sleeping_until)
+ after = after->next;
+
+ sleeping_threads->insert_before(
+ { .sleeping_until = sleeping_until, .thread = thread }, after);
+
+ }
+
+ extern "C" void acknowledge_rtc_interrupt();
+
+ extern "C" void on_rtc_interrupt() {
+
+ ++current_time;
+
+ while (sleeping_threads->first &&
+ sleeping_threads->first->value.sleeping_until <=
+ current_time) {
+ application::paused_threads->insert(
+ sleeping_threads->first->value.thread);
+ sleeping_threads->remove(sleeping_threads->first);
+ }
+
+ acknowledge_rtc_interrupt();
+
+ }
+
+}