SETUP equ 1

;**
;
; Machine-specific detection code
;
;--

.386p

include hal386.inc
include callconv.inc

;
; Include SystemPro detection code
;
SYSTEMPRO   equ     1
include halsp\i386\spdetect.asm


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

;
; Thunk functions.
; Equivalent Hal functions which various detection code may use
;

;++
;
; CMOS space read  functions.
;
;--

CMOSAddressPort equ     70H
CMOSDataPort    equ     71H

CMOSExAddressLSBPort    equ     74H
CMOSExAddressMSBPort    equ     75H
CMOSExDataPort          equ     76H

;++
;
;   VOID
;   ReadCMOS(
;       ULONG   StartingOffset
;       ULONG   Count
;       PUCHAR  ReturnValuePtr
;       )
;
;   Read CMOS starting at the given offset for the given number of
;   bytes putting the bytes read into the buffer pointed to by the
;   given address.
;
;   Arguments:
;
;       StartingOffset  : where to start in CMOS
;
;       Count           : how many bytes to read
;
;       ReturnValuePtr  : where to put bytes read
;
;   Returns:
;       None.
;
;--
StartingOffset  equ     2*4[ebp]
Count           equ     3*4[ebp]
ReturnValuePtr  equ     4*4[ebp]

cPublicProc _ReadCMOS,3

        push    ebp
        mov     ebp, esp
        push    ebx             ; caller's reg
        push    edi             ; caller's reg

        mov     ebx, StartingOffset
        mov     ecx, Count
        mov     edi, ReturnValuePtr

        align   dword
NextByte:
        cmp     bh, 0
        jne     ExCMOSRead

        mov     al, bl
        out     CMOSAddressPort, al
        in      al, CMOSDataPort
        mov     [edi], al

        add     ebx, 1
        add     edi, 1
        sub     ecx, 1
        jg      NextByte

        pop     edi             ; restore caller's reg
        pop     ebx             ; restore caller's reg
        pop     ebp
        stdRET  _ReadCmos

        align   dword
ExCMOSRead:

        mov     al, bl
        out     CMOSExAddressLSBPort, al
        mov     al, bh
        out     CMOSExAddressMSBPort, al
        in      al, CMOSExDataPort
        mov     [edi], al

        add     ebx, 1
        add     edi, 1
        sub     ecx, 1
        jg      ExCMOSRead

        pop     edi             ; restore caller's reg
        pop     ebx             ; restore caller's reg
        pop     ebp
        stdRET  _ReadCMOS

stdENDP _ReadCMOS


; 486 C step CPU detection code.

CR0_ET          equ     10h
CR0_TS          equ     08H
CR0_EM          equ     04H
CR0_MP          equ     02H

;
; The following equates define the control bits of EFALGS register
;

EFLAGS_AC       equ     40000h
EFLAGS_ID       equ     200000h

;
; Constants for Floating Point test
;

REALLONG_LOW          equ     00000000
REALLONG_HIGH         equ     3FE00000h
PSEUDO_DENORMAL_LOW   equ     00000000h
PSEUDO_DENORMAL_MID   equ     80000000h
PSEUDO_DENORMAL_HIGH  equ     0000h

;
; Define the iret frame
;

IretFrame       struc

IretEip        dd      0
IretCs         dd      0
IretEFlags     dd      0

IretFrame       ends

;++
;
; BOOLEAN
; Detect486CStep (
;    IN PBOOLEAN Dummy
;    )
;
; Routine Description:
;
;   Returns TRUE if the processor is a 486 C stepping.  We detect the CPU
;   in order to use a specific HAL.  This HAL attempts to work around
;   a 486 C stepping bug which the normal HAL tends to aggravate.
;

cPublicProc _Detect486CStep,1
        push    edi
        push    esi
        push    ebx                     ; Save C registers
        mov     eax, cr0
        push    eax
        pushfd                          ; save Cr0 & flags

        pop     ebx                     ; Get flags into eax
        push    ebx                     ; Save original flags

        mov     ecx, ebx
        xor     ecx, EFLAGS_AC          ; flip AC bit
        push    ecx
        popfd                           ; load it into flags
        pushfd                          ; re-save flags
        pop     ecx                     ; get flags into eax
        cmp     ebx, ecx                ; did bit stay flipped?
        je      short Not486C           ; No, then this is a 386

        mov     ecx, ebx
        xor     ecx, EFLAGS_ID          ; flip ID bit
        push    ecx
        popfd                           ; load it into flags
        pushfd                          ; re-save flags
        pop     ecx                     ; get flags into eax
        cmp     ebx, ecx                ; did bit stay flipped?
        jne     short Not486C           ; Yes, then this >= 586

        mov     eax, cr0
        and     eax, NOT (CR0_ET+CR0_MP+CR0_TS+CR0_EM)
        mov     cr0, eax

        call    IsNpxPresent            ; Check if cpu has coprocessor support?
        or      ax, ax
        jz      short Is486C            ; it is actually 486sx, assume C step

        call    Check486CStepping       ; Check for <= C stepping
        jnc     short Not486C           ; if nc, it is NOT a C stepping

