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

        page    78,132
        title   dhmodesw, DosHlp Mode Switching

;******************************************************************************
;*                                                                            *
;* Source File: Dhmodesw                                                      *
;*                                                                            *
;* Description:                                                               *
;*      This file contains the DosHlp code for switching modes, handling A20  *
;*      enabling/disabling NMI, and rebooting                                 *
;*                                                                            *
;* Entry Points:                                                              *
;*      DHEnableA20     -- enable A20 address line                            *
;*      DHNMI           -- enable/disable NMI                                 *
;*      DHReboot        -- reboot machine                                     *
;*                                                                            *
;* External References:                                                       *
;*                                                                            *
;*      Locations:                                                            *
;*          _DHMachModel     -- machine model                                 *
;*          _DHMachSubModel  -- machine submodel                              *
;*                                                                            *
;*      Routines:                                                             *
;*          DsToDosHlpData  -- point ds to DosHlpData Segment                 *
;*                                                                            *
;*                                                                            *
;*                                                                            *
;******************************************************************************


.286p

.xlist
include segments.inc
include basemaca.inc
include dbprtdef.inc    ; DBPRT macro definition
include doshlp.inc
include eisa.inc
include sysinvar.inc
include iodelay.inc
.list


;;; dumpdump
_DATA    segment
        extrn _DumpSeg:word                 ; system dump support
_DATA    ends


DosHlpData      segment

        extrn   _DHMachModel:byte
        extrn   _DHMachSubModel:byte
        extrn   _config_flags:word
        extrn   KAT:dword

        SaveArea        dw      3 dup (?)
        int_mask        db      0

DosHlpData      ends

DosHlpCode      SEGMENT



        extrn   DsToDosHlpData:near
        extrn   DosHlpDelay:near
        extrn   DHDisableWatchdogNMI:near

        assume cs:DosHlpCode, ds:nothing

;******************************************************************************
;*                                                                            *
;* DHNMI   -- enable/disable NMI                                              *
;*                                                                            *
;* Description:                                                               *
;*      This routine enables or disables the NMI interrupt, depending on the  *
;*      value passed in ax, it will reset the status bits for the enable      *
;*      process if requested                                                  *
;*                                                                            *
;* Linkage:                                                                   *
;*      call far                                                              *
;*                                                                            *
;* Input:                                                                     *
;*      al = 0   - enable nmi                                                 *
;*      al = 80h - disable nmi                                                *
;*      ah = 0   - do not reset NMI status bits                               *
;*      ah = 1   - reset NMI status bits                                      *
;*                                                                            *
;* Exit:                                                                      *
;*      nmi enabled/disabled, status bits may be set                          *
;*                                                                            *
;* Uses:                                                                      *
;*      ax, ds                                                                *
;*                                                                            *
;* Internal References:                                                       *
;*                                                                            *
;* External References:                                                       *
;*                                                                            *
;******************************************************************************

        assume  ds:nothing

        public  DHNMI
DHNMI   proc    far

        push    dx
        push    cx
        push    ds

        call    DsToDosHlpData
    assume ds:DosHlpData
        cmp     al,DISABLENMI_REQ
        jne     short dnm5              ; no - enable nmi
        jmp     short dnm50

;       enable nmi
dnm5:
        pushf
        cli                             ; begin crit section

        ; if requested reset NMI status bits

        or      ah,ah
        jz      dnm15                   ; no reset requested

        mov     al,DISABLENMI           ; disable NMI flip-flop
        out     NMI_PORT,al
        call    DosHlpDelay

        ; if on an EISA machine, more status bits must be reset

        test    _config_flags, CFG_EISA ; Are we on EISA?
        jnz     dnm10                   ; yes, reset extra bits

        ; AT machine must have IOCHK and FAILSAFE bits reset

        in      al,NMI_STATUS_PORT      ; fetch AT status bits
        call    DosHlpDelay
        or      al,(NMI_IOCHK_CONTROL OR NMI_FAILSAFE_CONTROL)
                                        ; disable and clear io_chk and fs_tmr
        out     NMI_STATUS_PORT,al      ; write changes to port
        call    DosHlpDelay
        and     al,NOT (NMI_IOCHK_CONTROL OR NMI_FAILSAFE_CONTROL)
                                        ; now renable NMIs
        out     NMI_STATUS_PORT,al
        jmp     short dnm15

        ; EISA machine must have IOCHK, PARITY, BUS, Software Generated
        ; and FAILSAFE bits reset

