real time clock

This commit is contained in:
Murphy 2025-04-04 00:10:16 -04:00
parent 3c2a519ee9
commit 3a44b8da25
Signed by: freya
GPG key ID: 9FBC6FFD6D2DBF17
7 changed files with 393 additions and 0 deletions
build.zig
include
kernel
drivers
include/comus/drivers
kernel.c
lib

View file

@ -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",
};

63
include/time.h Normal file
View file

@ -0,0 +1,63 @@
/**
* @file time.h
*
* @author Freya Murphy <freya@freyacat.org>
*
* System time structure
*/
#ifndef TIME_H_
#define TIME_H_
#include <stddef.h>
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 */

159
kernel/drivers/clock.c Normal file
View file

@ -0,0 +1,159 @@
#include <lib.h>
#include <time.h>
#include <comus/asm.h>
#include <comus/drivers/clock.h>
#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;
}

View file

@ -2,10 +2,12 @@
#include <comus/drivers/uart.h>
#include <comus/drivers/tty.h>
#include <comus/drivers/pci.h>
#include <comus/drivers/clock.h>
void drivers_init(void)
{
uart_init();
tty_init();
pci_init();
clock_update();
}

View file

@ -0,0 +1,14 @@
/**
* @file clock.h
*
* @author Freya Murphy <freya@freyacat.org>
*
* CMOS real time clock driver
*/
#ifndef CLOCK_H_
#define CLOCK_H_
extern void clock_update(void);
#endif /* clock.h */

View file

@ -3,6 +3,8 @@
#include <comus/mboot.h>
#include <comus/drivers.h>
#include <lib.h>
#include <stdio.h>
#include <time.h>
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");
}

144
lib/timetostr.c Normal file
View file

@ -0,0 +1,144 @@
#include <lib.h>
#include <time.h>
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';
}