2 * FILE: hal/halx86/generic/timer.S
3 * COPYRIGHT: See COPYING in the top level directory
4 * PURPOSE: System Timer Interrupt and Management
5 * PROGRAMMER: Alex Ionescu (alex@relsoft.net)
8 /* INCLUDES ******************************************************************/
10 #include <reactos/asm.h>
13 EXTERN _HalpAcquireSystemHardwareSpinLock@0:PROC
14 EXTERN _HalpReleaseCmosSpinLock@0:PROC
15 EXTERN _DbgBreakPoint@0:PROC
16 EXTERN _HalpCurrentRollOver:DWORD
17 EXTERN _HalpPerfCounterCutoff:DWORD
19 #define PIC1_BASE HEX(20) /* IO base address for master PIC */
20 #define PIC2_BASE HEX(A0) /* IO base address for slave PIC */
21 #define PIC1_COMMAND PIC1_BASE
22 #define PIC1_DATA (PIC1_BASE+1)
23 #define PIC2_COMMAND PIC2_BASE
24 #define PIC2_DATA (PIC2_BASE+1)
25 #define PIC_EOI HEX(20)
26 #define PIC_SPECIFIC_EOI2 HEX(62)
28 #define CMOS_ADDR HEX(70)
29 #define CMOS_DATA HEX(71)
30 #define CMOS_REGISTER_A HEX(0A)
31 #define CMOS_REGISTER_B HEX(0B)
32 #define CMOS_REGISTER_C HEX(0C)
33 #define CMOS_REGISTER_D HEX(0D)
35 #define PIT_CH0 HEX(40)
36 #define PIT_MODE HEX(43)
37 #define SYSTEM_CTRL_PORT_A HEX(92)
39 /* GLOBALS *******************************************************************/
42 ASSUME CS:NOTHING, DS:NOTHING, ES:NOTHING, FS:NOTHING, GS:NOTHING
44 PUBLIC _HalpPerfCounter
45 _HalpLastPerfCounterLow: .long 0
46 _HalpLastPerfCounterHigh: .long 0
48 _HalpPerfCounterLow: .long 0
49 _HalpPerfCounterHigh: .long 0
50 _HalpSystemHardwareFlags: .long 0
52 /* FUNCTIONS *****************************************************************/
55 PUBLIC _HalpCalibrateStallExecution@0
56 _HalpCalibrateStallExecution@0:
58 /* Setup the stack frame */
63 /* Save EFLAGS and kill interrupts */
67 /* Get the current interrupt mask on the PICs */
76 /* Now mask everything except the RTC and PIC 2 chain-interrupt */
77 mov eax, NOT (HEX(04) OR HEX(100))
79 /* Program the PICs */
88 /* Get the IDT entry for the RTC */
93 /* Save the original RTC ISR */
98 /* Now load our new handler */
99 mov eax, offset OnlyOnePersonCanWriteHalCode
101 mov word ptr [ecx+2], KGDT_R0_CODE
102 mov word ptr [ecx+4], HEX(08E00)
106 /* Reset our counter */
107 mov dword ptr [ebp-12], 0
109 /* Acquire CMOS lock */
110 call _HalpAcquireSystemHardwareSpinLock@0
112 /* Now initialize register A on the CMOS */
113 mov ax, HEX(2D00) OR CMOS_REGISTER_A
120 /* Read register B */
121 mov ax, CMOS_REGISTER_B
127 /* Don't touch the LastKnownGoodConfig hack */
131 /* Enable the interrupt */
134 /* Now write the register B */
135 mov al, CMOS_REGISTER_B
142 /* Read register C */
143 mov al, CMOS_REGISTER_C
149 /* Read register D */
150 mov al, CMOS_REGISTER_D
156 /* Release CMOS lock */
157 mov dword ptr [ebp-12], 0
158 call _HalpReleaseCmosSpinLock@0
160 /* Initialize looper */
163 /* Align to 16 bytes */
166 /* Enable interrupts! */
170 /* Align to 16 bytes */
173 /* Subtract one count */
178 /* ASSERT: If we got here, then the RTC never fired */
179 call _DbgBreakPoint@0
182 OnlyOnePersonCanWriteHalCode:
183 /*********************** THIS IS THE RTC HANDLER **************************/
185 /* Increment the interrupt count and check if this is the first one */
186 inc dword ptr [ebp-12]
187 cmp dword ptr [ebp-12], 1
191 * It is the first one -- we'll ignore it, since it fires randomly!
192 * Get rid of the old return address and push the new one in (our looper)
197 /* Acquire CMOS lock */
198 call _HalpAcquireSystemHardwareSpinLock@0
200 /* Now initialize register A on the CMOS */
201 mov ax, HEX(2D00) OR CMOS_REGISTER_A
208 /* Read register B */
209 mov ax, CMOS_REGISTER_B
215 /* Don't touch the LastKnownGoodConfig hack */
219 /* Enable the interrupt */
222 /* Now write the register B */
223 mov al, CMOS_REGISTER_B
230 /* Read register C */
231 mov al, CMOS_REGISTER_C
237 /* Read register D */
238 mov al, CMOS_REGISTER_D
244 /* Release CMOS lock */
245 call _HalpReleaseCmosSpinLock@0
247 /* Dismiss the interrupt */
250 mov al, PIC_SPECIFIC_EOI2
253 /* Reset the counter and return back to the looper */
257 /******************* THIS IS THE 2ND RTC HANDLER **************************/
260 /* Do the calculation */
263 mov ecx, 125000 /* RTC fires every 125 ms */
266 /* Is the remainder 0? */
270 /* Otherwise fix-up the loop count */
274 /* Save the stall scale factor */
275 mov fs:[KPCR_STALL_SCALE_FACTOR], eax
277 /* Prepare for interrupt return */
279 push offset AndItsNotYou
282 /* Acquire CMOS lock */
283 call _HalpAcquireSystemHardwareSpinLock@0
285 /* Now initialize register A on the CMOS */
286 mov ax, HEX(2D00) OR CMOS_REGISTER_A
293 /* Read register B */
294 mov ax, CMOS_REGISTER_B
300 /* Don't touch the LastKnownGoodConfig hack */
304 /* Disable the interrupt */
307 /* Now write the register B */
308 mov al, CMOS_REGISTER_B
315 /* Read register C */
316 mov al, CMOS_REGISTER_C
322 /* Release CMOS lock */
323 call _HalpReleaseCmosSpinLock@0
325 /* Dismiss the interrupt */
328 mov al, PIC_SPECIFIC_EOI2
331 /* Disable interrupts on return */
332 and word ptr [esp+8], NOT EFLAGS_INTERRUPT_MASK
335 /************************* WE ARE BACK FROM RTC ***************************/
338 /* Restore the IDT */
343 /* Restore the mask */
352 /* Restore stack and return */
359 PUBLIC _KeStallExecutionProcessor@4
360 _KeStallExecutionProcessor@4:
362 /* Get the number of microseconds required */
366 /* Multiply by the stall factor */
367 mov eax, fs:[KPCR_STALL_SCALE_FACTOR]
370 /* Align to 16 bytes */
373 /* Jump to subtraction loop */
376 /* Align to 16 bytes */
379 /* Subtract one count */
389 PUBLIC _KeQueryPerformanceCounter@4
390 _KeQueryPerformanceCounter@4:
392 /* Check if we were called too early */
393 cmp dword ptr _HalpCurrentRollOver, 0
402 /* Disable interrupts */
408 /* Get the current value */
409 mov ebx, dword ptr _HalpPerfCounterLow
410 mov esi, dword ptr _HalpPerfCounterHigh
412 /* Read 8254 timer */
413 mov al, 0 /* Interrupt on terminal count */
415 in al, SYSTEM_CTRL_PORT_A
416 or al, byte ptr _HalpPerfCounterCutoff
417 out SYSTEM_CTRL_PORT_A, al
425 /* Enable interrupts and do a short wait */
430 /* Disable them again */
434 /* Get the counter value again */
435 mov eax, dword ptr _HalpPerfCounterLow
436 mov edx, dword ptr _HalpPerfCounterHigh
438 /* Check if someone updated the counter */
444 /* Check if the current 8254 value causes rollover */
446 add ecx, dword ptr _HalpCurrentRollOver
451 /* Calculate the sum */
455 /* Check if we're above or below the last high value */
456 cmp edx, dword ptr _HalpLastPerfCounterHigh
460 /* Check if we're above or below the last low value */
461 cmp eax, dword ptr _HalpLastPerfCounterLow
466 /* Update the last value and bring back interrupts */
467 mov dword ptr _HalpLastPerfCounterLow, eax
468 mov dword ptr _HalpLastPerfCounterHigh, edx
471 /* Check if caller wants frequency */
472 cmp dword ptr [esp+12], 0
475 /* Save hard-coded frequency */
476 mov ecx, dword ptr [esp+12]
477 mov dword ptr [ecx], 1193182
478 mov dword ptr [ecx+4], 0
482 /* Restore volatiles */
489 /* Return empty, called too soon */
496 /* We might have an incoming interrupt, save EFLAGS and reset rollover */
498 mov ecx, dword ptr _HalpCurrentRollOver
501 /* Check if interrupts were enabled and try again */
502 test esi, EFLAGS_INTERRUPT_MASK
505 /* They're not, continue where we left */
511 /* Get the last counter values */
512 mov ebx, dword ptr _HalpLastPerfCounterLow
513 mov esi, dword ptr _HalpLastPerfCounterHigh
515 /* Check if the previous value was 0 and go back if yes */
520 /* Make sure that the count is still valid */
524 cmp ebx, dword ptr _HalpCurrentRollOver
527 /* Fixup the count with the last known value */
531 /* We might have an incoming interrupt, save EFLAGS */
535 /* Check if interrupts were enabled and try again */
536 test ecx, EFLAGS_INTERRUPT_MASK
539 /* They're not, continue where we left */
546 mov dword ptr _HalpLastPerfCounterLow, eax
547 mov dword ptr _HalpLastPerfCounterHigh, eax