; ****************************************************************************
; *                                                                          *
; *                       IBM/Microsoft Confidential                         *
; *                                                                          *
; *                 Copyright (c) IBM Corporation  1987, 1990                *
; *                 Copyright (c) Microsoft Corp.  1987, 1990                *
; *                           All Rights Reserved                            *
; *                                                                          *
; ****************************************************************************

        page    78, 132
        title   dhnpx, DosHlp NPX routines

;******************************************************************************
;*                                                                            *
;* Source File: dhnpx                                                         *
;*                                                                            *
;* Description:                                                               *
;*      This file contains the DosHlp routines for initializing the NPX,      *
;*      clear the NPX busy line, and acknowlege an NPX exception interrupt    *
;*                                                                            *
;* Entry Points:                                                              *
;*      DHInitNPX           -- determine the presence of an NPX               *
;*      DHClearBusyNPX      -- clear the NPX busy line                        *
;*                                                                            *
;* External References:                                                       *
;*      DHSendEOI           -- acknowlege an IRQ at the PIC                   *
;*                                                                            *
;******************************************************************************

.xlist
include basemaca.inc
include segments.inc
include doshlp.inc
include sysinvar.inc
include npx.inc
include mi.inc
include protmode.inc
.list

.386p
.387

; set up for popff macro to generate local iret
?farcode=-1

_DATA   SEGMENT

        extrn   DHConfigAX:word
        extrn   DHConfigBX:word
        extrn   DHConfigDH:byte

_DATA   ENDS

DosHlpData      Segment


        cSpin           dw      1       ; spin count for emulating fwait
                                        ; initialized to 1 for short sysinit
                                        ; wait time.
        NPX_CtrlWd      dw      0       ; NPX control word

DosHlpData      ends

        extrn   DsToDosHlpData:near
        extrn   DHSendEOI:near
        extrn   GetIRR:near
        extrn   GetISR:near

DosHlpDisc32Code        SEGMENT
        assume cs:DosHlpDisc32Code, ds:nothing



;******************************************************************************
;*                                                                            *
;* DHInitNPX -- determine if an npx is present                                *
;*                                                                            *
;* Description:                                                               *
;*      This doshlp determines if coprocessor exists and should               *
;*      be enabled.  If yes, the spin count for waitNPX will be set up        *
;*      according to the CPU type.  There are some cases the NPX will be      *
;*      disabled even coprocessor is present:                                 *
;*      coprocessor is 287 or                                                 *
;*      386B1 running with 387                                                *
;*      The returned code in ax will give kernel the option to display        *
;*      an informational message to the user indicating that a coprocessor    *
;*      is present but not being used.                                        *
;*                                                                            *
;* Linkage:                                                                   *
;*      near call                                                             *
;*                                                                            *
;* Input:                                                                     *
;*      (EAX) = CPU Stepping                                                  *
;*                                                                            *
;* Exit:                                                                      *
;*      zero flag is set if NPX present and should be enabled                 *
;*      zero flag is cleared if NPX NOT PRESENT or DISABLED.  AX              *
;*              contains the detail information:                              *
;*              AX = NPX_NOT_PRESENT                                          *
;*              AX = NPX_287_PRESENT                                          *
;*              AX = NPX_386B1_WITH_387                                       *
;*                                                                            *
;* Uses:                                                                      *
;*      flags, eax                                                            *
;*                                                                            *
;* Internal References:                                                       *
;*      none                                                                  *
;*                                                                            *
;* External References:                                                       *
;*      none                                                                  *
;*                                                                            *
;******************************************************************************

        public  DHInitNPX