dnm10:  in      al,EisaNMI_Port1        ; fetch EISA status bits from port 1
        call    DosHlpDelay
        or      al,(EisaNMI_IoChk_Control OR EisaNMI_Parity_Control)
                                        ; disable and clear io_chk and parity
        out     EisaNMI_Port1,al        ; write changes to port
        call    DosHlpDelay
        and     al,NOT (EisaNMI_IoChk_Control OR EisaNMI_Parity_Control)
                                        ; now renable NMIs
        out     EisaNMI_Port1,al

        mov     dx,EisaNMI_Port2
        in      al,dx                   ; fetch EISA status bits from port 2
        call    DosHlpDelay
        or      al,(EisaNMI_Software_Control OR EisaNMI_Bus_Control OR \
                    EisaNMI_FailSafe_Control)
                                        ; disable and clear parity, bus and
                                        ;  software_generated
        out     dx,al                   ; write changes to port
        call    DosHlpDelay
        and     al,NOT (EisaNMI_Software_Control OR EisaNMI_Bus_Control OR \
                        EisaNMI_FailSafe_Control)
                                        ; now renable NMIs
        out     dx,al

dnm15:
        mov     al,ENABLENMI            ; Set for enable
        out     NMI_PORT,al             ; enable NMI
        call    DosHlpDelay
        call    DosHlpDelay
        in      al,NMI_PORT+1           ; open standby latch

dnm20:  popf                            ; end critical section
        clc
        pop     ds
        pop     cx
        pop     dx
        ret

;       disable nmi

dnm50:  pushf
        mov     al,DISABLENMI           ; Set for disable
        cli                             ; begin crit section
        out     NMI_PORT,al             ; enable NMI
        call    DosHlpDelay
        call    DosHlpDelay
        in      al,NMI_PORT+1           ; open standby latch
dnm70:  popf                            ; end critical section
        clc
        pop     ds
        pop     cx
        pop     dx
        ret
DHNMI   endp


;******************************************************************************
;*                                                                            *
;* DHReboot -- reboot the machine                                             *
;*                                                                            *
;* Description:                                                               *
;*      This routine uses the 8042 to reboot the machine                      *
;*                                                                            *
;* Linkage:                                                                   *
;*      call far                                                              *
;*                                                                            *
;* Input:                                                                     *
;*      None                                                                  *
;*                                                                            *
;* Exit:                                                                      *
;*      DOESN'T (the machine reboots)                                         *
;*                                                                            *
;* Uses:                                                                      *
;*      doesn't matter (it never returns)                                     *
;*                                                                            *
;* Internal References:                                                       *
;*                                                                            *
;* External References:                                                       *
;*      DsToDosHlpData  -- sets ds to point at doshlp data                    *
;*                                                                            *
;******************************************************************************

        public  DHReboot
DHReboot        proc    far

        push    cs                      ; for simulated far call
        call    DHDisableWatchdogNMI    ; turn watchdog nmi off

        call    DsToDosHlpData
        assume  ds:DosHlpData

        test    _config_flags,CFG_ABIOS_PRESENT ; is ABIOS present?
        jz      rb05                    ; if not, go use general code

;       ABIOS is present, so reboot by diddling ports.  Works on PS/2s.

        mov     ax,RomDataSeg           ;point ds
        mov     ds,ax                   ;  to bios segment
        mov     si,72h                  ;set the
        mov     word ptr [si],1234h     ;   reset flag

        in      al, 92h
        and     al, 0feh
        out     92h, al
        in      al, 92h
        or      al, 1
        out     92h, al

        hlt
        hlt
        nop
        nop
        nop

