From 3a44b8da250ffafec26a1c61cf41eeb5978f4549 Mon Sep 17 00:00:00 2001 From: Freya Murphy Date: Fri, 4 Apr 2025 00:10:16 -0400 Subject: real time clock --- build.zig | 2 + include/time.h | 63 ++++++++++++++ kernel/drivers/clock.c | 159 +++++++++++++++++++++++++++++++++++ kernel/drivers/drivers.c | 2 + kernel/include/comus/drivers/clock.h | 14 +++ kernel/kernel.c | 9 ++ lib/timetostr.c | 144 +++++++++++++++++++++++++++++++ 7 files changed, 393 insertions(+) create mode 100644 include/time.h create mode 100644 kernel/drivers/clock.c create mode 100644 kernel/include/comus/drivers/clock.h create mode 100644 lib/timetostr.c diff --git a/build.zig b/build.zig index e673d1d..487681f 100644 --- a/build.zig +++ b/build.zig @@ -30,6 +30,7 @@ const kernel_src = &[_][]const u8{ "kernel/cpu/idt.c", "kernel/cpu/idt.S", "kernel/cpu/pic.c", + "kernel/drivers/clock.c", "kernel/drivers/drivers.c", "kernel/drivers/pci.c", "kernel/drivers/tty.c", @@ -71,6 +72,7 @@ const lib_src = &[_][]const u8{ "lib/strncpy.c", "lib/strtoux.c", "lib/strtox.c", + "lib/timetostr.c", "lib/uxtoa.c", "lib/xtoa.c", }; diff --git a/include/time.h b/include/time.h new file mode 100644 index 0000000..b3b0fc1 --- /dev/null +++ b/include/time.h @@ -0,0 +1,63 @@ +/** + * @file time.h + * + * @author Freya Murphy + * + * System time structure + */ + +#ifndef TIME_H_ +#define TIME_H_ + +#include + +typedef struct { + int sec; /// Seconds [0,59] + int min; /// Minutes [0,59] + int hour; /// Hour [0,23] + int mday; /// Day of month [1,31] + int mon; /// Month of year [0,11] + int year; /// Years since 1900 + int wday; /// Day of week [0,6] (Sunday = 0) + int yday; /// Day of year [0,365] + int yn; /// Year number [0,99] + int cen; /// Century [19,20] + int leap; /// If year is a leap year (True == 1) +} time_t; + +typedef enum { + TZ_UTC = 0, + TZ_EST = -5, + TZ_EDT = -4, +} timezone_t; + +/** + * Sets the current timezone + */ +extern void set_timezone(timezone_t tz); + +/** + * Returns current time in UTC + */ +extern time_t get_utctime(void); + +/** + * Returns current time from current Timezone + */ +extern time_t get_localtime(void); + +/** + * Return the time on the system clock + */ +extern size_t get_systemtime(void); + +/** + * Converts the time into a string format + * + * @param time - the current time + * @param format - see manpage for date + * @param buf - the buffer to store it in + */ +extern void timetostr(time_t *time, char *format, char *buf, size_t n); + +#endif /* time.h */ diff --git a/kernel/drivers/clock.c b/kernel/drivers/clock.c new file mode 100644 index 0000000..80a44df --- /dev/null +++ b/kernel/drivers/clock.c @@ -0,0 +1,159 @@ +#include +#include +#include +#include + +#define CMOS_WRITE_PORT 0x70 +#define CMOS_READ_PORT 0x71 + +#define CMOS_REG_SEC 0x00 +#define CMOS_REG_MIN 0x02 +#define CMOS_REG_HOUR 0x04 +#define CMOS_REG_WDAY 0x06 +#define CMOS_REG_MDAY 0x07 +#define CMOS_REG_MON 0x08 +#define CMOS_REG_YEAR 0x09 +#define CMOS_REG_CEN 0x32 + +// Live buffers to work on data +static time_t time; +static time_t localtime; + +// Front buffers so interupts dont request data that is half done +static time_t curr_time; +static time_t curr_localtime; + +// Current set time Zone +static timezone_t curr_timezone = TZ_UTC; +static timezone_t last_timezone = TZ_UTC; + +static uint8_t cmos_read(uint8_t reg) { + uint8_t hex, ret; + + outb(CMOS_WRITE_PORT, reg); + hex = inb(CMOS_READ_PORT); + + ret = hex & 0x0F; + ret += (hex & 0xF0) / 16 * 10; + + return ret; +} + +static int mday_offset[12] = { + 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 +}; + +static int month_days[12] = { + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; + +static void update_localtime(void) { + + int change, max; + + // set localtime + localtime = time; + + // if tz is UTC, we dont need to do anythin + if (last_timezone == TZ_UTC) return; + + localtime.hour += last_timezone; + + // check if day rolled over + change = localtime.hour < 0 ? -1 : localtime.hour >= 24 ? 1 : 0; + if (!change) return; + + // roll over day + localtime.hour = (localtime.hour + 24) % 24; + localtime.wday = (localtime.wday + change + 7) % 7; + localtime.mday += change; + localtime.yday += change; + + // check if month rolled over + max = month_days[localtime.mon]; + if (localtime.leap && localtime.mon == 1) max++; + change = localtime.mday < 0 ? -1 : localtime.mday >= max ? 1 : 0; + if (!change) return; + + // roll over month + localtime.mon = (localtime.mon + change + 12) % 12; + + // check if year rolled over + max = localtime.leap ? 366 : 365; + change = localtime.yday < 0 ? -1 : localtime.yday >= max ? 1 : 0; + if (!change) return; + + // roll over year + localtime.yn += change; + + // check if cen rolled over + change = localtime.yn < 0 ? -1 : localtime.yn >= 100 ? 1 : 0; + if (!change) goto year; + + // roll over cen + localtime.cen += change; + + +year: + + localtime.year = localtime.yn + localtime.cen * 100; + localtime.leap = localtime.year % 4 == 0 && localtime.year % 100 != 0; + + if (localtime.leap && localtime.yday == -1) + localtime.yday = 365; + else if (localtime.yday == -1) + localtime.yday = 364; + else + localtime.yday = 0; + + localtime.year -= 1900; + +} + +void clock_update(void) { + time.sec = cmos_read(CMOS_REG_SEC); + time.min = cmos_read(CMOS_REG_MIN); + time.hour = cmos_read(CMOS_REG_HOUR); + time.wday = cmos_read(CMOS_REG_WDAY) - 1; + time.mday = cmos_read(CMOS_REG_MDAY); + time.mon = cmos_read(CMOS_REG_MON) - 1; + time.yn = cmos_read(CMOS_REG_YEAR); + time.cen = 20; + + time.year = time.yn + time.cen * 100; + + time.leap = time.year % 4 == 0 && time.year % 100 != 0; + + time.yday = mday_offset[time.mon] + time.mday; + + if (time.leap && time.mon > 2) + time.yday++; + + time.year -= 1900; + + update_localtime(); + + curr_time = time; + curr_localtime = localtime; +} + +void set_timezone(timezone_t tz) { + curr_timezone = tz; +} + +time_t get_utctime(void) { + return curr_time; +} + +time_t get_localtime(void) { + if (curr_timezone != last_timezone) { + last_timezone = curr_timezone; + update_localtime(); + curr_localtime = localtime; + } + return curr_localtime; +} + +size_t get_systemtime(void) { + return 0; +} diff --git a/kernel/drivers/drivers.c b/kernel/drivers/drivers.c index 4790c3a..b4c2c90 100644 --- a/kernel/drivers/drivers.c +++ b/kernel/drivers/drivers.c @@ -2,10 +2,12 @@ #include #include #include +#include void drivers_init(void) { uart_init(); tty_init(); pci_init(); + clock_update(); } diff --git a/kernel/include/comus/drivers/clock.h b/kernel/include/comus/drivers/clock.h new file mode 100644 index 0000000..35f26c6 --- /dev/null +++ b/kernel/include/comus/drivers/clock.h @@ -0,0 +1,14 @@ +/** + * @file clock.h + * + * @author Freya Murphy + * + * CMOS real time clock driver + */ + +#ifndef CLOCK_H_ +#define CLOCK_H_ + +extern void clock_update(void); + +#endif /* clock.h */ diff --git a/kernel/kernel.c b/kernel/kernel.c index 4896b38..0b411be 100644 --- a/kernel/kernel.c +++ b/kernel/kernel.c @@ -3,6 +3,8 @@ #include #include #include +#include +#include struct memory_map mmap; @@ -23,6 +25,13 @@ void main(long magic, volatile void *mboot) // initalize devices drivers_init(); + // print current time + char date[40]; + set_timezone(TZ_EDT); + time_t time = get_localtime(); + timetostr(&time, "%a %b %d %Y %H:%M:%S", date, 40); + printf("The date is: %s\n\n", date); + // halt printf("halting...\n"); } diff --git a/lib/timetostr.c b/lib/timetostr.c new file mode 100644 index 0000000..0d4e9a5 --- /dev/null +++ b/lib/timetostr.c @@ -0,0 +1,144 @@ +#include +#include + +static char* ABB_WEEKDAY[7] = { + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" +}; + +static char* FULL_WEEKDAY[7] = { + "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturady" +}; + +static char* ABB_MONTH[12] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" +}; + +static char* FULL_MONTH[12] = { + "January", "Feburary", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" +}; + +static char *write_num(unsigned int num, unsigned int pad, char *buf, size_t n) { + size_t digits = 1; + unsigned int x = num; + + while (x /= 10, x > 0) digits++; + if (pad == 0) pad = digits; + + for (size_t i = 0; i < pad; i++) { + + size_t digit; + if (i >= digits) { + digit = 0; + } else { + digit = num % 10; + num /= 10; + } + + if (pad - i - 1 >= n) continue; + buf[pad - i - 1] = '0' + digit; + + } + + if (pad > n) pad = n; + + return buf + pad; +} + +void timetostr(time_t *time, char *format, char *buf, size_t n) { + char *index = buf; + char c; + int space; + + while ( + c = *format++, + space = (buf + n) - index, + c != '\0' && space > 0 + ) { + if (c != '%') { + *index++ = c; + continue; + } else { + c = *format++; + } + + switch (c) { + case '%': + *index++ = '%'; + break; + case 'a': + index = strncpy(index, ABB_WEEKDAY[time->wday], space); + break; + case 'A': + index = strncpy(index, FULL_WEEKDAY[time->wday], space); + break; + case 'b': + case 'h': + index = strncpy(index, ABB_MONTH[time->mon], space); + break; + case 'B': + index = strncpy(index, FULL_MONTH[time->mon], space); + break; + case 'C': + index = write_num(time->cen, 0, index, space); + break; + case 'd': + index = write_num(time->mday, 2, index, space); + break; + case 'H': + index = write_num(time->hour, 2, index, space); + break; + case 'I': + index = write_num((time->hour + 12) % 12 + 1, 2, index, space); + break; + case 'j': + index = write_num(time->yday, 3, index, space); + break; + case 'm': + index = write_num(time->mon + 1, 2, index, space); + break; + case 'M': + index = write_num(time->min, 2, index, space); + break; + case 'n': + *index++ = '\n'; + break; + case 'p': + index = strncpy(index, time->hour > 11 ? "PM" : "AM", space); + break; + case 'P': + index = strncpy(index, time->hour > 11 ? "pm" : "am", space); + break; + case 'q': + index = write_num((time->mon + 3) / 3, 0, index, space); + break; + case 'S': + index = write_num(time->sec, 2, index, space); + break; + case 't': + *index++ = '\t'; + break; + case 'u': + index = write_num(((time->wday + 1 )% 7) + 1, 0, index, space); + break; + case 'w': + index = write_num(time->wday, 0, index, space); + break; + case 'y': + index = write_num(time->yn, 2, index, space); + break; + case 'Y': + index = write_num(time->year + 1900, 0, index, space); + break; + default: { + char b[3] = {'%', c, '\0'}; + index = strncpy(index, b, space); + break; + } + } + } + + if (space < 1) + buf[n - 1] = '\0'; + else + *index = '\0'; +} -- cgit v1.2.3-freya