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 #define STEPS_PER_CYCLE 1024
39 /* VARIABLES ******************************************************************/
41 static LIST_ENTRY Timers
;
42 static LARGE_INTEGER StartPerfCount
, Frequency
;
43 static DWORD StartTickCount
;
46 static ULONGLONG Cycles
= 0ULL;
49 /* PRIVATE FUNCTIONS **********************************************************/
52 static VOID FASTCALL
IpsDisplayCallback(ULONGLONG ElapsedTime
)
54 DPRINT1("NTVDM: %I64u Instructions Per Second\n", Cycles
/ ElapsedTime
);
59 /* PUBLIC FUNCTIONS ***********************************************************/
61 VOID
ClockUpdate(VOID
)
63 extern BOOLEAN CpuRunning
;
66 LARGE_INTEGER Counter
;
68 while (VdmRunning
&& CpuRunning
)
70 /* Get the current number of ticks */
71 DWORD CurrentTickCount
= GetTickCount();
73 /* Get the current performance counter value */
74 /// DWORD_PTR oldmask = SetThreadAffinityMask(GetCurrentThread(), 0);
75 NtQueryPerformanceCounter(&Counter
, NULL
);
76 /// SetThreadAffinityMask(GetCurrentThread(), oldmask);
78 /* Continue CPU emulation */
79 for (i
= 0; VdmRunning
&& CpuRunning
&& (i
< STEPS_PER_CYCLE
); i
++)
88 for (Entry
= Timers
.Flink
; Entry
!= &Timers
; Entry
= Entry
->Flink
)
90 ULONGLONG Ticks
= (ULONGLONG
)-1;
91 PHARDWARE_TIMER Timer
= CONTAINING_RECORD(Entry
, HARDWARE_TIMER
, Link
);
93 ASSERT((Timer
->EnableCount
> 0) && (Timer
->Flags
& HARDWARE_TIMER_ENABLED
));
97 if (Timer
->Flags
& HARDWARE_TIMER_PRECISE
)
99 /* Use the performance counter for precise timers */
100 if (Counter
.QuadPart
<= Timer
->LastTick
.QuadPart
) continue;
101 Ticks
= (Counter
.QuadPart
- Timer
->LastTick
.QuadPart
) / Timer
->Delay
;
105 /* Use the regular tick count for normal timers */
106 if (CurrentTickCount
<= Timer
->LastTick
.LowPart
) continue;
107 Ticks
= (CurrentTickCount
- Timer
->LastTick
.LowPart
) / (ULONG
)Timer
->Delay
;
110 if (Ticks
== 0) continue;
113 Timer
->Callback(Ticks
);
115 if (Timer
->Flags
& HARDWARE_TIMER_ONESHOT
)
117 /* Disable this timer */
118 DisableHardwareTimer(Timer
);
121 /* Update the time of the last timer tick */
122 Timer
->LastTick
.QuadPart
+= Ticks
* Timer
->Delay
;
127 PHARDWARE_TIMER
CreateHardwareTimer(ULONG Flags
, ULONGLONG Delay
, PHARDWARE_TIMER_PROC Callback
)
129 PHARDWARE_TIMER Timer
;
131 Timer
= (PHARDWARE_TIMER
)RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(HARDWARE_TIMER
));
132 if (Timer
== NULL
) return NULL
;
134 Timer
->Flags
= Flags
& ~HARDWARE_TIMER_ENABLED
;
135 Timer
->EnableCount
= 0;
136 Timer
->Callback
= Callback
;
137 SetHardwareTimerDelay(Timer
, Delay
);
139 if (Flags
& HARDWARE_TIMER_ENABLED
) EnableHardwareTimer(Timer
);
143 VOID
EnableHardwareTimer(PHARDWARE_TIMER Timer
)
145 /* Increment the count */
146 Timer
->EnableCount
++;
148 /* Check if the count is above 0 but the timer isn't enabled */
149 if ((Timer
->EnableCount
> 0) && !(Timer
->Flags
& HARDWARE_TIMER_ENABLED
))
151 if (Timer
->Flags
& HARDWARE_TIMER_PRECISE
)
153 NtQueryPerformanceCounter(&Timer
->LastTick
, NULL
);
157 Timer
->LastTick
.LowPart
= GetTickCount();
160 Timer
->Flags
|= HARDWARE_TIMER_ENABLED
;
161 InsertTailList(&Timers
, &Timer
->Link
);
165 VOID
DisableHardwareTimer(PHARDWARE_TIMER Timer
)
167 /* Decrement the count */
168 Timer
->EnableCount
--;
170 /* Check if the count is 0 or less but the timer is enabled */
171 if ((Timer
->EnableCount
<= 0) && (Timer
->Flags
& HARDWARE_TIMER_ENABLED
))
173 /* Disable the timer */
174 Timer
->Flags
&= ~HARDWARE_TIMER_ENABLED
;
175 RemoveEntryList(&Timer
->Link
);
179 VOID
SetHardwareTimerDelay(PHARDWARE_TIMER Timer
, ULONGLONG NewDelay
)
181 if (Timer
->Flags
& HARDWARE_TIMER_PRECISE
)
183 /* Convert the delay from nanoseconds to performance counter ticks */
184 Timer
->Delay
= (NewDelay
* Frequency
.QuadPart
+ 500000000ULL) / 1000000000ULL;
188 Timer
->Delay
= NewDelay
/ 1000000ULL;
192 VOID
DestroyHardwareTimer(PHARDWARE_TIMER Timer
)
194 if (Timer
->Flags
& HARDWARE_TIMER_ENABLED
) RemoveEntryList(&Timer
->Link
);
195 RtlFreeHeap(RtlGetProcessHeap(), 0, Timer
);
198 BOOLEAN
ClockInitialize(VOID
)
201 PHARDWARE_TIMER IpsTimer
;
204 InitializeListHead(&Timers
);
206 /* Initialize the performance counter (needed for hardware timers) */
207 /* Find the starting performance */
208 NtQueryPerformanceCounter(&StartPerfCount
, &Frequency
);
209 if (Frequency
.QuadPart
== 0)
211 wprintf(L
"FATAL: Performance counter not available\n");
215 /* Find the starting tick count */
216 StartTickCount
= GetTickCount();
220 IpsTimer
= CreateHardwareTimer(HARDWARE_TIMER_ENABLED
, HZ_TO_NS(1), IpsDisplayCallback
);
221 if (IpsTimer
== NULL
)
223 wprintf(L
"FATAL: Cannot create IPS display timer.\n");