// Copyright (c) 2023-2026 Chris (boreddevnl) // This software is released under the GNU General Public License v3.0. See LICENSE file for details. // This header needs to maintain in any file it is present in, as per the GPL license terms. #include "rtc.h" #include "io.h" #define CMOS_ADDRESS 0x70 #define CMOS_DATA 0x71 static int updating_rtc() { outb(CMOS_ADDRESS, 0x0A); return (inb(CMOS_DATA) & 0x80); } static uint8_t get_rtc_register(int reg) { outb(CMOS_ADDRESS, reg); return inb(CMOS_DATA); } void rtc_get_datetime(int *year, int *month, int *day, int *hour, int *minute, int *second) { uint8_t century; uint8_t last_second; uint8_t last_minute; uint8_t last_hour; uint8_t last_day; uint8_t last_month; uint8_t last_year; uint8_t last_century; uint8_t registerB; while (updating_rtc()); *second = get_rtc_register(0x00); *minute = get_rtc_register(0x02); *hour = get_rtc_register(0x04); *day = get_rtc_register(0x07); *month = get_rtc_register(0x08); *year = get_rtc_register(0x09); do { last_second = *second; last_minute = *minute; last_hour = *hour; last_day = *day; last_month = *month; last_year = *year; while (updating_rtc()); *second = get_rtc_register(0x00); *minute = get_rtc_register(0x02); *hour = get_rtc_register(0x04); *day = get_rtc_register(0x07); *month = get_rtc_register(0x08); *year = get_rtc_register(0x09); } while( (last_second != *second) || (last_minute != *minute) || (last_hour != *hour) || (last_day != *day) || (last_month != *month) || (last_year != *year) ); registerB = get_rtc_register(0x0B); // Convert BCD to binary values if necessary if (!(registerB & 0x04)) { *second = (*second & 0x0F) + ((*second / 16) * 10); *minute = (*minute & 0x0F) + ((*minute / 16) * 10); *hour = ( (*hour & 0x0F) + (((*hour & 0x70) / 16) * 10) ) | (*hour & 0x80); *day = (*day & 0x0F) + ((*day / 16) * 10); *month = (*month & 0x0F) + ((*month / 16) * 10); *year = (*year & 0x0F) + ((*year / 16) * 10); } // Convert 12 hour clock to 24 hour clock if necessary if (!(registerB & 0x02) && (*hour & 0x80)) { *hour = ((*hour & 0x7F) + 12) % 24; } // Calculate full year *year += 2000; } static void set_rtc_register(int reg, uint8_t value) { outb(CMOS_ADDRESS, reg); outb(CMOS_DATA, value); } void rtc_set_datetime(int year, int month, int day, int hour, int minute, int second) { uint8_t registerB = get_rtc_register(0x0B); // Disable NMI and start update outb(CMOS_ADDRESS, 0x8B); uint8_t prev_b = inb(CMOS_DATA); outb(CMOS_DATA, prev_b | 0x80); // Set SET bit to prevent updates while writing if (year >= 2000) year -= 2000; if (!(registerB & 0x04)) { // Convert binary to BCD second = ((second / 10) << 4) | (second % 10); minute = ((minute / 10) << 4) | (minute % 10); hour = ((hour / 10) << 4) | (hour % 10); day = ((day / 10) << 4) | (day % 10); month = ((month / 10) << 4) | (month % 10); year = ((year / 10) << 4) | (year % 10); } set_rtc_register(0x00, (uint8_t)second); set_rtc_register(0x02, (uint8_t)minute); set_rtc_register(0x04, (uint8_t)hour); set_rtc_register(0x07, (uint8_t)day); set_rtc_register(0x08, (uint8_t)month); set_rtc_register(0x09, (uint8_t)year); // Re-enable updates outb(CMOS_ADDRESS, 0x8B); outb(CMOS_DATA, prev_b & ~0x80); }