DHInitNPX       proc    near

        SaveReg <ds, ebx, edx, ecx>
        push    selSysInitData
        pop     ds
        assume  ds:_DATA

        mov     edx, eax                        ; Save CPU stepping
        xor     eax, eax
        xor     ecx, ecx
        ; check if any NPX is present
        mov     bx, DHConfigAX
        not     bx
        mov     ax, NPX_NOT_PRESENT             ; Assume No NPX
        test    bx, CFGAX_NPX_PRESENT           ; Is ANY NPX present?
        jnz     short dhinitnpx3                ; No, just return
        mov     bh, DHConfigDH                  ; Pick up CPU and NPX types
        shr     bh,4                            ; BH = NPX type

        mov     ax, NPX_287_PRESENT             ; Assume, it is 287
        cmp     bh, NPX_287                     ; Is it a 287?
        jz      short dhinitnpx2
        mov     cx, DHConfigBX                  ; Pick up model and type

        push    DOSHLP_DATASEL          ; Put prot mode data selector in DS
        pop     ds
        assume  ds:DosHlpData

        mov     cSpin, SpinCount_387            ; Use 387 spin count
        xor     ax, ax
dhinitnpx2:
        or      ax, ax
dhinitnpx3:
        RestoreReg <ecx, edx, ebx, ds>
        ret

DHInitNPX       endp

DosHlpDisc32Code        ENDS

DosHlp32Code    SEGMENT

        assume cs:DosHlp32Code, ds:nothing


;******************************************************************************
;*                                                                            *
;* DHClrBusyNPX -- clear PAL NPX busy line                                    *
;*                                                                            *
;* Description:                                                               *
;*      This routine clears the busy line from the PAL                        *
;*                                                                            *
;* Linkage:                                                                   *
;*      far call                                                              *
;*                                                                            *
;* Input:                                                                     *
;*      none                                                                  *
;*                                                                            *
;* Exit:                                                                      *
;*      PAL NPX busy line cleared                                             *
;*                                                                            *
;* Uses:                                                                      *
;*      flags                                                                 *
;*                                                                            *
;* Internal References:                                                       *
;*      none                                                                  *
;*                                                                            *
;* External References:                                                       *
;*      none                                                                  *
;*                                                                            *
;******************************************************************************

        assume  ds:nothing

        public  DHClrBusyNPX
DHClrBusyNPX    proc    near
        push    ax                      ; save ax arround the doshlp
        fnstsw  ax                      ; store the status, checking err;91403
        test    ax,npxsw_error          ; test the error bit            ;91403
        jz      short bnpx20            ; if error, do the out to port  ;91403
        xor     al, al
        out     NPX287_BUSY_PORT, al
bnpx20:                                                                 ;91403
        pop     ax
        ret

DHClrBusyNPX    endp


;******************************************************************************
;*                                                                            *
;* DHAckIntNPX -- acknowlege NPX exception interrupt                          *
;*                                                                            *
;* Description:                                                               *
;*      This routine calls DHSendEOI to acknowlege the NPX exception     *
;*      (irq 13)                                                              *
;*                                                                            *
;* Linkage:                                                                   *
;*      far call                                                              *
;*                                                                            *
;* Input:                                                                     *
;*      none                                                                  *
;*                                                                            *
;* Exit:                                                                      *
;*      eoi sent to PIC's for IRQ 13                                          *
;*                                                                            *
;* Uses:                                                                      *
;*                                                                            *
;* Internal References:                                                       *
;*                                                                            *
;* External References:                                                       *
;*      DHSendEOI -- acknowlege an interrupt at the PIC's                     *
;*                                                                            *
;******************************************************************************

        assume  ds:nothing

        public  DHAckIntNPX
DHAckIntNPX     proc    near
        push    ax                      ; save ax arround the doshlp
        mov     ax, IRQ_NPX             ; Ack IRQ13
        call    DHSendEOI               ; Send the EOI
        pop     ax
        ret

DHAckIntNPX     endp


