;       SCCSID = @(#)dosv1api.asm       6.6 91/08/12
;      SCCSID = @(#)dosv1api.asm        6.6 91/08/12
page 60,132
.386p

include dc1mmpha.inc

;------------------------------ Module Header ------------------------------;
;ModName DOSV1API
                                                                    COMMENT $
Module Name: DOSV1API.ASM

This module contains functions defined for supporting the new API that is
contains in version 1.1 of OS/2.  These include:

    - fast, safe RAM semaphore support

    - ring 2 to ring 3 call back

History:
  11/18/93    noto    Define which segments for Page Tuning DLL
 28-Jan-93 [AllenW]   Defect 61117 DosFSRamSemXxx trashing top half of EAX
 14-Sep-88 [johnfr]   1.2 PTM 2078. Correct FSRAMSEMREQUEST timeout
 09-Jul-87 [stevewo]  Integrated into 1.0 source tree
 30-Jan-87 [stevewo]  Updated to reflect results of DR1/I0 review.
 21-Jan-87 [stevewo]  Created.

A fast safe RAM semaphore (FSRS) is a OS/2 RAM semaphore with an associated
process and thread owner field.  It combines the speed of RAM semaphores with
the safety provided by system semaphores.  It does this by relying upon the
caller to understand the FSRS data structure and perform the appropriate
cleanup action in their DosExitList handler whenever a process dies.

In order to implement fast safe RAM semaphores, these functions need some
form of mutual exclusion when accessing the semaphore data structure.  This
is provided by using the CLI/STI instructions to disable hardware interrupts
for a short period of time.  This requires that these functions run at ring
2 in order to have IOPL.

The following information is contained in the fast safe RAM semaphore data
structure:

    - 32 bit process/thread ID that uniquely identifies the thread of
      execution that owns the semaphore.  If this field is zero then the
      semaphore is unowned.

    - 16 bit client field that is not interpreted by the functions in this
      module.  Instead it is a field provided to the caller as a means for
      identifying which resource is currently owned by the owner of the
      semaphore.  It is initialized to zero when a semaphore is first
      acquired, and thereafter the value of the field is under the control of
      the caller, until the semaphore is released.  It is useful to a
      DosExitList handler in figuring out the appropriate cleanup action.

    - 16 bit usage count.  This count is incremented each time the current
      owner acquires the semaphore and decrement each time the current owner
      releases the semaphore.  The current owner does not actually release
      its ownership until the count decrements to zero.

    - a 32 bit OS/2 RAM semaphore.  This RAM semaphore is manipulated by
      the functions in this module in the same manner as the the ring 3
      semaphore code in OS/2 (e.g. DOSSEMREQUEST and DOSSEMCLEAR).

    - a 32 bit time out value to use when waiting on the OS/2 RAM
      semaphore (actually passed as a parameter to DosFSRamSemRequest).

Because there is a reference count associated with each semaphore,
the function that acquires a semaphore is call DosFSRamSemRequest
and the function that releases a semaphore is called DosFSRamSemClear.
Hopefully the names will imply what is true, namely every successful
call to DosFSRamSemRequest must be matched with a call to DosFSRamSemClear.

These functions accept a parameter word that is a far pointer to a
FSRS data structure.  These functions do no validity checking of
the pointer, which means that it is up to the caller to insure that:

    - pSem always points to a valid fast safe RAM semaphore data structure.

    - the caller should never directly modify any of the fields in the fast
      safe RAM semaphore.  If the caller knows that it owns a semaphore (e.g.
      after a call to DosFSRamSemRequest), then it can modify the fs_Client
      field all it wants in order to provide hints to its cleanup code in its
      DosExitList handler.

    - the caller should never directly read the values of any of the fields
      in the data structure, except for the fs_Client field when it owns the
      semaphore.  Another exception is in a DosExitList handler, where is
      okay to see if the process ID matches the current process ID, and if it
      does it is okay to examine the other fields, such as the client field to
      control cleanup actions and the usage count field to determine how many
      times to release the semaphore.

    - the caller should never call DosFSRamSemClear on a semaphore that
      is not owned by the current process/thread.  An error code will be
      returned.

    - the caller should never call any of the OS/2 RAM semaphore functions
      with the address of the RAM semaphore contained within the fast safe
      RAM semaphore data structure.

    - every successful call to DosFSRRamSemRequest must be matched with
      a call to DosFSRamSemClear.

This module is dependent upon the implementation of OS/2 in the following
ways:

    - it assumes that the local InfoSeg selector is always instantiated for
      each process and that it is the first private selector in the LDT.  If
      OS/2 were to export the InfoSeg selector as a constant, then this
      dependency could be eliminated.

    - it understands the meaning of the first two bytes in the OS/2 RAM
      semaphore data structure (RamSemOwner and RamSemFlag).

    - it calls an internal interface in OS/2, DOSIRAMSEMWAKE.

OS/2 2.0 UPDATE:
    - Since the FSRS structure is in user space, it is swappable.  This
      means that if the semaphore structure straddles a page boundary,
      it is possible to take a page fault between updates of any two
      fields in the semaphore structure.  This would have the same effect
      as taking an interrupt: the page fault may lead to a context switch,
      with the new runner potentially examining and modifying the fields
      of the semaphore and finding/leaving them in an inconsistent state.
      Furthermore, the FSRS code is also swappable, and any two
      instruction fetches could cause the same situation.

      In order to guarantee that all the code and the whole semaphore
      structure are in memory simultaneously,

          1. The code must be not-swappable, or must not span a page
             boundary.  Currently, the FSRS code segment contains about
             03xx (hex) instruction bytes, which is well below the size
             of a page 1000 (hex).   CAUTION --- DANGER --- CAUTION

          2. Simultaneous references have to be made to semaphore
             structure data bytes in both pages with a single instruction.
             In this way, the instruction page and both data pages must
             be present in order for the instruction to be executed.
             This can be achieved using an instruction such as CMPS
             or by referencing all fields in an overlapping fashion,
             so that both pages are touched.  The code that follows this
             sequence *must not touch any memory outside of the semaphore
             structure*.  Any memory references may cause a page fault and
             blow out our critical section.

Below is a summary of how a dynlink package can use fast safe RAM semaphores
to control access to a shared resource (such as its global data segment)
in a safe manner and successfully cleanup in the event of the death of the
process that owns the shared resource (via the semaphore).

1.  Per client process initialization:

    Issue the DOSEXITLIST system call to install a cleanup handler for
    the current process.

2.  Cleanup handler code:

        - call DosFSRamSemRequest on the semaphore to enter the critical
          section.  If the error code "ERROR_SEM_OWNED_DIED" is returned,
          then the owner died in the critical section.  The current
          exitlist thread now has ownership.  The usage is set to one.

        - examine the fs_Client field for hints as to the state of the
          shared resource.  It should cleanup the state of the shared
          resource as best as possible.

        - call DosFSRamSemClear on the semaphore to release the semaphore.

3.  The mainline code that manipulates the shared resource should:

        - call DosFSRamSemRequest before accessing the shared resource.

        - while modifying the shared resource, update the hints in the
          fs_Client field so that the cleanup handler will be able to
          cleanup the state of the shared resource in the event the current
          process dies or is killed asynchronously.

        - call DosFSRamSemClear when done accessing the shared resource
          to allow other processes to access it in a timely manner.

;---------------------------------------------------------------------------;$


;--------------------------- Internal Declarations -------------------------;

; Fast safe RAM semaphore structure

SET_DURING_EXITLIST     equ     -1      ; fs_ThrdID, set during exitlist

FastSafeRamSemStruc     struc
        fs_Length       dw  ?           ; Length of this structure

; warning: the following two fields are manipulated as a dword

        fs_ProcID       dw  ?           ; Process ID of owner or zero
        fs_ThrdID       dw  ?           ; Thread ID of owner or zero

        fs_Usage        dw  ?           ; reference count
        fs_Client       dw  ?           ; 16 bit field for use by owner
        fs_RAMSem       dd  ?           ; OS/2 RAM Semaphore
FastSafeRamSemStruc     ends


;----------------------- OS/2 Dependent Declarations ----------------------;


; This source file makes assumptions about which selector is the local
; infoseg selector.
;

EXTRN   DOSSEMWAIT:FAR
EXTRN   DOSIRAMSEMWAKE:FAR
EXTRN   DOSICALLBACK:FAR
EXTRN   DOSRETFORWARD:FAR
EXTRN   DOSLOCALINFO:ABS

.xlist
include basemaca.inc
include infoseg.inc
include sem.inc
include selxport.inc
include error.inc
include dc1segs.inc                                             ; DCR 704
.list

CODE16_TUNE1   SEGMENT DWORD USE16 PUBLIC 'CODE'
CODE16_TUNE1   ENDS

CODE16C_TUNE2  SEGMENT DWORD USE16 PUBLIC 'CODE'
CODE16C_TUNE2  ENDS



CODE16C_TUNE2    SEGMENT                                        ; DCR 704
        ASSUME CS:CODE16C_TUNE2, DS:NOTHING, ES:NOTHING, SS:NOTHING

;************************** START OF SPECIFICATIONS ****************/
;*                                                                 */
;*      SUBROUTINE NAME: MEDIATOR                                  */
;*                                                                 */
;*     DESCRIPTIVE NAME: Transfer Control from ring 3 to ring 0    */
;*                                                                 */
;*     FUNCTION: This routine resides in ring 3.                   */
;*               It will call DOSRETFORWARD to handle the ring     */
;*               transition from ring 0 to ring 2.                 */
;*                                                                 */
;*     ENTRY POINT:    MEDIATOR                                    */
;*        LINKAGE:     RETURN FAR                                  */
;*                                                                 */
;*     INPUT:          NONE                                        */
;*                                                                 */
;*     EXIT:           NONE                                        */
;*                                                                 */
;*                                                                 */
;*     EFFECTS: None                                               */
;*                                                                 */
;*     INTERNAL REFERENCES:                                        */
;*        ROUTINES:                                                */
;*                                                                 */
;*     EXTERNAL REFERENCES:                                        */
;*        ROUTINES:                                                */
;*         DOSRETFORWARD - Ring transition from ring 0 to ring 2   */
;*                                                                 */
;*************************** END OF SPECIFICATIONS *****************/
;************************** START OF PSEUDO CODE ****************/
;
;  Call DosRetForward via CallGate
;
;ring 3 stack during Mediator:
;       +---------+
;       |    ?    |
;ss:sp->+---------+
;
;************************** END   OF PSEUDO CODE ****************/
Procedure MEDIATOR,FAR

; Call DosRetForward via Call Gate
        call    DOSRETFORWARD

EndProc MEDIATOR

CODE16C_TUNE2   ENDS


DOSSUBCODE  SEGMENT                                             ; DCR 704
        ASSUME CS:DOSSUBCODE, DS:NOTHING, ES:NOTHING, SS:NOTHING

;       db      4096 dup (?)            ; bump code into second page

;************************** START OF SPECIFICATIONS ****************/
;*                                                                 */
;*      SUBROUTINE NAME: DOSCALLBACK                               */
;*                                                                 */
;*     DESCRIPTIVE NAME: Transfer Control from ring 2 to ring 3    */
;*                                                                 */
;*     FUNCTION: This routine resides in ring 2.                   */
;*               it will save the address of the routine,          */
;*               MEDIATOR, for ring 3 code to go back to the ring 0*/
;*               code, and then call the ring 0 code, DOSICALLBACK,*/
;*               through the call gate.                            */
;*                                                                 */
;*               If the caller the wrong information on the stack, */
;*               this routine will do the Far RETurn to generate   */
;*               the GP fault.                                     */
;*                                                                 */
;*     ENTRY POINT:    DOSCALLBACK                                 */
;*        LINKAGE:     CALL FAR                                    */
;*                                                                 */
;*     INPUT:          NONE                                        */
;*                                                                 */
;*     EXIT:                                                       */
;*     If caller passes the correct information:                   */
;*        Ring 2 stack contains the address of MEDIATOR            */
;*                                                                 */
;*     If caller passes the correct information:                   */
;*        Ring 2 stack contains the address of MEDIATOR and        */
;*             the wrong information passed by the caller          */
;*                                                                 */
;*     EFFECTS: None                                               */
;*                                                                 */
;*     INTERNAL REFERENCES:                                        */
;*        ROUTINES:                                                */
;*                                                                 */
;*     EXTERNAL REFERENCES:                                        */
;*        ROUTINES:                                                */
;*         DOSICALLBACK - Ring transition from ring 0 to ring 3    */
;*                                                                 */
;*************************** END OF SPECIFICATIONS *****************/

;************************** START OF PSEUDO CODE ****************/
;
; Push address of Mediator on the ring 2 stack
; Call DOSICALLBACK
; If the caller passed the wrong information on the stack,
;    this routine will get control again, and do the Far RETurn
;    to generate the GP fault
;                               +---------+
;                               | ss:sp(3)|
;                               +---------+
;                               | cs:ip(3)|
;                   +---------+ +---------+
;                   | ss:sp(3)| | xxxxx   |
;       +---------+ +---------+ +---------+
;       | ss:sp(3)| | cs:ip(3)| | @Subr   |
;       +---------+ +---------+ +---------+
;       | cs:ip(3)| | xxxxx   | | @Caller |
;       +---------+ +---------+ +---------+
;       | xxxxx   | | @Subr   | |@Mediator|
;       +---------+ +---------+ +---------+
;       | @Subr   | | @Caller | | ss:sp(3)|
;       +---------+ +---------+ +---------+
;       | @Caller | |@Mediator| | @Subr   |
;ss:sp->+---------+ +---------+ +---------+
;
;************************** END   OF PSEUDO CODE ****************/

Procedure DOSCALLBACK,FAR

; Push address of Mediator on the ring 2 stack
        push    seg MEDIATOR
        push    offset MEDIATOR

        call    DOSICALLBACK
        ret
EndProc DOSCALLBACK


;***    DOSFSRAMSEMREQUEST - acquire ownership of safe RAM semaphore.
;
;       This function attempts to acquire ownership of a fast safe
;       RAM semaphore.  If the semaphore is already owned by the
;       calling process/thread, then it just increments the usage
;       count in the semaphore.  If the semaphore is owned by some
;       other process/thread, then it blocks until the semaphore is
;       release, using a default timeout value.  If the semaphore is
;       unowned, then this function marks it as owned by the current
;       process/thread.
;
;       DOSFSRAMSEMREQUEST (pSem, pTimeout);
;
;       ENTRY:  DWORD   pointer to struct FastSafeRamSemStruc
;               DWORD   time out value
;
;       EXIT:   If success, AX = 0
;               If error, AX = error code
;
;       USES:   AX, Flags
;
;       HISTORY:
;
;       28-Jan-93 [AllenW]   Defect 61117 DosFSRamSemXxx trashing top of EAX
;       24-Jul-91            Rearrange code to reduce execution time
;       25-Jul-89 [robertre] Use 32 bit registers
;       02-Dec-87 [mikem]    Allow any thread in the owning process to
;                            request semaphore during an exitlist handler.
;       24-Jan-87 [stevewo]  Made unowned case the default (no jumps) path.
;       21-Jan-87 [stevewo]  Created.
;

;***DT  preDOSFSRAMSEMREQUEST - DosFSRamSemRequest pre-invocation trace point
;
;    TRACE MINOR = <RAS_DC1_PRE_DOSFSRAMSEMREQUEST>,
;          TP    = .DOSFSRAMSEMREQUEST,
;          TYPE  = (PRE,API),
;          GROUP = SEM,
;          DESC  = "(OS) DosFSRamSemRequest Pre-Invocation",

;***DT  postDOSFSRAMSEMREQUEST - DosFSRamSemRequest post-invocation trace point
;
;    TRACE MINOR = <RAS_DC1_POST_DOSFSRAMSEMREQUEST>,
;          TP    = .postDOSFSRAMSEMREQUEST,
;          TYPE  = (POST,API),
;          GROUP = SEM,
;          DESC  = "(OS) DosFSRamSemRequest Post-Invocation",
;          REGS  = (AX),
;          FMT   = "Return Code = <FMT_WORD>"

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

; DOSFSRAMSEMREQUEST stack frame AFTER saving registers
;
fsrsf_s         struc
        fsrsf_topofeax  dw  ?           ; top half of EAX saved. 61117
        fsrsf_ds        dw  ?           ; DS
        fsrsf_bx        dw  ?           ; BX
        fsrsf_ret       dd  ?           ; Return CS:IP
        fsrsf_to        dd  ?           ; API parameter - timeout value
        fsrsf_sem       dd  ?           ; API parameter - address of FSR
fsrsf_s         ends

        ;align   4 54686                 ; DWORD align beginning of procedure
Procedure DOSFSRAMSEMREQUEST,FAR

        SaveReg <bx,ds>
        rol     eax,16                   ;put top half of EAX in AX  61117
        push    ax                       ;Save EAX                   61117
        MMPHPreDOSFSRamSemRequest               ; MMIOPH hook

; Establish addressability to the local infoseg.
;
        mov     ax,DOSLOCALINFO
        mov     ds,ax

; If the semaphore was clear and then set during exitlist time, the threadID
; is set with a special value indicating this.
;
        mov     ax,SET_DURING_EXITLIST  ; (AX) = set during exitlist
        test    ds:[LIS_ProcStatus],LIS_PS_EXITLIST ; Exitlist?
        jnz     short fsr10             ; Yes, AX already set
        mov     ax,ds:[LIS_CurThrdID]   ; (AX) = current TID
fsr10:  shl     eax,16                  ; Move low word into high word
        mov     ax,ds:[LIS_CurProcID]   ; (AX) = current PID

; Get address of FSR structure from stack
;
        mov     bx,sp
        lds     bx,ss:[bx].fsrsf_sem    ; (DS:BX) = address of FSR

; Disable interrupts while we look at the fast, safe RAM semaphore.
; There are three possible paths to the matching STI instruction to reenable
; interrupts:
;
;   - semaphore is currently unowned
;   - semaphore is currently owned by the current process/thread
;   - semaphore is currently owned by some other process/thread
;
fsr20:  cli

;       WARNING DANGER WARNING DANGER WARNING DANGER WARNING
;
; The next three instructions make sure that the complete FSR structure
; is present in memory before we modify it.  This is done because a page
; fault would allow another thread access to the FSR structure after we
; have potentially corrupted it.  TO AVOID OTHER PAGE FAULTS, WE MUST NOT
; TOUCH ANY MEMORY OUTSIDE THE FSR STRUCTURE UNTIL WE HAVE FINISHED OUR
; MODIFICATIONS TO THE FSR STRUCTURE.
;
        cmp     dword ptr ds:[bx+6].fs_ProcID,eax
        cmp     dword ptr ds:[bx+3].fs_ProcID,eax
        cmp     dword ptr ds:[bx].fs_ProcID,eax
        .errnz  ((fs_RAMSem.RamSemFlag-fs_ProcID+1)-10)

        cmp     ds:[bx].fs_ProcID,0     ; Is the semaphore owned?
        jne     short fsr30             ; Yes, see if it is caller

; Here if the semaphore is currently unowned, marked it as owned by the current
; process ID and thread ID (or special exitlist value) and set the usage count
; to one.
;
        mov     dword ptr ds:[bx].fs_ProcID,eax

        mov     ds:[bx].fs_Usage,1      ; Set usage count to 1
fsrz:   xor     ax,ax                   ; Return with AX = 0
fsri:   sti
fsrx:   MMPHPostDOSFSRamSemRequest              ; MMIOPH post hook
        rol     eax,16           ; put AX in top half of EAX           61117
        pop     ax               ; restore value saved                 61117
        ror     eax,16           ; Top half of EAX contains old value, 61117
        RestoreReg <ds,bx>

        PUBLIC  postDOSFSRAMSEMREQUEST
postDOSFSRAMSEMREQUEST:                 ; RAS post-invocation trace
        ret     8

; Here if the semaphore is currently owned.  Check to see if current owner
; is the caller.
;
        ;align   4 54686                 ; DWORD align jump target
fsr30:  cmp     dword ptr ds:[bx].fs_ProcID,eax ; Semaphore owned by caller?
        jne     short fsr40             ; No, go check further

; If the semaphore is already owned by the current process ID and thread ID,
; then nothing to do but increment the usage count.
;
        inc     ds:[bx].fs_Usage
        jmp     short fsrz

; Here if the semaphore is currently owned.  Check to see if current owner
; is within the process.
;
        ;align   4 54686                 ; DWORD align jump target
fsr40:  cmp     ds:[bx].fs_ProcID,ax    ; Current process own semaphore?
        jne     short fsr50             ; No, go wait for it

; If exitlist time, any thread can request the semaphore
;
        ror     eax,16                  ; Swap PID and TID within EAX
        cmp     ax,SET_DURING_EXITLIST  ; Set during exitlist?
        je      short fsr70             ; Yes, do exitlist request
        rol     eax,16                  ; Restore PID and TID within EAX

; If the semaphore is already owned by some other process ID and thread ID,
; then call OS/2 RAM Semaphore mechanism to wait for the semaphore with a
; predefined timeout value.  When it returns, if successful, jump back to the
; the beginning of this function to try to get the semaphore again.  Otherwise
; return the error code to the caller, with the error code in AX.
;
; We must ensure that interrupts are disabled until after we have set the
; semaphore otherwise if we get a DosFsRamSemClear before we set the
; semaphore, we'll miss the clear and hang up.
;
fsr50:  mov     ds:[bx].fs_RAMSem.RamSemOwner,SEM_SET ; Set semaphore
        sti                             ; Safe to use other memory now
        push    eax                     ; Save EAX
        push    ds
        lea     ax,ds:[bx].fs_RAMSem    ; (DS:AX) = OS/2 RAM Semaphore
        push    ax
        movzx   eax,sp                  ; (EAX) = TOS
        push    dword ptr ss:[eax+8].fsrsf_to ; +8 bytes for pushes above
        call    DOSSEMWAIT              ; Wait on semaphore
        or      ax,ax                   ; Successful?
        jnz     short fsr60             ; No, fix stack and exit
        pop     eax                     ; Yes, fix stack and acquire semaphore
ifdef   MMIOPH
        jmp     fsr20
else
        jmp     short fsr20
endif

        ;align   4 54686                 ; DWORD align jump target
fsr60:  add     sp,4                    ; Compensate for saved EAX
ifdef   MMIOPH
        jmp     fsrx
else
        jmp     short fsrx
endif

; During exitlist the semaphores owned by the dieing process can
; be requested.  Ownership is changed to the requesting exitlist
; thread and an error code is returned.  This is similar to the
; way system semaphores work.
;
        ;align   4 54686
fsr70:  mov     ds:[bx].fs_ThrdID,ax    ; Set TID
        mov     ds:[bx].fs_Usage,1      ; Set usage count to 1
        mov     ax,ERROR_SEM_OWNER_DIED ; Return with error
ifdef   MMIOPH
        jmp     fsri
else
        jmp     short fsri
endif

EndProc DOSFSRAMSEMREQUEST


;***    DOSFSRAMSEMCLEAR - release ownership of a fast, safe RAM semaphore
;
;       This function releases ownership of the passed semaphore.  It
;       works by decrement the usage count associated with the semaphore
;       and only if it is zero, releases ownership.  If there is some
;       other process/thread waiting for this semaphore, then this
;       function calls OS/2 to wakeup all waiting process/threads.
;
;       This function does NOT check to see if the caller actually owns
;       the semaphore.
;
;       DOSFSRAMSEMCLEAR (pSem1);
;
;       ENTRY:  DWORD pointer to struct FastSafeRamSemStruc
;
;       EXIT:   If success, AX = 0
;               If error, AX = error code
;
;       HISTORY:
;       28-Jan-93 [AllenW]   Defect 61117 DosFSRamSemXxx trashing top of EAX
;       24-Jul-91            Rearrange code to reduce execution time
;       20-Jul-89 [robertre] update to 32 bit, protect against page faults.
;       02-Dec-87 [mikem]    Return error if thread doesn't own semaphore.
;       24-Jan-87 [stevewo]  Made releasing the semaphore the default
;                            (no jumps) path.
;       21-Jan-87 [stevewo]  Created.
;

;***DT  preDOSFSRAMSEMCLEAR - DosFSRamSemClear pre-invocation trace point
;
;    TRACE MINOR = <RAS_DC1_PRE_DOSFSRAMSEMCLEAR>,
;          TP    = .DOSFSRAMSEMCLEAR,
;          TYPE  = (PRE,API),
;          GROUP = SEM,
;          DESC  = "(OS) DosFSRamSemClear Pre-Invocation",

;***DT  postDOSFSRAMSEMCLEAR - DosFSRamSemClear post-invocation trace point
;
;    TRACE MINOR = <RAS_DC1_POST_DOSFSRAMSEMCLEAR>,
;          TP    = .postDOSFSRAMSEMCLEAR,
;          TYPE  = (POST,API),
;          GROUP = SEM,
;          DESC  = "(OS) DosFSRamSemClear Post-Invocation",
;          REGS  = (AX),
;          FMT   = "Return Code = <FMT_WORD>"

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

; DOSFSRAMSEMCLEAR stack frame AFTER saving registers
;
fscsf_s         struc
        fscsf_topofeax  dw  ?           ; top half of EAX saved. 61117
        fscsf_ds        dw  ?           ; DS
        fscsf_bx        dw  ?           ; BX
        fscsf_ret       dd  ?           ; Return CS:IP
        fscsf_sem       dd  ?           ; API parameter - address of FSR
fscsf_s         ends

        ;align   4  54686                ; DWORD align beginning of procedure
Procedure DOSFSRAMSEMCLEAR,FAR

        SaveReg <bx,ds>
        rol     eax,16                   ;put top half of EAX in AX  61117
        push    ax                       ;Save EAX                   61117
        MMPHPreDOSFSRamSemClear                 ; MMIOPH hook

; Establish addressability to the local infoseg.
;
        mov     ax,DOSLOCALINFO
        mov     ds,ax

        mov     ax,ds:[LIS_CurThrdID]   ; (AX) = current TID
        shl     eax,16                  ; Move low word into high word
        mov     ax,ds:[LIS_CurProcID]   ; (AX) = current PID

; Get address of FSR structure from stack
;
        mov     bx,sp
        lds     bx,ss:[bx].fscsf_sem    ; (DS:BX) = address of FSR

; Disable interrupts while we look at the fast, safe RAM semaphore.
;
;   - this thread owns the semaphore, otherwise return an error
;   - usage count is still non-zero, so don't release the semaphore
;   - no other process/thread is waiting on the RAM semaphore
;   - some other process/thread is waiting on the RAM semaphore
;
        cli

;       WARNING DANGER WARNING DANGER WARNING DANGER WARNING
;
; The next three instructions make sure that the complete FSR structure
; is present in memory before we modify it.  This is done because a page
; fault would allow another thread access to the FSR structure after we
; have potentially corrupted it.  TO AVOID OTHER PAGE FAULTS, WE MUST NOT
; TOUCH ANY MEMORY OUTSIDE THE FSR STRUCTURE UNTIL WE HAVE FINISHED OUR
; MODIFICATIONS TO THE FSR STRUCTURE.
;
        cmp     dword ptr ds:[bx+6].fs_ProcID,eax
        cmp     dword ptr ds:[bx+3].fs_ProcID,eax
        .errnz  ((fs_RAMSem.RamSemFlag-fs_ProcID+1)-10)

        xor     eax,dword ptr ds:[bx].fs_ProcID ; Semaphore owned by caller?
        jz      short fsc10             ; Yes, attempt to release

        or      ax,ax                   ; Current process own semaphore?
        jnz     fsc30                   ; No, return error code
                                        ; 61379 remove "short" from jnz

        xor     eax,eax
        cmp     ds:[bx].fs_ThrdID,SET_DURING_EXITLIST ; Exitlist thread?
        jne     short fsc30             ; No, return error code

fsc10:  dec     ds:[bx].fs_Usage        ; Decrement the usage count
        jnz     short fsci              ; If still non-zero, don't release

; The usage count is now zero, so release the semaphore by setting the
; process and thread ID fields to zero, clearing the RAM semaphore and
; remembering if any other threads were waiting on the RAM semaphore.
; If not, just re-enable interrupts and return.
;
        mov     dword ptr ds:[bx].fs_ProcID,eax ; Zero out TID also
        mov     ds:[bx].fs_Client,ax

; Zero out both the RamSemOwner byte and the extended owner nibble of
; the RamSemFlag.
;
        .errnz ((RamSemFlag-RamSemOwner)-1)     ; Asser Flag follows owner
        and     word ptr ds:[bx].fs_RAMSem.RamSemOwner, NOT RAMSEM_OWNED
        .errnz  SEM_CLEAR
        mov     al,ds:[bx].fs_RAMSem.RamSemFlag ; (AX) = RamSemFlag
        or      ax,ax
        jnz     short fsc20

fsci:   sti
fscx:   MMPHPostDOSFSRamSemClear                ; MMIOPH post hook
        rol     eax,16           ; put AX in top half of EAX           61117
        pop     ax               ; restore value saved                 61117
        ror     eax,16           ; Top half of EAX contains old value, 61117
                                 ; low half contains return code       61117
        RestoreReg <ds,bx>

        PUBLIC  postDOSFSRAMSEMCLEAR
postDOSFSRAMSEMCLEAR:                   ; RAS post-invocation trace
        ret     4

; Some other process/thread is waiting for this semaphore.  Call OS/2 to
; wakeup all waiting threads.  Non-deterministic as to which one will
; actually get the semaphore first.  The others will have to wait again.
;
        ;align   4  54686                ; DWORD align jump target
fsc20:  lea     bx,ds:[bx].fs_RAMSem    ; (DS:BX) -> OS/2 RAM Semaphore
        push    ds                      ; Safe to use other memory now
        push    bx
        push    ax                      ; Old value of RamSemFlag
        sti                             ; Set after the NEXT instruction
        call    DOSIRAMSEMWAKE          ; Internal call to wake up waiting proc
        jmp     short fscx

; The current thread doesn't own the semaphore.
;
        ;align   4  54686                ; DWORD align jump target
fsc30:  mov     ax,ERROR_EXCL_SEM_ALREADY_OWNED
        jmp     short fsci

EndProc   DOSFSRAMSEMCLEAR

DOSSUBCODE  ENDS


CODE16_TUNE1  SEGMENT
        ASSUME  CS:CODE16_TUNE1

;***EP  DosPTrace - stub for unsupported DosPTrace function
;
;       Unconditionally return ERROR_NOT_SUPPORTED.
;       Fill in the buffer with a bad command error
;       just in case the caller ignores the return code.
;
;       ENTRY   ((SS:SP)+4) = 16:16 pointer to command buffer
;       EXIT    (AX) = ERROR_NOT_SUPPORTED
;       USES    AX

Procedure DOSPTRACE,far
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING

        ArgVar  pt_pbuf,DWORD

        EnterProc
        push    ds
        push    si
        lds     si,pt_pbuf              ; (DS:SI) = buffer pointer
        mov     word ptr [si+4],-1      ; Error
        mov     word ptr [si+6],1       ; Bad command
        mov     ax,ERROR_NOT_SUPPORTED
        pop     si
        pop     ds
        LeaveProc
        ret     4

EndProc DOSPTRACE

CODE16_TUNE1  ENDS

        END
