title  "Stall Execution Support"
;++
;
; Copyright (c) 1989  Microsoft Corporation
;
; Module Name:
;
;    ixstall.asm
;
; Abstract:
;
;    This module implements the code necessary to field and process the
;    interval clock interrupt.
;
; Author:
;
;    Shie-Lin Tzong (shielint) 12-Jan-1990
;
; Environment:
;
;    Kernel mode only.
;
; Revision History:
;
;   bryanwi 20-Sep-90
;
;       Add KiSetProfileInterval, KiStartProfileInterrupt,
;       KiStopProfileInterrupt procedures.
;       KiProfileInterrupt ISR.
;       KiProfileList, KiProfileLock are delcared here.
;
;   shielint 10-Dec-90
;       Add performance counter support.
;       Move system clock to irq8, ie we now use RTC to generate system
;         clock.  Performance count and Profile use timer 1 counter 0.
;         The interval of the irq0 interrupt can be changed by
;         KiSetProfileInterval.  Performance counter does not care about the
;         interval of the interrupt as long as it knows the rollover count.
;       Note: Currently I implemented 1 performance counter for the whole
;       i386 NT.
;
;   John Vert (jvert) 11-Jul-1991
;       Moved from ke\i386 to hal\i386.  Removed non-HAL stuff
;
;   shie-lin tzong (shielint) 13-March-92
;       Move System clock back to irq0 and use RTC (irq8) to generate
;       profile interrupt.  Performance counter and system clock use time1
;       counter 0 of 8254.
;
;   Landy Wang (corollary!landy) 04-Dec-92
;       Created this module by moving routines from ixclock.asm to here.
;
;--

.386p
        .xlist
include hal386.inc
include callconv.inc                    ; calling convention macros
include i386\ix8259.inc
include i386\kimacro.inc
include mac386.inc
include i386\ixcmos.inc
include xxacpi.h
        .list

        EXTRNP  _KeBugCheckEx,5,IMPORT
        EXTRNP  _DbgBreakPoint,0,IMPORT
        EXTRNP  _HalpAcquireCmosSpinLock  ,0
        EXTRNP  _HalpReleaseCmosSpinLock  ,0
        extrn   _HalpFixedAcpiDescTable:DWORD
        extrn   _QueryTimer:DWORD
	extrn   _PMTimerFreq:DWORD
        
_DATA   SEGMENT  DWORD PUBLIC 'DATA'

MinimumLoopQuantum      equ     42
MinimumLoopCount        dd      MinimumLoopQuantum
KqpcStallCount          db      0
	
; temptemp
if DBG
HalpAcpiStallIns        dd      0
endif

_DATA   ends

INIT    SEGMENT PARA PUBLIC 'CODE'
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

        page ,132
        subttl  "Initialize Stall Execution Counter"
;++
;
; VOID
; HalpInitializeStallExecution (
;    IN CCHAR ProcessorNumber
;    )
;
; Routine Description:
;
;    This routine is obsolete in this HAL.
;
; Arguments:
;
;    ProcessorNumber - Processor Number
;
; Return Value:
;
;    None.
;
;--

cPublicProc _HalpInitializeStallExecution     ,1
        stdRET    _HalpInitializeStallExecution
stdENDP _HalpInitializeStallExecution

        page ,132
        subttl  "Stall Execution"

cPublicProc _HalpRemoveFences
        mov     word ptr fence1, 0c98bh
        stdRET    _HalpRemoveFences
stdENDP _HalpRemoveFences

INIT   ends

_TEXT   SEGMENT PARA PUBLIC 'CODE'
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

;++
;
; VOID
; HalpAcpiTimerStallExecProc(
;    IN ULONG MicroSeconds
;    )
;
; Routine Description:
;
;    This function stalls execution for the specified number of microseconds.
;    KeStallExecutionProcessor
;
; Arguments:
;
;    MicroSeconds - Supplies the number of microseconds that execution is to be
;        stalled.
;
; Return Value:
;
;    None.
;
; Comments:
;    
;    edi     - total ticks elapsed
;    ebx:esi - starting time, in ticks
;
;--

Target           equ [ebp + 8]
cyclesStalled    equ [ebp - 4]

MASK24           equ 0ff000000h
BIT24            equ 001000000h

cPublicProc _HalpAcpiTimerStallExecProc       ,1
cPublicFpo 1, 5

