diff options
Diffstat (limited to 'kernel/source/timer.cpp')
-rw-r--r-- | kernel/source/timer.cpp | 117 |
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(); + + } + +} |