;       SCCSID = @(#)modesw.asm 6.1 90/11/17
    SCCSID      equ     "@(#)modesw.asm 1.39 90/10/11"

       PAGE    ,132                    ; (CTRL-OH)   IBM PC PRINTER CONDENSED
NAME MODESW
TITLE MODESW.ASM - PC-AT/ABIOS  Mode Switching Support Routines

;       IBM/Microsoft Confidential
;
;       Copyright (c) IBM Corporation 1987, 1989
;       Copyright (c) Microsoft Corporation 1987, 1989
;
;       All Rights Reserved
;
;******************************************************************************
;*      Mode Switch Routines                                                  *
;*                                                                            *
;*      DESCRIPTION:                                                          *
;*                                                                            *
;*      This file contains routines for switching the Personal Computer AT    *
;*      and the Personal Computer XT 286 between real and protect mode        *
;*      The following hardware components can be manipulated:                 *
;*                                                                            *
;*                                                                            *
;*     CPU - switch between real and protected mode                           *
;*                                                                            *
;*              Routines:       RealMode - go to real mode                    *
;*                              ProtMode - go to protected mode               *
;*                              KFGotoReal - go to real mode (DosHlp)          *
;*                              KFGotoProt - got to protected mode (DosHlp)     *
;*                                                                            *
;*     CPU - mode switching with segment/selector mapping                     *
;*                                                                            *
;*              Routines:       GoProtAll  - go protected, map CS, SS, DS, ES *
;*                              GoRealAll  - go real, unmap CS, SS, DS, ES    *
;*                                                                            *
;*      CPU Mode Check          (Debugging routines)                          *
;*                                                                            *
;*              Routines:       RealTaskOnly - Assert real mode tasktime      *
;*                              ProtTaskOnly - Assert prot mode tasktime      *
;*                              TaskOnly - Assert running at task time        *
;*                              RealOnly - Assert the cpu is in real mode     *
;*                              ProtOnly - Assert the cpu is in prot mode     *
;*      Rebooting                                                             *
;*                                                                            *
;*              Routines:       Reboot - force a warm reboot                  *
;*                                                                            *
;*                                                                            *
;*      WARNING WARNING WARNING:                                              *
;*                                                                            *
;*              Much of this code is very Hardware specific; therefore        *
;*              this code will only work on the Personal Computer AT,         *
;*              Personal Computer XT 286.                                     *
;*                                                                            *
;******************************************************************************

BREAK
.XLIST
        include dosseg.inc
        include basemaca.inc
        include fnotask.inc
        include osmaca.inc
        include protmode.inc
        include seldesc.inc
        include selxport.inc
        include mi.inc
        include struc.inc
        include config.inc      ;Machine types
        include proc.inc
        include doshlp.inc
        include modesw.inc
.LIST

        ModName modesw, FNO_MODESW_ASM
        CpuMode 386

ReloadCS macro group,alias
local a
        db      MI_LONG_JMP             ; far jump to next instruction
        dw      offset group:a
ifb <alias>
        dw      group
else
        dw      alias
endif
a:
endm


; equates for 386 mode switch
REALMODE_IDT_LIMIT EQU 400h ; real mode IDT (Interrupt Descriptor Table) - limit
REALMODE_IDT_BASE  EQU 0    ; real mode IDT base

; Return conditions

GOOD              equ 0          ; Return Code is good
Present           equ 1          ; ABIOS is present

ScrnMemSeg      equ     0B800h          ; RM segment of CGA/EGA screen memory

.sall
        ModName modesw


        ReferTask       ProcType,BYTE

        ReferGlobal     GDTDesc,FWORD           ; GDT descriptor
        ReferGlobal     IDTDesc,FWORD           ; IDT descriptor
        ReferGlobal     InterruptLevel,BYTE

        ReferGlobal     ABIOS_present,BYTE      ;@@ ABIOS present indicator
        ReferGlobal     Initialize_Flags,BYTE   ;@@ Mode Switch Present

;       Real mode segment alias selectors from RmSelLst

        ReferGlobal     RSL_SelDS,WORD
        ReferGlobal     RSL_SelES,WORD
        ReferGlobal     DevConfigTbl            ;Machine Types
        ReferGlobal     _pgPhysPageDir,DWORD    ; physical addr of page dir

        ReferGDT        GDT,WORD                ; Global Descriptor Table
        ReferGDT        GDT_DOSALIAS,WORD       ; DOSGROUP descriptor
        ReferGDT        GDT_INTSTACK,WORD       ; Interrupt stack descriptor
        ReferGDT        GDT_LDT,WORD            ; LDT descriptor
        ReferGDT        GDT_OS2LDR,WORD         ; os2ldr descriptor
        ReferGDT        GDT_RM_IDT,WORD         ; Real Mode IDT descriptor
        ReferGDT        GDT_TSS,WORD            ; TSS descriptor
        ReferGDT        GDT_PTDA,WORD           ; Current Task's PTDA
        ; 386 mode switch requires data and code
        ; code segments, these variables help define them
        ; declaring them.
        ReferGDT         GDT_RMCODE,WORD
        ReferGDT         GDT_RMDATA,WORD

        ReferFlat       _TKSSBase,DWORD         ; Base of stack
        ReferFlat       _pgpPageDir,DWORD       ; Linear addr of page directory
        ReferFlat       _pPTDACur,DWORD

        Extrn           DisableNMI:near
        Extrn           EnableNMI:near
        Extrn           INTRealMode:far
        Extrn           INTProtMode:far

        EXTRN           _VMLinToPhys:NEAR       ; Convert linear to physical
        EXTRN           _SELSelToLa:NEAR

        EXTRNFAR        DescInvalidate
        EXTRNFAR        SegMapVirt
        EXTRNFAR        EnableNMI
        EXTRNFAR        NWDDisableWatchdogNMI
        EXTRNFAR        NWDRestoreWatchdogNMI


_DATA   SEGMENT
pdSave          dd      ?               ; page dir entry save area
ptSave          dd      ?               ; page tbl entry save area

                public  _MSladdr
_MSladdr        dd      ?               ; lin addr of linear==physical page tbl
                public  _MSpte
_MSpte          dd      ?               ; page table entry (frame == 0)
                public  _MSpde
_MSpde          dd      ?               ; page directory entry setup for the
                                        ;  linear==physical page table
_DATA   ENDS

DATA    SEGMENT
        KFSaveSS  dw      0
        KFSaveCS  dw      0
DATA    ENDS

CONSTANTS SEGMENT

        extrn   DHF:dword               ; Dos Help CAll Table.

SaveArea        dw      3 dup(?)        ; for saving real mode idt vectors
int_mask        db      0

; Table for Industrial AT, Just add the Model and SubModel
Config7552      db      0fch,06h        ; 7552 table  Model,Type
                db      0f8h,05h        ;
Config7552_len  equ     ($ - Config7552)/2 ; length of table

;
; set up the IDT (Interrupt Descriptor Table) for 386 mode switch
real_idt        dw      REALMODE_IDT_LIMIT      ; real mode idt - limit
                dw      REALMODE_IDT_BASE       ;       - base
                dw      REALMODE_IDT_BASE       ;       - base

;       Physical address and GDT limit for debugger

GDTDescPhys     df      ?                       ; GDT physical address

;       Special mode bits (CR0_PE and CR0_PG)

ModeBits        dd      CR0_PE                  ; Always has CR0_PE set

;       Protected mode SS selector

ProtSS          dw      GDT_OS2LDR

CONSTANTS ENDS


                FARCODE LOW,,NoProtReal
FIRSTCODE       SEGMENT

;***
;       NOTE:  The routines RealMode and ProtModeSub MUST be located within
;       the first page frame of DOSCODE.
;
;       Mode switching requires that linear addresses have identity mapping,
;       ie. linear addresses == physical addresses.  To accomplish this, an
;       arbitrary page frame (more or less--it can't be one used in real
;       mode) is setup as a temporary page table mapped in to the first page
;       directory entry to create the linear==physical mapping.  When
;       switching from real mode back to protected mode, the mapping is
;       undone.
;
;       RealMode and ProtModeSub are placed in segment FIRSTCODE to ensure
;       that they will reside within the first frame of DOSCODE.
;


BREAK
;********************** START OF SPECIFICATIONS **********************
;*                                                                   *
;* SUBROUTINE NAME:  RealMode                                        *
;*                                                                   *
;* DESCRIPTIVE NAME: Switch from Protect to Real Mode                *
;*                                                                   *
;* FUNCTION:                                                         *
;*         This routine performs the necessary operations required   *
;*         to switch the processor from Protect to Real mode.        *
;*         This routine should only be called when the processor is  *
;*         in protect mode.                                          *
;*                                                                   *
;* NOTES:                                                            *
;*                                                                   *
;* ENTRY POINT: RealMode                                             *
;*    LINKAGE:  NEAR                                                 *
;*                                                                   *
;* INPUT:       None                                                 *
;*                                                                   *
;* EXIT-NORMAL:                                                      *
;*              CS = Paragraph Number                                *
;*              DS = Screen Memory (B800) for debugging purposes.    *
;*                   This will help in debugging routines who use    *
;*                   RealMode but forget to reset DS                 *
;*              ES = Same as DS                                      *
;*              SS = Paragraph Number                                *
;*              GP = General purpose registers are perserved         *
;*             NMI = NMI is enabled                                  *
;*             Flags are preserved                                   *
;*                                                                   *
;* EXIT-ERROR:                                                       *
;*    RETURN CODE:   Internal Error if the Stack is in High Memory   *
;*                                                                   *
;* EFFECTS:          See Exit_Normal                                 *
;*                   Processor is placed in Real Mode                *
;*                                                                   *
;* INTERNAL REFERENCES:                                              *
;*    ROUTINES:                                                      *
;*                    SelToSeg                                       *
;*    DATA AREA:                                                     *
;*                                                                   *
;* EXTERNAL REFERENCES:                                              *
;*    ROUTINES:                                                      *
;*                    IntErr (Display Internal error)                *
;*                    INTRealMode                                    *
;*                                                                   *
;*    DATA AREA:      LockInfoSegRM                                  *
;*                    LockInfoSeg                                    *
;*********************** END OF SPECIFICATIONS ***********************

        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING

                                        ; keep far entry local!
Procedure RealMode,hybrid,farlocal      ; CALLED ONLY FROM DOSCODE!
;       PMONLY
        pushf                           ; save Flags
        pusha                           ; save all the registers
        push    eax                     ; save *all* of eax
        push    ebx                     ;  and ebx
        cli                             ; critical section
        call    INTRealMode             ; set up for real mode interrupts

rm_noint:                               ; entry point for RealModeNoInt @DB70340
        CALLFAR NWDDisableWatchdogNMI   ; don't want to get these in real mode
        smsw    ax                      ; save msw
        push    ax                      ;

;       MMIOPH Performance Hook Removed
;       Maj/Min = 0281h, Data = none

        DosContext <ds>,ax

;       MMIOPH Context Switch Removed
;
;       mov  ax,MMIOSegment
;       Mov  ds:SISData.SIS_MMIOBase,ax ; save Segment for real mode

;       Prepare GDTDescPhys for real mode LGDT, and save
;       mode bits for return to protected mode.

        db      MI_OPERANDSIZE          ; Use 32-bit instruction
        sgdt    GDTDescPhys             ; Do SGDT to set limit
        mov     eax,cr0
        and     eax,CR0_PG OR CR0_PE    ; Get bits we care about
        mov     ModeBits,eax            ; Save bits for ProtModeSub
        shl     eax,1                   ; Is paging enabled?
        .errnz  CR0_PG - (1 shl 31)
        jnc     short rm4               ;  no, skip translation
        mov     bx,DOSGDTDATA           ; (BX) = GDT alias selector
        call    SelToPhys               ; (BX:AX) = physical address
        jnc     short rm2
        InternalError <RealMode: SelToPhys failed>

rm2:    mov     word ptr GDTDescPhys+2,ax
        mov     word ptr GDTDescPhys+4,bx ; Store physical address of GDT

;       Convert SS to paragraph and save it for when we get back in real mode

rm4:    mov     [ProtSS],ss             ; save sel for reload in ProtModeSub
        mov     bx,ss                   ; bx = stack selector
        call    SelToSeg                ; ax = paragraph for ss
        mov     dx,ax                   ; save in dx for now
        jnc     rm6                     ; error if ss in high mem
        InternalError <RealMode: stack in high memory>
rm6:
        mov     eax,cr0
        test    eax,CR0_PG              ; Is paging enabled?
        jz      rm8                     ;  no, skip translation

;       Paging is enabled.  Map linear==physical for this code.   _MSpde has
;       been initialized for a page directory entry pointing to the page
;       table to be used in the mapping.  We need to initialize the correct
;       pte in the page table and copy _MSpde to page directory entry 0 to
;       get the correct mapping.
;
;       Note that when we load cs with GDT_RMCODE we will be executing in a
;       linear==physical page (1 page only!), and GDT_RMDATA has no mapping
;       to physical memory at all.

        FlatContext ds,ax
        mov     eax,[_pgpPageDir]
        mov     ebx,[eax]               ; fetch page directory entry 0
        mov     [pdSave],ebx            ; save it for return to protected mode
        mov     ebx,[_MSpde]            ; get new page directory entry
        mov     [eax],ebx               ; make it page table 0
        xor     eax,eax
        mov     ax,DOSCODE
        shl     eax,4                   ; compute frame
        mov     ebx,eax
        or      eax,[_MSpte]            ; set remaining pte fields
        push    eax                     ; save pte
        shr     ebx,10                  ; compute page table pointer
        add     ebx,[_MSladdr]
        mov     eax,[ebx]               ; get current pte
        mov     [ptSave],eax            ; save it for return to protected mode
ifdef ERRATA486
        pop     eax
        mov     [ebx],eax               ; set new pte
else
        pop     dword ptr [ebx]         ; set new pte
endif
        DosContext ds,ax
        mov     eax,[_pgPhysPageDir]
        mov     cr3,eax                 ; flush the tlb

;       load data segment registers with 64K writable selectors
rm8:
        mov     ax,GSEL GDT_RMDATA
        mov     ds,ax
        mov     es,ax
        mov     fs,ax
        mov     gs,ax
        assume  ds:nothing, es:nothing, fs:nothing, gs:nothing

        cli                             ; stack about to go invalid
        call    DisableNMI

;       Far jump to selector with 64K limit so cs is right for real mode

        ReloadCS DOSCODE,<GSEL GDT_RMCODE> ; LOAD CS CACHE WITH
                                        ; LIMIT OF 0FFFFh
        mov     ss, ax

        ; disable paging first (if enabled)

        mov     eax,cr0
        test    eax,CR0_PG              ; Is paging enabled?
        jz      rm10                    ;  no

        and     eax,not CR0_PG          ; turn off paging
        mov     cr0,eax
        jmp     $+2                     ; flush instruction pipeline

        ; we're in protected mode with paging disabled, linear==physical

        xor     ebx,ebx                 ; flush the tlb
        mov     cr3,ebx

        ; go into real mode
rm10:
        and     eax,not CR0_PE          ; turn off PE
        mov     cr0,eax
        ReloadCS DOSCODE                ; flush instruction pipeline

        ; restore segment registers

        mov     ss,dx
        DosContext      <ds,es>,ax
        NullContext     <fs,gs>,ax
        db      MI_OPERANDSIZE
        lidt    fword ptr ds:[real_idt] ; set up real mode idt
        call    EnableNMI               ; nmi ok now

;       Reload the GDT register for the debugger

        db      MI_OPERANDSIZE
        lgdt    GDTDescPhys

;       Restore msw without protect mode bit.

        pop     ax                      ; get old msw from stack
        and     ax,not CR0_PE
        lmsw    ax

;       Point es and ds to screen memory - just in case someone tries to
;       use our return es/ds, maybe the error will show up on the screen.

        mov     ax,ScrnMemSeg
        mov     ds,ax
        mov     es,ax
    ASSUME  DS:NOTHING,ES:NOTHING

        pop     ebx
        pop     eax
        popa                            ; restore scratch registers
        popf                            ; ints may be off!!
        ret

EndProc RealMode

; @DB70340 - Needed an entry point for switching modes without
;            enableing interrupts.
;
;********************** START OF SPECIFICATIONS **********************
;*                                                                   *
;* SUBROUTINE NAME:  RealModeNoInt                                   *
;*                                                                   *
;* FUNCTION:                                                         *
;*      This function provides an entry point for RealMode to allow  *
;*      the switch without unmasking the interrupts. At this time    *
;*      this is only called from ByteToScrnDM.                       *
;*                                                                   *
;*********************** END OF SPECIFICATIONS ***********************

        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING

                                        ; keep far entry local!
Procedure RealModeNoInt,hybrid,farlocal     ; CALLED ONLY FROM DOSCODE!
;       PMONLY
        pushf                           ; save Flags
        pusha                           ; save all the registers
        push    eax                     ; save *all* of eax
        push    ebx                     ;  and ebx
        cli                             ; critical section

        jmp     rm_noint                ; use previous code to avoid duplication

EndProc RealModeNoInt
; @DB70340 - End of insertion


BREAK
;********************** START OF SPECIFICATIONS **********************
;* SUBROUTINE NAME:  ProtModeSub                                     *
;*                                                                   *
;* DESCRIPTIVE NAME: Switch from Real to Protect Mode                *
;*                                                                   *
;* FUNCTION:                                                         *
;*         This routine performs the necessary operations required   *
;*         to switch the processor from Real to Protect mode.        *
;*         This routine should only be called from real mode.        *
;*         The Mode Switch Manager will always perform the           *
;*         mode switch by adjusting the bit in the Machine Status    *
;*         word.                                                     *
;*                                                                   *
;* NOTES:                                                            *
;*                                                                   *
;* ENTRY POINT: ProtModeSub                                          *
;*    LINKAGE:  NEAR                                                 *
;*                                                                   *
;* INPUT:       None                                                 *
;*                                                                   *
;* EXIT-NORMAL:                                                      *
;*              CS = DOSCODE selector                                *
;*           ES,DS = Segment                                         *
;*              SS = Stack selector last in use when in prot mode    *
;*             GDT = GDT Descriptor                                  *
;*             LDT = LDT Descriptor                                  *
;*             IDT = IDT Descriptor                                  *
;*              TR = TSS Descriptor                                  *
;*                                                                   *
;* EXIT-ERROR:                                                       *
;*    RETURN CODE:   Internal Error if the Stack does not belong     *
;*                   to Kernel (DOSGROUP) or os2ldr.                 *
;*                                                                   *
;* EFFECTS:     See Exit_Normal                                      *
;*              Processor is placed in Protect Mode                  *
;*                                                                   *
;* INTERNAL REFERENCES:                                              *
;*    ROUTINES:                                                      *
;*                ProtModeSub                                        *
;*    DATA AREA:                                                     *
;*                                                                   *
;* EXTERNAL REFERENCES:                                              *
;*    ROUTINES:                                                      *
;*********************** END OF SPECIFICATIONS ***********************

        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING

Procedure ProtModeSub,near

ArgVar  pms_flags,WORD                  ; Flags on the stack

        EnterProc
        DosContext ds,ax

        CALLFAR NWDRestoreWatchdogNMI   ; restore the state of watchdog timer
;       Put the NPX into Sync


;       Set the FLAGS to reflect protect mode flags
        cli

;       MMIOPH Context Switch Removed
;
;       mov  ax,MMIOSelector
;       mov  ds:SISData.SIS_MMIOBase,ax    ; Save Segment for real mode

        mov     ax,[pms_flags]
        and     ax,F_IOPLclear and F_NTCLEAR
        or      ax,F_IOPLSYS
        mov     [pms_flags],ax
        and     ax,NOT F_INTERRUPT
        push    ax                      ; Set the flags
        popf

        call    DisableNMI

        db      MI_OPERANDSIZE
        lgdt    GDTDesc
        db      MI_OPERANDSIZE
        lidt    IDTDesc

;       point to the page directory

        mov     eax,[_pgPhysPageDir]
        mov     cr3,eax

;       go protected, just need to add a bit to the machine status word

        mov     eax,cr0
        or      eax,[ModeBits]          ; Set PE and maybe PG
        push    eax                     ; Save cr0
        mov     cr0,eax

;       flush prefetch queue and reset cs by doing a far jump

        ReloadCS DOSCODE

;       reload ss with the selector it last had when in protected mode
        xor     ax,ax
        mov     es,ax
        DosContext ds,ax
        mov     ss,[ProtSS]

        call    EnableNMI

;       If paging is enabled, restore the page directory entry used to map
;       linear==physical.

        pop     eax                     ; (eax)=cr0
        test    eax,CR0_PG
        jz      pms4

        push    ebx
        FlatContext ds,ax
        mov     eax,[_pgpPageDir]
        mov     ebx,[pdSave]
        mov     [eax],ebx               ; restore original page directory entry
        xor     eax,eax
        mov     ax,DOSCODE
        shr     eax,10 - 4              ; compute pte address
        add     eax,[_MSladdr]
        mov     ebx,[ptSave]
        mov     [eax],ebx               ; restore original page table entry
        pop     ebx

;       Set the Task Register and LDT Register.
;       Clear the tss busy flag.

pms4:   GDTContext ds,ax
        and     byte ptr [GDT_TSS].d_access,D_TSSBUSY_CLR

        mov     ax,GSEL GDT_TSS
        ltr     ax

        mov     ax,GSEL GDT_LDT
        lldt    ax

        call    INTProtMode             ; reload protected mode PIC masks

        xor     ax,ax                   ; reload segment registers to zero
        mov     ds,ax
        mov     es,ax
        mov     fs,ax
        mov     gs,ax
    ASSUME   DS:NOTHING, ES:NOTHING

;       On return, INTERRUPTS ARE DISABLED. This allows the caller to
;       do any other necessary initialization.

        LeaveProc
        ret

EndProc ProtModeSub

FIRSTCODE       ENDS


        FARCODE LOW,,NoProtReal
CODE SEGMENT


;***LP  SelToPhys - convert selector to physical address
;
;       Given a selector, this procedure returns the physical
;       address of the first page in the segment.
;
;       ENTRY   (BX) = selector
;       EXIT    Carry clear
;                   (BX:AX) = physical address
;               Carry set
;                   Error
;       USES    EAX, EBX, Flags

Procedure SelToPhys,near,local
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING

LocalVar        Sel,WORD

        EnterProc       <bx>

        push    ds
        test    bx,TABLE_MASK           ; is the selector in the LDT?
        jz      short stp00             ; if GDT, jmp

        movzx   ebx,bx
        CCALL32 _SELSelToLa <ebx, [_pPTDACur]> ; (eax) = base of selector
        mov     ebx,eax
        shr     ebx,16                  ; (bx:ax) = base
        jmp     stp00a                  ; skip special cases that can't be ldt

stp00:  mov     ax,DOSGDTDATA           ; selector is in the GDT
        mov     ds,ax
        and     bl,SEL_LOW_MASK
        cmp     bx,1
        jc      short stp01             ; error if NULL selector

        test    ds:[bx].d_access,D_PRES
        jz      short stpie             ; IntErr if segment is not present

        mov     al,ds:[bx].d_hiaddr
        mov     ah,ds:[bx].d_extaddr    ; (AX) = high 16 bits of base
        mov     bx,ds:[bx].d_loaddr     ; low 16 bits of base address
        xchg    ax,bx                   ; (BX:AX) = base linear address

;       If paging not enabled, then physical = linear, and we're done.

        push    eax
        mov     eax,cr0
        shl     eax,1                   ; Is paging enabled?
        .errnz  CR0_PG - (1 shl 31)
        pop     eax
        jnc     short stp01             ;  no, skip translation

        cmp     Sel, GSEL GDT_PTDA      ; PTDA selector?
        jne     short stp00a            ; no.  No adjustment required.

;       The zeroth page of GDT_PTDA is not present.  However, we do
;       know that the page under SS:BP is present, so we use it instead.
;       PTDA STRUCTURES MUST BE PARAGRAPH ALIGNED FOR THIS TO WORK!

        add     ax,bp
        adc     bx,0                    ; Convert ss:bp to physical address

;       Now call _VMLinToPhys to get true base address

stp00a: push    ecx
        push    edx
        push    es
        FlatContext <ds,es>,dx
        sub     sp,4                    ; Make room for return value
        movzx   edx,sp                  ; (ECX) = stack-relative address
        SSToDS  edx                     ; (EDX) = flat address of temp loc
        shl     ebx,16                  ; (EBX) = linear address(bx:ax)
        mov     bx,ax
        CCALL32 _VMLinToPhys,<ebx,edx>  ; Convert to physical address
        cmp     eax,1
        cmc                             ; Carry set if error
        pop     ax
        pop     bx                      ; (BX:AX) = phys addr (if no error)
        pop     es
    ASSUME      DS:NOTHING, ES:NOTHING
        pop     edx
        pop     ecx
        pushf                           ; save carry flag
        cmp     Sel, GSEL GDT_PTDA      ; PTDA selector?
        jne     short stp00b            ; no - we're done.

        sub     ax,bp                   ; Readjust physical address to
        sbb     bx,0                    ; match base of stack segment.
stp00b: popf                            ; restore carry flag
stp01:  pop     ds
        LeaveProc
        ret

stpie:  int     3
        InternalError <SelToPhys:bad access>

EndProc SelToPhys


;********************** START OF SPECIFICATIONS *************************
;*                                                                      *
;* SUBROUTINE NAME:  SelToSeg                                           *
;*                                                                      *
;* DESCRIPTIVE NAME: Convert a selector to a corresponding segment      *
;*                                                                      *
;* FUNCTION:                                                            *
;*                                                                      *
;*       This routine converts a protected mode selector value          *
;*       to a real mode segment number.  This procedure must            *
;*       only be called from protect mode.                              *
;*                                                                      *
;* NOTES:                                                               *
;*                                                                      *
;* ENTRY POINT: SelToSeg                                                *
;*    LINKAGE:  Call NEAR from DOSCODE                                  *
;*            Call FAR from any other segment                           *
;* INPUT:                                                               *
;*            BX = The selector to be converted                         *
;*                                                                      *
;* EXIT-NORMAL:                                                         *
;*            If CF = 0                                                 *
;*               AX = Paragraph number                                  *
;*                                                                      *
;* EXIT-ERROR:                                                          *
;*    RETURN CODE:                                                      *
;*                                                                      *
;*            If CF = 1                                                 *
;*               Segment is in high memory                              *
;*                                                                      *
;* EFFECTS:       AX, BX, Flags                                         *
;*                                                                      *
;* INTERNAL REFERENCES:                                                 *
;*    ROUTINES:                                                         *
;*                                                                      *
;* EXTERNAL REFERENCES:                                                 *
;*    ROUTINES:                                                         *
;*                                                                      *
;*********************** END OF SPECIFICATIONS **************************


Procedure SelToSeg,hybrid
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING

;       PMONLY
        call    SelToPhys               ; (BX:AX) = physical address
        jc      short stsx              ;  exit if error
        test    ax,0fh                  ; Paragraph aligned?
        stc                             ; (assume no)
        jnz     short stsx              ;  no, return carry set
        test    bx,not 0fh              ; High memory segment?
        stc                             ; (assume yes)
        jnz     short stsx              ;  yes, return carry set
        shr     ax,4                    ; Convert address to paragraph number
        shl     bl,4
        or      ah,bl                   ; AX = segment number for selector
stsx:   ret

EndProc SelToSeg


BREAK



;*********************************************************************
;* FUNCTION:    KFGotoReal - Switch to real mode.  Will only be      *
;*              called from DosHlp.                                  *
;*                                                                   *
;* ENTRY:                                                            *
;*              None                                                 *
;*                                                                   *
;* EXIT-NORMAL:                                                      *
;*              CS,DS,ES,SS = Segment Numbers                        *
;*              GP = General purpose registers are perserved         *
;*              NMI = NMI is enabled                                 *
;*********************************************************************
Procedure KFGotoReal,far
    assume ds:nothing, es:nothing, ss:nothing
;       PMONLY
;
; Convert selectors in DS and ES to segments
;
        push    bp              ; SLT
        push    ax
        push    bx
        mov     bx,ds           ; Load BX with DS selector
        call    SelToSeg        ; AX = segment value for DS
        push    ax              ; Save DS segment value
        mov     bx,es           ; Load BX with ES selector
        call    SelToSeg        ; AX = segment value for ES
        push    ax              ; Save ES segment value
        mov     bp,sp           ; SLT
        mov     bx,[bp+12]      ; SLT Load BX with caller's CS selector
        DosContext <ds>         ; SLT
        mov     KFSaveCS,bx     ; SLT Save CS selector for GotoProt
        call    SelToSeg        ; SLT AX = segment value for CS
        mov     [bp+12],ax      ; SLT Change CS selector to segment value
        mov     KFSaveSS,ss     ; Save SS selector for GotoProt
;
; Switch to RealMode
;
        call    RealMode
        pop     es              ; Restore segments into DS and ES
        pop     ds
        pop     bx              ; Restore original AX and BX
        pop     ax
        pop     bp
        ret

EndProc KFGotoReal

;*********************************************************************
;* FUNCTION:    KFGotoProt - Switch to protected mode.  Will only be *
;*              called from DosHlp.                                  *
;*                                                                   *
;* ENTRY:                                                            *
;*              None                                                 *
;*                                                                   *
;* EXIT-NORMAL:                                                      *
;*              CS, SS = Selectors                                   *
;*              ES, DS = 0                                           *
;*              GP = General purpose registers are perserved         *
;*              NMI = NMI is unaffected                              *
;*********************************************************************
Procedure KFGotoProt,far
    assume ds:nothing, es:nothing, ss:nothing
;       RMONLY
        push    bp
        push    ax
        push    0                       ; Null values for ES and DS
        push    0
        pushf
        call    ProtModeSub             ; Switch to protected mode
;       DosContext <ds>
        mov     ax, DOSGROUP
        mov     ds, ax
        ASSUME  DS:DOSGROUP

        mov     SS,KFSaveSS             ; Restore stack selector
        push    cs                      ; Much faster than POPFF, and
        push    offset DOSCODE:kfp10    ; it reloads CS, too.
        iret
kfp10:
        mov     ax,KFSaveCS             ; SLT Restore CS selector
        mov     bp,sp                   ; SLT
        mov     [bp+10],ax              ; SLT Edit the return CS
        pop     ds
        pop     es
        pop     ax
        pop     bp
        ret

EndProc KFGotoProt

BREAK
;********************** START OF SPECIFICATIONS **********************
;* SUBROUTINE NAME:  ProtMode                                        *
;*                                                                   *
;* DESCRIPTIVE NAME: Switch from Real to Protect Mode                *
;*                                                                   *
;* FUNCTION:                                                         *
;*         This routine performs the necessary operations required   *
;*         to switch the processor from Real to Protect mode.        *
;*         The DS and ES registers will contain zero upon exit.      *
;*         The SS register will point to the current PTDA.           *
;*                                                                   *
;* NOTES:                                                            *
;*                                                                   *
;* ENTRY POINT: ProtMode                                             *
;*    LINKAGE:  NEAR                                                 *
;*                                                                   *
;* INPUT:       None                                                 *
;*                                                                   *
;* EXIT-NORMAL:                                                      *
;*              CS = DOSCODE selector                                *
;*          ES, DS = 0                                               *
;*              SS = Selector for current PTDA                       *
;*             GDT = GDT Descriptor                                  *
;*             LDT = LDT Descriptor                                  *
;*             IDT = IDT Descriptor                                  *
;*              TR = TSS Descriptor                                  *
;*              GP = General purpose registers are perserved         *
;*             NMI = NMI is unaffected                               *
;*              IF = Interrupt Flag unchanged                        *
;*              DF = Direction Flag unchanged                        *
;*              AF = Arithmetic Flags unchanged                      *
;*            IOPL = Set in Flags register                           *
;*                                                                   *
;* EXIT-ERROR:                                                       *
;*    RETURN CODE:   Internal Error if the Stack does not belong     *
;*                   to Kernel (DOSGROUP) or os2ldr.                 *
;*                                                                   *
;* EFFECTS:     See Exit_Normal                                      *
;*              Processor is placed in Protect Mode                  *
;*                                                                   *
;* INTERNAL REFERENCES:                                              *
;*    ROUTINES:                                                      *
;*                ProtModeSub                                        *
;*                INTProtMode                                        *
;*    DATA AREA:                                                     *
;*                                                                   *
;* EXTERNAL REFERENCES:                                              *
;*    ROUTINES:                                                      *
;*    DATA AREA:                                                     *
;*********************** END OF SPECIFICATIONS ***********************


        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING

        public  ProtMode                ; keep hybrid entry local!

Procedure ProtMode,hybrid,local         ; CALLED ONLY FROM DOSCODE, DOSCODE!
;       RMONLY

;       MMIOPH Performance Hook Removed
;       Maj/Min = 0201H, Data = none

        push    ax
        pushf                           ; THESE WILL BE EDITED BY PROTMODESUB
        call    ProtModeSub             ; IN PROTECTED MODE, INTERRUPTS OFF,

        push    cs                      ; Much faster than popf, and
        push    offset DOSCODE:prm40    ; it reloads CS, too.
        iret
prm40:  pop     ax
        ret

EndProc ProtMode

BREAK

;***    ProtModeICSub - Support routine for ProtModeIC
;
;       This routine is the same as ProtMode but does not muck with
;       the stack, data or extra segments/selectors.
;
;       entry:  CS = paragraph numbers
;       exit:   CS = selector for DOSCODE
;       notes:  DS,ES,SS are not reloaded by this routine! Even though the
;               paragraph number is not valid as a selector, the descriptor
;               cache still contains the base (physical) address of the
;               paragraph. The stack is still addressable!!!

Procedure ProtModeICSub,faronly,local ; called only from ProtModeIC
        pushf                           ; save flags for later IRET
        call    ProtModeSub             ; expects FLAGS on the stack
        push    cs                      ; fake out the IRET
        push    offset pmsx
        iret                            ; reload CS and FLAGS
pmsx:   ret
EndProc ProtModeICSub


ifdef MODECHECK                             ; Conditional Checking

;********************** START OF SPECIFICATIONS **********************
;*                                                                   *
;* SUBROUTINE NAME:  RealOnly                                        *
;*                                                                   *
;* DESCRIPTIVE NAME: Verify the CPU is in real mode                  *
;*                                                                   *
;* FUNCTION:                                                         *
;*                                                                   *
;*         This routine verifys that the CPU is currently in REAL    *
;*         mode.  The whatmode Macro is use to determine the mode.   *
;*         If the processor is in real mode the whatmode Macro       *
;*         set CF to zero.  If the processor is in Protect mode      *
;*         an internal error is generated.                           *
;*                                                                   *
;* NOTES:                                                            *
;*                                                                   *
;*         This Procedure is used for debugging purposes and is      *
;*         therefore conditionally assembled based on the            *
;*         MODECHECK flag.                                           *
;*                                                                   *
;* ENTRY POINT: RealOnly                                             *
;*    LINKAGE:  Call NEAR from DOSCODE                               *
;*              Call FAR from any other segment                      *
;* INPUT:                                                            *
;*              None                                                 *
;*                                                                   *
;* EXIT-NORMAL:                                                      *
;*                                                                   *
;* EXIT-ERROR:                                                       *
;*    RETURN CODE:   None                                            *
;*                                                                   *
;*                   An internal error is generated if processor     *
;*                   is in protect mode.                             *
;*                                                                   *
;* EFFECTS:          All registers and flags are preserved           *
;*                                                                   *
;* INTERNAL REFERENCES:                                              *
;*    ROUTINES:                                                      *
;*                   whatmode : Macro to determine processor mode    *
;*                                                                   *
;* EXTERNAL REFERENCES:                                              *
;*    ROUTINES:                                                      *
;*                                                                   *
;*********************** END OF SPECIFICATIONS ***********************

Procedure RealOnly,hybrid
    assume ds:nothing, es:nothing, ss:nothing
        pushf
        whatmode
        jnc     rox             ; skip internal error if real mode
        InternalError <RealOnly>,RealOnly
rox:    popf
        ret
EndProc RealOnly

BREAK

;********************** START OF SPECIFICATIONS **********************
;*                                                                   *
;* SUBROUTINE NAME:  ProtOnly                                        *
;*                                                                   *
;* DESCRIPTIVE NAME: Verify the CPU is in protect mode               *
;*                                                                   *
;* FUNCTION:                                                         *
;*                                                                   *
;*         This routine verifys that the CPU is currently in PROTECT *
;*         mode.  The whatmode Macro is use to determine the mode.   *
;*         If the processor is in protect mode the whatmode Macro    *
;*         set CF to one.   If the processor is in Real mode         *
;*         an internal error is generated.                           *
;*                                                                   *
;* NOTES:                                                            *
;*                                                                   *
;*         This Procedure is used for debugging purposes and is      *
;*         therefore conditionally assembled based on the            *
;*         MODECHECK flag.                                           *
;*                                                                   *
;* ENTRY POINT: ProtOnly                                             *
;*    LINKAGE:  Call NEAR from DOSCODE                               *
;*              Call FAR from any other segment                      *
;* INPUT:                                                            *
;*              None                                                 *
;*                                                                   *
;* EXIT-NORMAL:                                                      *
;*                                                                   *
;* EXIT-ERROR:                                                       *
;*    RETURN CODE:   None                                            *
;*                                                                   *
;*                   An internal error is generated if processor     *
;*                   is in real mode.                                *
;*                                                                   *
;* EFFECTS:          All registers and flags are preserved           *
;*                                                                   *
;* INTERNAL REFERENCES:                                              *
;*    ROUTINES:                                                      *
;*                   whatmode : Macro to determine processor mode    *
;*                                                                   *
;* EXTERNAL REFERENCES:                                              *
;*    ROUTINES:                                                      *
;*                                                                   *
;*********************** END OF SPECIFICATIONS ***********************

Procedure ProtOnly,hybrid
    assume ds:nothing, es:nothing, ss:nothing
        pushf
        whatmode
        jc      pox             ; skip internal error if protected mode
        InternalError <ProtOnly>,ProtOnly
pox:    popf
        ret
EndProc ProtOnly


BREAK

;********************** START OF SPECIFICATIONS **********************
;*                                                                   *
;* SUBROUTINE NAME:  TaskOnly                                        *
;*                                                                   *
;* DESCRIPTIVE NAME: Verify currently running task time              *
;*                                                                   *
;* FUNCTION:                                                         *
;*                                                                   *
;*         This routine verifys currently running at task time.      *
;*         It checks the variable name InterruptLevel to             *
;*         see if this is a task time operation                      *
;*                                                                   *
;* NOTES:                                                            *
;*                                                                   *
;*         This Procedure is used for debugging purposes and is      *
;*         therefore conditionally assembled based on the            *
;*         MODECHECK flag.                                           *
;*                                                                   *
;* ENTRY POINT: TaskOnly                                             *
;*    LINKAGE:  Call NEAR from DOSCODE                               *
;*              Call FAR from any other segment                      *
;* INPUT:                                                            *
;*              None                                                 *
;*                                                                   *
;* EXIT-NORMAL:                                                      *
;*                                                                   *
;* EXIT-ERROR:                                                       *
;*    RETURN CODE:   None                                            *
;*                                                                   *
;*                   An internal error is generated if task is       *
;*                   interrupt time.                                 *
;*                                                                   *
;* EFFECTS:          All registers and flags are preserved           *
;*                                                                   *
;* INTERNAL REFERENCES:                                              *
;*    ROUTINES:                                                      *
;*                                                                   *
;* EXTERNAL REFERENCES:                                              *
;*    ROUTINES:                                                      *
;*    DATA AREA:                                                     *
;*                   InterruptLevel : Byte indicating Task or        *
;*                                    interrupt level                *
;*                                                                   *
;*********************** END OF SPECIFICATIONS ***********************
Procedure TaskOnly,<near,faronly,far16>
    assume ds:nothing, es:nothing, ss:nothing
        pushf
        push    ds
        DosContext ds
        cmp     InterruptLevel,-1
        jne     to_ie                   ; internal error if interrupt time
        pop     ds
        popf
        RETP
to_ie:  InternalError <TaskOnly>,TaskOnly
EndProc TaskOnly

BREAK

;********************** START OF SPECIFICATIONS **********************
;*                                                                   *
;* SUBROUTINE NAME:  RealProcTaskOnly                                *
;*                                                                   *
;* DESCRIPTIVE NAME: Verify currently running task time and current  *
;*                   process is real mode                            *
;*                                                                   *
;* FUNCTION:                                                         *
;*                                                                   *
;*         This routine verifys currently running at task time and   *
;*         the current process is a real mode process.               *
;*         This routines checks the byte ProcType to determine       *
;*         if this is a real mode procedure.  The whatmode Macro     *
;*         is used to check the Machine Status Word to determine     *
;*         the current mode.  The whatmode macro set CF = 1          *
;*         if the current mode is protect.                           *
;*                                                                   *
;* NOTES:                                                            *
;*                                                                   *
;*         This Procedure is used for debugging purposes and is      *
;*         therefore conditionally assembled based on the            *
;*         MODECHECK flag.                                           *
;*                                                                   *
;* ENTRY POINT: RealProcTaskOnly                                     *
;*    LINKAGE:  Call NEAR from DOSCODE                               *
;*              Call FAR from any other segment                      *
;* INPUT:                                                            *
;*              None                                                 *
;*                                                                   *
;* EXIT-NORMAL:                                                      *
;*                                                                   *
;* EXIT-ERROR:                                                       *
;*    RETURN CODE:   None                                            *
;*                                                                   *
;*                   An internal error is generated if task is       *
;*                   interrupt time or the current process is not    *
;*                   a real mode process.                            *
;*                                                                   *
;* EFFECTS:          All registers and flags are preserved           *
;*                                                                   *
;* INTERNAL REFERENCES:                                              *
;*    ROUTINES:                                                      *
;*                   TaskOnly                                        *
;*                                                                   *
;* EXTERNAL REFERENCES:                                              *
;*    ROUTINES:                                                      *
;*                   ProcType : Byte indicating if Process is        *
;*                                  real mode process.               *
;*                                                                   *
;*********************** END OF SPECIFICATIONS ***********************

Procedure RealProcTaskOnly,hybrid
    assume ds:nothing, es:nothing, ss:TASKAREA
        call    TaskOnly
        pushf
        cmp     ProcType,PT_REALMODE    ; real mode process?
        jne     rpo_ie                  ; no, give error
        popf                            ; interrupts generally on at task time
        ret
rpo_ie: InternalError <RealProcOnly>,RealProcOnly
EndProc RealProcTaskOnly

BREAK

;********************** START OF SPECIFICATIONS **********************
;*                                                                   *
;* SUBROUTINE NAME:  RealTaskOnly                                    *
;*                                                                   *
;* DESCRIPTIVE NAME: Verify current mode is real and operation       *
;*                   is task time.                                   *
;*                                                                   *
;* FUNCTION:                                                         *
;*                                                                   *
;*         This routine verifys that the current mode of the         *
;*         processor is real and the operation is task time          *
;*         (not interrupt)                                           *
;*                                                                   *
;* NOTES:                                                            *
;*                                                                   *
;*         This Procedure is used for debugging purposes and is      *
;*         therefore conditionally assembled based on the            *
;*         MODECHECK flag.                                           *
;*                                                                   *
;* ENTRY POINT: RealTaskOnly                                         *
;*    LINKAGE:  Call NEAR from DOSCODE                               *
;*              Call FAR from any other segment                      *
;* INPUT:                                                            *
;*              None                                                 *
;*                                                                   *
;* EXIT-NORMAL:                                                      *
;*                                                                   *
;* EXIT-ERROR:                                                       *
;*    RETURN CODE:   None                                            *
;*                                                                   *
;*                   An internal error is generated if the processor *
;*                   is in protect mode or if the task is an         *
;*                   interrupt time task                             *
;*                                                                   *
;* EFFECTS:          All registers and flags are preserved           *
;*                                                                   *
;* INTERNAL REFERENCES:                                              *
;*    ROUTINES:                                                      *
;*                   RealOnly                                        *
;*                   TaskOnly                                        *
;*                   whatmode : Macro to determine processor mode    *
;*                                                                   *
;* EXTERNAL REFERENCES:                                              *
;*    ROUTINES:                                                      *
;*                                                                   *
;*********************** END OF SPECIFICATIONS ***********************

Procedure RealTaskOnly,hybrid
    assume ds:nothing, es:nothing, ss:nothing
        call    RealOnly
        call    TaskOnly
        ret
EndProc RealTaskOnly

BREAK


;********************** START OF SPECIFICATIONS **********************
;*                                                                   *
;* SUBROUTINE NAME:  ProtTaskOnly                                    *
;*                                                                   *
;* DESCRIPTIVE NAME: Verify currently running task time and          *
;*                   processor is in protect mode                    *
;*                                                                   *
;* FUNCTION:                                                         *
;*                                                                   *
;*         This routine verifys currently running at task time and   *
;*         the processor is in protect mode.  The whatmode MACRO     *
;*         is used to check the Machine Status Word to determine     *
;*         the current mode.  The whatmode macro set CF = 1          *
;*         if the current mode is protect.                           *
;*                                                                   *
;* NOTES:                                                            *
;*                                                                   *
;*         This Procedure is used for debugging purposes and is      *
;*         therefore conditionally assembled based on the            *
;*         MODECHECK flag.                                           *
;*                                                                   *
;* ENTRY POINT: ProtProcTaskOnly                                     *
;*    LINKAGE:  Call NEAR from DOSCODE                               *
;*              Call FAR from any other segment                      *
;* INPUT:                                                            *
;*              None                                                 *
;*                                                                   *
;* EXIT-NORMAL:                                                      *
;*                                                                   *
;* EXIT-ERROR:                                                       *
;*    RETURN CODE:   None                                            *
;*                                                                   *
;*                   An internal error is generated if task is       *
;*                   interrupt time or the current mode is not       *
;*                   protect mode.                                   *
;*                                                                   *
;* EFFECTS:          All registers and flags are preserved           *
;*                                                                   *
;* INTERNAL REFERENCES:                                              *
;*    ROUTINES:                                                      *
;*                   ProtOnly                                        *
;*                   TaskOnly                                        *
;*                   whatmode : Macro to determine processor mode    *
;*                                                                   *
;* EXTERNAL REFERENCES:                                              *
;*    ROUTINES:                                                      *
;*                                                                   *
;*********************** END OF SPECIFICATIONS ***********************
Procedure ProtTaskOnly,hybrid
    assume ds:nothing, es:nothing, ss:nothing
        call    ProtOnly
        call    TaskOnly
        ret
EndProc ProtTaskOnly


endif                                           ; End conditional Mode Check

BREAK


;******************************************************************************
;       GoProt/GoReal routines                                                *
;                                                                             *
;       These routines support real mode (for init & panic).  They allow      *
;       switching between real and protected mode, with segment to selector   *
;       mapping.  They store selectors which alias real mode segments in      *
;       a global location so that the memory manager can take special action  *
;       on them.  This is needed because there won't usually be a physical    *
;       arena handle for the objects the selectors point to.                  *
;                                                                             *
;       The array RmSegLst (in DOSGROUP, see sdata.asm), will contain those   *
;       selectors we use to alias to arbitrary real mode addresses.  The      *
;       memory manager scans this array in order to determine if it is        *
;       working on a selector which maps to an object that does not have      *
;       a handle in the physical arena.                                       *
;                                                                             *
;       WARNING These routines are *NOT* reentrant.  They are intended for    *
;               use in system initialization.
;                                                                             *
;       GoProtAll  - go protected, map CS, SS, DS, ES                         *
;       GoRealAll  - go real, unmap CS, SS, DS, ES                            *
;                                                                             *
;******************************************************************************


BREAK

;********************** START OF SPECIFICATIONS **********************
;* SUBROUTINE NAME:  GoProtAll                                       *
;*                                                                   *
;* DESCRIPTIVE NAME: Switch from Real to Protect Mode for sysinit    *
;*                   and map CS, SS, DS, and ES                      *
;* FUNCTION:                                                         *
;*         This routine performs the necessary operations required   *
;*         to switch the processor from Real to Protect mode.        *
;*         This routine is used to support system initialization.    *
;*         This routine should only be called from real mode.        *
;* NOTES:                                                            *
;*         Used in conjunction with GoRealAll                        *
;*                                                                   *
;* ENTRY POINT: GoProtAll                                            *
;*    LINKAGE:  NEAR                                                 *
;* INPUT:                                                            *
;*              DS = Must contain segment to provide ALIAS for       *
;*              ES = Must contain segment to provide ALIAS for       *
;* EXIT-NORMAL:                                                      *
;*              CS = Tiled                                           *
;*              DS = Alias selector for real memory segment          *
;*              ES = Alias selector for real memory segment          *
;*              SS = Selector for current PTDA                       *
;*             GDT = GDT Descriptor                                  *
;*             LDT = LDT Descriptor                                  *
;*             IDT = IDT Descriptor                                  *
;*              TR = TSS Descriptor                                  *
;*              GP = General purpose registers are perserved         *
;*      AF, DF, IF = Arithmetic, Direction, Int Flags Unchanged      *
;*             NMI = NMI is unaffected                               *
;*            IOPL = Set in Flags register                           *
;* EXIT-ERROR:                                                       *
;*    RETURN CODE:   None, ProtMode handles all errors               *
;*                                                                   *
;* EFFECTS:     See Exit_Normal, Processor is placed in Protect Mode *
;*                                                                   *
;* INTERNAL REFERENCES:                                              *
;*    ROUTINES:            ProtMode                                  *
;*                                                                   *
;* EXTERNAL REFERENCES:                                              *
;*    ROUTINES:                                                      *
;*             SegMapVirt - Map segment to Virtual Address           *
;*    DATA Area:                                                     *
;*             RmSelLst - Array of GDT selectors.                    *
;*                        Entries are addressed by RSL_SelDS and     *
;*                        RSL_SelES                                  *
;*********************** END OF SPECIFICATIONS ***********************

    assume ds:nothing, es:nothing, ss:TASKAREA

Procedure GoProtAll,hybrid              ; CALLED ONLY FROM DOSCODE!!
;       RMONLY

        push    ax
        push    bx
        push    cx
        push    dx
        push    si

        push    es                      ; save for mapping later
        push    ds

        call    ProtMode

;       Protmode translated SS
;       CS is tiled
;       We must map DS, ES

        DosContext DS
        mov     bx,[RSL_SelDS]          ; (bx) = DS's alias selector
        pop     ax                      ; (ax) = segment for DS
        push    ds
        xor     cx,cx
        CALLFAR SegMapVirt              ; SegMapVirt(ax=seg,bx=sel,cx=size)
        pop     ds

        mov     bx,[RSL_SelES]          ; (bx) = ES's alias selector
        pop     ax                      ; (ax) = segment for ES
        push    ds
        xor     cx,cx
        CALLFAR SegMapVirt              ; SegMapVirt(ax=seg,bx=sel,cx=size)
        pop     ds

        mov     es,[RSL_SelES]          ; (es) = alias selector
        mov     ds,[RSL_SelDS]          ; (ds) = alias selector

        pop     si
        pop     dx
        pop     cx
        pop     bx
        pop     ax

        ret

EndProc   GoProtAll

BREAK

;********************** START OF SPECIFICATIONS **********************
;* SUBROUTINE NAME:  GoRealAll                                       *
;*                                                                   *
;* DESCRIPTIVE NAME: Switch from Protect to Real Mode for sysinit    *
;*                   and invalidate DS and ES descriptors            *
;* FUNCTION:                                                         *
;*         This routine performs the necessary operations required   *
;*         to switch the processor from Protect to Real mode.        *
;*         This is used to support system initialization.            *
;*         GoReal unmaps CS and SS only.                             *
;* NOTES:                                                            *
;*         GoRealAll assumes the GoProtAll was used to enter         *
;*         protect mode and that the alias selectors RSL_SelDS       *
;*         and RSL_SelES have been defined  GoRealAll will           *
;*         invalidate the descriptors for these selectors.           *
;*                                                                   *
;*         Interrupts must be off upon Entry                         *
;*                                                                   *
;* ENTRY POINT: GoRealAll                                            *
;*    LINKAGE:  NEAR                                                 *
;*                                                                   *
;* INPUT:       None                                                 *
;* EXIT-NORMAL:                                                      *
;*              CS = Code Segment (CS is tiled)                      *
;*              DS = Screen Memory (B800) for debugging purposes.    *
;*                   This will help in debugging routines who use    *
;*                   RealMode but forget to reset DS                 *
;*              ES = Same as DS                                      *
;*              SS = Mapped from a Selector to Segment               *
;*              GP = General purpose registers are perserved         *
;*             NMI = NMI is enabled                                  *
;*      AF, DF, IF = Arithmetic, Direction, Int Flags Unchanged      *
;* EXIT-ERROR:                                                       *
;*    RETURN CODE:   None, RealMode procedure handles all errors     *
;*                                                                   *
;* EFFECTS:     See Exit_Normal                                      *
;*              Processor is placed in Real Mode                     *
;*              Invalidates two descriptors referenced by RSL_SelDS  *
;*              and RSL_SelES                                        *
;*                                                                   *
;* INTERNAL REFERENCES:                                              *
;*    ROUTINES:                                                      *
;*               RealMode                                            *
;*               RmSelLst - Array of GDT Selectors.  This uses the   *
;*               entries addressed by RSL_SelDS and RSL_SelES        *
;*                                                                   *
;* EXTERNAL REFERENCES:                                              *
;*    ROUTINES:                                                      *
;*                     DescInvalidate                                *
;*********************** END OF SPECIFICATIONS ***********************

    assume ds:nothing, es:nothing, ss:TASKAREA

Procedure GoRealAll,hybrid              ; CALLED ONLY FROM DOSCODE!!
;       PMONLY

;       Realmode will set SS segment from SS selector on entry
;       CS is tiled
;       We must invalidate the descriptors being used with DS, ES

        push    ax
        push    bx
        push    dx
        push    si
        pushf

        DosContext DS
        mov     ax,[RSL_SelDS]          ; (ax) = alias selector for DS
        push    ds
        CALLFAR DescInvalidate
        pop     ds
        mov     ax,[RSL_SelES]          ; (bx) = alias selector for ES
        CALLFAR DescInvalidate

        popf
        pop     si
        pop     dx
        pop     bx
        pop     ax

        call    RealMode

        ret

EndProc   GoRealAll

BREAK

;***    Reboot - force a warm reboot
;
;       Reboot is used to restore the state of the machine as much as possible.
;       This is typically accomplished via a keyboard command.  This code is
;       lifted from the keyboard device driver.
;
;       ENTRY   none
;       EXIT    never
;       USES    You can't care

Procedure   Reboot,far

; this code has been moved to bios/dhmodesw.asm dhreboot

        DosContext      ds
        cli
        call    DHF.DosHlpReboot        ; never returns

EndProc Reboot


CODE    ends

        FARCODE INIT
INITCODE segment
    assume ds:nothing,es:nothing,ss:nothing

;***    RealModeIC - Switch CPU to real mode
;***    ProtModeIC - Switch CPU to protected mode
;
;       By the Ghost Coder !!!!!!!!!!!!!!!!!!!!!
;
;       These mode-switching calls support non-tiled DOSCODE.
;
;       NOTE: These calls must be paired: A call to RealModeIC must be
;             followed (eventually) by a call to ProtModeIC. (See the logic
;             that saves and restore the SS registers in these routines!)
;       NOTE: The state of the interrupt flag is not affected by either of
;             these calls.

DATA segment
SSSelSave  dw      0
SSSegSave  dw      0
DATA ends

;       RealModeIC
;
;       entry:  CS, DS, ES, SS = selectors
;       exit:   CS, DS, ES, SS = paragraph numbers
;               SSSelSave = SS selector
;               SSSegSave = SS segment

assume  ds:NOTHING,es:NOTHING,ss:NOTHING

Procedure RealModeIC,near       ;DOSCODE only!
;       PMONLY

        SaveReg         <ax,bx>                 ; save registers
        mov             bx, ds                  ; bx = DS:selector
        CALLFAR         SelToSeg                ; ax = DS:segment
        jnc             rmi20                   ; C => DS above 1M
        mov             ax, ScrnMemSeg          ; ax = 0B800h

rmi20:  SaveReg         <ax>                    ; push DS:segment
        mov             bx, es                  ; bx = ES:selector
        CALLFAR         SelToSeg                ; ax = ES:segment
        jnc             rmi30                   ; C => ES above 1M
        mov             ax, ScrnMemSeg          ; ax = 0B800h

rmi30:  SaveReg         ax                      ; push ES:segment
        DosContext      <ds>                    ; ds = DOSGROUP
        cmp             SSSelSave, 0            ; check for proper call sequence
        jz              rmi40                   ; Z => ok
        InternalError   <RealModeIC: calling sequence violated>
        int             3                       ; in case internal error fails

rmi40:  mov             bx, ss                  ; bx = SS:selector
        CALLFAR         SelToSeg                ; ax = SS:segment
        jnc             rmi50                   ; C => SS above 1M
        InternalError   <RealModeIC: stack segment above 1M>
        int             3                       ; in case internal error fails

rmi50:  mov             SSSegSave, ax           ; save SS:segment
        mov             SSSelSave, ss           ; save SS:selector
        mov             bx, cs                  ; bx = CS:selector
        CALLFAR         SelToSeg                ; ax = CS:segment
        jnc             rmi60                   ; C => CS above 1M
        InternalError   <RealModeIC: code segment above 1M>
        int             3                       ; in case internal error fails

rmi60:  push            ax                      ; push CS:segment
        push            offset DOSCODE:rmi70    ; push IP
        JMPFAR          RealMode                ; simulated far call

rmi70:  RestoreReg      <es,ds,bx,ax>           ; restore registers
        ret
EndProc RealModeIC

;       ProtModeIC
;
;       entry:  CS = paragraph numbers
;               SSSelSave = SS selector (from RealModeIC)
;               SSSegSave = SS segment (from RealModeIC)
;       exit:   CS = selector for DOSCODE
;               SS = saved SS selector (from RealModeIC)
;               DS, ES = 0

Procedure ProtModeIC,near               ; DOSCODE only!
;       RMONLY

        SaveReg         <ax>                    ; save registers
        push            DOSCODE         ; push CS:selector
        push            offset DOSCODE:pmi20    ; push IP
        JMPFAR          ProtModeICSub           ; simulate far call

pmi20:  DosContext      <ds>                    ; ds = DOSGROUP
        cmp             SSSelSave, 0            ; check for proper call sequence
        jnz             pmi80                   ; NZ => ok
        InternalError   <ProtModeIC: calling sequence violated>
        int             3                       ; in case internal error fails

pmi80:
        mov             ax, ss                  ; ax = SS:segment
        cmp             ax, SSSelSave           ; check that it's what we saved
        jeq             pmi90                   ; EQ => ok
        InternalError   <ProtModeIC: stack has been moved>
        int             3                       ; in case internal error fails

pmi90:
;;   Stack reload moved to ProtModSub
;;      mov             ss, SSSelSave           ; ax = SS:selector
        xor             ax, ax
        mov             es, ax                  ; zero out ES
        mov             SSSelSave, ax           ; zero for call sequence check
        mov             ds, ax                  ; zero out DS
        mov             fs, ax                  ; zero out FS
        mov             gs, ax                  ; zero out GS
        assume          ds:nothing,es:nothing,fs:nothing,gs:nothing
        RestoreReg      <ax>                    ; restore registers
        ret
EndProc ProtModeIC


INITCODE ends
        END
