2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
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 *******************************************************************/
19 #include "hardware/cmos.h"
20 #include "hardware/ps2.h"
21 #include "hardware/pit.h"
22 #include "hardware/video/vga.h"
23 #include "hardware/mouse.h"
25 /* Extra PSDK/NDK Headers */
26 #include <ndk/kefuncs.h>
28 /* DEFINES ********************************************************************/
31 * Activate IPS_DISPLAY if you want to display the
32 * number of instructions per second.
34 // #define IPS_DISPLAY
37 * Activate WORKING_TIMER when the PIT timing problem is fixed.
39 // #define WORKING_TIMER
42 #define STEPS_PER_CYCLE 1024
44 /* VARIABLES ******************************************************************/
46 static LIST_ENTRY Timers
;
47 static LARGE_INTEGER StartPerfCount
, Frequency
;
48 static DWORD StartTickCount
;
51 static ULONGLONG Cycles
= 0ULL;
54 /* PRIVATE FUNCTIONS **********************************************************/
57 static VOID FASTCALL
IpsDisplayCallback(ULONGLONG ElapsedTime
)
59 DPRINT1("NTVDM: %I64u Instructions Per Second\n", Cycles
/ ElapsedTime
);
64 /* PUBLIC FUNCTIONS ***********************************************************/
66 VOID
ClockUpdate(VOID
)
68 extern BOOLEAN CpuRunning
;
71 LARGE_INTEGER Counter
;
73 while (VdmRunning
&& CpuRunning
)
75 /* Get the current number of ticks */
76 DWORD CurrentTickCount
= GetTickCount();
79 if ((PitResolution
<= 1000) && (RtcFrequency
<= 1000))
81 /* Calculate the approximate performance counter value instead */
82 Counter
.QuadPart
= StartPerfCount
.QuadPart
83 + ((CurrentTickCount
- StartTickCount
)
84 * Frequency
.QuadPart
) / 1000;
89 /* Get the current performance counter value */
90 /// DWORD_PTR oldmask = SetThreadAffinityMask(GetCurrentThread(), 0);
91 NtQueryPerformanceCounter(&Counter
, NULL
);
92 /// SetThreadAffinityMask(GetCurrentThread(), oldmask);
95 /* Continue CPU emulation */
96 for (i
= 0; VdmRunning
&& CpuRunning
&& (i
< STEPS_PER_CYCLE
); i
++)
105 for (Entry
= Timers
.Flink
; Entry
!= &Timers
; Entry
= Entry
->Flink
)
107 ULONGLONG Ticks
= (ULONGLONG
)-1;
108 PHARDWARE_TIMER Timer
= CONTAINING_RECORD(Entry
, HARDWARE_TIMER
, Link
);
110 ASSERT((Timer
->EnableCount
> 0) && (Timer
->Flags
& HARDWARE_TIMER_ENABLED
));
114 if (Timer
->Flags
& HARDWARE_TIMER_PRECISE
)
116 /* Use the performance counter for precise timers */
117 if (Counter
.QuadPart
<= Timer
->LastTick
.QuadPart
) continue;
118 Ticks
= (Counter
.QuadPart
- Timer
->LastTick
.QuadPart
) / Timer
->Delay
;
122 /* Use the regular tick count for normal timers */
123 if (CurrentTickCount
<= Timer
->LastTick
.LowPart
) continue;
124 Ticks
= (CurrentTickCount
- Timer
->LastTick
.LowPart
) / (ULONG
)Timer
->Delay
;
127 if (Ticks
== 0) continue;
130 Timer
->Callback(Ticks
);
132 if (Timer
->Flags
& HARDWARE_TIMER_ONESHOT
)
134 /* Disable this timer */
135 DisableHardwareTimer(Timer
);
138 /* Update the time of the last timer tick */
139 Timer
->LastTick
.QuadPart
+= Ticks
* Timer
->Delay
;
144 PHARDWARE_TIMER
CreateHardwareTimer(ULONG Flags
, ULONGLONG Delay
, PHARDWARE_TIMER_PROC Callback
)
146 PHARDWARE_TIMER Timer
;
148 Timer
= (PHARDWARE_TIMER
)RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(HARDWARE_TIMER
));
149 if (Timer
== NULL
) return NULL
;
151 Timer
->Flags
= Flags
& ~HARDWARE_TIMER_ENABLED
;
152 Timer
->EnableCount
= 0;
153 Timer
->Callback
= Callback
;
154 SetHardwareTimerDelay(Timer
, Delay
);
156 if (Flags
& HARDWARE_TIMER_ENABLED
) EnableHardwareTimer(Timer
);
160 VOID
EnableHardwareTimer(PHARDWARE_TIMER Timer
)
162 /* Increment the count */
163 Timer
->EnableCount
++;
165 /* Check if the count is above 0 but the timer isn't enabled */
166 if ((Timer
->EnableCount
> 0) && !(Timer
->Flags
& HARDWARE_TIMER_ENABLED
))
168 if (Timer
->Flags
& HARDWARE_TIMER_PRECISE
)
170 NtQueryPerformanceCounter(&Timer
->LastTick
, NULL
);
174 Timer
->LastTick
.LowPart
= GetTickCount();
177 Timer
->Flags
|= HARDWARE_TIMER_ENABLED
;
178 InsertTailList(&Timers
, &Timer
->Link
);
182 VOID
DisableHardwareTimer(PHARDWARE_TIMER Timer
)
184 /* Decrement the count */
185 Timer
->EnableCount
--;
187 /* Check if the count is 0 or less but the timer is enabled */
188 if ((Timer
->EnableCount
<= 0) && (Timer
->Flags
& HARDWARE_TIMER_ENABLED
))
190 /* Disable the timer */
191 Timer
->Flags
&= ~HARDWARE_TIMER_ENABLED
;
192 RemoveEntryList(&Timer
->Link
);
196 VOID
SetHardwareTimerDelay(PHARDWARE_TIMER Timer
, ULONGLONG NewDelay
)
198 if (Timer
->Flags
& HARDWARE_TIMER_PRECISE
)
200 /* Convert the delay from nanoseconds to performance counter ticks */
201 Timer
->Delay
= (NewDelay
* Frequency
.QuadPart
+ 500000000ULL) / 1000000000ULL;
205 Timer
->Delay
= NewDelay
/ 1000000ULL;
209 VOID
DestroyHardwareTimer(PHARDWARE_TIMER Timer
)
211 if (Timer
->Flags
& HARDWARE_TIMER_ENABLED
) RemoveEntryList(&Timer
->Link
);
212 RtlFreeHeap(RtlGetProcessHeap(), 0, Timer
);
215 BOOLEAN
ClockInitialize(VOID
)
218 PHARDWARE_TIMER IpsTimer
;
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();
237 IpsTimer
= CreateHardwareTimer(HARDWARE_TIMER_ENABLED
, HZ_TO_NS(1), IpsDisplayCallback
);
238 if (IpsTimer
== NULL
)
240 wprintf(L
"FATAL: Cannot create IPS display timer.\n");