Author: aandrejevic
Date: Sun Nov 3 21:33:22 2013
New Revision: 60854
URL:
http://svn.reactos.org/svn/reactos?rev=60854&view=rev
Log:
[NTVDM]
Implement the CMOS and Real Time Clock (RTC).
Improve the performance of the PIT and RTC (correctly this time).
Added:
branches/ntvdm/subsystems/ntvdm/cmos.c (with props)
branches/ntvdm/subsystems/ntvdm/cmos.h (with props)
Modified:
branches/ntvdm/subsystems/ntvdm/CMakeLists.txt
branches/ntvdm/subsystems/ntvdm/emulator.c
branches/ntvdm/subsystems/ntvdm/ntvdm.c
Modified: branches/ntvdm/subsystems/ntvdm/CMakeLists.txt
URL:
http://svn.reactos.org/svn/reactos/branches/ntvdm/subsystems/ntvdm/CMakeLis…
==============================================================================
--- branches/ntvdm/subsystems/ntvdm/CMakeLists.txt [iso-8859-1] (original)
+++ branches/ntvdm/subsystems/ntvdm/CMakeLists.txt [iso-8859-1] Sun Nov 3 21:33:22 2013
@@ -14,6 +14,7 @@
ps2.c
speaker.c
vga.c
+ cmos.c
ntvdm.c
ntvdm.rc
${CMAKE_CURRENT_BINARY_DIR}/ntvdm.def)
Added: branches/ntvdm/subsystems/ntvdm/cmos.c
URL:
http://svn.reactos.org/svn/reactos/branches/ntvdm/subsystems/ntvdm/cmos.c?r…
==============================================================================
--- branches/ntvdm/subsystems/ntvdm/cmos.c (added)
+++ branches/ntvdm/subsystems/ntvdm/cmos.c [iso-8859-1] Sun Nov 3 21:33:22 2013
@@ -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);
+ }
+}
Propchange: branches/ntvdm/subsystems/ntvdm/cmos.c
------------------------------------------------------------------------------
svn:eol-style = native
Added: branches/ntvdm/subsystems/ntvdm/cmos.h
URL:
http://svn.reactos.org/svn/reactos/branches/ntvdm/subsystems/ntvdm/cmos.h?r…
==============================================================================
--- branches/ntvdm/subsystems/ntvdm/cmos.h (added)
+++ branches/ntvdm/subsystems/ntvdm/cmos.h [iso-8859-1] Sun Nov 3 21:33:22 2013
@@ -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 */
Propchange: branches/ntvdm/subsystems/ntvdm/cmos.h
------------------------------------------------------------------------------
svn:eol-style = native
Modified: branches/ntvdm/subsystems/ntvdm/emulator.c
URL:
http://svn.reactos.org/svn/reactos/branches/ntvdm/subsystems/ntvdm/emulator…
==============================================================================
--- branches/ntvdm/subsystems/ntvdm/emulator.c [iso-8859-1] (original)
+++ branches/ntvdm/subsystems/ntvdm/emulator.c [iso-8859-1] Sun Nov 3 21:33:22 2013
@@ -19,6 +19,7 @@
#include "pic.h"
#include "ps2.h"
#include "timer.h"
+#include "cmos.h"
/* PRIVATE VARIABLES **********************************************************/
@@ -123,6 +124,12 @@
case PS2_DATA_PORT:
{
*(Address++) = KeyboardReadData();
+ break;
+ }
+
+ case CMOS_DATA_PORT:
+ {
+ *(Address++) = CmosReadData();
break;
}
@@ -211,6 +218,18 @@
break;
}
+ case CMOS_ADDRESS_PORT:
+ {
+ CmosWriteAddress(*(Address++));
+ break;
+ }
+
+ case CMOS_DATA_PORT:
+ {
+ CmosWriteData(*(Address++));
+ break;
+ }
+
case SPEAKER_CONTROL_PORT:
{
SpeakerWriteCommand(*(Address++));
Modified: branches/ntvdm/subsystems/ntvdm/ntvdm.c
URL:
http://svn.reactos.org/svn/reactos/branches/ntvdm/subsystems/ntvdm/ntvdm.c?…
==============================================================================
--- branches/ntvdm/subsystems/ntvdm/ntvdm.c [iso-8859-1] (original)
+++ branches/ntvdm/subsystems/ntvdm/ntvdm.c [iso-8859-1] Sun Nov 3 21:33:22 2013
@@ -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 @@
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 @@
/* 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();
- /* Get the current performance counter value */
- QueryPerformanceCounter(&Counter);
+ 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)
@@ -159,6 +180,21 @@
{
PitDecrementCount(TimerTicks);
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 */