OSDev.org

The Place to Start for Operating System Developers
It is currently Mon May 20, 2024 1:05 pm

All times are UTC - 6 hours




Post new topic Reply to topic  [ 1 post ] 
Author Message
 Post subject: Is this RTC code incorrect?
PostPosted: Sat Mar 06, 2021 9:50 pm 
Offline
Member
Member

Joined: Sun Jun 23, 2019 5:36 pm
Posts: 618
Location: North Dakota, United States
So, I ported the code on the wiki about reading the time from the RTC. It looks something like this:
Code:
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;
        }
    }

My code defines the following constants:
Code:
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

And here are how I define each bit:
Code:
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;
}
}

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.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 1 post ] 

All times are UTC - 6 hours


Who is online

Users browsing this forum: No registered users and 30 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group