.386p
page    58,132
;******************************************************************************
        title   WINEMM
;******************************************************************************
;
;   (C) Copyright MICROSOFT Corp. 1989-1991
;   (C) Copyright COMPAQ Computer Corp. 1989-1991
;
;   Title:    EMM386.EXE - MICROSOFT Expanded Memory Manager 386 Driver
;
;   Module:   WINEMM - Routines to fill in data structure to pass to win386
;
;   Version:  0.001
;
;   Date:     July 26,1989
;
;   Author:   Harish K. Naidu
;             Leo Cohen
;
;******************************************************************************
;
;   Change log:
;
;     DATE    REVISION                  DESCRIPTION
;   --------  --------  -------------------------------------------------------
;   07/26/89  Original
;   08/11/89  4.10      Add WinInit & updated Windows/CEMM interface.   (LC)
;   07/31/90  4.20      Win 3.1 and UMB support.                        (LC)
;
;   02/14/91  M010      Added support to validate LoadHi Vxd path at win
;                       startup int 2f.
;
;   02/27/91  M013      First Check if standard mode has issued the int 2f
;                       startup in rint2fhHandler. If so just chain.
;
;   03/07/91  M015      Clear out the high word of edx when initializing
;                       dx with the arena length in VxdUsedUMBList.
;
;   03/03/93  ELM716    Assume for data segment ds:nothing.  Merged from MS
;                         Beta drop.
;******************************************************************************
;
;   Functional Description:
;
;
;******************************************************************************
;=============================================================================
;==     P U B L I C   D E C L A R A T I O N S
;=============================================================================
        public  pWinEMMGlobImpDisp
        public  rINT2FhHandler
        public  GetPathName
        public  StorePath               ; M010
        public  VxDInit

        public  EMMGIDataVer
        public  CEMMpath
        public  InstanceData
        public  Win386VxDRefDat

;=============================================================================
;==     L O C A L   C O N S T A N T S / E Q U A T E S
;=============================================================================
        include VDMseg.inc
        include winemm.inc
        include emmfunct.inc
        include emmdata.inc
        include vdmsel.inc
        include emm386.inc
        include vm386.inc
        include XMM.inc
        include driver.equ
ifdef BETA
        include winemm.pub
        include dbg.inc
endif
;=============================================================================
;==     E X T E R N A L  D E C L A R A T I O N S
;=============================================================================
_DATA   SEGMENT
        extrn   EMS_window_location:word
        extrn   register_set:word
        extrn   number_EMS_windows:word
        extrn   context_save_area_size:word
        extrn   HMAfree:byte
        extrn   _PFUser:word
_DATA   ENDS

R_CODE  segment
        extrn   GoVirtual:near
        extrn   ChkA20:near
        extrn   GoProtMode:near
        extrn   GoVirtualMode:near
        extrn   GoRealMode:near
        extrn   EMM_rEntry:near
        extrn   Devname:byte
;       extrn   WINEMM_Mess:byte
ifdef DEBUG
        extrn   ReInitDeb:near
endif
        extrn   CopyInstData:dword
        extrn   ValidPathFar:dword
        extrn   EMM_rFarEntry:word

R_CODE  ends

R1_CODE segment
        extrn   WinBackfillMess:byte
        extrn   WinInvPathMess:byte     ; M010
R1_CODE ends


_TEXT   SEGMENT
        extrn   MapHandlePage:near
        extrn   EnterVirtual:far
        extrn   Log2Phy:near
        extrn   Lin2Phy:near
_TEXT   ENDS

LAST    SEGMENT
        extrn   tempBuffer:dword
LAST    ENDS
        extrn   MajVer:abs
        extrn   MinVer1:abs
        extrn   MinVer2:abs
;=============================================================================
;==     L O C A L   D A T A
;=============================================================================
R_CODE  segment

WinVer  label   word            ; Version of Windows V86MMGR
WinVerMin       db      0       ; Minor version
WinVerMaj       db      0       ; Major version

EMMGIDataVer label word
EMMGIDataVerMin db      0       ; Minor version
EMMGIDataVerMaj db      1       ; Major version

; VCB - Move EMM Global Import product data to _DATA segment
;ifndef MSFLAG
;EMMGIVendor     db      "COMPAQ              "
;EMMGIProduct    db      "CEMM ",MajVer+'0',".",MinVer1+'0',MinVer2+'0',"           "
;else
;EMMGIVendor     db      "IBM                 "                      ;IBMDOS60;
;EMMGIProduct    db      "EMM386 ",MajVer+'0',".",MinVer1+'0',MinVer2+'0',"         "
;endif

pRDSdata        dw      0

R_CODE  ends

R1_CODE segment


ConfigBuffer    equ     128     ; up to 128 character path will be kept
Win386Struc     Win386StartupStruc <>

MaxUMBInstObj   equ     10      ; up to 10 instance objects in UMB area supported
InstanceData    label   byte
rept MaxUMBInstObj
        InstanceDataStruct<>    ; possible XBDA/UMB instance data
endm
        dd      0       ; termination of structure array

CEMMpath        db      ConfigBuffer dup (0)

R1_CODE ends

_DATA   segment

Win386VxDRefDat VxDRefDataStruct<>

; VCB - Move EMM Global Import product data to _DATA segment
ifndef MSFLAG
EMMGIVendor     db      "COMPAQ              "
EMMGIProduct    db      "CEMM ",MajVer+'0',".",MinVer1+'0',MinVer2+'0',"           "
else
EMMGIVendor     db      "IBM                 "                      ;IBMDOS60;
EMMGIProduct    db      "EMM386 ",MajVer+'0',".",MinVer1+'0',MinVer2+'0',"         "
endif

_DATA   ends

;=============================================================================
;==     C O D E  S E G M E N T
;=============================================================================
_TEXT   segment
        assume  cs:_TEXT,ds:nothing,es:nothing,fs:nothing,gs:nothing
;==============================================================================
;==  pWinEMMGItable jump table
;==============================================================================
pWinEMMGItable  label   word
        dw      offset UpdateStructure
        dw      offset UpdateInternals
        dw      offset WinBroadcast
pWEGImax        equ     $-pWinEMMGItable
;==============================================================================
;==
;==  pWinEMMGlobImpDisp: This is a dispatcher for the routine which update
;==                      the EMM Global Import data structure during a Windows'
;==                      Virtual Disable Call and gets info from the structure
;==                      during a Windows Enable Call.
;==
;==  Entry: (Protected Mode)
;==     AX      = 0     Windows Disable Call
;==             = 1     Windows Enable Call
;==             = 2     Windows Broadcast
;==     GS      = R_CODE segment
;==
;==  Exit:
;==     [ebp][VTFO].VMTF_EFLAGS carry flag set if error, else zero
;==
;==============================================================================
pWinEMMGlobImpDisp proc near
        push    eax

        shl     eax,24
        mov     ax,VDMD_GSEL    ; DS/GS are setup for CEMM's 2 data areas.
        mov     ds,ax
        mov     ax,DATA32_GSEL  ; access 4GB address space
        mov     fs,ax
        assume  ds:_DATA,es:nothing,fs:ABS0,gs:R_CODE

        shr     eax,24
        call    cs:[pWinEMMGItable][eax*2]      ; call function

        pop     eax
        ret
pWinEMMGlobImpDisp      endp

;==============================================================================
;==
;==  UpdateStructure: This routine updates the EMM Global Import data structure
;==                   during a Windows' Virtual Disable Call.
;==
;==  Entry: (Protected Mode)
;==     AX      = 0   Windows Disable Call
;==     DS      = _DATA
;==     FS      = DATA32_GSEL
;==     GS      = R_CODE
;==
;==  Exit:
;==     [ebp][VTFO].VMTF_EFLAGS carry flag set if error, else zero
;==
;==============================================================================
UpdateStructure proc    near
        push    ebx
        push    esi
;
;  Get FS:[EBX] pointing to EMM Global Import Data Structure
;
        call    GetEMMGlobImpDatArea

        mov     fs:[ebx].EMMGI_Len,EMMGIDS100size  ; assume 1.00 version size

        call    Fill_Flags
        call    Fill_EMMCtxt
        call    Fill_HandleInfo

;
;  If version 1.11 or later continue, else end of structure
;
        cmp     word ptr fs:[ebx][EMMGI_Vers],EMMGI_VERSION_111 ;Q: Atleast 1.10?
        jb      short USexit                                    ; N: exit
        mov     fs:[ebx].EMMGI_Len,EMMGIDS110size               ; Y: size as 1.10 version

        call    FillFreeMem
        call    FillXMS
        call    FillFreeUMBs

        call    FillProductName

;
;  ESI contains the size of the variable portion of the Data Structure
;
USexit:
        add     fs:[ebx].EMMGI_Len,si   ; add variable portion of data structure
        pop     esi
        pop     ebx
        ret
UpdateStructure endp

;==============================================================================
;==
;==  UpdateInternals: This routine updates CEMM's internals using the  EMM
;==                   Global Import data structure during a Windows' Virtual
;==                   Enable Call.
;==
;==  Entry: (Protected Mode)
;==     AX      = 1   Windows Enable Call
;==     DS      = _DATA
;==     FS      = DATA32_GSEL
;==     GS      = R_CODE
;==
;==  Exit:
;==     [ebp][VTFO].VMTF_EFLAGS carry flag set if error, else zero
;==
;==============================================================================
UpdateInternalsFar proc far
        push    fs

        mov     ax,DATA32_GSEL
        mov     fs,ax

        call    UpdateInternals

        pop     fs
        ret
UpdateInternalsFar      endp

UpdateInternals proc    near
        push    ebx
;
;  Get FS:[EBX] pointing to EMM Global Import Data Structure
;
        call    GetEMMGlobImpDatArea
;
;  Update current EMS mapping
;
        call    Update_Mapping_State

        pop     ebx
        ret
UpdateInternals endp

;==============================================================================
;==
;==  WinBroadcast: Windows INT 2Fh AX=1605 broadcast.  Need to sync up the
;==                version numbers and fill the VxD information for Win 3.0
;==                and UMB information.
;==
;==  Entry: (Protected Mode)
;==     AX      = 2   Windows Broadcast Call
;==     DS      = _DATA
;==     FS      = DATA32_GSEL
;==     GS      = R_CODE
;==
;==  Exit:
;==     [ebp][VTFO].VMTF_EFLAGS carry flag set if error, else zero
;==
;==============================================================================
WinBroadcast    proc    near
        push    eax
;
;  Check if Windows Init is still OK
;
        mov     ax,[bp][VTFO].VMTF_DS

        or      cx,cx                   ;Q: Anybody fail Windows Init?
        jnz     short WBerror           ; Y: fail Win Init

        or      ax,si                   ;Q: Is a Virtual Disable Call set?
        jnz     short WBerror           ; Y: impossible/error - fail init

        mov     gs:[WinVer],di          ; save Windows version
        mov     gs:[EMMGIDataVer],EMMGI_VERSION_HI ; most recent version supported
        cmp     di,WIN_VERSION_HI       ;Q: Version supported?
        jae     short WBSetEntry        ; Y: continue