Is486C:
        mov     eax, 1                  ; Return TRUE
        jmp     short DetectCpuExit

Not486C:
        xor     eax, eax

DetectCpuExit:
        popfd
        pop     ebx
        mov     cr0, ebx
        pop     ebx
        pop     esi
        pop     edi
        stdRET  _Detect486CStep

stdENDP _Detect486CStep

;++
;
; BOOLEAN
; Check486CStepping (
;    VOID
;    )
;
; Routine Description:
;
;    This routine checks for 486 C Stepping.
;
;    This routine takes advantage of the fact that FSCALE produces
;    wrong result with Denormal or Pseudo-denormal operand on 486
;    C and earlier steps.
;
;    If the value contained in ST(1), second location in the floating
;    point stack, is between 1 and 11, and the value in ST, top of the
;    floating point stack, is either a pseudo-denormal number or a
;    denormal number with the underflow exception unmasked, the FSCALE
;    instruction produces an incorrect result.
;
; Arguments:
;
;    None.
;
; Return Value:
;
;    Carry Flag clear if D or later stepping.
;    Carry Flag set if C stepping.
;
;--

FpControl       equ     [ebp - 2]
RealLongSt1     equ     [ebp - 10]
PseudoDenormal  equ     [ebp - 20]
FscaleResult    equ     [ebp - 30]

        public  Check486CStepping
Check486CStepping       proc

        push    ebp
        mov     ebp, esp
        sub     esp, 30                 ; Allocate space for temp real variables

;
; Initialize the local FP variables to predefined values.
; RealLongSt1 = 1.0 * (2 ** -1) = 0.5 in normalized double precision FP form
; PseudoDenormal =  a unsupported format by IEEE.
;                   Sign bit = 0
;                   Exponent = 000000000000000B
;                   Significand = 100000...0B
; FscaleResult = The result of FSCALE instruction.  Depending on 486 step,
;                the value will be different:
;                Under C and earlier steps, 486 returns the original value
;                in ST as the result.  The correct returned value should be
;                original significand and an exponent of 0...01.
;

        mov     dword ptr RealLongSt1, REALLONG_LOW
        mov     dword ptr RealLongSt1 + 4, REALLONG_HIGH
        mov     dword ptr PseudoDenormal, PSEUDO_DENORMAL_LOW
        mov     dword ptr PseudoDenormal + 4, PSEUDO_DENORMAL_MID
        mov     word ptr PseudoDenormal + 8, PSEUDO_DENORMAL_HIGH

.387
        fnstcw  FpControl               ; Get FP control word
        or      word ptr FpControl, 0FFh ; Mask all the FP exceptions
        fldcw   FpControl               ; Set FP control

        fld     qword ptr RealLongSt1   ; 0 < ST(1) = RealLongSt1 < 1
        fld     tbyte ptr PseudoDenormal; Denormalized operand. Note, i486
                                        ; won't report denormal exception
                                        ; on 'FLD' instruction.
                                        ; ST(0) = Extended Denormalized operand
        fscale                          ; try to trigger 486Cx errata
        fstp    tbyte ptr FscaleResult  ; Store ST(0) in FscaleResult
        cmp     word ptr FscaleResult + 8, PSEUDO_DENORMAL_HIGH
                                        ; Is Exponent changed?
        jz      short c4ds00            ; if z, no, it is C step
        clc
        jmp     short c4ds10
c4ds00: stc
c4ds10: mov     esp, ebp
        pop     ebp
        ret

Check486CStepping       endp

;++
;
; BOOLEAN
; IsNpxPresent(
;     VOID
;     );
;
; Routine Description:
;
;     This routine determines if there is any Numeric coprocessor
;     present.
;
; Arguments:
;
;     None.
;
; Return:
;
;     TRUE - If NPX is present.  Else a value of FALSE is returned.
;
;--

        public  IsNpxPresent
IsNpxPresent   proc    near

        push    ebp                     ; Save caller's bp
        xor     edx, edx
.287
        fninit                          ; Initialize NPX
        mov     ecx, 5A5A5A5Ah          ; Put non-zero value
        push    ecx                     ;   into the memory we are going to use
        mov     ebp, esp
        fnstsw  word ptr [ebp]          ; Retrieve status - must use non-wait
        cmp     byte ptr [ebp], 0       ; All bits cleared by fninit?
        jne     Inp10

        mov     edx, 1

Inp10:
        pop     eax                     ; clear scratch value
        pop     ebp                     ; Restore caller's bp
        mov     eax, edx
        ret

IsNpxPresent   endp


_TEXT   ENDS

        END