mirror of
https://github.com/reactos/reactos.git
synced 2024-10-15 05:37:44 +00:00
[NTVDM]
Implement the CMOS and Real Time Clock (RTC). Improve the performance of the PIT and RTC (correctly this time). svn path=/branches/ntvdm/; revision=60854
This commit is contained in:
parent
e9f8b230a6
commit
36afa0ea12
|
@ -14,6 +14,7 @@ list(APPEND SOURCE
|
|||
ps2.c
|
||||
speaker.c
|
||||
vga.c
|
||||
cmos.c
|
||||
ntvdm.c
|
||||
ntvdm.rc
|
||||
${CMAKE_CURRENT_BINARY_DIR}/ntvdm.def)
|
||||
|
|
419
subsystems/ntvdm/cmos.c
Normal file
419
subsystems/ntvdm/cmos.c
Normal file
|
@ -0,0 +1,419 @@
|
|||
/*
|
||||
* COPYRIGHT: GPL - See COPYING in the top level directory
|
||||
* PROJECT: ReactOS Virtual DOS Machine
|
||||
* FILE: cmos.c
|
||||
* PURPOSE: CMOS Real Time Clock emulation
|
||||
* PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
|
||||
*/
|
||||
|
||||
/* INCLUDES *******************************************************************/
|
||||
|
||||
#define NDEBUG
|
||||
|
||||
#include "cmos.h"
|
||||
#include "pic.h"
|
||||
|
||||
/* PRIVATE VARIABLES **********************************************************/
|
||||
|
||||
static BOOLEAN NmiEnabled = TRUE;
|
||||
static BYTE StatusRegA = CMOS_DEFAULT_STA;
|
||||
static BYTE StatusRegB = CMOS_DEFAULT_STB;
|
||||
static BYTE StatusRegC = 0;
|
||||
static BYTE AlarmHour, AlarmMinute, AlarmSecond;
|
||||
static CMOS_REGISTERS SelectedRegister = CMOS_REG_STATUS_D;
|
||||
|
||||
/* PUBLIC FUNCTIONS ***********************************************************/
|
||||
|
||||
BOOLEAN IsNmiEnabled(VOID)
|
||||
{
|
||||
return NmiEnabled;
|
||||
}
|
||||
|
||||
VOID CmosWriteAddress(BYTE Value)
|
||||
{
|
||||
/* Update the NMI enabled flag */
|
||||
NmiEnabled = !(Value & CMOS_DISABLE_NMI);
|
||||
|
||||
/* Get the register number */
|
||||
Value &= ~CMOS_DISABLE_NMI;
|
||||
|
||||
if (Value < CMOS_REG_MAX)
|
||||
{
|
||||
/* Select the new register */
|
||||
SelectedRegister = Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Default to Status Register D */
|
||||
SelectedRegister = CMOS_REG_STATUS_D;
|
||||
}
|
||||
}
|
||||
|
||||
BYTE CmosReadData(VOID)
|
||||
{
|
||||
SYSTEMTIME CurrentTime;
|
||||
|
||||
/* Get the current time */
|
||||
GetLocalTime(&CurrentTime);
|
||||
|
||||
switch (SelectedRegister)
|
||||
{
|
||||
case CMOS_REG_SECONDS:
|
||||
{
|
||||
return (StatusRegB & CMOS_STB_BINARY)
|
||||
? CurrentTime.wSecond
|
||||
: BINARY_TO_BCD(CurrentTime.wSecond);
|
||||
}
|
||||
|
||||
case CMOS_REG_ALARM_SEC:
|
||||
{
|
||||
return (StatusRegB & CMOS_STB_BINARY)
|
||||
? AlarmSecond
|
||||
: BINARY_TO_BCD(AlarmSecond);
|
||||
}
|
||||
|
||||
case CMOS_REG_MINUTES:
|
||||
{
|
||||
return (StatusRegB & CMOS_STB_BINARY)
|
||||
? CurrentTime.wMinute
|
||||
: BINARY_TO_BCD(CurrentTime.wMinute);
|
||||
}
|
||||
|
||||
case CMOS_REG_ALARM_MIN:
|
||||
{
|
||||
return (StatusRegB & CMOS_STB_BINARY)
|
||||
? AlarmMinute
|
||||
: BINARY_TO_BCD(AlarmMinute);
|
||||
}
|
||||
|
||||
case CMOS_REG_HOURS:
|
||||
{
|
||||
BOOLEAN Afternoon = FALSE;
|
||||
BYTE Value = CurrentTime.wHour;
|
||||
|
||||
if (!(StatusRegB & CMOS_STB_24HOUR) && (Value >= 12))
|
||||
{
|
||||
Value -= 12;
|
||||
Afternoon = TRUE;
|
||||
}
|
||||
|
||||
if (!(StatusRegB & CMOS_STB_BINARY)) Value = BINARY_TO_BCD(Value);
|
||||
|
||||
/* Convert to 12-hour */
|
||||
if (Afternoon) Value |= 0x80;
|
||||
|
||||
return Value;
|
||||
}
|
||||
|
||||
case CMOS_REG_ALARM_HRS:
|
||||
{
|
||||
BOOLEAN Afternoon = FALSE;
|
||||
BYTE Value = AlarmHour;
|
||||
|
||||
if (!(StatusRegB & CMOS_STB_24HOUR) && (Value >= 12))
|
||||
{
|
||||
Value -= 12;
|
||||
Afternoon = TRUE;
|
||||
}
|
||||
|
||||
if (!(StatusRegB & CMOS_STB_BINARY)) Value = BINARY_TO_BCD(Value);
|
||||
|
||||
/* Convert to 12-hour */
|
||||
if (Afternoon) Value |= 0x80;
|
||||
|
||||
return Value;
|
||||
}
|
||||
|
||||
case CMOS_REG_DAY_OF_WEEK:
|
||||
{
|
||||
return (StatusRegB & CMOS_STB_BINARY)
|
||||
? CurrentTime.wDayOfWeek
|
||||
: BINARY_TO_BCD(CurrentTime.wDayOfWeek);
|
||||
}
|
||||
|
||||
case CMOS_REG_DAY:
|
||||
{
|
||||
return (StatusRegB & CMOS_STB_BINARY)
|
||||
? CurrentTime.wDay
|
||||
:BINARY_TO_BCD(CurrentTime.wDay);
|
||||
}
|
||||
|
||||
case CMOS_REG_MONTH:
|
||||
{
|
||||
return (StatusRegB & CMOS_STB_BINARY)
|
||||
? CurrentTime.wMonth
|
||||
: BINARY_TO_BCD(CurrentTime.wMonth);
|
||||
}
|
||||
|
||||
case CMOS_REG_YEAR:
|
||||
{
|
||||
return (StatusRegB & CMOS_STB_BINARY)
|
||||
? (CurrentTime.wYear % 100)
|
||||
: BINARY_TO_BCD(CurrentTime.wYear % 100);
|
||||
}
|
||||
|
||||
case CMOS_REG_STATUS_A:
|
||||
{
|
||||
return StatusRegA;
|
||||
}
|
||||
|
||||
case CMOS_REG_STATUS_B:
|
||||
{
|
||||
return StatusRegB;
|
||||
}
|
||||
|
||||
case CMOS_REG_STATUS_C:
|
||||
{
|
||||
BYTE Value = StatusRegC;
|
||||
|
||||
/* Clear status register C */
|
||||
StatusRegC = 0;
|
||||
|
||||
/* Return the old value */
|
||||
return Value;
|
||||
}
|
||||
|
||||
case CMOS_REG_STATUS_D:
|
||||
{
|
||||
/* Our CMOS battery works perfectly forever */
|
||||
return CMOS_BATTERY_OK;
|
||||
}
|
||||
|
||||
case CMOS_REG_DIAGNOSTICS:
|
||||
{
|
||||
/* Diagnostics found no errors */
|
||||
return 0;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
/* Read ignored */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return to Status Register D */
|
||||
SelectedRegister = CMOS_REG_STATUS_D;
|
||||
}
|
||||
|
||||
VOID CmosWriteData(BYTE Value)
|
||||
{
|
||||
BOOLEAN ChangeTime = FALSE;
|
||||
SYSTEMTIME CurrentTime;
|
||||
|
||||
/* Get the current time */
|
||||
GetLocalTime(&CurrentTime);
|
||||
|
||||
switch (SelectedRegister)
|
||||
{
|
||||
case CMOS_REG_SECONDS:
|
||||
{
|
||||
ChangeTime = TRUE;
|
||||
CurrentTime.wSecond = (StatusRegB & CMOS_STB_BINARY)
|
||||
? Value
|
||||
: BCD_TO_BINARY(Value);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case CMOS_REG_ALARM_SEC:
|
||||
{
|
||||
AlarmSecond = (StatusRegB & CMOS_STB_BINARY)
|
||||
? Value
|
||||
: BCD_TO_BINARY(Value);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case CMOS_REG_MINUTES:
|
||||
{
|
||||
ChangeTime = TRUE;
|
||||
CurrentTime.wMinute = (StatusRegB & CMOS_STB_BINARY)
|
||||
? Value
|
||||
: BCD_TO_BINARY(Value);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case CMOS_REG_ALARM_MIN:
|
||||
{
|
||||
AlarmMinute = (StatusRegB & CMOS_STB_BINARY)
|
||||
? Value
|
||||
: BCD_TO_BINARY(Value);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case CMOS_REG_HOURS:
|
||||
{
|
||||
BOOLEAN Afternoon = FALSE;
|
||||
|
||||
ChangeTime = TRUE;
|
||||
|
||||
if (!(StatusRegB & CMOS_STB_24HOUR) && (Value & 0x80))
|
||||
{
|
||||
Value &= ~0x80;
|
||||
Afternoon = TRUE;
|
||||
}
|
||||
|
||||
CurrentTime.wHour = (StatusRegB & CMOS_STB_BINARY)
|
||||
? Value
|
||||
: BCD_TO_BINARY(Value);
|
||||
|
||||
/* Convert to 24-hour format */
|
||||
if (Afternoon) CurrentTime.wHour += 12;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case CMOS_REG_ALARM_HRS:
|
||||
{
|
||||
BOOLEAN Afternoon = FALSE;
|
||||
|
||||
if (!(StatusRegB & CMOS_STB_24HOUR) && (Value & 0x80))
|
||||
{
|
||||
Value &= ~0x80;
|
||||
Afternoon = TRUE;
|
||||
}
|
||||
|
||||
AlarmHour = (StatusRegB & CMOS_STB_BINARY)
|
||||
? Value
|
||||
: BCD_TO_BINARY(Value);
|
||||
|
||||
/* Convert to 24-hour format */
|
||||
if (Afternoon) AlarmHour += 12;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case CMOS_REG_DAY_OF_WEEK:
|
||||
{
|
||||
ChangeTime = TRUE;
|
||||
CurrentTime.wDayOfWeek = (StatusRegB & CMOS_STB_BINARY)
|
||||
? Value
|
||||
: BCD_TO_BINARY(Value);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case CMOS_REG_DAY:
|
||||
{
|
||||
ChangeTime = TRUE;
|
||||
CurrentTime.wDay = (StatusRegB & CMOS_STB_BINARY)
|
||||
? Value
|
||||
: BCD_TO_BINARY(Value);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case CMOS_REG_MONTH:
|
||||
{
|
||||
ChangeTime = TRUE;
|
||||
CurrentTime.wMonth = (StatusRegB & CMOS_STB_BINARY)
|
||||
? Value
|
||||
: BCD_TO_BINARY(Value);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case CMOS_REG_YEAR:
|
||||
{
|
||||
ChangeTime = TRUE;
|
||||
|
||||
/* Clear everything except the century */
|
||||
CurrentTime.wYear = (CurrentTime.wYear / 100) * 100;
|
||||
|
||||
CurrentTime.wYear += (StatusRegB & CMOS_STB_BINARY)
|
||||
? Value
|
||||
: BCD_TO_BINARY(Value);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case CMOS_REG_STATUS_A:
|
||||
{
|
||||
StatusRegA = Value;
|
||||
break;
|
||||
}
|
||||
|
||||
case CMOS_REG_STATUS_B:
|
||||
{
|
||||
StatusRegB = Value;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
/* Write ignored */
|
||||
}
|
||||
}
|
||||
|
||||
if (ChangeTime) SetLocalTime(&CurrentTime);
|
||||
|
||||
/* Return to Status Register D */
|
||||
SelectedRegister = CMOS_REG_STATUS_D;
|
||||
}
|
||||
|
||||
DWORD RtcGetTicksPerSecond(VOID)
|
||||
{
|
||||
BYTE RateSelect = StatusRegB & 0x0F;
|
||||
|
||||
if (RateSelect == 0)
|
||||
{
|
||||
/* No periodic interrupt */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 1 and 2 act like 8 and 9 */
|
||||
if (RateSelect <= 2) RateSelect += 7;
|
||||
|
||||
return 1 << (16 - RateSelect);
|
||||
}
|
||||
|
||||
VOID RtcPeriodicTick(VOID)
|
||||
{
|
||||
/* Set PF */
|
||||
StatusRegC |= CMOS_STC_PF;
|
||||
|
||||
/* Check if there should be an interrupt on a periodic timer tick */
|
||||
if (StatusRegB & CMOS_STB_INT_PERIODIC)
|
||||
{
|
||||
StatusRegC |= CMOS_STC_IRQF;
|
||||
|
||||
/* Interrupt! */
|
||||
PicInterruptRequest(RTC_IRQ_NUMBER);
|
||||
}
|
||||
}
|
||||
|
||||
/* Should be called every second */
|
||||
VOID RtcTimeUpdate(VOID)
|
||||
{
|
||||
SYSTEMTIME CurrentTime;
|
||||
|
||||
/* Get the current time */
|
||||
GetLocalTime(&CurrentTime);
|
||||
|
||||
/* Set UF */
|
||||
StatusRegC |= CMOS_STC_UF;
|
||||
|
||||
/* Check if the time matches the alarm time */
|
||||
if ((CurrentTime.wHour == AlarmHour)
|
||||
&& (CurrentTime.wMinute == AlarmMinute)
|
||||
&& (CurrentTime.wSecond == AlarmSecond))
|
||||
{
|
||||
/* Set the alarm flag */
|
||||
StatusRegC |= CMOS_STC_AF;
|
||||
|
||||
/* Set IRQF if there should be an interrupt */
|
||||
if (StatusRegB & CMOS_STB_INT_ON_ALARM) StatusRegC |= CMOS_STC_IRQF;
|
||||
}
|
||||
|
||||
/* Check if there should be an interrupt on update */
|
||||
if (StatusRegB & CMOS_STB_INT_ON_UPDATE) StatusRegC |= CMOS_STC_IRQF;
|
||||
|
||||
if (StatusRegC & CMOS_STC_IRQF)
|
||||
{
|
||||
/* Interrupt! */
|
||||
PicInterruptRequest(RTC_IRQ_NUMBER);
|
||||
}
|
||||
}
|
76
subsystems/ntvdm/cmos.h
Normal file
76
subsystems/ntvdm/cmos.h
Normal file
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* COPYRIGHT: GPL - See COPYING in the top level directory
|
||||
* PROJECT: ReactOS Virtual DOS Machine
|
||||
* FILE: cmos.h
|
||||
* PURPOSE: Real Time Clock emulation (header file)
|
||||
* PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
|
||||
*/
|
||||
|
||||
#ifndef _CMOS_H_
|
||||
#define _CMOS_H_
|
||||
|
||||
/* INCLUDES *******************************************************************/
|
||||
|
||||
#include "ntvdm.h"
|
||||
|
||||
/* DEFINES ********************************************************************/
|
||||
|
||||
#define RTC_IRQ_NUMBER 8
|
||||
#define CMOS_ADDRESS_PORT 0x70
|
||||
#define CMOS_DATA_PORT 0x71
|
||||
#define CMOS_DISABLE_NMI (1 << 7)
|
||||
#define CMOS_BATTERY_OK 0x80
|
||||
|
||||
/* Status Register B flags */
|
||||
#define CMOS_STB_24HOUR (1 << 1)
|
||||
#define CMOS_STB_BINARY (1 << 2)
|
||||
#define CMOS_STB_SQUARE_WAVE (1 << 3)
|
||||
#define CMOS_STB_INT_ON_UPDATE (1 << 4)
|
||||
#define CMOS_STB_INT_ON_ALARM (1 << 5)
|
||||
#define CMOS_STB_INT_PERIODIC (1 << 6)
|
||||
|
||||
/* Status Register C flags */
|
||||
#define CMOS_STC_UF CMOS_STB_INT_ON_UPDATE
|
||||
#define CMOS_STC_AF CMOS_STB_INT_ON_ALARM
|
||||
#define CMOS_STC_PF CMOS_STB_INT_PERIODIC
|
||||
#define CMOS_STC_IRQF (1 << 7)
|
||||
|
||||
/* Default register values */
|
||||
#define CMOS_DEFAULT_STA 0x26
|
||||
#define CMOS_DEFAULT_STB CMOS_STB_24HOUR
|
||||
|
||||
/* BCD-Binary conversion */
|
||||
#define BINARY_TO_BCD(x) (((x / 10) << 4) | (x % 10))
|
||||
#define BCD_TO_BINARY(x) (((x >> 4) * 10) + (x & 0x0F))
|
||||
|
||||
typedef enum _CMOS_REGISTERS
|
||||
{
|
||||
CMOS_REG_SECONDS,
|
||||
CMOS_REG_ALARM_SEC,
|
||||
CMOS_REG_MINUTES,
|
||||
CMOS_REG_ALARM_MIN,
|
||||
CMOS_REG_HOURS,
|
||||
CMOS_REG_ALARM_HRS,
|
||||
CMOS_REG_DAY_OF_WEEK,
|
||||
CMOS_REG_DAY,
|
||||
CMOS_REG_MONTH,
|
||||
CMOS_REG_YEAR,
|
||||
CMOS_REG_STATUS_A,
|
||||
CMOS_REG_STATUS_B,
|
||||
CMOS_REG_STATUS_C,
|
||||
CMOS_REG_STATUS_D,
|
||||
CMOS_REG_DIAGNOSTICS,
|
||||
CMOS_REG_MAX
|
||||
} CMOS_REGISTERS, *PCMOS_REGISTERS;
|
||||
|
||||
BOOLEAN IsNmiEnabled(VOID);
|
||||
VOID CmosWriteAddress(BYTE Value);
|
||||
BYTE CmosReadData(VOID);
|
||||
VOID CmosWriteData(BYTE Value);
|
||||
DWORD RtcGetTicksPerSecond(VOID);
|
||||
VOID RtcPeriodicTick(VOID);
|
||||
VOID RtcTimeUpdate(VOID);
|
||||
|
||||
#endif // _CMOS_H_
|
||||
|
||||
/* EOF */
|
|
@ -19,6 +19,7 @@
|
|||
#include "pic.h"
|
||||
#include "ps2.h"
|
||||
#include "timer.h"
|
||||
#include "cmos.h"
|
||||
|
||||
/* PRIVATE VARIABLES **********************************************************/
|
||||
|
||||
|
@ -126,6 +127,12 @@ static VOID WINAPI EmulatorReadIo(PFAST486_STATE State, ULONG Port, PVOID Buffer
|
|||
break;
|
||||
}
|
||||
|
||||
case CMOS_DATA_PORT:
|
||||
{
|
||||
*(Address++) = CmosReadData();
|
||||
break;
|
||||
}
|
||||
|
||||
case SPEAKER_CONTROL_PORT:
|
||||
{
|
||||
*(Address++) = SpeakerReadStatus();
|
||||
|
@ -211,6 +218,18 @@ static VOID WINAPI EmulatorWriteIo(PFAST486_STATE State, ULONG Port, PVOID Buffe
|
|||
break;
|
||||
}
|
||||
|
||||
case CMOS_ADDRESS_PORT:
|
||||
{
|
||||
CmosWriteAddress(*(Address++));
|
||||
break;
|
||||
}
|
||||
|
||||
case CMOS_DATA_PORT:
|
||||
{
|
||||
CmosWriteData(*(Address++));
|
||||
break;
|
||||
}
|
||||
|
||||
case SPEAKER_CONTROL_PORT:
|
||||
{
|
||||
SpeakerWriteCommand(*(Address++));
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "timer.h"
|
||||
#include "pic.h"
|
||||
#include "ps2.h"
|
||||
#include "cmos.h"
|
||||
|
||||
/*
|
||||
* Activate this line if you want to be able to test NTVDM with:
|
||||
|
@ -72,9 +73,12 @@ INT wmain(INT argc, WCHAR *argv[])
|
|||
DWORD Cycles = 0;
|
||||
DWORD LastCyclePrintout = GetTickCount();
|
||||
DWORD LastVerticalRefresh = GetTickCount();
|
||||
LARGE_INTEGER Frequency, LastTimerTick, Counter;
|
||||
DWORD LastClockUpdate = GetTickCount();
|
||||
LARGE_INTEGER Frequency, LastTimerTick, LastRtcTick, Counter;
|
||||
LONGLONG TimerTicks;
|
||||
HANDLE InputThread = NULL;
|
||||
LARGE_INTEGER StartPerfCount;
|
||||
DWORD StartTickCount;
|
||||
|
||||
/* Set the handler routine */
|
||||
SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE);
|
||||
|
@ -138,17 +142,34 @@ INT wmain(INT argc, WCHAR *argv[])
|
|||
/* Start the input thread */
|
||||
InputThread = CreateThread(NULL, 0, &InputThreadProc, NULL, 0, NULL);
|
||||
|
||||
/* Set the last timer tick to the current time */
|
||||
QueryPerformanceCounter(&LastTimerTick);
|
||||
/* Find the starting performance and tick count */
|
||||
StartTickCount = GetTickCount();
|
||||
QueryPerformanceCounter(&StartPerfCount);
|
||||
|
||||
/* Set the last timer ticks to the current time */
|
||||
LastTimerTick = LastRtcTick = StartPerfCount;
|
||||
|
||||
/* Main loop */
|
||||
while (VdmRunning)
|
||||
{
|
||||
DWORD PitResolution = PitGetResolution();
|
||||
DWORD RtcFrequency = RtcGetTicksPerSecond();
|
||||
|
||||
/* Get the current number of ticks */
|
||||
CurrentTickCount = GetTickCount();
|
||||
|
||||
if ((PitResolution <= 1000) && (RtcFrequency <= 1000))
|
||||
{
|
||||
/* Calculate the approximate performance counter value instead */
|
||||
Counter.QuadPart = StartPerfCount.QuadPart
|
||||
+ (CurrentTickCount - StartTickCount)
|
||||
* (Frequency.QuadPart / 1000);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Get the current performance counter value */
|
||||
QueryPerformanceCounter(&Counter);
|
||||
}
|
||||
|
||||
/* Get the number of PIT ticks that have passed */
|
||||
TimerTicks = ((Counter.QuadPart - LastTimerTick.QuadPart)
|
||||
|
@ -161,6 +182,21 @@ INT wmain(INT argc, WCHAR *argv[])
|
|||
LastTimerTick = Counter;
|
||||
}
|
||||
|
||||
/* Check for RTC update */
|
||||
if ((CurrentTickCount - LastClockUpdate) >= 1000)
|
||||
{
|
||||
RtcTimeUpdate();
|
||||
LastClockUpdate = CurrentTickCount;
|
||||
}
|
||||
|
||||
/* Check for RTC periodic tick */
|
||||
if ((Counter.QuadPart - LastRtcTick.QuadPart)
|
||||
>= (Frequency.QuadPart / (LONGLONG)RtcFrequency))
|
||||
{
|
||||
RtcPeriodicTick();
|
||||
LastRtcTick = Counter;
|
||||
}
|
||||
|
||||
/* Check for vertical retrace */
|
||||
if ((CurrentTickCount - LastVerticalRefresh) >= 16)
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue