Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

750 lines
19 KiB

  1. title "Interval Clock Interrupt"
  2. ;++
  3. ;
  4. ; Copyright (c) 1989 Microsoft Corporation
  5. ;
  6. ; Module Name:
  7. ;
  8. ; ixclock.asm
  9. ;
  10. ; Abstract:
  11. ;
  12. ; This module implements the code necessary to field and process the
  13. ; interval clock interrupt.
  14. ;
  15. ; Author:
  16. ;
  17. ; Shie-Lin Tzong (shielint) 12-Jan-1990
  18. ;
  19. ; Environment:
  20. ;
  21. ; Kernel mode only.
  22. ;
  23. ; Revision History:
  24. ;
  25. ; bryanwi 20-Sep-90
  26. ;
  27. ; Add KiSetProfileInterval, KiStartProfileInterrupt,
  28. ; KiStopProfileInterrupt procedures.
  29. ; KiProfileInterrupt ISR.
  30. ; KiProfileList, KiProfileLock are delcared here.
  31. ;
  32. ; shielint 10-Dec-90
  33. ; Add performance counter support.
  34. ; Move system clock to irq8, ie we now use RTC to generate system
  35. ; clock. Performance count and Profile use timer 1 counter 0.
  36. ; The interval of the irq0 interrupt can be changed by
  37. ; KiSetProfileInterval. Performance counter does not care about the
  38. ; interval of the interrupt as long as it knows the rollover count.
  39. ; Note: Currently I implemented 1 performance counter for the whole
  40. ; i386 NT.
  41. ;
  42. ; John Vert (jvert) 11-Jul-1991
  43. ; Moved from ke\i386 to hal\i386. Removed non-HAL stuff
  44. ;
  45. ; shie-lin tzong (shielint) 13-March-92
  46. ; Move System clock back to irq0 and use RTC (irq8) to generate
  47. ; profile interrupt. Performance counter and system clock use time1
  48. ; counter 0 of 8254.
  49. ;
  50. ; Landy Wang (corollary!landy) 04-Dec-92
  51. ; Move much code into separate modules for easy inclusion by various
  52. ; HAL builds.
  53. ;
  54. ;--
  55. .386p
  56. .xlist
  57. include hal386.inc
  58. include callconv.inc ; calling convention macros
  59. include i386\ix8259.inc
  60. include i386\kimacro.inc
  61. include mac386.inc
  62. include i386\ixcmos.inc
  63. .list
  64. EXTRNP _KeUpdateSystemTime,0
  65. EXTRNP Kei386EoiHelper,0,IMPORT
  66. EXTRNP _KeSetTimeIncrement,2,IMPORT
  67. EXTRNP _HalEndSystemInterrupt,2
  68. EXTRNP _HalBeginSystemInterrupt,3
  69. EXTRNP _HalpReleaseCmosSpinLock ,0
  70. EXTRNP _HalpMcaQueueDpc, 0
  71. EXTRNP _HalpBrokenPiix4TimerTick, 0
  72. extrn _HalpBrokenAcpiTimer:byte
  73. extrn _QueryTimer:DWORD
  74. extrn _KdEnteredDebugger:DWORD
  75. extrn _HalpTimerWatchdogEnabled:DWORD
  76. extrn _HalpTimerWatchdogStorage:DWORD
  77. extrn _HalpTimerWatchdogCurFrame:DWORD
  78. extrn _HalpTimerWatchdogLastFrame:DWORD
  79. extrn _HalpTimerWatchdogStorageOverflow:DWORD
  80. ;
  81. ; Constants used to initialize timer 0
  82. ;
  83. TIMER1_DATA_PORT0 EQU 40H ; Timer1, channel 0 data port
  84. TIMER1_CONTROL_PORT0 EQU 43H ; Timer1, channel 0 control port
  85. TIMER2_DATA_PORT0 EQU 48H ; Timer1, channel 0 data port
  86. TIMER2_CONTROL_PORT0 EQU 4BH ; Timer1, channel 0 control port
  87. TIMER1_IRQ EQU 0 ; Irq 0 for timer1 interrupt
  88. COMMAND_8254_COUNTER0 EQU 00H ; Select count 0
  89. COMMAND_8254_RW_16BIT EQU 30H ; Read/Write LSB firt then MSB
  90. COMMAND_8254_MODE2 EQU 4 ; Use mode 2
  91. COMMAND_8254_BCD EQU 0 ; Binary count down
  92. COMMAND_8254_LATCH_READ EQU 0 ; Latch read command
  93. PERFORMANCE_FREQUENCY EQU 1193182
  94. COUNTER_TICKS_AVG_SHIFT EQU 4
  95. COUNTER_TICKS_FOR_AVG EQU 16
  96. PAGE_SIZE EQU 1000H
  97. FRAME_COPY_SIZE EQU 64
  98. ;
  99. ; ==== Values used for System Clock ====
  100. ;
  101. _DATA SEGMENT DWORD PUBLIC 'DATA'
  102. ;
  103. ; The following array stores the per microsecond loop count for each
  104. ; central processor.
  105. ;
  106. ;
  107. ; 8254 performance counter.
  108. ;
  109. public HalpCurrentRollOver, HalpCurrentTimeIncrement, _HalpCurrentMSRateTableIndex
  110. HalpCurrentRollOver dd 0
  111. HalpCurrentTimeIncrement dd 0
  112. _HalpCurrentMSRateTableIndex dd 0
  113. public _HalpClockWork, _HalpClockSetMSRate, _HalpClockMcaQueueDpc
  114. _HalpClockWork label dword
  115. _HalpClockSetMSRate db 0
  116. _HalpClockMcaQueueDpc db 0
  117. _bReserved1 db 0
  118. _bReserved2 db 0
  119. ;
  120. ; timer latency watchdog variables
  121. ;
  122. public _HalpWatchdogAvgCounter, _HalpWatchdogCountLow, _HalpWatchdogCountHigh
  123. public _HalpWatchdogTscLow, _HalpWatchdogTscHigh
  124. _HalpWatchdogAvgCounter dd 0
  125. _HalpWatchdogCountLow dd 0
  126. _HalpWatchdogCountHigh dd 0
  127. _HalpWatchdogTscLow dd 0
  128. _HalpWatchdogTscHigh dd 0
  129. _DATA ends
  130. _TEXT SEGMENT DWORD PUBLIC 'DATA'
  131. ;
  132. ; Convert the interval to rollover count for 8254 Timer1 device.
  133. ; Timer1 counts down a 16 bit value at a rate of 1.193181667M counts-per-sec.
  134. ; (The main crystal freq is 14.31818, and this is a divide by 12)
  135. ;
  136. ; The best fit value closest to 10ms is 10.0144012689ms:
  137. ; ROLLOVER_COUNT 11949
  138. ; TIME_INCREMENT 100144
  139. ; Calculated error is -.0109472 s/day
  140. ;
  141. ;
  142. ; The following table contains 8254 values timer values to use at
  143. ; any given ms setting from 1ms - 15ms. All values work out to the
  144. ; same error per day (-.0109472 s/day).
  145. ;
  146. public HalpRollOverTable
  147. ; RollOver Time
  148. ; Count Increment MS
  149. HalpRollOverTable dd 1197, 10032 ; 1 ms
  150. dd 2394, 20064 ; 2 ms
  151. dd 3591, 30096 ; 3 ms
  152. dd 4767, 39952 ; 4 ms
  153. dd 5964, 49984 ; 5 ms
  154. dd 7161, 60016 ; 6 ms
  155. dd 8358, 70048 ; 7 ms
  156. dd 9555, 80080 ; 8 ms
  157. dd 10731, 89936 ; 9 ms
  158. dd 11949, 100144 ; 10 ms
  159. dd 13125, 110000 ; 11 ms
  160. dd 14322, 120032 ; 12 ms
  161. dd 15519, 130064 ; 13 ms
  162. dd 16695, 139920 ; 14 ms
  163. dd 17892, 149952 ; 15 ms
  164. TimeIncr equ 4
  165. RollOver equ 0
  166. _TEXT ends
  167. _DATA SEGMENT DWORD PUBLIC 'DATA'
  168. public HalpLargestClockMS, _HalpNextMSRate, HalpPendingMSRate
  169. HalpLargestClockMS dd 15 ; Table goes to 15MS
  170. _HalpNextMSRate dd 14
  171. HalpPendingMSRate dd 0
  172. extrn _TimerInfo:DWORD
  173. BiasLow equ 20
  174. BiasHigh equ 24
  175. _DATA ends
  176. PAGELK SEGMENT DWORD PUBLIC 'CODE'
  177. ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
  178. page ,132
  179. subttl "Initialize Clock"
  180. ;++
  181. ;
  182. ; VOID
  183. ; HalpInitializeClock (
  184. ; )
  185. ;
  186. ; Routine Description:
  187. ;
  188. ; This routine initialize system time clock using 8254 timer1 counter 0
  189. ; to generate an interrupt at every 15ms interval at 8259 irq0.
  190. ;
  191. ; See the definitions of TIME_INCREMENT and ROLLOVER_COUNT if clock rate
  192. ; needs to be changed.
  193. ;
  194. ; Arguments:
  195. ;
  196. ; None
  197. ;
  198. ; Return Value:
  199. ;
  200. ; None.
  201. ;
  202. ;--
  203. cPublicProc _HalpInitializeClock ,0
  204. mov eax, PCR[PcPrcb]
  205. cmp byte ptr [eax].PbCpuType, 4 ; 486 or better?
  206. jc short @f ; no, skip
  207. mov HalpLargestClockMS, 10 ; Limit 486's to 10MS
  208. mov _HalpNextMSRate, 9
  209. @@:
  210. mov eax, HalpLargestClockMS
  211. mov _HalpCurrentMSRateTableIndex, eax
  212. dec _HalpCurrentMSRateTableIndex
  213. mov ecx, HalpRollOverTable.TimeIncr
  214. mov edx, HalpRollOverTable[eax*8-8].TimeIncr
  215. mov eax, HalpRollOverTable[eax*8-8].RollOver
  216. mov HalpCurrentTimeIncrement, edx
  217. ;
  218. ; (ecx) = Min time_incr
  219. ; (edx) = Max time_incr
  220. ; (eax) = max roll over count
  221. ;
  222. push eax
  223. stdCall _KeSetTimeIncrement, <edx, ecx>
  224. pop ecx
  225. ;
  226. ; timer latency watchdog initialization
  227. ;
  228. cmp _HalpTimerWatchdogEnabled, 0
  229. jz short @f
  230. .586p
  231. rdtsc
  232. .386p
  233. mov _HalpWatchdogAvgCounter, COUNTER_TICKS_FOR_AVG
  234. mov _HalpWatchdogTscLow, eax
  235. mov _HalpWatchdogTscHigh, edx
  236. xor eax, eax
  237. mov _HalpWatchdogCountLow, eax
  238. mov _HalpWatchdogCountHigh, eax
  239. @@:
  240. pushfd ; save caller's eflag
  241. cli ; make sure interrupts are disabled
  242. ;
  243. ; Set clock rate
  244. ; (ecx) = RollOverCount
  245. ;
  246. mov al,COMMAND_8254_COUNTER0+COMMAND_8254_RW_16BIT+COMMAND_8254_MODE2
  247. out TIMER1_CONTROL_PORT0, al ;program count mode of timer 0
  248. IoDelay
  249. mov al, cl
  250. out TIMER1_DATA_PORT0, al ; program timer 0 LSB count
  251. IoDelay
  252. mov al,ch
  253. out TIMER1_DATA_PORT0, al ; program timer 0 MSB count
  254. popfd ; restore caller's eflag
  255. mov HalpCurrentRollOver, ecx ; Set RollOverCount & initialized
  256. stdRET _HalpInitializeClock
  257. stdENDP _HalpInitializeClock
  258. PAGELK ends
  259. _TEXT$03 SEGMENT DWORD PUBLIC 'CODE'
  260. ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
  261. page ,132
  262. subttl "System Clock Interrupt"
  263. ;++
  264. ;
  265. ; Routine Description:
  266. ;
  267. ; This routine is entered as the result of an interrupt generated by CLOCK.
  268. ; Its function is to dismiss the interrupt, raise system Irql to
  269. ; CLOCK2_LEVEL, update performance counter and transfer control to the
  270. ; standard system routine to update the system time and the execution
  271. ; time of the current thread
  272. ; and process.
  273. ;
  274. ; Arguments:
  275. ;
  276. ; None
  277. ; Interrupt is disabled
  278. ;
  279. ; Return Value:
  280. ;
  281. ; Does not return, jumps directly to KeUpdateSystemTime, which returns
  282. ;
  283. ; Sets Irql = CLOCK2_LEVEL and dismisses the interrupt
  284. ;
  285. ;--
  286. ENTER_DR_ASSIST Hci_a, Hci_t
  287. cPublicProc _HalpClockInterrupt ,0
  288. ;
  289. ; Save machine state in trap frame
  290. ;
  291. ENTER_INTERRUPT Hci_a, Hci_t
  292. ;
  293. ; (esp) - base of trap frame
  294. ;
  295. ifdef MCA
  296. ;
  297. ; Special hack for MCA machines
  298. ;
  299. in al, 61h
  300. jmp $+2
  301. or al, 80h
  302. out 61h, al
  303. jmp $+2
  304. endif ; MCA
  305. ;
  306. ; Dismiss interrupt and raise irq level to clock2 level
  307. ;
  308. Hci10:
  309. push CLOCK_VECTOR
  310. sub esp, 4 ; allocate space to save OldIrql
  311. stdCall _HalBeginSystemInterrupt, <CLOCK2_LEVEL, CLOCK_VECTOR, esp>
  312. or al,al ; check for spurious interrupt
  313. jz Hci100
  314. ;
  315. ; Check to see if we need to fix up a broken PIIX4
  316. ;
  317. .if (_HalpBrokenAcpiTimer)
  318. stdCall _HalpBrokenPiix4TimerTick
  319. .endif
  320. ;
  321. ; Timer latency watchdog
  322. ;
  323. cmp _HalpTimerWatchdogEnabled, 0
  324. jz Hci14
  325. .586p
  326. rdtsc
  327. .386p
  328. ;
  329. ; Compare difference to watchdog count, while storing a copy of the
  330. ; current counter.
  331. ;
  332. xor ebx, ebx
  333. push eax
  334. push edx
  335. sub eax, _HalpWatchdogTscLow
  336. sbb edx, _HalpWatchdogTscHigh
  337. pop _HalpWatchdogTscHigh
  338. pop _HalpWatchdogTscLow
  339. js Hci115 ; Was this a bogus counter?
  340. ; (e.g, negative delta)
  341. push eax
  342. mov ecx, dword ptr _KdEnteredDebugger
  343. xor eax, eax
  344. xchg eax, [ecx]
  345. or al, al
  346. pop eax
  347. jnz Hci14
  348. cmp HalpPendingMSRate, ebx ; Was a new rate set during last
  349. jnz Hci14 ; tick? Yes, skip this compare
  350. ;
  351. ; If we need to compute the average of the time-stamp counter for
  352. ; the current period, add the delta to the counter.
  353. ;
  354. cmp _HalpWatchdogAvgCounter, ebx
  355. jnz Hci12
  356. cmp edx, _HalpWatchdogCountHigh
  357. ja short Hci11
  358. jb Hci14
  359. cmp eax, _HalpWatchdogCountLow
  360. jbe Hci14
  361. Hci11:
  362. cmp dword ptr [_HalpTimerWatchdogStorageOverflow], 0
  363. jne short Hci115
  364. ;
  365. ; copy FRAME_COPY_SIZE dwords from the stack, or to next page boundary,
  366. ; whichever is less
  367. ;
  368. push esi
  369. push edi
  370. lea esi, [esp + 8]
  371. lea ecx, [esi + PAGE_SIZE - 1]
  372. and ecx, NOT(PAGE_SIZE - 1)
  373. sub ecx, esi
  374. shr ecx, 2
  375. cmp ecx, FRAME_COPY_SIZE
  376. jbe short Hci111
  377. mov ecx, FRAME_COPY_SIZE
  378. Hci111:
  379. mov edi, dword ptr _HalpTimerWatchdogCurFrame
  380. rep movsd
  381. add dword ptr _HalpTimerWatchdogCurFrame, (FRAME_COPY_SIZE*4)
  382. ;
  383. ; If we didn't copy an entire FRAME_COPY_SIZE dwords, zero fill.
  384. ;
  385. mov ecx, dword ptr _HalpTimerWatchdogCurFrame
  386. sub ecx, edi
  387. shr ecx, 2
  388. xor eax, eax
  389. rep stosd
  390. cmp edi, dword ptr _HalpTimerWatchdogLastFrame
  391. jbe short Hci112
  392. mov dword ptr [_HalpTimerWatchdogStorageOverflow], 1
  393. Hci112:
  394. pop edi
  395. pop esi
  396. Hci115:
  397. ;
  398. ; reset last time so that we're accurate after the trap
  399. ;
  400. .586p
  401. rdtsc
  402. .386p
  403. mov _HalpWatchdogTscHigh, edx
  404. mov _HalpWatchdogTscLow, eax
  405. jmp short Hci14
  406. Hci12:
  407. ;
  408. ; Increment the total counter, perform average when the count is reached
  409. ;
  410. add _HalpWatchdogCountLow, eax
  411. adc _HalpWatchdogCountHigh, edx
  412. dec _HalpWatchdogAvgCounter
  413. jnz short Hci14
  414. mov edx, _HalpWatchdogCountHigh
  415. mov eax, _HalpWatchdogCountLow
  416. ;
  417. ; compute the average * 2, this measures when we have missed
  418. ; an interrupt at this rate.
  419. ;
  420. mov ecx, COUNTER_TICKS_AVG_SHIFT - 1
  421. Hci13:
  422. shr edx, 1
  423. rcr eax, 1
  424. loop short Hci13
  425. mov _HalpWatchdogCountLow, eax
  426. mov _HalpWatchdogCountHigh, edx
  427. Hci14:
  428. ;
  429. ; Check for any more work
  430. ;
  431. mov eax, HalpCurrentTimeIncrement
  432. xor ebx, ebx
  433. cmp _HalpClockWork, ebx ; Any clock interrupt work desired?
  434. jz _KeUpdateSystemTime@0 ; No, process tick
  435. cmp _HalpClockMcaQueueDpc, bl
  436. je short Hci20
  437. mov _HalpClockMcaQueueDpc, bl
  438. ;
  439. ; Queue MCA Dpc
  440. ;
  441. push eax
  442. stdCall _HalpMcaQueueDpc ; Queue MCA Dpc
  443. pop eax
  444. Hci20:
  445. ;
  446. ; (esp) = OldIrql
  447. ; (esp+4) = Vector
  448. ; (esp+8) = base of trap frame
  449. ; ebp = trap frame
  450. ; eax = time increment
  451. ; ebx = 0
  452. ;
  453. cmp _HalpClockSetMSRate, bl ; New clock rate desired?
  454. jz _KeUpdateSystemTime@0 ; No, process tick
  455. ;
  456. ; Time of clock frequency is being changed. See if the 8254 was
  457. ; was reprogrammed for a new rate during last tick
  458. ;
  459. cmp HalpPendingMSRate, ebx ; Was a new rate set durning last
  460. jnz short Hci50 ; tick? Yes, go update globals
  461. Hci40:
  462. ; (eax) = time increment for current tick
  463. ;
  464. ; A new clock rate needs to be set. Setting the rate here will
  465. ; cause the tick after the next tick to be at the new rate.
  466. ; (the next tick is already in progress by the 8254 and will occur
  467. ; at the same rate as this tick)
  468. ;
  469. mov ebx, _HalpNextMSRate
  470. mov HalpPendingMSRate, ebx ; pending rate
  471. mov ecx, HalpRollOverTable[ebx*8-8].RollOver
  472. ;
  473. ; Set clock rate
  474. ; (ecx) = RollOverCount
  475. ;
  476. push eax ; save current tick's rate
  477. mov al,COMMAND_8254_COUNTER0+COMMAND_8254_RW_16BIT+COMMAND_8254_MODE2
  478. out TIMER1_CONTROL_PORT0, al ;program count mode of timer 0
  479. IoDelay
  480. mov al, cl
  481. out TIMER1_DATA_PORT0, al ; program timer 0 LSB count
  482. IoDelay
  483. mov al,ch
  484. out TIMER1_DATA_PORT0, al ; program timer 0 MSB count
  485. pop eax
  486. ;
  487. ; (esp) = OldIrql
  488. ; (esp+4) = Vector
  489. ; (esp+8) = base of trap frame
  490. ; ebp = trap frame
  491. ; eax = time increment
  492. ;
  493. jmp _KeUpdateSystemTime@0 ; dispatch this tick
  494. Hci50:
  495. ;
  496. ; The next tick will occur at the rate which was programmed during the last
  497. ; tick. Update globals for new rate which starts with the next tick.
  498. ;
  499. ; (eax) = time increment for current tick
  500. ;
  501. mov ebx, HalpPendingMSRate
  502. mov _HalpCurrentMSRateTableIndex, ebx
  503. dec _HalpCurrentMSRateTableIndex
  504. mov ecx, HalpRollOverTable[ebx*8-8].RollOver
  505. mov edx, HalpRollOverTable[ebx*8-8].TimeIncr
  506. mov HalpCurrentRollOver, ecx
  507. mov HalpCurrentTimeIncrement, edx ; next tick rate
  508. mov HalpPendingMSRate, 0 ; no longer pending, clear it
  509. cmp _HalpTimerWatchdogEnabled, 0
  510. jz short @f
  511. ;
  512. ; Schedule to recalibrate watchdog counter
  513. ;
  514. push eax
  515. .586p
  516. rdtsc
  517. .386p
  518. mov _HalpWatchdogAvgCounter, COUNTER_TICKS_FOR_AVG
  519. mov _HalpWatchdogTscLow, eax
  520. mov _HalpWatchdogTscHigh, edx
  521. xor eax,eax
  522. mov _HalpWatchdogCountHigh, eax
  523. mov _HalpWatchdogCountLow, eax
  524. pop eax
  525. @@:
  526. cmp ebx, _HalpNextMSRate ; new rate == NextRate?
  527. jne Hci40 ; no, go set new pending rate
  528. mov _HalpClockSetMSRate, 0 ; all done setting new rate
  529. jmp _KeUpdateSystemTime@0 ; dispatch this tick
  530. Hci100:
  531. add esp, 8 ; spurious, no EndOfInterrupt
  532. SPURIOUS_INTERRUPT_EXIT ; exit interrupt without eoi
  533. stdENDP _HalpClockInterrupt
  534. ;++
  535. ;
  536. ; ULONG
  537. ; HalpAcpiTimerSetTimeIncrement (
  538. ; IN ULONG DesiredIncrement
  539. ; )
  540. ;
  541. ; /*++
  542. ;
  543. ; Routine Description:
  544. ;
  545. ; This routine initialize system time clock to generate an
  546. ; interrupt at every DesiredIncrement interval.
  547. ;
  548. ; Arguments:
  549. ;
  550. ; DesiredIncrement - desired interval between every timer tick (in
  551. ; 100ns unit.)
  552. ;
  553. ; Return Value:
  554. ;
  555. ; The *REAL* time increment set.
  556. ;--
  557. cPublicProc _HalpAcpiTimerSetTimeIncrement,1
  558. mov eax, [esp+4] ; desired setting
  559. xor edx, edx
  560. mov ecx, 9990
  561. div ecx ; round to MS
  562. cmp eax, HalpLargestClockMS ; MS > max?
  563. jc short @f
  564. mov eax, HalpLargestClockMS ; yes, use max
  565. @@:
  566. or eax, eax ; MS < min?
  567. jnz short @f
  568. inc eax ; yes, use min
  569. @@:
  570. mov _HalpNextMSRate, eax
  571. mov _HalpClockSetMSRate, 1 ; New clock rate desired.
  572. mov eax, HalpRollOverTable[eax*8-8].TimeIncr
  573. stdRET _HalpAcpiTimerSetTimeIncrement
  574. stdENDP _HalpAcpiTimerSetTimeIncrement
  575. page ,132
  576. subttl "Query 8254 Counter"
  577. ;++
  578. ;
  579. ; ULONG
  580. ; HalpQuery8254Counter(
  581. ; VOID
  582. ; )
  583. ;
  584. ; Routine Description:
  585. ;
  586. ; This routine returns the current value of the 8254 counter
  587. ;
  588. ; Arguments:
  589. ;
  590. ; None
  591. ;
  592. ; Return Value:
  593. ;
  594. ; Current value of the 8254 counter is returned
  595. ;
  596. ;--
  597. cPublicProc _HalpQuery8254Counter, 0
  598. pushfd
  599. cli
  600. ;
  601. ; Fetch the current counter value from the hardware
  602. ;
  603. mov al, COMMAND_8254_LATCH_READ + COMMAND_8254_COUNTER0
  604. ; Latch PIT Ctr 0 command.
  605. out TIMER1_CONTROL_PORT0, al
  606. IODelay
  607. in al, TIMER1_DATA_PORT0 ; Read PIT Ctr 0, LSByte.
  608. IODelay
  609. movzx ecx, al ; Zero upper bytes of (ECX).
  610. in al, TIMER1_DATA_PORT0 ; Read PIT Ctr 0, MSByte.
  611. mov ch, al ; (CX) = PIT Ctr 0 count.
  612. mov eax, ecx
  613. popfd ; restore interrupt flag
  614. stdRET _HalpQuery8254Counter
  615. stdENDP _HalpQuery8254Counter
  616. _TEXT$03 ends
  617. end