[MEDIA][FONTS] Improve tahoma.ttf (Cyrillic, Greek and Latin) No.24
[reactos.git] / hal / halx86 / generic / timer.c
1 /*
2 * PROJECT: ReactOS HAL
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: hal/halx86/generic/timer.c
5 * PURPOSE: HAL Timer Routines
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 * Timo Kreuzer (timo.kreuzer@reactos.org)
8 */
9
10 /* INCLUDES ******************************************************************/
11
12 #include <hal.h>
13 #define NDEBUG
14 #include <debug.h>
15
16 #if defined(ALLOC_PRAGMA) && !defined(_MINIHAL_)
17 #pragma alloc_text(INIT, HalpInitializeClock)
18 #endif
19
20 /* GLOBALS *******************************************************************/
21
22 #define PIT_LATCH 0x00
23
24 LARGE_INTEGER HalpLastPerfCounter;
25 LARGE_INTEGER HalpPerfCounter;
26 ULONG HalpPerfCounterCutoff;
27 BOOLEAN HalpClockSetMSRate;
28 ULONG HalpCurrentTimeIncrement;
29 ULONG HalpCurrentRollOver;
30 ULONG HalpNextMSRate = 14;
31 ULONG HalpLargestClockMS = 15;
32
33 static struct _HALP_ROLLOVER
34 {
35 ULONG RollOver;
36 ULONG Increment;
37 } HalpRolloverTable[15] =
38 {
39 {1197, 10032},
40 {2394, 20064},
41 {3591, 30096},
42 {4767, 39952},
43 {5964, 49984},
44 {7161, 60016},
45 {8358, 70048},
46 {9555, 80080},
47 {10731, 89936},
48 {11949, 100144},
49 {13125, 110000},
50 {14322, 120032},
51 {15519, 130064},
52 {16695, 139920},
53 {17892, 149952}
54 };
55
56 /* PRIVATE FUNCTIONS *********************************************************/
57
58 FORCEINLINE
59 ULONG
60 HalpRead8254Value(void)
61 {
62 ULONG TimerValue;
63
64 /* Send counter latch command for channel 0 */
65 __outbyte(TIMER_CONTROL_PORT, PIT_LATCH);
66 __nop();
67
68 /* Read the value, LSB first */
69 TimerValue = __inbyte(TIMER_CHANNEL0_DATA_PORT);
70 __nop();
71 TimerValue |= __inbyte(TIMER_CHANNEL0_DATA_PORT) << 8;
72
73 return TimerValue;
74 }
75
76 VOID
77 NTAPI
78 HalpSetTimerRollOver(USHORT RollOver)
79 {
80 ULONG_PTR Flags;
81 TIMER_CONTROL_PORT_REGISTER TimerControl;
82
83 /* Disable interrupts */
84 Flags = __readeflags();
85 _disable();
86
87 /* Program the PIT for binary mode */
88 TimerControl.BcdMode = FALSE;
89
90 /*
91 * Program the PIT to generate a normal rate wave (Mode 3) on channel 0.
92 * Channel 0 is used for the IRQ0 clock interval timer, and channel
93 * 1 is used for DRAM refresh.
94 *
95 * Mode 2 gives much better accuracy than Mode 3.
96 */
97 TimerControl.OperatingMode = PitOperatingMode2;
98 TimerControl.Channel = PitChannel0;
99
100 /* Set the access mode that we'll use to program the reload value */
101 TimerControl.AccessMode = PitAccessModeLowHigh;
102
103 /* Now write the programming bits */
104 __outbyte(TIMER_CONTROL_PORT, TimerControl.Bits);
105
106 /* Next we write the reload value for channel 0 */
107 __outbyte(TIMER_CHANNEL0_DATA_PORT, RollOver & 0xFF);
108 __outbyte(TIMER_CHANNEL0_DATA_PORT, RollOver >> 8);
109
110 /* Restore interrupts if they were previously enabled */
111 __writeeflags(Flags);
112 }
113
114 INIT_FUNCTION
115 VOID
116 NTAPI
117 HalpInitializeClock(VOID)
118 {
119 ULONG Increment;
120 USHORT RollOver;
121
122 DPRINT("HalpInitializeClock()\n");
123
124 /* Get increment and rollover for the largest time clock ms possible */
125 Increment = HalpRolloverTable[HalpLargestClockMS - 1].Increment;
126 RollOver = (USHORT)HalpRolloverTable[HalpLargestClockMS - 1].RollOver;
127
128 /* Set the maximum and minimum increment with the kernel */
129 KeSetTimeIncrement(Increment, HalpRolloverTable[0].Increment);
130
131 /* Set the rollover value for the timer */
132 HalpSetTimerRollOver(RollOver);
133
134 /* Save rollover and increment */
135 HalpCurrentRollOver = RollOver;
136 HalpCurrentTimeIncrement = Increment;
137 }
138
139 #ifdef _M_IX86
140 #ifndef _MINIHAL_
141 VOID
142 FASTCALL
143 HalpClockInterruptHandler(IN PKTRAP_FRAME TrapFrame)
144 {
145 ULONG LastIncrement;
146 KIRQL Irql;
147
148 /* Enter trap */
149 KiEnterInterruptTrap(TrapFrame);
150
151 /* Start the interrupt */
152 if (HalBeginSystemInterrupt(CLOCK2_LEVEL, PRIMARY_VECTOR_BASE, &Irql))
153 {
154 /* Update the performance counter */
155 HalpPerfCounter.QuadPart += HalpCurrentRollOver;
156 HalpPerfCounterCutoff = KiEnableTimerWatchdog;
157
158 /* Save increment */
159 LastIncrement = HalpCurrentTimeIncrement;
160
161 /* Check if someone changed the time rate */
162 if (HalpClockSetMSRate)
163 {
164 /* Update the global values */
165 HalpCurrentTimeIncrement = HalpRolloverTable[HalpNextMSRate - 1].Increment;
166 HalpCurrentRollOver = HalpRolloverTable[HalpNextMSRate - 1].RollOver;
167
168 /* Set new timer rollover */
169 HalpSetTimerRollOver((USHORT)HalpCurrentRollOver);
170
171 /* We're done */
172 HalpClockSetMSRate = FALSE;
173 }
174
175 /* Update the system time -- the kernel will exit this trap */
176 KeUpdateSystemTime(TrapFrame, LastIncrement, Irql);
177 }
178
179 /* Spurious, just end the interrupt */
180 KiEoiHelper(TrapFrame);
181 }
182
183 VOID
184 FASTCALL
185 HalpProfileInterruptHandler(IN PKTRAP_FRAME TrapFrame)
186 {
187 KIRQL Irql;
188
189 /* Enter trap */
190 KiEnterInterruptTrap(TrapFrame);
191
192 /* Start the interrupt */
193 if (HalBeginSystemInterrupt(PROFILE_LEVEL, PRIMARY_VECTOR_BASE + 8, &Irql))
194 {
195 /* Spin until the interrupt pending bit is clear */
196 HalpAcquireCmosSpinLock();
197 while (HalpReadCmos(RTC_REGISTER_C) & RTC_REG_C_IRQ)
198 ;
199 HalpReleaseCmosSpinLock();
200
201 /* If profiling is enabled, call the kernel function */
202 if (!HalpProfilingStopped)
203 {
204 KeProfileInterrupt(TrapFrame);
205 }
206
207 /* Finish the interrupt */
208 _disable();
209 HalEndSystemInterrupt(Irql, TrapFrame);
210 }
211
212 /* Spurious, just end the interrupt */
213 KiEoiHelper(TrapFrame);
214 }
215 #endif
216
217 #endif
218
219 /* PUBLIC FUNCTIONS ***********************************************************/
220
221 /*
222 * @implemented
223 */
224 VOID
225 NTAPI
226 HalCalibratePerformanceCounter(IN volatile PLONG Count,
227 IN ULONGLONG NewCount)
228 {
229 ULONG_PTR Flags;
230
231 /* Disable interrupts */
232 Flags = __readeflags();
233 _disable();
234
235 /* Do a decrement for this CPU */
236 _InterlockedDecrement(Count);
237
238 /* Wait for other CPUs */
239 while (*Count);
240
241 /* Restore interrupts if they were previously enabled */
242 __writeeflags(Flags);
243 }
244
245 /*
246 * @implemented
247 */
248 ULONG
249 NTAPI
250 HalSetTimeIncrement(IN ULONG Increment)
251 {
252 /* Round increment to ms */
253 Increment /= 10000;
254
255 /* Normalize between our minimum (1 ms) and maximum (variable) setting */
256 if (Increment > HalpLargestClockMS) Increment = HalpLargestClockMS;
257 if (Increment <= 0) Increment = 1;
258
259 /* Set the rate and tell HAL we want to change it */
260 HalpNextMSRate = Increment;
261 HalpClockSetMSRate = TRUE;
262
263 /* Return the increment */
264 return HalpRolloverTable[Increment - 1].Increment;
265 }
266
267 LARGE_INTEGER
268 NTAPI
269 KeQueryPerformanceCounter(PLARGE_INTEGER PerformanceFrequency)
270 {
271 LARGE_INTEGER CurrentPerfCounter;
272 ULONG CounterValue, ClockDelta;
273 KIRQL OldIrql;
274
275 /* If caller wants performance frequency, return hardcoded value */
276 if (PerformanceFrequency) PerformanceFrequency->QuadPart = PIT_FREQUENCY;
277
278 /* Check if we were called too early */
279 if (HalpCurrentRollOver == 0) return HalpPerfCounter;
280
281 /* Check if interrupts are disabled */
282 if(!(__readeflags() & EFLAGS_INTERRUPT_MASK)) return HalpPerfCounter;
283
284 /* Raise irql to DISPATCH_LEVEL */
285 OldIrql = KeGetCurrentIrql();
286 if (OldIrql < DISPATCH_LEVEL) KfRaiseIrql(DISPATCH_LEVEL);
287
288 do
289 {
290 /* Get the current performance counter value */
291 CurrentPerfCounter = HalpPerfCounter;
292
293 /* Read the 8254 counter value */
294 CounterValue = HalpRead8254Value();
295
296 /* Repeat if the value has changed (a clock interrupt happened) */
297 } while (CurrentPerfCounter.QuadPart != HalpPerfCounter.QuadPart);
298
299 /* After someone changed the clock rate, during the first clock cycle we
300 might see a counter value larger than the rollover. In this case we
301 pretend it already has the new rollover value. */
302 if (CounterValue > HalpCurrentRollOver) CounterValue = HalpCurrentRollOver;
303
304 /* The interrupt is issued on the falling edge of the OUT line, when the
305 counter changes from 1 to max. Calculate a clock delta, so that directly
306 after the interrupt it is 0, going up to (HalpCurrentRollOver - 1). */
307 ClockDelta = HalpCurrentRollOver - CounterValue;
308
309 /* Add the clock delta */
310 CurrentPerfCounter.QuadPart += ClockDelta;
311
312 /* Check if the value is smaller then before, this means, we somehow
313 missed an interrupt. This is a sign that the timer interrupt
314 is very inaccurate. Probably a virtual machine. */
315 if (CurrentPerfCounter.QuadPart < HalpLastPerfCounter.QuadPart)
316 {
317 /* We missed an interrupt. Assume we will receive it later */
318 CurrentPerfCounter.QuadPart += HalpCurrentRollOver;
319 }
320
321 /* Update the last counter value */
322 HalpLastPerfCounter = CurrentPerfCounter;
323
324 /* Restore previous irql */
325 if (OldIrql < DISPATCH_LEVEL) KfLowerIrql(OldIrql);
326
327 /* Return the result */
328 return CurrentPerfCounter;
329 }
330
331 /* EOF */