[NTVDM]
[reactos.git] / reactos / subsystems / 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/timer.h"
22 #include "hardware/vga.h"
23
24 /* DEFINES ********************************************************************/
25
26 /*
27 * Activate IPS_DISPLAY if you want to display the
28 * number of instructions per second, as well as
29 * the computed number of ticks for the PIT.
30 */
31 // #define IPS_DISPLAY
32
33 /*
34 * Activate WORKING_TIMER when the PIT timing problem is fixed.
35 */
36 // #define WORKING_TIMER
37
38
39 /* Processor speed */
40 #define STEPS_PER_CYCLE 256
41 #define IRQ1_CYCLES 16
42 #define IRQ12_CYCLES 16
43
44 /* VARIABLES ******************************************************************/
45
46 LARGE_INTEGER StartPerfCount, Frequency;
47
48 LARGE_INTEGER LastTimerTick, LastRtcTick, Counter;
49 LONGLONG TimerTicks;
50 DWORD StartTickCount, CurrentTickCount;
51 DWORD LastClockUpdate;
52 DWORD LastVerticalRefresh;
53
54 UINT Irq1Counter = 0;
55 UINT Irq12Counter = 0;
56
57 #ifdef IPS_DISPLAY
58 DWORD LastCyclePrintout;
59 DWORD Cycles = 0;
60 #endif
61
62 /* PUBLIC FUNCTIONS ***********************************************************/
63
64 VOID ClockUpdate(VOID)
65 {
66 extern BOOLEAN CpuRunning;
67 UINT i;
68
69 #ifdef WORKING_TIMER
70 DWORD PitResolution = PitGetResolution();
71 #endif
72 DWORD RtcFrequency = RtcGetTicksPerSecond();
73
74 /* Get the current number of ticks */
75 CurrentTickCount = GetTickCount();
76
77 #ifdef WORKING_TIMER
78 if ((PitResolution <= 1000) && (RtcFrequency <= 1000))
79 {
80 /* Calculate the approximate performance counter value instead */
81 Counter.QuadPart = StartPerfCount.QuadPart
82 + (CurrentTickCount - StartTickCount)
83 * (Frequency.QuadPart / 1000);
84 }
85 else
86 #endif
87 {
88 /* Get the current performance counter value */
89 QueryPerformanceCounter(&Counter);
90 }
91
92 /* Get the number of PIT ticks that have passed */
93 TimerTicks = ((Counter.QuadPart - LastTimerTick.QuadPart)
94 * PIT_BASE_FREQUENCY) / Frequency.QuadPart;
95
96 /* Update the PIT */
97 if (TimerTicks > 0)
98 {
99 PitClock(TimerTicks);
100 LastTimerTick = Counter;
101 }
102
103 /* Check for RTC update */
104 if ((CurrentTickCount - LastClockUpdate) >= 1000)
105 {
106 RtcTimeUpdate();
107 LastClockUpdate = CurrentTickCount;
108 }
109
110 /* Check for RTC periodic tick */
111 if ((Counter.QuadPart - LastRtcTick.QuadPart)
112 >= (Frequency.QuadPart / (LONGLONG)RtcFrequency))
113 {
114 RtcPeriodicTick();
115 LastRtcTick = Counter;
116 }
117
118 /* Check for vertical retrace */
119 if ((CurrentTickCount - LastVerticalRefresh) >= 15)
120 {
121 VgaRefreshDisplay();
122 LastVerticalRefresh = CurrentTickCount;
123 }
124
125 if (++Irq1Counter == IRQ1_CYCLES)
126 {
127 GenerateIrq1();
128 Irq1Counter = 0;
129 }
130
131 if (++Irq12Counter == IRQ12_CYCLES)
132 {
133 GenerateIrq12();
134 Irq12Counter = 0;
135 }
136
137 /* Horizontal retrace occurs as fast as possible */
138 VgaHorizontalRetrace();
139
140 /* Continue CPU emulation */
141 for (i = 0; VdmRunning && CpuRunning && (i < STEPS_PER_CYCLE); i++)
142 {
143 CpuStep();
144 #ifdef IPS_DISPLAY
145 Cycles++;
146 #endif
147 }
148
149 #ifdef IPS_DISPLAY
150 if ((CurrentTickCount - LastCyclePrintout) >= 1000)
151 {
152 DPRINT1("NTVDM: %lu Instructions Per Second; TimerTicks = %I64d\n", Cycles, TimerTicks);
153 LastCyclePrintout = CurrentTickCount;
154 Cycles = 0;
155 }
156 #endif
157 }
158
159 BOOLEAN ClockInitialize(VOID)
160 {
161 /* Initialize the performance counter (needed for hardware timers) */
162 if (!QueryPerformanceFrequency(&Frequency))
163 {
164 wprintf(L"FATAL: Performance counter not available\n");
165 return FALSE;
166 }
167
168 /* Find the starting performance and tick count */
169 StartTickCount = GetTickCount();
170 QueryPerformanceCounter(&StartPerfCount);
171
172 /* Set the different last counts to the starting count */
173 LastClockUpdate = LastVerticalRefresh =
174 #ifdef IPS_DISPLAY
175 LastCyclePrintout =
176 #endif
177 StartTickCount;
178
179 /* Set the last timer ticks to the current time */
180 LastTimerTick = LastRtcTick = StartPerfCount;
181
182 return TRUE;
183 }