summaryrefslogtreecommitdiffstats
path: root/rsnix/src/rtc.rs
diff options
context:
space:
mode:
Diffstat (limited to 'rsnix/src/rtc.rs')
-rw-r--r--rsnix/src/rtc.rs101
1 files changed, 101 insertions, 0 deletions
diff --git a/rsnix/src/rtc.rs b/rsnix/src/rtc.rs
new file mode 100644
index 0000000..535f32e
--- /dev/null
+++ b/rsnix/src/rtc.rs
@@ -0,0 +1,101 @@
1// ---------------------------------------------------------------------------
2// RTC/CMOS clock — the standard PC real-time clock on ports 0x70/0x71.
3//
4// Reads the time registers (0x00-0x09). Values may be BCD or binary and
5// hours may be 12- or 24-hour depending on status register B; we normalize
6// to binary 24-hour. The century register (0x32) is used when it reports
7// a sane value, otherwise we assume 2000+. NMI is masked (bit 7 set) while
8// poking CMOS — this kernel has no NMI handler, so a stray NMI would fault.
9// ---------------------------------------------------------------------------
10
11pub struct DateTime {
12 pub year: u16,
13 pub month: u8,
14 pub day: u8,
15 pub hour: u8,
16 pub minute: u8,
17 pub second: u8,
18}
19
20/// Read one CMOS register. Writes bit 7 to the address port, which masks
21/// NMIs during the access.
22fn cmos_read(reg: u8) -> u8 {
23 unsafe {
24 core::arch::asm!(
25 "out dx, al",
26 in("dx") 0x70u16,
27 in("al") reg | 0x80,
28 options(nomem, nostack, preserves_flags)
29 );
30 let val: u8;
31 core::arch::asm!(
32 "in al, dx",
33 out("al") val,
34 in("dx") 0x71u16,
35 options(nomem, nostack, preserves_flags)
36 );
37 val
38 }
39}
40
41fn bcd_to_bin(v: u8) -> u8 {
42 (v & 0x0F) + (v >> 4) * 10
43}
44
45/// Read the current wall-clock time from the RTC.
46pub fn now() -> DateTime {
47 // Wait until an in-progress update finishes (status A, UIP bit 7).
48 while cmos_read(0x0A) & 0x80 != 0 {
49 core::hint::spin_loop();
50 }
51
52 let reg_b = cmos_read(0x0B);
53 let bcd = reg_b & 0x04 == 0; // bit 2: 0 = BCD, 1 = binary
54 let hour12 = reg_b & 0x02 == 0; // bit 1: 1 = 24-hour, 0 = 12-hour
55
56 let mut second = cmos_read(0x00);
57 let mut minute = cmos_read(0x02);
58 let mut hour = cmos_read(0x04);
59 let mut day = cmos_read(0x07);
60 let mut month = cmos_read(0x08);
61 let mut year = cmos_read(0x09);
62 let century = cmos_read(0x32);
63
64 if bcd {
65 second = bcd_to_bin(second);
66 minute = bcd_to_bin(minute);
67 hour = bcd_to_bin(hour);
68 day = bcd_to_bin(day);
69 month = bcd_to_bin(month);
70 year = bcd_to_bin(year);
71 }
72 if hour12 {
73 let pm = hour & 0x80 != 0; // top bit of a 12-hour hour is the PM flag
74 hour &= 0x7F;
75 if hour == 12 {
76 hour = 0; // 12 AM/PM are both 0 in 24-hour form
77 }
78 if pm {
79 hour += 12;
80 }
81 }
82 let year = {
83 let full = century as u16 * 100 + year as u16;
84 if (1970..=2200).contains(&full) {
85 full
86 } else {
87 // Unreadable century register (QEMU returns garbage here) —
88 // assume 2000+ for the 2-digit year.
89 2000 + year as u16
90 }
91 };
92
93 DateTime {
94 year,
95 month,
96 day,
97 hour,
98 minute,
99 second,
100 }
101}