rb05:

;       To read or write a particular location within the CMOS memory, we must
;       first write out that address to the control port (CMOS_CTRL).  Then we can
;       perform our read/write operation

;       Reinitialize CMOS clock Status Reg A and B to what SETUP would do.

        cli                             ; no races with CMOS interrupt
        mov     al,0Bh                  ; read from location 0B
        out     CMOS_CTRL,AL
        call    DosHlpDelay             ; let hardware settle
        in      AL,CMOS_DATA
        and     AL,0BFh                 ; Clear "enable periodic interrupt"
        push    ax
        mov     AL,0Bh                  ; write to location 0B
        out     CMOS_CTRL,al
        call    DosHlpDelay             ; let hardware settle
        pop     ax
        out     CMOS_DATA,al            ; the status mask without periodic ints
        call    DosHlpDelay             ; Delay a little.

        mov     AL,0Ah                  ; read from location 0A
        out     CMOS_CTRL,AL
        call    DosHlpDelay             ; Delay a little.
        in      AL,CMOS_DATA
        and     AL,0F0h                 ; Clear rate selection field.
        or      AL,6                    ; Set rate to what SETUP would.
        push    AX
        call    DosHlpDelay             ; Delay a little.
        mov     AL,0Ah                  ; write to location 0A
        out     CMOS_CTRL,AL
        call    DosHlpDelay             ; Delay a little.
        pop     AX
        out     CMOS_DATA,AL            ; The new rate selection
        call    DosHlpDelay             ; Delay a little.
        mov     AL,15                   ; Set neutral CMOS address (to prevent spurious writes).
        out     CMOS_CTRL,AL
        call    DosHlpDelay             ; Delay a little.
        in      AL,CMOS_DATA            ; Finish last access. REQUIRED for newer machines.
        call    DosHlpDelay             ; Delay a little.

rb10:   cmp     _DHMachModel, 0f8h      ; The current jda 1.2 sources for reboot
        jnz     rb30                    ; two versions of the 7552.
        cmp     _DHMachSubModel, 05h
        jz      rb20
        cmp     _DHMachSubModel, 1dh
        jnz     rb30

; model fc submodel 06 and model f8 submodels 05 and 1d will do the followings:
rb20:   mov     ax,40h                  ;point ds
        mov     ds,ax                   ;  to bios segment
        mov     si,72h                  ;set the
        mov     word ptr [si],1234h     ;   reset flag
        in      al,65h                  ;read current value
        and     al,0FBh                 ;02h clear = A20 identical to CPU
        or      al,011h                 ;10h=shutdown,01h=enable NMI
        nop                             ;
        nop                             ;
        nop                             ;
        nop                             ;
        nop                             ;
        nop                             ;
        nop                             ;
        nop                             ;
        out     65h,al                  ;write current value
rb2:    hlt                             ;
        jmp     rb2                     ; end 7552 code

;       Now force Reboot.

rb30:   mov     ax,RomDataSeg
        mov     ds,ax                   ; Point to ROM BIOS data area.
        mov     SI,72h                  ; Set warm/cold start indicator offset.
        mov     word ptr [SI],1234h     ; Write warm start indicator.
        mov     AL,0FEh                 ; Set magic value that causes reboot.
        out     KBD_STATUS_PORT,AL      ; Pass to status port, and the reboot is done!

; Ctrl-Alt-Del Fix Begin      106176 - 113198

        mov     cx,5000h
rb1:    loop    rb1

        call    DsToDosHlpData
        assume  ds:DosHlpData

        smsw    ax
        test    al,1
        jnz     rb99                    ; Skip if we're prot mode
        call    dword ptr ds:[KAT.KernelGotoProt]
rb99:
        mov     es,ds:KAT.Kernel_IDT
        mov     cx,20*8
        xor     di,di
        xor     ax,ax
        rep     stosb
        mov     ds,ax
        mov     [si],al

; Ctrl-Alt-Del Fix End  106176 - 113198

        hlt                             ; Stop while waiting for reboot.
        jmp     rb1                     ; Make sure interrupt doesn't screw things up.

DHReboot        endp



;;; dumpdump
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; system dump support code
;

        public _SaveDumpSeg       ; Blast the dump code selector into the
_SaveDumpSeg proc near            ; code - have to do this the nasty way
                                  ; as not supported by linker !

                                  ; WARNING - declared near, but called far

        push    si                ; save regs (called by 'C' )
        push    ds

        mov     si,sp
        mov     ax,ss:[si+6]      ; use cs from far return address as ds
        mov     ds,ax
        assume  ds:_DATA
        mov     ax,_DumpSeg

        push    DOSHLP_CODESEL
        pop     ds
        assume  ds:_TEXT                          ; where to blast #1
        mov     si,offset _TEXT:DHInitSystemDump
        add     si,3

        mov     ds:[si],ax                        ; and blast

        mov     si,offset _TEXT:DHSystemDump      ; where to blast #2
        add     si,3

        mov     ds:[si],ax                        ; and blast

        pop     ds                ; all done - restore regs and exit
        pop     si

        db      0CBh              ; force far return from call declared near
                                  ; but not really near

_SaveDumpSeg endp


        public DHInitSystemDump
DHInitSystemDump proc far
        ; NOTE - preserve registers - parameters being passed
        db      9Ah                 ; hand assembled far call
        dw      0000h               ; to - offset 0
        dw      0000h               ;    - dump selector (blasted above)
        ret
DHInitSystemDump endp


        public DHSystemDump
DHSystemDump proc far
        ; NOTE - preserve registers - parameters being passed
        db      9Ah                 ; hand assembled far call
        dw      0003h               ; to - offset 0
        dw      0000h               ;    - dump selector (blasted above)
        ret
DHSystemDump endp

;
; end system dump support code
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;






DosHlpCode      ends

_TEXT           segment

        assume cs:_TEXT, ds:nothing



; do not call DosHlpDelay in this code below - it has moved, and is
; no longer a valid call
A20Delay PROC Near
          dhIODelay dx
          ret
A20Delay ENDP


;******************************************************************************
;*                                                                            *
;* DHEnableA20 - Enable A20 address line                                      *
;*                                                                            *
;* Description:                                                               *
;*      This routine enables the A20 address line.                            *
;*                                                                            *
;* Linkage:                                                                   *
;*      near call                                                             *
;*                                                                            *
;* Input:                                                                     *
;*      interrupts disabled                                                   *
;*                                                                            *
;* Exit:                                                                      *
;*      A20 line enabled                                                      *
;*                                                                            *
;* Uses:                                                                      *
;*      ax                                                                    *
;*                                                                            *
;* Internal References:                                                       *
;*      empty_8042  --  waits for 8042 input buffer to drain                  *
;*                                                                            *
;* External References:                                                       *
;*      none                                                                  *
;*                                                                            *
;******************************************************************************

        assume  ds:nothing

        public  DHEnableA20
DHEnableA20     proc    near

        push    dx
        push    ds

        push    DOSHLP_CODESEL
        pop     ds
        assume  ds:DosHlpData

        ; try to enable the A20 line using the BIOS function before doing direct
        ; manipulation of the 8042 controller.

        ;Note: Chips & Technology BIOS doesn't return error for INT 15h
        ;      AX=2401h.  We must query A20 status and check registers
        ;      returned.

        mov     ax,2402h   ;Query A20 status
        int     15h
        jc      com1       ;Error, not supported
        cmp     ah,00h     ;Operation successfully completed?
        jne     com1       ; No, try old way
        cmp     al,0       ;A20 disabled? (Must be disabled!!)
        jne     com1       ; No, must be a problem with INT15h

        mov     al,1
        mov     ah,024h
        int     015h
        jc      com1   ; Error?
        cmp     ah,00  ; Operation completed successfully?
        je      comX   ; it worked! beats checking for model/submodel bytes

com1:

        cmp     _DHMachModel, 0fch
        jne     com5
        cmp     _DHMachSubModel, 009h    ; Use ABIOS code for mod 30-286
        je      com10                    ; even though not an ABIOS machine.
                                         ; Kingston 386SX upgrade card.

com5:
        cmp     _DHMachModel, 0f8h
        jne     com4
        cmp     _DHMachSubModel, 019h
        je      com10
        cmp     _DHMachSubModel, 023h
        je      com10
        cmp     _DHMachSubModel, 036h
        je      com10

        cmp     _DHMachSubModel, 030h
        jb      com4
        cmp     _DHMachSubModel, 034h
        jbe     com10

        ; Note - we must test for ABIOS_SUPPORTED, not ABIOS_PRESENT. On a
        ; RAM loaded ABIOS system in server mode, the 8042 will be disabled,
        ; so the non-ABIOS to use the 8042 will fail.

com4:   test    _config_flags,CFG_ABIOS_SUPPORTED ; is ABIOS supported ?
        jz      com0                              ; if not, go use general code

com10:
        ; ABIOS is supported, so diddle with ports to enable A20 line
        ; Note: for ABIOS, A20 is always enabled

        in      al, ALT_SUPP_PORT
        call    A20Delay
        or      al, ALT_SUPP_A20

        out     ALT_SUPP_PORT, al
        jmp     short comX

com0:
        ; no ABIOS, so use general method to enable or disable A20 line

        mov     ah,0dfh                 ; (AH) = Code for enable

        call    empty_8042              ; ensure 8042 input buffer empty
        jnz     comX                    ; 8042 error return

        ; Enable the A20 line

        mov     al,0d1h                 ; 8042 cmd to write output port
        out     KBD_STATUS_PORT,al      ; send cmd to 8042
        call    empty_8042              ; wait for 8042 to accept cmd
        jnz     comX                    ; 8042 error return
        mov     al,ah                   ; 8042 port data
        out     PORT_A,al               ; output port data to 8042
        call    empty_8042

        ; We must wait for the a20 line to settle down, which (on an AT)
        ; may not happen until up to 20 usec after the 8042 has accepted
        ; the command.  We make use of the fact that the 8042 will not
        ; accept another command until it is finished with the last one.
        ; The 0FFh command does a NULL 'Pulse Output Port'.  Total execution
        ; time is on the order of 30 usec, easily satisfying the IBM 8042
        ; settling requirement.

        mov     al,0FFh                 ;* Pulse Output Port (pulse no lines)
        out     KBD_STATUS_PORT,al      ;* send cmd to 8042
        call    empty_8042              ;* wait for 8042 to accept cmd

comX:   pop     ds
        pop     dx

        ret
DHEnableA20      endp

;******************************************************************************
;*                                                                            *
;* empty_8042 -- wait for 8042 input buffer to drain                          *
;*                                                                            *
;* Description:                                                               *
;*                                                                            *
;* Linkage:                                                                   *
;*      near call                                                             *
;*                                                                            *
;* Input:                                                                     *
;*      interrupts disabled                                                   *
;*                                                                            *
;* Exit:                                                                      *
;*      z=0 => 8042 input buffer empty                                        *
;*                                                                            *
;* Uses:                                                                      *
;*      flags                                                                 *
;*                                                                            *
;* Internal References:                                                       *
;*      none                                                                  *
;*                                                                            *
;* External References:                                                       *
;*      none                                                                  *
;*                                                                            *
;******************************************************************************

        public  DHEmpty8042
DHEmpty8042     proc    near
empty_8042:
        push    ax
        push    cx
        push    dx

        call    A20Delay

        sub     cx,cx                   ; cx = 0, timeout loop counter

emp1:   in      al,KBD_STATUS_PORT      ; read 8042 status port
        call    A20Delay
        call    A20Delay
        call    A20Delay
        call    A20Delay
        and     al,BUF_FULL             ; test buffer full bit
        loopnz  emp1

        pop     dx
        pop     cx
        pop     ax
        ret

DHEmpty8042     endp




_TEXT           ends

        end
