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