;       SCCSID = @(#)muxsem.asm 6.7 92/03/11
SCCSID  equ     "@(#)muxsem.asm 6.7 92/03/11"
PAGE    ,132
TITLE   MUXSEM - Multiple wait semaphore routines
NAME    MUXSEM
.286p

;***    MUXSEM.ASM
;
;       IBM/Microsoft Confidential
;
;       Copyright (c) IBM Corporation 1987, 1989
;       Copyright (c) Microsoft Corporation 1987, 1989
;
;       All Rights Reserved
;
;       Title:
;
;       OS/2 multiple wait semaphore routines
;
;
;       Description:
;
;       This module allows multiple RAM or system semaphores to be
;       waited upon until any of the semaphores are cleared. Unlike
;       the other semaphore wait functions, MuxSemWait is edge
;       triggered and will always return when one of the semaphores
;       is cleared no matter how short the clear is.  SemWait could
;       miss a clear and set of a semaphore and not wake up.
;
;       w_MuxSemWait    Wait for one of N semaphores to be cleared. This
;                       function can not be used internally by the kernel
;                       with data segments at ring 0.
;
;       MuxSemWait      Wait for one of N semaphores to be cleared. Can
;                       be used by kernel data segments at ring 0.
;
;       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 code that initializes the MuxSemWait
;       table's free list.
;
;
;       Design/Implementation Notes:
;
;       DosMuxMuxWait uses two major data structures: the event
;       list (MuxList) passed by the API and the muxwait table
;       (MuxTable).
;
;       The event list contains a count of the number of entries
;       which are composed of a reserved word of 0 and a dword
;       containing the RAM or system semaphore handle.  The maximum
;       number of semaphores one can wait on is 16.
;
;       DosMuxSemWait is "edge" triggered.  This means that clearing
;       a semaphore that is being waited on with DosMuxSemWait will
;       always be detected and wake up the muxwait.  The other semaphore
;       functions may miss a clear semaphore if the semaphore is cleared
;       and set before the thread has a chance to run.  To keep track
;       of a clear on a semaphore for DosMuxSemWait, a list containing
;       the semaphores being muxwaited upon is needed.  This list consists
;       of linked together muxwait table entries.  Each entry contains the
;       following:
;
;       MuxLink word
;       MuxThreadID word
;       MuxType byte
;       MuxSemID dword
;
;       Free muxwait table entries are chained together with the MuxLink
;       field with the head in FreeChain.  All the entries currently
;       being used in a muxwait are kept on the ActiveChain list chained
;       together with the MuxLink field.
;
;       The MuxThreadID is the slot number of the muxwaiting thread.  It
;       is the low word of the block ID for the muxwaiting thread.  The
;       high word is the value MUX_BLOCK_ID.
;
;       The MuxType field indicates the type of MuxSemID.  Since MuxSemWait
;       is "edge" triggered, a unique identifer for the semaphore is
;       needed.  The MuxType field can have the following values:
;
;       MUXTYPE_CLEAR           The semaphore this entry represents has
;                               been cleared.  The muxwaiter has been
;                               woken up.
;
;       MUXTYPE_SYSSEM          The muxwait table entry represents a system
;                               semaphore.  The MuxSemID field contains the
;                               address of the global system semaphore table
;                               entry.
;
;       MUXTYPE_RAMHANDLE       The muxwait table entry represents a RAM
;                               semaphore.  The MuxSemID field contains the
;                               virtual memory manager's handle and offset
;                               of the RAM semaphore.  This ID aways uniquely
;                               idenifies the RAM semaphore.
;
;       MUXTYPE_RAMPHYS         The muxwait table entry represents a RAM
;                               semaphore.  The MuxSemID field contains the
;                               physical address of the RAM semaphore.  When
;                               the segment has no memory manager handle or
;                               the semaphore needs to be cleared at interrupt
;                               time, the physical address is used.  This
;                               assumes the memory is fixed or locked and the
;                               DosMuxSemWait and SemClear are done with a
;                               "UVIRT" type selector alias.
;
;       When a DosMuxSemWait is done, a block of muxwait table entries
;       are removed from the free list and filled in with the thread
;       number and a unique idenifier the semaphore the entry represents.
;       This list is added to the active muxwait list which is search
;       when a SemClear is done on a semaphore.  There is a flag in
;       the semaphore data structure to indicate that someone is muxwaiting
;       on this semaphore so the muxwait active list is not search for
;       every SemClear.  If the SemClear is done at interrupt time on
;       a RAM semaphore, the selector of the RAM semaphore passed to the
;       DosMuxSemWait and the SemClear has to be a "UVIRT" alias.  This
;       limitation is present because the virtual memory manager handle
;       can not be obtained at interrupt time.
;
;       DosMuxSemWait uses a performance optimiztation of doing as
;       much as possible at ring 0 without going into kernal mode
;       (Indos == 0).  This could introduce very subtle bugs if some
;       subroutine called in this special mode blocks.  Accessing
;       user data segments that are not present is an implicit block.
;       Faulting a process (via SemProtFault) because of a bad handle
;       could possibly block.  The subroutines that can be used in this
;       special kernel mode return carry set and NO_ERROR (0) to indicate
;       that real kernel mode is needed.
;
;
;       Public routines:
;
;       w_MuxSemWait
;       MuxSemWait
;       SysWakeMuxWait
;       RamWakeMuxWait
;
;
;       Modification History:
;
;       28 Aug 1986 - MikeM
;           Initial split from sem.asm.
;
;       20 Nov 1986 - MikeM
;           Allowed MuxSemWait to be used from kernel routines.
;
;       18 Jun 1989 - Sudeepb
;           Cleaned up to use new Enter/Exit kmode interfaces
;
;       3  Aug 1994 - RAC Strip off sig bits from semaphore id before
;                     calling QUERYEVENTSEM worker routine.
;

        CpuMode 286
.xlist
include basemaca.inc
include fnotask.inc
include osmaca.inc
include dosseg.inc
include error.inc
include error2.inc
include mi.inc
include protmode.inc
include seldesc.inc
include proc.inc
include sem.inc
include kmode.inc
include sysentry.inc
include ordinals.inc
include tkexport.inc
include ostypes.inc
include osconst.inc
include dossymc.inc
include sigdef.inc
include except.inc
include infoseg.inc
include pgexport.inc
include vmowner.inc
include vmexport.inc
include selxport.inc
include curdir.inc
include dirent.inc
include find.inc
include screen.inc
include tkptda.inc
include semxport.inc
include mmpha.inc
.list


        ModName MUXSEM, FNO_MUXSEM_ASM

        ReferFlat       _flGlobalActions,DWORD
        ReferFlat       SmlPostCt,DWORD

        ReferGlobal     TaskNumber,WORD
        ReferGlobal     InterruptLevel,BYTE

        EXTRNFAR        GetSysSemPtr            ; in syssem.asm
        EXTRNFAR        SemProtFault            ; in sem.asm
        EXTRNFAR        SemBlock                ; in sem.asm
        EXTRNFAR        ExitCritSecUnHoldSig

        EXTRN           UVirtToPhys:near

        EXTRN32         _VMGetHandle
        EXTRN32         _SELConvertToLaddr
        EXTRN32         _PGVerifyInfoSegs
        EXTRN32         KMSETCLIENTREGISTER
        EXTRN32         _SEMQueryEventSem
        EXTRN32         _SEMMuxWait16
        EXTRN           ProcRun:NEAR

        ReferFlat       _pTCBCur,DWORD
        ReferFlat       _pPTDACur,DWORD

        PUBLIC          MuxTable

DATA segment

;*  The linked list of active MuxSemWait table entries

ActiveChain     dw      END_LINK


;*  The linked list of free MuxSemWait table entries

FreeChain       dw      offset DOSGROUP:MuxTable


;*  The MuxSemWait data table

MuxTable        MuxTableEntry   MAX_MUX_ENTRIES dup (<>)

DATA ends


         FARCODE HIGH3
HIGH3CODE SEGMENT

BREAK   <DOSMUXSEMWAIT - Wait for one of N semaphores to be cleared>

;***    DOSMUXSEMWAIT - Wait for one of N semaphores to be cleared
;
;       INVOKE  push@   DWORD IndexNbr  ; index number returned (2 words)
;               push@   OTHER ListAddr  ; semaphore list        (2 words)
;               push    DWORD TimeOut   ; timeout value         (2 words)
;               call    DOSMUXSEMWAIT
;
;       RETURN  (AX) = return code
;                       0 - success

;***DT  preDOSMUXSEMWAIT - DosMuxSemWait pre-invocation trace point
;
;    TRACE MINOR = <RAS_PRE_DOSMUXSEMWAIT>,
;          TP    = .DOSMUXSEMWAIT,
;          TYPE  = (PRE,API),
;          GROUP = SEM,
;          DESC  = "(OS) DosMuxSemWait Pre-Invocation",
;          MEM   = (RSS+SP+8,D,4),      /* ArgVar TimeOut - skip CS:EIP. */
;          FMT   = "Timeout=<FMT_C_DWORD>"

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

SysEntryBegin DOSMUXSEMWAIT,ORD_DOSMUXSEMWAIT,6,,inline,0

ArgVar  UserBP, WORD
ArgVar  UserCSIP, <size FarPtr32>
ArgVar  TimeOut, DWORD
ArgVar  ListAddr, DWORD
ArgVar  IndexPtr, DWORD

        CpuMode 386

        push    bp
        mov     bp,sp                   ; (BP) = argument/register pointer
        xor     eax,eax                 ; (EAX) = NO_ERROR (0), set return code
        .errnz  NO_ERROR
        SaveReg <ds, es, fs, gs>        ; @DB73658
        pushad

        MMPHPreDOSMuxSemWait            ; Performance Hook (180Ah)
                                        ; Note: Post hook #1 is handled thru SCI

        mov     ax,IndexPtr.Segmt       ; (AX) = Index selector
        StampRPL ax,bx                  ; RPL stamp Index pointer
        mov     IndexPtr.Segmt,ax

        mov     ax,ListAddr.Segmt       ; (AX) = MuxList selector
        StampRPL ax,bx                  ; RPL stamp MuxList pointer
        mov     ListAddr.Segmt,ax

        mov     bx,ListAddr.Offst       ; (AX:BX) = MuxList address
        push    bp                      ; ScanMuxList uses BP
        call    ScanMuxList             ; (AX) = index of entry found clear
        pop     bp
        jc      dmw3                    ; if carry set, (AX) = error code

    ASSUME   DS:NOTHING

;       (AX) = index of entry found clear

        SetTrap <offset cs:dmw4>,,,si   ; install local fault handler
        lds     bx,IndexPtr             ; (DS:BX) points to word
        mov     ds:[bx],ax              ; store index in user space
        ClrTrap si                      ; remove local fault handler

;       This code checks the Global Actions and TCB force flags
;       and enter/exits kernal mode to do the rescheding or
;       force action.
;
;       DS and ES are the user's segment registers so
;       that KMExitKmodeCallGate can validate these selectors
;       on the way out.
dmw1:

;       MMIOPH Performance Hook
;       Maj/Min = 188AH, Data = ax  (index or return code)
        MMPHPostDOSMuxSemWait2          ; Performance Hook (188Ah)

;       If any global, process, or thread actions, call Enter/Exit Kmode

        FlatContext ds
        mov     eax,[_flGlobalActions]
        or      eax,ss:[TKOPTDA].ptda_ForcedActions
        mov     si,ss:[TKOPTDA].CurrTCB ; (SS:SI) = current TCB pointer
        or      eax,ss:[si].TCBForcedActions
        jnz     short dmw2

        popad
        RestoreReg <gs, fs, es, ds>     ; @DB73658
        pop     bp

;***DT  postDOSMUXSEMWAIT2 - DosMuxSemWait post-invocation trace point (2)
;
;    TRACE MINOR = <RAS_POST_DOSMUXSEMWAIT2>,
;          TP    = .postDOSMUXSEMWAIT2,
;          TYPE  = (PRE,API),
;          GROUP = SEM,
;          DESC  = "(OS) DosMuxSemWait Post-Invocation (No Wait)",
;          MEM   = (RSS+SP+8+<SKIP_DWORD>+<SKIP_PTR>, I, 2),
;          REGS  = (AX),
;          FMT   = "Index = <FMT_C_WORD>  Return Code = <FMT_WORD>"

        PUBLIC  postDOSMUXSEMWAIT2
postDOSMUXSEMWAIT2:                     ; RAS post-invocation trace

        RETD    <?argbytes>             ; EXIT to user space

dmw2:   popad                           ; restore registers
        RestoreReg <gs, fs, es, ds>     ; @DB73658
        pop     bp
        KMEnterKmodeCallGate <?argwords*2>,noinline,PASCAL
        KMExitKmodeCallGate  <?argwords*2>,noinline,PASCAL

dmw3:   or      ax,ax                   ; Special error?
        .errnz  NO_ERROR
        je      short dmw5              ; yes, use SCI

        mov     word ptr [esp].pads_eax,ax  ; use pushad struc to return error
        jmp     dmw1

dmw4:   ClrTrap
;       We can't handle the situation, so we'll
;       fall through into SCI.
dmw5:   popad
        CpuMode reset
        RestoreReg <gs, fs, es, ds>     ; @DB73658
        pop     bp

        CallSCI
        ld_cx                           ; double word timeout
        ld_di                           ; . . . in di:cx
        ld_bx                           ; Semaphore list address
        rplstampsel
        ld_ax                           ; . . . in ax:bx

        farcall w_MuxSemWait, ORD_DOSMUXSEMWAIT                           ;@SEC
        skip_2ptr                       ; skip list addr and timeout
        st_ax                           ; Semaphore index returned

;***DT  postDOSMUXSEMWAIT - DosMuxSemWait post-invocation trace point (1)
;
;    TRACE MINOR = <RAS_POST_DOSMUXSEMWAIT>,
;          TP    = .postDOSMUXSEMWAIT,
;          TYPE  = (PRE,API),
;          GROUP = SEM,
;          DESC  = "(OS) DosMuxSemWait Post-Invocation (Waited)",
;          <LOG_AX>,
;          <LOG_RETVAL>,
;          FMT   = "Index = <FMT_AX>  Return Code = <FMT_RETVAL>"

        RASposttrace                    ; RAS post-invocation trace

EndEntry DOSMUXSEMWAIT,,NoCheck


BREAK   <ScanMuxList - scans MuxSemWait list>

;***    ScanMuxList - scans MuxSemWait list>
;
;       This function scans the MuxWait list for a clear
;       semaphore.  If it finds one, it returns the index
;       into the list for that semaphore.  If no semaphore
;       on the list is clear, it returns a special error
;       code. If the ram semaphore is invalid or not present
;       it will stop the scan and cause the normal entry
;       into kernel mode.
;
;       ENTRY:  (AX:BX) = MuxSemWait list address
;
;       EXIT:   Carry set
;                   (AX) = error code
;                       ERROR_PROTECTION_VIOLATION
;                       ERROR_INVALID_LIST_FORMAT
;                       NO_ERROR (special case)
;               Carry clear
;                   (AX) = index into MuxSemWait list
;
;       USES:   AX, BX, CX, DX, DI, SI, BP, ES, Flags
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING

Procedure ScanMuxList,,LOCAL

        push    ax
        CALLFAR GetMuxListCount         ; (CX) = event list count
        pop     dx                      ; (DX:BX) = event list address
        .386p
        jc      scm7                    ; if carry set, (AX) = error code
        CpuMode reset

;       GetMuxListCount checks if the segment is present and valid,
;       and if so returns the event list count. The next is to valid
;       the length of the MuxSemWait event list.  We will not block
;       while we scan the list so probing the end of the list needs
;       only to be done at the beginning of the scan.

        mov     bp,ss:[TKOPTDA].CurrTSD ; (SS:BP) = Current TSD pointer

;       (SS:BP) = current TSD pointer
;       (DX:BX) = present and valid event list address
;       (CX) = event list count

        SetTrap <offset cs:scm4a>,,bp   ; install local GPF handler
        mov     es,dx                   ; (ES) = event list selector
        lea     di,MuxEventList[bx]     ; (ES:DI) = event list pointer

        mov     bx,cx                   ; (BX) = number of entries
        imul    bx,size MuxListEntry    ; (BX) = number of bytes
        mov     al,es:[di+bx-1]         ; probe end of event list buffer
        ClrTrap bp                      ; remove local GPF handler

        xor     si,si                   ; List index = 0

;       (ES:DI) = pointer to current item
;       (CX) = number of items left in list
;       (SI) = current index number

scm1:   cmp     es:[di].MuxReserved,0
        .386p
        jne     scm5
        CpuMode reset

        mov     ax,es:[di].MuxSemHandle.HighWord ; (AX) = handle high word
        cmp     ax,USERHANDLE_HIGHWORD  ; User system semaphore?
        je      scm2
        push    ax                      ; Save in case not 32-bit
        and     ax,SEM_16SIGBITS        ; 32-bit semaphore?
        cmp     ax,SEM_16HANDLESIG
        pop     ax
        je      scm8

        StampRPL ax,bx                  ; stamp RPL of ram sem selector

        SetTrap <offset cs:scm4a>,,bp   ; install local GPF handler
        mov     bx,es:[di].MuxSemHandle.LowWord
        mov     ds,ax                   ; (DS:BX) = RAM semaphore address
        mov     al,ds:[bx].RamSemOwner
        mov     ah,ds:[bx].RamSemFlag
        shr     ah,RAMSEM_OWNER_SHIFT   ; (AX) = semaphore owner
        ClrTrap bp                      ; remove local GPF handler

        xor     bx,bx
        mov     ds,bx                   ; (DS) = 0, get rid of user selector
        jmp     short scm3              ; Go check ownership

;       We have a system semaphore handle.

scm2:   mov     bx,es:[di].MuxSemHandle.LowWord ; (BX) = handle low word
        CALLFAR GetSysSemPtr            ; (DS:BX) = system sem table pointer
        jc      scm7                    ; if carry set, (AX) = error code
        mov     ax,ds:[bx].SysSemOwner  ; (AX) = semaphore owner

scm3:   or      ax,ax                   ; Semaphore owned?
        je      scm6                    ;  no, success!

;       Try the next element in the list.

scm9:   inc     si                      ; Increment index
        add     di,SIZE MuxListEntry    ; Advance pointer
        loop    scm1                    ; Loop

;       Either no clear semaphore was found, or
;       we detected a potential GP fault.  Since we
;       cannot handle these cases, we return carry
;       set and AX = NO_ERROR, our special signal.

scm4:   xor     ax,ax                   ; (AX) = NO_ERROR
        .errnz  NO_ERROR
        stc
        ret                             ; EXIT with error

;       Local fault handler. AX = error code.
scm4a:  ClrTrap
        jmp     short scm4

scm8:   SaveReg <cx,es>
        .386p
        SaveAlignESP
        mov     eax,OFFSET FLAT:SmlPostCt       ; Add the offset
        PushP   eax

; 92009 RAC - Strip off SEM SIG BITS from semaphore handle before calling
;             worker routine.
;
        mov     eax,es:[di].MuxSemHandle
        and     eax,NOT SEM_SIGBITS
        PushP   eax
; 92009 END

        CpuMode reset
        FlatContext <ds,es>,ax
        CCALL32 _SEMQueryEventSem,,,PushP    ; Query the 32-bit event sem
        .386p
        pop     esp
        RestoreReg <es,cx>
        or      eax,eax                 ; ERROR?
        jnz     scm5
        cmp     SmlPostCt,0             ; UnPosted?
        CpuMode reset
        DosContext ds
        jne     scm6                    ; Nope, posted so exit...
        jmp     short scm9              ; Try next element

;       We have an invalid MuxSemWait list.

scm5:   mov     ax,ERROR_INVALID_LIST_FORMAT
        stc
        ret                             ; EXIT with error

;       Success!  Return carry clear with the list index in AX.

scm6:   mov     ax,si                   ; (AX) = index
        clc
scm7:   ret                             ; EXIT

EndProc   ScanMuxList


BREAK   <w_MuxSemWait - Multiple semaphore wait>

;***    w_MuxSemWait - Multiple semaphore wait
;
;       Wait for one of multiple system or ram semaphore
;       to be cleared.
;
;       ENTRY:  (AX:BX) = address of muxwait list.
;               (DI:CX) = dword time out value.
;
;       EXIT:   If carry clear, then
;                       (AX) = index into muxwait list of semaphore that was
;                       cleared.
;               If carry set, then
;                       (AX) = error code (see MuxSemWait).
;
;       USES:   AX, BX, CX, DX, SI, DI, ES, DS, Flags
;
;
;       NOTE:   The w_MuxSemWait routine can not be used with
;               the event list address or ram semaphore addresses
;               in the list that are ring 0 data. This is because
;               the ram semaphore addresses are RPL stamped for ring 3.
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:NOTHING

Procedure w_MuxSemWait,FARONLY                  ; api entry only

;       PMONLY  TASKTIME
        TaskTime
        mov     dl,1
        call    MuxSemWaitSub

;       MMIOPH Performance Hook Removed
;       Maj/Min = 188AH , Data = ax, bx (index, return code)

        ret

EndProc   w_MuxSemWait


BREAK   <MuxSemWait - Multiple semaphore wait>

;***    MuxSemWait - Multiple semaphore wait from kernel
;***    MuxSemWaitSub - Multiple semaphore wait
;
;       The event list count is retrieved and a block of muxwait table
;       entries are removed from the freelist.  Being allocated as a
;       block of entries allows the easy addition and deletion from
;       the active list.  The muxwait table entries are added to the
;       active list before they have been setup, but the MuxType is
;       clear so there will be no matches.  SetupMuxList initializes
;       each muxwait table entry from the event list and returns an
;       indicator if any of the semaphores are cleared.  If the semaphores
;       on the event list are all set, the thread blocks until one these
;       semaphores are cleared (MuxWait).  The block of muxwait table
;       entries are removed from the active list and put on the free list
;       with RemActiveList.  No exclusive system semaphore checking is
;       done because this function doesn't change the state of the
;       semaphore.
;
;       The MuxSemWait can be used with kernel or user address as
;       either the event table or the RAM semaphore addresses, but
;       it is up to the caller to correctly RPL stamp the ring 3
;       addresses.  Kernel (ring 0) addresses should have a RPL of
;       0 (the default).
;
;       ENTRY:  (AX:BX) = address of muxwait list.
;               (DI:CX) = dword time out value.
;               (DL) = api/kernel flag (MuxSemWaitSub only).
;
;       EXIT:   If carry clear, then
;                       (AX) = index into muxwait list of semaphore that was
;                       cleared.
;               If carry set, then
;                       (AX) = error code.
;                               ERROR_SEM_TIMEOUT
;                               ERROR_EXCL_SEM_ALREADY_OWNED
;                               ERROR_INVALID_EVENT_COUNT
;                               ERROR_TOO_MANY_MUXWAITERS
;                               ERROR_INVALID_LIST_FORMAT
;                               ERROR_INTERRUPT
;                               ERROR_PROTECTION_VIOLATION
;
;       USES:   AX, BX, CX, DX, SI, DI, ES, DS, Flags
;
;       MuxSemWait(EventList, TimeOut)
;       {
;           Count = GetMuxListCount(EventList)
;           ListHead, ListTail = GetFreeList(Count)
;           ListTail->MuxLink = ActiveChain
;           ActiveChain = ListHead
;           Index = SetupMuxList(EventList, Count, ListHead)
;           if(all the semaphores were set) {
;               Index = MuxWait(ListHead, ListTail, TimeOut)
;           }
;           RemActiveList(ListHead, ListTail)
;           return(Index)
;       }
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:NOTHING

Procedure MuxSemWait,HYBRID                     ; kernel entry

LocalVar  ListAddr,DWORD                        ; event list address
LocalVar  TimeOut,DWORD                         ; time out value
LocalVar  ListHead,WORD                         ; head of linked list
LocalVar  ListTail,WORD                         ; tail of linked list

;       PMONLY  TASKTIME
        TaskTime
        xor     dl,dl

Entry   MuxSemWaitSub,,LOCAL

        EnterProc <ax,bx,di,cx>                 ; set up frame
        call    GetMuxListCount                 ; (CX) = event list count
        jc      mux2                            ; if carry set, AX = error code

;       Get a list of free MuxSemWait table entries

        push    cx
        call    GetFreeList                     ; (SI,DI) = head and tail
        pop     cx
    ASSUME  DS:DOSGROUP
        jc      mux2                            ; if carry set, AX = error code

        mov     ListHead,si                     ; save head of free list
        mov     ListTail,di                     ; save tail of free list

;       Put new chain on front of active list. This code depends
;       on that the MuxSemWait table's is DOSGROUP. The DS returned
;       from GetFreeList is DOSGROUP.

        mov     ax,ActiveChain
        mov     [di].MuxLink,ax
        mov     ActiveChain,si                  ; put on active chain
    ASSUME  DS:NOTHING

;       Initialize the MuxSemWait table entries.
;
;       (CX) = event list count
;       (DL) = api/kernel flag

        mov     ax,ListAddr.Segmt               ; (AX:BX) = event list addr
        mov     bx,ListAddr.Offst
        call    SetupMuxList
        jc      mux1                            ; jump if error, AX = error code

        cmp     ax,NO_SEM_CLEAR                 ; jump if semaphore is clear
        clc
        jne     mux1

        mov     si,ListHead                     ; (SI) = head of mux list
        mov     bx,ListTail                     ; (DI) = tail of mux list
        mov     di,TimeOut.HighWord
        mov     cx,TimeOut.LowWord              ; (DI:CX) = time out value
        call    MuxWait                         ; if error, AX = error code
mux1:   pushf                                   ; save carry flag
        push    ax                              ; save error code/index number
        mov     si,ListHead
        mov     di,ListTail
        call    RemActiveList                   ; remove from active chain
        pop     ax                              ; (AX) = error code/index number
        popf                                    ; restore carry flag
mux2:   LeaveProc
        ret
EndProc   MuxSemWait


BREAK   <GetMuxListCount - get and check MuxSemWait event list count>

;***    GetMuxListCount - get and check MuxSemWait event list count
;
;       If this routine is called when Indos == 0, then it will return
;       the special error code NO_ERROR to indicate that we must be
;       in real kernel mode. This routine can be called by the semaphore
;       routines running in the special state of ring 0, Indos == 0 for
;       performance reasons. When we are running in this state, none of
;       the subroutines can block.
;
;       ENTRY:  AX:BX = MuxSemWait event list address
;
;       EXIT:   If carry clear, then no errors
;                       CX = number of MuxSemWait event list entries
;               If carry set, then
;                       AX = error code
;                               ERROR_PROTECTION_VIOLATION
;                               ERROR_INVALID_EVENT_COUNT
;                               NO_ERROR (special case)
;
;       USES:   AX, CX, DI, DS, Flags
;
;       GetMuxListCount(EventList)
;       {
;           set local GP fault handler
;           Count = EventList->MuxSemCount
;           clear local GP fault handler
;           if(Count == 0 || Count > MAX_MUX_EVENTS) {
;               return(ERROR_INVALID_EVENT_COUNT)
;           }
;           return(Count)
;       }
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING

Procedure GetMuxListCount,HYBRID,LOCAL

        TaskTime
        mov     di,ss:[TKOPTDA].CurrTSD ; (SS:DI) = current TSD pointer
        SetTrap <offset cs:gmc1>,ax,di  ; install local GP fault handler
        mov     ds,ax                   ; (DS:BX) = address
        mov     cx,[bx].MuxSemCount     ; (CX) = number of entries
        ClrTrap di                      ; remove local fault handler

        xor     ax,ax
        mov     ds,ax                   ; (DS) = 0, get rid of user selector

        mov     ax,ERROR_INVALID_EVENT_COUNT
        jcxz    gmc2                    ; count of zero is invalid
        cmp     cx,MAX_MUX_EVENTS+1     ; check if count is greater than max
        cmc
        ret

;       (AX) = error code from local fault handler
;       (DI) = [CurrTSD]

gmc1:   ClrTrap di
        mov     bx,ss:[di].TSDselFault  ; (BX) = faulty selector
        CALLFAR SemProtFault
gmc2:   stc
gmc3:   ret                             ; EXIT error

EndProc  GetMuxListCount


BREAK   <GetFreeList - get a list of free entries>

;***    GetFreeList - get a list of free entries
;
;       Gets a number of free entries from the muxwait table
;       free chain. Returns them chained together.
;
;       The routine links through the free list until enough free
;       entries have been found to satisfy the request. If there is
;       enough free entries, an error is returned.  If there are
;       no errors, the list of free entries is removed from the
;       free chain and the head and tail pointers return to the
;       caller. The MuxType field is cleared because the this
;       list is put on the active chain before it is initialized
;       by SetupMuxList.
;
;       ENTRY:  CX = number of free entries to be obtained.
;
;       EXIT:   If carry clear, then
;                       DS:SI = head of free list
;                       DS:DI = tail of free list
;               If carry set, then
;                       AX = error code
;                               ERROR_TOO_MANY_MUXWAITERS
;
;       USES:   AX, SI, DI, DS, Flags
;
;       NOTE:   This routine depends on that the link field in the
;               record is the first word and that "FreeChain" is in
;               the same segment as the MuxSemWait table. If it isn't
;               then the "FreeChain" pointer will have to be changed
;               to have the head link at the same offset as in the record.
;               This code also assumes that the MuxTable is in DOSGROUP.
;
;       GetFreeList(Count)
;       {
;           ListTail = &FreeChain
;           do {
;               if(ListTail->MuxLink == END_LINK) {
;                  return(ERROR_TOO_MANY_MUXWAITERS)
;               }
;               ListTail = ListTail->MuxLink
;               ListTail->MuxType = MUXTYPE_CLEAR
;           } while(--Count != 0)
;           ListHead = FreeChain
;           FreeChain = ListTail->MuxLink
;           return(ListHead, ListTail)
;       }
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:NOTHING

Procedure GetFreeList,,LOCAL

;       Link through free chain for the number of entries required

        DosContext ds
        mov     di,offset DOSGROUP:FreeChain

gfl1:   cmp     [di].MuxLink,END_LINK           ; error if at the end of list
        je      gfl2
        mov     di,[di].MuxLink                 ; next link in the free list
        mov     [di].MuxType,MUXTYPE_CLEAR      ; clear the type field
        loop    gfl1

;       Remove from free chain

        mov     si,FreeChain
        mov     ax,[di].MuxLink
        mov     FreeChain,ax
        clc
        ret

gfl2:   mov     ax,ERROR_TOO_MANY_MUXWAITERS
        stc
        ret
EndProc   GetFreeList


BREAK   <SetupMuxList - sets up the free mux table entries>

;***    SetupMuxList - sets up the free mux table entries
;
;       For each entry in the event list, a muxwait table entry is
;       initialized with the thread number, MuxSemID and MuxType.
;       The MUXWAITING flag is set in the semaphore's data structure
;       and the semaphore owner field is checked.  If the semaphore
;       is clear, the setup is stopped and the index into the event
;       list of the clear semaphore is returned.  It is important that
;       the muxwait table entry, the MUXWAITING flag and the owner field
;       check be done in this order because we need to catch interrupt
;       time clears.  The MuxType field has to be set after the MuxSemID
;       field so there will be no spurious MuxSemID matches (since these
;       entries are on the active list).
;
;       If api/kernel flag is !0, this means the entry to MuxSemWait
;       was thru the api version so we stamp all RAM semaphore addresses
;       with the appropriate 2/3 RPL.  If api/kernel flag is 0, then
;       no RPL stamping is done and internal system semaphore handles are
;       allowed.
;
;       ENTRY:  AX:BX = pointer to Event List entry
;               CX = number of MuxSemWait list events
;               DL = api/kernel flag, !0 if from api entry
;               SI = pointer to MuxSemWait table entry
;
;       EXIT:   If carry clear, then
;                       AX = event list index of cleared, or -1 if none cleared
;               If carry set, then
;                       AX = error code.
;                               ERROR_INVALID_LIST_FORMAT
;                               ERROR_PROTECTION_VIOLATION
;
;       USES:   AX, BX, CX, DX, SI, DI, Flags
;
;       NOTE:   This code assumes that the MuxTable is in DOSGROUP.
;
;       SetupMuxList(EventList, Count, ListHead)
;       {
;           p = ListHead
;           l = &EventList.MuxEventList
;           do {
;               SemHandle = l->MuxSemHandle
;               if(l->MuxReserved != 0) {
;                   return(ERROR_INVALID_LIST_FORMAT)
;               }
;               l->MuxThreadID = TaskNumber
;               if(SemHandle is a RAM semaphore) {
;                   p->MuxType, p->MuxSemID = RamGetMuxSemID(SemHandle)
;                   SemHandle->RamSemFlag =| RAMSEM_MUXWAITING
;                   if(SemHandle->RamSemOwner == SEM_CLEAR) {
;                       return(Index)
;                   }
;               }
;               else {
;                   SemPtr = GetSysSemPtr(SemHandle)
;                   p->MuxSemID.HighWord,0
;                   p->MuxSemID.LowWord = SemHandle.LowWord
;                   p->MuxType = MUXTYPE_SYSSEM
;                   SemPtr->SysSemFlag =| SYSSEM_MUXWAITING
;                   if(SemPtr->SysSemOwner == SEM_CLEAR) {
;                       return(Index)
;                   }
;               }
;               ++Index
;               l += sizeof(MuxListEntry)
;               p = p->MuxLink
;           } while(--Count != 0)
;           return(OK)
;       }
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:NOTHING

Procedure SetupMuxList,,LOCAL

LocalVar  Index,WORD                            ; index into event list
LocalVar  Count,WORD                            ; number of events in list
LocalVar  ListAddr,DWORD                        ; MuxSemWait list address
LocalVar  DXreg,DBYTE,APIflag                   ; api/kernel entry flag

        EnterProc <0,cx,ax,bx,dx>               ; initialize stack frame
        add     ListAddr.Offst,MuxEventList     ; (ListAddr) = event list ptr
        DosContext ds

;       (ListAddr) = pointer to Event List entry
;       (Index) = current index number
;       (DS:SI) = pointer to MuxSemWait table entry

sml1:   mov     ax,ListAddr.Segmt               ; (AX) = event list selector
        SetTrap <offset cs:sml4>,ax,,di         ; install local GPF handler
        mov     es,ax
        mov     bx,ListAddr.Offst               ; (ES:BX) = event list pointer

        mov     ax,es:[bx].MuxSemHandle.Segmt   ; (AX) = semaphore high word
        mov     dx,es:[bx].MuxReserved          ; (DX) = reserved word
        mov     bx,es:[bx].MuxSemHandle.Offst   ; (BX) = semaphore low word
        ClrTrap di                              ; remove local GPF handler

        xor     di,di
        mov     es,di                           ; (ES) = 0

        or      dx,dx                           ; check if reserved word is zero
        .386p
        jnz     sml5
        CpuMode reset

sml1_5: mov     dx,TaskNumber                   ; (DX) = thread ID
        mov     [si].MuxThreadID,dx             ; put thread ID into table entry

        cmp     ax,USERHANDLE_HIGHWORD          ; check if user sys sem handle
        .386p
        je      sml6
        CpuMode reset
        push    ax
        and     ax,SEM_16SIGBITS                ; 32-bit semaphore?
        cmp     ax,SEM_16HANDLESIG
        pop     ax
        .386p
        je      sml11                           ;   yes!
        CpuMode reset

        cmp     APIflag,0                       ; if APIflag !0, came from api
        jne     sml2
        cmp     ax,SYSHANDLE_HIGHWORD           ; check if internal handle if
        jne     sml2                            ;  from kernel entry point.
        jmp     sml6

;       Get the unique MuxSemWait ID.

sml2:   push    ax                              ; save RAM semaphore handle
        push    bx
        push    si
        call    RamGetMuxSemID                  ; (DL,AX:BX) = MuxType,MuxSemID
        pop     si
        pop     di
        pop     cx                              ; (CX:DI) = RAM semaphore handle
        jnc     sml2a
        jmp     sml10                           ; jump to exit, (AX) = error

;       (DL) = MuxSemID type
;       (AX:BX) = MuxSemID
;       (CX:DI) = RAM semaphore address
;       (SI) = pointer to entry in muxwait list

sml2a:  DosContext ds
        mov     [si].MuxSemID.HighWord,ax       ; put MuxSemID into entry
        mov     [si].MuxSemID.LowWord,bx
        mov     [si].MuxType,dl

        cmp     APIflag,0                       ; if APIflag !0, came from api
        je      sml3
        StampRPL cx,bx                          ; (CX) = RPL stamped selector

sml3:   SetTrap <offset cs:sml4>,cx,,bx         ; install local GPF handler
        mov     ds,cx                           ; (DS:DI) = ram semaphore addr
        or      [di].RamSemFlag,RAMSEM_MUXWAITING
        mov     al,[di].RamSemOwner
        mov     ah,[di].RamSemFlag
        shr     ah,RAMSEM_OWNER_SHIFT           ; (AX) = RamSemOwner
        ClrTrap bx                              ; remove local GPF handler

        DosContext ds                           ; (DS) = DOSGROUP
        jmp     short sml7                      ; jump to common loop code

;       (AX) = error code from local fault handler

sml4:   ClrTrap
        mov     bx,ss:[TKOPTDA].CurrTSD         ; (SS:BX) = current TSD pointer
        mov     bx,ss:[bx].TSDselFault          ; (BX) = faulty selector
        CALLFAR SemProtFault
        jmp     short sml10

sml5:   stc                                     ; return error if not zero
        mov     ax,ERROR_INVALID_LIST_FORMAT
        jmp     short sml10

;       Get offset into system semaphore data table
;
;       (AX:BX) = system semaphore handle

sml6:   CALLFAR GetSysSemPtr                    ; (DS:BX) = pointer to sem table
   ASSUME  DS:DOSGROUP
        jc      sml10                           ; if carry set, AX = error code

;       (DS:BX) = address of system semaphore table entry

        mov     [si].MuxSemID.HighWord,0        ; put MuxSemID into entry
        mov     [si].MuxSemID.LowWord,bx
        mov     [si].MuxType,MUXTYPE_SYSSEM

        or      [bx].SysSemFlag,SYSSEM_MUXWAITING
        mov     ax,[bx].SysSemOwner             ; (AX) = SysSemOwner flag

;       The MuxSemID, MuxType, MuxThreadID, SYSSEM_MUXWAITING
;       or RAMSEM_MUXWAITING flag have to all be set before we
;       check if the semaphore is already clear so we can catch
;       interrupt time SemClears.

;       (AX) = semaphore owner flag

sml7:   or      ax,ax                           ; check if sem is clear
        .errnz  SEM_CLEAR
        jz      sml9

;       Get next entry in event and free list
;
;       (ListAddr) = pointer to Event List entry
;       (Index) = current index number
;       (Count) = current number of entries left
;       (DS:SI) = pointer to MuxSemWait table entry

sml12:  inc     Index                           ; next event list entry
        add     ListAddr.Offst,size MuxListEntry
        mov     si,[si].MuxLink
        dec     Count
        jz      sml8
        jmp     sml1                            ; jump back to begining of loop

;       (AX:BX) = 32-bit semaphore handle
sml11:  mov     [si].MuxSemID.HighWord,ax       ; put MuxSemID into entry
        mov     [si].MuxSemID.LowWord,bx
        mov     [si].MuxType,MUXTYPE_EVENTSEM
        push    ds
        FlatContext <es,ds>
        .386p
        rol     eax,16                          ; (EAX) = event handle
        mov     ax,bx                           ;
        CCALL32 _SEMMuxWait16, <eax>            ; OR in the DCE_16BIT_MW flag
        pop     ds
        .386p
        or      eax,eax                         ; Error?
        CpuMode reset
        jnz     sml5
        jmp     short sml12                     ; Common loop code.

sml8:   mov     Index,NO_SEM_CLEAR

sml9:   clc
        mov     ax,Index                        ; (AX) = event list index

sml10:  LeaveProc
        ret

EndProc   SetupMuxList


BREAK   <MuxWait - wait until an MuxSemWait semaphore is cleared>

;***    MuxWait - wait until an MuxSemWait semaphore is cleared
;
;       This routine checks if the MuxType field for each entry on
;       this thread's MuxSemWait table active list. If it is zero
;       (MUXTYPE_CLEAR), this means the semaphore represented by
;       this entry has been cleared and this routine returns. If
;       no zero MuxType field's are found then the current thread
;       is blocked on an ID that idenifies this thread. When awaken
;       the process is repeated until a zero MuxType field is found.
;
;       ENTRY:  SI = head of linked list
;               BX = tail of linked list
;               DI:CX = TimeOut
;
;       EXIT:   If carry clear, then
;                       AX = event list index of clear semaphore
;                       DI:CX = time out left over
;               If carry set, then
;                       AX = error code
;                               ERROR_SEM_TIMEOUT
;                               ERROR_INTERRUPT
;               Enables Interrupts
;
;       USES:   AX, CX, DX, DI, DS, ES, Flags
;
;       NOTE:   This routine assumes the MuxTable is in DOSGROUP. It
;               will also freeze the current process if the force flag
;               has been set.
;
;       MuxWait(ListHead, ListTail, TimeOut)
;       {
;           do {
;               p = ListHead
;               Index = 0
;               while(p != ListTail) {
;                   if(p->MuxType == SEM_CLEAR) {
;                       return(Index)
;                   }
;                   p = p->MuxLink
;                   Index++
;               }
;               BlockID.HighWord = MUX_BLOCK_ID
;               BlockID.LowWord = TaskNumber
;               SemBlock(BlockID, TimeOut)
;           } while(no SemBlock errors)
;       }
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:NOTHING

Procedure MuxWait,,LOCAL

;       Loop until an clear on a MuxSemWait semaphore is detected

msw1:   DosContext ds

;       (SI) = head of linked list of MuxSemWait table entries
;       (BX) = tail of linked list
;       (DI:CX) = time out value

        cli                             ; disable interrupts

;       For each entry of this threads in active list, check flag

        push    si
        xor     ax,ax                   ; (AX) = index counter

msw2:   cmp     [si].MuxType,MUXTYPE_CLEAR ; check if sem was cleared
        je      msw3
        cmp     si,bx                   ; check if end of our list
        je      msw4
        mov     si,[si].MuxLink
        inc     ax
        jmp     short msw2

msw3:   pop     si                      ; clean up stack
        clc
        jmp     short msw5              ; return no errors, AX = index

msw4:   pop     si                      ; (SI) = head of mux list
        SaveReg <bx>
        mov     ax,MUX_BLOCK_ID
        mov     bx,TaskNumber           ; (AX:BX) = block ID
        CALLFAR SemBlock
        RestoreReg <bx>
        jnc     msw1                    ; If no errors, jump to begining of loop

msw5:   sti                             ; enable interrupts
        ret

EndProc   MuxWait


BREAK   <RemActiveList - remove the entries from the active chain>

;***    RemActiveList - remove the entries from the active chain
;
;       This routine searches the active chain for the record preceding
;       the head of the list of entries to be put on the free chain. It
;       then moves the contents of the tail's link into the head of list's
;       precedessor's link thus removing it from the active list. The list
;       is then added to the free chain. This routine not work if the
;       active list is null (ActiveChain == END_LINK).
;
;       ENTRY:  SI = head of list of entries
;               DI = tail of list of entries
;
;       EXIT:   NONE
;
;       USES:   AX, BX, Flags
;
;       NOTE:   This routine depends on that the link field in the
;               record is the first word and be in the same segment
;               as the MuxSemWait table entries. If it isn't then the
;               "ActiveChain" pointer will have to be changed to have
;               the head link at the same offset as in the record. This
;               code also assumes that the MuxTable is in DOSGROUP.
;
;       RemActiveList(ListHead, ListTail)
;       {
;           p = &ActiveChain
;           while(p->MuxLink != ListHead) {
;               p = p->MuxLink
;           }
;           p->MuxLink = ListTail->MuxLink
;           ListTail->MuxLink = FreeChain
;           FreeChain = ListHead
;       }
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:NOTHING

Procedure RemActiveList,,LOCAL

        DosContext ds

;       Find the record preceding the list's head

        mov     bx,offset DOSGROUP:ActiveChain ; (DS:BX) = address ActiveChain

pfl1:   cmp     [bx].MuxLink,si         ; if link is equal to list head
        je      pfl2
        mov     bx,[bx].MuxLink         ; (BX) = next link
        jmp     short pfl1

;       Remove list from active chain

pfl2:   mov     ax,[di].MuxLink         ; (AX) = entry after tail of list
        mov     [bx].MuxLink,ax

;       Put list on free chain

        mov     ax,FreeChain            ; (AX) = first entry in free list
        mov     [di].MuxLink,ax
        mov     FreeChain,si            ; FreeChain = head of list
        ret

EndProc   RemActiveList


;***    RamGetMuxSemID - gets the unique MuxSemID for a RAM semaphore
;
;       This routine determines the MuxSemID for ram semaphores.  If
;       the ram semaphore selector has the "UVIRT" bit set this routine
;       will use the physical base address for the MuxSemID.  Otherwise,
;       it calls VMGetHandle to get the memory manager handle for the
;       high part of the ID and the offset is the low word.  At interrupt
;       time this routine only allows "UVIRT" selectors addresses.
;
;       ENTRY:  (AX:BX) = RAM semaphore handle
;
;       EXIT:   If carry clear,
;                       (DL) = MuxWait ID type
;                       (AX:BX) = MuxWait ID
;               If carry set,
;                       (AX) = error code
;                               ERROR_PROTECTION_VIOLATION
;
;       USES:   AX, BX, CX, DX, SI, DI, DS, ES, Flags
;
;       RamGetMuxSemID(SemAddr)
;       {
;           if(InterruptLevel == interrupt time) {
;               MuxSemID = UVirtToPhys(SemAddr)
;               Type = MUXTYPE_RAMPHYS
;           }
;           else {
;               Verify SemAddr.Segmt
;               MuxSemID = UVirtToPhys(SemAddr)
;               Type = MUXTYPE_RAMPHYS
;               if(not a UVIRT selector) {
;                   MuxSemID = GetSegHandle(SemAddr.Segmt)
;                   Type = MUXTYPE_RAMHANDLE
;               }
;           }
;           return(Type, MuxSemID)
;       }
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:NOTHING

Procedure RamGetMuxSemID,HYBRID,LOCAL
        DosContext ds
        cmp     [InterruptLevel],-1
        jne     rgm3
;        je      rgm0                    ; jump if not interrupt time
;        jmp     short rgm3              ; jump if interrupt time
;       PMONLY  TASKTIME
rgm0:   TaskTime

;       Validate ram semaphore selector

        SetTrap <offset cs:rgm2a>,ax,,si ; install local GP fault handler
        mov     ds,ax
        ClrTrap si                      ; remove local GP fault handler
        xor     dx,dx                   ; clear user's DS
        mov     ds,dx

;       Call the memory manager to check if the selector is
;       "UVIRT" selector.  Returns the physical address if
;       it is a "UVIRT" selector.

        push    ax
        push    bx
        call    UVirtToPhys             ; (AX:BX) = physical address
        pop     dx
        pop     cx                      ; (CX:DX) = ram sem selector:offset
        jnc     short rgm4              ; jump if UVIRT selector

;       Now that we made sure that the selector was present
;       valid and not UVIRT, get the virtual memory manager
;       handle.

        FlatContext <ds,es>,di
        CALLF32 semRamGetMuxSemIDSub    ; (AX) = handle
        assume ds:nothing,es:nothing
        jc      short rgm1

        mov     dl,MUXTYPE_RAMHANDLE    ; (DL) = type
        clc
        ret                             ; EXIT

rgm1:   mov     ax,ERROR_PROTECTION_VIOLATION
        DosContext ds

rgm2:   mov     si,ss:[TKOPTDA].CurrTSD ; (SS:SI) = current TSD pointer
        mov     bx,ss:[si].TSDselFault  ; (BX) = faulting selector
        CALLFAR SemProtFault            ; carry set, (AX) = error code
        ret                             ; EXIT (CARRY SET by SemProtFault)
    ASSUME   SS:NOTHING

;       Local fault handler. AX = error code.
rgm2a:  ClrTrap
        jmp     short rgm2

rgm3:   call    UVirtToPhys             ; (AX:BX) = physical address
        jc      short rgm5              ; jump if error

rgm4:   mov     dl,MUXTYPE_RAMPHYS
        ret                             ; EXIT (CARRY CLEAR by UVirtToPhys)

rgm5:   mov     ax,ERROR_PROTECTION_VIOLATION
        ret                             ; EXIT (CARRY SET by UVirtToPhys)

EndProc   RamGetMuxSemID

HIGH3CODE ENDS


CpuMode 386
FARCODE HIGH32
HIGH32CODE segment

;***LP  semRamGetMuxSemIDSub - converts a selector to a VM handle
;
;       This routine is a wrapper for a pair of calls that convert
;       a non-UVIRT selector into a VM handle.
;
;       ENTRY   (BX) = selector
;               (DS) = FLAT
;               (ES) = FLAT
;       EXIT    no error
;                       carry clear
;                       (ax) = VM handle
;               error
;                       carry set
;       USES    EAX, EBX, ECX, EDX, Flags

Procedure semRamGetMuxSemIDSub,far32,local
    assume ds:FLAT,es:FLAT,fs:nothing,gs:nothing,ss:nothing
        LocalVar srg_laddr,dword
        EnterProc

        movzx   ebx,dx                  ; (bx) = ram sem offset
        CCALL32 _SELConvertToLaddr,ecx  ; (eax) = laddr of selector
        cmp     eax,-1
        je      short srge
        SSToDS  ecx,[srg_laddr]         ; addr for mem handle
        CCALL32 _VMGetHandle,<eax,0,ecx>
        or      eax,eax                 ; test for error, clears carry
        mov     eax,[srg_laddr]         ; (ax) = mem handle
        jnz     short srge
srgx:   LeaveProc
        RETC

srge:   stc
        jmp     short srgx
EndProc semRamGetMuxSemIDSub

;***    semEventWakeMuxWait - wake up muxwait'ers for a 32-bit Event semaphore
;
;
;       ENTRY:  hEvt    - Event semaphore handle
;
;       EXIT:   Success - NO_ERROR
;               Failure - ERROR_PROTECTION_VIOLATION
;
;       USES:   AX, SI, DS, Flags
;
;
        FlatContext
Procedure  _semEventWakeMuxWait
ArgVar  hEvt,DWORD

        EnterProc
        mov     ebx,hEvt                ; Only lower word counts
        mov     eax,ebx
        shr     eax,16                  ; Now AX:BX = MuxSemID
        mov     dl,MUXTYPE_EVENTSEM     ; (DL,AX:BX) = MuxType,MuxSemID
        CALL16  EvtWakeMuxWait16
        FlatContext <ds,es>
        LeaveProc
        RETC
EndProc   _semEventWakeMuxWait

HIGH32CODE ends

CpuMode 286

         FARCODE LOW
CODE     SEGMENT

;***    EvtWakeMuxWait16 - A thunk WakeMuxWait for _semEventWakeMuxWait to use

Procedure EvtWakeMuxWait16,far16
        call    WakeMuxWait
        RETC
EndProc EvtWakeMuxWait16

BREAK   <SysWakeMuxWait, RamWakeMuxWait - wake up muxwait'ers>

;***    SysWakeMuxWait - wake up muxwait'ers for a system semaphore
;
;       This routine starts at the begining of the MuxSemWait active linked
;       list, comparing the semaphore ID passed to the MuxSemID in the table
;       entry. If there is a match, the a flag is set and the thread waiting
;       on this muxwait list is ProcRun.
;
;       ENTRY:  (AX:BX) = system semaphore handle
;
;       EXIT:   (DS) = DOSGROUP
;               If carry clear, no errors
;               If carry set,
;                       (AX) = error code
;                               ERROR_PROTECTION_VIOLATION
;
;       USES:   AX, SI, DS, Flags
;
;       SysWakeMuxWait(SemHandle)
;       {
;           MuxSemID.HighWord = 0
;           MuxSemID.LowWord = SemHandle.LowWord
;           WakeMuxWait(MUXTYPE_SYSSEM, MuxSemID)
;       }
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:NOTHING

Procedure SysWakeMuxWait

;       DUALMODE
        xor     ax,ax
        mov     dl,MUXTYPE_SYSSEM       ; (DL,AX:BX) = MuxType,MuxSemID
        jmp     short WakeMuxWait

EndProc   SysWakeMuxWait


;***    RamWakeMuxWait - wake up muxwait'ers for a RAM semaphore
;
;       Wakes up the muxwait'ers on the RAM semaphore indicated.
;
;       ENTRY:  (AX:BX) = RAM semaphore handle
;
;       EXIT:   If carry clear, no errors
;               If carry set,
;                       (AX) = error code
;                               ERROR_PROTECTION_VIOLATION
;
;       USES:   AX, BX, CX, DX, SI, DI, DS, ES, Flags
;
;       RamWakeMuxWait(SemAddr)
;       {
;           if(protected mode) {
;               MuxType, MuxSemID = RamGetMuxSemID(SemAddr)
;           }
;           else {
;               MuxType = MUXTYPE_RAMPHYS
;               MuxSemID = physical address of SemAddr
;           }
;           WakeMuxWait(MuxType, MuxSemID)
;       }
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:NOTHING

Procedure RamWakeMuxWait

;       DUALMODE
        DosContext ds
        whatmode cx
        jc      wmw1                    ; jump if protected mode

        mov     dx,ax                   ; (DX) = segment
        shl     dx,4                    ; (DX) = low 3 nibbles of seg
        shr     ax,12                   ; (AX) = high nibble of segment
        add     bx,dx                   ; (AX:BX) = (AX:BX) + (DX)
        adc     ax,0

        mov     dl,MUXTYPE_RAMPHYS      ; (DL,AX:DX) = MuxType,MuxSemID
        jmp     short WakeMuxWait

wmw1:   CALLFAR RamGetMuxSemID          ; (DL,AX:BX) = MuxType,MuxSemID
    ASSUME  DS:NOTHING
        jc      wmwx                    ; jump if error

;***    WakeMuxWait - wake up muxwait'ers
;
;       ENTRY:  (AX:BX) = MuxSemID
;               (DL) = MuxSemID type
;
;       EXIT:   If carry clear, no errors
;               If carry set,
;                       (AX) = error code
;
;       USES:   AX, SI, DS, Flags
;
;       WakeMuxWait(MuxType, MuxSemID)
;       {
;           p = ActiveChain
;           while(p != END_LINK) {
;               if(p->MuxType == MuxType) {
;                   if(p->MuxSemID == MuxSemID) {
;                       p->MuxType = MUXTYPE_CLEAR
;                       BlockID.LowWord = p->MuxThreadID
;                       BlockID.HighWord = MUX_BLOCK_ID
;                       ProcRun(BlockID)
;                   }
;               }
;           }
;       }
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:NOTHING

Entry   WakeMuxWait,near,local

        DosContext ds
        mov     si,ActiveChain

;       For each entry in the MuxSemWait table active chain

wmw2:   cmp     si,END_LINK             ; if end of linked list
        je      wmw4

        cmp     [si].MuxType,dl         ; compare ID type
        jne     wmw3

        cmp     [si].MuxSemID.LowWord,bx   ; compare low word
        jne     wmw3
        cmp     [si].MuxSemID.HighWord,ax  ; compare high word
        jne     wmw3

        mov     [si].MuxType,MUXTYPE_CLEAR ; indicate we have a SemClear
        SaveReg <ax, bx>
        mov     ax,MUX_BLOCK_ID         ; (AX) = MuxWait block ID
        mov     bx,[si].MuxThreadID     ; (BX) = thread ID
        call    ProcRun
        RestoreReg <bx, ax>

wmw3:   mov     si,[si].MuxLink         ; (SI) = next link
        jmp     short wmw2              ; jump back to begining of loop

wmw4:   clc                             ; no error
wmwx:   ret

EndProc   RamWakeMuxWait

CODE    ENDS

        END
