;       SCSID= @(#)selwrk.asm   6.13 92/03/11
page    ,132
title   selwrk - Selector Manager

SCCSID  equ     "@(#)selwrk.asm 6.13 92/03/11"

;***    selwrk - Selector Manager
;
;       IBM/Microsoft Confidential
;
;       Copyright (c) IBM Corporation 1987, 1989
;       Copyright (c) Microsoft Corporation 1987, 1989
;
;       All Rights Reserved
;
; 09/18/91  Khoa Dang Huynh
;       CP20 DCR 1486  Provide selector packing capability.
;

INCL_ERROR_H    equ     1
.xlist
include dosseg.inc
include basemaca.inc
include fnomem.inc
include osmaca.inc
include mi.inc
include bseerr.inc
include osconst.inc
include vmowner.inc
include seldesc.inc
include kmode.inc
include pgexport.inc
include vmexport.inc
include sellocal.inc
include selxport.inc
include virtsym.inc
include dossymc.inc          ; included for tkptda.inc
include except.inc           ; included for tkptda.inc
include sigdef.inc           ; included for tkptda.inc
include tkexport.inc         ; included for tkptda.inc
include infoseg.inc          ; included for tkptda.inc
include sem.inc              ; included for tkptda.inc
include screen.inc           ; included for tkptda.inc
include curdir.inc           ; included for tkptda.inc
include dirent.inc           ; included for tkptda.inc
include find.inc             ; included for tkptda.inc
include tkptda.inc
include mmpha.inc
.list

        ModName , FNO_SELWRK_ASM
        CpuMode 386

        ReferFlat       _pPTDACur,DWORD
        ReferFlat       _pTCBCur,DWORD
        ReferFlat       _vmNamShrSem,DWORD
        ReferFlat       _pobvmZero,DWORD
        ReferFlat       _VMProtectedMem,dword
        ReferFlat       _SMswapping,BYTE
        ReferFlat       _smDFMac,DWORD
        ReferFlat       _smFileSize,DWORD
        ReferFlat       _pgPhysPages,DWORD              ;@DB73108
        ReferFlat       _pgResidentPages,DWORD          ;@DB73108
        ReferFlat       _pgSwappablePages,DWORD         ;@DB73108
        ReferFlat       _pgDiscardablePages,DWORD       ;@DB73108

;
; These are all the 32 bit external subroutines needed by this source file.
;

        EXTRN   _KSEMRequestExclusive:near
        EXTRN   _KSEMRelease:near
        EXTRN   _SELGetDescInfo:near
        EXTRN   _SELGetSize:near
;;;;;;; EXTRN   _SELObjectInfo:near
        EXTRN   _selGetSeg:near
        EXTRN   _selGiveSeg:near
        EXTRN   _SELGetOd:near
        EXTRN   _selReallocSub:near
        EXTRN   _TKPidGetSem:near
        EXTRN   _VMAllocMem:near
        EXTRN   _VMAttach:near
        EXTRN   _VMClearSem:near
        EXTRN   _VMFreeMemOd:near
        EXTRN   _VMGetOwner:near
        EXTRN   _VMGetPTDAAddr:near
        EXTRN   _VMGetSem:near
        EXTRN   _VMMapAlias:near
        EXTRN   _VMSelAlias:near

        EXTRN16         Rmp_Alloc
        EXTRN16         Rmp_Add
        EXTRN16         Rmp_Find
        EXTRN16         Rmp_Rem

        EXTRNFAR        Rmp_Find
        EXTRNFAR        Rmp_Rem

        EXTRN32         _KSEMAlloc
        EXTRNF32        _PGPhysAvail
        EXTRN32         _smSIOSpaceQuery

WAS_VM1 equ VMAC_LOCANY or VMAC_ALIGNSEL or VMAC_SELALLOC or VMAC_OBAPI
WAS_VM3 equ VMAC_OBHUGE
WAS_PG equ PG_SWAPPABLE or PG_U or PG_R or PG_W

DATA segment
; Moved to sdata.asm for System Anchor Block - f122427
        extrn   hDiscSegRmpStruc:dword
        extrn   DiscSegRmpRec:byte
DATA ends

_DATA segment
         public _SELDiscSegRmpSem
      _SELDiscSegRmpSem dd 0                  ; discardable segments semaphore
_DATA ends

        FARCODE HIGH3
HIGH3CODE segment


;***EP  w_AllocSeg - allocate a data segment
;***    w_AllocProtSeg - allocate a data segment in protected memory if possible
;
;       w_AllocSeg allocates a private or unnamed shared data segment in
;       the 286 chip, 16 bit format.
;       w_AllocProtSeg is identical to w_AllocSeg except that a GIVESEG or
;       GETSEG segment will be allocated from the protected region of the
;       shared arena.
;
;       ENTRY   (AX) = allocation flag bits:
;                   0: private segment
;                   1: GIVESEG segment
;                   2: GETSEG segment
;                   4: discardable segment
;                   8: shrinkable segment
;               (BX) = size in bytes (0 = 64K)
;       EXIT    Carry clear
;                   (AX) = selector
;               Carry set
;                   (AX) = error code
;       USES    EAX, EBX, ECX, EDX, EDI, ESI, DS, ES
;
;
;       PseudoCode:
;
;       Set or clear VMAC_PROT flag
;       Call selAllocSeg
;
Procedure w_AllocProtSeg,faronly
    assume ds:nothing, es:nothing, ss:nothing, fs:nothing, gs:nothing

        mov     edx, VMAC_PROT          ; allocate protected memory (if shared)
        jmp     short was00

Entry   w_AllocSeg,faronly
        xor     edx, edx                ; don't allocate protected memory

was00:
;       MMIOPH Performance Hook Removed
;       Maj/Min = 2461H, Data = bx (size)

        FlatContext <ds,es>, si
        CALLF32 selAllocSeg

;       MMIOPH Performance Hook Removed
;       Maj/Min = 24E1H, Data = ax (selector / error code)

        ret
EndProc w_AllocProtSeg


;***EP  w_ReallocSeg - realloc a segment
;
;       This function is called to realloc 16 bit unshared segments and
;       shared segments.  It will fail on CS aliases and huge
;       segments.
;
;       ENTRY   (AX) = selector
;               (BX) = size in bytes (0 = 64K)
;       EXIT    Carry clear on success
;               Carry set on error
;                   (AX) = error code
;       USES    EAX, EBX, ECX, EDX, EDI, ESI, DS, ES, EFlags
;
;       Pseudocode:
;
;       Call selReallocSeg
;       return

Procedure w_ReallocSeg,hybrid
    assume ds:nothing, es:nothing, ss:nothing, fs:nothing, gs:nothing

;       MMIOPH Performance Hook Removed
;       Maj/Min = 2464H, Data = ax, bx (selector, size)

        FlatContext     <ds, es>, dx
        CALLF32 selReallocSeg

;       MMIOPH Performance Hook Removed
;       Maj/Min = 24E4H, Data = ax (return code)

        ret
EndProc w_ReallocSeg


;***EP  w_FreeSeg - free a segment
;
;       This function is called to free unshared segments, shared
;       segments, huge segments, and CS aliases, and local aliases.
;       When called with an alias selector, it will simply unmap
;       the selector from the segment.
;       WARNING: This procedure must not block between the return from
;           SELGetOd call to VMFreeMemOd call. Otherwise the information
;           in object buffer (vmoi_s) may become invalid.
;
;       ENTRY   (AX) = selector
;               (BX) = flags (No longer used)
;               (DX) = ptda handle (No longer used, current ptda assumed)
;       EXIT    Carry set
;                   (AX) = error code
;               Carry clear
;                   no error
;       USES    EAX, EBX, ECX, EDX, EDI, ESI, DS, ES, EFlags
;
;       PseudoCode
;
;       If (not LDT selector) return(ERROR_ACCESS_DENIED)
;       Call    SELGetOd(don't take sem)
;       If (external request && Ring 0 sel) return (ERROR_ACCESS_DENIED)
;       If (UVIRT  & Ring 0 || infoseg)
;           return (ERROR_ACCESS_DENIED)
;       Endif
;       Call VMFreeMemOd
;       return

SEL_FREEFLAGS equ (VMGS_SLOCKWAIT or VMGS_VERIFY)

Procedure w_FreeSeg,hybrid
    assume ds:nothing,es:nothing,ss:nothing,fs:nothing,gs:nothing

    LocalVar wfs_sel,WORD
    LocalVar wfs_seldesc,<size seldesc_s>
    LocalVar wfs_od,<size vmod_s>

        TaskTime

        EnterProc <ax>              ; selector

;       MMIOPH Performance Hook Removed
;       Maj/Min = 2465H, Data = ax (selector)

        FlatContext     <ds, es>, cx
        test    ax,SEL_LDTBIT           ; must be LDT selector
        jz      short wfs20

; KDH - Ignore frees on packed segments.
;       We have to check both "fences" to determine whether a segment is
;       packed or not.
;       Here we also assume that the Infoseg segments are never freed.
;

        mov     ebx,[_pPTDACur]
        cmp     ax,[ebx].ptda_infoseg.LIS_PackSel
        jb      wfs26                           ; jump if packed
        cmp     _VMProtectedMem,0               ; Is protected region enabled?
        je      wfs06                           ; If not, continue at wfs07
        cmp     ax,0dfffh                       ; Selector in protected region?
        ja      wfs05                           ; If so, try to free it.
wfs06:  cmp     ax,[ebx].ptda_infoseg.LIS_PackShrSel
        ja      wfs26                           ; jump if packed

    ; _SELGetOd (fl, sel, &seldesc_s, &vmod_s)

wfs05:  SSToDS  ebx, wfs_od
        SSToDS  ecx, wfs_seldesc
        xor     edx, edx
        mov     wfs_od.od_pPTDA,edx
        movzx   edi, [wfs_sel]
        CCALL32 _SELGetOd <SEL_FREEFLAGS, edi, ecx, ebx>
        or      eax, eax
        .errnz  NO_ERROR
        jnz     short wfs10             ; change error code
                                        ; for v1.1, v1.2 compatibility

        mov     cl, [wfs_seldesc.desc_acc]   ; (cl) = access
        test    cl, D_SEG
        jz      short wfs20         ; Don't allow if not segment

        test    cl, D_PRIV
        jnz     short wfs30         ; Don't allow if Ring 0 in external req.
        jmp     short wfs20

wfs10:  cmp     ax, ERROR_INTERRUPT
        jz      short wfs25

wfs20:  mov     eax, ERROR_ACCESS_DENIED
wfs25:  stc
        jmp     short wfsx

wfs26:  xor     ax,ax
        jmp     wfsx

wfs30:  and     di, SEL_RPLCLR              ; di = selector with RPL bits clear
        cmp     di, SEL_TASKINFOSEG         ; infoseg ?
        jz      short wfs20                 ; don't let them free infoseg

        cmp     di, SEL_SYSINFOSEG          ; global infoseg?
        jz      short wfs20                 ; don't let them free sys infoseg

;   VMFreeMemOd checks for long term locks. No need to do it here.

        mov     ecx, [wfs_od.od_laddr]      ; don't allow free if not base
        cmp     ecx, [wfs_seldesc.desc_laddr]
        jnz     short   wfs20

    ;  _VMFreeMemOd(fl, pod)
    ;  (ebx) = Flat address of wfs_oi

wfs60:  CCALL32 _VMFreeMemOd <VMFM_OD, ebx>
        or      eax, eax
        .errnz  NO_ERROR                ; clears carry
        jnz     short wfs25

wfsx:   LeaveProc
        ret
EndProc w_FreeSeg


;***EP  w_GiveSeg - Give access to a sharable segment to another task.
;**     w_GiveSeg2 - Give access to a sharable segment to another task.
;
;       This function is called to allow another task access to
;       a sharable segment.
;
;       ENTRY   (AX) = Selector
;               (BX) = ProcessID        (w_GiveSeg)
;               (DX) = PTDA handle      (w_GiveSeg2)
;       EXIT    Carry clear on success
;                   (AX) = selector
;               Carry set on error
;                   (AX) = error code
;       USES    EAX, EBX, ECX, EDX, EDI, ESI, DS, ES, EFlags
;
;       PseudoCode:
;
;       Call selGiveSeg
;       return

Procedure w_GiveSeg2,faronly            ; w_GiveSeg2 is also faronly!!
    assume ds:nothing, es:nothing, ss:nothing, fs:nothing, gs:nothing

;   ptda handle is given
        mov     bx, dx
        mov     dx, SEL_GS_HOBPTDA
        jmp     short wgi00

Entry   w_GiveSeg,faronly               ; w_GiveSeg is also faronly!!

        xor     dx, dx
wgi00:

;       MMIOPH Performance Hook Removed
;       Maj/Min = 2469H, Data = ax, bx, dx (selector, Pid, PTDA handle)

        push    eax
        FlatContext <ds, es>, cx
        CCALL32 _selGiveSeg <edx, eax, ebx>
        or      eax,eax
        jnz     wgier
        pop     eax

;       MMIOPH Performance Hook Removed
;       Maj/Min = 24E9H, Data = si (recipient selector handle)

wgix:   ret

wgier:  add     sp,4
        stc
        jmp     short wgix
EndProc w_GiveSeg2


;***EP  w_GetSeg - Get access to a sharable segment.
;
;       This function is called to allow this task access
;       to other task's sharable segments.
;
;       ENTRY   (AX) = Selector
;       EXIT    Carry clear on success
;                   (AX) = selector
;               Carry set on error
;                   (AX) = error code
;       USES    EAX, EBX, ECX, EDX, EDI, ESI, DS, ES. EFlags
;
;       PseudoCode:
;
;       Call selGetSeg
;       return

Procedure w_GetSeg,faronly
    assume ds:nothing, es:nothing, ss:nothing, fs:nothing, gs:nothing

        FlatContext  <ds, es>, cx
        push    eax
        movzx   eax,ax
        CCALL32 _selGetSeg <eax>
        or      ax,ax
        jnz     wgser
        pop     eax
wgsx:   ret

wgser:  add     sp,4
        stc
        jmp     short wgsx
EndProc w_GetSeg


;***EP  w_LockSeg - Discard-lock a segment.
;
;       This function is called to lock a segment
;       to keep it from being discarded.
;
;       ENTRY   (AX) = Selector
;       EXIT    Carry clear on success
;               Carry set on error
;                   (AX) = error code
;       USES    EAX, EBX, ECX, EDX, EDI, ESI, DS, ES, EFlags
;
;       PseudoCode:
;
;       call selLockSeg

Procedure w_LockSeg,faronly
    assume ds:nothing, es:nothing, ss:nothing, fs:nothing, gs:nothing
        MMPHPreDOSLockSeg                       ; Performance Hook (24/13)

        FlatContext     <ds, es>, bx
        CALLF32 selLockSeg
        MMPHPostDOSLockSeg                      ; Performance Hook (24/93)
        ret
EndProc w_LockSeg


;***EP  w_UnlockSeg - Discard-unlock a segment.
;
;       This function is called to lock a segment
;       to keep it from being discarded.
;
;       ENTRY   (AX) = Selector
;       EXIT    Carry clear on success
;               Carry set on error
;                   (AX) = error code
;       USES    EAX, EBX, ECX, EDX, EDI, ESI, DS, ES, EFlags
;
;       PseudoCode:
;
;       call selUnlockSeg

Procedure  w_UnlockSeg,faronly
    assume ds:nothing, es:nothing, ss:nothing, fs:nothing, gs:nothing
        MMPHPreDOSUnLockSeg                     ; Performance Hook (24/1A)

        FlatContext     <ds, es>, bx
        CALLF32 selUnlockSeg
        MMPHPostDOSUnLockSeg                    ; Performance Hook (24/9A)
        ret
EndProc w_UnlockSeg


;***EP  w_AllocHuge - Allocate a huge memory segment
;***    w_AllocProtHuge - Allocate a huge memory segment in protected memory
;
;       w_AllocHuge allocates a huge memory segment. We allocate
;       a sparse object for the maximum size, and then validate the initial
;       requested size. Selectors are set up to show the requested size.
;       w_AllocProtHuge does the same except the memory is allocated from
;       the protected region of the shared arena if shared memory is requested.
;
;       ENTRY   (AX) = NumSeg - number of 64k segments to allocate.
;               (BX) = Size - size of the last segment to allocate.
;               (CX) = MaxNumSeg - the maximum number of segments ever
;                   to be required by a w_ReallocHuge call (or 0).
;               (DX) = allocation flag bits:
;                   0: private segment
;                   1: GIVESEG segment
;                   2: GETSEG segment
;                   4: discardable segment
;                   8: shrinkable segment
;       EXIT    Carry Flag clear
;                   (AX) = Selector - the base selector of the newly
;                               allocated huge segment.
;               Carry Flag set
;                   (AX) = error code
;       USES    EAX, EBX, ECX, EDX, ESI, EDI, DS, ES, EFlags
;
;       Pseudocode:
;
;       Call selAllocHuge
;       return

Procedure w_AllocProtHuge,faronly
    assume ds:nothing, es:nothing, ss:nothing, fs:nothing, gs:nothing

        mov     esi, VMAC_PROT          ; allocate protected memory (if shared)
        jmp     short wah00

Entry   w_AllocHuge,faronly
        xor     esi, esi                ; don't allocate protected memory

wah00:

;       MMIOPH Performance Hook Removed
;       Maj/Min = 2466H, Data = bx, ax, (size, segments to allocate)

        FlatContext <ds,es>, di
        CALLF32 selAllocHuge

;       MMIOPH Performance Hook Removed
;       Maj/Min = 24E6H, Data = ax (selector)

        ret
EndProc w_AllocProtHuge


;***EP  w_ReallocHuge - Reallocate a huge memory segment
;
;       w_ReallocHuge changes the size of a huge memory segment.
;
;       ENTRY   (AX) = NumSeg - number of 64k segments desired.
;               (BX) = Size - the size of the last segment desired.
;               (CX) = Selector - huge base selector.
;       EXIT    Carry Flag clear
;                   NONE
;               Carry Flag set
;                   (AX) = error code
;       USES    AX, BX, CX, DX, SI, DI, DS, ES, Flags
;
;       PseudoCode:
;
;       Call selReallocHuge
;       return

Procedure w_ReallocHuge,faronly
    assume ds:nothing, es:nothing, ss:nothing, fs:nothing, gs:nothing

;       MMIOPH Performance Hook Removed
;       Maj/Min = 2467H, Data = cx, bx, ax (selector, size, segments to realloc)

        FlatContext  <ds, es>, dx
        CALLF32 selReallocHuge

;       MMIOPH Performance Hook Removed
;       Maj/Min = 24E7H, Data = ax (return code)

        ret
EndProc w_ReallocHuge


;***EP  w_GetHugeShift - Get huge model selector increment shift count
;
;       w_GetHugeShift returns the huge model selector increment shift
;       count.  The huge selector increment is computed by shifting
;       one left by the w_GetHugeShift return value.  This routine
;       is called by the DosGetHugeShift API.  No error is possible.
;
;       ENTRY   NONE
;       EXIT    Carry Flag clear
;               (AX) = Increment Shift Count
;       USES    AX

Procedure w_GetHugeShift,faronly
    assume ds:nothing, es:nothing, ss:nothing, fs:nothing, gs:nothing
        MMPHPreDOSGetHugeShift                  ; Performance Hook (24/0D)
        TaskTime
        mov     ax,HUGESHIFT
        clc
        MMPHPostDOSGetHugeShift                 ; Performance Hook (24/8D)
        ret
EndProc w_GetHugeShift


;***EP  w_CreateCSAlias - Create 16 bit code selector to
;               access a data segment
;
;       This function allocates a selector in the LDT, and sets it up
;       so that it can be used as 16 bit code selector for the
;       given data segment. The value of the 16 bit code selector
;       is returned.
;
;       ENTRY   (AX) = Data Selector to be aliased.
;       EXIT    Carry clear on success
;                   (AX) = 16 bit code Selector
;               Carry set on error
;                   (AX) = Error code
;       USES    EAX, EBX, ECX, EDX, ESI, EDI, DS, ES, EFlags
;
;       PseudoCode:
;
;       Call selCreateCSAlias
;       return

Procedure w_CreateCSAlias,faronly
    assume ds:nothing, es:nothing, ss:nothing, fs:nothing, gs:nothing

        FlatContext <ds, es>, cx
        CALLF32 selCreateCSAlias
        ret
EndProc w_CreateCSAlias


;***EP  w_MemAvail - return size of free memory
;
;       This function returns the amount of free memory in the system at the
;       time of the call.  This is only a snapshot, and can be expected to
;       change.
;
;       ENTRY   None
;       EXIT    DX:AX = size in bytes of free memory
;
;       USES    EAX, EBX, ECX, EDX, ESI, EDI, EBP, DS, ES

; @DB73108 - fix for defect 68302 had an adverse affect on this function when
;            swapping was enabled. Since this fix mearly was trying to make the
;            number more reasonable when swapping is disabled, I'm going to
;            restore the old algorithm and reduce the returned value by the
;            values that the original fix used in the case of no swapping.

Procedure w_MemAvail,faronly
        MMPHPreDOSMemAvail              ; Performance Hook (24/16)
        FlatContext <ds, es>, ax

;@DB73108 - Start of Insertion
        cmp     _SMswapping,0           ; if not swapping
        jne     short wmavsw

        mov     eax,_pgPhysPages        ; perform a calculation for available
        sub     eax,_pgResidentPages    ; memory, as per the defect #68302 fix
        jc      short wmavzr            ; which was removed for the swapping
        sub     eax,_pgSwappablePages   ; case.
        jc      short wmavzr
        sub     eax,_pgDiscardablePages
        jc      short wmavzr
        sub     eax,10
        jnc     short wmav1
wmavzr: xor     eax,eax
        jmp     short wmav1
;@DB73108 - End of insertion            ; otherwise -

wmavsw: CALLF32 _PGPhysAvail            ; (eax) = amount of free memory
        cmp     eax,524288              ; if less than 512K physical avail
        jb      short wmav3             ;    attempt to return 512K
wmav1:  mov     edx, eax                ; move value into dx:ax
        shr     edx, 16
        jmp     short wmav6

wmav2:  pop     eax                     ; restore physical space
        sub     edx,_smDFMac            ; current swapper size - last page used
        jle     wmav1                   ; return physical if no swap space
        shr     eax,12                  ; physical pages
        cmp     ax,dx                   ; if more physical than swap space
        jae     wmav4                   ;    go return physical size
        mov     ax,dx                   ;
        jmp     wmav5                   ; go return swap size

wmav3:  push    eax                     ; save physical available
        sub     esp,4                   ; temp allocate a dword
        mov     eax,esp
        SSToDS  eax                     ; create Flat pointer
                                        ; pointer to potential swapper size
        CCALL32 _smSIOSpaceQuery,<eax>  ;
        pop     eax                     ; potential swapper size (pages)
        mov     edx,_smFileSize         ; current swapper size (pages)
        sub     eax,edx                 ; compute pages available to grow
        cmp     eax,256                 ; if available swapper space < 1 MB
        jb      wmav2                   ;    use available swapper space

        add     esp,4                   ; discard physical available value
        mov     ax,128                  ; limit to 512K
wmav4:  mov     dx,ax                   ; dupe value into dx
wmav5:  shr     dx,4                    ; set dx:ax to size in bytes (512K max)
        shl     ax,12                   ;

wmav6:  clc
        MMPHPostDOSMemAvail             ; Performance Hook (24/96)
        ret
EndProc w_MemAvail

;***EP  w_SizeSeg - return size of a segment.
;
;       This function returns the size of an allocated segment.
;
;       ENTRY   (AX) = Selector
;       EXIT    Carry clear on success
;                   (DX:AX) = size of segment in bytes
;                   (CX) = max number of selectors (>1 if huge)
;               Carry set on error
;                   (AX) = error code
;       USES    EAX, EBX, ECX, EDX, EDI, ESI, DS, ES

Procedure w_SizeSeg,faronly
    assume ds:nothing, es:nothing, ss:nothing, fs:nothing, gs:nothing
    LocalVar    wss_desc,<size seldesc_s>
;;; LocalVar    wss_oi,<size vmoi_s>
    LocalVar    wss_od,<size vmod_s>
    LocalVar    wss_cb,DWORD                ; size

        TaskTime
        EnterProc

        MMPHPreDOSSizeSeg                   ; Peformance Hook (24/19)

        test    ax,SEL_LDTBIT           ; must be LDT selector
        mov     bx, ax                  ; (bx) = selector
        jz      short wsse1

        FlatContext <ds, es>, cx

;       _SELGetOd(fl, sel, pdesc, pod)

;;;;;;; SSToDS  ecx, ss:wss_oi
        SSToDS  ecx, ss:wss_od
        mov     eax, _pPTDACur
        mov     [ecx].od_pPTDA, eax
        SSToDS  edx, ss:wss_desc
;;;;;;; xor     edi, edi
;;;;;;; CCALL32 _SELObjectInfo <dword ptr VMGS_VERIFY, eax, edi, edx, ecx>
        CCALL32 _SELGetOd <dword ptr VMGS_VERIFY, ebx, edx, ecx>
        or      eax, eax
        jnz     short wsse

        test    [wss_desc].desc_acc,D_DPL2  ; sufficient to test for ring 2/3
        .errnz  (D_DPL2 and (not 040h))
        .errnz  (D_DPL3 and (not 060h))
        jnz     short wss30             ; reject DPL 0 and 1 segments

wsse:   cmp     ax, ERROR_INTERRUPT     ; don't change ERROR_INTERUPT
        jz      short wsse2
wsse1:
        mov     ax,ERROR_ACCESS_DENIED  ; return error code if no size
wsse2:  stc
        jmp     short wssx

wss30:  mov     edx, [wss_desc].desc_cb
        mov     cx, 1                           ; assume one selector
;;;;;;; cmp     [wss_oi].oi_cb, WORDMASK + 1    ; is size < 64Kb?, clears carry
        mov     eax, [wss_od].od_par
        mov     eax, dword ptr [eax].ar_union5
        shl     eax, PAGESHIFT
        cmp     eax, WORDMASK + 1               ; is size < 64Kb?, clears carry
        jbe     short wss90                     ; if so all done

;   size is larger than 64Kb, make sure selector is the base one, and
;   call _SELGetSize(laddr, sel, hptda, cbmax, pcb)
;   (bx)  = selector
;   (eax) = max size

        mov     edi, eax
;;;;;;; mov     eax, [wss_desc].desc_laddr  ; descriptor's base address
;;;;;;; cmp     eax, [wss_oi].oi_va         ; is it same as object's base?
        mov     eax, [wss_desc].desc_laddr  ; descriptor's base address
        cmp     eax, [wss_od].od_laddr      ; is it same as object's base?
        jz      short wss80                 ; continue if true  @@b730221

;;;;;;; test    [wss_oi].oi_fsob, OB_HUGE   ; was object allocated by AllocHuge @@b730221
        mov     ecx, [wss_od].od_pob
        test    [ecx].ob_fs, OB_HUGE        ; was object allocated by AllocHuge @@b730221
        jnz     short wsse1                 ; reject if so @@b730221

wss80:
        SSToDS  ecx, ss:wss_cb
        xor     edx, edx
;;;;;;; CCALL32 _SELGetSize <eax, ebx, edi, [wss_oi].oi_cb, ecx>
        CCALL32 _SELGetSize <eax, ebx, edx, edi, ecx>
        or      eax, eax
        .errnz  NO_ERROR
        jnz     short wsse

;   all OK, return info

;;;;;;; mov     ecx, [wss_oi].oi_cb         ; max size
        mov     ecx, edi                    ; max size
        shr     ecx, WORDSHIFT              ; cx = max no of selectors
        mov     edx, [wss_cb]               ; current byte granular size
wss90:  mov     eax, edx
        shr     edx, WORDSHIFT              ; (dx:ax) = size
        clc

wssx:   MMPHPostDOSSizeSeg                  ; Peformance Hook (24/99)
        LeaveProc
        ret
EndProc w_SizeSeg

HIGH3CODE ends


        FARCODE HIGH32
SWAP32_13 segment


;***LP  selAllocSeg - allocate a data segment
;
;       selAllocSeg allocates a private or unnamed shared data segment in
;       the 286 chip, 16 bit format.
;
;       ENTRY   (AX) = allocation flag bits:
;                   0: private segment
;                   1: GIVESEG segment
;                   2: GETSEG segment
;                   4: discardable segment
;                   8: shrinkable segment
;               (BX) = size in bytes (0 = 64K)
;              (EDX) = VMAC_PROT if allocate from protected region, 0 otherwise
;       EXIT    Carry clear
;                   (AX) = selector
;               Carry set
;                   (AX) = error code
;       USES    EAX, EBX, ECX, EDX, EDI, ESI, EFlags
;
;
;       PseudoCode:
;
;       Turn off higest bit in input flags
;       If(inputflags and not WAS_FLAGSMASK)
;           return ERROR_INVALID_PARAMETER
;       Endif
;       If (size == 0), set size = 64K
;       Call VMAllocMem(SEL_64K, Sel Size, [VMALLOC FLAGS],
;           HOBPTDA, HOBOWNER, HOBMTE, [SELMGR FLAGS], BLKNO, RETURN_BUFFER);
;       return(selector)
;
Procedure selAllocSeg,far32,local
    assume ds:FLAT, es:FLAT, ss:nothing, fs:nothing, gs:nothing
    LocalVar    was_prot,DWORD
    LocalVar    was_ac,<size vmac_s>
    LocalVar    was_callermte,DWORD

        EnterProc       <edx>           ; protected memory flag
        TaskTime

        and     ax,not 8000h            ; ignore 8000h bit
        mov     si,ax                   ; save flags
        test    ax,not WAS_FLAGSMASK
        mov     eax,ERROR_INVALID_PARAMETER
        jnz     was80                   ; jmp if bad share indicator

        mov     edi,[_pTCBCur]
        mov     edi,[edi].TCB_pFrameBase
        movzx   eax,[edi].ssf_cs
        CCALL32 _VMGetOwner <eax, [edi].ssf_eip>
        mov     [was_callermte],eax

        .errnz  ((D_DPL3 shr SELAL_PRIVSHIFT) - 3)
        mov     edx, SELAL_SELMAP or SELAL_W or D_DPL3  ; VMAlloc flSelMgr flags

        test    si,WAS_DISCARDABLE      ; is discardable seg request ?
        jz      short was10             ; jmp if no!
        or      edx,SELAL_DISCSEG

was10:
        mov     cx, si                                  ; (DI) = flags
        and     cx, WAS_GIVESEG+WAS_GETSEG              ;
        jnz     short was20                             ; jmp if shared

    ; private allocation

        mov     eax, WAS_VM1 or WAS_PG or VMAC_ARENAPRV ; VMAlloc flags
        mov     ecx, [_pPTDACur]
        movzx   ecx, [ecx].ptda_handle                  ; hobowner
        jmp     short was30

was20:
    ; shared allocation

        mov     eax, WAS_VM1 or WAS_PG or VMAC_ARENASHR or VMAC_SHARED
        or      eax, [was_prot]         ; get protection flag
        or      ecx, SHRINDRSVD         ; owner = shared indicator
        .errnz  (SHRINDRSVD + WAS_GIVESEG - GIVESEGOWNER)
        .errnz  (SHRINDRSVD + WAS_GETSEG - GETSEGOWNER)
        .errnz  (SHRINDRSVD + WAS_GIVESEG + WAS_GETSEG - SHRDOWNER)

was30:
        test    si, WAS_SHRINKABLE      ; is shrinkable ?
        jz      short was40             ; jmp if no!
        or      eax, VMAC_OBSHRINK

was40:
        movzx   ebx, bx
        or      ebx, ebx                ; size = 64Kb ?
        jnz     short was50             ; jmp if less
        mov     ebx, SEL_64K

was50:
        SSToDS  edi, was_ac             ; Flat address of was_ac

    ; _VMAllocMem(cb, cbcommit, fl, hobptda, hobowner, hobmte, flsel, blkno, pac)

        CCALL32 _VMAllocMem <SEL_64K, ebx, eax, 0, ecx, [was_callermte], \
                    edx, 0, edi>

was80:
        or      eax, eax                ; successful ???
        stc
        jnz     short wasx
        mov     ax, [was_ac].ac_sel     ; ax = selector
        clc

wasx:
        LeaveProc
        RETC

EndProc selAllocSeg

SWAP32_13 ENDS
SWAP32_8 segment

;***LP  selReallocSeg - realloc a segment
;
;       This function is called to realloc 16 bit unshared segments and
;       shared segments.  It will fail on CS aliases and huge
;       segments.
;
;       ENTRY   (AX) = selector
;               (BX) = size in bytes (0 = 64K)
;       EXIT    Carry clear on success
;               Carry set on error
;                   (AX) = error code
;       USES    EAX, EBX, ECX, EDX, EDI, ESI, EFlags
;
;       Pseudocode:
;
;       If (GDT elector), return (ERROR_ACCESS_DENIED)
;       Call SELGetOd(sel)
;       If (error), return (error code)
;       if(ob_flags == VMOB_DISC_SEG)        ; if a discardable segment
;          if(KSEMRequestExclusive(Discardable Seg Sem4) == ERROR)
;              return (error code)
;          endif
;       endif
;       If (not segment desc.), return ERROR_INVALID_PARAMETERS
;       If (D_CODE || ring 0 data || D_BIG || D_UVIRT)
;           rc = ERROR_ACCESS_DENIED
;           Jump exit
;       If (an alias || sel addres != object address || maxsize > 64K)
;           rc = ERROR_INVALID_PARAMETER
;           Jump exit
;       Endif
;       If (long term locks)
;           rc = ERROR_LOCKED
;           Jump exit
;       Endif
;       If (huge)
;           rc = ERROR_ACCESS_DENIED
;           Jump exit
;       Endif
;       If (newsize == 0), set newsize = 64K
;       If (OB_SHARED && not OB_SHRINKABLE && newsize < cursize)
;           rc = ERROR_ACCESS_DENIED
;           Jump exit
;       Endif
;       if ( (rc = selReallocSub()) == NO_ERROR)
;          if(ob_flags == VMOB_DISC_SEG)        ; if a discardable segment
;              if(Rmp_Find(hob) == ERROR)  ; Find rmp record using hob as key
;                  Panic
;              endif
;              if(softlocks < 0xff)        ; if not locked forever
;                  softlocks += 1;
;              endif
;          endif
;       endif
;exit:  if(ob_flags == VMOB_DISC_SEG)        ; if a discardable segment
;          Release semaphore
;       endif
;       Call VMClearSem
;       return( rc )
;

SEL_REALLOCFLAGS equ (VMGS_TAKESEM or VMGS_SLOCKWAIT or VMGS_VERIFY)

Procedure selReallocSeg,far32,local
    assume ds:FLAT, es:FLAT, ss:nothing, fs:nothing, gs:nothing
    LocalVar wrs_sel,WORD
    LocalVar wrs_newsize,DWORD
    LocalVar wrs_seldesc,<size seldesc_s>
;;; LocalVar wrs_oi,<size vmoi_s>
    LocalVar wrs_od,<size vmod_s>
    LocalVar wrs_hob,WORD

        TaskTime

        xor     cx,cx                   ; (cx:bx) = 32 bit size
        or      bx,bx
        jnz     short wrs10             ; if (bx) was 0

        inc     cx                      ;  (cx:bx) = 64k
wrs10:  EnterProc <ax,cx,bx>            ; sel, newsize

        test    ax,SEL_LDTBIT           ; must be LDT selector
        mov     eax, ERROR_ACCESS_DENIED
        stc
        jz      wrse

        CCALL32 _KSEMRequestExclusive, <[_vmNamShrSem],-1>
        or      eax, eax
        stc
        jnz     wrse

    ; _SELGetOd (fl, sel, &seldesc_s, &vmod_s)

        mov     ebx, [_TKSSBase]
;;;;;;; lea     eax, ss:wrs_oi
        lea     eax, ss:wrs_od
        add     eax, ebx                ; eax = flat address of wrs_oi
        mov     ecx, _pPTDACur
        mov     [eax].od_pPTDA, ecx
        lea     ecx, ss:wrs_seldesc
        add     ecx, ebx                ; eax = flat address of wrs_seldesc
;;;;;;; xor     edx, edx
        movzx   edi, [wrs_sel]
;;;;;;; CCALL32 _SELObjectInfo <dword ptr SEL_REALLOCFLAGS, edi, edx, ecx, eax>
        CCALL32 _SELGetOd <dword ptr SEL_REALLOCFLAGS, edi, ecx, eax>
        cmp     ax, ERROR_INTERRUPT     ; save error code if ERROR_INTERRUPT
        jz      wrs90

        or      eax, eax
        .errnz  NO_ERROR
        mov     ax, ERROR_ACCESS_DENIED ; change error code for v1.1 v1.2
                                        ; compatibility
        jnz     wrs90

        mov     esi, [wrs_od].od_pob
        vmPobToHob esi
        mov     [wrs_hob], ax

;;;;;;; test    [wrs_oi.oi_xflags],VMOB_DISC_SEG  ; check if discardable seg
        test    [esi].ob_xflags, VMOB_DISC_SEG    ; check if discardable seg
        jz      short wrs15                       ; jmp if no

    ; Obtain discarable segment sem4
    ; KSEMRequestExclusive(sem handle, timeout)

        CCALL32 _KSEMRequestExclusive,<[_SELDiscSegRmpSem],-1>
        or      eax, eax
        jz      short wrs15             ; jmp if no error
        push    eax                     ; save error code
        jmp     wrs77

    ; Don't let realloc of Infosegs

wrs15:
        mov     ax, ERROR_ACCESS_DENIED ; change error code for v1.1 v1.2
        cmp     [wrs_seldesc.desc_laddr], SEL_TASKINFOLADDR
        jz      short   wrs20

        cmp     [wrs_seldesc.desc_laddr], SEL_SYSINFOLADDR
        jz      short   wrs20

        test    [wrs_seldesc.desc_attr], (D_UVIRT or D_DBIG)
        jnz     short wrs30             ; Don't allow if UVIRT or BIG default

        mov     cl, [wrs_seldesc.desc_acc]   ; (cl) = access
        test    cl, D_SEG
        jz      short wrs20             ; Don't allow if not segment

        test    cl, D_CODE
        jnz     short wrs30             ; Don't allow if code

        test    cl, D_PRIV
wrs20:  jz      wrs75                   ; Don't allow if Ring 0

        mov     ax, ERROR_LOCKED
;;;;;;; cmp     [wrs_oi.oi_cllock], 0
        cmp     [esi].ob_cllock, 0
        jnz     wrs75

        mov     ax, ERROR_INVALID_PARAMETER
        mov     ecx, [wrs_seldesc.desc_laddr]
;;;;;;; cmp     ecx, [wrs_oi.oi_va]
        cmp     ecx, [wrs_od].od_laddr
        jnz     wrs75                   ; OK if (selector = starting address)

;;;;;;; cmp     [wrs_oi.oi_cb], (WORDMASK + 1)  ; Max size must be 64K
        mov     ecx, [wrs_od].od_par
        mov     edx, dword ptr [ecx].ar_union5
        shl     edx, 12
        cmp     edx, (WORDMASK + 1)     ; Max size must be 64K
        jnz     wrs75

;;;;;;; cmp     [wrs_oi.oi_hal], 0
        cmp     [ecx].ar_hal, 0
        jnz     wrs75                   ; Must not be alias

        mov     ax, ERROR_ACCESS_DENIED
;;;;;;; mov     dx, [wrs_oi].oi_fsob
        mov     dx, [esi].ob_fs
        test    dx, OB_HUGE or OB_UVIRT
wrs30:  jnz     wrs75                   ; Must not be huge or uvirt

        test    dx, OB_SHARED
        jz      short wrs50             ; skip if not shared

        test    dx, OB_SHRINKABLE
        jnz     short wrs50             ; OK if shrinkable shared

        mov     edx, [wrs_newsize]
        cmp     edx, [wrs_seldesc.desc_cb]
        jb      wrs75                   ; Don't allow if shrink request

wrs50:
    ; All is OK, Now go ahead with realloc.
    ;   _selReallocSub(cbnew, cbcur, pod)
    ; (edi) = selector
    ; (ebx) = [_TKSSBase]

;;;;;;; lea     ecx, ss:wrs_oi
        lea     ecx, ss:wrs_od
        add     ecx, ebx                ; flat address of information buffer
        CCALL32 _selReallocSub <[wrs_newsize], [wrs_seldesc].desc_cb, ecx>
        or      eax,eax                 ;
        jnz     wrs75                   ; jmp if un-succesful

        push    eax                               ; save return code
;;;;;;; test    [wrs_oi.oi_xflags],VMOB_DISC_SEG  ; check if discardable seg
        test    [esi].ob_xflags,VMOB_DISC_SEG    ; check if discardable seg
        jz      wrs77                             ; jmp if no

    ; Rmp_Find()
    ;     ds:si = Address of match pattern,
    ;        cx = length of match pattern,
    ;        bx = offset into the record,
    ;     es:di = address of handle to managed seg
    ; Returns:
    ;  CF clear = match found
    ;     es:di = start of found record

wrs60:
;;;;;;; push    [wrs_oi.oi_hob]             ; setup match pattern in stack
        push    [wrs_hob]
        push    ss
        pop     ds
        assume ds:nothing
        mov     si, sp                      ; ds:si points to match pattern
        mov     cx, 2                       ; length of match pattern
        mov     bx, offset hob              ; offset of match pattern
        DosContext es, di
        mov     di,offset DOSGROUP:hDiscSegRmpStruc
        CALL16  Rmp_Find                    ; Find the disc seg record
        pop     ax                          ; Clear stack 1 word
        jnc     short wrs65                 ; jmp if found

        Panic <'w_ReallocSeg: HOB not found'>

wrs65:  mov     al,es:[di].softlocks        ; get discard lock count
        inc     al                          ; increment discard lock count
        clc
        jz      short wrs70                 ; jmp if maxed out, return success
        mov     es:[di].softlocks,al        ; set discard lock count
wrs70:  FlatContext <ds, es>, ax
        jmp     short wrs76

wrs75:  push    eax                               ; save return/error code
;;;;;;; test    [wrs_oi.oi_xflags],VMOB_DISC_SEG  ; check if discardable seg
        test    [esi].ob_xflags,VMOB_DISC_SEG     ; check if discardable seg
        jz      short wrs77                       ; jmp if no
wrs76:
        CCALL32 _KSEMRelease,<[_SELDiscSegRmpSem]>
wrs77:
;;;;;;; movzx   eax, [wrs_oi.oi_hob]
        movzx   eax, [wrs_hob]
        CCALL32 _VMClearSem <eax>
        pop     eax                     ; restore error code
        or      eax, eax
        .errnz  NO_ERROR                ; clears carry
        jz      short wrsx

wrs90:  stc
wrsx:   pushf
        push    eax
        CCALL32 _KSEMRelease,<[_vmNamShrSem]>
        pop     eax
        popf
wrse:
        LeaveProc
        RETC
EndProc selReallocSeg

SWAP32_8 ENDS
SWAP32_16 segment

;***LP  _SELAddDiscRec - Add a discardable segments rmp record
;
;       _SELAddDiscRec adds a discardable segments rmp record which is
;       used to contain the segments soft lock count.
;
;       ENTRY   NONE
;
;       EXIT    AX = 0, ES:DI = Address of discardable segment rmp record
;               AX = Error Code
;
;       USES    EAX, ECX, EDX, Flags
;
Procedure _SELAddDiscRec,near
    assume ds:FLAT, es:FLAT , fs:nothing, gs:nothing, ss:nothing

        ArgVar  adr_hob,WORD
        EnterProc
        SaveReg <ebx, esi, edi>
    ;
    ; Obtain discarable segment sem4
    ; KSEMRequestExclusive(sem handle, timeout)
    ;

        CCALL32 _KSEMRequestExclusive,<[_SELDiscSegRmpSem],-1>
        or      eax, eax
        jnz     short adr20             ; jmp if error

    ;
    ; Rmp_Add()
    ;     ds:si = Address of structure to add,
    ;        cx = length of item to add,
    ;     es:di = address of handle to managed seg
    ; Returns:
    ;  CF clear = record added
    ;     es:di = address of added record
    ;

        DosContext <ds, es>, ax
        mov     cx,size DiscSegRmpRec                ; record size
        mov     si,offset DOSGROUP:DiscSegRmpRec     ;
        mov     ax,adr_hob                           ;
        mov     [si].hob,ax                          ; set hob
        mov     [si].softlocks,1                     ; set soft lock count to 1
        mov     di,offset DOSGROUP:hDiscSegRmpStruc  ; handle to managed segment
        CALL16  Rmp_Add                              ; add record
        mov     eax,ERROR_NOT_ENOUGH_MEMORY          ; default rc
        jc      short adr10                          ; jmp if error
        xor     eax, eax                             ; zero rc
    ;
    ; Release discarable segment semaphore
    ; KSEMRelease(sem handle)
    ;

adr10:
        SaveReg <eax>                                ; save rc, record seg
        FlatContext <ds, es>, ax
        CCALL32 _KSEMRelease,<[_SELDiscSegRmpSem]>
        RestoreReg <eax>                             ; restore rc, record seg
adrx:
        RestoreReg <edi, esi, ebx>
        LeaveProc
        RETC

adr20:
        cmp     eax, ERROR_INTERRUPT    ; preserve ERROR_INTERRUPT
        jz      short adrx
        mov     eax, ERROR_NOT_ENOUGH_MEMORY ; all others out of memory
        jmp     short adrx

EndProc _SELAddDiscRec

SWAP32_16 ENDS
SWAP32_27  segment

;***LP  _SELRemDiscRec - remove a discardable segments rmp record
;
;       _SELRemDiscRec removes a discardable segments rmp record which is
;       used to contain the segments soft lock count.
;
;       ENTRY   hob - Object Handle of discardable segment whose rmp
;                     record is to be deleted. If hob == HOBNULL then
;                     es:di is the address of the record to remove.
;       EXIT    NONE (Panic if error)
;       USES    EAX, ECX, EDX, Flags, ES
;
;       PseudoCode:
;
;       ;Request exclusive ownership of the Discardable segments RMP sem4
;       while ( (rc = KSEMRequestExclusive(Discardable Seg Sem4))
;                   == ERROR_INTERUPT)
;       if(rc != NO_ERROR)
;         Panic
;       endif
;       if hob != HOBNULL
;         if(Rmp_Find(hob) == ERROR)       ; Find rmp record using hob as key
;           Panic
;         endif
;       endif
;       Rmp_Remove(found or passed record) ; Remove found record from rmp seg
;       Release semaphore
;
Procedure _SELRemDiscRec,near
    assume ds:FLAT, es:FLAT , fs:nothing, gs:nothing, ss:nothing

        ArgVar  rdr_hob,DWORD
        EnterProc

    ; Save registers for c calling conventions
        push    ebx
        push    esi
        push    edi

    ; Obtain discarable segment sem4
    ; KSEMRequestExclusive(sem handle, timeout)

        push    es                      ; save es
        FlatContext es, ax
rdr5:
        CCALL32 _KSEMRequestExclusive,<[_SELDiscSegRmpSem],-1>
        or      eax, eax
        jz      short rdr10             ; jmp if no error
        cmp     eax, ERROR_INTERRUPT
        jz      short rdr5              ; retry if ERROR_INTERRUPT

        Panic <'_SELRemDiscRec: Semaphore Error'>

rdr10:
        pop     es                      ; restore es

    ; Rmp_Find()
    ;     ds:si = Address of match pattern,
    ;        cx = length of match pattern,
    ;        bx = offset into the record,
    ;     es:di = address of handle to managed seg
    ; Returns:
    ;  CF clear = match found
    ;     es:di = start of found record

        cmp     rdr_hob, HOBNULL            ;
        je      short rdr70                 ; jump if hob == HOBNULL
        push    rdr_hob                     ; setup match pattern in stack
        push    ss
        pop     ds
        assume ds:nothing
        mov     si, sp                      ; ds:si points to match pattern
        mov     cx, 2                       ; length of match pattern
        mov     bx, offset hob              ; offset of match pattern
        DosContext es, di
        mov     di,offset DOSGROUP:hDiscSegRmpStruc
        CALL16  Rmp_Find                    ; Find the disc seg record
        pop     eax                         ; Clear stack 2 words!
        jnc     short rdr70                 ; jmp if found

        Panic <'_SELRemDiscRec: HOB not found'>

    ; Rmp_Rem()
    ;    es:di = Address of record to remove,
    ;    ds:bx = address of handle to managed segment

rdr70:
        DosContext ds, ax
        mov     bx,offset DOSGROUP:hDiscSegRmpStruc
        CALL16  Rmp_Rem                     ; remove disc seg rmp record
        FlatContext <ds, es>, ax
        CCALL32 _KSEMRelease,<[_SELDiscSegRmpSem]>

    ; Restore all registers
        pop     edi
        pop     esi
        pop     ebx

        LeaveProc
        RETC
EndProc _SELRemDiscRec

SWAP32_27 ENDS
LOAD_TUNEDIT1_SWP segment

;***LP  selUnlockSeg - Discard-unlock a segment
;
;       This function is called to unlock a segment
;       to allow it to be discarded.
;       We don't really have runtime allocated discardable segments any
;       more, so this routine is a bit of an anomoly.  We tried just
;       validating the selector and then returning no error if it was ok,
;       but we found that some apps did their unlocking by going into
;       a loop repeatedly unlocking a segment and waiting for an error
;       code to be returned (in order to undo the effect of multiple
;       locks).  To keep these apps from hanging we've added an RMP segment
;       to keep track of the soft lock count for each discardable segment,
;       using the segments hob as the key.
;
;       ENTRY   (AX) = selector
;       EXIT    Carry clear on success
;               Carry set on error
;                   (AX) = error code
;       USES    EAX, EBX, ECX, EDX, EDI, ESI, EFlags
;
;
;       Pseudo-code:
;
;       ;Request exclusive ownership of the Discardable segments RMP sem4
;       If(KSEMRequestExclusive(Discardable Seg Sem4) == ERROR)
;           Return ERROR_ACCESS_DENIED
;       Endif
;       Call selValidateLockSeg
;       If(Not a discardable segment)
;           Goto Exit
;       Else
;           If(Rmp_Find(hob) == ERROR) ; Find rmp record using hob as key
;               Panic
;           Endif
;           If(SoftLocks == 0xff)      ; Segment locked forever
;               Goto Exit
;           Endif
;           If(SoftLocks == 0)         ; Segment not locked
;               Release semaphore
;               Retun ERROR_NOT_LOCKED
;           Endif
;           SoftLocks = SoftLocks - 1  ; Set softlock count
;       Endif
;Exit:
;       Release semaphore
;       Return NO_ERROR
;
Procedure selUnlockSeg,far32,local
    assume ds:FLAT, es:FLAT, ss:nothing, fs:nothing, gs:nothing
    LocalVar wus_sel,WORD

        TaskTime
        EnterProc <ax>                      ; selector

    ; Obtain discarable segment sem4
    ; KSEMRequestExclusive(sem handle, timeout)

        CCALL32 _KSEMRequestExclusive,<[_SELDiscSegRmpSem],-1>
        or      eax, eax
        stc
        mov     ax, ERROR_ACCESS_DENIED
        jnz     wusx                        ; jmp if error

        mov     ax,[wus_sel]
        CALL32  selValidateLockSeg
        jc      wus90          ; (AX) = error code, or hob
        jz      wus80          ; Attempts to unlock non-disc segs are ignored

    ; Rmp_Find()
    ;     ds:si = Address of match pattern,
    ;        cx = length of match pattern,
    ;        bx = offset into the record,
    ;     es:di = address of handle to managed seg
    ; Returns:
    ;  CF clear = match found
    ;     es:di = start of found record

        push    ax                          ; setup match pattern in stack
        push    ss
        pop     ds
        assume ds:nothing
        mov     si, sp                      ; ds:si points to match pattern
        mov     cx, 2                       ; length of match pattern
        mov     bx, offset hob              ; offset of match pattern
        DosContext es, di
        mov     di,offset DOSGROUP:hDiscSegRmpStruc
        CALL16  Rmp_Find                    ; Find the disc seg record
        pop     ax                          ; Clear stack 1 word
        jnc     short wus70                 ; jmp if found

        Panic <'w_UnLockSeg: HOB not found'>

wus70:
        mov     bl,es:[di].softlocks        ; get discard lock count
        inc     bl                          ; increment discard lock count
        clc
        jz      short wus80                 ; jmp if maxed out,locked forever
        mov     ax, ERROR_NOT_LOCKED        ;
        sub     bl,2                        ; decrement discard lock count
        jc      short wus90                 ; jmp if not locked
        mov     es:[di].softlocks,bl        ; set discard lock count

wus80:  mov     ax, NO_ERROR
wus90:  pushf
        push    ax
        FlatContext <ds, es>, bx
        CCALL32 _KSEMRelease,<[_SELDiscSegRmpSem]>
        pop     ax
        popf
wusx:   LeaveProc
        RETC
EndProc selUnlockSeg


;***LP  selLockSeg - Discard-lock a segment.
;
;       This function is called to lock a segment
;       to keep it from being discarded.
;       (See selUnlockSeg for notes on discardable segments)
;
;       ENTRY   (AX) = selector
;       EXIT    Carry clear on success
;               Carry set on error
;                   (AX) = error code
;       USES    EAX, EBX, ECX, EDX, EDI, ESI, EFlags
;
;
;       Pseudo-code:
;
;       ;Request exclusive ownership of the Discardable segments RMP sem4
;       If(KSEMRequestExclusive(Discardable Seg Sem4) == ERROR)
;           Return ERROR_ACCESS_DENIED
;       Endif
;       Call selValidateLockSeg
;       If(Not a discardable segment)
;           Goto Exit
;       Else
;           If(Rmp_Find(hob) == ERROR) ; Find rmp record using hob as key
;               Panic
;           Endif
;           If(SoftLocks == 0xff)      ; Segment locked forever
;               Goto Exit
;           Endif
;           SoftLocks = SoftLocks + 1  ; Set softlock count
;       Endif
;Exit:
;       Release semaphore
;       Return NO_ERROR
;
Procedure selLockSeg,far32,local
    assume ds:FLAT, es:FLAT, ss:nothing, fs:nothing, gs:nothing
    LocalVar ls_sel,WORD

        TaskTime
        EnterProc <ax>                      ; selector

    ; Obtain discarable segment sem4
    ; KSEMRequestExclusive(sem handle, timeout)

        CCALL32 _KSEMRequestExclusive,<[_SELDiscSegRmpSem],-1>
        or      eax, eax
        stc
        mov     ax, ERROR_ACCESS_DENIED
        jnz     lsx                         ; jmp if error

        mov     ax,[ls_sel]
        CALL32  selValidateLockSeg
        jc      ls90         ; (AX) = error code, or hob
        jz      ls80         ; Attempts to lock non-disc segs are ignored

    ; Rmp_Find()
    ;     ds:si = Address of match pattern,
    ;        cx = length of match pattern,
    ;        bx = offset into the record,
    ;     es:di = address of handle to managed seg
    ; Returns:
    ;  CF clear = match found
    ;     es:di = start of found record

        push    ax                          ; setup match pattern in stack
        push    ss
        pop     ds
        assume ds:nothing
        mov     si, sp                      ; ds:si points to match pattern
        mov     cx, 2                       ; length of match pattern
        mov     bx, offset hob              ; offset of match pattern
        DosContext es, di
        mov     di,offset DOSGROUP:hDiscSegRmpStruc
        CALL16  Rmp_Find                    ; Find the disc seg record
        pop     ax                          ; Clear stack 1 word
        jnc     short ls70                  ; jmp if found

        Panic <'w_LockSeg: HOB not found'>

ls70:
        mov     al,es:[di].softlocks        ; get discard lock count
        inc     al                          ; increment discard lock count
        clc
        jz      short ls80                  ; jmp if maxed out, return success
        mov     es:[di].softlocks,al        ; set discard lock count

ls80:   mov     ax, NO_ERROR
ls90:   pushf
        push    ax
        FlatContext <ds, es>, bx
        CCALL32 _KSEMRelease,<[_SELDiscSegRmpSem]>
        pop     ax
        popf
lsx:    LeaveProc
        RETC
EndProc selLockSeg



;***LP  selValidateLockSeg - validate a segment for discard-lock
;
;       Validate a selector for discard lockability.
;       (See selUnlockSeg for notes on discardable segments)
;
;       For v1.1 and v1.2 compatibility, this procedure should return:
;       * NO_ERROR for local and global infosegs
;       * ERROR_ACCESS_DENIED for Ring 0 or 1 selectors.
;       * ERROR_DIRECT_ACCESS_HANDLE for R2 or 3 UVIRT selectors.
;       * ERROR_INVALID_PARAMETER for non base huge selectors.
;
;       ENTRY   (AX) = Selector
;       EXIT    Carry clear on success
;                   Zero Flag set means segment is not discardable
;                   Zero Flag clear means segment is discard lockable
;                       (AX) = HOB
;               Carry set on error
;                   (AX) = error code
;       USES    EAX, EBX, ECX, EDX, EDI, ESI, EFlags
;
;       PseudoCode:
;
;       SELGetDescInfo(selector)
;       If (not ring 2 or 3 data)
;           Return (ERROR_ACCESS_DENIED)
;       Endif
;       If (UVIRT selector)
;           Return (ERROR_DIRECT_ACCESS_HANDLE)
;       Endif
;       If (Huge selector and not base)
;           Return (ERROR_INVALID_PARAMETER)
;       Endif
;       If (Discardable segment)
;           Clear CF and ZF
;           Return (hob)
;       Else
;           Clear CF and set ZF
;           Return
;       Endif
;

Procedure selValidateLockSeg,near,local
    assume ds:FLAT, es:FLAT, ss:nothing, fs:nothing, gs:nothing
    LocalVar wls_seldesc,<size seldesc_s>
    LocalVar wls_od,<size vmod_s>

        TaskTime
        EnterProc

        SSToDS  ebx, wls_od
        SSToDS  ecx, wls_seldesc
        xor     edx, edx
        mov     wls_od.od_pPTDA,edx
        movzx   edi, ax
        CCALL32 _SELGetOd <SEL_FREEFLAGS, edi, ecx, ebx>
        cmp     ax, ERROR_INTERRUPT
        jz      short wls90

        or      eax, eax
        .errnz  NO_ERROR
        mov     ax, ERROR_ACCESS_DENIED ; change error code for v1.1, v1.2
                                        ; compatibility
        jnz     short wls90

    ; check for access byte, must be ring 2 or 3

        test    [wls_seldesc.desc_acc], D_DPL2
        jz      short wls90

    ; If UVIRT, must return error

        mov     ax, ERROR_DIRECT_ACCESS_HANDLE
        test    [wls_seldesc.desc_attr], D_UVIRT
        jnz     short wls90

    ; Must be base, if huge selector
    ; (ebx) = descriptor address

        mov     ax, ERROR_INVALID_PARAMETER
        mov     ebx, [wls_seldesc.desc_laddr]
        cmp     [wls_od.od_laddr], ebx
        jnz     short wls90

    ; check for discardable segment

        mov      esi,[wls_od].od_pob        ;
        movzx    di,[esi].ob_xflags         ; (DI) = ob_xflags
        test     di,VMOB_DISC_SEG           ; Is a discardable seg ?
        jz       short wlsx                 ; jmp if no! (ZF set,CF clear)
        mov      eax,esi                    ; calculate hob
        sub      eax,[_pobvmZero]           ;
        mov      ecx,size vmob_s            ;
        cdq                                 ;
        idiv     ecx                        ; (AX) = hob
        or       si,1                       ; clear ZF,CF
        jmp      short wlsx                 ;

wls90:  stc
wlsx:   LeaveProc
        RETC
EndProc selValidateLockSeg

LOAD_TUNEDIT1_SWP ENDS
SWAP32_16 segment
;***LP  selAllocHuge - Allocate a huge memory segment
;
;       selAllocHuge allocates a huge memory segment. We reserve
;       for the maximum size, and then commit the initial
;       requested size. Selectors are set up to show the requested size.
;
;       ENTRY   (AX) = NumSeg - number of 64k segments to allocate.
;               (BX) = Size - size of the last segment to allocate.
;               (CX) = MaxNumSeg - the maximum number of segments ever
;                   to be required by a w_ReallocHuge call (or 0).
;               (DX) = allocation flag bits:
;                   0: private segment
;                   1: GIVESEG segment
;                   2: GETSEG segment
;                   4: discardable segment
;                   8: shrinkable segment
;              (ESI) = VMAC_PROT if to alloc from protected region, 0 otherwise
;       EXIT    Carry Flag clear
;                   (AX) = Selector - the base selector of the newly
;                               allocated huge segment.
;               Carry Flag set
;                   (AX) = error code
;       USES    EAX, EBX, ECX, EDX, ESI, EDI, EFlags
;
;       Pseudocode:
;
;       If (maxnumseg == 0), set maxnumseg=numseg + 1
;           set maxnum = numseg + 1
;       Endif
;       Call VMAllocMem(HUGE, size, maxsize, SELMAP, RING3 DATA) ;reserve memory
;       return(error code or selector)
;

Procedure selAllocHuge,far32,local
    assume ds:FLAT, es:FLAT, ss:nothing, fs:nothing, gs:nothing
    LocalVar wah_size,DWORD
    LocalVar wah_maxsize,DWORD
    LocalVar wah_prot,DWORD
    LocalVar wah_ac,<size vmac_s>
    LocalVar wah_pob,DWORD
    LocalVar wah_callermte,DWORD

        TaskTime
        EnterProc <ax,bx,cx,WORD PTR 0,esi> ; size, max no of selectors, protect fl

        mov     ebx, [wah_size]
        or      ebx, ebx                    ; is size = 0?
        jz      wah90                       ; 0 sized huge is unacceptable

        shl     ecx, WORDSHIFT              ; maxsel to maxsize
        or      ecx, ecx                    ; maxsize = 0 ?
        jnz     short wah10                 ; jmp if not

        inc     ax                          ; maxseg = numseg + 1
        shrd    ecx, eax, WORDSHIFT         ; maxsel to maxsize in ecx
        mov     [wah_maxsize], ecx

wah10:
        cmp     ecx, [wah_size]
        jb      wah90                       ; OK if size <= maxsize

        and     dx, not 8000h               ; ignore 8000h bit
        mov     si, dx                      ; save flags
        test    dx, not WAS_FLAGSMASK
        jnz     wah90                       ; jmp if bad share indicator

    ; Get the handle to the mte for VMAllocMem

        mov     edi,[_pTCBCur]
        mov     edi,[edi].TCB_pFrameBase
        movzx   eax,[edi].ssf_cs
        CCALL32 _VMGetOwner <eax, [edi].ssf_eip>
        mov     [wah_callermte],eax

    ; If discardable segment addRMP record for soft-lock count. Data is
    ; filled in later

        mov     edx, SELAL_SELMAP or SELAL_W or D_DPL3 ; VMAlloc flSelMgr
        .errnz  ((D_DPL3 shr SELAL_PRIVSHIFT) - 3)
        test    si,WAS_DISCARDABLE      ; is discardable seg request ?
        jz      short wah20             ; jmp if no!
        or      edx, SELAL_DISCSEG

wah20:
        movzx   ecx, si                          ; cx = flags
        and     cx, WAS_GIVESEG + WAS_GETSEG     ; giveable/getable
        jnz     short wah30                      ; jmp if shared

        ; private allocation

        mov     eax, WAS_VM1 or WAS_VM3 or WAS_PG or VMAC_ARENAPRV
        mov     ecx, [_pPTDACur]
        movzx   ecx, [ecx].ptda_handle  ; (ecx) = hobptda is the owner
        jmp     short wah40

wah30:
        ; shared allocation

        mov     eax, WAS_VM1 or WAS_VM3 or WAS_PG or VMAC_ARENASHR or VMAC_SHARED
        or      eax, [wah_prot]         ; get protection flag
        or      ecx, SHRINDRSVD         ; owner = shared indicator
        .errnz  (SHRINDRSVD + WAS_GIVESEG - GIVESEGOWNER)
        .errnz  (SHRINDRSVD + WAS_GETSEG - GETSEGOWNER)
        .errnz  (SHRINDRSVD + WAS_GIVESEG + WAS_GETSEG - SHRDOWNER)

wah40:
        test    si,WAS_SHRINKABLE       ; is shrinkable ?
        jz      short wah50             ; jmp if no!
        or      eax, VMAC_OBSHRINK

wah50:
        SSToDS  edi, wah_ac

;   _VMAllocMem(cb, cbcommit, fl, hobptda, hobowner, hobmte, flsel, blkno, pac)

        CCALL32 _VMAllocMem <[wah_maxsize], [wah_size], eax, 0, ecx, \
                    [wah_callermte], edx, 0, edi>

wah80:
        or      eax, eax                 ; successful ???
        stc
        jnz     short wahx               ; jump if error
        mov     ax, [wah_ac].ac_sel      ; ax = selector
        clc

wahx:
        LeaveProc
        RETC

wah90:
        mov     ax,ERROR_INVALID_PARAMETER
        jmp     short wah80

EndProc selAllocHuge


;***LP  selReallocHuge - Reallocate a huge memory segment
;
;       selReallocHuge changes the size of a huge memory segment.
;
;       ENTRY   (AX) = NumSeg - number of 64k segments desired.
;               (BX) = Size - the size of the last segment desired.
;               (CX) = Selector - huge base selector.
;       EXIT    Carry Flag clear
;                   NONE
;               Carry Flag set
;                   (AX) = error code
;       USES    EAX, EBX, ECX, EDX, ESI, EDI, EFlags
;
;       PseudoCode:
;
;       If (GDT selector), return (ERROR_ACCESS_DENIED)
;       Call SELGetOd(take semaphore)
;       if(ob_flags == VMOB_DISC_SEG)   ; if a discardable segment
;           if(KSEMRequestExclusive(Discardable Seg Sem4) == ERROR)
;               return (error code)
;           endif
;       endif
;       If (error, or not D_SEG, or selector's address is not base)
;           rc = ERROR_INVALID_PARAMETER
;           Jump exit
;       If (D_UVIRT || D_CODE || D_BIG || not RING2 or 3 DATA)
;           rc = ERROR_ACCESS_DENIED
;           Jump exit
;       Endif
;       If (locked)
;           rc = ERROR_LOCKED
;           Jump exit
;       Endif
;       If (OB_UVIRT || not OB_HUGE || an alias || not AR_SELMAP)
;           rc = ERROR_ACCESS_DENIED
;           Jump exit
;       Endif
;       If (HF_LALIAS)
;           Call VMSELAlias(export)
;           If (cs alias) InternalError
;       Endif
;       Call _SELGetSize
;       If (OB_SHARED && not OB_SHRINKABLE)
;           If (newsize < cursize)
;               Call VMClearSem
;               return (ERROR_ACCESS_DENIED)
;           Endif
;       Endif
;       if (selReallocSub() == NO_ERROR)
;          if(ob_flags == VMOB_DISC_SEG)   ; if a discardable segment
;              if(Rmp_Find(hob) == ERROR)  ; Find rmp record using hob as key
;                  Panic
;              endif
;              if(softlocks < 0xff)        ; if not locked forever
;                  softlocks += 1;
;              endif
;          endif
;       endif
;exit:  if(ob_flags == VMOB_DISC_SEG)      ; if a discardable segment
;           release semaphore
;       endif
;       Call VMClearSem
;       return (error code from ReallocSub)
;

Procedure selReallocHuge,far32,local
    assume ds:FLAT, es:FLAT, ss:nothing, fs:nothing, gs:nothing
    LocalVar wrh_newsize,DWORD
    LocalVar wrh_sel,WORD
    LocalVar wrh_desc,<size seldesc_s>
;;; LocalVar wrh_oi,<size vmoi_s>
    LocalVar wrh_od,<size vmod_s>
    LocalVar wrh_hob,WORD
    LocalVar wrh_cursize,DWORD
ifdef SELSTRICT
    LocalVar wrh_pal,DWORD
endif

        TaskTime
        EnterProc <ax,bx,cx>            ; size, selector

        mov     ax, ERROR_ACCESS_DENIED
        test    cx, SEL_LDTBIT
        jz      short wrh30                 ; jmp if GDT selector

        mov     ax, ERROR_INVALID_PARAMETER
        cmp     [wrh_newsize], 0            ; Is new size = 0 ?
wrh30:  jz      wrh99                       ; error if so

;   _SELGetOd(fl, sel, pdesc, pod)
;       (cx) = sel

        mov     edi, [_TKSSBase]
;;;;;;; lea     eax, ss:wrh_oi
        lea     eax, ss:wrh_od
        add     eax, edi
        mov     edx, _pPTDACur
        mov     [eax].od_pPTDA, edx
        lea     edx, ss:wrh_desc
        add     edx, edi
;;;;;;; xor     esi, esi
;;;;;;; CCALL32 _SELObjectInfo < \
;;;;;;;             (VMGS_TAKESEM or VMGS_SLOCKWAIT or VMGS_VERIFY), \
;;;;;;;             ecx, esi, edx, eax>
        CCALL32 _SELGetOd <(VMGS_TAKESEM or VMGS_SLOCKWAIT or VMGS_VERIFY),\
                               ecx, edx, eax>
        cmp     ax, ERROR_INTERRUPT
        jz      wrh99

        or      eax, eax
        .errnz  NO_ERROR
        mov     eax, ERROR_INVALID_PARAMETER
        jnz     wrh99

        mov     esi, [wrh_od].od_pob
        vmPobToHob esi
        mov     [wrh_hob], ax

;;;;;;; test    [wrh_oi.oi_xflags],VMOB_DISC_SEG  ; check if discardable seg
        test    [esi].ob_xflags,VMOB_DISC_SEG     ; check if discardable seg
        jz      short wrh32                       ; jmp if no

        CCALL32 _KSEMRequestExclusive,<[_SELDiscSegRmpSem],-1>
        or      eax, eax
        jz      short wrh32             ; jmp if no error
        mov     ebx, eax                ; save error code
        jmp     wrh92

;   Error if gate desc, 32 bit default, uvirt, code, or Ring 0
;       (EDI) = _TKSSBase

wrh32:
;;;;;;; mov     ebx, eax                    ; (ebx) = ERROR_INVALID_PARAMETER
        mov     ebx, ERROR_INVALID_PARAMETER
        mov     ecx, [wrh_newsize]
;;;;;;; cmp     [wrh_oi.oi_cb], ecx
        mov     eax, [wrh_od].od_par
        mov     edx, dword ptr [eax].ar_union5
        shl     edx, 12
        cmp     edx, ecx
        jb      wrh90

        mov     cx, word ptr [wrh_desc].desc_acc    ; bh = attr, bl = acc
        test    cl, D_SEG
        jz      short wrh35                 ; jmp if not segment

        test    cl, D_CODE
        jnz     short wrh40                 ; jmp if code

;;;;;;; mov     esi, [wrh_desc].desc_laddr
;;;;;;; cmp     esi, [wrh_oi].oi_va         ; is it base descriptor?
        mov     edx, [wrh_desc].desc_laddr
        cmp     edx, [wrh_od].od_laddr      ; is it base descriptor?
        jnz     short wrh40                 ; jmp if not base

        mov     bx, ERROR_LOCKED
;;;;;;; cmp     [wrh_oi].oi_cllock, 0       ; long term locked ?
        cmp     [esi].ob_cllock, 0          ; long term locked ?
        jnz     short wrh40

        mov     bx, ERROR_ACCESS_DENIED
        test    ch, D_UVIRT or D_DBIG
        jnz     short wrh40                 ; jmp if UVIRT or 32 bit default

        test    cl, D_DPL2                  ; jmp if Ring 0 or 1
wrh35:  jz      wrh90

;;;;;;; mov     ax, [wrh_oi].oi_fsob
;;;;;;; and     ax, OB_HUGE or OB_UVIRT
;;;;;;; cmp     ax, OB_HUGE
        mov     dx, [esi].ob_fs
        and     dx, OB_HUGE or OB_UVIRT
        cmp     dx, OB_HUGE
        jnz     short wrh40                 ; jmp if UVIRT or not HUGE

;;;;;;; test    [wrh_oi].oi_fsar, AR_SELMAP
        mov     dx, word ptr [eax].ar_union0
        test    dx, AR_SELMAP
        jz      short wrh35                 ; jmp if not tiled

;;;;;;; cmp     [wrh_oi].oi_hal, 0
        cmp     [eax].ar_hal, 0
wrh40:  jnz     wrh90                       ; jmp if ALIAS

ifdef SELSTRICT

;;;;;;; test    [wrh_oi].oi_fsob, OB_LALIAS
        test    [esi].ob_fs, OB_LALIAS
        jz      short  wrh45                ; jmp if not aliased

;   _VMSelAlias(fl, hob, &pal)

        SSToDS  ecx, ss:wrh_pal
;;;;;;; CCALL32 _VMSelAlias <VMMA_CSALIAS, dword ptr [wrh_oi].oi_hob, ecx>
        CCALL32 _VMSelAlias <VMMA_CSALIAS, dword ptr [wrh_hob], ecx>
        or      eax, eax
        .errnz  NO_ERROR
        jnz     short wrh45                 ; this better be error

        Panic <'w_ReallocHuge: cs alias'>
wrh45:
endif

;   Get the committed size of the object
;   _SELGetSize(laddr, sel, hptda, cbmax, pcb)
;       (edi) = _TKSSBase

        lea     eax, ss:wrh_cursize
        add     eax, edi
;;;;;;; mov     ecx, [wrh_oi].oi_cb
        mov     ecx, [wrh_od].od_par
        mov     ecx, dword ptr [ecx].ar_union5
        shl     ecx, PAGESHIFT
        xor     edx, edx
        mov     bx, [wrh_sel]
;;;;;;; CCALL32 _SELGetSize <esi, ebx, edx, ecx, eax>
        CCALL32 _SELGetSize <[wrh_desc].desc_laddr, ebx, edx, ecx, eax>

        or      eax, eax
        .errnz  NO_ERROR
        jz      short wrh50

        Panic  <'w_ReallocHuge: SELGetSize'>

wrh50:  mov     bx, ERROR_ACCESS_DENIED
        mov     edx, [wrh_newsize]
        mov     ecx, [wrh_cursize]
;;;;;;; mov     ax, [wrh_oi].oi_fsob
        mov     ax, [esi].ob_fs
        test    ax, OB_SHARED
        jz      short wrh60                 ; OK if not shared

        test    ax, OB_SHRINKABLE
        jnz     short wrh60                 ; OK if shared and shrinkable

;   Make sure newsize >= cursize

        cmp     edx, ecx
        jb      wrh90                       ; Deny access if newsize < cursize

;   All is OK, Now realloc the segment
;   _selReallocSub(cbnew, cbcur, poi)
;       (edi) = _TKSSBase
;       (ecx) = current size
;       (edx) = new size


wrh60:
;;;;;;; lea     esi, ss:wrh_oi
;;;;;;; add     esi, edi
;;;;;;; CCALL32 _selReallocSub < edx, ecx, esi>
        lea     eax, ss:wrh_od
        add     eax, edi
        CCALL32 _selReallocSub < edx, ecx, eax>
        mov     ebx, eax                ; (ebx) = return code
        or      ebx,ebx                 ;
        jnz     wrh90                   ; jmp if un-succesful

;;;;;;; test    [wrh_oi.oi_xflags],VMOB_DISC_SEG  ; check if discardable seg
        test    [esi].ob_xflags,VMOB_DISC_SEG     ; check if discardable seg
        jz      wrh92                             ; jmp if no
        push    ebx                               ; save return code

    ; Rmp_Find()
    ;     ds:si = Address of match pattern,
    ;        cx = length of match pattern,
    ;        bx = offset into the record,
    ;     es:di = address of handle to managed seg
    ; Returns:
    ;  CF clear = match found
    ;     es:di = start of found record

wrh65:
;;;;;;; push    [wrh_oi.oi_hob]             ; setup match pattern in stack
        push    [wrh_hob]                   ; setup match pattern in stack
        push    ss
        pop     ds
        assume ds:nothing
        mov     si, sp                      ; ds:si points to match pattern
        mov     cx, 2                       ; length of match pattern
        mov     bx, offset hob              ; offset of match pattern
        DosContext es, di
        mov     di,offset DOSGROUP:hDiscSegRmpStruc
        CALL16  Rmp_Find                    ; Find the disc seg record
        pop     ax                          ; Clear stack 1 word
        jnc     short wrh70                 ; jmp if found

        Panic <'w_ReallocHuge: HOB not found'>

wrh70:  pop     ebx                         ; restore return code
        mov     al,es:[di].softlocks        ; get discard lock count
        inc     al                          ; increment discard lock count
        clc
        jz      short wrh75                 ; jmp if maxed out, return success
        mov     es:[di].softlocks,al        ; set discard lock count
wrh75:  FlatContext <ds, es>, ax
        jmp     short wrh91

;       (ebx) = error code

wrh90:
;;;;;;; test    [wrh_oi.oi_xflags],VMOB_DISC_SEG  ; check if discardable seg
        test    [esi].ob_xflags,VMOB_DISC_SEG     ; check if discardable seg
        jz      wrh92                             ; jmp if no
wrh91:
        CCALL32 _KSEMRelease,<[_SELDiscSegRmpSem]>
wrh92:
;;;;;;; mov     ax, [wrh_oi].oi_hob
        mov     ax, [wrh_hob]
        CCALL32 _VMClearSem <eax>
        mov     eax, ebx                    ; (eax) = error code
wrh99:
        or      eax, eax                    ; clears carry
        .errnz  NO_ERROR
        jz      short wrhx
        stc
wrhx:   LeaveProc
        RETC
EndProc selReallocHuge
SWAP32_16 ENDS
SWAP32_15 segment

;***LP  selCreateCSAlias - Create 16 bit code selector to
;               access a data segment
;
;       This function allocates a selector in the LDT, and sets it up
;       so that it can be used as 16 bit code selector for the
;       given data segment. The value of the 16 bit code selector
;       is returned.
;
;       ENTRY   (AX) = Data Selector to be aliased.
;       EXIT    Carry clear on success
;                   (AX) = 16 bit code Selector
;               Carry set on error
;                   (AX) = Error code
;       USES    EAX, EBX, ECX, EDX, ESI, EDI, EFlags
;
;       PseudoCode:
;
;       If (GDT selector), return ERROR_ACCESS_DENIED
;       Call SELGetOd(datasel, take semaphore)
;       If (error),
;           return ERROR_INVALID_PARAMETER
;       Endif
;       If (not ring2 or 3 data)
;           Call VMClearSem
;           return (ERROR_ACCESS_DENIED)
;       Endif
;       If (shared, or discardable, or huge, or uvirt, or maxsize > 64Kb)
;           Call VMClearSem
;           return ERROR_ACCESS_DENIED
;       Endif
;       If (OB_LALIAS)          ; already aliased?
;           Call VMSelAlias(export cs alias record  address)
;           If (no error)
;               If ( (pla->cs_cref >> CS_REFSHIFT) < 1000 )
;                   Set pla->cs_cref =+ (1 << CS_REFSHIFT)  ; Inc ref count
;                   Call VMClearSem
;                   return(cs selector and NO_ERROR)
;               Else
;                   Call VMClearSem
;                   return(ERROR_NOT_ENOUGH_MEMORY)
;               Endif
;           Endif
;       Endif
;       ; No cs aliases exist
;       Call VMMapAlias (laddr, 64K or datasel size, private LDT)
;       If (error)
;           Call VMClearSem
;           return (error code)
;       Endif
;       Call VMSelAlias
;       Set up cs_selcode, cs_seldata, cs_fs, and cs_cref fields
;       Set retcode = (NO_ERROR)
;       Write codesel value
;       Call VMClearSem
;       return(retcode)

WCS_FLSEL equ SELAL_SELMAP or SELAL_CODE or SELAL_R or SELAL_CONFORM or D_DPL2
WCS_FLVM  equ VMAC_ARENAPRV or PG_X or PG_R or PG_U or VMAC_SELALLOC \
        or VMAC_LOCANY or VMAC_ALIGNSEL

Procedure selCreateCSAlias,far32,local
    assume ds:FLAT, es:FLAT, ss:nothing, fs:nothing, gs:nothing
        LocalVar    wcs_datasel,WORD        ; Data selector to be aliased.
        LocalVar    wcs_desc, <size seldesc_s>
;;;;;;; LocalVar    wcs_oi, <size vmoi_s>
        LocalVar    wcs_od, <size vmod_s>
        LocalVar    wcs_hob,WORD
        LocalVar    wcs_pal, DWORD          ; address of alias record
        LocalVar    wcs_ac, <size vmac_s>

        TaskTime
        EnterProc <ax>            ; selector

        mov     ebx, ERROR_ACCESS_DENIED
        test    ax, SEL_LDTBIT          ; make sure it is LDT
        jz      wcs90

;   _SELGetOd(fl, sel, pdesc, pod)
;   (ax) = selector

wcs20:  mov     edi, [_TKSSBase]
;;;;;;; lea     ecx, ss:wcs_oi
        lea     ecx, ss:wcs_od
        add     ecx, edi
        mov     edx, _pPTDACur
        mov     [ecx].od_pPTDA, edx
        lea     edx, ss:wcs_desc
        add     edx, edi
;;;;;;; xor     ebx, ebx
;;;;;;; CCALL32 _SELObjectInfo < \
;;;;;;;             (VMGS_TAKESEM or VMGS_SLOCKWAIT or VMGS_VERIFY), \
;;;;;;;             eax, ebx, edx, ecx>
        CCALL32 _SELGetOd < (VMGS_TAKESEM or VMGS_SLOCKWAIT or VMGS_VERIFY), \
                            eax, edx, ecx>
        mov     ebx, eax
        cmp     eax, ERROR_INTERRUPT
        jz      wcs90j

        or      eax, eax
        .errnz  NO_ERROR
        mov     ebx, ERROR_INVALID_PARAMETER
        jnz     short wcs90j

        mov     edx, [wcs_od].od_pob
        vmPobToHob edx
        mov     [wcs_hob], ax

        mov     ebx, ERROR_ACCESS_DENIED
        test    [wcs_desc].desc_acc, D_DPL2 ; must be Ring 2 or 3
        jz      short wcs80j                ; jmp if Ring 0

        test    [wcs_desc].desc_acc, D_CODE ; must be Data selector
        jnz     short wcs80j                ; jmp if code

        test    [wcs_desc].desc_attr, D_UVIRT   ; must not be uvirt
        jnz     short wcs80j

;;;;;;; mov     ax, [wcs_oi].oi_fsob        ; object record flags
        mov     ax, [edx].ob_fs             ; object record flags
        and     ax, OB_UVIRT or OB_HUGE or OB_SHARED
        jnz     short wcs80j                ; can't be huge, uvirt or shared

;;;;;;; cmp     [wcs_oi].oi_cb, WORDMASK + 1
        mov     eax, [wcs_od].od_par
        mov     eax, dword ptr [eax].ar_union5
        shl     eax, PAGESHIFT
        cmp     eax, WORDMASK + 1
        ja      short wcs80j                ; max size can't be > 64Kb

;   See if the object already has a cs alias

;;;;;;; test    [wcs_oi].oi_fsob, OB_LALIAS
        test    [edx].ob_fs, OB_LALIAS
        jz      short   wcs50               ; no aliases, call VMMapAlias

;   _VMSelAlias(fl, hob, &pal)
;   (edi) = _TKSSBase
;   (ebx) = ERROR_ACCESS_DENIED

        lea     eax, ss:wcs_pal
        add     eax, edi
;;;;;;; CCALL32 _VMSelAlias <VMMA_CSALIAS, dword ptr [wcs_oi].oi_hob, eax>
        CCALL32 _VMSelAlias <VMMA_CSALIAS, dword ptr [wcs_hob], eax>
        or      eax, eax
        .errnz  NO_ERROR
        jnz     short wcs50                 ; no cs aliases, create one

;   cs alias already exist, just increment the reference count and return sel.

        mov     ecx, [wcs_pal]              ; address of alias record
        mov     ax, [ecx].sal_cref
        shr     ax, SAL_ALIASREFSHIFT
        cmp     ax, 1000                ; can't allow more than 1000 aliases
        jae     short wcs80j            ; ebx = ERROR_ACCESS_DENIED

        add     [ecx].sal_cref, (1 shl SAL_ALIASREFSHIFT)
        mov     si, [ecx].sal_selcode   ; si = selector
        xor     ebx, ebx                ; return success
        .errnz  NO_ERROR
wcs80j: jmp     wcs80

wcs90j: jmp     wcs90

;   _VMMapAlias(flal, flsel, fs, laddral, hptdaal, laddr, hptda, cb, &pac)
;   (edi) = _TKSSBase
;   (ebx) = ERROR_ACCESS_DENIED

wcs50:  lea     ecx, ss:wcs_ac
        add     ecx, edi
        xor     eax, eax
;;;;;;; CCALL32 _VMMapAlias < WCS_FLVM, WCS_FLSEL, VMMA_CSALIAS, eax, eax,\
;;;;;;;             [wcs_oi].oi_va, eax, [wcs_desc].desc_cb, ecx>
        CCALL32 _VMMapAlias < WCS_FLVM, WCS_FLSEL, VMMA_CSALIAS, eax, eax,\
                    [wcs_od].od_laddr, eax, [wcs_desc].desc_cb, ecx>
        mov     ebx, eax
        or      ebx, ebx
        .errnz  NO_ERROR
        jnz     short wcs80j            ; return if error

;       VMMapAlias succeeded, now set up local record and return
;   _VMSelAlias(fl, hob, &pal)
;   (edi) = _TKSSBase
;   (ebx) = NO_ERROR

        lea     eax, ss:wcs_pal
        add     eax, edi
;;;;;;; CCALL32 _VMSelAlias <VMMA_CSALIAS, dword ptr [wcs_oi].oi_hob, eax>
        CCALL32 _VMSelAlias <VMMA_CSALIAS, dword ptr [wcs_hob], eax>
        or      eax, eax
        .errnz  NO_ERROR
        jz     short wcs60

        Panic <'w_CreateCSAlias:VMSelAlias'>

wcs60:  mov     ecx, [wcs_pal]
        mov     ax, [ecx].sal_cref
        and     ax, not SAL_ALIASREFMASK    ; keep flags
        add     ax, (1 shl SAL_ALIASREFSHIFT)
        or      ax, SAL_CSDSVALID           ; turn on ds valid flag
        mov     [ecx].sal_cref, ax
        mov     ax, wcs_datasel
        mov     [ecx].sal_seldata, ax       ; store data selector
        mov     si, [wcs_ac].ac_sel         ; (si) = code selector
        mov     [ecx].sal_selcode, si       ; store code selector

;   (ebx) = error code (NO_ERROR if no error)
;   (si)  = selector if success

wcs80:
;;;;;;; CCALL32 _VMClearSem <dword ptr [wcs_oi].oi_hob>
        CCALL32 _VMClearSem <dword ptr [wcs_hob]>
        mov     ax, si                      ; set ax = sel if success
        or      ebx, ebx                    ; clears carry
        .errnz  NO_ERROR
        jz      short wcsx

;       failure, set carry and return error code

wcs90:  mov     eax, ebx
        stc
wcsx:   LeaveProc
        RETC
EndProc selCreateCSAlias

SWAP32_15 ENDS

    FARCODE HIGH32

CINIT_TEXT segment

;***LP  _SELDiscSegInit - Initialize the discardable segment
;                         RMP segment and sem4.
;
;       _SELDiscSegInit calls Rmp_Alloc to initialize the
;       discardable segments RMP records segment and KSEMAlloc
;       to initialize the discarable segments semaphore.
;
;       ENTRY   NONE
;       EXIT    NONE
;       USES    EAX, ECX, EDX, Flags

Procedure _SELDiscSegInit
    assume ds:nothing,es:nothing,fs:nothing,gs:nothing,ss:TASKAREA
        ; Save registers for C calling conventions

        SaveReg <ds,es,ebx,esi,edi>

        ;   Initialize discardable segments RMP records segment

        mov     ax,DISCARDOWNER                 ; (ax) = hobowner
        xor     ebx,ebx                         ; (bx) = hobmte
        mov     esi,PG_SWAPPABLE                ; (esi) = PG alloc flags
        DosContext es                           ; (es:di) -> rmp handle
        mov     di,offset hDiscSegRmpStruc
        CALL16  Rmp_Alloc
        jc      short sdsi_p

        ;   Initialize discardable segments semaphore
        ;   eax = KSEMAlloc(&sem handle, sem type, flags)

        FlatContext <ds, es> ax

        CCALL32 _KSEMAlloc, <<offset FLAT:_SELDiscSegRmpSem>, KRNL_SHARESEM, 0>
        or      eax, eax
        .errnz  NO_ERROR
        jnz     short sdsi_p

        ; Restore saved registers

        RestoreReg <edi,esi,ebx,es,ds>
        RETC
sdsi_p: Panic <'_SELDiscSegInit'>
EndProc _SELDiscSegInit

CINIT_TEXT ends
        end
