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