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 LARGE_INTEGER StartPerfCount
, Frequency
;
42 // static ULONG StartTickCount;
43 static LARGE_INTEGER Counter
;
44 static ULONG CurrentTickCount
;
45 static ULONGLONG LastCycles
= 0ULL;
46 static PHARDWARE_TIMER IpsTimer
;
48 ULONGLONG CurrentCycleCount
= 0ULL;
49 ULONGLONG CurrentIps
= 20000000ULL; // 20 MIPS is a good estimate
51 /* PRIVATE FUNCTIONS **********************************************************/
53 static VOID FASTCALL
IpsCallback(ULONGLONG ElapsedTime
)
56 static INT NumCalls
= 0;
59 ULONGLONG NewIps
= 10ULL * (CurrentCycleCount
- LastCycles
) / ElapsedTime
;
60 CurrentIps
= (CurrentIps
+ NewIps
) >> 1;
66 DPRINT1("NTVDM: %I64u Instructions Per Second\n", CurrentIps
);
71 LastCycles
= CurrentCycleCount
;
74 /* PUBLIC FUNCTIONS ***********************************************************/
76 VOID
ClockUpdate(VOID
)
78 extern BOOLEAN CpuRunning
;
81 PHARDWARE_TIMER Timer
;
83 while (VdmRunning
&& CpuRunning
)
85 /* Get the current counters */
86 /// DWORD_PTR oldmask = SetThreadAffinityMask(GetCurrentThread(), 0);
87 CurrentTickCount
= GetTickCount();
88 NtQueryPerformanceCounter(&Counter
, NULL
);
89 /// SetThreadAffinityMask(GetCurrentThread(), oldmask);
91 /* Continue CPU emulation */
92 for (i
= 0; VdmRunning
&& CpuRunning
&& (i
< STEPS_PER_CYCLE
); i
++)
99 while (Entry
!= &Timers
)
101 ULONGLONG Ticks
= (ULONGLONG
)-1;
103 Timer
= CONTAINING_RECORD(Entry
, HARDWARE_TIMER
, Link
);
104 Entry
= Entry
->Flink
;
106 ASSERT((Timer
->EnableCount
> 0) && (Timer
->Flags
& HARDWARE_TIMER_ENABLED
));
110 if (Timer
->Flags
& HARDWARE_TIMER_PRECISE
)
112 /* Use the performance counter for precise timers */
113 if (Counter
.QuadPart
<= Timer
->LastTick
.QuadPart
) continue;
114 Ticks
= (Counter
.QuadPart
- Timer
->LastTick
.QuadPart
) / Timer
->Delay
;
118 /* Use the regular tick count for normal timers */
119 if (CurrentTickCount
<= Timer
->LastTick
.LowPart
) continue;
120 Ticks
= (CurrentTickCount
- Timer
->LastTick
.LowPart
) / (ULONG
)Timer
->Delay
;
123 if (Ticks
== 0) continue;
126 Timer
->Callback(Ticks
);
128 if (Timer
->Flags
& HARDWARE_TIMER_ONESHOT
)
130 /* Disable this timer */
131 DisableHardwareTimer(Timer
);
134 /* Update the time of the last timer tick */
135 Timer
->LastTick
.QuadPart
+= Ticks
* Timer
->Delay
;
138 /* Yield execution to other threads */
139 // FIXME: Disabled because it causes timing issues (slowdowns).
140 // NtYieldExecution();
144 PHARDWARE_TIMER
CreateHardwareTimer(ULONG Flags
, ULONGLONG Delay
, PHARDWARE_TIMER_PROC Callback
)
146 PHARDWARE_TIMER Timer
;
148 Timer
= RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*Timer
));
149 if (Timer
== NULL
) return NULL
;
151 Timer
->Flags
= Flags
& ~HARDWARE_TIMER_ENABLED
;
152 Timer
->EnableCount
= 0;
153 Timer
->Callback
= Callback
;
154 Timer
->LastTick
.QuadPart
= 0;
155 SetHardwareTimerDelay(Timer
, Delay
);
157 if (Flags
& HARDWARE_TIMER_ENABLED
) EnableHardwareTimer(Timer
);
161 VOID
EnableHardwareTimer(PHARDWARE_TIMER Timer
)
163 /* Increment the count */
164 Timer
->EnableCount
++;
166 /* Check if the count is above 0 but the timer isn't enabled */
167 if ((Timer
->EnableCount
> 0) && !(Timer
->Flags
& HARDWARE_TIMER_ENABLED
))
169 if (Timer
->Flags
& HARDWARE_TIMER_PRECISE
)
171 NtQueryPerformanceCounter(&Timer
->LastTick
, NULL
);
175 Timer
->LastTick
.LowPart
= GetTickCount();
178 Timer
->Flags
|= HARDWARE_TIMER_ENABLED
;
179 InsertTailList(&Timers
, &Timer
->Link
);
183 VOID
DisableHardwareTimer(PHARDWARE_TIMER Timer
)
185 /* Decrement the count */
186 Timer
->EnableCount
--;
188 /* Check if the count is 0 or less but the timer is enabled */
189 if ((Timer
->EnableCount
<= 0) && (Timer
->Flags
& HARDWARE_TIMER_ENABLED
))
191 /* Disable the timer */
192 Timer
->Flags
&= ~HARDWARE_TIMER_ENABLED
;
193 RemoveEntryList(&Timer
->Link
);
197 VOID
SetHardwareTimerDelay(PHARDWARE_TIMER Timer
, ULONGLONG NewDelay
)
199 if (Timer
->Flags
& HARDWARE_TIMER_PRECISE
)
201 /* Convert the delay from nanoseconds to performance counter ticks */
202 Timer
->Delay
= (NewDelay
* Frequency
.QuadPart
+ 500000000ULL) / 1000000000ULL;
206 Timer
->Delay
= NewDelay
/ 1000000ULL;
210 VOID
DestroyHardwareTimer(PHARDWARE_TIMER Timer
)
214 if (Timer
->Flags
& HARDWARE_TIMER_ENABLED
) RemoveEntryList(&Timer
->Link
);
215 RtlFreeHeap(RtlGetProcessHeap(), 0, Timer
);
219 BOOLEAN
ClockInitialize(VOID
)
221 InitializeListHead(&Timers
);
223 /* Initialize the performance counter (needed for hardware timers) */
224 /* Find the starting performance */
225 NtQueryPerformanceCounter(&StartPerfCount
, &Frequency
);
226 if (Frequency
.QuadPart
== 0)
228 wprintf(L
"FATAL: Performance counter not available\n");
232 /* Find the starting tick count */
233 // StartTickCount = GetTickCount();
235 IpsTimer
= CreateHardwareTimer(HARDWARE_TIMER_ENABLED
, HZ_TO_NS(10), IpsCallback
);
236 if (IpsTimer
== NULL
)
238 wprintf(L
"FATAL: Cannot create IPS timer.\n");