| git.druid.rocks | index | druid520 | kaboom | src/ | drivers/ | rtc.nsc |
src/drivers/rtc.nsc
/*
* cmos real-time clock, port 0x70 (index)/0x71 (data) -- the same
* pair every pc-compatible has had since the original at. polling
* only, no periodic-interrupt mode (register 0x0b's bit 6, unused
* here) -- a program that wants the time reads it once, same as
* every other "read a value" syscall in this kernel.
*
* one real, documented race: reading all 6 fields isn't atomic, so a
* read that straddles the clock actually ticking over (e.g. reading
* hour=23 minute=59 second=59, then the clock ticks to the next
* minute, then this reads day/month/year -- still showing yesterday)
* can come back very slightly wrong, extremely rarely. checking
* "update in progress" (status register a, bit 7) before reading
* avoids reading MID-update, which is the real failure mode (torn,
* nonsensical values, not just off-by-a-few-seconds); the remaining
* tick-over race is a known, accepted imprecision, not worth a
* read-twice-and-compare loop for what's still just a debug-grade
* clock with no periodic sync to anything real.
*/
void outb(u16 port, u8 val);
u8 inb(u16 port);
u8
rtc_read_reg(u8 reg)
{
outb((u16)0x70, reg);
return inb((u16)0x71);
}
i32
rtc_update_in_progress(void)
{
if((rtc_read_reg((u8)0x0a) & (u8)0x80) != (u8)0)
{
return 1;
}
return 0;
}
u64
rtc_bcd_to_bin(u8 v)
{
return (u64)(v & (u8)0x0f) + (u64)((v >> (u8)4) & (u8)0x0f) * (u64)10;
}
/* fills buf (room for 6 u64s = 48 bytes) with second, minute, hour,
* day, month, year (full, e.g. 2026 -- assumes the 2000s, same as
* every other from-scratch rtc reader that doesn't bother with the
* century register). buf is word-aligned by construction (a fresh
* caller-side u64[6] or equivalent), so these are plain aligned
* 8-byte stores, no packed-word technique needed. */
global void
rtc_read_datetime(ptr buf)
{
u8 sec;
u8 min;
u8 hour;
u8 day;
u8 month;
u8 year;
u8 status_b;
i32 binary_mode;
while(rtc_update_in_progress() == 1)
{
}
sec = rtc_read_reg((u8)0x00);
min = rtc_read_reg((u8)0x02);
hour = rtc_read_reg((u8)0x04);
day = rtc_read_reg((u8)0x07);
month = rtc_read_reg((u8)0x08);
year = rtc_read_reg((u8)0x09);
status_b = rtc_read_reg((u8)0x0b);
binary_mode = 0;
if((status_b & (u8)0x04) != (u8)0)
{
binary_mode = 1;
}
if(binary_mode == 1)
{
*buf = (i64)(u64)sec;
*(buf + 8) = (i64)(u64)min;
*(buf + 16) = (i64)(u64)hour;
*(buf + 24) = (i64)(u64)day;
*(buf + 32) = (i64)(u64)month;
*(buf + 40) = (i64)((u64)2000 + (u64)year);
}
else
{
*buf = (i64)rtc_bcd_to_bin(sec);
*(buf + 8) = (i64)rtc_bcd_to_bin(min);
*(buf + 16) = (i64)rtc_bcd_to_bin(hour);
*(buf + 24) = (i64)rtc_bcd_to_bin(day);
*(buf + 32) = (i64)rtc_bcd_to_bin(month);
*(buf + 40) = (i64)((u64)2000 + rtc_bcd_to_bin(year));
}
}