;******************************************************************************
;*                                                                            *
;* WaitNPX -- wait for the current NPX instruction to finish                  *
;*                                                                            *
;* Description:                                                               *
;*                                                                            *
;       BUGBUG - There are some interesting hardware features that add some
;       complexity to the NPX code. Note the term AT applies specifically to
;       the IBM PC/AT, but also generically to further AT class machines. The
;       term PS/2 applies generically to microchannel machines, although not
;       all PS/2s are microchannel machines.
;
;       In the PC/AT, the NPX is not connected to the CPU in the way that.
;       Intel designed them to be connected -
;
;       IBM put a piece of hardware, known as "The Busy Latch" between the NPX
;       and the CPU and routed the NPX's error signal thru the busy latch and
;       into the interrupt controller on level 13 (0Dh).  This was done on an AT
;       to make the 80286/80287 look and act the same as the 8088/8087 did on
;       the original PC, thus preserving compatibility.
;
;       As Intel designed it, the CPU would have seen the busy signal go false
;       when the NPX finished executing the current instruction.  If the NPX had
;       detected an error, the error signal would have gone true before the busy
;       signal went false.
;
;       As implemented, the CPU still sees the busy signal go false when
;       the NPX finishes an instruction if no error was detected.  But if an
;       error was detected, the busy latch would cause the CPU to still see the
;       NPX as being busy even tho it had finished executing the instruction.
;       Also, the NPX's error signal would cause an interrupt to be made pending
;       at the interrupt controller.  The NPX interrupt service handler would
;       then clear the busy latch, thus allowing the CPU to see the true state
;       of the NPX's busy signal (now false) and removing the interrupt request
;       from the interrupt controller.  All this is well and good, for now at
;       least.
;
;       One other problem arose out of the existance of the busy latch.  It
;       seems a glitch on the NPX's error signal can occur during the execution
;       of a particular NPX instruction sequence.  This glitch is enough to
;       latch the busy signal and cause the CPU to see the NPX as being busy,
;       but not enough for the interrupt controller to make an interrupt request
;       pending.
;
;       Now, remember that the CPU waits to see the busy signal go false before
;       executing another NPX instruction.  Because of the glitch, the CPU will
;       NEVER see the busy signal go false and will therefore wait FOREVER.
;       This is a real problem since the kernel needs to wait for the NPX to
;       become not busy before doing such things as a thread switch, mode switch
;       or moving or freeing up memory.
;
;       The solution to this problem was to clear the busy latch before the wait
;       in the kernel.  Thats where the CLEARPAL macro came from.  Again, all is
;       well and good.
;
;       One side affect of this solution is that the busy latch is also latching
;       the NPX's interrupt request.  When the busy latch is cleared, the
;       interrupt request is removed.  But the interrupt controllers on the AT
;       are EDGE sensitive.  Therefore, the interrupt request is remembered by
;       the controller and not forgotten.  Hence, no problem on the AT.
;
;       One side note here.  When the NPX has detected an error, the CPU will
;       always see busy true until the NPX's interrupt is handled.  Any NPX
;       instruction will cause the CPU to hang forever waiting for busy to go
;       false if that NPX instruction is executed when interrupts are disabled.
;
;       When PS/2s came along, the busy latch was preserved to maintain
;       compatibility between the AT and PS/2s.
;
;       However, PS/2 interrupt controllers are LEVEL sensitive (remember AT
;       controllers are EDGE sensitive).  Hence, CLEARING THE BUSY LATCH WILL
;       ALSO DISCARD THE NPX'S INTERRUPT REQUEST!  Therefore, DO NOT clear the
;       NPX busy latch on PS/2s anywhere except in the NPX interrupt handler.
;
;* Linkage:                                                                   *
;*      call far                                                              *
;*                                                                            *
;* Input:                                                                     *
;*      none                                                                  *
;*                                                                            *
;* Exit:                                                                      *
;*      current NPX instruction is finished                                   *
;*                                                                            *
;* Uses:                                                                      *
;*      ax                                                                    *
;*                                                                            *
;* Internal References:                                                       *
;*      DsToDosHlpData                                                        *
;*      DHClearBusyNPX                                                        *
;*      GetISR                                                                *
;*                                                                            *
;* External References:                                                       *
;*                                                                            *
;******************************************************************************
;
;   procedure DHWaitNPX
;
;       if interrupts disabled or
;         interrupt in service on pic1 or      ; all interrupts on pic 1 higher
;                                              ; priority than pic2
;         higher priority interrupt in service on pic 2
;
;           simulate fwait
;
;       else
;           clear busy latch
;           clear ts
;           fwait
;           restore ts
;
;   end procedure DHWaitNPX
;

        assume cs:DosHlp32Code, ds:nothing, es:nothing, ss:nothing

        public  DHWaitNPX