;
; Issue a CPUID to implement a "fence"
;
        push    ebp
        mov     ebp, esp
        sub     esp, 4                          ; make room for locals        
        push    ebx                             ; cpuid uses eax, ebx, ecx, edx
        push    esi
        push    edi

        xor     eax, eax                        ; Processor zero
        
    .586p
fence1: cpuid
    .386p

        xor     edi, edi                        ; zero total stall count
        
        mov     eax, Target
        
        or      eax, eax
        jz      aese10                          ; return if no loop needed

        ; 'Target' starts out as the argument of the function.
        ; It is in uSeconds.  We convert to timer ticks.
        mov     ebx, _PMTimerFreq
        mul     ebx
        mov     ebx, 1000000
        div     ebx
	sub     eax, 1		                ; fudge factor
	
	mov     Target, eax
        
        mov     eax, _QueryTimer                ; move current counter into edx:eax
        call    eax
        
        mov     esi, eax                        ; record the starting tick count
        mov     ebx, edx

if DBG        
        inc     HalpAcpiStallIns
endif        
        mov     cyclesStalled, 0
        mov     eax, MinimumLoopCount
        
AcpiLoop:
        add     cyclesStalled, eax              ; update total cycles stalled
ALIGN 16
        YIELD
        jmp     short aese05

ALIGN 16
        YIELD
aese05: sub     eax, 1                          ; (eax) = (eax) - 1
        jnz     short aese05

        ;
        ; Now figure out if we have to loop again
        ;
        mov     eax, _QueryTimer                ; move current counter into edx:eax
        call    eax
        
        sub     eax, esi                        ; get actual elapsed ticks
        sbb     edx, ebx                        ; check to see that the upper 32 bits agrees
        
if 0        
        jnl     short @f
        int 3                                   ; time seems to have gone backwards
@@:        
endif        
        
        jz      short aese06
        ;
        ; If the upper 32 bits doesn't agree, then something, perhaps the debugger,
        ; has caused time to slip a whole lot.  Just fix up the bottom 32-bits to
        ; reflect a large time slip to make the math simpler.
        ;
        
        mov     eax, 7fffffffh
aese06:
        
        ; edx:eax now contains the number of timer ticks elapsed
        
        cmp     eax, 3                          ; if 1 less than 1uS elapsed, loop some more
        jge     short aese09
        
        add     MinimumLoopCount, MinimumLoopQuantum
        mov     eax, MinimumLoopCount
        jmp     short AcpiLoop                  ; loop some more
        
aese09: mov     edi, eax                        ; edi <- total number of ticks elapsed
                
if DBG        
        or      edi, edi                        ; if the total elapsed ticks is still 0,
        jz      short aese20                    ; the timer hardware is broken.  Bugcheck.
endif        
        
        ; if we have waited long enough, quit
        cmp     edi, Target
        jge     short aese10
        
        ; calculate remaining wait
        push    ebx
        mov     ebx, Target
        sub     ebx, edi                        ; ebx <- remaining ticks
        
        mov     eax, cyclesStalled
        mul     ebx                             ; multiply by number of ticks remaining to wait
        and     edx, 011b                       ; chop edx so that we don't overflow
        div     edi                             ; divide by the number of ticks we have waited
	inc     eax		                ; Never zero!
        pop     ebx
@@:     jmp     AcpiLoop

aese10:
        ;
        ; Knock down MinimumLoopCount once every 0x100 calls to this
        ; function so that we don't accidentally stall for very
        ; large amounts of time.
        ;
        
        inc     KqpcStallCount
        .if ((KqpcStallCount == 0) && (MinimumLoopCount > MinimumLoopQuantum))
        mov     eax, MinimumLoopCount
        sub     eax, MinimumLoopQuantum
        mov     MinimumLoopCount, eax
        .endif

        pop     edi
        pop     esi
        pop     ebx
        mov     esp, ebp
        pop     ebp
        stdRET    _HalpAcpiTimerStallExecProc
        
if DBG
aese20:
        mov     eax, 0a5h
        mov     ebx, 020000h
        xor     esi, esi
        xor     edi, edi
        stdCall _KeBugCheckEx, <eax, ebx, edx, esi, edi>
        jmp     short aese10
endif        
        
stdENDP _HalpAcpiTimerStallExecProc

_TEXT   ends

        end