;
;  As new Windows versions get added and supported, need to place the most
;  recent EMM Global Import Data Structure supported in [EMMGIDataVer]
;
        cmp     di,WIN_VERSION_LO       ;Q: Version supported?
        jb      short WBerror           ; N: cannot do interface
        mov     gs:[EMMGIDataVer],EMMGI_VERSION_LO ; assume CEMM can handle latest version
        or      gs:[GenFlags],fWin30    ; Win 3.0 broadcast occurred
;
;  Add VxD information to Windows Version 3.00 (called only for this version!)
;
        call    VxDSupport
;
;  Set Virtual Disable Call entry point
;
WBSetEntry:
        mov     si,seg R_CODE
        mov     [bp][VTFO].VMTF_DS,si
        mov     si,offset rWinV86Proc
WBexit:
        pop     eax
        ret

WBerror:
        mov     cx,-1                   ; fail Windows Init
        jmp     short WBexit
WinBroadcast    endp

;==============================================================================
;==
;==  GetEMMGlobImpDatArea: Gets pointer to start of EMM Global Import Data
;==                        structure.  Also, clears the page directory entries
;==                        which correspond to the Page Fault Tables.  The Page
;==                        Fault Tables will not be used while Windows is in
;==                        control.
;==
;==  Entry: (Protected Mode)
;==     DS      = _DATA
;==     FS      = DATA32_GSEL
;==     GS      = R_CODE
;==
;==  Exit:  (Page Fault Handler Reset)
;==
;==     FS:[EBX]= pointer to start of EMM Global Import Data Structure
;==
;==============================================================================
GetEMMGlobImpDatArea    proc    near
        push    eax
        push    ecx
;
;  Get FS:[EBX] pointing to the Page Directory and clear Page Fault Table entries
;
        mov     ebx,[page_directory]
        mov     ecx,2                   ; two page fault tables
GEGIDAloop:
        movzx   eax,[_PFUser][ecx*2-2]
        or      ax,ax                   ;Q: Is Page Fault Table being used?
        jz      short GEGIDAnext        ; N: skip it
        mov     dword ptr fs:[ebx][eax],0;Y: reset it
GEGIDAnext:
        loop    GEGIDAloop
;
;  Get FS:[EBX] pointing to EMM Global Import Data Structure
;
        add     ebx,PAGE_SIZE           ; address for PF0
        pop     ecx
        pop     eax
        ret
GetEMMGlobImpDatArea    endp

;############################################################################
;
;       Procedure name          : Fill_Flags
;
;       ENTRY                   : Protected mode
;                                 DS->_DATA
;                                 FS->DATA32_GSEL
;                                 GS->R_CODE
;
;       EXIT                    : Fills the EMMOSKey flag and fills the
;                                 EMMFlags with the regiser set info.
;
;       REGS MOD                : NONE
;
;############################################################################
Fill_Flags      proc    near
        push    eax
        push    cx
;
;  Initialize data structure
;
        mov     fs:[ebx].EMMGI_Flags,0
;
;  EMM Global Import Interface version number
;
        mov     ax,gs:[EMMGIDataVer]
        mov     word ptr fs:[ebx].EMMGI_Vers,ax
;
;  OS Key
;
        mov     eax,[OS_Key]
        mov     fs:[ebx].EMMGI_OSKey,eax

        mov     si,size RegisterSet_Struc       ; access the register set 1
        movzx   cx, BYTE PTR [total_register_sets]
        dec     cx                              ; not register set 0

chk_active_reg_set:
        cmp     register_set[si].active,TRUE      ;Q: Is register set active?
        jne     short next_reg_set                ; N: OK, continue
        or      fs:[ebx].EMMGI_Flags,NONZR_REG_SET; Y: set flag accordingly
        jmp     short fill_flags_done

next_reg_set:
        add     si,size RegisterSet_Struc
        loop    chk_active_reg_set
;
;  If version 1.10 or later set HMA flag accordingly
;
        cmp     word ptr fs:[ebx][EMMGI_Vers],EMMGI_VERSION_111 ;Q: Atleast 1.10?
        jb      short fill_flags_done                           ; N: exit

        or      fs:[ebx].EMMGI_Flags,NO_XMS_HANDLE ; EMBs not managed by CEMM

        cmp     gs:[UMBptr],0                   ;Q: Is CEMM managing UMBs?
        jne     short FFcont                    ; Y: continue
        or      fs:[ebx].EMMGI_Flags,NO_XMS_UMB ; N: UMBs not managed by CEMM
FFcont:
        cmp     gs:[HMAptr],100000h             ;Q: Virtual HMA?
        je      short fill_flags_done           ; N: exit

        cmp     [HMAfree],TRUE                  ;Q: Is virtual HMA free?
        jne     short fill_flags_done           ; N: exit
        or      fs:[ebx].EMMGI_Flags,HMA_INFO   ; Y: allow windows to use it

fill_flags_done:
        pop     cx
        pop     eax
        ret
Fill_Flags      endp

;########################################################################
;
;       Procedure name  : Fill_EmmCtxt
;
;       ENTRY   :       Protected mode
;                       DS -> _DATA
;                       FS->DATA32_GSEL
;                       GS->R_CODE
;
;       EXIT    :       EMMCtxt array initialized with current values.
;
;       Regs Mod:       NONE
;
;       written :       7/26/89 HKN
;
;##########################################################################
Fill_EmmCtxt    proc    near
        push    ebp
        push    eax
        push    ecx
        push    edx
        push    edi
        push    es

        mov     dx,PAGET_GSEL
        mov     es,dx
;
;  Fill in the context save map size for CEMM and initialize UMB count
;
        mov     ax,[context_save_area_size]
        mov     fs:[ebx].EMMGI_CntxtSz,al
        mov     fs:[ebx].EMMGI_UMBcnt,0
;
;  Loop through first megabyte looking for EMS/UMBs
;
        xor     esi,esi
        xor     edi,edi
        xor     edx,edx
next_16K_page:
;
;  Initialize context
;
        mov     fs:[ebx][EMMGI_Cntxt][edi].EMMCntx_Flags,0
        mov     fs:[ebx][EMMGI_Cntxt][edi].EMMCntx_HMap,-1
        mov     fs:[ebx][EMMGI_Cntxt][edi].EMMCntx_Lpag,-1
        mov     fs:[ebx][EMMGI_Cntxt][edi].EMMCntx_PPag,-1
        mov     fs:[ebx][EMMGI_Cntxt][edi].EMMCntx_EFlgs,0
;
;  Get physical EMS window number for current 16K context
;
        call    Get_Phys_PNum

        cmp     eax, -1         ;Q: Valid physical page number?
        je      short get_UMBs  ; N: check for UMBs
        call    GetEMSInfo      ; Y: get EMS information

ifdef QEMS
;
;  Index into the EMS_window array to obtain handle and the logical page to
;  which this physical page belongs. Note that the logical page number in the
;  EMS_window array refers 4K pages and hence has to be divided by 4 to get
;  the 16K logical page #
;
;  eax is the physical page number. For each physical page number there are
;  3 bytes in the EMS_window array. The current EMS_window array is pointed
;  to by the EMS_window_ptr. Therefore [EMS_window_ptr]+eax*3 will access
;  the appropriate entry
;
        mov     ebp, eax
        shl     ebp, 1
        add     ebp, eax                ; ebp = eax * 3
        add     ebp, [EMS_window_ptr]   ; ebp = offset in EMS_window

;AR     mov     cl,EMS_window[eax+eax*2].handle ; handle is only 8 bits
        mov     cl,ds:[ebp].handle      ; handle is only 8 bits

        mov     fs:[ebx][EMMGI_Cntxt][edi].EMMCntx_HMap,cl

;AR     mov     cx,EMS_window[eax+eax*2].logical_4k_page
        mov     cx,ds:[ebp].logical_4k_page

        shr     cx,2
        mov     fs:[ebx][EMMGI_Cntxt][edi].EMMCntx_Lpag,cx

;
; fill in physical page number
;
        mov     fs:[ebx][EMMGI_Cntxt][edi].EMMCntx_PPag,al
;
; indicate that it is an EMM page
;
        mov     fs:[ebx][EMMGI_Cntxt][edi].EMMCntx_Flags,EMM_PAGE
        cmp     dx,8000h                ;Q: Possible LIM 3.2 page frame?
        jbe     short get_next          ; N: too low (no page frame)
        cmp     ax,4
        jae     short get_next
        or      fs:[ebx][EMMGI_Cntxt][edi].EMMCntx_Flags,FRAME_32
endif

        jmp     short get_next

get_UMBs:
        cmp     word ptr fs:[ebx][EMMGI_Vers],EMMGI_VERSION_111 ;Q: Atleast 1.10?
        jb      short get_next                                  ; N: exit

        cmp     gs:[UMBptr],0                           ;Q: UMBs on the system?
        jb      short get_next                          ; N: exit
;
;  Need to get UMB information
;
        call    GetUMBInfo

get_next:
        add     edi,size EMMContext
        add     dx, 0400h
        jnc     next_16K_page

        pop     es
        pop     edi
        pop     edx
        pop     ecx
        pop     eax
        pop     ebp
        ret
Fill_EMMCtxt    endp

;############################################################################
;
;       Procedure name  :       Fill_HandleInfo
;
;       ENTRY           :       Protected mode
;                               DS -> _DATA
;                               FS->DATA32_GSEL
;                               GS->R_CODE
;                               ESI= total size of variable sized entries in structure
;
;       EXIT            :       EMM_HandleInfo array initialized
;
;       REGs MOD        :       NONE
;
;       written         :       7/27/89 HKN
;
;############################################################################
Fill_HandleInfo         proc    near
        push    eax
        push    edx
        push    edi
        push    ebp
        push    cx
        push    es

        push    esi                             ; access to HndlCnt field

        mov     ax,PAGED_GSEL
        mov     es,ax

        mov     fs:[ebx][esi].EMMGI_HndlCnt,0   ;(initialize)

        cmp     word ptr fs:[ebx][EMMGI_Vers],EMMGI_VERSION_111
                                                ; Q: Atleast 1.10?
        jb      short FH_hndl0                  ; N: we cannot pass a null
                                                ;    map as win 3.0 can't
                                                ;    handle it.

;;      cmp     gs:[NoEMSset],TRUE              ; Q: Is NoEMS mode active?
;;      je      FH_exit                         ; Y: no handle info

        cmp     gs:[VCPIset], -1                ; Q: has noems been specifed
        jne     FH_exit                         ; Y: no handle info

