[NTVDM] Revert r69435.
[reactos.git] / reactos / subsystems / mvdm / ntvdm / clock.c
1 /*
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)
8 */
9
10 /* INCLUDES *******************************************************************/
11
12 #include "ntvdm.h"
13
14 #define NDEBUG
15 #include <debug.h>
16
17 #include "emulator.h"
18 #include "clock.h"
19
20 #include "cpu/cpu.h"
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"
26
27 /* DEFINES ********************************************************************/
28
29 /*
30 * Activate IPS_DISPLAY if you want to display the
31 * number of instructions per second.
32 */
33 // #define IPS_DISPLAY
34
35 /* Processor speed */
36 #define STEPS_PER_CYCLE 1024
37
38 /* VARIABLES ******************************************************************/
39
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;
49
50 /* PRIVATE FUNCTIONS **********************************************************/
51
52 static VOID FASTCALL IpsCallback(ULONGLONG ElapsedTime)
53 {
54 CurrentIps = (Cycles - LastCycles) / ElapsedTime;
55
56 #ifdef IPS_DISPLAY
57 DPRINT1("NTVDM: %I64u Instructions Per Second\n", CurrentIps);
58 #endif
59
60 LastCycles = Cycles;
61 }
62
63 /* PUBLIC FUNCTIONS ***********************************************************/
64
65 VOID ClockUpdate(VOID)
66 {
67 extern BOOLEAN CpuRunning;
68 UINT i;
69 PLIST_ENTRY Entry;
70 PHARDWARE_TIMER Timer;
71
72 while (VdmRunning && CpuRunning)
73 {
74 /* Get the current counters */
75 /// DWORD_PTR oldmask = SetThreadAffinityMask(GetCurrentThread(), 0);
76 CurrentTickCount = GetTickCount();
77 NtQueryPerformanceCounter(&Counter, NULL);
78 /// SetThreadAffinityMask(GetCurrentThread(), oldmask);
79
80 /* Continue CPU emulation */
81 for (i = 0; VdmRunning && CpuRunning && (i < STEPS_PER_CYCLE); i++)
82 {
83 CpuStep();
84 ++Cycles;
85 }
86
87 Entry = Timers.Flink;
88 while (Entry != &Timers)
89 {
90 ULONGLONG Ticks = (ULONGLONG)-1;
91
92 Timer = CONTAINING_RECORD(Entry, HARDWARE_TIMER, Link);
93 Entry = Entry->Flink;
94
95 ASSERT((Timer->EnableCount > 0) && (Timer->Flags & HARDWARE_TIMER_ENABLED));
96
97 if (Timer->Delay)
98 {
99 if (Timer->Flags & HARDWARE_TIMER_PRECISE)
100 {
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;
104 }
105 else
106 {
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;
110 }
111
112 if (Ticks == 0) continue;
113 }
114
115 Timer->Callback(Ticks);
116
117 if (Timer->Flags & HARDWARE_TIMER_ONESHOT)
118 {
119 /* Disable this timer */
120 DisableHardwareTimer(Timer);
121 }
122
123 /* Update the time of the last timer tick */
124 Timer->LastTick.QuadPart += Ticks * Timer->Delay;
125 }
126
127 /* Yield execution to other threads */
128 // FIXME: Disabled because it causes timing issues (slowdowns).
129 // NtYieldExecution();
130 }
131 }
132
133 PHARDWARE_TIMER CreateHardwareTimer(ULONG Flags, ULONGLONG Delay, PHARDWARE_TIMER_PROC Callback)
134 {
135 PHARDWARE_TIMER Timer;
136
137 Timer = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*Timer));
138 if (Timer == NULL) return NULL;
139
140 Timer->Flags = Flags & ~HARDWARE_TIMER_ENABLED;
141 Timer->EnableCount = 0;
142 Timer->Callback = Callback;
143 Timer->LastTick.QuadPart = 0;
144 SetHardwareTimerDelay(Timer, Delay);
145
146 if (Flags & HARDWARE_TIMER_ENABLED) EnableHardwareTimer(Timer);
147 return Timer;
148 }
149
150 VOID EnableHardwareTimer(PHARDWARE_TIMER Timer)
151 {
152 /* Increment the count */
153 Timer->EnableCount++;
154
155 /* Check if the count is above 0 but the timer isn't enabled */
156 if ((Timer->EnableCount > 0) && !(Timer->Flags & HARDWARE_TIMER_ENABLED))
157 {
158 if (Timer->Flags & HARDWARE_TIMER_PRECISE)
159 {
160 NtQueryPerformanceCounter(&Timer->LastTick, NULL);
161 }
162 else
163 {
164 Timer->LastTick.LowPart = GetTickCount();
165 }
166
167 Timer->Flags |= HARDWARE_TIMER_ENABLED;
168 InsertTailList(&Timers, &Timer->Link);
169 }
170 }
171
172 VOID DisableHardwareTimer(PHARDWARE_TIMER Timer)
173 {
174 /* Decrement the count */
175 Timer->EnableCount--;
176
177 /* Check if the count is 0 or less but the timer is enabled */
178 if ((Timer->EnableCount <= 0) && (Timer->Flags & HARDWARE_TIMER_ENABLED))
179 {
180 /* Disable the timer */
181 Timer->Flags &= ~HARDWARE_TIMER_ENABLED;
182 RemoveEntryList(&Timer->Link);
183 }
184 }
185
186 VOID SetHardwareTimerDelay(PHARDWARE_TIMER Timer, ULONGLONG NewDelay)
187 {
188 if (Timer->Flags & HARDWARE_TIMER_PRECISE)
189 {
190 /* Convert the delay from nanoseconds to performance counter ticks */
191 Timer->Delay = (NewDelay * Frequency.QuadPart + 500000000ULL) / 1000000000ULL;
192 }
193 else
194 {
195 Timer->Delay = NewDelay / 1000000ULL;
196 }
197 }
198
199 VOID DestroyHardwareTimer(PHARDWARE_TIMER Timer)
200 {
201 if (Timer)
202 {
203 if (Timer->Flags & HARDWARE_TIMER_ENABLED) RemoveEntryList(&Timer->Link);
204 RtlFreeHeap(RtlGetProcessHeap(), 0, Timer);
205 }
206 }
207
208 ULONGLONG GetCycleCount(VOID)
209 {
210 return Cycles;
211 }
212
213 ULONGLONG GetCycleSpeed(VOID)
214 {
215 return CurrentIps;
216 }
217
218 BOOLEAN ClockInitialize(VOID)
219 {
220 InitializeListHead(&Timers);
221
222 /* Initialize the performance counter (needed for hardware timers) */
223 /* Find the starting performance */
224 NtQueryPerformanceCounter(&StartPerfCount, &Frequency);
225 if (Frequency.QuadPart == 0)
226 {
227 wprintf(L"FATAL: Performance counter not available\n");
228 return FALSE;
229 }
230
231 /* Find the starting tick count */
232 // StartTickCount = GetTickCount();
233
234 IpsTimer = CreateHardwareTimer(HARDWARE_TIMER_ENABLED, HZ_TO_NS(1), IpsCallback);
235 if (IpsTimer == NULL)
236 {
237 wprintf(L"FATAL: Cannot create IPS timer.\n");
238 return FALSE;
239 }
240
241 return TRUE;
242 }