X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=src%2Fclock.c;h=d88515c45c482153144365c90b099c465b5076e8;hb=b29200c51beb1e75e7f844a4093da05b8394ad5e;hp=8c4f301b4475443b558f8600e020692d801e2894;hpb=6c78122091367f87ea2338e38b175d586cee1389;p=seabios.git diff --git a/src/clock.c b/src/clock.c index 8c4f301..d88515c 100644 --- a/src/clock.c +++ b/src/clock.c @@ -1,51 +1,331 @@ // 16bit code to handle system clocks. // -// Copyright (C) 2008 Kevin O'Connor +// Copyright (C) 2008-2010 Kevin O'Connor // Copyright (C) 2002 MandrakeSoft S.A. // -// This file may be distributed under the terms of the GNU GPLv3 license. +// This file may be distributed under the terms of the GNU LGPLv3 license. -#include "biosvar.h" // struct bregs +#include "biosvar.h" // SET_BDA #include "util.h" // debug_enter #include "disk.h" // floppy_tick #include "cmos.h" // inb_cmos +#include "pic.h" // eoi_pic1 +#include "bregs.h" // struct bregs +#include "biosvar.h" // GET_GLOBAL +#include "usb-hid.h" // usb_check_event + +// RTC register flags +#define RTC_A_UIP 0x80 + +#define RTC_B_SET 0x80 +#define RTC_B_PIE 0x40 +#define RTC_B_AIE 0x20 +#define RTC_B_UIE 0x10 +#define RTC_B_BIN 0x04 +#define RTC_B_24HR 0x02 +#define RTC_B_DSE 0x01 + + +// Bits for PORT_PS2_CTRLB +#define PPCB_T2GATE (1<<0) +#define PPCB_SPKR (1<<1) +#define PPCB_T2OUT (1<<5) + +// Bits for PORT_PIT_MODE +#define PM_SEL_TIMER0 (0<<6) +#define PM_SEL_TIMER1 (1<<6) +#define PM_SEL_TIMER2 (2<<6) +#define PM_SEL_READBACK (3<<6) +#define PM_ACCESS_LATCH (0<<4) +#define PM_ACCESS_LOBYTE (1<<4) +#define PM_ACCESS_HIBYTE (2<<4) +#define PM_ACCESS_WORD (3<<4) +#define PM_MODE0 (0<<1) +#define PM_MODE1 (1<<1) +#define PM_MODE2 (2<<1) +#define PM_MODE3 (3<<1) +#define PM_MODE4 (4<<1) +#define PM_MODE5 (5<<1) +#define PM_CNT_BINARY (0<<0) +#define PM_CNT_BCD (1<<0) +#define PM_READ_COUNTER0 (1<<1) +#define PM_READ_COUNTER1 (1<<2) +#define PM_READ_COUNTER2 (1<<3) +#define PM_READ_STATUSVALUE (0<<4) +#define PM_READ_VALUE (1<<4) +#define PM_READ_STATUS (2<<4) + + +/**************************************************************** + * TSC timer + ****************************************************************/ + +#define CALIBRATE_COUNT 0x800 // Approx 1.7ms + +u32 cpu_khz VAR16VISIBLE; +u8 no_tsc VAR16VISIBLE; -#define DEBUGF1(fmt, args...) bprintf(0, fmt , ##args) -#define DEBUGF(fmt, args...) +static void +calibrate_tsc(void) +{ + u32 eax, ebx, ecx, edx, cpuid_features = 0; + cpuid(0, &eax, &ebx, &ecx, &edx); + if (eax > 0) + cpuid(1, &eax, &ebx, &ecx, &cpuid_features); + + if (!(cpuid_features & CPUID_TSC)) { + SET_GLOBAL(no_tsc, 1); + SET_GLOBAL(cpu_khz, PIT_TICK_RATE / 1000); + dprintf(3, "386/486 class CPU. Using TSC emulation\n"); + return; + } + + // Setup "timer2" +#if 0 + u8 orig = inb(PORT_PS2_CTRLB); + outb((orig & ~PPCB_SPKR) | PPCB_T2GATE, PORT_PS2_CTRLB); + /* binary, mode 0, LSB/MSB, Ch 2 */ + outb(PM_SEL_TIMER2|PM_ACCESS_WORD|PM_MODE0|PM_CNT_BINARY, PORT_PIT_MODE); + /* LSB of ticks */ + outb(CALIBRATE_COUNT & 0xFF, PORT_PIT_COUNTER2); + /* MSB of ticks */ + outb(CALIBRATE_COUNT >> 8, PORT_PIT_COUNTER2); + + u64 start = rdtscll(); + while ((inb(PORT_PS2_CTRLB) & PPCB_T2OUT) == 0) + ; + u64 end = rdtscll(); + + // Restore PORT_PS2_CTRLB + outb(orig, PORT_PS2_CTRLB); + + // Store calibrated cpu khz. + u64 diff = end - start; + dprintf(6, "tsc calibrate start=%u end=%u diff=%u\n" + , (u32)start, (u32)end, (u32)diff); + u32 hz = diff * PIT_TICK_RATE / CALIBRATE_COUNT; + SET_GLOBAL(cpu_khz, hz / 1000); +#else + u32 hz = 800 * 1000000; + SET_GLOBAL(cpu_khz, hz / 1000); +#endif + + dprintf(1, "CPU Mhz=%u\n", hz / 1000000); +} + +static u64 +emulate_tsc(void) +{ + int cnt, d; + u16 ebda_seg = get_ebda_seg(); + u64 ret; + /* read timer 0 current count */ + ret = GET_EBDA2(ebda_seg, tsc_8254); + /* readback mode has slightly shifted registers, works on all 8254, readback PIT0 latch */ + outb(PM_SEL_READBACK | PM_READ_VALUE | PM_READ_COUNTER0, PORT_PIT_MODE); + cnt = (inb(PORT_PIT_COUNTER0) | (inb(PORT_PIT_COUNTER0) << 8)); + d = GET_EBDA2(ebda_seg, last_tsc_8254) - cnt; + /* Determine the ticks count from last invocation of this function */ + ret += (d > 0) ? d : (PIT_TICK_INTERVAL + d); + SET_EBDA2(ebda_seg, last_tsc_8254, cnt); + SET_EBDA2(ebda_seg, tsc_8254, ret); + return ret; +} + +static u64 +get_tsc(void) +{ + if (unlikely(GET_GLOBAL(no_tsc))) + return emulate_tsc(); + return rdtscll(); +} + +int +check_tsc(u64 end) +{ + return (s64)(get_tsc() - end) > 0; +} static void -init_rtc() +tscdelay(u64 diff) { - outb_cmos(0x26, CMOS_STATUS_A); - outb_cmos(0x02, CMOS_STATUS_B); - inb_cmos(CMOS_STATUS_C); - inb_cmos(CMOS_STATUS_D); + u64 start = get_tsc(); + u64 end = start + diff; + while (!check_tsc(end)) + cpu_relax(); +} + +static void +tscsleep(u64 diff) +{ + u64 start = get_tsc(); + u64 end = start + diff; + while (!check_tsc(end)) + yield(); +} + +void ndelay(u32 count) { + tscdelay(count * GET_GLOBAL(cpu_khz) / 1000000); +} +void udelay(u32 count) { + tscdelay(count * GET_GLOBAL(cpu_khz) / 1000); +} +void mdelay(u32 count) { + tscdelay(count * GET_GLOBAL(cpu_khz)); +} + +void nsleep(u32 count) { + tscsleep(count * GET_GLOBAL(cpu_khz) / 1000000); +} +void usleep(u32 count) { + tscsleep(count * GET_GLOBAL(cpu_khz) / 1000); +} +void msleep(u32 count) { + tscsleep(count * GET_GLOBAL(cpu_khz)); +} + +// Return the TSC value that is 'msecs' time in the future. +u64 +calc_future_tsc(u32 msecs) +{ + u32 khz = GET_GLOBAL(cpu_khz); + return get_tsc() + ((u64)khz * msecs); +} +u64 +calc_future_tsc_usec(u32 usecs) +{ + u32 khz = GET_GLOBAL(cpu_khz); + return get_tsc() + ((u64)(khz/1000) * usecs); } -static u8 -rtc_updating() + +/**************************************************************** + * Init + ****************************************************************/ + +static int +rtc_updating(void) { // This function checks to see if the update-in-progress bit // is set in CMOS Status Register A. If not, it returns 0. // If it is set, it tries to wait until there is a transition - // to 0, and will return 0 if such a transition occurs. A 1 + // to 0, and will return 0 if such a transition occurs. A -1 // is returned only after timing out. The maximum period - // that this bit should be set is constrained to 244useconds. - // The count I use below guarantees coverage or more than - // this time, with any reasonable IPS setting. - - u16 count = 25000; - while (--count != 0) { - if ( (inb_cmos(CMOS_STATUS_A) & 0x80) == 0 ) + // that this bit should be set is constrained to (1984+244) + // useconds, but we wait for longer just to be sure. + + if ((inb_cmos(CMOS_STATUS_A) & RTC_A_UIP) == 0) + return 0; + u64 end = calc_future_tsc(15); + for (;;) { + if ((inb_cmos(CMOS_STATUS_A) & RTC_A_UIP) == 0) return 0; + if (check_tsc(end)) + // update-in-progress never transitioned to 0 + return -1; + yield(); } - return 1; // update-in-progress never transitioned to 0 +} + +static void +pit_setup(void) +{ + // timer0: binary count, 16bit count, mode 2 + outb(PM_SEL_TIMER0|PM_ACCESS_WORD|PM_MODE2|PM_CNT_BINARY, PORT_PIT_MODE); + // maximum count of 0000H = 18.2Hz + outb(0x0, PORT_PIT_COUNTER0); + outb(0x0, PORT_PIT_COUNTER0); +} + +static void +init_rtc(void) +{ + outb_cmos(0x26, CMOS_STATUS_A); // 32,768Khz src, 976.5625us updates + u8 regB = inb_cmos(CMOS_STATUS_B); + outb_cmos((regB & RTC_B_DSE) | RTC_B_24HR, CMOS_STATUS_B); + inb_cmos(CMOS_STATUS_C); + inb_cmos(CMOS_STATUS_D); +} + +static u32 +bcd2bin(u8 val) +{ + return (val & 0xf) + ((val >> 4) * 10); +} + +void +timer_setup(void) +{ + dprintf(3, "init timer\n"); +#if 1 + calibrate_tsc(); +#endif + dprintf(3, "init timer: 01\n"); + pit_setup(); + dprintf(3, "init timer: 02\n"); + + init_rtc(); + dprintf(3, "init timer: 03\n"); + rtc_updating(); + dprintf(3, "init timer: 04\n"); + u32 seconds = bcd2bin(inb_cmos(CMOS_RTC_SECONDS)); + dprintf(3, "init timer: 05\n"); + u32 minutes = bcd2bin(inb_cmos(CMOS_RTC_MINUTES)); + dprintf(3, "init timer: 06\n"); + u32 hours = bcd2bin(inb_cmos(CMOS_RTC_HOURS)); + dprintf(3, "init timer: 07\n"); + u32 ticks = (hours * 60 + minutes) * 60 + seconds; + dprintf(3, "init timer: 08\n"); + ticks = ((u64)ticks * PIT_TICK_RATE) / PIT_TICK_INTERVAL; + dprintf(3, "init timer: 09\n"); + SET_BDA(timer_counter, ticks); + dprintf(3, "init timer: 10\n"); + + enable_hwirq(0, FUNC16(entry_08)); + dprintf(3, "init timer: 11\n"); + enable_hwirq(8, FUNC16(entry_70)); + dprintf(3, "init timer: 12\n"); +} + + +/**************************************************************** + * Standard clock functions + ****************************************************************/ + +#define TICKS_PER_DAY (u32)((u64)60*60*24*PIT_TICK_RATE / PIT_TICK_INTERVAL) + +// Calculate the timer value at 'count' number of full timer ticks in +// the future. +u32 +calc_future_timer_ticks(u32 count) +{ + return (GET_BDA(timer_counter) + count + 1) % TICKS_PER_DAY; +} + +// Return the timer value that is 'msecs' time in the future. +u32 +calc_future_timer(u32 msecs) +{ + if (!msecs) + return GET_BDA(timer_counter); + u32 kticks = DIV_ROUND_UP((u64)msecs * PIT_TICK_RATE, PIT_TICK_INTERVAL); + u32 ticks = DIV_ROUND_UP(kticks, 1000); + return calc_future_timer_ticks(ticks); +} + +// Check if the given timer value has passed. +int +check_timer(u32 end) +{ + return (((GET_BDA(timer_counter) + TICKS_PER_DAY - end) % TICKS_PER_DAY) + < (TICKS_PER_DAY/2)); } // get current clock count static void handle_1a00(struct bregs *regs) { + yield(); u32 ticks = GET_BDA(timer_counter); regs->cx = ticks >> 16; regs->dx = ticks; @@ -61,6 +341,7 @@ handle_1a01(struct bregs *regs) u32 ticks = (regs->cx << 16) | regs->dx; SET_BDA(timer_counter, ticks); SET_BDA(timer_rollover, 0); // reset flag + // XXX - should use set_code_success()? regs->ah = 0; set_success(regs); } @@ -70,14 +351,14 @@ static void handle_1a02(struct bregs *regs) { if (rtc_updating()) { - set_fail(regs); + set_invalid(regs); return; } regs->dh = inb_cmos(CMOS_RTC_SECONDS); regs->cl = inb_cmos(CMOS_RTC_MINUTES); regs->ch = inb_cmos(CMOS_RTC_HOURS); - regs->dl = inb_cmos(CMOS_STATUS_B) & 0x01; + regs->dl = inb_cmos(CMOS_STATUS_B) & RTC_B_DSE; regs->ah = 0; regs->al = regs->ch; set_success(regs); @@ -105,7 +386,8 @@ handle_1a03(struct bregs *regs) outb_cmos(regs->cl, CMOS_RTC_MINUTES); outb_cmos(regs->ch, CMOS_RTC_HOURS); // Set Daylight Savings time enabled bit to requested value - u8 val8 = (inb_cmos(CMOS_STATUS_B) & 0x60) | 0x02 | (regs->dl & 0x01); + u8 val8 = ((inb_cmos(CMOS_STATUS_B) & (RTC_B_PIE|RTC_B_AIE)) + | RTC_B_24HR | (regs->dl & RTC_B_DSE)); outb_cmos(val8, CMOS_STATUS_B); regs->ah = 0; regs->al = val8; // val last written to Reg B @@ -118,13 +400,20 @@ handle_1a04(struct bregs *regs) { regs->ah = 0; if (rtc_updating()) { - set_fail(regs); + set_invalid(regs); return; } regs->cl = inb_cmos(CMOS_RTC_YEAR); regs->dh = inb_cmos(CMOS_RTC_MONTH); regs->dl = inb_cmos(CMOS_RTC_DAY_MONTH); - regs->ch = inb_cmos(CMOS_CENTURY); + if (CONFIG_COREBOOT) { + if (regs->cl > 0x80) + regs->ch = 0x19; + else + regs->ch = 0x20; + } else { + regs->ch = inb_cmos(CMOS_CENTURY); + } regs->al = regs->ch; set_success(regs); } @@ -145,14 +434,16 @@ handle_1a05(struct bregs *regs) // My assumption: RegB = (RegB & 01111111b) if (rtc_updating()) { init_rtc(); - set_fail(regs); + set_invalid(regs); return; } outb_cmos(regs->cl, CMOS_RTC_YEAR); outb_cmos(regs->dh, CMOS_RTC_MONTH); outb_cmos(regs->dl, CMOS_RTC_DAY_MONTH); - outb_cmos(regs->ch, CMOS_CENTURY); - u8 val8 = inb_cmos(CMOS_STATUS_B) & 0x7f; // clear halt-clock bit + if (!CONFIG_COREBOOT) + outb_cmos(regs->ch, CMOS_CENTURY); + // clear halt-clock bit + u8 val8 = inb_cmos(CMOS_STATUS_B) & ~RTC_B_SET; outb_cmos(val8, CMOS_STATUS_B); regs->ah = 0; regs->al = val8; // AL = val last written to Reg B @@ -175,9 +466,9 @@ handle_1a06(struct bregs *regs) // My assumption: RegB = ((RegB & 01111111b) | 00100000b) u8 val8 = inb_cmos(CMOS_STATUS_B); // Get Status Reg B regs->ax = 0; - if (val8 & 0x20) { + if (val8 & RTC_B_AIE) { // Alarm interrupt enabled already - set_fail(regs); + set_invalid(regs); return; } if (rtc_updating()) { @@ -187,9 +478,8 @@ handle_1a06(struct bregs *regs) outb_cmos(regs->dh, CMOS_RTC_SECONDS_ALARM); outb_cmos(regs->cl, CMOS_RTC_MINUTES_ALARM); outb_cmos(regs->ch, CMOS_RTC_HOURS_ALARM); - outb(inb(PORT_PIC2_DATA) & ~PIC2_IRQ8, PORT_PIC2_DATA); // enable IRQ 8 // enable Status Reg B alarm bit, clear halt clock bit - outb_cmos((val8 & 0x7f) | 0x20, CMOS_STATUS_B); + outb_cmos((val8 & ~RTC_B_SET) | RTC_B_AIE, CMOS_STATUS_B); set_success(regs); } @@ -209,31 +499,24 @@ handle_1a07(struct bregs *regs) // My assumption: RegB = (RegB & 01010111b) u8 val8 = inb_cmos(CMOS_STATUS_B); // Get Status Reg B // clear clock-halt bit, disable alarm bit - outb_cmos(val8 & 0x57, CMOS_STATUS_B); // disable alarm bit + outb_cmos(val8 & ~(RTC_B_SET|RTC_B_AIE), CMOS_STATUS_B); regs->ah = 0; regs->al = val8; // val last written to Reg B set_success(regs); } -static void -handle_1ab1(struct bregs *regs) -{ - // XXX - pcibios stuff - set_fail(regs); -} - // Unsupported static void handle_1aXX(struct bregs *regs) { - set_fail(regs); + set_unimplemented(regs); } // INT 1Ah Time-of-day Service Entry Point void VISIBLE16 handle_1a(struct bregs *regs) { - //debug_enter(regs); + debug_enter(regs, DEBUG_HDL_1a); switch (regs->ah) { case 0x00: handle_1a00(regs); break; case 0x01: handle_1a01(regs); break; @@ -248,25 +531,18 @@ handle_1a(struct bregs *regs) } } -// User Timer Tick -void VISIBLE16 -handle_1c(struct bregs *regs) -{ - //debug_enter(regs); -} - // INT 08h System Timer ISR Entry Point void VISIBLE16 -handle_08(struct bregs *regs) +handle_08(void) { -// debug_isr(regs); + debug_isr(DEBUG_ISR_08); floppy_tick(); u32 counter = GET_BDA(timer_counter); counter++; // compare to one days worth of timer ticks at 18.2 hz - if (counter >= 0x001800B0) { + if (counter >= TICKS_PER_DAY) { // there has been a midnight rollover at this point counter = 0; SET_BDA(timer_rollover, GET_BDA(timer_rollover) + 1); @@ -274,55 +550,113 @@ handle_08(struct bregs *regs) SET_BDA(timer_counter, counter); + usb_check_event(); + // chain to user timer tick INT #0x1c - struct bregs br; - memset(&br, 0, sizeof(br)); - call16_int(0x1c, &br); + u32 eax=0, flags; + call16_simpint(0x1c, &eax, &flags); - eoi_master_pic(); + eoi_pic1(); +} + + +/**************************************************************** + * Periodic timer + ****************************************************************/ + +void +useRTC(void) +{ + u16 ebda_seg = get_ebda_seg(); + int count = GET_EBDA2(ebda_seg, RTCusers); + SET_EBDA2(ebda_seg, RTCusers, count+1); + if (count) + return; + // Turn on the Periodic Interrupt timer + u8 bRegister = inb_cmos(CMOS_STATUS_B); + outb_cmos(bRegister | RTC_B_PIE, CMOS_STATUS_B); +} + +void +releaseRTC(void) +{ + u16 ebda_seg = get_ebda_seg(); + int count = GET_EBDA2(ebda_seg, RTCusers); + SET_EBDA2(ebda_seg, RTCusers, count-1); + if (count != 1) + return; + // Clear the Periodic Interrupt. + u8 bRegister = inb_cmos(CMOS_STATUS_B); + outb_cmos(bRegister & ~RTC_B_PIE, CMOS_STATUS_B); +} + +static int +set_usertimer(u32 usecs, u16 seg, u16 offset) +{ + if (GET_BDA(rtc_wait_flag) & RWS_WAIT_PENDING) + return -1; + + // Interval not already set. + SET_BDA(rtc_wait_flag, RWS_WAIT_PENDING); // Set status byte. + SET_BDA(user_wait_complete_flag, SEGOFF(seg, offset)); + SET_BDA(user_wait_timeout, usecs); + useRTC(); + return 0; +} + +static void +clear_usertimer(void) +{ + if (!(GET_BDA(rtc_wait_flag) & RWS_WAIT_PENDING)) + return; + // Turn off status byte. + SET_BDA(rtc_wait_flag, 0); + releaseRTC(); +} + +#define RET_ECLOCKINUSE 0x83 + +// Wait for CX:DX microseconds +void +handle_1586(struct bregs *regs) +{ + // Use the rtc to wait for the specified time. + u8 statusflag = 0; + u32 count = (regs->cx << 16) | regs->dx; + int ret = set_usertimer(count, GET_SEG(SS), (u32)&statusflag); + if (ret) { + set_code_invalid(regs, RET_ECLOCKINUSE); + return; + } + while (!statusflag) + wait_irq(); + set_success(regs); } // Set Interval requested. static void handle_158300(struct bregs *regs) { - if (GET_BDA(rtc_wait_flag) & RWS_WAIT_PENDING) { + int ret = set_usertimer((regs->cx << 16) | regs->dx, regs->es, regs->bx); + if (ret) // Interval already set. - DEBUGF("int15: Func 83h, failed, already waiting.\n" ); - set_code_fail(regs, RET_EUNSUPPORTED); - } - // Interval not already set. - SET_BDA(rtc_wait_flag, RWS_WAIT_PENDING); // Set status byte. - u32 v = (regs->es << 16) | regs->bx; - SET_BDA(ptr_user_wait_complete_flag, v); - v = (regs->dx << 16) | regs->cx; - SET_BDA(user_wait_timeout, v); - - // Unmask IRQ8 so INT70 will get through. - u8 irqDisable = inb(PORT_PIC2_DATA); - outb(irqDisable & ~PIC2_IRQ8, PORT_PIC2_DATA); - // Turn on the Periodic Interrupt timer - u8 bRegister = inb_cmos(CMOS_STATUS_B); - outb_cmos(CMOS_STATUS_B, bRegister | CSB_EN_ALARM_IRQ); - - set_success(regs); // XXX - no set ah? + set_code_invalid(regs, RET_EUNSUPPORTED); + else + set_success(regs); } // Clear interval requested static void handle_158301(struct bregs *regs) { - SET_BDA(rtc_wait_flag, 0); // Clear status byte - // Turn off the Periodic Interrupt timer - u8 bRegister = inb_cmos(CMOS_STATUS_B); - outb_cmos(CMOS_STATUS_B, bRegister & ~CSB_EN_ALARM_IRQ); - set_success(regs); // XXX - no set ah? + clear_usertimer(); + set_success(regs); } static void handle_1583XX(struct bregs *regs) { - set_code_fail(regs, RET_EUNSUPPORTED); + set_code_unimplemented(regs, RET_EUNSUPPORTED); regs->al--; } @@ -336,52 +670,52 @@ handle_1583(struct bregs *regs) } } +#define USEC_PER_RTC DIV_ROUND_CLOSEST(1000000, 1024) + // int70h: IRQ8 - CMOS RTC void VISIBLE16 -handle_70(struct bregs *regs) +handle_70(void) { - debug_isr(regs); + debug_isr(DEBUG_ISR_70); // Check which modes are enabled and have occurred. u8 registerB = inb_cmos(CMOS_STATUS_B); u8 registerC = inb_cmos(CMOS_STATUS_C); - if (!(registerB & 0x60)) + if (!(registerB & (RTC_B_PIE|RTC_B_AIE))) goto done; - if (registerC & 0x20) { + if (registerC & RTC_B_AIE) { // Handle Alarm Interrupt. - struct bregs br; - memset(&br, 0, sizeof(br)); - call16_int(0x4a, &br); + u32 eax=0, flags; + call16_simpint(0x4a, &eax, &flags); } - if (!(registerC & 0x40)) + if (!(registerC & RTC_B_PIE)) goto done; // Handle Periodic Interrupt. + check_preempt(); + if (!GET_BDA(rtc_wait_flag)) goto done; // Wait Interval (Int 15, AH=83) active. u32 time = GET_BDA(user_wait_timeout); // Time left in microseconds. - if (time < 0x3D1) { - // Done waiting. - u32 segoff = GET_BDA(ptr_user_wait_complete_flag); - u16 segment = segoff >> 16; - u16 offset = segoff & 0xffff; - // Turn off status byte. - SET_BDA(rtc_wait_flag, 0); - // Clear the Periodic Interrupt. - outb_cmos(registerB & 0x37, CMOS_STATUS_B); - // Write to specified flag byte. - u8 oldval = GET_FARVAR(segment, *(u8*)(offset+0)); - SET_FARVAR(segment, *(u8*)(offset+0), oldval | 0x80); + if (time < USEC_PER_RTC) { + // Done waiting - write to specified flag byte. + struct segoff_s segoff = GET_BDA(user_wait_complete_flag); + u16 ptr_seg = segoff.seg; + u8 *ptr_far = (u8*)(segoff.offset+0); + u8 oldval = GET_FARVAR(ptr_seg, *ptr_far); + SET_FARVAR(ptr_seg, *ptr_far, oldval | 0x80); + + clear_usertimer(); } else { // Continue waiting. - time -= 0x3D1; + time -= USEC_PER_RTC; SET_BDA(user_wait_timeout, time); } done: - eoi_both_pics(); + eoi_pic2(); }