FH_hndl0:
;
;  Fill out the handle array
;
        mov     edi,[hndl_tbl_ptr]
        mov     ebp,[hndl_nam_ptr]
        mov     cx,[total_handles]
        xor     edx,edx                 ; clear handle index
get_handle_info:
        cmp     ds:[edi][edx*4].base_PTE_index,FREE;Q: Is handle allocated
        je      next_handle                        ; N: process next handle

        pop     eax                                ; Y: restore original offset
        inc     fs:[ebx][eax].EMMGI_HndlCnt        ; increment handle array
        push    eax

        mov     fs:[ebx][EMMGI_Hndls][esi].HandleNum,dl ; handle number
;
;  Initialize Handle structure
;
        mov     fs:[ebx][EMMGI_Hndls][esi].HandleFlags,0
        mov     dword ptr fs:[ebx][EMMGI_Hndls][esi].HandleName,0
        mov     dword ptr fs:[ebx][EMMGI_Hndls][esi].HandleName[4],0
;
;  Get the # of logical pages alloc'd to this handle. Note that the number_PTEs
;  field gives the # of 4K logical pages, so divide by 4.
;
        mov     ax,ds:[edi][edx*4].number_PTEs
        shr     ax,2
        mov     fs:[ebx][EMMGI_Hndls][esi].NumLogPages,ax
;
;  Get Physical address of PTEs. The base_PTE_index field in the handle table
;  points to the required handle space. The selector for this is PAGED_GSEL.
;
        movzx   eax,ds:[edi][edx*4].base_PTE_index
        shl     eax,2                           ; make an offset
        call    Log2Phy                         ; physical address for ES:EAX
        mov     fs:[ebx][EMMGI_Hndls][esi].PhysPtrToPTEs,eax
;
;  Check if handle has a name
;                                       ; check if handle is named
        mov     eax,ds:[ebp][edx*8]
        or      eax,eax                 ;Q: Name?
        jnz     short named_hndl        ; Y: get name
        mov     eax,ds:[ebp][edx*8][4]  ; N: check second dword
        or      eax,eax                 ;Q: Name?
        jz      short chk_context       ; N: check if it has a context
;
;  Get/Save name of handle
;
named_hndl:
        mov     eax,ds:[ebp][edx*8]
        mov     dword ptr fs:[ebx][EMMGI_Hndls][esi].HandleName,eax
        mov     eax,ds:[ebp][edx*8][4]
        mov     dword ptr fs:[ebx][EMMGI_Hndls][esi].HandleName[4],eax
        or      fs:[ebx][EMMGI_Hndls][esi].HandleFlags,HANDLE_HAS_NAME
;
;  Check if handle has a context
;
chk_context:
        cmp     byte ptr save_flag[edx],0                ;Q: Saved context?
        jne     short proc_next_handle                   ; N: next handle
        or      fs:[ebx][EMMGI_Hndls][esi].HandleFlags,HANDLE_HAS_CTXT; Y: indicate in handle flag
        or      fs:[ebx].EMMGI_Flags,ANY_HANDLE_CTXT     ;    & also in the EMMFlags
;
;  Next handle structure
;
proc_next_handle:
        add     esi,size EMM_Handle             ; point to next EMM_Handle struc
;
;  Get next handle
;
next_handle:
        inc     dx
        dec     cx
        jnz     get_handle_info

FH_exit:
        add     sp,4                            ; restore stack
        pop     es
        pop     cx
        pop     ebp
        pop     edi
        pop     edx
        pop     eax
        ret
Fill_HandleInfo endp

;==============================================================================
;==
;==  FillFreeMem: Fill free EMS memory structures, virtual HMA info, and Int 67h
;==               entry point information into the EMM Global Import Data Structure.
;==
;==  Entry: (Protected Mode)
;==     FS:[EBX]= start of EMM Global Import Data Structure
;==     ESI     = size of variable fields in EMM Global Import Data Structure
;==     DS      = _DATA
;==     FS      = DATA32_GSEL
;==     GS      = R_CODE
;==
;==  Exit:
;==
;==============================================================================
FillFreeMem     proc    near
        push    eax
        push    ecx
        push    edx
        push    edi
        push    es
ifdef QEMS
        mov     ax,PAGET_GSEL
        mov     es,ax
endif
;
;  Fill Int 67h entry point field
;
        mov     ax,seg R_CODE
        shl     eax,16
        mov     ax,offset R_CODE:EMM_rEntry
        mov     fs:[ebx][esi].EMMGI_Int67,eax
;
;  Fill physical HMA pointer
;
        mov     fs:[ebx][esi].EMMGI_HMA,0
        mov     eax,[HMAptr]                    ; get HMA physical memory
        cmp     eax,100000h                     ;Q: Phy=Lin (Virtual HMA)?
        je      short FFMFreeMem                ; N: no virtual HMA
                                                ; Y: get PTEs for virtual HMA
        mov     eax,ds:[page_tables]
        add     eax,(100000h shr 12)*4          ; linear address of first HMA PTE
        call    Lin2Phy                         ; Y: pointer to virtual HMA PTEs
        mov     fs:[ebx][esi].EMMGI_HMA,eax
;
;  Fill free memory region structures
;
FFMFreeMem:
        mov     fs:[ebx][esi].EMMGI_FreeMEMcnt,0; initialize count to zero
        mov     edx,esi
        xor     edi,edi
        mov     di,FIRST_HANDLE_PTE
FFMloop:
        call    GetFreeEMSRange
        cmp     eax,-1                  ;Q: Free EMS range found?
        je      short FFMexit           ; N: done, exit

        inc     fs:[ebx][edx].EMMGI_FreeMEMcnt
        mov     fs:[ebx][EMMGI_FreeMEM][esi].MEMstart,eax
        mov     fs:[ebx][EMMGI_FreeMEM][esi].MEMcount,ecx

        add     si,size MEM_FREE_Map
        add     eax,ecx                 ; search for next free range
        jmp     short FFMloop

FFMexit:
        pop     es
        pop     edi
        pop     edx
        pop     ecx
        pop     eax
        ret
FillFreeMem     endp

;==============================================================================
;==
;==  FillXMS: Fill XMS information into the EMM Global Import Data Structure.
;==
;==  Entry: (Protected Mode)
;==     FS:[EBX]= start of EMM Global Import Data Structure
;==     ESI     = size of variable fields in EMM Global Import Data Structure
;==     DS      = _DATA
;==     FS      = DATA32_GSEL
;==     GS      = R_CODE
;==
;==  Exit:
;==
;==============================================================================
FillXMS proc    near
;
;  No XMS EMB management is done in CEMM
;
        mov     fs:[ebx][esi].EMMGI_XMScnt,0

        ret
FillXMS endp

;==============================================================================
;==
;==  FillFreeUMBs: Fill free UMB memory structures in the EMM Global Import
;==                Data Structure.
;==
;==  Entry: (Protected Mode)
;==     FS:[EBX]= start of EMM Global Import Data Structure
;==     ESI     = size of variable fields in EMM Global Import Data Structure
;==     DS      = _DATA
;==     FS      = DATA32_GSEL
;==     GS      = R_CODE
;==
;==  Exit:
;==
;==============================================================================
FillFreeUMBs    proc    near
        push    eax
        push    ecx
        push    edi
        push    dx

;
;  Fill free UMB memory region structures
;
        mov     ecx,esi
        mov     fs:[ebx][ecx].EMMGI_FreeUMBcnt,0; initialize count to zero

        movzx   edi,gs:[UMBptr]
        or      di,di                           ;Q: UMBs provided?
        jz      short FFUexit                   ; N: exit
        shl     edi,4                           ; Y: offset to first ARENA
FFUloop:
        cmp     fs:[edi].Sig,'M'                ;Q: Is it an ARENA?
        je      short FFUlcont                  ; Y: OK
        cmp     fs:[edi].Sig,'Z'                ;Q: Is it an ARENA?
        jne     short FFUexit                   ; N: exit, corruption
;
;  Valid ARENA: Get starting paragraph and length
;
FFUlcont:
        mov     eax,edi                         ; get current paragraph
        shr     eax,4
        inc     ax                              ; AX is starting paragraph
        mov     dx,fs:[edi].Len                 ; get length

        cmp     fs:[edi].Own,0                  ;Q: Is it a FREE UMB?
        jne     short FFUnext                   ; N: get next ARENA
;
;  Free UMB encountered: place info in structure
;
        inc     fs:[ebx][ecx].EMMGI_FreeUMBcnt
        mov     fs:[ebx][EMMGI_FreeUMB][esi].UMBseg,ax
        mov     fs:[ebx][EMMGI_FreeUMB][esi].UMBsize,dx

        add     si,size UMB_FREE_Map

FFUnext:
        cmp     fs:[edi].Sig,'Z'                ;Q: Is it end of ARENA chain?
        je      short FFUexit                   ; Y: exit
        add     ax,dx                           ; N: next ARENA
        shl     eax,4
        mov     edi,eax
        jmp     short FFUloop

FFUexit:
        pop     dx
        pop     edi
        pop     ecx
        pop     eax
        ret
FillFreeUMBs    endp

;==============================================================================
;==
;==  FillProductName: Fill in the name of the vendor and the product
;==
;==  Entry: (Protected Mode)
;==     FS:[EBX]= start of EMM Global Import Data Structure
;==     ESI     = size of variable fields in EMM Global Import Data Structure
;==     DS      = _DATA
;==     FS      = DATA32_GSEL
;==     GS      = R_CODE
;==
;==  Exit:
;==
;==============================================================================
FillProductName proc    near

        push    esi
        push    edi
        push    ecx
;       push    ds
        push    es

        mov     ecx, esi

        lea     edi, fs:[ebx][ecx].EMMGI_Vendor
        push    fs
        pop     es              ; es:di -> destination


; VCB - Move EMM Global Import product data to _DATA segment
        lea     esi, ds:[EMMGIVendor]
;       lea     esi, gs:[EMMGIVendor]
;       push    gs
;       pop     ds              ; ds:si -> source string

        mov     cx, 20          ; move 40 bytes

        REP32MOVSW

        pop     es
;       pop     ds
        pop     ecx
        pop     edi
        pop     esi

        ret

FillProductName endp


;############################################################################
;
;       Procedure name          : Update_mapping_state
;
;       ENTRY                   : Protected mode
;                               : DS -> _DATA
;                                 FS->DATA32_GSEL
;                                 GS->R_CODE
;
;       EXIT                    : mapping state is updated with the info
;                                 passed by win386 in EMMCtxt array
;
;       REGs MOD                : NONE
;
;
;       written                 : 7/27/89 HKN
;
;############################################################################
Update_mapping_state    proc    near
        push    ebx
        push    esi
        push    edi
        push    ax
        push    cx
        push    dx
        push    es

        mov     di,DATA32_GSEL  ; need ES = DATA32_GSEL for MapHandlePage function
        mov     es,di
