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/svga.h"
23 #include "hardware/mouse.h"
25 /* DEFINES ********************************************************************/
28 * Activate IPS_DISPLAY if you want to display the
29 * number of instructions per second.
31 // #define IPS_DISPLAY
34 #define STEPS_PER_CYCLE 1024
36 /* VARIABLES ******************************************************************/
38 static LIST_ENTRY Timers
;
39 static ULONGLONG Cycles
= 0ULL;
40 static ULONGLONG CurrentIps
= 20000000ULL; // 20 MIPS is a good estimate
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 /* PRIVATE FUNCTIONS **********************************************************/
50 static VOID FASTCALL
IpsCallback(ULONGLONG ElapsedTime
)
52 CurrentIps
= (Cycles
- LastCycles
) / ElapsedTime
;
55 DPRINT1("NTVDM: %I64u Instructions Per Second\n", CurrentIps
);
61 /* PUBLIC FUNCTIONS ***********************************************************/
63 VOID
ClockUpdate(VOID
)
65 extern BOOLEAN CpuRunning
;
68 PHARDWARE_TIMER Timer
;
70 while (VdmRunning
&& CpuRunning
)
72 /* Get the current counters */
73 /// DWORD_PTR oldmask = SetThreadAffinityMask(GetCurrentThread(), 0);
74 CurrentTickCount
= GetTickCount();
75 NtQueryPerformanceCounter(&Counter
, NULL
);
76 /// SetThreadAffinityMask(GetCurrentThread(), oldmask);
78 /* Continue CPU emulation */
79 for (i
= 0; VdmRunning
&& CpuRunning
&& (i
< STEPS_PER_CYCLE
); i
++)
86 while (Entry
!= &Timers
)
88 ULONGLONG Ticks
= (ULONGLONG
)-1;
90 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
;
125 /* Yield execution to other threads */
126 // FIXME: Disabled because it causes timing issues (slowdowns).
127 // NtYieldExecution();
131 PHARDWARE_TIMER
CreateHardwareTimer(ULONG Flags
, ULONGLONG Delay
, PHARDWARE_TIMER_PROC Callback
)
133 PHARDWARE_TIMER Timer
;
135 Timer
= RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*Timer
));
136 if (Timer
== NULL
) return NULL
;
138 Timer
->Flags
= Flags
& ~HARDWARE_TIMER_ENABLED
;
139 Timer
->EnableCount
= 0;
140 Timer
->Callback
= Callback
;
141 Timer
->LastTick
.QuadPart
= 0;
142 SetHardwareTimerDelay(Timer
, Delay
);
144 if (Flags
& HARDWARE_TIMER_ENABLED
) EnableHardwareTimer(Timer
);
148 VOID
EnableHardwareTimer(PHARDWARE_TIMER Timer
)
150 /* Increment the count */
151 Timer
->EnableCount
++;
153 /* Check if the count is above 0 but the timer isn't enabled */
154 if ((Timer
->EnableCount
> 0) && !(Timer
->Flags
& HARDWARE_TIMER_ENABLED
))
156 if (Timer
->Flags
& HARDWARE_TIMER_PRECISE
)
158 NtQueryPerformanceCounter(&Timer
->LastTick
, NULL
);
162 Timer
->LastTick
.LowPart
= GetTickCount();
165 Timer
->Flags
|= HARDWARE_TIMER_ENABLED
;
166 InsertTailList(&Timers
, &Timer
->Link
);
170 VOID
DisableHardwareTimer(PHARDWARE_TIMER Timer
)
172 /* Decrement the count */
173 Timer
->EnableCount
--;
175 /* Check if the count is 0 or less but the timer is enabled */
176 if ((Timer
->EnableCount
<= 0) && (Timer
->Flags
& HARDWARE_TIMER_ENABLED
))
178 /* Disable the timer */
179 Timer
->Flags
&= ~HARDWARE_TIMER_ENABLED
;
180 RemoveEntryList(&Timer
->Link
);
184 VOID
SetHardwareTimerDelay(PHARDWARE_TIMER Timer
, ULONGLONG NewDelay
)
186 if (Timer
->Flags
& HARDWARE_TIMER_PRECISE
)
188 /* Convert the delay from nanoseconds to performance counter ticks */
189 Timer
->Delay
= (NewDelay
* Frequency
.QuadPart
+ 500000000ULL) / 1000000000ULL;
193 Timer
->Delay
= NewDelay
/ 1000000ULL;
197 VOID
DestroyHardwareTimer(PHARDWARE_TIMER Timer
)
201 if (Timer
->Flags
& HARDWARE_TIMER_ENABLED
) RemoveEntryList(&Timer
->Link
);
202 RtlFreeHeap(RtlGetProcessHeap(), 0, Timer
);
206 ULONGLONG
GetCycleCount(VOID
)
211 ULONGLONG
GetCycleSpeed(VOID
)
216 BOOLEAN
ClockInitialize(VOID
)
218 InitializeListHead(&Timers
);
220 /* Initialize the performance counter (needed for hardware timers) */
221 /* Find the starting performance */
222 NtQueryPerformanceCounter(&StartPerfCount
, &Frequency
);
223 if (Frequency
.QuadPart
== 0)
225 wprintf(L
"FATAL: Performance counter not available\n");
229 /* Find the starting tick count */
230 // StartTickCount = GetTickCount();
232 IpsTimer
= CreateHardwareTimer(HARDWARE_TIMER_ENABLED
, HZ_TO_NS(1), IpsCallback
);
233 if (IpsTimer
== NULL
)
235 wprintf(L
"FATAL: Cannot create IPS timer.\n");