DHWaitNPX       proc    near

        push    ds
        push    ebx
        push    edx
        push    eax

        push    DOSHLP_DATASEL          ; Put prot mode data selector in DS
        pop     ds
        assume  ds:DosHlpData

        pushfd
        pop     eax
        test    eax, F_INTERRUPT ; if interrupts disabled
        jz      short dwn20      ; ints disabled

        call    GetISR
        test    eax, ISR_HIGHER_PRIORITY_NPX ; or if higher priority int in serv
        jz      short dwn40     ; higher priority in service

dwn20:
        mov     ax, cSpin       ; emulate fwait

dwn30:
        dec     ax              ; BUGBUG -- this loop is a little strange, but
        or      ax, ax          ;               the loop counts have been
        jnz     dwn30           ;               calculated and tested for this
        jmp     short dwn50     ;               loop

dwn40:
        smsw    ax
        clts                    ; prevent npx not available exception
        fwait                   ; wait for end of current NPX instr
        lmsw    ax              ; restore TS

dwn50:
        pop     eax
        pop     edx
        pop     ebx
        pop     ds
        ret

DHWaitNPX endp


;******************************************************************************
;*                                                                            *
;* DHValidNPXSwitch -- validates NPX context switch                           *
;*                                                                            *
;* Description:                                                               *
;*      this routine validates that an NPX context switch does not violate    *
;*      the NPX design.                                                       *
;*                                                                            *
;* Linkage:                                                                   *
;*      call far                                                              *
;*                                                                            *
;* Input:                                                                     *
;*      none                                                                  *
;*                                                                            *
;* Exit:                                                                      *
;*      ax = error code                                                       *
;*              VNS_NOERROR              -- valid npx context switch          *
;*              VNS_ERROR_INT_PENDING    -- npx interrupt pending             *
;*              VNS_ERROR_INT_DISABLED   -- interrupts disabled               *
;*              VNS_ERROR_INT_IN_SERVICE -- higher priority interrupt in ser. *
;*                                                                            *
;* Uses:                                                                      *
;*      ax                                                                    *
;*                                                                            *
;* Internal References:                                                       *
;*      GetISR                                                                *
;*      GetIRR                                                                *
;*      DsToDosHlpData                                                        *
;*                                                                            *
;* External References:                                                       *
;*      none                                                                  *
;*                                                                            *
;******************************************************************************
        assume cs:DosHlp32Code, ds:nothing, es:nothing, ss:nothing

;       procedure DHValidNPXSwitch
;
;               if interrupts disabled
;                       return VNS_ERROR_INT_DISABLED
;               else if NPX interrupt pending
;                       return VNS_ERROR_INT_PENDING
;               else if higher priority interrupt in service
;                       return VNS_ERROR_INT_IN_SERVICE
;               else
;                       return VNS_NOERROR
;       end DHValidNPXSwitch

        public  DHValidNPXSwitch

