[NTVDM]
[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 "emulator.h"
15 #include "cpu/cpu.h"
16
17 #include "clock.h"
18
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"
24
25 /* Extra PSDK/NDK Headers */
26 #include <ndk/kefuncs.h>
27
28 /* DEFINES ********************************************************************/
29
30 /*
31 * Activate IPS_DISPLAY if you want to display the
32 * number of instructions per second.
33 */
34 // #define IPS_DISPLAY
35
36 /*
37 * Activate WORKING_TIMER when the PIT timing problem is fixed.
38 */
39 // #define WORKING_TIMER
40
41 /* Processor speed */
42 #define STEPS_PER_CYCLE 256
43
44 /* VARIABLES ******************************************************************/
45
46 static LIST_ENTRY Timers;
47 static LARGE_INTEGER StartPerfCount, Frequency;
48 static DWORD StartTickCount;
49
50 #ifdef IPS_DISPLAY
51 static ULONGLONG Cycles = 0ULL;
52 #endif
53
54 /* PRIVATE FUNCTIONS **********************************************************/
55
56 #ifdef IPS_DISPLAY
57 static VOID FASTCALL IpsDisplayCallback(ULONGLONG ElapsedTime)
58 {
59 DPRINT1("NTVDM: %I64u Instructions Per Second\n", Cycles / ElapsedTime);
60 Cycles = 0ULL;
61 }
62 #endif
63
64 /* PUBLIC FUNCTIONS ***********************************************************/
65
66 VOID ClockUpdate(VOID)
67 {
68 extern BOOLEAN CpuRunning;
69 UINT i;
70 PLIST_ENTRY Entry;
71 LARGE_INTEGER Counter;
72
73 while (VdmRunning && CpuRunning)
74 {
75 /* Get the current number of ticks */
76 DWORD CurrentTickCount = GetTickCount();
77
78 #ifdef WORKING_TIMER
79 if ((PitResolution <= 1000) && (RtcFrequency <= 1000))
80 {
81 /* Calculate the approximate performance counter value instead */
82 Counter.QuadPart = StartPerfCount.QuadPart
83 + ((CurrentTickCount - StartTickCount)
84 * Frequency.QuadPart) / 1000;
85 }
86 else
87 #endif
88 {
89 /* Get the current performance counter value */
90 /// DWORD_PTR oldmask = SetThreadAffinityMask(GetCurrentThread(), 0);
91 NtQueryPerformanceCounter(&Counter, NULL);
92 /// SetThreadAffinityMask(GetCurrentThread(), oldmask);
93 }
94
95 /* Continue CPU emulation */
96 for (i = 0; VdmRunning && CpuRunning && (i < STEPS_PER_CYCLE); i++)
97 {
98 CpuStep();
99
100 #ifdef IPS_DISPLAY
101 ++Cycles;
102 #endif
103 }
104
105 for (Entry = Timers.Flink; Entry != &Timers; Entry = Entry->Flink)
106 {
107 ULONGLONG Ticks = (ULONGLONG)-1;
108 PHARDWARE_TIMER Timer = CONTAINING_RECORD(Entry, HARDWARE_TIMER, Link);
109
110 ASSERT((Timer->EnableCount > 0) && (Timer->Flags & HARDWARE_TIMER_ENABLED));
111
112 if (Timer->Delay)
113 {
114 if (Timer->Flags & HARDWARE_TIMER_PRECISE)
115 {
116 /* Use the performance counter for precise timers */
117 if (Counter.QuadPart <= Timer->LastTick.QuadPart) continue;
118 Ticks = (Counter.QuadPart - Timer->LastTick.QuadPart) / Timer->Delay;
119 }
120 else
121 {
122 /* Use the regular tick count for normal timers */
123 if (CurrentTickCount <= Timer->LastTick.LowPart) continue;
124 Ticks = (CurrentTickCount - Timer->LastTick.LowPart) / (ULONG)Timer->Delay;
125 }
126
127 if (Ticks == 0) continue;
128 }
129
130 Timer->Callback(Ticks);
131
132 if (Timer->Flags & HARDWARE_TIMER_ONESHOT)
133 {
134 /* Disable this timer */
135 DisableHardwareTimer(Timer);
136 }
137
138 /* Update the time of the last timer tick */
139 Timer->LastTick.QuadPart += Ticks * Timer->Delay;
140 }
141 }
142 }
143
144 PHARDWARE_TIMER CreateHardwareTimer(ULONG Flags, ULONG Delay, PHARDWARE_TIMER_PROC Callback)
145 {
146 PHARDWARE_TIMER Timer;
147
148 Timer = (PHARDWARE_TIMER)RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(HARDWARE_TIMER));
149 if (Timer == NULL) return NULL;
150
151 Timer->Flags = Flags & ~HARDWARE_TIMER_ENABLED;
152 Timer->EnableCount = 0;
153 Timer->Callback = Callback;
154 SetHardwareTimerDelay(Timer, (ULONGLONG)Delay);
155
156 if (Flags & HARDWARE_TIMER_ENABLED) EnableHardwareTimer(Timer);
157 return Timer;
158 }
159
160 VOID EnableHardwareTimer(PHARDWARE_TIMER Timer)
161 {
162 /* Increment the count */
163 Timer->EnableCount++;
164
165 /* Check if the count is above 0 but the timer isn't enabled */
166 if ((Timer->EnableCount > 0) && !(Timer->Flags & HARDWARE_TIMER_ENABLED))
167 {
168 if (Timer->Flags & HARDWARE_TIMER_PRECISE)
169 {
170 NtQueryPerformanceCounter(&Timer->LastTick, NULL);
171 }
172 else
173 {
174 Timer->LastTick.LowPart = GetTickCount();
175 }
176
177 Timer->Flags |= HARDWARE_TIMER_ENABLED;
178 InsertTailList(&Timers, &Timer->Link);
179 }
180 }
181
182 VOID DisableHardwareTimer(PHARDWARE_TIMER Timer)
183 {
184 /* Decrement the count */
185 Timer->EnableCount--;
186
187 /* Check if the count is 0 or less but the timer is enabled */
188 if ((Timer->EnableCount <= 0) && (Timer->Flags & HARDWARE_TIMER_ENABLED))
189 {
190 /* Disable the timer */
191 Timer->Flags &= ~HARDWARE_TIMER_ENABLED;
192 RemoveEntryList(&Timer->Link);
193 }
194 }
195
196 VOID SetHardwareTimerDelay(PHARDWARE_TIMER Timer, ULONGLONG NewDelay)
197 {
198 if (Timer->Flags & HARDWARE_TIMER_PRECISE)
199 {
200 /* Convert the delay from nanoseconds to performance counter ticks */
201 Timer->Delay = (NewDelay * Frequency.QuadPart + 500000000ULL) / 1000000000ULL;
202 }
203 else
204 {
205 Timer->Delay = NewDelay;
206 }
207 }
208
209 VOID DestroyHardwareTimer(PHARDWARE_TIMER Timer)
210 {
211 if (Timer->Flags & HARDWARE_TIMER_ENABLED) RemoveEntryList(&Timer->Link);
212 RtlFreeHeap(RtlGetProcessHeap(), 0, Timer);
213 }
214
215 BOOLEAN ClockInitialize(VOID)
216 {
217 #ifdef IPS_DISPLAY
218 PHARDWARE_TIMER IpsTimer;
219 #endif
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 #ifdef IPS_DISPLAY
236
237 IpsTimer = CreateHardwareTimer(HARDWARE_TIMER_ENABLED, 1000, IpsDisplayCallback);
238 if (IpsTimer == NULL)
239 {
240 wprintf(L"FATAL: Cannot create IPS display timer.\n");
241 return FALSE;
242 }
243
244 #endif
245
246 return TRUE;
247 }