6c06a1ab75f33d2fad98b561ebb4c1507b5c83c4
[reactos.git] / reactos / hal / halx86 / generic / systimer.S
1 /*
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)
6 */
7
8 /* INCLUDES ******************************************************************/
9
10 #include <asm.h>
11 #include <internal/i386/asmmacro.S>
12 .intel_syntax noprefix
13
14 /* GLOBALS *******************************************************************/
15
16 _HalpLastPerfCounterLow: .long 0
17 _HalpLastPerfCounterHigh: .long 0
18 _HalpPerfCounterLow: .long 0
19 _HalpPerfCounterHigh: .long 0
20
21 _UnhandledMsg:
22 .asciz "\n\x7\x7!!! Unhandled or Unexpected Code at line: %lx!!!\n"
23
24 /* FUNCTIONS *****************************************************************/
25
26 .globl _KeStallExecutionProcessor@4
27 .func KeStallExecutionProcessor@4
28 _KeStallExecutionProcessor@4:
29
30 /* Get the number of microseconds required */
31 mov ecx, [esp+4]
32 jecxz Done
33
34 /* Multiply by the stall factor */
35 mov eax, fs:[KPCR_STALL_SCALE_FACTOR]
36 mul ecx
37
38 /* Align to 16 bytes */
39 .align 16
40
41 /* Jump to subtraction loop */
42 jmp SubtractLoop
43
44 /* Align to 16 bytes */
45 .align 16
46
47 /* Subtract one count */
48 SubtractLoop:
49 sub eax, 1
50 jnz SubtractLoop
51
52 Done:
53 /* Return */
54 ret 4
55 .endfunc
56
57 .global _KeQueryPerformanceCounter@4
58 .func KeQueryPerformanceCounter@4
59 _KeQueryPerformanceCounter@4:
60
61 /* Check if we were called too early */
62 cmp dword ptr _HalpCurrentRollOver, 0
63 je NoCount
64
65 /* Save volatiles */
66 push ebx
67 push esi
68
69 LoopPreInt:
70
71 /* Disable interrupts */
72 pushf
73 cli
74
75 LoopPostInt:
76
77 /* Get the current value */
78 mov ebx, _HalpPerfCounterLow
79 mov esi, _HalpPerfCounterHigh
80
81 /* Read 8254 timer */
82 mov al, 0
83 out 0x43, al
84 jmp $+2
85 in al, 0x40
86 jmp $+2
87 movzx ecx, al
88 in al, 0x40
89 mov ch, al
90
91 /* Enable interrupts and do a short wait */
92 popf
93 nop
94 jmp $+2
95
96 /* Disable them again */
97 pushf
98 cli
99
100 /* Get the counter value again */
101 mov eax, _HalpPerfCounterLow
102 mov edx, _HalpPerfCounterHigh
103
104 /* Check if someone updated the counter */
105 cmp eax, ebx
106 jnz LoopPostInt
107 cmp edx, esi
108 jnz LoopPostInt
109
110 /* Check if the current 8254 value causes rollover */
111 neg ecx
112 add ecx, _HalpCurrentRollOver
113 jnb DoRollOver
114
115 SetSum:
116
117 /* Calculate the sum */
118 add eax, ecx
119 adc edx, 0
120
121 /* Check if we're above or below the last high value */
122 cmp edx, _HalpLastPerfCounterHigh
123 jb short BelowHigh
124 jnz short BelowLow
125
126 /* Check if we're above or below the last low value */
127 cmp eax, _HalpLastPerfCounterLow
128 jb BelowHigh
129
130 BelowLow:
131
132 /* Update the last value and bring back interrupts */
133 mov _HalpLastPerfCounterLow, eax
134 mov _HalpLastPerfCounterHigh, edx
135 popf
136
137 /* Check if caller wants frequency */
138 cmp dword ptr [esp+12], 0
139 jz ReturnNoFreq
140
141 /* Save hard-coded frequency */
142 mov ecx, dword ptr [esp+12]
143 mov dword ptr [ecx], 1193182
144 mov dword ptr [ecx+4], 0
145
146 ReturnNoFreq:
147
148 /* Restore volatiles */
149 pop esi
150 pop ebx
151 ret 4
152
153 NoCount:
154
155 /* Return empty, called too soon */
156 mov eax, 0
157 mov edx, 0
158 ret 4
159
160 DoRollOver:
161
162 /* We might have an incoming interrupt, save EFLAGS and reset rollover */
163 mov esi, [esp]
164 mov ecx, _HalpCurrentRollOver
165 popf
166
167 /* Check if interrupts were enabled and try again */
168 test esi, EFLAGS_INTERRUPT_MASK
169 jnz LoopPreInt
170
171 /* They're not, continue where we left */
172 pushf
173 jmp SetSum
174
175 BelowHigh:
176
177 /* Get the last counter values */
178 mov ebx, _HalpLastPerfCounterLow
179 mov esi, _HalpLastPerfCounterHigh
180
181 /* Check if the previous value was 0 and go back if yes */
182 mov ecx, ebx
183 or ecx, esi
184 jz BelowLow
185
186 /* Make sure that the count is still valid */
187 sub ebx, eax
188 sbb esi, edx
189 jnz InvalidCount
190 cmp ebx, _HalpCurrentRollOver
191 jg InvalidCount
192
193 /* Fixup the count with the last known value */
194 sub eax, ebx
195 sbb edx, esi
196
197 /* We might have an incoming interrupt, save EFLAGS */
198 mov ecx, [esp]
199 popf
200
201 /* Check if interrupts were enabled and try again */
202 test ecx, EFLAGS_INTERRUPT_MASK
203 jnz LoopPreInt
204
205 /* They're not, continue where we left */
206 pushf
207 jmp BelowLow
208
209 InvalidCount:
210 popf
211 xor eax, eax
212 mov _HalpLastPerfCounterLow, eax
213 mov _HalpLastPerfCounterHigh, eax
214 jmp LoopPreInt
215 .endfunc
216
217 .globl _HalpClockInterrupt@0
218 .func HalpClockInterrupt@0
219 TRAP_FIXUPS hci_a, hci_t, DoFixupV86, DoFixupAbios
220 _HalpClockInterrupt@0:
221
222 /* Enter trap */
223 INT_PROLOG hci_a, hci_t, DoPushFakeErrorCode
224
225 /* Push vector and make stack for IRQL */
226 push 0x30
227 sub esp, 4
228
229 /* Begin the interrupt */
230 push esp
231 push 0x30
232 push CLOCK2_LEVEL
233 call _HalBeginSystemInterrupt@12
234
235 /* Check if it's spurious */
236 or al, al
237 jz Spurious
238
239 /* Update the performance counter */
240 xor ebx, ebx
241 mov eax, _HalpCurrentRollOver
242 add _HalpPerfCounterLow, eax
243 adc _HalpPerfCounterHigh, ebx
244
245 /* Get the time increment and check if someone changed the clock rate */
246 mov eax, _HalpCurrentTimeIncrement
247 cmp _HalpClockSetMSRate, ebx
248 jz _KeUpdateSystemTime@0
249
250 /* FIXME: Someone did! */
251 int 3
252
253 Spurious:
254
255 /* Exit the interrupt */
256 add esp, 8
257 jmp _Kei386EoiHelper@0
258 .endfunc
259