Is this RTC code incorrect?
Posted: Sat Mar 06, 2021 9:50 pm
So, I ported the code on the wiki about reading the time from the RTC. It looks something like this:
My code defines the following constants:
And here are how I define each bit:
Part of this code was taken from the wiki and part of it was taken from Ralf Brown's Interrupt List. When this code runs, I get weird dates that aren't valid, like 2021-33-62. So is this a bug in QEMU (I'm having QEMU set the RTC time to the host time) or a bug with my code? (I'm using "-rtc base=utc,clock=host" with QEMU.)
Edit: So I think I just realized the problem -- I forgot that BCD was only being used if the DATMD bit was clear. Whoops.
Code: Select all
fn read(index: u8) -> u8 {
let idx = index | NO_NMI;
unsafe {
outb(idx, IDX);
inb(DATA)
}
}
fn write(index: u8, val: u8) {
let idx = index | NO_NMI;
unsafe {
outb(idx, IDX);
outb(val, DATA);
}
}
fn mask(index: u8, off: u8, on: u8) {
let index = index | NO_NMI;
unsafe {
outb(index, IDX);
let val = inb(DATA);
outb((val & !off) | on, DATA);
}
}
// ...
// Initialize RTC
without_interrupts(|| {
write(STTSA, 0x26);
mask(STTSB, !(1 << 0), 1 << 1);
let _ = read(STTSC);
let _ = read(STTSD);
let prev = read(STTSB);
write(STTSB, prev | 0x40);
});
// Read the time from the RTC
loop {
lsec = second;
lmin = minute;
lhr = hour;
lday = day;
lmo = month;
lyr = year;
lcent = century;
loop {
if !StatusA::from_bits_truncate(read(STTSA)).contains(StatusA::UIP) {
break;
}
}
second = read(SECS) as u128;
minute = read(MINS) as u128;
hour = read(HRS) as u128;
day = read(DAYMO) as u128;
month = read(MON) as u128;
year = read(YR) as u128;
century = read(CENT) as u128;
if (lsec != second)
|| (lmin != minute)
|| (lhr != hour)
|| (lday != day)
|| (lmo != month)
|| (lyr != year)
|| (lcent != century)
{
break;
}
}
let sttsb = StatusB::from_bits(read(STTSB)).unwrap();
if sttsb.contains(StatusB::DATMD) {
second = (second & 0x0F) + ((second / 16) * 10);
minute = (minute & 0x0F) + ((minute / 16) * 10);
hour = ((hour & 0x0F) + (((hour & 0x70) / 16) * 10)) | (hour & 0x80);
day = (day & 0x0F) + ((day / 16) * 10);
month = (month & 0x0F) + ((month / 16) * 10);
year = (year & 0x0F) + ((year / 16) * 10);
century = (century & 0x0F) + ((century / 16) * 10);
if sttsb.contains(StatusB::HR24) && hour.get_bit(7) {
hour = ((hour & 0x7F) + 12) % 24;
}
}
if century * 100 == CURYR {
year += century * 100;
} else {
year += (CURYR / 100) * 100;
if year < CURYR {
year += 100;
}
}
Code: Select all
const IDX: u16 = 0x0070; // Index port
const DATA: u16 = 0x0071; // Data port
const NO_NMI: u8 = 0x80;
const SECS: u8 = 0x00; // Seconds
const SECSALRM: u8 = 0x01;// Seconds alarm
const MINS: u8 = 0x02; // Minutes
const MINSALRM: u8 = 0x03; // Minutes alarm
const HRS: u8 = 0x04; // Hours
const HRSALRM: u8 = 0x05; // Hours alarm
const DAYWK: u8 = 0x06; // Day of week
const DAYMO: u8 = 0x07; // Day of month
const MON: u8 = 0x08; // Month
const YR: u8 = 0x09; // Year
const STTSA: u8 = 0x0A; // Status A
const STTSB: u8 = 0x0B; // Status B
const STTSC: u8 = 0x0C; // Status C
const STTSD: u8 = 0x0D; // Status D
const RST: u8 = 0x0F; // Reset
const FLP_DRV_TYP: u8 = 0x10; // Floppy disk drive type
// Other RTC registers that are probably not relevant
//...
const CURYR: u128 = 2021; // Current year
Code: Select all
bitflags! {
struct StatusA: u8 {
/// Update in progress
const UIP = 0x80;
}
}
bitflags! {
struct StatusB: u8 {
/// enable clock setting by freezing updates
const CLKSET = 1 << 7;
/// enable periodic interrupt
const PIE = 1 << 6;
/// enable alarm interrupt
const AIE = 1 << 5;
/// enable update-ended interrupt
const UEIE = 1 << 4;
/// enable square wave output
const SQWOE = 1 << 3;
/// Data Mode - 0: BCD, 1: Binary
const DATMD = 1 << 2;
/// 24/12 hour selection - 1 enables 24 hour mode
const HR24 = 1 << 1;
/// Daylight Savings Enable
const DSTE = 1 << 0;
}
}
bitflags! {
struct StatusC: u8 {
/// Interrupt request flag =1 when any or all of bits 6-4 are 1 and appropriate enables
/// (Register B) are set to 1. Generates IRQ 8 when triggered.
const IRQ = 1 << 7;
/// Periodic Interrupt flag
const PIRQ = 1 << 6;
/// Alarm Interrupt flag
const AI = 1 << 5;
/// Update-Ended Interrupt Flag
const UEI = 1 << 4;
}
}
bitflags! {
struct StatusD: u8 {
/// Valid RAM - 1 indicates battery power good, 0 if dead or disconnected.
const VRAM = 1 << 7;
}
}
Edit: So I think I just realized the problem -- I forgot that BCD was only being used if the DATMD bit was clear. Whoops.