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 } 244