;
;  Loop through the 64 Context structures and map the EMS ones
;
        mov     edi,ebx         ; need EBX for MapHandlePage routine
        xor     esi,esi
        mov     cx,64
UMSloop:
;
; Check if this is a 16K EMS window
;
        test    fs:[edi][EMMGI_Cntxt][esi].EMMCntx_Flags,EMM_PAGE
        jz      short UMSnext   ; no - try next context structure

;;      cmp     gs:[NoEMSset],TRUE      ;Q: NoEMS mode active?
;;      je      short UMSnext           ; Y: must be an error, skip it

        cmp     gs:[VCPIset], -1        ; Q: has noems been specifed
        jne     short UMSnext           ; Y: must be an error, skip it

;
;  Get current state from this EMS context structure
;
        movzx   ax,fs:[edi][EMMGI_Cntxt][esi].EMMCntx_PPag
        mov     bx,fs:[edi][EMMGI_Cntxt][esi].EMMCntx_LPag
        movzx   dx,fs:[edi][EMMGI_Cntxt][esi].EMMCntx_HMap
;
;  We are using the MapHandlePage function to set the mapping.
;  SS:[EBP].reg_EAX must have access to the AX register.
;

;       push    ax
;       push    ebp
;       mov     bp,sp
;       sub     bp,reg_EAX-4
;       push    edi
;       push    esi

        pushad
        mov     bp, sp

        call    MapHandlePage

;       pop     esi
;       pop     edi
;       add     bp,reg_EAX-4
;       pop     ebp
;       pop     ax

        popad

;
;  Check for an error during the mapping
;
        or      ah,ah                           ;Q: Succesful mapping?
        jnz     short UMSerror                  ; N: set error flag (carry)

UMSnext:

        add     esi,size EMMContext             ; next EMMCntx entry
        loop    UMSloop
        clc

UMSexit:
        pop     es
        pop     dx
        pop     cx
        pop     ax
        pop     edi
        pop     esi
        pop     ebx
        ret

UMSerror:
        cmp     ah,INVALID_HANDLE       ;Q: Handle which didn't unmap?
        je      short UMSnext           ; Y: continue

        stc                             ; set carry (error)
        jmp     short UMSexit

Update_mapping_state    endp

