;       SCCSID = @(#)syssem.asm 6.4 92/03/11
SCCSID  equ     "@(#)syssem.asm 6.4 92/03/11"

PAGE    ,132
TITLE   SYSSEM - System Semaphores Create, Open, and Close routines
NAME    SYSSEM

;***    SYSSEM.ASM
;
;       IBM/Microsoft Confidential
;
;       Copyright (c) IBM Corporation 1987, 1989
;       Copyright (c) Microsoft Corporation 1987, 1989
;
;       All Rights Reserved
;
;       Title:
;
;       OS/2 system semaphore routines
;
;
;       Description:
;
;       This module provides the routines to create, open
;       and remove system semaphores.  Thread and process
;       termination clean up routines are also contained
;       in this module.  SEM.ASM contains all the functions
;       to set, clear or wait on system semaphores. MUXSEM.ASM
;       contains the functions to wait on multiple system
;       or RAM semaphores.
;
;       These are the functions provided:
;
;       w_CreateSem     Create a system semaphore.
;
;       w_OpenSem       Open a system semaphore.
;
;       w_CloseSem      Close a system semaphore.
;
;       The "w_*" functions should be only called from the
;       api interface. Kernel callable entries will have to
;       be added when needed.
;
;       SEM.ASM contains the intialization routine that allocates
;       the RMP segment selector.
;
;
;       Design/Implementation Notes:
;
;       System semaphore use these data structures: the system semaphore
;       handle, a per-process handle table (SysSemPTDAStruc), a RMP segment
;       for system semaphore names (SysSemRmpHdl), a the global system
;       semaphore data table (SysSemDataTable) and an extension to the global
;       system table in the high data segment (SysSemHighTable).
;
;       There are two types of system semaphore handles just in this system:
;       per-process and global. "Per-process" handles are only valid in the
;       process that created or opened the semaphore.  "Global" semaphore
;       handles are valid in all processes.  Applications can only use
;       "per-process" handles while the kernel and device drivers can
;       generate and use "global" semaphore handles.  "Global" semaphore
;       handles are used for interrupt time SemClears and are generated by
;       the SemHandle function.  CreateAnonSem also returns a global system
;       semaphore handle.  The high word of the handle determines the
;       type: USERHANDLE_HIGHWORD for "per-process" or SYSHANDLE_HIGHWORD
;       for "global".  The low word of the handle is the index into the
;       per-process handle table and the global system semaphore table.
;
;       The per-process handle table (SysSemPTDAStruc) is an array of bytes,
;       maximum number of system semaphores in size.  The application handle
;       returned by CreateSem or OpenSem is an index into this table.  If
;       the entry is zero, the handle is invalid; otherwise, it contains
;       the number of CreateSems and OpenSems on the semaphore in this
;       process.  An entry in this per-process table corresponds directly
;       to an entry in the global semaphore data structure.  Every process
;       that opens a particular system semaphore will get the same handle.
;
;       The RMP system semaphore name table (pointed to by SysSemRmpHdl)
;       contains the offset into the global system semaphore table and a
;       variable length record containing the name.  When creating a system
;       semaphore this table is search to see if the semaphore already
;       exists.  For opens this table is searched to get the global
;       system semaphore table entry offset.  The name table entry is
;       removed when the semaphore reference count goes to zero unless
;       it's interrupt time (via SemHandle) where the name table entry
;       is delayed until the next CreateSem or OpenSem (CleanUpSysSemName).
;
;       The global system semaphore table (SysSemDataTable) contains
;       the following:
;
;       owner byte      (SysSemOwner)
;       flag byte       (SysSemFlag)
;       reference count (SysSemRefCnt)
;       use count       (SysSemProcCnt)
;
;       The owner byte is like the ram semaphore owner byte, but it
;       always contains the slot number of the owning thread when the
;       semaphore is set.  Zero still means the the system semaphore
;       is unowned.
;
;       The flag byte contains the following flags:
;
;       SYSSEM_WAITING            This flag indicates that a thread
;                                 is waiting on this system semaphore.
;
;       SYSSEM_MUXWAITING         This flag indicates that a thread
;                                 is DosMuxSemWait'ing on this system
;                                 semaphore.
;
;       SYSSEM_EXCLUSIVE          This flag is set when the system
;                                 semaphore is created as exclusive.
;
;       SYSSEM_NAME_CLEANUP       This bit indicates that the reference
;                                 count went zero (via SemHandle) at
;                                 interrupt time and the system semaphore
;                                 name table entry needs to be removed on
;                                 the next OpenSem or CreateSem.
;
;       SYSSEM_THREAD_OWNER_DIED  This flag is set when a thread has died
;                                 while owning a semaphore when the whole
;                                 process is being terminated by an exception
;                                 fault, etc.  It indicates that the process
;                                 owns the semaphore (instead of a particular
;                                 thread in the process owning it) and the
;                                 exitlist handler should get the first shot
;                                 at claiming it.
;
;       SYSSEM_EXITLIST_OWNER     This flag is set before the exitlist
;                                 handler is executed on all semaphores
;                                 owned by the dieing process.  This flag
;                                 indicates that the exitlist thread can
;                                 SemRequest this semaphore.
;
;       SYSSEM_OWNER_DIED         This flag indicates that a individual
;                                 thread died while owning a semaphore
;                                 or the process exits still owning a
;                                 semaphore after the exitlist handler
;                                 has executed.
;
;       The reference count is the number of creates/opens/semhandles
;       on the system semaphore.  Opens incremented the count and
;       closes decremented it.  The SemHandle devhlp can either increment
;       or decrement the reference count.  If the reference count goes
;       to zero, the semaphore is no longer exists.
;
;       The use count is the number of times the owning thread has
;       SemRequest'ed an exclusive semaphore.  The semaphore will need
;       that many SemClears before the semaphore becomes unowned.  On
;       a non-exclusive semaphore the use count is not used.
;
;       When a whole process dies and while each thread owning a system
;       semaphore dies within this process, the owning PID is place in a
;       parallel table to the global system semaphore table called the
;       SysSemHighTable in the SysSemPidOwner field.  This table is kept
;       in a high data segment because it is only accessed in protected
;       mode by the process termination semaphore cleanup code.  This table
;       contains the pid of the owning process when the corresponding
;       SYSSEM_THREAD_OWNER_DIED flag is set in the global semaphore
;       table entry.
;
;
;       Public routines:
;
;       w_CreateSem
;       w_OpenSem
;       w_CloseSem
;       Canonbuf
;       InitSemPTDAtbl
;       FreeSysSem
;       SysSemExitList
;       SysSemOwnerDied
;       dh_SemHandle
;       SemHandle
;       GetSysSemPtr
;       GetSysSemPtrChk
;
;       Modification History:
;
;       04 Sept 1986 - MikeM
;           Initial version from sem.asm.
;       10 July 1991 - Steve Best
;          add performance counters for system semaphore b724624
;       06 Feb  1992 - Steve Best sfb
;          decrease perfview overhead and add pvwkillinstance to clean
;          routine for system semaphores
;       03 Mar 1992 - Steve Best b734380 remove perfview
;       64286 - 03/23/93 Catherine E. Bundy
;          Strcpy returns cy if the last character processed is DBCS
;          which is passed through CanonBuf and was incorrectly
;          interpreted as an error.  Changed FindSysSemName and
;          AddSysSemName to check AX for error code upon return from
;          CanonBuf, and process error if AX is not zero.
;       74655 Nizinski 10/14/93  Changed CanonBuf routine to obtain/release
;                          the ptda semaphore around the call to
;                          Canonicalize, as Canonicalize updates the
;                          WFP_END pointer in the ptda.
;       78544 ChangeTeam 02/22/94 REGR:64286  Changed "jc" to "jne" in
;                          AddSysSemName and FindSysSemName, to process
;                          error if AX is not zero.
;       108606 ChangeTeam 01/05/95 REGR:64286  Moved EnterProc statement
;                          in FindSysSemName prior to jump instructions to
;                          avoid trap c accessing local variable.
;       72417  MLM  11/27/95: moved from PTDA to TCB: cMeta
;

.xlist
include dosseg.inc
include dossymc.inc
include basemaca.inc
include fnotask.inc
include osmaca.inc
include error.inc
include protmode.inc
include tcbdata.inc
include curdir.inc
include mi.inc
include proc.inc
include osconst.inc
include pgexport.inc
include vmexport.inc
.list

include sem.inc

        ModName SYSSEM, FNO_SYSSEM_ASM
        CpuMode 386


        ReferTask       SysSemPTDATbl,WORD
                                                ;72417 removed PTDA entry now
                                                ;      in TCB
;       ReferTask       cMeta,BYTE                                     ;72417
        ReferTask       ProcStatus,BYTE
        ReferTask       Pid,WORD
        ReferTask       CurrTCB,WORD

        ReferFlat       _papTCBSlots,DWORD
        ReferFlat       _pPTDACur,DWORD
        ReferFlat       _pTCBCur,DWORD                                 ;b724624

;b734380ReferFlat       _fPerfview,DWORD

        ReferGlobal     TaskNumber,WORD
        ReferGlobal     InterruptLevel,BYTE

        EXTRN           ProtMode:NEAR
        EXTRN           RealMode:NEAR
        EXTRN           SemProtFault:NEAR

        EXTRN           GetTempBuffer:near
        EXTRN           FreeTempBuffer:near
        EXTRN           Rmp_Add:near
        EXTRN           Rmp_Find:near
        EXTRN           Rmp_Rem:near


; 74712 movement of WFP from PTDA makes 74655 obsolete
;       EXTRN           Take_Kernel_Sem:near       ; 74655
;       EXTRN           Clear_Kernel_Sem:near      ; 74655

        EXTRNF32        ValidateName

        EXTRNFAR        ProcRun
        EXTRNFAR        Canonicalize
        EXTRNFAR        SemProtFault
        EXTRNFAR        SemRequest
        EXTRNFAR        SemClear
        EXTRNFAR        StrCpy
        EXTRNFAR        TCYield

;b734380EXTRN32         _PVWKillInstance_Sem                ;b724624
;b734380EXTRN32         _PVWCreateInstance_Sem              ;b724624

        EXTRN32         _SELConvertToLinear                 ;b724624


DATA segment

SysSemPrefix    db      SemPrefixLen,"\SEM\"
SemPrefixLen    equ     $-SysSemPrefix-1

; Move to sdata.asm for System Anchor Block - f122427
        extrn           SysSemDataTable:byte
        extrn           SysSemRmpHdl:dword

NameTblCleanUp  db      0       ; if non-zero, RMP name table clean up required
NameCleanUpSem  dd      0       ; serializes name table clean up

DATA ends


DATA segment

SysSemHighTable SysSemHighTableS SYSSEM_MAXIMUM dup(<>)

DATA ends


         FARCODE HIGH3
HIGH3CODE SEGMENT

BREAK   <w_CreateSem - Create system semaphore>

;***    w_CreateSem - Create system semaphore
;***    CreateSem - Create system semaphore
;
;       This routine verifies and checks if the name already exists.  If
;       it doesn't, then a global semaphore table entry is allocated for
;       it, and the PTDA open table entry is incremented.  The name is added
;       to the RMP managed name table and the global reference count is
;       incremented.  The handle returned corresponds to the global semaphore
;       table, and will be same for all processes.
;
;       CreateSem cleans up any RMP name table entries left from interrupt
;       time SemHandles and gets and intializes an unused entry the global
;       system semphore table with GetSysSemEntry.  The corresponding entry
;       in the per-process table is allocated with GetSysSemPTDAIndex. Finally
;       the semaphore name is added to the RMP name table.  If the name table
;       addition fails, the other two table entries are freed.  CreateAnonSem
;       does what CreateSem does except there is no name to add to the RMP
;       name table and the handle returned is a global semaphore handle.  This
;       function is used to by other kernel components that need named system
;       semaphores.
;
;       ENTRY:  AX = NoExclusive flag.
;               DS:SI = address of semaphore name.
;
;       EXIT:   If carry clear, then
;                       DX:AX = semaphore handle.
;               If carry set, then
;                       AX = error code
;                               ERROR_INVALID_PARAMETER
;                               ERROR_INVALID_NAME
;                               ERROR_ALREADY_EXISTS
;                               ERROR_TOO_MANY_SEMAPHORES
;
;       USES:   AX, BX, CX, DX, DI, SI, DS, ES, Flags
;
;       CreateSem(NoExcl, SemName)
;       {
;           CleanUpSysSemName()
;           SemPtr = GetSysSemEntry(NoExcl)
;           SemHandle = GetSysSemPTDAIndex(SemPtr)
;           AddSysSemName(SemName)
;           return(SemHandle)
;       }
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:TASKAREA

Procedure w_CreateSem,FARONLY           ; api entry only

Entry   CreateSem,faronly               ; kernel entry point

;       PMONLY  TASKTIME
        TaskTime

;       MMIOPH Performance Hook Removed
;       Maj/Min = 186CH, Data = sem name (asciiz string at ds:si)

;       Clean up left over names from semaphores freed at interrupt time

        push    si                      ; save the name address
        push    ds

        push    ax
        call    CleanUpSysSemName
        pop     dx                      ; (DX) = exclusive/non-exclusive flag

;       Get and initialize a free system semaphore entry

        call    GetSysSemEntry          ; (BX) = system semaphore entry addr
        pop     ds
        pop     si                      ; (DS:SI) = name address
        jc      short crs4              ; if error return error code
;b734380jc      crs4                    ; if error return error code

;       Increment the PTDA Open Semaphore Table Entry

        call    GetSysSemPTDAIndex      ; (DI) = open sys semaphore PTDA Index
        jc      short crs3
;b734380jc      crs3

;       (DS:SI) = semaphore name string

        SaveReg <bx,di>
        call    AddSysSemName           ; (AX) = error code, if carry is set
        RestoreReg <di,bx>
        jc      short crs2              ; jump if error, free both table entries
;b734380jc      crs2                    ; jump if error, free both table entries

;       (DI) = offset of PTDA open system semaphore entry

;       get a performance counter data block from the kernel           ;b724624
;       The address of the counter data block will be placed into      ;b724624
;       SysSemPpvwBlk field in the system semaphore table              ;b724624
                                                                       ;b724624
                                                                       ;b724624

;b734380mov     [bx].SysSempPvwBlk.LowWord,0    ;clear pvw data blk DD ;sfb
;b734380mov     [bx].SysSempPvwBlk.HighWord,0   ;clear pvw data blk DD ;sfb
;b734380FlatContext  fs,ax                                             ;b724624

;b734380cmp     _fPerfview,0                                           ;sfb
;b734380je      short crs1                                             ;sfb
;b734380push    ecx                                                    ;b724624
;b734380push    esi                                                    ;b724624
;b734380push    edi                                                    ;b724624
;b734380push    edx                                                    ;b724624
;b734380mov     ecx,fs:_pPTDACur        ; (ECX) = pointer to current PTDA  ;b724624
;b734380mov     esi,fs:_pTCBCur         ; (ESI) = pointer to current TCB   ;b724624
;b734380push    ecx                                                    ;b724624
;b734380push    ebx                                                    ;b724624
;b734380xor     eax,eax                                                ;b724624
;b734380mov     ax,ds                                                  ;b724624
;b734380push    ds                                                     ;b724624
;b734380push    es                                                     ;b724624
;b734380FlatContext <ds,es>,edx                                        ;b724624
                                                                       ;b724624
;b734380CCALL32 _SELConvertToLinear <eax,ecx> ; take 16:16 address convert 0:32
;b734380pop     es                                                     ;b724624
;b734380pop     ds                                                     ;b724624
;b734380ASSUME DS:NOTHING, ES:NOTHING                                  ;b724624
                                                                       ;b724624
;b734380pop     ebx                                                    ;b724624
;b734380pop     ecx                                                    ;b724624
;b734380lea     edx,ds:[bx].SysSempPvwBlk                              ;b724624
;b734380xor     edi,edi                                                ;b724624
;b734380mov     di,dx                                                  ;b724624
;b734380add     eax,edi                                                ;b724624
;b734380push    ds                                                     ;b724624
;b734380push    es                                                     ;b724624
;b734380FlatContext <ds,es>,edx                                        ;b724624
                                                                       ;b724624
;b734380CCALL32 _PVWCreateInstance_Sem,<ecx,esi,eax>                   ;b724624
;b734380                                ; Create Perfview per-process data blk;
;b734380pop     es                                                     ;b724624
;b734380pop     ds                                                     ;b724624

;b734380ASSUME DS:NOTHING, ES:NOTHING                                  ;b724624
;b734380pop     edx                                                    ;b724624
;b734380pop     edi                                                    ;b724624
;b734380pop     esi                                                    ;b724624
;b734380pop     ecx                                                    ;b724624
;b734380crs1:                                                                  ;sfb
;b734380        ASSUME  fs:NOTHING                                             ;b724624
;       Return handle to user

        mov     ax,di                   ; low word of handle is PTDA byte #
        mov     dx,USERHANDLE_HIGHWORD

;       MMIOPH Performance Hook Removed
;       Maj/Min = 18ECH, Data = ax, dx (sem handle lo:hi)

        clc                             ; indicate no error
        ret

crs2:   dec     SysSemPTDATbl.OpenSemTable[di]

crs3:   DosContext ds
        mov     [bx].SysSemRefCnt,0     ; free system semaphore table entry

crs4:   stc                             ; indicate error, (AX) = error code
        ret

EndProc   w_CreateSem


BREAK   <CreateAnonSem - Create anonymous system semaphore>

;***    CreateAnonSem - Create anonymous system semaphore
;
;       Create a system semaphore without a name. This is useful for
;       kernel clients that don't need to open it by name. The global
;       handle returned can be used by any process.  A system semaphore
;       table entry is allocated and a system (global) type handle is
;       returned that works in all processes.  The handle will work
;       in any of the other semaphore functions and CloseSem (but not
;       w_CloseSem) can be used to get rid of it.
;
;       ENTRY:  DX = NoExclusive flag.
;
;       EXIT:   If carry clear, then
;                       DX:AX = semaphore handle.
;               If carry set, then
;                       AX = error code
;                               ERROR_INVALID_PARAMETER
;                               ERROR_TOO_MANY_SEMAPHORES
;
;       USES:   AX, BX, CX, DX, DI, SI, DS, ES, Flags
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:TASKAREA

Procedure CreateAnonSem,HYBRID

;       PMONLY  TASKTIME
        TaskTime

;       Get and initialize a free system semaphore entry

        call    GetSysSemEntry          ; (BX) = system semaphore entry addr
        jc      short cas1              ; if error return error code

        sub     bx,offset DOSGROUP:SysSemDataTable
        mov     ax,size SysSemTblStruc
        xchg    ax,bx
        xor     dx,dx
        div     bx                      ; divide by size of SysSemTblStruc
        mov     dx,SYSHANDLE_HIGHWORD   ; (DX:AX) = global handle
        clc                             ; indicate no error
cas1:   ret

EndProc   CreateAnonSem


BREAK   <w_OpenSem - Open a system semaphore>

;***    w_OpenSem - Open a system semaphore
;***    OpenSem - Open a system semaphore
;
;       This routine verifies and gets the pointer for the handle
;       passed to the global system semaphore table from the semaphore
;       name table.  The byte count in the PTDA table is incremented,
;       as is the global reference count, indicating one more process
;       using this semaphore.  It then returns the handle to the user,
;       which will be the same for all processes.
;
;       OpenSem does the RMP name table clean up and searches the table
;       for the semaphore name passed.  The global semaphore table entry
;       offset is returned and the per-process handle table entry is
;       allocated.  One for reference is added to the semaphore and the
;       handle is returned.
;
;       ENTRY:  DS:SI = address of semaphore name.
;
;       EXIT:   If carry clear, then
;                       DX:AX = system semaphore handle.
;               If carry set, then
;                       AX = error code
;                               ERROR_INVALID_NAME
;                               ERROR_SEM_NOT_FOUND
;                               ERROR_SEM_USER_LIMIT
;
;       USES:   AX, BX, CX, DX, DI, SI, DS, ES, Flags
;
;       OpenSem(SemName)
;       {
;           CleanUpSysSemName()
;           SemPtr = FindSysSemName(SemName)
;           SemHandle = GetSysSemPTDAIndex(SemPtr)
;           SemPtr->SysSemRefCnt++
;           return(SemHandle)
;       }
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:TASKAREA

Procedure w_OpenSem,FARONLY             ; api entry only

Entry   OpenSem,faronly                 ; kernel entry point

;       PMONLY  TASKTIME
        TaskTime

;       MMIOPH Performance Hook Removed
;       Maj/Min = 186DH, Data = sem name (asciiz string at ds:si)

        SaveReg <si,ds>
        call    CleanUpSysSemName       ; clean up left over names
        RestoreReg <ds,si>

;       Verify that this semaphore already exists

        call    FindSysSemName          ; (BX) = offset of sys sem table entry
        jc      short osm2              ; if error return with error code

;       Increment the PTDA Open Semaphore Table Entry

        call    GetSysSemPTDAIndex      ; (DI) = open sys semaphore PTDA Index
        jc      short osm2

;       Add 1 to the System Semaphore Reference Count

        DosContext ds
        inc     [bx].SysSemRefCnt       ; increment system reference count
        jnz     short osm1
        dec     [bx].SysSemRefCnt       ; correct system reference count
        mov     ax,ERROR_SEM_USER_LIMIT
        stc
        jmp     short osm2

;       Return handle to user

osm1:   mov     ax,di                   ; low word of handle is PTDA byte #
        mov     dx,USERHANDLE_HIGHWORD
        clc                             ; indicate no error
osm2:

;       MMIOPH Performance Hook Removed
;       Maj/Min = 18EDH, Data = ax, dx (sem handle lo:hi)

        ret                             ; if error occurred, (AX) = error code

EndProc   w_OpenSem


BREAK   <w_CloseSem - Close a system semaphore>

;***    w_CloseSem - Close a system semaphore
;***    CloseSem - Close a system semaphore
;
;       This routine validates the handle passed, gets the pointer to the
;       system semaphore data table entry for this handle and decrements
;       the reference count if any thread in this process doesn't own the
;       semaphore. If the reference count is zero, then the system name
;       entry is removed.
;
;       CloseSem returns an error when any of the threads in the process
;       owns the semaphore being closed.  The per-process table reference
;       is removed for "per-process" handles.  The global semaphore table
;       reference is decrement and if goes to zero the semaphore name table
;       entry is removed.
;
;       ENTRY:  AX:BX = system semaphore handle.
;
;       EXIT:   If carry clear, no error
;               If carry set, then
;                       AX = error code
;                               ERROR_PROTECTION_VIOLATION
;                               ERROR_SEM_IS_SET
;
;       USES:   EAX, BX, CX, DI, ESI, DS, ES, FS, Flags
;
;       CloseSem(SemHandle)
;       {
;           SemPtr = GetSysSemPtr(SemHandle)
;           if (owned by a thread in this process) {
;               return(ERROR_SEM_IS_SET)
;           }
;           if(SemHandle is per-process) {
;               --SysSemPTDATbl.OpenSemTable[SemHandle]
;           }
;           --SemPtr->SysSemRefCnt
;           if(SemPtr->SysSemRefCnt == 0) {
;               RemSysSemName(SemPtr)
;           }
;       }
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:TASKAREA

Procedure w_CloseSem,HYBRID             ; api entry only

;       MMIOPH Performance Hook Removed
;       Maj/Min = 186BH, Data = bx, ax (sem handle lo:hi)

        cmp     ax,SYSHANDLE_HIGHWORD
;b724624je      short csm8              ; jump to invalid handle error
        je      csm8                    ; jump to invalid handle error

Entry   CloseSem,HYBRID                 ; kernel entry point

;       PMONLY  TASKTIME
        TaskTime

;       Validate handle and get the pointer to the global data table

        push    ax                      ; save semaphore handle
        push    bx
        CALLFAR GetSysSemPtr            ; (DS:BX) = sys semaphore entry address
   ASSUME   DS:DOSGROUP
        pop     di
        pop     dx                      ; (DX:DI) = semaphore handle
;b724624jc      short csm9
        jc      csm9

;       If the owner has died, SysSemOwner doesn't contain a valid
;       owner slot number.

        test    [bx].SysSemFlag,(SYSSEM_OWNER_DIED+SYSSEM_THREAD_OWNER_DIED)
        jnz     short csm4              ; jump if owner died

;       The handle is valid and BX contains the sys semaphore entry address.
;       If any thread in this process still owns the semaphore, return
;       with an error.

        FlatContext fs,ax
        movzx   eax,ds:[bx].SysSemOwner
        or      eax,eax
        jz      short csm4              ; ok - not owned by this process

        mov     esi,fs:[_papTCBSlots]
        mov     esi,fs:[esi+eax*4]      ; (ESI) = SysSemOwner pTCB
        or      esi,esi
        jz      short csm4              ; jump slot number invalid

        mov     eax,fs:[esi].TCBpPTDA
        cmp     eax,fs:[_pPTDACur]
        jne     short csm4              ; ok - not owned by this process

        ASSUME  fs:NOTHING

;       The semaphore is owned by some thread in this process. The close fails
;       if there are one or less local references to this semaphore. There must
;       be atleast one local (PTDA) reference left so the "owner died" logic at
;       process termination can clean up this semaphore.
;
;       (DX) = handle high word

        cmp     dx,SYSHANDLE_HIGHWORD   ; Is this a global semaphore handle ?
        je      short csm3              ; Yes, always return sem is set error

        cmp     SysSemPTDATbl.OpenSemTable[di],1 ; Are there other references
        ja      short csm5

csm3:   mov     ax,ERROR_SEM_IS_SET     ; return (AX) = error code
        jmp     short csm9

;       The semaphore is not owned by a thread in this process. The refernce
;       counts are decremented.
;
;       (DX:DI) = semaphore handle

csm4:   cmp     dx,SYSHANDLE_HIGHWORD   ; Is this a global semaphore handle ?
        je      short csm6              ; Yes, don't decrement PTDA ref

;       Convert to PTDA index and reduce refernces for this process.

csm5:   dec     [di].SysSemPTDATbl.OpenSemTable

csm6:   dec     [bx].SysSemRefCnt       ; decrement system reference count
        jnz     short csm7              ; jump if threads still have open

;       Remove the performance counter data block                      ;b724624
                                                                       ;b724624
;sfb    FlatContext fs,ax                                              ;b724624
;sfb    push    edx                                                    ;b724624
;sfb    mov     edx,fs:_pPTDACur                                       ;b724624
                                                                       ;b724624;
;sfb    mov     eax,[bx].SysSempPvwBlk  ; (EAX) = value of data blk    ;b724624
;sfb    ASSUME fs:NOTHING                                              ;b724624
;sfb    push    edi                                                    ;b724624
;sfb    push    ds                                                     ;b724624
;sfb    push    es                                                     ;b724624
;sfb    FlatContext <ds,es>,edi                                        ;b724624
                                                                       ;b724624
;sfb    CCALL32 _PVWKillInstance_Sem,<edx,eax>                         ;b724624
;sfb    pop     es                                                     ;b724624
;sfb    pop     ds                                                     ;b724624
;sfb    pop     edi                                                    ;b724624
;sfb    pop     edx                                                    ;b724624
                                                                       ;b724624
;sfb    ASSUME  ds:NOTHING                                             ;b724624
;sfb    ASSUME  es:NOTHING                                             ;b724624



;       (BX) = the table semaphore offset

        call    RemSysSemName           ; remove system semaphore name
csm7:   clc
        jmp     short csmx

;       (AX) = bad selector to be displayed in popup.

csm8:   mov     bx,ERROR_PROTECTION_VIOLATION
        xchg    ax,bx                   ; (AX) = err code, (BX) = bad selector
        CALLFAR SemProtFault            ; carry set, (AX) = error code
csm9:   stc                             ; EXIT error, (AX) = error code
csmx:   ret

EndProc   w_CloseSem


BREAK   <FindSysSemName - Find system semaphore name>

;***    FindSysSemName - Find system semaphore name
;
;       Searches the system semaphore name table and returns
;       the system semaphore table index.  This routine validates
;       the semaphore name by verifying that the name begins with
;       the \SEM\ prefix.  A buffer is allocated put the name string
;       when CanonBuf is called.  This is called to force the name
;       upper case and to change any "/" to "\".
;
;       Note that after the "\SEM\" is validated, it is ignored. The
;       "\SEM\" in the stored names has been partially overwritten by
;       the pointer into the syssem table.
;
;       ENTRY:  DS:SI = address of semaphore name (with "\SEM\" prefix).
;               BX = offset of syssem semaphore table entry.
;
;       EXIT:   If carry is clear, then
;               If carry is set, then
;                       AX = error code
;                               ERROR_INVALID_NAME
;                               ERROR_SEM_NOT_FOUND
;                               ERROR_TOO_MANY_SEMAPHORES
;
;       USES:   AX, BX, CX, DX, SI, DI, ES, DS, Flags
;
;       FindSysSemName(SemName)
;       {
;           Canonicalize(SemName)
;           Size = ValidateName(SemName, "\SEM\")
;           TCYield()
;           Rmp_Find(SemName, Size, &SemSysRmpHdl, SysSemName)
;           if(record not found) {
;               return(ERROR_SEM_NOT_FOUND)
;           }
;           return(OK)
;       }
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:TASKAREA

Procedure FindSysSemName,,LOCAL

LocalVar  BufferAddr,DWORD              ; place for temp buffer address

        xor     bx,bx
        call    CanonBuf                ; copy name string into temp buffer
        EnterProc <es,di>               ;108606 setup stack frame and save buf addr
        jnc     short fsn1              ;64286 if no error continue
        cmp     ax,0                    ;64286 is it a real error or DBCS?
;78544  jc      fsn2                    ;64286 real error
        jz      fsn1                    ;78544 not a real error
        stc                             ;78544 set carry from CanonBuf
        jmp     fsn2                    ;78544 real error

fsn1:                                   ;108606
;108606 jc      short fsn2              ; if carry set, (AX) = error code

;       Check to see if the "\SEM\" prefix is correct

        xor     esi, esi
        lds     si,BufferAddr           ; (DS:SI) = pointer to name in buffer
        DosContext es
        xor     edi, edi
        mov     di,offset DOSGROUP:SysSemPrefix ; (ES:DI) = "\SEM\"
        CALLF32 ValidateName,,nocheck
        mov     ax,ERROR_INVALID_NAME
        jc      short fsn2              ; if carry set, (AX) = error code

;       Must yield to time critical tasks to meet dispatch
;       latency requirements.

        CALLFAR TCYield

        lds     si,BufferAddr           ; (DS:SI) = pointer to name in buffer
        add     si,SemPrefixLen         ; ... not including \SEM\ prefix
        DosContext es                   ; (ES:DI) = addr of RMP handle
        mov     di,offset DOSGROUP:SysSemRmpHdl
        mov     bx,SemPrefixLen         ; set offset into record of name

;       DS:SI = address of name string
;       ES:DI = address of RMP handle structure
;       CX = Length of sem name including terminating NULL but not prefix
;       BX = offset in RMP record of name without the \SEM\ prefix

        call    Rmp_Find
        mov     ax,ERROR_SEM_NOT_FOUND
        jc      short fsn2
        mov     bx,es:[di].SysSemPtr    ; (BX) = offset into sys sem table

fsn2:   pushf                           ; save carry flag
        les     di,BufferAddr
        call    FreeTempBuffer          ; free up the temporary buffer now
        popf                            ; interrupt should always be enabled
        LeaveProc
        ret

EndProc   FindSysSemName


BREAK   <AddSysSemName - Add system semaphore name entry>

;***    AddSysSemName - Add system semaphore name entry
;
;       CanonBuf is called to force the name upper case, to change
;       any "/" to "\" and to move it to the temp buffer allocated.
;       The prefix is verified to be "\SEM\". The part of "\SEM\" is
;       overwritten by the system table pointer - no biggie since we'll
;       never use the "\SEM" again. The name table is searched
;       to see if the semaphore already exists.  The record that has
;       been set up in a temp buffer is added to the system semaphore
;       name table with RMP_Add.  RMP_Add should never be passed a user
;       address; kernel address should always be used for the "record
;       to be added" buffer.
;
;       ENTRY:  DS:SI = address of semaphore name (with "\SEM\" prefix).
;
;       EXIT:   If carry clear, then
;                       BX = offset of system semaphore table entry.
;               If carry set, then
;                       AX = error code
;                               ERROR_INVALID_NAME
;                               ERROR_ALREADY_EXISTS
;                               ERROR_TOO_MANY_SEMAPHORES
;
;       USES:   AX, BX, CX, DX, SI, DI, ES, DS, Flags
;
;       AddSysSemName(SemName, SemPtr)
;       {
;           Canonicalize(SemName)
;           Size = ValidateName(SemName, "\SEM\")
;           TCYield()
;           Rmp_Find(SemName, Size, &SemSysRmpHdl, SysSemName)
;           if(record found) {
;               return(ERROR_ALREADY_EXISTS)
;           }
;           SemRecord.SysSemPtr = SemPtr
;           Rmp_Add(SemRecord, Size, &SemSysRmpHdl)
;           return(OK)
;       }
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:TASKAREA

Procedure AddSysSemName,,LOCAL

LocalVar  ASSN_SysSemPtr,WORD           ; place for ptr into sem table
LocalVar  ASSN_BufferAddr,DWORD         ; place for temp buffer address

        EnterProc <bx,0,0>
        xor     bx,bx                   ; (BX) = offset into buf of string
        call    CanonBuf                ; copy in temp buffer
        mov     ASSN_BufferAddr.Segmt,es; Save buffer address returned
        mov     ASSN_BufferAddr.Offst,di; by CanonBuf
;64286    jc      short asn2              ; if carry set (AX) = error code
        jnc     short asn1              ;64286 if no error continue
        cmp     ax,0                    ;64286 is it a real error or DBCS?
;78544  jc      asn3                    ;64286 real error
        jz      asn1                    ;78544 not a real error
        stc                             ;78544 set carry from CanonBuf
        jmp     asn3                    ;78544 real error

;       Check to see if the "\SEM\" prefix is correct

asn1:   xor     esi, esi
        lds     si,ASSN_BufferAddr      ; (DS:SI) = pointer to buffer
        DosContext es
        xor     edi, edi
        mov     di,offset DOSGROUP:SysSemPrefix ; (ES:DI) = "\SEM\"
        CALLF32 ValidateName,,nocheck
        mov     ax,ERROR_INVALID_NAME
        jc      short asn3              ; if carry set, (AX) = error code

;       Must yield to time critical tasks to meet dispatch
;       latency requirements.

        CALLFAR TCYield

;       store the syssem table ptr with the semaphore name

        mov     ax,ASSN_SysSemPtr       ; (AX) = Ptr into syssem table
        lds     si,ASSN_BufferAddr      ; (DS:SI) = pointer into buffer

;       NOTE: The next instruction will overwrite part of the "\SEM\"
;       prefix. After validation the prefix is no longer needed.

        mov     ds:[si].SysSemPtr,ax    ; Grab ptr into system table
        add     si,SemPrefixLen         ; (SI) = name field after \SEM\
        DosContext es                   ; (ES:DI) = addr of RMP handle
        mov     di,offset DOSGROUP:SysSemRmpHdl
        mov     bx,SemPrefixLen         ; set offset into record

;       DS:SI = address of semaphore name string (without the "\SEM\")
;       ES:DI = address of RMP handle structure
;       CX = length of semaphore name including terminating NULL but nor prefix.
;       BX = offset in RMP record

        push    cx                      ; save name length
        call    Rmp_Find                ; check if name already exists
        pop     cx                      ; (CX) = length of name string
        jc      short asn2
        stc                             ; return error
        mov     ax,ERROR_ALREADY_EXISTS
        jmp     short asn3

asn2:   add     cx,SemPrefixLen         ; (CX) = size of RMP record (full name)
        lds     si,ASSN_BufferAddr      ; (DS:SI) = pointer to record in buffer
        DosContext es                   ; (ES:DI) = addr of RMP handle
        mov     di,offset DOSGROUP:SysSemRmpHdl

;       DS:SI = address of record to add
;       ES:DI = address of RMP handle structure
;       CX = length of record

        call    Rmp_Add
        mov     ax,ERROR_TOO_MANY_SEMAPHORES ; error code in case carry is set

asn3:   pushf                           ; save carry flag
        les     di,ASSN_BufferAddr
        call    FreeTempBuffer          ; free up the temporary buffer now
        popf                            ; interrupt should always be enabled
        LeaveProc
        ret

EndProc   AddSysSemName


BREAK   <CanonBuf - copy and canonicalize name>

;***    CanonBuf - copy and canonicalize name into temp buffer
;
;       We allocate a temp buffer, and copy the name into it.  We
;       uppercase the name and convert all / into \. Checks for
;       invalid characters. Will also check if name has any wild
;       card characters in it and return an error.
;
;       ENTRY   (DS:SI) point to name in user space
;               (BX) is offset in temp buffer where name gets placed
;
;       EXIT:   if carry clear, no errors.
;                       (ES:DI) point to temp buffer name
;               if carry set, (AX) = error code (temp buffer is NOT freed)
;                       ERROR_INVALID_NAME
;
;       USES:   AX, BX, CX, DX, SI, DI, FLAGS
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA

Procedure CanonBuf,<hybrid, far16>

        push    bp
        call    GetTempBuffer           ; (ES:DI) = temporary string buffer
        push    di                      ; save temp area
; 72417 Removed 74655- moved the WFP stuff to TCB so fix isn't needed any more
;       call    Take_Kernel_Sem         ; 74655 get ptda semaphore
;       jc      short cnb98             ; 74655 abnormal wakeup
        lea     bp,[di+MAXPATHLEN]      ; (BP) = past the last byte in buffer
        add     di,bx                   ; (DI) points to beginning of name
        mov     bx,di                   ; (BX) = backup limit
        SetBP_CurrTCB           ; start 72417
        CMP     [bp].TCBcMeta,-1; set meta char count to nothing
        UnSetBP_CurrTCB         ; end   72417

;       (DS:SI) = pointer to ASCIIZ string
;       (ES:DI) = pointer to buffer to put string
;       (BX) = backup limit (offset from ES) on buffer
;       (BP) = pointer past the last byte of (ES:DI) buffer

        CALLFAR Canonicalize
        jc      short cnb99             ; jump if error, (AX) = error code

        SetBP_CurrTCB           ; start 72417
        inc     [bp].TCBcMeta   ; if not -1, then wildcard chars
        UnSetBP_CurrTCB         ; end   72417
        clc
        jnz     short cnb99             ; jump if no wildcard chars

; uppercase string in place

        pop     di                      ; (ES:DI) = temporary buffer addr
        push    di                      ; save temp area

        push    ds                      ; save ds

        push    es
        pop     ds
        mov     si,di                   ; (DS:SI) = (ES:DI) = temp buf
        CALLFAR StrCpy                  ; uppercase string in place

        pop     ds


cnbx:
;72417  Removed 74655- moved the WFP stuff to TCB so fix isn't needed any more
;       pushf                           ; 74655 save carry flag
;       call    Clear_Kernel_Sem        ; 74655 release ptda semaphore
;       popf                            ; 74655 restore carry flag
cnby:                                   ; 74655
        pop     di                      ; (ES:DI) = temporary buffer addr
        pop     bp
        ret

;72417  Removed 74655- moved the WFP stuff to TCB so fix isn't needed any more
;cnb98:  mov     ax,ERROR_INTERRUPT      ; 74655 get ptda semaphore failed
;        stc                             ; 74655
;        jmp     SHORT cnby              ; 74655

cnb99:  mov     ax,ERROR_INVALID_NAME   ; set error code if carry set
        stc
        jmp     SHORT cnbx

EndProc   CanonBuf


BREAK   <FreeSysSem - Frees all system semaphores at process termination>

;***    FreeSysSem - Frees all system semaphores at process termination
;
;       This routine loops thru each entry in the process PTDA open
;       system semaphore table and decrements the reference count. If
;       no other processes have the semaphore opened, then the semaphore
;       is removed from the system.
;
;       ENTRY:  NONE
;
;       EXIT:   NONE
;
;       USES:   AX, BX, CX, DX, SI, DI, DS, ES, Flags
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:TASKAREA

Procedure FreeSysSem,NEAR

;       PMONLY  TASKTIME
        TaskTime
        mov     dx,offset DOSHIGH3CODE:FreeSysSemWorker
        call    EachSysSem              ; free the open system semaphores
        ret

EndProc   FreeSysSem


BREAK   <FreeSysSemWorker - Frees all system semaphores at process termination>

;***    FreeSysSemWorker - Frees all system semaphores at process termination
;
;       This routine is called after all the threads except the last
;       have died and the exitlist handler have been executed.  For
;       each semaphore this process has open, the semaphore's reference
;       count is decremented by the process's total number of references.
;       If the owner died flag is clear and the semaphore is owned
;       by this task, the semaphore is marked owner died.  Any threads
;       waking on this semaphore are woken up.
;
;       ENTRY:  (DS:BX) = ptr to semaphore table entry address (DS = DOSGROUP)
;               (ES:DI) = ptr to high semaphore table entry (ES = DOSGROUP)
;               (SS:SI) = ptr to system semaphore PTDA entry address
;
;       EXIT:   NONE
;
;       USES:   AX, Flags
;
;       FreeSysSem()
;       {
;           SysSemExitList()
;           for(each open system semaphore) {
;               Count = SysSemPTDATbl.OpenSemTable[index]
;               SysSemPTDATbl.OpenSemTable[index] = 0
;               if(!(SemPtr->SysSemFlag & SYSSEM_OWNER_DIED)) {
;                   if(SemPtr->SysSemOwner == TaskNumber) {
;                       SemPtr->SysSemFlag &= ~SYSSEM_EXITLIST_OWNER
;                       SemPtr->SysSemFlag |= SYSSEM_OWNER_DIED
;                       if(SemPtr->SysSemFlag & SYSSEM_WAITING) {
;                           SemPtr->SysSemFlag &= ~SYSSEM_WAITING
;                           ProcRun(SemPtr)
;                       }
;                   }
;               }
;               SemPtr->SysSemRefCnt -= Count
;               if(SemPtr->SysSemRefCnt < 0) {
;                   SemPtr->SysSemRefCnt = 0
;                   RemSysSemName(SemPtr)
;               }
;           }
;       }
;
        ASSUME  DS:DOSGROUP, ES:DOSGROUP, SS:TASKAREA

Procedure FreeSysSemWorker,NEAR,LOCAL

        call    SysSemExitListWorker

        xor     al,al
        xchg    ss:[si].OpenSemTable,al ; (AL) = count, clear PTDA entry

;       Change the thread owner died flag to the real owner
;       died because the whole process is dieing.

        cli
        test    [bx].SysSemFlag,SYSSEM_OWNER_DIED
        jnz     short fss1              ; jump if already owner died

        push    cx
        mov     cx,TaskNumber           ; (CX) = current slot number
        cmp     [bx].SysSemOwner,cx
        pop     cx
        jne     short fss1              ; jump if this thread doesn't own it

        and     [bx].SysSemFlag,not SYSSEM_EXITLIST_OWNER
        or      [bx].SysSemFlag,SYSSEM_OWNER_DIED
        test    [bx].SysSemFlag,SYSSEM_WAITING ; check if any waiting threads
        jz      short fss1              ; jump if no one waiting

        and     [bx].SysSemFlag,not SYSSEM_WAITING
        push    ax
        mov     ax,ds                   ; (AX:BX) = block/run ID
        CALLFAR ProcRun                 ; wake up waiting threads
        pop     ax

fss1:   sti                             ; enable interrupts

;       (AL) = handle reference

        sub     [bx].SysSemRefCnt,al    ; reduce system reference count by AL
        ja      short fss2              ; jump if ref cnt > 0
        mov     [bx].SysSemRefCnt,0     ; ref cnt could be < 0 (or 0)

;       (BX) = system semaphore entry to remove

        call    RemSysSemName           ; remove semaphore name, ignore errors
fss2:   ret

EndProc   FreeSysSemWorker


BREAK   <SysSemExitList - do semaphore processing for exitlist thread>

;***    SysSemExitList - do semaphore processing for exitlist thread
;
;       If there are exitlist handlers and before the exitlist thread
;       start executing these handlers this routine is called to change
;       all the semaphores marked with the "thread died" flag to be owned
;       by the exitlist thread.
;
;       ENTRY:  NONE
;
;       EXIT:   NONE
;
;       USES:   AX, BX, CX, DX, SI, DI, DS, ES, Flags
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:TASKAREA

Procedure SysSemExitList,far16

;       PMONLY  TASKTIME
        TaskTime
        mov     dx,offset DOSHIGH3CODE:SysSemExitListWorker
        call    EachSysSem              ; do semaphore exitlist processing
        RETC

EndProc   SysSemExitList


BREAK   <SysSemExitListWorker - do semaphore processing for exitlist thread>

;***    SysSemExitListWorker - do semaphore processing for exitlist thread
;
;       This routine is called right before the exitlist handlers
;       are called.  Each system semaphore this process has open is
;       checked and if the SYSSEM_THREAD_OWNER_DIED flag is set and
;       this process owns the semaphore (the pid stored in the high
;       data segment parallel table), the SYSSEM_EXITLIST_OWNER flag
;       is set and the owner set to this exitlist thread number.  If
;       the semaphore is SemRequested by the exitlist thread, the flag
;       will indicate the owner died and the owner died error code should
;       be returned.
;
;       ENTRY:  (DS:BX) = ptr to semaphore table entry address (DS = DOSGROUP)
;               (ES:DI) = ptr to high semaphore table entry (ES = DOSGROUP)
;               (SS:SI) = ptr to system semaphore PTDA entry address
;
;       EXIT:   NONE
;
;       USES:   AX, Flags
;
;       SysSemExitList()
;       {
;           SysSemOwnerDied()
;           for(each open system semaphore) {
;               if(SemPtr->SysSemFlag & SYSSEM_THREAD_OWNER_DIED) {
;                   if(SysSemHighData[index].SysSemPidOwner == Pid) {
;                       SemPtr->SysSemFlag &= ~SYSSEM_THREAD_OWNER_DIED
;                       SemPtr->SysSemFlag |= SYSSEM_EXITLIST_OWNER
;                       SemPtr->SysSemOwner = TaskNumber
;                   }
;               }
;           }
;       }
;
        ASSUME  DS:DOSGROUP, ES:DOSGROUP, SS:TASKAREA

Procedure SysSemExitListWorker,NEAR,LOCAL

        call    SysSemOwnerDiedWorker

        cli
        test    [bx].SysSemFlag,SYSSEM_THREAD_OWNER_DIED
        jz      short sel1

        mov     ax,Pid
        cmp     ax,es:[di].SysSemPidOwner
        jne     short sel1

        and     [bx].SysSemFlag,not SYSSEM_THREAD_OWNER_DIED
        or      [bx].SysSemFlag,SYSSEM_EXITLIST_OWNER
        mov     ax,TaskNumber           ; exitlist thread number
        mov     [bx].SysSemOwner,ax

sel1:   sti
        ret

EndProc   SysSemExitListWorker


BREAK   <SysSemOwnerDied - do thread owner died processing>

;***    SysSemOwnerDied -  do thread owner died processing
;
;       For every open system semaphore in this process that is
;       owned by this dieing thread is marked with the thread
;       owner died flag.  This will be turned into a real owner
;       died mark when the whole process dies unless cleaned up
;       by the exitlist handler.
;
;       ENTRY:  NONE
;
;       EXIT:   NONE
;
;       USES:   AX, BX, CX, DX, SI, DI, DS, ES, Flags
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:TASKAREA

Procedure SysSemOwnerDied,far16

;       PMONLY  TASKTIME
        TaskTime
        mov     dx,offset DOSHIGH3CODE:SysSemOwnerDiedWorker
        call    EachSysSem
        RETC

EndProc   SysSemOwnerDied


BREAK   <SysSemOwnerDiedWorker - do thread owner died processing>

;***    SysSemOwnerDiedWorker - do thread owner died processing
;
;       This routine is called right before the exitlist handlers
;       are called.  Each system semaphore this process has open is
;       checked and if the SYSSEM_THREAD_OWNER_DIED flag is set and
;       this process owns the semaphore (the pid stored in the high
;       data segment parallel table), the SYSSEM_EXITLIST_OWNER flag
;       is set and the owner set to this exitlist thread number.  If
;       the semaphore is SemRequested by the exitlist thread, the flag
;       will indicate the owner died and the owner died error code should
;       be returned.
;
;       ENTRY:  (DS:BX) = ptr to semaphore table entry address (DS = DOSGROUP)
;               (ES:DI) = ptr to high semaphore table entry (ES = DOSGROUP)
;               (SS:SI) = ptr to system semaphore PTDA entry address
;
;       EXIT:   NONE
;
;       USES:   AX, Flags
;
;       SysSemExitList()
;       {
;           SysSemOwnerDied()
;           for(each open system semaphore) {
;               if(SemPtr->SysSemFlag & SYSSEM_THREAD_OWNER_DIED) {
;                   if(SysSemHighData[index].SysSemPidOwner == Pid) {
;                       SemPtr->SysSemFlag &= ~SYSSEM_THREAD_OWNER_DIED
;                       SemPtr->SysSemFlag |= SYSSEM_EXITLIST_OWNER
;                       SemPtr->SysSemOwner = TaskNumber
;                   }
;               }
;           }
;       }
;
        ASSUME  DS:DOSGROUP, ES:DOSGROUP, SS:TASKAREA

Procedure SysSemOwnerDiedWorker,NEAR,LOCAL

        cli                             ; disable interrupts

;       If the owner has died, the SysSemOwner can't not be compared
;       to because the slot number might have been reused.

        test    [bx].SysSemFlag,(SYSSEM_OWNER_DIED+SYSSEM_THREAD_OWNER_DIED+SYSSEM_EXITLIST_OWNER)
        jnz     short sod2              ; jump if owner died

        mov     ax,TaskNumber           ; (AX) = current thread number
        cmp     [bx].SysSemOwner,ax
        jne     short sod2

        test    [ProcStatus],PS_XITALL
        jz      short sod1              ; jump if only this thread is dieing

;       The whole process is going to die so do the special type
;       of owner died processing.  The process now owns the system
;       semaphore, the slot number in the owner field doesn't mean
;       anything.  The pid of the process is stored away in the high
;       data extension of the sem table.

        or      [bx].SysSemFlag,SYSSEM_THREAD_OWNER_DIED
        mov     ax,Pid
        mov     es:[di].SysSemPidOwner,ax
        jmp     short sod2

   ASSUME   DS:DOSGROUP
sod1:   or      [bx].SysSemFlag,SYSSEM_OWNER_DIED
        test    [bx].SysSemFlag,SYSSEM_WAITING ; check if any waiting threads
        jz      short sod2              ; jump if no one waiting

        and     [bx].SysSemFlag,not SYSSEM_WAITING
        mov     ax,ds                   ; (AX:BX) = block/run ID
        CALLFAR ProcRun                 ; wake up waiting threads

sod2:   sti                             ; enable interrupts
        ret

EndProc   SysSemOwnerDiedWorker


BREAK   <EachSysSem - call a routine for every open system semaphore>

;***    EachSysSem - call a routine for every open system semaphore>
;
;       Calls the near routine for each valid open system semaphore
;       passing the global system semaphore table address pointer and
;       the system semaphore PTDA table address pointer to this worker
;       routine.
;
;       ENTRY:  (DX) = address of near routine to call for each system
;                      semaphore in this process.
;
;       EXIT:   NONE
;
;       USES:   AX, BX, CX, DX, SI, DI, DS, ES, Flags
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:TASKAREA

Procedure EachSysSem,NEAR,LOCAL

;       PMONLY  TASKTIME
        TaskTime
        mov     di,offset TASKAREA:SysSemPTDATbl.OpenSemTable
        mov     cx,SYSSEM_MAXIMUM       ; set loop counter to max sems allowed

;       (DI) = pointer to PTDA table
;       (CX) = number of PTDA entries
;       (DX) = worker routine address

ess1:   DosContext <ds>,ax
        TaskContext <es>,ax
        xor     al,al
        .errnz  INVALID_ENTRY           ; (AL) = INVALID_ENTRY
        repe scasb                      ; scan for valid entries
        jz      short ess2              ; jump if at end and/or it's zero

        mov     bx,di                   ; convert to relative table position
        sub     bx,offset TASKAREA:SysSemPTDATbl.OpenSemTable + 1
        mov     ax,bx
        shl     bx,1                    ; adjust for high sem table size (2)
        .errnz  (size SysSemHighTableS - 2)
        mov     si,bx                   ; (SI) = index into high sem table
        imul    bx,ax,size SysSemTblStruc
        add     bx,offset DOSGROUP:SysSemDataTable

        cmp     [bx].SysSemRefCnt,0     ; check if semaphore still around
        je      short ess1

        DosContext es
        add     si,offset DOSGROUP:SysSemHighTable

;       (SS:DI) = system semaphore PTDA entry address + 1
;       (ES:SI) = pointer to high semaphore table entry (ES = DOSGROUP)

        pusha
        dec     di                      ; (DI) = now points to the PTDA entry
        xchg    si,di

;       (DS:BX) = semaphore table entry address (DS = DOSGROUP)
;       (SS:SI) = system semaphore PTDA entry address
;       (ES:DI) = pointer to high semaphore table entry (ES = DOSGROUP)
;       (DX) = worker routine address

        call    dx
        popa
        jmp     short ess1

ess2:   ret

EndProc   EachSysSem


BREAK   <CleanUpSysSemName - clean up system semaphore names>

;***    CleanUpSysSemName - clean up system semaphore names>
;
;       If the system semaphore name flag is set, then the system semaphore
;       global table is searched for the entries that need the name entry
;       removed from the RMP segment. The name entries are normally freed
;       when the last reference of the system semaphore is used except when
;       it is at interrupt time with the SemHandle routine passed the
;       NOT_IN_USE flag.
;
;       Since the RMP functions can not be used at interrupt time and
;       SemHandle sometimes decrements a reference to zero at interrupt
;       time, the name table removal has to be delayed to task time.  This
;       routine is called from CreateSem and OpenSem to do this name table
;       cleanup.  Name clean up has to be serialized so no other process
;       (threads are serialized by the enterfscrit thop in sci) can go on
;       and try to create or open the semaphore being cleaned up.  Another
;       process can get into create or open sem because the Rmp_Find and
;       Rmp_Rem in RemSysSemName can block.
;
;       ENTRY:  NONE
;
;       EXIT:   If carry clear, no error
;               If carry set,
;                       (AX) = error code
;                               ERROR_INTERRUPT
;
;       USES:   AX, BX, CX, DX, SI, DI, DS, ES, Flags
;
;       CleanUpSysSemName()
;       {
;           SemRequest(&NameCleanUpSem)
;           if(NameTblCleanUp != 0) {
;               NameTableCleanUp = 0
;               for(each entry in SysSemDataTable) {
;                   if(SemPtr->SysSemFlag & SYSSEM_NAME_CLEANUP) {
;                       SemPtr->SysSemFlag &= ~SYSSEM_NAME_CLEANUP
;                       RemSysSemName(SemPtr)
;                   }
;               }
;           }
;           SemClear(&NameCleanUpSem)
;       }
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING

Procedure CleanUpSysSemName,,LOCAL

        DosContext ,ax                          ; (AX:BX) = sem address
        mov     bx,offset DOSGROUP:NameCleanUpSem
        mov     cx,-1
        mov     di,cx                           ; no timeout
        CALLFAR SemRequest
        jc      short csnx

        DosContext <ds>,ax
        cmp     NameTblCleanUp,0                ; check clean up name table flag
        je      short csn3
        mov     NameTblCleanUp,0                ; clear flag
        mov     cx,SYSSEM_MAXIMUM
        mov     bx,offset DOSGROUP:SysSemDataTable

csn1:   test    [bx].SysSemFlag,SYSSEM_NAME_CLEANUP
        jz      short csn2
        and     [bx].SysSemFlag,not SYSSEM_NAME_CLEANUP
        SaveReg <bx,cx,ds>
        call    RemSysSemName
        RestoreReg <ds,cx,bx>
csn2:   add     bx,size SysSemTblStruc
        loop    csn1
csn3:   mov     ax,ds
        mov     bx,offset DOSGROUP:NameCleanUpSem
        CALLFAR SemClear
csnx:   ret

EndProc   CleanUpSysSemName


BREAK   <RemSysSemName - Remove system semaphore name>

;***    RemSysSemName - Remove system semaphore name
;
;       Remove a system semaphore name table entry.  This routine
;       first searches the RMP managed system semaphore name table
;       for the system semaphore table offset passed.  When it finds
;       the entry, it is removed with RMP_REM.
;
;       ENTRY:  BX = pointer to system semaphore table entry.
;
;       EXIT:   NONE
;
;       USES:   AX, BX, CX, SI, DI, DS, ES, Flags
;
;       RemSysSemName(SemPtr)
;       {
;           SemEntry = Rmp_Find(SemPtr, &SysSemRmpHdl, SysSemPtr)
;           Rmp_Rem(SemEntry, &SysSemRmpHdl)
;       }
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING

Procedure RemSysSemName,HYBRID,LOCAL

LocalVar  SearchHandle,WORD

        EnterProc <bx>                  ; set up stack frame and SearchHandle
;       Remove the performance counter data block
;b734380push     fs
;b734380push     ax
;b734380FlatContext fs,ax

;b734380mov     eax,[bx].SysSempPvwBlk  ;(eax) = value of data blk
;b734380cmp     eax,0
;b734380je      short rem1a
;b734380push    edx
;b734380mov     edx,fs:_pPTDACur

;b734380mov     eax,[bx].SysSempPvwBlk  ; (EAX) = value of data blk
;b734380push    edi
;b734380push    ds
;b734380push    es
;b734380FlatContext <ds,es>,edi

;b734380CCALL32 _PVWKillInstance_Sem,<edx,eax>
;b734380pop     es
;b734380pop     ds
;b734380pop     edi
;b734380pop     edx

;b734380ASSUME  ds:NOTHING
;b734380ASSUME  es:NOTHING
;b734380rem1a:
;b734380pop     ax
;b734380pop     fs
;b734380ASSUME fs:NOTHING

        push    ss
        pop     ds
        lea     si,SearchHandle         ; (DS:SI) = search pattern for RMP
        DosContext es                   ; (ES:DI) = RMP handle address
        mov     di,offset DOSGROUP:SysSemRmpHdl
        mov     cx,2                    ; two bytes to match
        mov     bx,offset SysSemPtr
        call    Rmp_Find
        jc      short rem1              ; don't remove anything

        DosContext ds

;       ES:DI = address of record to remove

        mov     bx,offset DOSGROUP:SysSemRmpHdl
        call    Rmp_Rem
rem1:   LeaveProc
        ret

EndProc   RemSysSemName


BREAK   <GetSysSemPTDAIndex - Increment PTDA entry for system semaphore>

;***    GetSysSemPTDAIndex - Increment PTDA entry for system semaphore
;
;       Increment entry in PTDA Open Semaphore Table, to track
;       references per process.  This routine computes the PTDA
;       open semaphore table index for the given system semaphore
;       address and increments that byte in the PTDA table.  This
;       tracking is done so that multiple references within a process
;       are handled correctly and still allow identical handles to
;       be used.
;
;       ENTRY:  (BX) = system semaphore table entry offset
;
;       EXIT:   If carry clear, then
;                       (DI) = PTDA table index.
;               If carry set, then
;                       (AX) = error code
;                               ERROR_SEM_USER_LIMIT
;
;       USES:   AX, DI, Flags
;
;       GetSysSemPTDAIndex(SemPtr)
;       {
;           SemIndex = (SemPtr - &SysSemDataTable)/(size SysSemTblStruc)
;           ++SysSemPTDATbl.OpenSemTable[SemIndex]
;           return(SemIndex)
;       }
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:TASKAREA

Procedure GetSysSemPTDAIndex,,LOCAL

        mov     di,bx
        sub     di,offset DOSGROUP:SysSemDataTable
        mov     ax,di
        mov     di,size SysSemTblStruc
        push    dx
        xor     dx,dx
        div     di
        pop     dx
        mov     di,ax                   ; convert global address into byte #

        inc     SysSemPTDATbl.OpenSemTable[di]
        jz      short gsi1              ; usage overflow
        clc                             ; otherwise, indicate no errors
        ret

;       we overflowed the count for this process

gsi1:   mov     ax,ERROR_SEM_USER_LIMIT
        dec     SysSemPTDATbl.OpenSemTable[di]
        stc                             ; indicate error, (AX) = error code
        ret

EndProc   GetSysSemPTDAIndex


BREAK   <GetSysSemEntry - Get a free system semaphore table entry>

;***    GetSysSemEntry - Get a free system semaphore table entry
;
;       This routine searches thru the system semaphore data table
;       until it finds a entry with a reference count of zero.  This
;       is the indicator the entry is free. It returns the address of
;       the entry after initializing the various fields in the structure.
;
;       ENTRY:  DX = exclusive/non-exclusive option
;
;       EXIT:   If carry clear, then
;                       DS:BX = pointer to system semaphore table entry.
;               If carry set, then
;                       AX = error code
;                               ERROR_TOO_MANY_SEMAPHORES
;                               ERROR_INVALID_PARAMETER
;
;       USES:   AX, BX, CX, DS, Flags
;
;       GetSysSemEntry(NoExcl)
;       {
;           for(each entry in SysSemDataTable) {
;               if(SemPtr->SysSemRefCnt == 0) {
;                   if(!(SemPtr->SysSemFlag & SYSSEM_NAME_CLEANUP)) {
;                       SemPtr->SysSemFlag = 0
;                       if(NoExcl == EXCLUSIVE) {
;                          SemPtr->SysSemFlag |= SYSSEM_EXCLUSIVE
;                       }
;                       SemPtr->SysSemOwner = SEM_CLEAR
;                       SemPtr->SysSemRefCnt = 1
;                       SemPtr->SysSemProcCnt = 0
;                       return(SemPtr)
;                   }
;               }
;           }
;           return(ERROR_TOO_MANY_SEMAPHORES)
;       }
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING

Procedure GetSysSemEntry,,LOCAL

;       Validate exclusive parameter

        cmp     dx,NONEXCLUSIVE         ; check exclusive option
        jbe     short gse1
        mov     ax,ERROR_INVALID_PARAMETER
        jmp     short gse4              ; return an error

gse1:   DosContext ds
        mov     bx,offset DOSGROUP:SysSemDataTable
        mov     cx,SYSSEM_MAXIMUM

gse2:   cmp     [bx].SysSemRefCnt,0
        jne     short gse3
        test    [bx].SysSemFlag,SYSSEM_NAME_CLEANUP
        jz      short gse5

gse3:   add     bx,size SysSemTblStruc
        loop    gse2

;       This is the end of the loop and we did not find an available entry

        mov     ax,ERROR_TOO_MANY_SEMAPHORES
gse4:   stc                             ; indicate error, (AX) = error code
        ret

gse5:   mov     [bx].SysSemFlag,0       ; clear flag byte
        cmp     dx,NONEXCLUSIVE         ; see if owner exclusive flag set
        je      short gse6
        or      [bx].SysSemFlag,SYSSEM_EXCLUSIVE
gse6:   mov     [bx].SysSemOwner,SEM_CLEAR
        mov     [bx].SysSemRefCnt,1     ; initialize system reference count
        mov     [bx].SysSemProcCnt,0    ; initialize process requested count
        clc                             ; indicate no errors
        ret

EndProc   GetSysSemEntry

HIGH3CODE ENDS

        FARCODE LOW
CODE    SEGMENT

BREAK   <dh_SemHandle - Change a user semaphore handle to an system handle>

;***    dh_SemHandle - Change a user semaphore handle to an system handle
;
;       Used by device drivers to convert a user per-process semaphore
;       handle into a handle that can be used at interrupt time and in
;       another process's context.
;
;       ENTRY:  AX:BX = semaphore handle.
;               DH = usage flag, NOT_IN_USE or IN_USE.
;
;       EXIT:   If carry clear, then
;                       if DH == IN_USE
;                               AX:BX = system handle for the semaphore.
;               If carry set, then
;                       AX = error code
;                               ERROR_PROTECTION_VIOLATION
;
;       USES:   AX, BX, Flags
;
;               FS and GS must be preserved (DevHelp requirement)
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING

Procedure dh_SemHandle

;       DUALMODE
        SaveReg <cx,dx,si,di,ds,es,fs,gs>
        call    SemHandle
        RestoreReg <gs,fs,es,ds,di,si,dx,cx>
        ret

EndProc   dh_SemHandle


BREAK   <SemHandle - Change a user semaphore handle to an system handle>

;***    SemHandle - Change a user semaphore handle to an system handle
;
;       This routine is used to convert a "per-process" handle into
;       a "global" for use at interrupt time and to remove "global"
;       handle references from a system semaphore.  RAM semaphores pass
;       right thru this routine untouched.  If the IN_USE flag is passed,
;       the reference count of the semaphore is incremented.  If the
;       NOT_IN_USE is passed, the reference count of the semaphore is
;       decremented and the semaphore name entry removed if the reference
;       count went zero and we are not at interrupt time.  If it is interrupt
;       time, the semaphore entry is marked with the SYSSEM_NAME_CLEANUP
;       flag and the global cleanup flag is set.  The next time CreateSem
;       or OpenSem is called, the name table entry for this semaphore
;       is removed (at task time when RMP can be called).
;
;       ENTRY:  AX:BX = semaphore handle.
;               DH = usage flag, NOT_IN_USE or IN_USE.
;
;       EXIT:   If carry clear, then
;                       if DH == IN_USE
;                               AX:BX = system handle for the semaphore.
;               If carry set, then
;                       AX = error code
;                               ERROR_PROTECTION_VIOLATION
;                               ERROR_SEM_USER_LIMIT
;                               ERROR_INVALID_PARAMETER
;
;       USES:   AX, BX, CX, DX, SI, DI, DS, ES, Flags
;
;       NOTE:   Can be called in real and protected mode.
;
;       SemHandle(SemHandle, UsageFlag)
;       {
;           if(SemHandle is not a per-process handle) {
;               if(SemHandle is not a global handle) {
;                   return(OK)
;               }
;           }
;           SemPtr = GetSysSemPtr(SemHandle)
;           if(UsageFlag == IN_USE) {
;               SemPtr->SysSemRefCnt++
;               SemHandle.HighWord = SYSHANDLE_HIGHWORD
;               return(SemHandle)
;           }
;           else {
;               if(UsageFlag == NOT_IN_USE) {
;                   if(SemPtr->SysSemRefCnt == 0) {
;                       return(ERROR_INVALID_PARAMETER)
;                   }
;                   SemPtr->SysSemRefCnt--
;                   if(SemPtr->SysSemRefCnt == 0) {
;                       if(InterruptLevel == interrupt time) {
;                           SemPtr->SysSemFlag |= SYSSEM_NAME_CLEANUP
;                           NameTblCleanUp = 1
;                       }
;                       else {
;                           RemSysSemName(SemPtr)
;                       }
;                   }
;               }
;               else {
;                   return(ERROR_INVALID_PARAMETER)
;               }
;           }
;           return(OK)
;       }
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING

Procedure SemHandle,<near,faronly,far16>

;       DUALMODE
        push    bx                      ; save handle for later

;       Check if an RAM semaphore

        cmp     ax,USERHANDLE_HIGHWORD
        je      short shd1
        cmp     ax,SYSHANDLE_HIGHWORD
        jne     short shd7              ; return if an ram semaphore handle

;       we have an user process semaphore handle

shd1:   call    GetSysSemPtr            ; (DS:BX) = sem table pointer for handle
   ASSUME   DS:DOSGROUP
        jc      short shd8              ; jump if any errors

        cmp     dh,NOT_IN_USE           ; check in use flag
        je      short shd2

        cmp     dh,IN_USE               ; make sure the use flag is valid
        jne     short shd3

        inc     [bx].SysSemRefCnt       ; one more reference
        jnz     short shd6              ; if the count doesn't rolls over, exit
        dec     [bx].SysSemRefCnt       ; fix reference
        mov     ax,ERROR_SEM_USER_LIMIT
        stc
        jmp     short shd8

shd2:   cmp     [bx].SysSemRefCnt,0     ; if already zero, error
        je      short shd3
        dec     [bx].SysSemRefCnt       ; one less reference
        jnz     short shd6              ; check if the semaphore should go away

        cmp     InterruptLevel,-1       ; can not remove the name entry at
        je      short shd4              ;  interrupt time.

        or      [bx].SysSemFlag,SYSSEM_NAME_CLEANUP
        mov     NameTblCleanUp,1
        jmp     short shd6

shd3:   mov     ax,ERROR_INVALID_PARAMETER
        stc
        jmp     short shd8

;       (BX) = system semaphore entry to remove
;
;       We must allow SemHandle to be called in real mode, but
;       to remove the name entry with RMP we must switch to
;       protected mode.

shd4:   whatmode ax
        jc      short shd5
        call    ProtMode
        CALLFAR RemSysSemName           ; remove system semaphore name
        call    RealMode
        jmp     short shd7

shd5:   CALLFAR RemSysSemName           ; remove system semaphore name
        jmp     short shd7

;       Since the low word of the "per-process" handle is the
;       same as the low word of the "global" handle, all we
;       have to do is to change the high word to indicate this
;       is now at "global" handle.  The BX restored at the end
;       of this procedure is the low word part of the handle.

shd6:   mov     ax,SYSHANDLE_HIGHWORD
shd7:   clc                             ; no errors
shd8:   pop     bx
        ret

EndProc   SemHandle


BREAK   <GetSysSemPtrChk - Get system semaphore table pointer and check>

;***    GetSysSemPtrChk - Get system semaphore table pointer and check
;
;       Converts the system semaphore handle into a index
;       into semaphore table and checks to see if the Owner
;       Exclusive flag is set, and if it is not set, check
;       to see if requested thread belongs to the process that
;       currently owns the semaphore.
;
;       If this system semaphore is an exclusive semaphore and the
;       semaphore is not clear, this routine will check if this
;       process is the owner.  If it isn't, an error is returned.
;
;       ENTRY:  AX:BX = semaphore dword handle.
;
;       EXIT:   If carry clear, then
;                       DS:BX = pointer to the system semaphore table entry.
;               If carry set, then
;                       AX = error code
;                               ERROR_PROTECTION_VIOLATION
;                               ERROR_EXCL_SEM_ALREADY_OWNED
;                               ERROR_INVALID_AT_INTERRUPT_TIME
;                               NO_ERROR (special case, see SemProtFault)
;
;       USES:   AX, BX, DS, Flags
;
;       NOTE:   This call was a hybrid that has been explicitly broken
;               into FAR and NEAR routines.  FAR routine MUST immediately
;               precede the NEAR routine, and the NEAR routine must
;               immediately precede the NEAR version of GetSysSemPtr.
;               All the actual code for this routine has been moved to the
;               other side of GetSysSemPtr, in the routine ChkSysSemPtr.
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING

Procedure GetSysSemPtrChk,FARONLY

        push    offset cs:FarRet
; IF2
;        .errnz $-GetSysSemPtrChk
;ENDIF

EndProc GetSysSemPtrChk

Procedure GetSysSemPtrChk,NEAR

        push    offset cs:ChkSysSemPtr
;IF2
;        .errnz $-GetSysSemPtr
;ENDIF

EndProc   GetSysSemPtrChk

BREAK   <GetSysSemPtr - Get a system semaphore table pointer>

;***    GetSysSemPtr - Get a system semaphore table pointer
;
;       This routine takes the system semaphore handle checks if it is
;       an internal/device driver semaphore handle. If it is, it will
;       return the offset into the system semaphore table for this
;       handle as the pointer. If it is a user semaphore handle, it
;       validates that it is with in the range of the PTDA open semaphore
;       table, checks that the entry in this table is valid.  We always
;       simulate a GP fault in a system for even bad system semaphore
;       handles because really don't know if what was intended.
;
;       If there is a bad handle which then SemProtFault is called and
;       Indos == 0, SemProtFault will return NO_ERROR indicating that
;       this operation needs to be done in real kernel mode.
;
;       ENTRY:  AX:BX = semaphore dword handle.
;
;       EXIT:   If carry clear, then
;                       DS:BX = pointer to the system semaphore table entry.
;               If carry set, then
;                       AX = error code
;                               ERROR_PROTECTION_VIOLATION
;                               NO_ERROR (special case, see SemProtFault)
;               (DS) = DOSGROUP
;
;       USES:   AX, BX, DS, Flags
;
;       NOTE:   Can be called in real and protected mode.  It was a hybrid
;               procedure that has been split into explicit NEAR and FAR
;               routines, for performance reasons.  The NEAR routine must
;               be defined first, so that execution can fall through from
;               above.
;
;       GetSysSemPtr(SemHandle)
;       {
;           if(SemHandle.LowWord < SYSSEM_MAXIMUM) {
;               if(SemHandle.HighWord == USERHANDLE_HIGHWORD) {
;                   if(SysSemPTDATbl[SemHandle.LowWord] == INVALID_ENTRY) {
;                       goto error
;                   }
;               }
;               else {
;                   if(SemHandle.HighWord != SYSHANDLE_HIGHWORD) {
;                       goto error
;                   }
;               }
;               SemPtr = SemHandle.LowWord * (size SysSemTblStruc)
;               SemPtr += &SysSemDataTable
;               if(SemPtr->SysSemRefCnt != 0) {
;                   return(SemPtr)
;               }
;           }
;       error:
;           return(SemProtFault(ERROR_PROTECTION_VIOLATION, SemHandle.HighWord))
;       }
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING

Procedure GetSysSemPtr,NEAR

;       DUALMODE
        DosContext ds

        cmp     bx,SYSSEM_MAXIMUM       ; check to see if user handle is valid
        jae     short gsp2

;       Check what kind of handle we have; it is either a user handle where
;       we have to look up the semaphore table offset in the open semaphore
;       PTDA table or it is a system handle used by device drivers for
;       system semaphore operation where the low word of the handle is the
;       index into the global system semaphore table.

        cmp     ax,SYSHANDLE_HIGHWORD
        je      short gsp1

        cmp     ax,USERHANDLE_HIGHWORD  ; make sure we have a user semaphore
        jne     short gsp2

;       we have an user process semaphore handle

;       DUALMODE TASKTIME               ; can be only used at task time
        TaskTime
   ASSUME   SS:TASKAREA

        cmp     SysSemPTDATbl.OpenSemTable[bx],INVALID_ENTRY
        je      short gsp2              ; invalid handle for this process

;       make index into global data table

gsp1:   imul    bx,bx,size SysSemTblStruc ; multiply by size of SysSemTblStruc
        add     bx,offset DOSGROUP:SysSemDataTable

        cmp     [bx].SysSemRefCnt,0     ; check if global table entry is valid
        je      short gsp2
        clc                             ; EXIT no errors
        ret

;       (AX) = bad selector to be displayed in popup.

gsp2:   mov     bx,ERROR_PROTECTION_VIOLATION
        xchg    ax,bx                   ; (AX) = err code, (BX) = bad selector
        call    SemProtFault            ; EXIT with error, (AX) = error code
        ret

EndProc   GetSysSemPtr


Procedure GetSysSemPtr,FARONLY

        call    GetSysSemPtr
FarRet: ret

EndProc   GetSysSemPtr


BREAK   <ChkSysSemPtr - Body of GetSysSemPtrChk>

;***    ChkSysSemPtr - Body of GetSysSemPtrChk
;
;       This routine does the check for exclusive semaphores.  If an
;       exclusive semaphore is not owned by the current process, this
;       routine returns an error.  If we at interrupt time context, an
;       error is returned.  If the SYSSEM_THREAD_OWNER_DIED flag is set
;       consider, no thread or process (yet) owns the semaphore so an
;       error is returned.  If the SYSSEM_OWNER_DIED flag is set, the
;       ownership of the semaphore doesn't matter and no error is
;       returned.
;
;       GetSysSemPtrChk(SemHandle)
;       {
;           SemPtr = GetSysSemPtr(SemHandle)
;           if(SemPtr->SysSemFlag & SYSSEM_EXCLUSIVE) {
;               if(SemPtr->SysSemOwner == SEM_CLEAR) {
;                   return(SemPtr)
;               }
;               if(InterruptLevel == interrupt time) {
;                   return(ERROR_INVALID_AT_INTERRUPT_TIME)
;               }
;               if(SemPtr->SysSemFlag & SYSSEM_THREAD_OWNER_DIED) {
;                   return(ERROR_EXCL_SEM_ALREADY_OWNED)
;               }
;               if(SemPtr->SysSemFlag & SYSSEM_OWNER_DIED) {
;                   return(SemPtr)
;               }
;               if(SemPtr->SysSemOwner != TaskNumber) {
;                   return(ERROR_EXCL_SEM_ALREADY_OWNED)
;               }
;           }
;           return(SemPtr)
;       }
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING

Procedure ChkSysSemPtr,NEAR,LOCAL

    ASSUME   DS:DOSGROUP
        jc      short soe5                      ; jump if error in GetSysSemPtr

        test    [bx].SysSemFlag,SYSSEM_EXCLUSIVE; jump if semaphore is exclusive
        jnz     short soe1
        ret                                     ; EXIT (carry cleared by TEST)

soe1:   cmp     [bx].SysSemOwner,SEM_CLEAR
        jne     short soe2                      ; jump if semaphore set
        ret                                     ; EXIT (carry already clear)

soe2:   cmp     InterruptLevel,-1               ; running at interrupt time?
        jne     short soe3                      ; jump if interrupt time

;       The "SysSemOwner" doesn't contain a valid slot number
;       for determining the owner if SYSSEM_THREAD_OWNER_DIED is
;       set.  We are saving semaphores marked this way for the
;       exitlist handler to have a chance at first before it becomes
;       a owner died semaphore.  The exitlist handler thread (which
;       we don't know yet) effectively owns this semaphore so the
;       SemClear or SemSet request is failed.

        test    [bx].SysSemFlag,SYSSEM_THREAD_OWNER_DIED
        jnz     short soe4                      ; jump if thread owner died

;       If the previous owner has died, nobody really owns
;       the semaphore, so SemClears and SemSets are ok on
;       owner died exclusive semaphores.

        test    [bx].SysSemFlag,SYSSEM_OWNER_DIED
        jnz     short soe5                      ; exit (carry cleared by TEST)

        mov     ax,TaskNumber                   ; (AX) = current thread number
        cmp     [bx].SysSemOwner,ax             ; see if thread is from
        jne     short soe4                      ;  owner's process
        ret                                     ; EXIT (carry already clear)

soe3:   mov     ax,ERROR_INVALID_AT_INTERRUPT_TIME
        ret                                     ; EXIT (carry already set)

soe4:   mov     ax,ERROR_EXCL_SEM_ALREADY_OWNED
        stc                                     ; EXIT (error)
soe5:   ret

EndProc   ChkSysSemPtr

CODE    ENDS

        END