DHValidNPXSwitch        proc    near

        push    ds
        push    ebx
        push    DOSHLP_DATASEL          ; Put prot mode data selector in DS
        pop     ds
        assume  ds:DosHlpData

        pushfd                                  ; if interrupts disabled
        pop     eax
        test    eax, F_INTERRUPT
        jz      short dvns40                    ; ints disabled

        call    GetIRR                          ; if NPX int pending
        test    eax, word ptr NPXIRQ_MASK
        jnz     short dvns50                    ; npx int pending

        call    GetISR                          ; if higher priorty int in ser.
        test    eax, ISR_HIGHER_PRIORITY_NPX
        jnz     short dvns60                    ; higher priority in service

        mov     ax, VNS_NOERROR                 ; no error

dvns20:
        pop     ebx
        pop     ds
        ret

dvns40:
        mov     ax, VNS_ERROR_INT_DISABLED
        jmp     dvns20

dvns50:
        mov     ax, VNS_ERROR_INT_PENDING
        jmp     dvns20

dvns60:
        mov     ax, VNS_ERROR_INT_IN_SERVICE
        jmp     dvns20

DHValidNPXSwitch        endp


;**************************************************************************
;***    DHVNPXReset        This routine is called from VNPX to write to
;                          port F1.
;
;       The VNPX calls VTONPXReset (a VDH service within the VDM
;       Manager) which in turn calls this routine.
;       The write to port F1 is treated as finit.  The status word is
;       then manipulated to look like a hardware reset.
;
;       NOTES: This routine is entered in kernel mode, ring 0
;
;       ENTRY POINT: DHVNPXReset
;        LINKAGE: Call Near
;
;       ENTRY:  ECX = port to reset
;
;       EXIT:   Carry clear if succeed.
;               Carry set if wrong port for NPX reset
;
;       EXIT-ERROR: NONE
;
;       USES: NONE
;
;       INTERNAL REFERENCES:
;        ROUTINES: SwitchNPX
;
;       EXTERNAL REFERENCES:
;        ROUTINES: Panic
;**************************************************************************

Procedure DHVNPXReset,near
        ASSUME  CS:DosHlp32Code, ES:NOTHING

        SaveReg <ecx, eax, ds>
        cmp     ecx,NPX_REAL_PORT
        je      short doreset
        stc
        jc      short npxrst_exit
doreset:

; software initialize the NPX
        fninit
        fninit                          ; 2 needed for 387 errata

; make status word look like it was a hardware initialization
; page 6-5 of 80387 Programmer's Reference Manual
        push    ax
        fnstsw  ax                      ; get status word
        or      ax,npxsw_busy + npxcw_invop + npxsw_error
        fstsw   ax                      ; store status word
        pop     ax

        push    DOSHLP_DATASEL          ; Put prot mode data selector in DS
        pop     ds
        assume  ds:DosHlpData
; make control word look like it was a hardware initialization
        fnstcw  NPX_CtrlWd              ; store the control word and do not
                                        ; check for unmasked exceptions
        and     NPX_CtrlWd,npxcw_invopoff ; reset the mask bit for inv op
        fldcw   NPX_CtrlWd              ; load the processor control word
        clc
npxrst_exit:
        RestoreReg <ds, eax, ecx>
        ret
EndProc DHVNPXReset

;**************************************************************************
;***    DHVNPXClrBusy      This routine is called from VNPX to write to
;                          port F0.
;
;       The VNPX calls VTONPXClearBusy (a VDH service within the VDM
;       Manager) which in turn calls this routine
;       Only a return is performed here, it is not possible to emulate
;       the BUSY# latch.
;
;
;       ENTRY POINT: DHVNPXClrBusy
;        LINKAGE: Call Near
;
;       ENTRY:  ECX = port to clear
;
;       EXIT:   NONE
;
;       EXIT-ERROR: NONE
;
;       USES: NONE
;
;       INTERNAL REFERENCES: NONE
;
;       EXTERNAL REFERENCES: NONE
;**************************************************************************

Procedure DHVNPXClrBusy,near
        ASSUME  CS:DosHlp32Code, ES:NOTHING
        ret
EndProc DHVNPXClrBusy

DosHlp32Code    ends

                end
