2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: subsystems/mvdm/ntvdm/clock.c
5 * PURPOSE: Clock for VDM
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
7 * Hermes Belusca-Maito (hermes.belusca@sfr.fr)
10 /* INCLUDES *******************************************************************/
21 #include "hardware/cmos.h"
22 #include "hardware/ps2.h"
23 #include "hardware/pit.h"
24 #include "hardware/video/svga.h"
25 #include "hardware/mouse.h"
27 /* DEFINES ********************************************************************/
30 * Activate IPS_DISPLAY if you want to display the
31 * number of instructions per second.
33 // #define IPS_DISPLAY
36 #define STEPS_PER_CYCLE 1024
38 /* VARIABLES ******************************************************************/
40 static LIST_ENTRY Timers
;
41 static ULONGLONG Cycles
= 0ULL;
42 static ULONGLONG CurrentIps
= 20000000ULL; // 20 MIPS is a good estimate
43 static LARGE_INTEGER StartPerfCount
, Frequency
;
44 // static ULONG StartTickCount;
45 static LARGE_INTEGER Counter
;
46 static ULONG CurrentTickCount
;
47 static ULONGLONG LastCycles
= 0ULL;
48 static PHARDWARE_TIMER IpsTimer
;
50 /* PRIVATE FUNCTIONS **********************************************************/
52 static VOID FASTCALL
IpsCallback(ULONGLONG ElapsedTime
)
54 CurrentIps
= (Cycles
- LastCycles
) / ElapsedTime
;
57 DPRINT1("NTVDM: %I64u Instructions Per Second\n", CurrentIps
);
63 /* PUBLIC FUNCTIONS ***********************************************************/
65 VOID
ClockUpdate(VOID
)
67 extern BOOLEAN CpuRunning
;
70 PHARDWARE_TIMER Timer
;
72 while (VdmRunning
&& CpuRunning
)
74 /* Get the current counters */
75 /// DWORD_PTR oldmask = SetThreadAffinityMask(GetCurrentThread(), 0);
76 CurrentTickCount
= GetTickCount();
77 NtQueryPerformanceCounter(&Counter
, NULL
);
78 /// SetThreadAffinityMask(GetCurrentThread(), oldmask);
80 /* Continue CPU emulation */
81 for (i
= 0; VdmRunning
&& CpuRunning
&& (i
< STEPS_PER_CYCLE
); i
++)
88 while (Entry
!= &Timers
)
90 ULONGLONG Ticks
= (ULONGLONG
)-1;
92 Timer
= CONTAINING_RECORD(Entry
, HARDWARE_TIMER
, Link
);
95 ASSERT((Timer
->EnableCount
> 0) && (Timer
->Flags
& HARDWARE_TIMER_ENABLED
));
99 if (Timer
->Flags
& HARDWARE_TIMER_PRECISE
)
101 /* Use the performance counter for precise timers */
102 if (Counter
.QuadPart
<= Timer
->LastTick
.QuadPart
) continue;
103 Ticks
= (Counter
.QuadPart
- Timer
->LastTick
.QuadPart
) / Timer
->Delay
;
107 /* Use the regular tick count for normal timers */
108 if (CurrentTickCount
<= Timer
->LastTick
.LowPart
) continue;
109 Ticks
= (CurrentTickCount
- Timer
->LastTick
.LowPart
) / (ULONG
)Timer
->Delay
;
112 if (Ticks
== 0) continue;
115 Timer
->Callback(Ticks
);
117 if (Timer
->Flags
& HARDWARE_TIMER_ONESHOT
)
119 /* Disable this timer */
120 DisableHardwareTimer(Timer
);
123 /* Update the time of the last timer tick */
124 Timer
->LastTick
.QuadPart
+= Ticks
* Timer
->Delay
;
127 /* Yield execution to other threads */
128 // FIXME: Disabled because it causes timing issues (slowdowns).
129 // NtYieldExecution();
133 PHARDWARE_TIMER
CreateHardwareTimer(ULONG Flags
, ULONGLONG Delay
, PHARDWARE_TIMER_PROC Callback
)
135 PHARDWARE_TIMER Timer
;
137 Timer
= RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*Timer
));
138 if (Timer
== NULL
) return NULL
;
140 Timer
->Flags
= Flags
& ~HARDWARE_TIMER_ENABLED
;
141 Timer
->EnableCount
= 0;
142 Timer
->Callback
= Callback
;
143 Timer
->LastTick
.QuadPart
= 0;
144 SetHardwareTimerDelay(Timer
, Delay
);
146 if (Flags
& HARDWARE_TIMER_ENABLED
) EnableHardwareTimer(Timer
);
150 VOID
EnableHardwareTimer(PHARDWARE_TIMER Timer
)
152 /* Increment the count */
153 Timer
->EnableCount
++;
155 /* Check if the count is above 0 but the timer isn't enabled */
156 if ((Timer
->EnableCount
> 0) && !(Timer
->Flags
& HARDWARE_TIMER_ENABLED
))
158 if (Timer
->Flags
& HARDWARE_TIMER_PRECISE
)
160 NtQueryPerformanceCounter(&Timer
->LastTick
, NULL
);
164 Timer
->LastTick
.LowPart
= GetTickCount();
167 Timer
->Flags
|= HARDWARE_TIMER_ENABLED
;
168 InsertTailList(&Timers
, &Timer
->Link
);
172 VOID
DisableHardwareTimer(PHARDWARE_TIMER Timer
)
174 /* Decrement the count */
175 Timer
->EnableCount
--;
177 /* Check if the count is 0 or less but the timer is enabled */
178 if ((Timer
->EnableCount
<= 0) && (Timer
->Flags
& HARDWARE_TIMER_ENABLED
))
180 /* Disable the timer */
181 Timer
->Flags
&= ~HARDWARE_TIMER_ENABLED
;
182 RemoveEntryList(&Timer
->Link
);
186 VOID
SetHardwareTimerDelay(PHARDWARE_TIMER Timer
, ULONGLONG NewDelay
)
188 if (Timer
->Flags
& HARDWARE_TIMER_PRECISE
)
190 /* Convert the delay from nanoseconds to performance counter ticks */
191 Timer
->Delay
= (NewDelay
* Frequency
.QuadPart
+ 500000000ULL) / 1000000000ULL;
195 Timer
->Delay
= NewDelay
/ 1000000ULL;
199 VOID
DestroyHardwareTimer(PHARDWARE_TIMER Timer
)
203 if (Timer
->Flags
& HARDWARE_TIMER_ENABLED
) RemoveEntryList(&Timer
->Link
);
204 RtlFreeHeap(RtlGetProcessHeap(), 0, Timer
);
208 ULONGLONG
GetCycleCount(VOID
)
213 ULONGLONG
GetCycleSpeed(VOID
)
218 BOOLEAN
ClockInitialize(VOID
)
220 InitializeListHead(&Timers
);
222 /* Initialize the performance counter (needed for hardware timers) */
223 /* Find the starting performance */
224 NtQueryPerformanceCounter(&StartPerfCount
, &Frequency
);
225 if (Frequency
.QuadPart
== 0)
227 wprintf(L
"FATAL: Performance counter not available\n");
231 /* Find the starting tick count */
232 // StartTickCount = GetTickCount();
234 IpsTimer
= CreateHardwareTimer(HARDWARE_TIMER_ENABLED
, HZ_TO_NS(1), IpsCallback
);
235 if (IpsTimer
== NULL
)
237 wprintf(L
"FATAL: Cannot create IPS timer.\n");