mirror of https://github.com/lianthony/NT4.0
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.
782 lines
21 KiB
782 lines
21 KiB
title "Interval Clock Interrupt"
|
|
;++
|
|
;
|
|
; Copyright (c) 1989 Microsoft Corporation
|
|
; Copyright (c) 1993 Sequent Computer Systems, Inc.
|
|
;
|
|
; Module Name:
|
|
;
|
|
; w3clock.asm
|
|
;
|
|
; Abstract:
|
|
;
|
|
; This module implements the code necessary to field and process the
|
|
; interval clock interrupt.
|
|
;
|
|
; Author:
|
|
;
|
|
; Phil Hochstetler ([email protected])
|
|
;
|
|
; Environment:
|
|
;
|
|
; Kernel mode only.
|
|
;
|
|
; Revision History:
|
|
;
|
|
;--
|
|
|
|
.386p
|
|
.xlist
|
|
include hal386.inc
|
|
include callconv.inc
|
|
include i386\kimacro.inc
|
|
include mac386.inc
|
|
include i386\apic.inc
|
|
include i386\ixcmos.inc
|
|
include i386\w3.inc
|
|
.list
|
|
|
|
EXTRNP _DbgBreakPoint,0,IMPORT
|
|
EXTRNP _KeUpdateSystemTime,0
|
|
EXTRNP _KeUpdateRunTime,1,IMPORT
|
|
EXTRNP _KeProfileInterrupt,1,IMPORT
|
|
EXTRNP Kei386EoiHelper,0,IMPORT
|
|
EXTRNP _KeSetTimeIncrement,2,IMPORT
|
|
EXTRNP _HalEndSystemInterrupt,2
|
|
EXTRNP _HalBeginSystemInterrupt,3
|
|
extrn _HalpLocalUnitBase:DWORD
|
|
extrn _HalpW3PostRegisterImage:DWORD
|
|
|
|
;
|
|
; Constants used to initialize timer 0
|
|
;
|
|
|
|
TIMER1_DATA_PORT0 EQU 40H ; Timer1, channel 0 data port
|
|
TIMER1_CONTROL_PORT0 EQU 43H ; Timer1, channel 0 control port
|
|
TIMER2_DATA_PORT0 EQU 48H ; Timer1, channel 0 data port
|
|
TIMER2_CONTROL_PORT0 EQU 4BH ; Timer1, channel 0 control port
|
|
TIMER1_IRQ EQU 0 ; Irq 0 for timer1 interrupt
|
|
|
|
COMMAND_8254_COUNTER0 EQU 00H ; Select count 0
|
|
COMMAND_8254_RW_16BIT EQU 30H ; Read/Write LSB firt then MSB
|
|
COMMAND_8254_MODE2 EQU 4 ; Use mode 2
|
|
COMMAND_8254_BCD EQU 0 ; Binary count down
|
|
COMMAND_8254_LATCH_READ EQU 0 ; Latch read command
|
|
|
|
PERFORMANCE_FREQUENCY EQU 1193182
|
|
|
|
;
|
|
; ==== Values used for System Clock ====
|
|
;
|
|
|
|
_DATA SEGMENT DWORD PUBLIC 'DATA'
|
|
|
|
;
|
|
; 8254 spinlock. This must be acquired before touching the 8254 chip.
|
|
;
|
|
public _Halp8254Lock
|
|
_Halp8254Lock dd 0
|
|
|
|
|
|
public HalpPerfCounterLow, HalpPerfCounterHigh
|
|
public HalpLastPerfCounterLow, HalpLastPerfCounterHigh
|
|
HalpPerfCounterLow dd 0
|
|
HalpPerfCounterHigh dd 0
|
|
HalpLastPerfCounterLow dd 0
|
|
HalpLastPerfCounterHigh dd 0
|
|
|
|
|
|
public HalpCurrentRollOver, HalpCurrentTimeIncrement
|
|
HalpCurrentRollOver dd 0
|
|
HalpCurrentTimeIncrement dd 0
|
|
|
|
;
|
|
; Convert the interval to rollover count for 8254 Timer1 device.
|
|
; Timer1 counts down a 16 bit value at a rate of 1.193181667M counts-per-sec.
|
|
;
|
|
;
|
|
; The best fit value closest to 10ms is 10.0144012689ms:
|
|
; ROLLOVER_COUNT 11949
|
|
; TIME_INCREMENT 100144
|
|
; Calculated error is -.0109472 s/day
|
|
;
|
|
;
|
|
; The following table contains 8254 values timer values to use at
|
|
; any given ms setting from 1ms - 15ms. All values work out to the
|
|
; same error per day (-.0109472 s/day).
|
|
;
|
|
|
|
public HalpRollOverTable
|
|
|
|
; RollOver Time
|
|
; Count Increment MS
|
|
HalpRollOverTable dd 1197, 10032 ; 1ms
|
|
dd 2394, 20064 ; 2 ms
|
|
dd 3591, 30096 ; 3 ms
|
|
dd 4767, 39952 ; 4 ms
|
|
dd 5964, 49984 ; 5 ms
|
|
dd 7161, 60016 ; 6 ms
|
|
dd 8358, 70048 ; 7 ms
|
|
dd 9555, 80080 ; 8 ms
|
|
dd 10731, 89936 ; 9 ms
|
|
dd 11949, 100144 ; 10 ms
|
|
dd 13125, 110000 ; 11 ms
|
|
dd 14322, 120032 ; 12 ms
|
|
dd 15519, 130064 ; 13 ms
|
|
dd 16695, 139920 ; 14 ms
|
|
dd 17892, 149952 ; 15 ms
|
|
|
|
TimeIncr equ 4
|
|
RollOver equ 0
|
|
|
|
public HalpLargestClockMS, HalpNextMSRate, HalpPendingMSRate
|
|
HalpLargestClockMS dd 15 ; Table goes to 15MS
|
|
HalpNextMSRate dd 0
|
|
HalpPendingMSRate dd 0
|
|
|
|
|
|
_DATA ends
|
|
|
|
|
|
INIT SEGMENT DWORD PUBLIC 'CODE'
|
|
ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
|
|
|
|
page ,132
|
|
subttl "Initialize Clock"
|
|
;++
|
|
;
|
|
; VOID
|
|
; HalpInitializeClock (
|
|
; )
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; This routine initialize system time clock using 8254 timer1 counter 0
|
|
; to generate an interrupt at every 10ms interval at EISA_CLOCK_VECTOR
|
|
;
|
|
; The Eisa clock interrupt handler then IPIs all processors at
|
|
; APIC_CLOCK_VECTOR
|
|
;
|
|
; See the definitions of TIME_INCREMENT and ROLLOVER_COUNT if clock rate
|
|
; needs to be changed.
|
|
;
|
|
; This routine assumes it runs during Phase 0 on P0.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; None
|
|
;
|
|
; Return Value:
|
|
;
|
|
; None.
|
|
;
|
|
;--
|
|
cPublicProc _HalpInitializeClock ,0
|
|
|
|
mov eax, PCR[PcPrcb]
|
|
cmp byte ptr [eax].PbCpuType, 4 ; 486 or better?
|
|
jc short @f ; no, skip
|
|
|
|
mov HalpLargestClockMS, 10 ; Limit 486's to 10MS
|
|
@@:
|
|
mov eax, HalpLargestClockMS
|
|
mov ecx, HalpRollOverTable.TimeIncr
|
|
mov edx, HalpRollOverTable[eax*8-8].TimeIncr
|
|
mov eax, HalpRollOverTable[eax*8-8].RollOver
|
|
|
|
mov HalpCurrentTimeIncrement, edx
|
|
|
|
;
|
|
; (ecx) = Min time_incr
|
|
; (edx) = Max time_incr
|
|
; (eax) = max roll over count
|
|
;
|
|
|
|
push eax
|
|
stdCall _KeSetTimeIncrement, <edx, ecx>
|
|
pop ecx
|
|
|
|
pushfd ; save caller's eflag
|
|
cli ; make sure interrupts are disabled
|
|
|
|
;
|
|
; Set clock rate
|
|
; (ecx) = RollOverCount
|
|
;
|
|
|
|
mov al,COMMAND_8254_COUNTER0+COMMAND_8254_RW_16BIT+COMMAND_8254_MODE2
|
|
out TIMER1_CONTROL_PORT0, al ;program count mode of timer 0
|
|
IoDelay
|
|
mov al, cl
|
|
out TIMER1_DATA_PORT0, al ; program timer 0 LSB count
|
|
IoDelay
|
|
mov al,ch
|
|
out TIMER1_DATA_PORT0, al ; program timer 0 MSB count
|
|
|
|
popfd ; restore caller's eflag
|
|
mov HalpCurrentRollOver, ecx ; Set RollOverCount & initialized
|
|
|
|
stdRET _HalpInitializeClock
|
|
|
|
stdENDP _HalpInitializeClock
|
|
|
|
INIT ends
|
|
|
|
_TEXT$03 SEGMENT DWORD PUBLIC 'CODE'
|
|
ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
|
|
|
|
page ,132
|
|
subttl "Query Performance Counter"
|
|
;++
|
|
;
|
|
; LARGE_INTEGER
|
|
; KeQueryPerformanceCounter (
|
|
; OUT PLARGE_INTEGER PerformanceFrequency OPTIONAL
|
|
; )
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; This routine returns current 64-bit performance counter and,
|
|
; optionally, the Performance Frequency.
|
|
;
|
|
; Note this routine can NOT be called at Profiling interrupt
|
|
; service routine. Because this routine depends on IRR0 to determine
|
|
; the actual count.
|
|
;
|
|
; Also note that the performace counter returned by this routine
|
|
; is not necessary the value when this routine is just entered.
|
|
; The value returned is actually the counter value at any point
|
|
; between the routine is entered and is exited.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; PerformanceFrequency [TOS+4] - optionally, supplies the address
|
|
; of a variable to receive the performance counter frequency.
|
|
;
|
|
; Return Value:
|
|
;
|
|
; Current value of the performance counter will be returned.
|
|
;
|
|
;--
|
|
|
|
;
|
|
; Parameter definitions
|
|
;
|
|
|
|
KqpcFrequency EQU [esp+12] ; User supplied Performance Frequence
|
|
|
|
cPublicProc _KeQueryPerformanceCounter ,1
|
|
cPublicFpo 1, 0
|
|
;
|
|
; First check to see if the performance counter has been initialized yet.
|
|
; Since the kernel debugger calls KeQueryPerformanceCounter to support the
|
|
; !timer command, we need to return something reasonable before Timer
|
|
; initialization has occured. Reading garbage off the Timer is not reasonable.
|
|
;
|
|
cmp HalpCurrentRollOver, 0
|
|
je Kqpc50
|
|
|
|
push ebx
|
|
push esi
|
|
|
|
Kqpc01: pushfd
|
|
cli
|
|
Kqpc20:
|
|
ifndef NT_UP
|
|
lea eax, _Halp8254Lock
|
|
ACQUIRE_SPINLOCK eax, Kqpc198
|
|
endif
|
|
|
|
;
|
|
; Fetch the base value. Note that interrupts are off.
|
|
;
|
|
|
|
@@:
|
|
mov ebx, HalpPerfCounterLow
|
|
mov esi, HalpPerfCounterHigh ; [esi:ebx] = Performance counter
|
|
|
|
cmp ebx, HalpPerfCounterLow
|
|
jne @b
|
|
|
|
;
|
|
; Fetch the current counter value from the hardware
|
|
;
|
|
|
|
mov al, COMMAND_8254_LATCH_READ+COMMAND_8254_COUNTER0
|
|
;Latch PIT Ctr 0 command.
|
|
out TIMER1_CONTROL_PORT0, al
|
|
IoDelay
|
|
in al, TIMER1_DATA_PORT0 ;Read PIT Ctr 0, LSByte.
|
|
movzx ecx,al ;Zero upper bytes of (ECX).
|
|
IoDelay
|
|
in al, TIMER1_DATA_PORT0 ;Read PIT Ctr 0, MSByte.
|
|
mov ch, al ;(CX) = PIT Ctr 0 count.
|
|
|
|
ifndef NT_UP
|
|
lea eax, _Halp8254Lock
|
|
RELEASE_SPINLOCK eax
|
|
endif
|
|
|
|
|
|
;
|
|
; Now enable interrupts such that if timer interrupt is pending, it can
|
|
; be serviced and update the PerformanceCounter. Note that there could
|
|
; be a long time between the sti and cli because ANY interrupt could come
|
|
; in in between.
|
|
;
|
|
|
|
popfd ; don't re-enable interrupts if
|
|
nop ; the caller had them off!
|
|
|
|
jmp $+2 ; allow interrupt in case counter
|
|
; has wrapped
|
|
|
|
pushfd
|
|
cli
|
|
|
|
;
|
|
; Fetch the base value again.
|
|
;
|
|
|
|
@@:
|
|
mov eax, HalpPerfCounterLow
|
|
mov edx, HalpPerfCounterHigh ; [edx:eax] = new counter value
|
|
|
|
cmp eax, HalpPerfCounterLow
|
|
jne @b
|
|
;
|
|
; Compare the two reads of Performance counter. If they are different,
|
|
; start over
|
|
;
|
|
|
|
cmp eax, ebx
|
|
jne Kqpc20
|
|
cmp edx, esi
|
|
jne Kqpc20
|
|
|
|
neg ecx ; PIT counts down from 0h
|
|
add ecx, HalpCurrentRollOver
|
|
jnc short Kqpc60
|
|
|
|
Kqpc30:
|
|
add eax, ecx
|
|
adc edx, 0 ; [edx:eax] = Final result
|
|
|
|
cmp edx, HalpLastPerfCounterHigh
|
|
jc short Kqpc70 ; jmp if edx < lastperfcounterhigh
|
|
jne short Kqpc35 ; jmp if edx > lastperfcounterhigh
|
|
|
|
cmp eax, HalpLastPerfCounterLow
|
|
jc short Kqpc70 ; jmp if eax < lastperfcounterlow
|
|
|
|
Kqpc35:
|
|
mov HalpLastPerfCounterLow, eax
|
|
mov HalpLastPerfCounterHigh, edx
|
|
|
|
popfd ; restore interrupt flag
|
|
|
|
|
|
;
|
|
; Return the freq. if caller wants it.
|
|
;
|
|
cmp dword ptr KqpcFrequency, 0 ; is it a NULL variable?
|
|
jz short Kqpc40 ; if z, yes, go exit
|
|
|
|
mov ecx, KqpcFrequency ; (ecx)-> Frequency variable
|
|
mov DWORD PTR [ecx], PERFORMANCE_FREQUENCY ; Set frequency
|
|
mov DWORD PTR [ecx+4], 0
|
|
|
|
Kqpc40:
|
|
pop esi ; restore esi and ebx
|
|
pop ebx
|
|
stdRET _KeQueryPerformanceCounter
|
|
|
|
Kqpc50:
|
|
; Initialization hasn't occured yet, so just return zeroes.
|
|
mov eax, 0
|
|
mov edx, 0
|
|
stdRET _KeQueryPerformanceCounter
|
|
|
|
Kqpc60:
|
|
;
|
|
; The current count is larger then the HalpCurrentRollOver. The only way
|
|
; that could happen is if there is an interrupt in route to the processor
|
|
; but it was not processed while interrupts were enabled.
|
|
;
|
|
mov esi, [esp] ; (esi) = flags
|
|
mov ecx, HalpCurrentRollOver ; (ecx) = max possible value
|
|
popfd ; restore flags
|
|
|
|
test esi, EFLAGS_INTERRUPT_MASK
|
|
jnz Kqpc01 ; ints are enabled, problem should go away
|
|
|
|
pushfd ; fix stack
|
|
jmp short Kqpc30 ; ints are disabled, use max count (ecx)
|
|
|
|
Kqpc70:
|
|
;
|
|
; The current count is smaller then the last returned count. The only way
|
|
; this should occur is if there is an interrupt in route to the processor
|
|
; which was not been processed.
|
|
;
|
|
|
|
mov ebx, HalpLastPerfCounterLow
|
|
mov esi, HalpLastPerfCounterHigh
|
|
|
|
mov ecx, ebx
|
|
or ecx, esi ; is last returned value 0?
|
|
jz short Kqpc35 ; Yes, then just return what we have
|
|
|
|
; sanity check - make sure count is not off by bogus amount
|
|
sub ebx, eax
|
|
sbb esi, edx
|
|
jnz short Kqpc75 ; off by bogus amount
|
|
cmp ebx, HalpCurrentRollOver
|
|
jg short Kqpc75 ; off by bogus amount
|
|
|
|
sub eax, ebx
|
|
sbb edx, esi ; (edx:eax) = last returned count
|
|
|
|
mov ecx, [esp] ; (ecx) = flags
|
|
popfd
|
|
|
|
test ecx, EFLAGS_INTERRUPT_MASK
|
|
jnz Kqpc01 ; ints enabled, problem should go away
|
|
|
|
pushfd ; fix stack
|
|
jmp Kqpc35 ; ints disabled, just return last count
|
|
|
|
Kqpc75:
|
|
popfd
|
|
xor eax, eax ; reset bogus values
|
|
mov HalpLastPerfCounterLow, eax
|
|
mov HalpLastPerfCounterHigh, eax
|
|
jmp Kqpc01 ; go try again
|
|
|
|
ifndef NT_UP
|
|
Kqpc198: popfd
|
|
SPIN_ON_SPINLOCK eax,<Kqpc01>
|
|
endif
|
|
|
|
stdENDP _KeQueryPerformanceCounter
|
|
|
|
;++
|
|
;
|
|
; VOID
|
|
; HalCalibratePerformanceCounter (
|
|
; IN volatile PLONG Number
|
|
; )
|
|
;
|
|
; /*++
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; This routine calibrates the performance counter value for a
|
|
; multiprocessor system. The calibration can be done by zeroing
|
|
; the current performance counter, or by calculating a per-processor
|
|
; skewing between each processors counter.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; Number - Supplies a pointer to count of the number of processors in
|
|
; the configuration.
|
|
;
|
|
; Return Value:
|
|
;
|
|
; None.
|
|
;--
|
|
cPublicProc _HalCalibratePerformanceCounter,1
|
|
cPublicFpo 1, 0
|
|
|
|
mov eax, [esp+4] ; ponter to Number
|
|
cPublicFpo 1, 1
|
|
pushfd ; save previous interrupt state
|
|
cli ; disable interrupts (go to high_level)
|
|
|
|
lock dec dword ptr [eax] ; count down
|
|
|
|
@@: cmp dword ptr [eax], 0 ; wait for all processors to signal
|
|
jnz short @b
|
|
|
|
;
|
|
; Nothing to calibrate on this apic based MP machine. There is only
|
|
; a single 8254 device in use
|
|
;
|
|
|
|
cPublicFpo 1, 0
|
|
popfd ; restore interrupt flag
|
|
stdRET _HalCalibratePerformanceCounter
|
|
|
|
stdENDP _HalCalibratePerformanceCounter
|
|
|
|
|
|
|
|
page ,132
|
|
subttl "System Clock Interrupt"
|
|
;++
|
|
;
|
|
; Routine Description:
|
|
;
|
|
;
|
|
; This routine is entered as the result of an interrupt generated by CLOCK2.
|
|
; Its function is to dismiss the interrupt, raise system Irql to
|
|
; CLOCK2_LEVEL, update performance counter and transfer control to the
|
|
; standard system routine to update the system time and the execution
|
|
; time of the current thread
|
|
; and process.
|
|
;
|
|
;
|
|
; Arguments:
|
|
;
|
|
; None
|
|
; Interrupt is disabled
|
|
;
|
|
; Return Value:
|
|
;
|
|
; Does not return, jumps directly to KeUpdateSystemTime, which returns
|
|
;
|
|
; Sets Irql = CLOCK2_LEVEL and dismisses the interrupt
|
|
;
|
|
;--
|
|
|
|
ENTER_DR_ASSIST Hci_a, Hci_t
|
|
|
|
cPublicProc _HalpClockInterrupt ,0
|
|
|
|
;
|
|
; Save machine state in trap frame
|
|
;
|
|
|
|
ENTER_INTERRUPT Hci_a, Hci_t
|
|
|
|
;
|
|
; (esp) - base of trap frame
|
|
;
|
|
; dismiss interrupt and raise Irql
|
|
;
|
|
|
|
push APIC_CLOCK_VECTOR
|
|
sub esp, 4 ; allocate space to save OldIrql
|
|
stdCall _HalBeginSystemInterrupt, <CLOCK2_LEVEL,APIC_CLOCK_VECTOR,esp>
|
|
or al,al ; check for spurious interrupt
|
|
jz Hci100
|
|
|
|
;
|
|
; Turn off (on) the processor active light to reflect whether
|
|
; we are (are not) currently executing the Idle Thread.
|
|
;
|
|
|
|
mov edi, PCR[PcPrcb] ; (edi) -> Prcb
|
|
mov eax, [edi].PbCurrentThread
|
|
cmp eax, [edi].PbIdleThread ; running idle thread?
|
|
setnz al ; set al to desired light state
|
|
cmp al, byte ptr PCR[PcHal.ProcLightState]
|
|
jne Hci05
|
|
Hci10:
|
|
|
|
ifndef NT_UP
|
|
cmp byte ptr PCR[PcHal.PcrNumber], 0 ; Only P0 Will update System Time
|
|
je Do_P0Timer
|
|
|
|
;
|
|
; All processors will update RunTime for current thread
|
|
;
|
|
|
|
sti
|
|
; TOS const PreviousIrql
|
|
stdCall _KeUpdateRunTime,<dword ptr [esp]>
|
|
|
|
INTERRUPT_EXIT ; lower irql to old value, iret
|
|
|
|
;
|
|
; We don't return here
|
|
;
|
|
|
|
|
|
Do_P0Timer:
|
|
|
|
endif
|
|
|
|
;
|
|
; Update front panel light show
|
|
;
|
|
mov eax, _HalpW3PostRegisterImage ; get current bits
|
|
and al, 01111111b ; clear disk error bit
|
|
out PostRegisterPort, al ; write PostCode Reg
|
|
|
|
;
|
|
; Update performance counter
|
|
;
|
|
|
|
mov eax, HalpCurrentRollOver
|
|
add HalpPerfCounterLow, eax ; update performace counter
|
|
adc HalpPerfCounterHigh, dword ptr 0
|
|
|
|
mov eax, HalpCurrentTimeIncrement
|
|
Hci30:
|
|
|
|
;
|
|
; (esp) = OldIrql
|
|
; (esp+4) = Vector
|
|
; (esp+8) = base of trap frame
|
|
; ebp = trap frame
|
|
; eax = time increment
|
|
;
|
|
cmp HalpNextMSRate, 0 ; New clock rate desired?
|
|
jz _KeUpdateSystemTime@0 ; No, process tick
|
|
|
|
;
|
|
; Time of clock frequency is being changed. See if the 8254 was
|
|
; was reprogrammed for a new rate during last tick
|
|
;
|
|
cmp HalpPendingMSRate, 0 ; Was a new rate set durning last
|
|
jnz short Hci50 ; tick? Yes, go update globals
|
|
|
|
Hci40:
|
|
; (eax) = time increment for current tick
|
|
|
|
;
|
|
; A new clock rate needs to be set. Setting the rate here will
|
|
; cause the tick after the next tick to be at the new rate.
|
|
; (the next tick is already in progress by the 8254 and will occur
|
|
; at the same rate as this tick)
|
|
;
|
|
Kci01: pushfd
|
|
cli
|
|
ifndef NT_UP
|
|
lea ecx, _Halp8254Lock
|
|
ACQUIRE_SPINLOCK ecx, Kci198
|
|
endif
|
|
mov ebx, HalpNextMSRate
|
|
mov HalpPendingMSRate, ebx ; pending rate
|
|
|
|
mov ecx, HalpRollOverTable[ebx*8-8].RollOver
|
|
|
|
;
|
|
; Set clock rate
|
|
; (ecx) = RollOverCount
|
|
;
|
|
|
|
push eax ; save current tick's rate
|
|
|
|
|
|
mov al,COMMAND_8254_COUNTER0+COMMAND_8254_RW_16BIT+COMMAND_8254_MODE2
|
|
out TIMER1_CONTROL_PORT0, al ;program count mode of timer 0
|
|
IoDelay
|
|
mov al, cl
|
|
out TIMER1_DATA_PORT0, al ; program timer 0 LSB count
|
|
IoDelay
|
|
mov al,ch
|
|
out TIMER1_DATA_PORT0, al ; program timer 0 MSB count
|
|
|
|
ifndef NT_UP
|
|
lea eax, _Halp8254Lock
|
|
RELEASE_SPINLOCK eax
|
|
endif
|
|
|
|
pop eax
|
|
popfd
|
|
jmp Hci30 ; dispatch this tick
|
|
|
|
Hci50:
|
|
;
|
|
; The next tick will occur at the rate which was programmed during the last
|
|
; tick. Update globals for new rate which starts with the next tick.
|
|
;
|
|
; (eax) = time increment for current tick
|
|
;
|
|
mov ebx, HalpPendingMSRate
|
|
mov ecx, HalpRollOverTable[ebx*8-8].RollOver
|
|
mov edx, HalpRollOverTable[ebx*8-8].TimeIncr
|
|
|
|
mov HalpCurrentRollOver, ecx
|
|
mov HalpCurrentTimeIncrement, edx ; next tick rate
|
|
mov HalpPendingMSRate, 0 ; no longer pending, clear it
|
|
|
|
cmp ebx, HalpNextMSRate ; new rate == NextRate?
|
|
jne Hci40 ; no, go set new pending rate
|
|
|
|
mov HalpNextMSRate, 0 ; we are at this rate, clear it
|
|
jmp Hci30 ; process this tick
|
|
|
|
Hci100:
|
|
add esp, 8 ; spurious, no EndOfInterrupt
|
|
SPURIOUS_INTERRUPT_EXIT ; exit interrupt without eoi
|
|
|
|
ifndef NT_UP
|
|
Kci198: popfd
|
|
SPIN_ON_SPINLOCK ecx,<Kqpc01>
|
|
endif
|
|
|
|
;
|
|
; Update the state of hardware lights and state variable
|
|
;
|
|
Hci05:
|
|
cmp al, 1 ; identify light going
|
|
je Hci06 ; on (or going off)
|
|
|
|
; Turn light off that was on
|
|
|
|
xor eax, eax
|
|
mov al, byte ptr PCR[PcHal.PcrNumber] ; get processor number
|
|
lock btr _HalpW3PostRegisterImage, eax ; clear the bit
|
|
mov byte ptr PCR[PcHal.ProcLightState], 0 ; update flag
|
|
jmp hci10 ; return
|
|
|
|
; Turn light on that was off
|
|
Hci06:
|
|
xor eax, eax
|
|
mov al, byte ptr PCR[PcHal.PcrNumber] ; get processor number
|
|
lock bts _HalpW3PostRegisterImage, eax ; set the bit
|
|
mov byte ptr PCR[PcHal.ProcLightState], 1 ; update flag
|
|
jmp hci10 ; return
|
|
|
|
stdENDP _HalpClockInterrupt
|
|
|
|
;++
|
|
;
|
|
; ULONG
|
|
; HalSetTimeIncrement (
|
|
; IN ULONG DesiredIncrement
|
|
; )
|
|
;
|
|
; /*++
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; This routine initialize system time clock to generate an
|
|
; interrupt at every DesiredIncrement interval.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; DesiredIncrement - desired interval between every timer tick (in
|
|
; 100ns unit.)
|
|
;
|
|
; Return Value:
|
|
;
|
|
; The *REAL* time increment set.
|
|
;--
|
|
cPublicProc _HalSetTimeIncrement,1
|
|
|
|
mov eax, [esp+4] ; desired setting
|
|
xor edx, edx
|
|
mov ecx, 10000
|
|
div ecx ; round to MS
|
|
|
|
cmp eax, HalpLargestClockMS ; MS > max?
|
|
jc short @f
|
|
mov eax, HalpLargestClockMS ; yes, use max
|
|
@@:
|
|
or eax, eax ; MS < min?
|
|
jnz short @f
|
|
inc eax ; yes, use min
|
|
@@:
|
|
mov HalpNextMSRate, eax
|
|
|
|
mov eax, HalpRollOverTable[eax*8-8].TimeIncr
|
|
stdRET _HalSetTimeIncrement
|
|
|
|
stdENDP _HalSetTimeIncrement
|
|
_TEXT$03 ends
|
|
|
|
end
|