#include namespace hilbert::kernel::timer { struct sleeping_thread { uint64_t sleeping_until; application::thread *thread; }; //sorted ascending by sleeping_until static utility::list *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(); 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(); } }