;==============================================================================
;==
;==  Get_Phys_PNum
;==
;==  Entry: (Protected mode)
;==     DS = _DATA
;==     DX = segment value (0 thru A000) (16K bounded
;==     DS = _DATA
;==     FS = DATA32_GSEL
;==     GS = R_CODE
;==     EBX= global import data
;==
;==  Exit: (Protected Mode)
;==     EAX = modified
;==
;==             : if ( segment in DX is a mappable EMM Page )
;==                          &&
;==                          (
;==                            (import version >= 1.11 && noems not specified)
;==                            ||
;==                            import version < 1.11
;==                          )
;==
;==                             EAX = Physical page number
;==                       else
;==                             EAX = -1
;==
;==============================================================================
Get_Phys_Pnum   proc    near
        push    edx

        mov     eax,-1          ; assume this is not an EMS window
;
;  Because of a bug in Win 3.0, we cannot pass a null if in NoEMS mode
;

        cmp     word ptr fs:[ebx][EMMGI_Vers],EMMGI_VERSION_111 ;Q: Win 3.0?
        jb      short GPPcont           ; Y: can't pass null ems map as

        cmp     gs:[VCPIset],-1         ;Q: Has noems been specifed
        jne     short GPPexit           ; Y: assumption is correct
GPPcont:
        mov     dl,dh
        movzx   edx,dl
        movsx   eax,[EMSsegLoc][edx]

GPPexit:
        pop     edx
        ret
Get_Phys_Pnum   endp

ifdef QEMS
;###########################################################################
;
;       Procedure name  : Get_Phys_PNum
;
;       ENTRY           : Protected mode
;                       : DS -> _DATA
;                       : DX = segment value (0 thru A000) (16K bounded
;                       : DS -> _DATA
;                       : FS->DATA32_GSEL
;                       : GS->R_CODE
;                       : EBX -> global import data
;
;       EXIT            : if ( segment in DX is a mappable EMM Page )
;                            &&
;                            (
;                              (import version >= 1.11 && noems not specified)
;                              ||
;                              import version < 1.11
;                            )
;
;                               EAX = Physical page number
;                         else
;                               EAX = -1
;
;       REGs MOD        : EAX
;
;       written         : 7/26/89 HKN
;
;###########################################################################
Get_Phys_Pnum   proc    near
        push    dx
        push    cx

        cmp     word ptr fs:[ebx][EMMGI_Vers],EMMGI_VERSION_111
                                        ; Q: Atleast 1.10?
        jb      short GPPcont           ; N: can't pass null ems map as
                                        ;    win3.0 cannot handle it.

;;      cmp     gs:[NoEMSset],TRUE      ; Q: Is NoEMS mode active?
;;      je      short GPPnotFound       ; Y: don't check for EMS

        cmp     gs:[VCPIset], -1                ; Q: has noems been specifed
        jne     short GPPnotFound       ; Y: don't check for EMS

GPPcont:
        shr     dx,8                    ; convert segment in dx to PTE offset
        mov     cx,[number_EMS_windows]
        xor     eax,eax

next_window:
        cmp     dx,EMS_window_location[eax*2]
        je      short got_phys_page
        inc     eax
        loop    next_window

GPPnotFound:
        mov     eax, -1

got_phys_page:
        pop     cx
        pop     dx
        ret
Get_Phys_Pnum   endp
endif
even
;==============================================================================
;==
;==  GetEMSInfo: Get handle and the logical page to which this physical page
;==              belongs.
;==
;==  Entry: (Protected Mode)
;==     EAX     = index for EMS windows
;==     EDX     = segment address for 16K block size
;==     FS:[EBX]= start of EMM Global Import Data Structure
;==     ESI     = offset into current UMB structure
;==     EDI     = offset into current CONTEXT structure
;==     DS      = _DATA
;==     ES      = PAGET_GSEL
;==     FS      = DATA32_GSEL
;==     GS      = R_CODE
;==
;==  Exit:
;==
;==============================================================================
GetEMSinfo proc near
        push    edx
        push    esi
        push    es

        push    ebx
;
;  Fill in physical page number
;
        mov     fs:[ebx][EMMGI_Cntxt][edi].EMMCntx_PPag,al

;
;  Indicate that it is an EMM page
;
        mov     fs:[ebx][EMMGI_Cntxt][edi].EMMCntx_Flags,EMM_PAGE

        cmp     dx,8000h                ;Q: Possible LIM 3.2 page frame?
        jbe     short GEiCont           ; N: too low (no page frame)

        cmp     al,4                    ;Q: Is it page frame?
        jae     short GEiCont           ; N: must be windows 0.3

        or      fs:[ebx][EMMGI_Cntxt][edi].EMMCntx_Flags,FRAME_32
GEiCont:
;
;  Setup for searching through Handles
;
        movzx   esi,dx
        shr     si,8
        mov     eax,es:[esi*4]  ; get page currently mapped at this window
        shr     eax,12          ; clear control bits

        mov     si,PAGED_GSEL   ; access handle space
        mov     es,si

        mov     cx,[total_handles]      ; number of handles to loop through
GEiLoop:
;
;  Search each handle for ownership of this page
;
        dec     cx                      ; handle index
        movzx   esi,cx                  ; handle index
        shl     si,2
        add     esi,[hndl_tbl_ptr]      ; (EDI) = pointer to handle structure
        .errnz  (SIZE HandleTable_struc-4)

        movzx   edx,ds:[si].number_PTEs     ; get number of PTEs
        movzx   esi,ds:[si].base_PTE_index  ; get base_PTE_index

        cmp     si,FREE                     ;Q: Handle in use?
        je      short GEiNextHandle         ; N: next handle

        shr     dx,2                    ; number of EMS pages to this handle
        shl     esi,2                   ; starting offset for handle space

        or      dx,dx                   ;Q: Does this handle have pages?
        jz      short GEiNextHandle     ; N: it can't be the one we're looking for
GEiFindPage:
        dec     dx
        shl     dx,2                    ; adjust to 4K page number
        mov     ebx,es:[esi][edx*4]     ; get logical page
        shr     dx,2                    ; re-adjust to 16K page number
        shr     ebx,12                  ; make a PTE index

        cmp     eax,ebx                 ;Q: Is there a match?
        je      short GEiExit           ; Y: exit

        or      dx,dx                   ;Q: More logical pages for this handle
        jnz     short GEiFindPage       ; Y: continue

GEiNextHandle:
        or      cx,cx                   ;Q: Anymore handles?
        jnz     short GEiLoop           ; Y: continue

;
;  This window is currently not mapped
;
        mov     cl,FREE         ; this window is free
        mov     dx,NULL_PAGE    ; no logical page
;
;  At this point, CL is the handle index and DX is the logical page number
;
GEiExit:
        pop     ebx
        mov     fs:[ebx][EMMGI_Cntxt][edi].EMMCntx_HMap,cl
        mov     fs:[ebx][EMMGI_Cntxt][edi].EMMCntx_Lpag,dx

        pop     es
        pop     esi
        pop     edx
        ret
GetEMSinfo      endp

even
;==============================================================================
;==
;==  GetUMBInfo: Detects a UMB in a 16K area.  If detected, the EMM Global
;==              Import Data Structure will be updated in the CONTEXT structure
;==              and the UMB structure.
;==
;==  Entry: (Protected Mode)
;==     EDX     = segment address for 16K block size
;==     FS:[EBX]= start of EMM Global Import Data Structure
;==     ESI     = offset into current UMB structure
;==     EDI     = offset into current CONTEXT structure
;==     DS      = _DATA
;==     ES      = PAGET_GSEL
;==     FS      = DATA32_GSEL
;==     GS      = R_CODE
;==
;==  Exit:
;==
;==============================================================================
GetUMBinfo proc near
        push    eax
        push    ecx
        push    dx

;
;  ESI is offset into current UMB array structure entry: Initialize it
;
        mov     fs:[ebx][EMMGI_UMBs][esi].EUM_Page0,0
        mov     fs:[ebx][EMMGI_UMBs][esi].EUM_Page1,0
        mov     fs:[ebx][EMMGI_UMBs][esi].EUM_Page2,0
        mov     fs:[ebx][EMMGI_UMBs][esi].EUM_Page3,0

        xor     ecx,ecx                 ; start with first 4K page
        shr     dx,8                    ; linear index into page table
GUiLoop:
;
;  Assume this page is "unuseable"
;
        shl     cx,1
        mov     ax,CNTXT_NOTUSED
        shl     ax,cl
        or      fs:[ebx][EMMGI_Cntxt][edi].EMMCntx_EFlgs,al
        shr     cx,1
;
;  Check linear=physical
;
        cmp     dx,0FFh                 ;Q: Last ROM page?
        je      short GUiNext           ; Y: Windows can't write protect it!

        mov     eax,es:[edx*4]          ; get PTE entry
        bt      eax, fWINPageBit        ;Q: WINdows page?
        jnc     short GUiNotWinPage     ; N:

        call    GUiUseable              ; Y: mark page "useable" for Windows
        jmp     short GUiNext

GUiNotWinPage:
        shr     eax,12                  ; turn into physical index

        cmp     eax,edx                 ;Q: Linear=Physical?
        je      short GUiNext           ; Y: not a UMB and thus assume unuseable
;
;  UMB found: unmark "unuseable" assumption
;
        call    GUiUseable
;
;  Indicate a UMB
;
        push    ax
        mov     ax,1
        shl     ax,cl
        shl     ax,CNTXT_UMB0bit
        or      fs:[ebx][EMMGI_Cntxt][edi].EMMCntx_Flags,al
        pop     ax
;
;  Save physical index in UMB structure
;
        mov     fs:[ebx][EMMGI_UMBs][esi].EUM_Page0,eax
;
;  Next 4K page
;
GUiNext:
        add     si,type EUM_Page0
        inc     dx
        inc     cx
        cmp     cx,4
        jb      short GUiLoop
        sub     si,size EMM_UMB_Map
;
;  Update UMB array pointer
;
        test    fs:[ebx][EMMGI_Cntxt][edi].EMMCntx_Flags,-1 ;Q: Any UMBs?
        jz      short GUiExit                      ; N: next context
        mov     al,fs:[ebx].EMMGI_UMBcnt           ; Y: save UMB array index

        mov     fs:[ebx][EMMGI_Cntxt][edi].EMMCntx_UMBindex,al
        inc     fs:[ebx].EMMGI_UMBcnt           ; increment UMB array count
        add     si,size EMM_UMB_Map             ; add to variable portion

GUiExit:
        pop     dx
        pop     ecx
        pop     eax
        ret
GetUMBinfo      endp


;  mark page as "useable"

GUiUseable proc near

;  Unmark "unusable" assumption

        shl     cx,1
        push    ax
        mov     ax,CNTXT_NOTUSED
        shl     ax,cl
        not     ax
        and     fs:[ebx][EMMGI_Cntxt][edi].EMMCntx_EFlgs,al ; "unknown" status
;
;  Mark as "useable"
;
        mov     ax,CNTXT_USEABLE
        shl     ax,cl
        or      fs:[ebx][EMMGI_Cntxt][edi].EMMCntx_EFlgs,al
        shr     cx,1
        pop     ax
        ret

GUiUseable endp

;==============================================================================
;==
;==  GetFreeEMSRange: Finds next free EMS range.
;==
;==  Entry: (Protected Mode)
;==     EDI     = starting index in handle space to search
;==     DS      = _DATA
;==     ES      = PAGET_GSEL
;==     FS      = DATA32_GSEL
;==     GS      = R_CODE
;==
;==  Exit:
;==     EAX     = starting page number of free range (-1 if range not found)
;==     ECX     = free region size in number of pages
;==
;==============================================================================
GetFreeEMSRange proc    near
        push    ebx
        push    edx
        push    si

        mov     edx,[page_directory]
GFERloop1:
        cmp     di,[TopOfFreeEMSspace]
        jae     short GFERdone

        mov     eax,fs:[edx][edi*4]
        add     di,4

        test    ax,fEMSPageAllocated
        jnz     short GFERloop1

        mov     ecx,4
        mov     ebx,eax
GFERloop2:
        add     ebx,4000h

        cmp     di,[TopOfFreeEMSspace]
        jae     short GFERcont

        cmp     ebx,fs:[edx][edi*4]
        jne     short GFERcont

        add     cx,4
        add     di,4
        jmp     short GFERloop2

GFERcont:
        shr     eax,12

;
;  EAX = free pool start (or -1) and ECX = count of free pool
;
GFERexit:
        pop     si
        pop     edx
        pop     ebx
        ret
GFERdone:
        mov     eax,-1
        jmp     short GFERexit
GetFreeEMSRange endp

ifdef QEMS
;==============================================================================
;==
;==  GetFreeEMSRange: Finds next free EMS range.
;==
;==  Entry: (Protected Mode)
;==     EAX     = starting page number for search
;==     DS      = _DATA
;==     ES      = PAGET_GSEL
;==     FS      = DATA32_GSEL
;==     GS      = R_CODE
;==
;==  Exit:
;==     EAX     = starting page number of free range (-1 if range not found)
;==     ECX     = free region size in number of pages
;==
;==============================================================================
GetFreeEMSRange proc    near
        push    ebx
        push    edx
        push    si
;
;  Initialize local variables
;
GFERinit:
        mov     si,word ptr [AllocMapPtr]

        xor     ebx,ebx
        xor     ecx,ecx
        xor     edx,edx
        dec     edx
;
;  Figure out the EMS pool above and below where we are
;
        cmp     gs:[starting_conv_mem_PTE],0
        jz      short GFERhigh

        cmp     ax,gs:[starting_conv_mem_PTE]
        jb      short GFERcon2
        movzx   ebx,gs:[starting_conv_mem_PTE]
        mov     cx,gs:[number_conv_mem_PTEs]
        jmp     short GFERhigh
GFERcon2:
        movzx   edx,gs:[starting_conv_mem_PTE]
        ror     ecx,16
        mov     cx,gs:[number_conv_mem_PTEs]
        ror     ecx,16

GFERhigh:
        cmp     gs:[starting_high_mem_PTE],0
        jz      short GFERext

        cmp     ax,gs:[starting_high_mem_PTE]
        jb      short GFERhigh2
        cmp     bx,gs:[starting_high_mem_PTE]
        ja      short GFERext
        movzx   ebx,gs:[starting_high_mem_PTE]
        mov     cx,gs:[number_high_mem_PTEs]
        jmp     short GFERext
GFERhigh2:
        cmp     dx,gs:[starting_high_mem_PTE]
        jb      short GFERext
        movzx   edx,gs:[starting_high_mem_PTE]
        ror     ecx,16
        mov     cx,gs:[number_high_mem_PTEs]
        ror     ecx,16

GFERext:
        cmp     gs:[starting_ext_mem_PTE],0
        je      short GFERcont

        cmp     ax,gs:[starting_ext_mem_PTE]
        jb      short GFERext2
        cmp     bx,gs:[starting_ext_mem_PTE]
        jae     short GFERcont
        movzx   ebx,gs:[starting_ext_mem_PTE]
        mov     cx,gs:[number_ext_mem_PTEs]
        jmp     short GFERcont
GFERext2:
        cmp     dx,gs:[starting_ext_mem_PTE]
        jb      short GFERcont
        movzx   edx,gs:[starting_ext_mem_PTE]
        ror     ecx,16
        mov     cx,gs:[number_ext_mem_PTEs]
        ror     ecx,16
;
;  We now have the lower pool in EBX,ECX(low) and high pool at EDX,ECX(high)
;
GFERcont:
        add     cx,bx
        cmp     ax,cx           ;Q: Are we in lower pool?
        jae     short GFERhiPool; N: try higher pool
        movzx   edx,cx          ; Y: last PTE+1 of this pool
        jmp     short GFERusedloop

GFERhiPool:
        shr     ecx,16
        mov     eax,edx         ; start of next pool
        add     dx,cx           ; end of this pool
        cmp     eax,-1          ;Q: Have we reached the end of EMS memory?
        je      short GFERexit  ; Y: exit

GFERusedloop:
        bt      ds:[si],ax                      ;Q: Allocated?
        jnc     short GFERfree                  ; N: this range is free
        inc     ax                              ; Y: try next bit
        cmp     ax,dx                           ;Q: Have we reached the end of pool?
        jb      short GFERusedloop              ; N: continue searching
        jmp     GFERinit                        ; Y: go to next pool

GFERfree:
        mov     bx,ax                           ; free pool start
        xor     ecx,ecx                         ; initialize count to zero
        inc     bx                              ; we have already tested first page
        inc     cx                              ; we know we have atleast one page
GFERfreeloop:
        bt      ds:[si],ax                      ;Q: Allocated?
        jc      short GFERexit                  ; Y: end of the free pool
        inc     cx
        inc     bx
        cmp     bx,dx                           ;Q: Reached end of EMS pool?
        jb      short GFERfreeloop              ; N: see if next PTE is free
;
;  EAX = free pool start (or -1) and ECX = count of free pool
;
GFERexit:
        pop     si
        pop     edx
        pop     ebx
        ret
GetFreeEMSRange endp
endif
ifdef QEMS
;==============================================================================
;==
;==  GetFreeEMSRange: Finds next free EMS range.
;==
;==  Entry: (Protected Mode)
;==     EAX     = starting page number for search
;==     DS      = _DATA
;==     ES      = PAGET_GSEL
;==     FS      = DATA32_GSEL
;==     GS      = R_CODE
;==
;==  Exit:
;==     EAX     = starting page number of free range (-1 if range not found)
;==     ECX     = free region size in number of pages
;==
;==============================================================================
        push    ebx
        push    edx
        push    si

        mov     si,word ptr [AllocMapPtr]
        xor     ecx,ecx

GFERloop:
        movzx   ebx,gs:[starting_conv_mem_PTE]
        movzx   edx,gs:[number_conv_mem_PTEs]

        call    CheckRange
        jnc     short GFERcont

        movzx   ebx,gs:[starting_ext_mem_PTE]
        movzx   edx,gs:[number_ext_mem_PTEs]

        call    CheckRange
        jnc     short GFERcont

        movzx   ebx,gs:[starting_high_mem_PTE]
        movzx   edx,gs:[number_high_mem_PTEs]

        call    CheckRange
        jnc     short GFERcont
        mov     eax,-1
        jmp     short GFERexit

GFERcont:
        mov     ebx,eax                         ; start of range
        bt      ds:[si],ax                      ;Q: Allocated?
        jnc     short GFERfree                  ; N: this range is free

;QEMS   test    es:[eax*4],PAGE_ALLOCATED_BIT_MASK;Q: Allocated?
;QEMS   jz      short GFERfree                    ; N: this range is free
        inc     eax                               ; Y: try next PTE

        cmp     eax,edx                         ;Q: Reached end of EMS pool?
        jb      short GFERcont                  ; N: try next PTE
        jmp     short GFERloop                  ; Y: try next EMS pool

GFERfree:
        inc     ecx                             ; free range size
        inc     ebx                             ; try next PTE

        cmp     ebx,edx                         ;Q: Reached end of EMS pool?
        jae     short GFERexit                  ; Y: exit

        bt      ds:[si],bx                      ;Q: Allocated?
        jnc     short GFERfree                  ; N: increase range size

;QEMS   test    es:[ebx*4],PAGE_ALLOCATED_BIT_MASK;Q: Allocated?
;QEMS   jz      short GFERfree                    ; N: increase range size
GFERexit:                                         ; Y: return free region
        pop     si
        pop     edx
        pop     ebx
        ret
GetFreeEMSRange endp
;==============================================================================
;==
;==  CheckRange: Find an EMS range.
;==
;==  Entry: (Protected Mode)
;==     EAX     = entry to search higher than
;==     EBX     = start of a EMS pool range
;==     EDX     = number of PTE entries in pool range
;==     DS      = _DATA
;==     ES      = PAGET_GSEL
;==     FS      = DATA32_GSEL
;==     GS      = R_CODE
;==
;==  Exit:
;==     EAX     = starting page number to start search
;==     EDX     = last PTE entry for this pool + 1
;==     NC      = within EMS range
;==     CY      = outside EMS range
;==
;==============================================================================
CheckRange proc near

        add     edx,ebx         ; boundary for EMS pool

        or      edx,edx         ;Q: Valid EMS pool range?
        jz      short CRerror   ; N: exit

        cmp     eax,ebx         ;Q: Below EMS pool?
        jbe     short CRbelow   ; Y: start at base of EMS pool

        cmp     eax,edx         ;Q: Above EMS pool?
        jb      short CRok      ; N: OK, within range
CRerror:
        stc                     ; check next EMS pool range
        jmp     short CRexit

CRbelow:
        mov     eax,ebx         ; start at the beginning of EMS pool
CRok:
        clc                     ; Check this EMS pool
CRexit:
        ret
CheckRange      endp
endif

;==============================================================================
;==
;==  VxDSupport: Add a Win386 Startup Info Structure for a VxD which handles
;==              UMBs.
;==
;==  Entry: (Real Mode)
;==     DS      = _DATA
;==     ES      = PAGET_GSEL
;==     FS      = DATA32_GSEL
;==     GS      = R_CODE
;==     DI      = Version number: major in upper byte, minor in lower
;==
;==  VMTF_ES:BX = points to next data structure in linked chain
;==
;==
;==  Exit:
;==  VMTF_ES:BX = pointer to our data structure
;==
;==============================================================================
VxDSupport proc near


        push    eax
;
;  Check Page Table 0 for aliasing of first megabyte
;
        call    VxDlistUMB
;
;  If no aliasing in first megabyte, VxD not needed.
;
        cmp     ds:[Win386VxDRefDat].RDSUMBstart,0
        je      short VxDSexit
;
;  Fill in used UMB information in VxD structure
;
        call    VxDUsedUMBList
;
;  Delete EMS windows from the UMB list
;
        call    VxDEMSPages
;
;  Delete C6 and C7 if Cega installed and length > 24 K
;
        call    VxdCROMPages
;
;  We now initialize the WSSnextPtr and WSSVxDFilePtr fields in Win386Struc.
;  The former is initilazed to ES:BX in segment:offset form. The latter
;  is initialized to CEMMpath that is defined in the R1_CODE segment as
;  a linear address. Note that the segment of R1_CODE segment is obtained
;  from any one of the entries in the UMBFARTABLE defined in umbseg.asm.
;
;
        push    edi
        movzx   edi, word ptr gs:[EMM_rFarEntry+2]
        shl     edi, 4
        add     edi, OFFSET R1_CODE:Win386Struc

        ;
        ; edi = 32 bit pointer to Win386Struc defined in R1_CODE
        ;

        mov     ax,[bp][VTFO].VMTF_ES
        shl     eax,16
        mov     ax,bx
        mov     fs:[edi].WSSnextPtr,eax

        mov     ax, gs:[EMM_rFarEntry+2]
        shl     eax,16
        lea     ax,R1_CODE:CEMMpath
        mov     fs:[edi].WSSVxDFilePtr,eax
        shr     eax, 16

;
;  Make ES:BX point to Win386 Startup Structure for UMB VxD
;
        mov     [bp][VTFO].VMTF_ES,ax
        mov     bx,OFFSET R1_CODE:Win386Struc
        pop     edi

VxDSexit:
        pop     eax
        ret
VxDSupport      endp

;==============================================================================
;==
;==  VxDlistUMB: Initialize Windows 386 UMB VxD refrence data struture with
;==              physical addresses of UMBs.
;==
;==  Entry: (Real Mode)
;==     DS      = _DATA
;==     ES      = PAGET_GSEL
;==     FS      = DATA32_GSEL
;==     GS      = R_CODE
;==
;==  Exit:
;==
;==============================================================================
VxDlistUMB proc near
        push    eax
        push    ebx
        push    esi
        push    edi
        push    cx
;
;  Make ESI point to PT0 and EDI point to [Win386VxDRefDat].RDSUMBarray
;
        mov     esi,ds:[page_tables]

;;      mov     di,seg R_CODE
;;      movzx   edi,di
;;      shl     edi,4
        mov     edi, gs:[p_DATA];
        add     edi,offset ds:[Win386VxDRefDat].RDSUMBarray

        mov     ebx,0A0h
        mov     cx,100h-0A0h            ; loop thru PTEs from A000 to 1MB
VlUFindFirst:
        mov     eax,fs:[esi][ebx*4]
        shr     eax,12
        cmp     eax,ebx                 ;Q: Have we reached the first lin<>phy?
        jne     short VlUFirst          ; Y: first UMB page
        inc     bx                      ; get next PTE
        loop    VlUFindFirst
        jmp     short VlUexit

;
;  First UMB has been detected
;
VlUFirst:
        mov     ds:[Win386VxDRefDat].RDSUMBstart,ebx
VlUFound:
        cmp     eax,ebx                 ; Q: lin = phy
        jne     short VlUFcont          ; N:
        xor     eax,eax                 ; Y: stuff 0 in UMBarray
VlUFcont:
        mov     fs:[edi],eax
        add     edi,4
        inc     bx
        mov     eax,fs:[esi][ebx*4]
        shr     eax,12
        loop    VlUFound

VlUexit:
        mov     gs:[pRDSdata],di        ; address of next entry in Win386VxDRefData

        pop     cx
        pop     edi
        pop     esi
        pop     ebx
        pop     eax
        ret
VxDlistUMB      endp

;==============================================================================
;==
;==  VxDUsedUMBList: Fills VxD data structure with used UMB area.
;==
;==  Entry: (Protected Mode)
;==     DS      = _DATA
;==     ES      = PAGET_GSEL
;==     FS      = DATA32_GSEL
;==     GS      = R_CODE
;==
;==  Exit:
;==
;==============================================================================
VxDUsedUMBList  proc    near
        push    eax
        push    edx
        push    edi
;
;  VxD Used List only if UMBptr
;
        cmp     gs:[UMBptr],0           ;Q: UMB ARENAs on the system?
        je      VUULexit                ; N: exit
;
;  Get free UMB memory region areas
;
        movzx   edi,gs:[UMBptr]
        or      di,di                           ;Q: UMBs provided?
        jz      VUULexit                        ; N: exit
        shl     edi,4                           ; Y: offset to first ARENA
VUULloop:
        cmp     fs:[edi].Sig,'M'                ;Q: Is it an ARENA?
        je      short VUULlcont                 ; Y: OK
        cmp     fs:[edi].Sig,'Z'                ;Q: Is it an ARENA?
        jne     short VUULexit                  ; N: exit, corruption
;
;  Valid ARENA: Get starting paragraph and length
;
VUULlcont:
        mov     eax,edi                         ; get current paragraph
        shr     eax,4
        movzx   edx,fs:[edi].Len                ; M015: get length and
                                                ; M015: Zxtend it.
        add     dx,2

        cmp     fs:[edi].Own,0                  ;Q: Is it a FREE UMB?
        jne     short VUULnext                  ; N: get next ARENA

        push    eax
        push    edx

        shl     eax,4
        shl     edx,4

        add     edx,eax
        add     eax,1000h-1

        shr     eax,12
        shr     edx,12

        sub     eax,ds:[Win386VxDRefDat].RDSUMBstart
        sub     edx,ds:[Win386VxDRefDat].RDSUMBstart
        cmp     eax,edx
        jae     short VUULnoFreePage
VUULclear:
        mov     ds:[Win386VxDRefDat][eax*4].RDSUMBarray,0
        inc     eax
        cmp     eax,edx
        jb      short VUULclear
VUULnoFreePage:
        pop     edx
        pop     eax

VUULnext:
        cmp     fs:[edi].Sig,'Z'                ;Q: Is it end of ARENA chain?
        je      short VUULexit                  ; Y: exit
        add     ax,dx                           ; N: next ARENA
        dec     ax
        shl     eax,4
        mov     edi,eax
        jmp     VUULloop

VUULexit:
        pop     edi
        pop     edx
        pop     eax
        ret
VxDUsedUMBList  endp

;==============================================================================
;==
;==  VxDEMSPages: Clear EMS pages from the VxD UMB list
;==
;==  Entry: (Protected Mode)
;==     DS      = _DATA
;==     ES      = PAGET_GSEL
;==     FS      = DATA32_GSEL
;==     GS      = R_CODE
;==
;==  Exit:
;==
;==============================================================================
VxDEMSPages     proc    near
        push    eax
        push    esi
        push    cx

;
;  Check if any EMS pages exist
;
        cmp     gs:[NoEMSset],TRUE      ;Q: EMS available?
        je      short VEPexit           ; N: exit

        cmp     gs:[NoPFset],TRUE       ;Q: FRAME=NONE specified?
        je      short VEPexit           ; Y: exit

;
;  Check to see if EMS pages are part of the UMB list
;
        xor     esi,esi
        mov     cx,[number_EMS_windows] ; number of EMS windows

VEPEMSLoop:
        movzx   eax,[EMS_window_location][esi*2]        ; get EMS linear page
        sub     eax,ds:[Win386VxDRefDat].RDSUMBstart    ; offset into UMB list
        jb      short VEPnext                           ; not pat of UMB list

;
;  Delete the EMS page from the UMB list
;
        push    cx
        mov     cx,4
VEPClearLoop:
        mov     ds:[Win386VxDRefDat][eax*4].RDSUMBarray,0
        inc     eax
        loop    VEPClearLoop
        pop     cx
VEPnext:
        inc     si
        loop    VEPEMSLoop

VEPexit:
        pop     cx
        pop     esi
        pop     eax
        ret
VxDEMSPages     endp

;==============================================================================
;==
;==  VxDCROMPages: Clear the Video ROM alias pages if a non-CPQ ROM is detected.
;==                This is to prevent Win 3.0 from crashing. Also clear page
;==                page at FF/reset vector.
;==
;==  Entry: (Protected Mode)
;==     DS      = _DATA
;==     ES      = PAGET_GSEL
;==     FS      = DATA32_GSEL
;==     GS      = R_CODE
;==
;==  Exit:
;==
;==============================================================================
VxDCROMPages    proc    near
        push    eax
        push    cx
;
;  Do not alias PTE 0FFh because Win 3.0 will not write protect it.

        mov     eax, 0ffh
        sub     eax,ds:[Win386VxDRefDat].RDSUMBstart
                                        ; offset into UMB list
        mov     ds:[Win386VxDRefDat][eax*4].RDSUMBarray,0
                                        ; no aliasing
;
;  Do not alias VIDEO ROM if it's greater than 24K - due to a Win 3.0 problem.
;
        test    gs:[Current_State], fstate_CEGAinst ;Q Shadowed Video ROM installed?
        jz      short VCRdone                       ; N: don't worry

        cmp     gs:[CROM_Length],600h   ;Q: Larger than 24K
        jbe     short VCRdone           ; N: no problem with Win 3.0!

        mov     cx,gs:[CROM_Length]     ; get actual size
        shr     cx,8                    ; size in pages
        mov     eax,0C0h                ; starting page

        sub     eax,ds:[Win386VxDRefDat].RDSUMBstart    ; offset into UMB list
VCRloop:
        mov     ds:[Win386VxDRefDat][eax*4].RDSUMBarray,0 ; no aliasing
        inc     eax                                       ; next page
        loop    VCRloop


ifdef 901004                                    ; CROM_length has length in paras
        cmp     gs:[CROM_Length], 0600h ; Q: is Rom length > 24 K
        jbe     VCRdone                 ; N:

                                        ; Y: zero out c6 and c7
        mov     eax, 0c6h
        sub     eax,ds:[Win386VxDRefDat].RDSUMBstart    ; offset into UMB list
        mov     ds:[Win386VxDRefDat][eax*4].RDSUMBarray,0
        inc     eax
        mov     ds:[Win386VxDRefDat][eax*4].RDSUMBarray,0
endif

VCRdone:
        pop     cx
        pop     eax
        ret
VxDCROMPages    endp

_TEXT   ends

;=============================================================================
;==     C O D E  S E G M E N T
;=============================================================================
R1_CODE segment
        assume  cs:R1_CODE,ds:nothing,es:nothing
;==============================================================================
;==
;==  rINT2FhHandler: This is an interrupt 2Fh handler which monitors for the
;==                  Windows 386 and DOS Extender Initialization.
;==
;==  Entry: (Real Mode)
;==     AX      = 1605h Windows Initialization
;==     ES:BX   = 0:0
;==     DS:SI   = 0:0
;==     DI      = Version number: major in upper byte, minor in lower
;==     CX      = 0
;==     DX      = Flags
;==       Bit 0 = 0 Windows 386 inititalization
;==               1 Windows 286 DOS extender inititalization
;==
;==  Exit:
;==
;==     AX      = 1605h Windows Initialization
;==     ES:BX   = pointer to linked instance data structure chain
;==     DS:SI   = far call entry point for Virtual Disable Call
;==     DI      = Version number: major in upper byte, minor in lower
;==     CX      = 0 if OK for Windows to initialize
;==             <>0 if Windows should NOT initialize
;==     DX      = Flags
;==       Bit 0 = 0 Windows 386 inititalization
;==               1 Windows 286 DOS extender inititalization
;==
;==============================================================================
rINT2FhHandler:

        pushf

        cmp     ah,16h                  ;Q: Windows Init or Exit?
        jne     short rI2Fquick         ; N: chain

        push    ds
        push    seg R_CODE
        pop     ds
        assume  ds:R_CODE

        cmp     al,05h                  ;Q: Windows Init?
        je      short rI2FWinInit       ; Y: process
        cmp     al,06h                  ;Q: Windows Exit?
        jne     short rI2FoldHandler    ; N: exit
        and     [GenFlags],not fWin30   ; Y: Win 3.0 broadcast over

        mov     [EMMGIDataVer],EMMGI_VERSION_LO
                                        ; set import veriosn back to base 1.00

        cmp     [Current_Mode],MODE_OFF ;Q: Is CEMM off?
        je      short rI2FoldHandler    ; Y: chain
        mov     [DevName],'E'           ; N: make sure EMS on

;;        cmp     cs:[NoEMSset],TRUE    ;Q: Is CEMM in NoEMS mode?
;;        jne   short rI2FoldHandler    ; N: chain

        mov     [DevName+3], 'X'        ; ensure that 4th char in device
                                        ; name is X rather than Q.

        cmp     [VCPIset], -1           ; Q: has noems been specifed.
        je      short rI2FoldHandler    ; N: chain

        cmp     [VCPIset], FALSE        ; Q: did user specify only NOEMS
        jne     short rI2FSpecialSig    ; N: noems+poolsize was specfied

        mov     [DevName],'$'           ; Y: make sure EMS is off
        jmp     short rI2foldHandler

rI2FSpecialSig:
        mov     [Devname+3], 'Q'        ; set up sig to EMMQXXX.

rI2FoldHandler:
        pop     ds
rI2Fquick:
        popf
        jmp     cs:[PrevInt2F]

;============================================================================
;=  Instance data, install a driver, and Virtual Disable Call entry point
;============================================================================
rI2FWinInit:
        test    dx,1                            ; M013: Q: Win Standard Mode?
        jnz     short rI2FStdInit               ; M013: Y: do std. mode init

        cmp     [Current_Mode],MODE_OFF         ;Q: Is CEMM off?
        je      short rI2FoldHandler            ; Y: chain

        call    [ValidPathFar]                  ; M010: Q: is the path valid
        jc      short rI2FInvPath               ; M010: N: fail win start up

        .errnz  fBackfillBit-15
        test    byte ptr [GenFlags+1], 80h      ; Q: is backfill on
        jnz     short rI2Fbackfill              ; Y: fail win start up

        test    [Current_State],fState_Active   ;Q: Is CEMM active?
        jnz     short rI2Fhook                  ; Y: hook broadcast

        mov     [DevName],'$'                   ; N: turn EMS off
        jmp     short rI2FoldHandler            ; chain

;============================================================================
;       Standard mode init: Change device name to EMMQXXX
;============================================================================

rI2fStdInit:
        cmp     di, WIN_VERSION_LO      ; Q: is version > 3.0
        jbe     short rI2foldHandler    ; N: just chain
        mov     [Devname], 'E'          ; Y: change device name
        mov     [Devname+3], 'Q'
        jmp     short rI2foldHandler

;==============================================================================
;==  Fail Windows start up due to base memory back fill
;==============================================================================

rI2Fbackfill:
        push    dx
        lea     dx, R1_CODE:WinBackfillMess
        jmp     short rI2fPrintFail             ; print msg and ret failure

;==============================================================================
;==  Fail Windows start up due to invalid path
;==============================================================================

rI2FInvPath:                                    ; M010 - Start
        push    dx
        lea     dx, R1_CODE:WinInvPathMess      ; print error message

rI2fPrintFail:
        push    ax
;       mov     ax, cs                                    ;ELM716
;       mov     ds, ax        ; ds = segment of R1_CODE   ;ELM716
        push    cs
        pop     ds
        assume  ds:nothing                                ;ELM716
        mov     ah, 09h
        int     21h           ; print error msg
        pop     ax
        pop     dx
        mov     cx, 1                           ; indicate failure cx != 0
        jmp     short rI2FoldHandler            ; M010 - End

;==============================================================================
;==  Call previous INT 2Fh to get instance data or installable device
;==============================================================================
        assume  ds:R_CODE               ;ELM716
rI2Fhook:
        mov     [DevName],'E'           ; make sure EMS is on
        mov     [DevName+3], 'X'
;
;  Call previous INT 2Fh to get instance data or installable device
;
        pop     ds
        assume  ds:NOTHING
        popf                            ; restore original flags
        pushf                           ; set up for emulated int 2Fh
        call    cs:[PrevInt2F]

        push    ax
        push    ds
        mov     ax, seg R_CODE
        mov     ds, ax
        assume  ds:R_CODE
        mov     ax,2                    ; code for Win broadcast
;==============================================================================
;==  Update Windows Version and VxD (if Win 3.0)
;==============================================================================
        cli
        or      [TrapFlags],fWinTrap    ; dispatch to Ein Global Import
        pop     ds
        assume  ds:NOTHING
        int     ProtTrap                ; enter protected mode
        pop     ax
        iret

R1_CODE ends

;=============================================================================
;==     C O D E  S E G M E N T
;=============================================================================
R_CODE  segment
        assume  cs:R_CODE,ds:nothing,es:nothing
;==============================================================================
;==
;==  rWinV86Proc: This routine is called by Windows to disable/enable V86
;==               mode.  During V86 disable, the EMM Global Import data
;==               structure is updated to reflect CEMM's current EMS and
;==               linear to physical mapping state.  During V86 enable,
;==               CEMM will update its internal data structures and page
;==               tables with information from the EMM Global data structure.
;==               This allows critical information to be passed to Windows
;==               during V86 disable, and to CEMM from Windows during V86
;==               enable.
;==
;==  Entry: (Real/V86 Mode)
;==     AX      = 0     Disable Virtual 8086 mode (enter Real Mode)
;==             = 1     Re-enable Virtual 8086 mode (from Real Mode)
;==
;==  Exit:  (V86/Real Mode)
;==
;==============================================================================
rWinV86Proc     proc    far

        cmp     ax,1
        jb      short rWinVirDisable
        je      short rWinVirEnable

rWinV86exit:
        ret
rWinV86Proc     endp

;==============================================================================
;==
;==  rWinVirDisable: This routine is called by Windows to disable V86 mode
;==                  During V86 disable, the EMM Global Import data structure
;==                  is updated to reflect CEMM's current EMS and linear to
;==                  physical mapping state. This allows critical information
;==                  to be passed to Windows during V86 disable.
;==
;==  Entry: (V86 Mode)
;==     AX      = 0     Disable Virtual 8086 mode (enter Real Mode)
;==
;==  Exit:  (Real Mode)
;==
;==============================================================================
rWinVirDisable  proc    far
;
;  Windows Virtual Disable Call
;
        and     cs:[GenFlags],not fWin30        ; Win 3.0 broadcast complete

        test    cs:[Current_State],fState_Active;Q: Virtual Mode?
        jz      short rWVDerror                 ; N: error

;==============================================================================
;==  Update EMM Global Import data structure from internals
;==============================================================================
        cli
        or      cs:[TrapFlags],fWinTrap ; dispatch to Ein Global Import
        int     ProtTrap                ; enter protected mode
;
;  Leave A20 enabled for Windows and indicate Windows is in control
;
        or      cs:[Current_State],fState_A20Ena+fState_WinCntrl

        pushad

;;      push    cs
;;      pop     es

        mov     di, cs:[EMM_rFarEntry+2]
        mov     es, di                  ; es = segment of R1_CODE
        assume  es:R1_CODE
        lea     di,es:[InstanceData]    ; es:di -> data struc into which prev.
                                        ; instance info. is to be copied
        mov     cx,MaxUMBInstObj        ; maximum instance objects in UMBs
        call    cs:[CopyInstData]
;QLEO
;;      lea     ax,cs:[Win386Struc]
;QLEO
        popad

;
;  V86 disable
;
        call    CEMMoff                 ; turn CEMM off
        mov     cs:[Devname],'$'        ; inactivate CEMM
;
;  Return to Windows
;
rWVDexit:
        clc
        ret
rWVDerror:
        stc
        ret
rWinVirDisable  endp

;==============================================================================
;==
;==  rWinVirEnable: This routine is called by Windows to enable V86 mode.
;==                 During V86 enable, CEMM will update its internal data
;==                 structures and page tables with information from the
;==                 EMM Global data structure.
;==                 This allows critical information to be received from
;==                 Windows during V86 enable.
;==
;==  Entry: (Real Mode)
;==     AX      = 1     Re-enable Virtual 8086 mode (from Real Mode)
;==
;==  Exit:  (V86 Mode)
;==
;==============================================================================
rWinVirEnable   proc    far

        test    cs:[Current_State],fState_Active;Q: Virtual Mode?
        jnz     short rWVDerror                 ; Y: already on!

ifdef   MSFLAG
        bt      cs:[Current_State],fState_WinCntrlBit;Q: Waiting for Windows to exit?
else
        btr     cs:[Current_State],fState_WinCntrlBit;Q: Waiting for Windows to exit?
endif

        jnc     short rWVDerror                      ; N: why this call?

;
;  The user stack is changed to the CEMM Real Stack
;
        mov     cs:[UserSS],ss
        mov     cs:[UserSP],sp
        push    seg R_STACK
        pop     ss
        lea     sp,R_STACK:RealStackTop

ifdef DEBUG
        call    ReInitDeb
endif
;
;  The user's segment registers are pushed on the stack.
;
        push    gs
        push    fs
        push    es
        push    ds

        cli                             ;;; no ints now
        call    GoProtMode
        jc      short WINEMM_rExit

;
; The DS and GS selectors are set up.
;
        push    VDMD_GSEL
        pop     ds
        assume  ds:_DATA
        push    RCODEA_GSEL
        pop     gs
        assume  gs:R_CODE


;
;  Save CEMM real stack and change to protected mode stack
;
        mov     gs:[RealSS],ss
        mov     gs:[RealSP],sp
        push    VDMS_GSEL
        pop     ss
        mov     sp,gs:[StackTop]
;
; The function number is saved and the call done.
;
        PCall   VDMC_GSEL,_TEXT:UpdateInternalsFar
;
;  Restore CEMM real stack
;
        mov     ss,gs:[RealSS]
        mov     sp,gs:[RealSP]

        jc      short gotoreal                  ; error, don't turn on

        PCall   VDMC_GSEL,_TEXT:EnterVirtual
        call    GoVirtualMode

        jc      short WINEMM_rExit

        or      cs:[Current_State],fState_Active
        mov     cs:[Devname],'E'                     ; activate CEMM
        jmp     short WINEMM_rExit

gotoreal:
        jmp     $
;       call    GoRealMode
;       mov     cs:[Devname],'$'                  ; inactivate CEMM
;       and     cs:[Current_State],not fState_Active
;
;       mov     dx,offset WINEMM_Mess           ; Y: print out message
;       mov     ah,9
;       int     21h                             ; print message

WINEMM_rExit:

ifdef   MSFLAG
        and     cs:[Current_State], NOT fState_WinCntrl
endif

        pop     ds
        pop     es
        pop     fs
        pop     gs
;
;  Restore user stack
;
        mov     ss,cs:[UserSS]
        mov     sp,cs:[UserSP]

WINEMM_done:
        ret

;WINEMM_error:
;        stc
;        ret

ifdef QLEO      ; look into problems with switching to V86 mode???? QLEO
;
;  The user stack is changed to the CEMM Real Stack
;
        mov     cs:[UserSS],ss
        mov     cs:[UserSP],sp
        push    seg R_STACK
        pop     ss
        lea     sp,R_STACK:RealStackTop

        call    GoVirtual               ;Q: Enter V86 mode
        jc      rWVPerrorX              ; N: error
;
;  Restore user stack
;
        mov     ss,cs:[UserSS]
        mov     sp,cs:[UserSP]
;
;  Update internal with EMM Global Import data structure
;
        cli
        or      cs:[TrapFlags],fWinTrap ; dispatch to EMM Global Import
        int     ProtTrap                ; enter protected mode
        jnc     short rWVPexit          ; if no error, return to Windows

;
;  Error restoring EMM Global Import state: set carry
;
        mov     dx,offset WINEMM_Mess   ; print out message
        mov     ah,9
        int     21h
        stc                             ; indicate error

;
;  Restore user stack
;
rWVPerrorX:
        mov     ss,cs:[UserSS]
        mov     sp,cs:[UserSP]

;
;  Set carry due to error
;
rWVEerror:
        stc                             ; indicate error
        ret
endif
rWinVirEnable   endp

;==============================================================================
;==
;==  CEMMoff: Procedure to turn CEMM off via diagnostic ports.
;==
;==  Entry: (V86 Mode)
;==
;==  Exit:  (Real Mode)
;==
;==============================================================================
        public  CEMMoff
CEMMoff proc    near
        push    ax
        pushf
        cli
;
;  CEMM traps the following IOs and turns off
;
        mov     al,0Fh
        out     84h,al
        mov     al,00h
        out     85h,al
        jmp     $+2

        popf
        pop     ax
        ret
CEMMoff endp


R_CODE  ends

;=============================================================================
;==     C O D E  S E G M E N T
;=============================================================================
LAST    segment
        assume  cs:LAST,ds:nothing,es:nothing
;==============================================================================
;==
;==  VxDInit: Initialize Windows 386 VxD.
;==
;==  Entry: (Real Mode)
;==     GS      = R_CODE
;==     DS      = _DATA
;==     R1_CODE has not been moved.
;==
;==  Exit:
;==
;==============================================================================
VxDInit proc    near

        push    eax
        push    ebx
        push    es

        push    fs

        mov     ax, seg R1_CODE         ; R1_CODE seg not moved yet.
        mov     fs, ax
        assume  fs:R1_CODE
;
;  Place virtual device file name pointer
;
;;      mov     ax,fs
;;      shl     eax,16
;;      lea     ax,fs:[CEMMpath]
;;      mov     fs:[Win386Struc].WSSVxDFilePtr,eax
;
;  Place VxD reference data structure pointer
;
;;      xor     ax,ax
;;      shr     eax,12
;;      add     eax,offset gs:[Win386VxDRefDat]
;;      mov     gs:[Win386Struc].WSSVxDRefData,eax

        mov     eax, gs:[p_DATA]
        add     eax,offset [Win386VxDRefDat]
        mov     fs:[Win386Struc].WSSVxDRefData,eax

;
;  Now fill information for VxD in VxDRefDataStruct: start of device driver chain
;
        xor     eax,eax
        mov     ebx,eax
        mov     ah,52h          ; get address of start of device driver chain
        int     21h
        mov     ax,es           ; address of ES:[BX][22h]
        shl     eax,4
        add     eax,ebx
        add     eax,22h
        mov     ds:[Win386VxDRefDat].RDSDDchain,eax
;
;  Place instance data structure pointer
;
        xor     eax, eax
        mov     ax, fs
        shl     eax, 4
        add     eax, offset fs:[InstanceData]
        mov     ds:[Win386VxDRefDat].RDSdata,eax

        pop     fs
        pop     es
        pop     ebx
        pop     eax
        ret
VxDInit endp

;==============================================================================
;==
;==  GetPathName: This procedure will get the path and name of the CEMM device
;==               driver from the CONFIG.SYS buffer.
;==
;==  Entry: (Real Mode)
;==     ES:DI   = pointer to CONFIG.SYS line
;==     GS      = R_CODE
;==     R1_CODE has not been moved.
;==
;==  Exit:
;==
;==============================================================================
GetPathName     proc    near

        push    ax
        push    cx
        push    si
        push    di
        push    ds
        push    es
;
;  Move ES:DI to DS:SI and place R1_CODE:[CEMMpath] in ES:DI
;
        mov     ax,es
        mov     si,gs:[EMM_rFarEntry+2]
                                ; si = R1_CODE segment
        mov     ds,ax
        mov     es,si
        mov     si,di
        lea     di,R1_CODE:[CEMMpath]

        cld
        mov     cx,ConfigBuffer ; max length cannot be greater than 128 chars

        push    di              ; save pointer to destination buffer

GPNnext:
        lodsb                     ; get a char from buffer
        cmp     al, 0dh           ; Q: is it CR
        je      short GPNfullname ; Y: try to get full name
        cmp     al, 0ah           ; Q: is is LF
        je      short GPNfullname ; Y: try to get full name
        cmp     al, ' '           ; Q: is it a blank
        je      short GPNfullname ; Y: try to get a full name
        cmp     al, 0             ; M010: Q: is it a NULL
        je      short GPNfullname ; M010: Y: try to get a full name
        stosb                     ; save char in destination buffer
        loop    GPNnext           ; get next char

        ;
        ; We shall now make an attempt to get the full actual path of the
        ; device by calling Int 21 function ah=60h.
        ;
GPNfullname:
        mov     byte ptr es:[di], 0
                                ; M010: NULL terminate
        pop     di              ; restore destination buffer pointer

        push    ax              ; save last char obtained
        mov     byte ptr ds:[si-1], 0
                                ; terminate name with NULL
        sub     cx, ConfigBuffer+1
        neg     cx              ; cx = length of string
        sub     si, cx          ; rewind si to start of string
        mov     ah, 060h        ; call $nametrans (undocumented call)
        int     21h             ; returns full path in es:di
                                ; Note: if this call returns it's not bad as
                                ; we already copied the device name into the
                                ; destination.
        pop     ax              ; restore last char found in source string
        add     si, cx          ; si pointing to end of name
        mov     byte ptr ds:[si-1], al
                                ; put last char back

GPNexit:
        pop     es
        pop     ds
        pop     di
        pop     si
        pop     cx
        pop     ax
        ret
GetPathName     endp

;---------------------------------------------------------------------------
;
;       M010:
;
;       Procedure Name  : StorePath
;
;       Inputs          : DS:SI -> points to path spec
;       Output          : stores path in CEMMPath
;
;       Uses            : None
;
;---------------------------------------------------------------------------

StorePath       proc    near

        push    es
        push    di

        push    ds
        pop     es
        mov     di, si          ; set es:di to path
        call    GetPathName

        pop     di
        pop     es
        ret

StorePath       endp


LAST    ends

        END
