// --------------------------------------------------------------------------- // RTC/CMOS clock — the standard PC real-time clock on ports 0x70/0x71. // // Reads the time registers (0x00-0x09). Values may be BCD or binary and // hours may be 12- or 24-hour depending on status register B; we normalize // to binary 24-hour. The century register (0x32) is used when it reports // a sane value, otherwise we assume 2000+. NMI is masked (bit 7 set) while // poking CMOS — this kernel has no NMI handler, so a stray NMI would fault. // --------------------------------------------------------------------------- pub struct DateTime { pub year: u16, pub month: u8, pub day: u8, pub hour: u8, pub minute: u8, pub second: u8, } /// Read one CMOS register. Writes bit 7 to the address port, which masks /// NMIs during the access. fn cmos_read(reg: u8) -> u8 { unsafe { core::arch::asm!( "out dx, al", in("dx") 0x70u16, in("al") reg | 0x80, options(nomem, nostack, preserves_flags) ); let val: u8; core::arch::asm!( "in al, dx", out("al") val, in("dx") 0x71u16, options(nomem, nostack, preserves_flags) ); val } } fn bcd_to_bin(v: u8) -> u8 { (v & 0x0F) + (v >> 4) * 10 } /// Read the current wall-clock time from the RTC. pub fn now() -> DateTime { // Wait until an in-progress update finishes (status A, UIP bit 7). while cmos_read(0x0A) & 0x80 != 0 { core::hint::spin_loop(); } let reg_b = cmos_read(0x0B); let bcd = reg_b & 0x04 == 0; // bit 2: 0 = BCD, 1 = binary let hour12 = reg_b & 0x02 == 0; // bit 1: 1 = 24-hour, 0 = 12-hour let mut second = cmos_read(0x00); let mut minute = cmos_read(0x02); let mut hour = cmos_read(0x04); let mut day = cmos_read(0x07); let mut month = cmos_read(0x08); let mut year = cmos_read(0x09); let century = cmos_read(0x32); if bcd { second = bcd_to_bin(second); minute = bcd_to_bin(minute); hour = bcd_to_bin(hour); day = bcd_to_bin(day); month = bcd_to_bin(month); year = bcd_to_bin(year); } if hour12 { let pm = hour & 0x80 != 0; // top bit of a 12-hour hour is the PM flag hour &= 0x7F; if hour == 12 { hour = 0; // 12 AM/PM are both 0 in 24-hour form } if pm { hour += 12; } } let year = { let full = century as u16 * 100 + year as u16; if (1970..=2200).contains(&full) { full } else { // Unreadable century register (QEMU returns garbage here) — // assume 2000+ for the 2-digit year. 2000 + year as u16 } }; DateTime { year, month, day, hour, minute, second, } }