f03d6c6905732bee2ed48cf05bb73fe730354850
[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 /* Processor speed */
37 #define STEPS_PER_CYCLE 1024
38
39 /* VARIABLES ******************************************************************/
40
41 static LIST_ENTRY Timers;
42 static LARGE_INTEGER StartPerfCount, Frequency;
43 static DWORD StartTickCount;
44
45 #ifdef IPS_DISPLAY
46 static ULONGLONG Cycles = 0ULL;
47 #endif
48
49 /* PRIVATE FUNCTIONS **********************************************************/
50
51 #ifdef IPS_DISPLAY
52 static VOID FASTCALL IpsDisplayCallback(ULONGLONG ElapsedTime)
53 {
54 DPRINT1("NTVDM: %I64u Instructions Per Second\n", Cycles / ElapsedTime);
55 Cycles = 0ULL;
56 }
57 #endif
58
59 /* PUBLIC FUNCTIONS ***********************************************************/
60
61 VOID ClockUpdate(VOID)
62 {
63 extern BOOLEAN CpuRunning;
64 UINT i;
65 PLIST_ENTRY Entry;
66 LARGE_INTEGER Counter;
67
68 while (VdmRunning && CpuRunning)
69 {
70 /* Get the current number of ticks */
71 DWORD CurrentTickCount = GetTickCount();
72
73 /* Get the current performance counter value */
74 /// DWORD_PTR oldmask = SetThreadAffinityMask(GetCurrentThread(), 0);
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
83 #ifdef IPS_DISPLAY
84 ++Cycles;
85 #endif
86 }
87
88 for (Entry = Timers.Flink; Entry != &Timers; Entry = Entry->Flink)
89 {
90 ULONGLONG Ticks = (ULONGLONG)-1;
91 PHARDWARE_TIMER Timer = CONTAINING_RECORD(Entry, HARDWARE_TIMER, Link);
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 }
126
127 PHARDWARE_TIMER CreateHardwareTimer(ULONG Flags, ULONGLONG Delay, PHARDWARE_TIMER_PROC Callback)
128 {
129 PHARDWARE_TIMER Timer;
130
131 Timer = (PHARDWARE_TIMER)RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(HARDWARE_TIMER));
132 if (Timer == NULL) return NULL;
133
134 Timer->Flags = Flags & ~HARDWARE_TIMER_ENABLED;
135 Timer->EnableCount = 0;
136 Timer->Callback = Callback;
137 SetHardwareTimerDelay(Timer, Delay);
138
139 if (Flags & HARDWARE_TIMER_ENABLED) EnableHardwareTimer(Timer);
140 return Timer;
141 }
142
143 VOID EnableHardwareTimer(PHARDWARE_TIMER Timer)
144 {
145 /* Increment the count */
146 Timer->EnableCount++;
147
148 /* Check if the count is above 0 but the timer isn't enabled */
149 if ((Timer->EnableCount > 0) && !(Timer->Flags & HARDWARE_TIMER_ENABLED))
150 {
151 if (Timer->Flags & HARDWARE_TIMER_PRECISE)
152 {
153 NtQueryPerformanceCounter(&Timer->LastTick, NULL);
154 }
155 else
156 {
157 Timer->LastTick.LowPart = GetTickCount();
158 }
159
160 Timer->Flags |= HARDWARE_TIMER_ENABLED;
161 InsertTailList(&Timers, &Timer->Link);
162 }
163 }
164
165 VOID DisableHardwareTimer(PHARDWARE_TIMER Timer)
166 {
167 /* Decrement the count */
168 Timer->EnableCount--;
169
170 /* Check if the count is 0 or less but the timer is enabled */
171 if ((Timer->EnableCount <= 0) && (Timer->Flags & HARDWARE_TIMER_ENABLED))
172 {
173 /* Disable the timer */
174 Timer->Flags &= ~HARDWARE_TIMER_ENABLED;
175 RemoveEntryList(&Timer->Link);
176 }
177 }
178
179 VOID SetHardwareTimerDelay(PHARDWARE_TIMER Timer, ULONGLONG NewDelay)
180 {
181 if (Timer->Flags & HARDWARE_TIMER_PRECISE)
182 {
183 /* Convert the delay from nanoseconds to performance counter ticks */
184 Timer->Delay = (NewDelay * Frequency.QuadPart + 500000000ULL) / 1000000000ULL;
185 }
186 else
187 {
188 Timer->Delay = NewDelay / 1000000ULL;
189 }
190 }
191
192 VOID DestroyHardwareTimer(PHARDWARE_TIMER Timer)
193 {
194 if (Timer->Flags & HARDWARE_TIMER_ENABLED) RemoveEntryList(&Timer->Link);
195 RtlFreeHeap(RtlGetProcessHeap(), 0, Timer);
196 }
197
198 BOOLEAN ClockInitialize(VOID)
199 {
200 #ifdef IPS_DISPLAY
201 PHARDWARE_TIMER IpsTimer;
202 #endif
203
204 InitializeListHead(&Timers);
205
206 /* Initialize the performance counter (needed for hardware timers) */
207 /* Find the starting performance */
208 NtQueryPerformanceCounter(&StartPerfCount, &Frequency);
209 if (Frequency.QuadPart == 0)
210 {
211 wprintf(L"FATAL: Performance counter not available\n");
212 return FALSE;
213 }
214
215 /* Find the starting tick count */
216 StartTickCount = GetTickCount();
217
218 #ifdef IPS_DISPLAY
219
220 IpsTimer = CreateHardwareTimer(HARDWARE_TIMER_ENABLED, HZ_TO_NS(1), IpsDisplayCallback);
221 if (IpsTimer == NULL)
222 {
223 wprintf(L"FATAL: Cannot create IPS display timer.\n");
224 return FALSE;
225 }
226
227 #endif
228
229 return TRUE;
230 }