;       SCCSID = @(#)nampipe2.asm       6.10 91/09/25
SCCSID  equ     "@(#)nampipe2.asm       6.10 91/09/25"
TITLE   NAMPIPE2 - Named pipe I/O code
NAME    NAMPIPE2

;***    nampipe2.asm - Named pipe I/O code
;
;               Copyright(c) Microsoft Corp., 1987
;
;       IMPORTANT NOTE: This file MUST be linked immediately after
;               nampipe1.asm.  Failure to do so will result in an
;               InternalError at boot time.
;
;       This file contains the source to implement named pipes, a
;       form of interprocess communication styled after XENIX named
;       pipes (FIFOs).
;
;       A 'named pipe' implements a full duplex I/O channel between
;       two unrelated processes.  Data written to the 'write end' of
;       the pipe are made available for reading on the 'read end'.
;       The DosMakeNmPipe call creates a named pipe, after which
;       the pipe may be opened by name.  The file handles resulting
;       from the DosMakeNmPipe call or the open of the pipe name are
;       manipulated in the same way as normal file handles.  Data may
;       be written into a pipe via a 'write' system call on one
;       end of the pipe.  Likewise, data are read from the pipe
;       via a 'read' call on the read end.
;
;       Data that are written to a pipe are captured in a circular
;       buffer.  The size of the buffer is specified when the pipe
;       is created; if no size is specified, a default size is used.
;       Full duplex pipes have two of these buffers allocated.
;
;       The circular buffer is described by an 'npb' (Named Pipe
;       Buffer) structure, which resides in a kernel heap memory object
;       (mapped by a GDT selector) allocated when the pipe is created.
;       There is one npb for incoming data and one npb for outgoing
;       data.  The npb contains all of the per-buffer information, such
;       as reader, writer, and reference counts, and also holds the
;       pointers into the circular buffer proper.  The buffer itself
;       is contiguous with the npb.
;
;       When the in and out pointers are equal, the circular buffer
;       is empty.  When the in pointer trails the out pointer by 1,
;       the buffer is full.  Thus, a 512 byte buffer can hold only
;       511 bytes; one byte is lost so that full and empty conditions
;       can be distinguished.
;
;       Associated with each end of the pipe is a pipe mode which
;       may modify the behavior of read and write calls to the pipe.
;       The primary modes available are message mode and non-blocking
;       mode.  Message mode is used to allow writes to the pipe to
;       distinguish the entire I/O transfer as a unit.  Reads from the
;       pipe will not receive more than one message (they could get a
;       partial message).
;
;       Non-blocking mode can be used to avoid doing an I/O operation
;       if it required that the process block.  One exception to this
;       is a write in which there is enough space in the pipe to start
;       a transfer.  Such a write will always run to completion unless
;       the process is interrupted or all the readers go away.
;
;       NOTE:   This module has been derived from the anonymous pipe
;               code.  It still retains much of the structure of
;               pipe.asm and the I/O routines, readiob, rcb, writeiob
;               and wcb could probably be combined.  In addition,
;               there are similar semaphore routines in both sets of
;               code.
;
;   Public routines:
;       w_PeekNmPipe
;       w_RawReadNmPipe
;       NmPipeRead
;       w_TransactNmPipe
;       w_RawWriteNmPipe
;       NmPipeWrite
;       NmPipeCommit
;       np_sem_clear
;
;   Private routines:
;       np_handle_io
;       np_sft_io
;       nmpipe_read_peek
;       readiob
;       nprcb
;       rd_chk_avail
;       chk_buf_wrap
;       np_trans_sft
;       nmpipe_write_raw
;       writeiob
;       wrt_chk_avail
;       npwcb
;       get_lock_sem
;       np_sem_blk
;       np_sem_sig
;       np_sem_clear
;       np_poke_user_sem
;
;   Modification History:
;
;       Created 08 Jan 1987, alanw
;               cribbed from OS/2 .../drv2/src/pipe.asm:
SCCSID  equ     "pipe.asm       8.5 86/11/23"
; @0081 08/28/91  ROC  PTR    CP20 AB00081 Changed writeiob to check for a
;                                          shutdown before blocking.
; @6074 09/04/91  ROC  PTR    CP20 B726074 Changed nprcb to check for a -1
;                                          message length (which is used as
;                                          as flag).  If found, change to 0.
; @6535 09/04/91  GAG  PTR    CP20 B726535 Enable DosRead/DosWrite timeout
;                                          in blocking mode.
; @6877 09/12/91  ROC  PTR    CP20 B726877 Pulled B726074.  That ptr had an
;                                          impact on pipe function used by
;                                          the spooler (fast print).
; 67244 04-19-93  Rix (ChgTeam)            Moved misplaced pushf.
; 73844 09-22-93  AWJ (ChgTeam)            Doswrite: named pipe blocks forever
; 82466 04-20-94  WDM (ChgTeam)            fix savereg/restorereg mismatch
; 83633 05-10-94  Change Team              RestoreReg <AX> was missing which
;                                          caused stack pointer to be off
; 86021 07-07-94  Skip Nizinski            Changed np_sem_clear to also
;                                          save/restore esi and edi regs.
;150101 04-02-96  TPL                      Use _ShutdownFlag instead

.xlist
include dosseg.inc
.xcref
include defdbug.inc
include basemaca.inc
include osmaca.inc
include fnofs.inc
include oldmaca.inc
include curdir.inc
include dossymc.inc
include argframe.inc
include error.inc
include fsc.inc
include struc.inc
include mi.inc
include mult.inc
include sft.inc
include shutdown.inc                        ; @0081
include nampipe.inc
include osconst.inc
include pgexport.inc
include tkexport.inc
include vmexport.inc
include codelock.inc
.cref
.list
.286p
        ModName , FNO_NAMPIPE2_ASM


;*      External Per-Task variables referenced in this file

        ReferTask       Pid, WORD
        ReferTask       CurrTCB,WORD            ; current thread control block


        EXTRNFAR        FuBuff
        EXTRNFAR        ProbeBuff
        EXTRNFAR        ProcBlock
        EXTRNFAR        ProcRun
        EXTRNFAR        SFFromHandle
        EXTRNFAR        SuWord
        EXTRNFAR        SuBuff
        EXTRNFAR        SemClear
        EXTRNFAR        dh_PostEventSem

        EXTRN32         CodeLockProc
        EXTRN32         CodeUnlockProc
        EXTRN32         _SEMFindType


            FARCODE HIGH4
NMPIPESWAPCODE   SEGMENT

        ASSUME  SS:TASKAREA


        public startofnampipe2
startofnampipe2 label byte

;***    np_handle_io - common setup for named pipe I/O
;***    np_sft_io - common setup for named pipe I/O
;
;       Various setup is done in preparation for a named pipe I/O
;
;       ENTRY   (dx)    = file handle of pipe   (np_handle_io only)
;               (es:di) = pointer to SFT        (np_sft_io only)
;
;       EXIT    Carry clear, no problems
;                   (ds:si) = FSC
;
;                   if local named pipe (si == FSC_LOCAL)
;                       TCBThisSFT set
;                       (es:di) = pointer to named_pipe structure
;                       (ax)    = read buffer selector
;                       (bx)    = write buffer selector
;                       (dx)    = pipe mode
;
;                   if remote named pipe (si != FSC_LOCAL)
;                       TCBThisSFT = (es:di) = SFT pointer
;
;               Carry set, error occurred
;                       (ax) = error code
;
;       USES    AX, BX, DX, SI, DI, DS, ES, Flags
;

Procedure       np_handle_io,NEAR,local
        ASSUME  DS:NOTHING,ES:NOTHING

        mov     bx,dx
        CALLFAR SFFromHandle            ; (es:di) = sft pointer
        jc      cnioe

Entry           np_sft_io
        mov     bx,CurrTCB
        mov     ss:[bx.TCBThisSFT.Offst],di
        mov     ss:[bx.TCBThisSFT.Segmt],es

        lds     si,es:[di].sf_FSC       ; ds:si -> fsc
    assume ds:nothing
        cmp     si,FSC_LOCAL            ; what file system?
        jne     cniox                   ; remote

; set up for local named pipe

        mov     ax,ERROR_INVALID_FUNCTION
        test    es:[di.sf_flags],SF_PIPE ; is it a pipe?
        jz      cnioe                   ; no
        test    es:[di.sf_flags],SF_NMPIPE ; is it a named pipe?
        jz      cnioe                   ; no

        mov     dx,es:[di.sf_pipmod]    ; get named pipe mode
        test    dx,NP_SDISCON           ; disconnected by server?
        jnz     cnio8                   ; yes, return error
        les     di,es:[di.sf_np]        ; get named_pipe structure
        mov     ax,es:[di.np_selector2] ; (ax) = incoming buffer selector
        mov     bx,es:[di.np_selector1] ; (bx) = outgoing buffer selector
        test    dx,NP_SERVER            ; server end of pipe?
        jnz     cniox                   ; yes, we're set up
        xchg    ax,bx                   ; (bx) = incoming buffer selector
                                        ; (ax) = outgoing buffer selector
cniox:  clc
        ret

cnio8:  mov     ax,ERROR_PIPE_NOT_CONNECTED
cnioe:  stc
        ret

EndProc         np_handle_io


BREAK   <Reading from pipes>

;***    w_PeekNmPipe - Read but do not discard from a pipe
;
;       ENTRY   (dx)    = named pipe file handle
;               (cx)    = length of buffer
;               (ss:bx) = argument frame; contains pointer to buffer
;                         and BytesAvail return paramater
;
;       EXIT    C flag clear:
;                       (ax) = BytesRead
;                       (dx) = PipeState
;               C flag set if error
;                       (ax) = error code
;
;       USES    EAX, BX, CX, DX, SI, DI, DS, ES, Flags
;

Procedure       w_PeekNmPipe,<FARONLY,far16>
        ASSUME  DS:NOTHING,ES:NOTHING

        LocalVar        npp_blen,WORD   ; size of user's data area
        LocalVar        npp_pbyta,DWORD ; bytes available return area

        LocalVar        npp_req,<size npp_param>

        CodeLock        NMPIPE_GROUP

        EnterProc       <cx>

        CpuMode 386
        mov     eax,ss:[bx].pnmp_BytesAvail
        mov     npp_pbyta,eax           ; save ptr to BytesAvail ret param
        mov     di,CurrTCB
        mov     eax,ss:[bx].pnmp_Buffer
        mov     ss:[di].TCBDMAAdd,eax   ; save address of user read buffer
        CpuMode reset

        call    np_handle_io
        jnc     short npp10             ; no error occurred
;
;       The error ERROR_PIPE_NOT_CONNECTED is not considered to be an error
;       for peek.
;
        cmp     ax,ERROR_PIPE_NOT_CONNECTED
        stc
        LJNE    nppx
        xor     ax,ax                   ; (ax) = bytes left in pipe
        mov     bx,ax                   ; (bx) = bytes left in message
        mov     cx,ax                   ; (cx) = bytes peeked
        mov     dx,NP_DISCONNECTED      ; (dx) = pipe state
        jmp     short npp40

npp10:  cmp     si,FSC_LOCAL            ; what file system?
        je      short npp30             ; jump if local named pipe

; set up parameter for FSD

        mov     npp_req.npp_len,size npp_param
        mov     ax,npp_blen
        mov     npp_req.npp_dlen,ax     ; set data buffer size


; FSD named pipe
; ds:si -> FSC

        mov     bx,CurrTCB
        les     di,TASKAREA:[bx].TCBThisSFT ; es:di -> sft
        lea     ax,[di].sf_fsi          ; es:ax -> sf_fsi
        push    es
        push    ax                      ; push pointer to sf_fsi
        lea     ax,[di].sf_fsd          ; es:ax -> sf_fsd
        push    es
        push    ax                      ; push pointer to sf_fsd
        push    NMP_PeekPipe            ; Op code
        lea     ax,npp_req              ; ss:ax -> parameter block
        push    ss
        push    ax                      ; push pointer to parameter block
        xor     ax,ax                   ; ax = null pointer
        les     di,TASKAREA:[bx].TCBDMAAdd ; es:di -> buffer
        push    es
        push    di                      ; push pData = buffer address
        push    ax
        push    ax                      ; push null pName
        call    ds:[si].FS_NMPIPE
        xor     bx,bx
        cmp     bx,ax                   ; set carry if ax != 0

        jc      short nppx              ; error, just return
        mov     cx,npp_req.npp_nbyt
        mov     ax,npp_req.npp_avl0
        mov     bx,npp_req.npp_avl1
        mov     dx,npp_req.npp_state
        jmp     short npp40             ; transfer avail. bytes to user buffer

;*      Do a peek on a local pipe
;
;       (es:di) = named_pipe structure
;       (ax) = handle of input buffer
;       (bx) = handle of output buffer
;       (cx) = byte count
;       (dx) = pipe mode

npp30:  and     dx,NOT NP_RMESG
        or      dx,NP_NBLKR+NP_PEEK
        mov     bl,es:[di.np_state]     ; save state for return
        and     bx,NP_STATE_MASK        ; mask out internal states
        push    dx
        push    bx
        call    nmpipe_read_peek
        pop     dx                      ; restore pipe state
        pop     si                      ; (si) = pipe mode
        jc      short nppx              ; return if error

;*      Set the available bytes return parameters in the user data area
;
;       (ax) = number of bytes remaining in pipe
;       (bx) = residual message size
;       (cx) = number of bytes peeked
;       (dx) = pipe state
;       (si) = pipe mode

        test    si,NP_WMESG             ; message mode pipe?
        jz      short npp40
        add     bx,cx                   ; add bytes read to resid. msg size
npp40:
        push    bx
        mov     di,npp_pbyta.loword
        mov     bx,npp_pbyta.hiword     ; (bx:di) = pointer to BytesAvail return
        CALLFAR SuWord                  ; save bytes remaining in buffer
        pop     ax
        jc      short nppx
        CALLFAR SuWord                  ; save bytes remaining in message
        mov     ax,cx                   ; (ax) = number of bytes read
nppx:
        LeaveProc
        CodeUnlock NMPIPE_GROUP
        ret
EndProc         w_PeekNmPipe


;***    w_RawReadNmPipe - direct read of named pipe
;
;       ENTRY   (dx)    = named pipe file handle
;               (cx)    = length of buffer
;               (ss:bx) = argument frame; has pointer to buffer
;
;       EXIT    C flag clear:
;                       (cx) = number of bytes read
;               C flag set if error
;                       (ax) = error code
;
;       USES    EAX, BX, CX, DX, SI, DI, DS, ES, Flags
;

Procedure       w_RawReadNmPipe,<FARONLY,far16>
        ASSUME  DS:NOTHING,ES:NOTHING

        LocalVar        nprr_req,<size nprw_param>
        CodeLock        NMPIPE_GROUP
        EnterProc

        mov     di,CurrTCB
        CpuMode 386
        mov     eax,ss:[bx].rrnp_Buffer
        mov     ss:[di].TCBDMAAdd,eax   ; save pointer to user buffer
        CpuMode reset

        call    np_handle_io
        jc      short nprr3             ; error occurred

        cmp     si,FSC_LOCAL            ; what file system?
        je      short nprr5             ; do local named pipe

; set up request packet for FSD

        mov     nprr_req.nprw_len,size nprw_param
        mov     nprr_req.nprw_dlen,cx   ; set data buffer size

; do FSD named pipe

        mov     bx,CurrTCB
        les     di,TASKAREA:[bx].TCBThisSFT ; es:di -> sft
        lea     ax,[di].sf_fsi          ; es:ax -> sf_fsi
        push    es
        push    ax                      ; push pointer to sf_fsi
        lea     ax,[di].sf_fsd          ; es:ax -> sf_fsd
        push    es
        push    ax                      ; push pointer to sf_fsd
        push    NMP_READRAW             ; Op code
        lea     ax,nprr_req             ; ss:ax -> parameter block
        push    ss
        push    ax                      ; push pointer to parameter block
        les     di,TASKAREA:[bx].TCBDMAAdd ; es:di -> buffer
        push    es
        push    di                      ; pData -> buffer
        xor     ax,ax                   ; ax = null pointer
        push    ax
        push    ax                      ; push null pName
        call    ds:[si].FS_NMPIPE
        xor     bx,bx
        cmp     bx,ax                   ; set carry if ax != 0

        mov     cx,nprr_req.nprw_nbyt   ; (cx) = bytes read
        jmp     short nprrx             ; return, C set as appropriate

nprr3:  mov     cx,0
        jmp     short nprrx

;  nprr5:  and     dx,NOT (NP_RMESG+NP_WMESG+NP_NBLKR)         @6535
nprr5:  and     dx,NOT (NP_RMESG+NP_WMESG+NP_NBLKR+NP_TIMOUT) ;@6535
        call    nmpipe_read_peek

nprrx:  LeaveProc
        CodeUnlock NMPIPE_GROUP
        ret
EndProc         w_RawReadNmPipe


;***    NmPipeRead - read data from a pipe
;***    nmpipe_read_peek - alternate entry point for raw read and peek
;
;       Most of work is getting ready for a call to readiob,
;       which does the work of getting the data from the
;       circular buffer.
;
;       We lock the pipe for reading; this prevents simultaneous
;       reads of more than the buffer length from 'interspersing'
;       data among the reads.  This interleaving might also
;       occur if the reads are short but block when the buffer
;       becomes empty.
;
;       ENTRY   (es:di) = ptr to SFT
;               TCBDMAAdd = ptr to data destination
;               (cx) = byte count
;
;       EXIT    (cx) = Number of bytes read
;               if (PEEK)
;                       (ax) = number of unread bytes in pipe
;                       (bx) = number of bytes in message
;
;       USES    AX, BX, CX, DX, SI, DI, ES, Flags
;

Procedure       NmPipeRead,hybrid
        ASSUME  DS:NOTHING,ES:NOTHING

LocalVar npr_locked,WORD                ; whether code is locked

        CodeLock NMPIPE_GROUP
        EnterProc <CODE_IS_LOCKED>      ; indicate code locked

        DBPRT   20h,40h, <NmPipeRead entered Pid $x\n>,ss:Pid

        call    np_sft_io
        jnc     short npr1
        jmp     short nprx2

Entry   nmpipe_read_peek,,local
        EnterProc <CODE_NOT_LOCKED>,NoCheck     ; indicate code not locked
npr1:   or      ax,ax                   ; allocated buffer?
        jne     short npr2              ; yes, no problem
        mov     ax,ERROR_ACCESS_DENIED
        stc
        jmp     short nprx2             ; exit, no need to unlock code

npr2:   SaveReg <dx,di,ds,es>
        mov     ds,ax                   ; (ds:0) = ptr to pipe header
        mov     bx, npb_rdlck           ; (ds:bx) = ptr to read lock sem
        call    get_lock_sem            ; entering critical section
        jnc     short npr3              ; no error acquiring semaphore

;*      Error acquiring semaphore.  Set up to return the error.
;
;       C set
;       (ax) = error code

        mov     dx,cx                   ; indicate no bytes read
        push    ax                      ; save registers as below
        pushf
        push    bx
        jmp     short npr5
npr3:
        mov     di,CurrTCB
        mov     bx,ss:[di].TCBDMAAdd.Segmt
        mov     di,ss:[di].TCBDMAAdd.Offst      ; (bx:di) = ptr to src of data
        xor     si,si                   ; (ds:si) = ptr to pipe buffer
        push    cx                      ; preserve byte count
        call    readiob                 ; get the data
        pop     dx                      ; (dx) = original byte count
        push    ax                      ; preserve possible error code
        pushf                           ; preserve flags
        push    bx
        mov     bx, npb_rdlck           ; (ds:bx) = ptr to read lock sem
        call    np_sem_sig              ; leaving critical section
npr5:
        sub     dx, cx
        mov     cx, dx                  ; (cx) = number of bytes read
        pop     bx
        popf                            ; restore flags (ints are on)
        pop     ax                      ; restore error code
        RestoreReg <es,ds,di,dx>
        call    np_poke_user_sem        ; let the other guy know about read

        DBPRT   20h,40h, <NmPipeRead return count $x Pid $x\n>,<cx,ss:Pid>

nprx2:  pushf
        cmp     npr_locked,CODE_IS_LOCKED       ; is code locked?
        jne     short nprx1             ; no, don't unlock
        CodeUnlock NMPIPE_GROUP
nprx1:  popf
        LeaveProc
        ret

EndProc NmPipeRead



;***    readiob - read from an npb
;
;       ENTRY   (ds:si) = ptr to struct npb (in fixed or locked segment)
;               (bx:di) = ptr to dest of data
;               (cx) = byte count
;               (dx) = pipe mode
;
;       EXIT    (cx) = residual byte count
;               if PEEK
;                       (bx) = unread message size
;                       (ax) = buffered data size
;               carry set = error occurred
;                       (ax) = error code
;
;       USES    AX, BX, CX, SI, DI, ES, Flags
;

Procedure       readiob,NEAR,local
        ASSUME  DS:NOTHING,ES:NOTHING

        LocalVar        riob_dstseg,WORD

        EnterProc       <bx>            ; save destination selector

;*      transfer as much as we can without blocking
;
;       (di) = ptr to dest of data
;       (ds:si) = ptr to npb
;       (cx) = byte count
;       (dx) = pipe mode
riob1:
        mov     bx,riob_dstseg          ; (bx:di) = pointer to dest of data
        push    cx                      ; (tos) requested byte count
        call    nprcb                   ; (cx) = number of bytes remaining
        pushf                           ; save error state
        push    ax                      ; save returned message length or error

;       wake up any waiting writer

        lea     bx,[si].npb_wtsem       ; (ds:bx) = offset of wtr sem
        call    np_sem_sig              ; wake up writer that may be waiting

        pop     ax                      ; (ax) = residual message length
        popf
        pop     bx                      ; (bx) = requested number of bytes
        LJC     riobx                   ; if nprcb error, return

        xchg    ax,bx                   ; (ax) = request, (bx) = residual
        jcxz    short riob8             ; transfer done?
        cmp     ds:[si].npb_wtcnt,0
        jz      short riob8             ; no writers => no more data forever
        cmp     ds:[si].npb_rdcnt,0
        jnz     short riob3             ; jump if not disconnected
        mov     ax,ERROR_PIPE_NOT_CONNECTED
        stc
        JMP     short riobx

riob3:  test    dx,NP_WMESG             ; message mode pipe?
        jz      short riob4             ; no
        cmp     bx,-1                   ; started message read?
        je      short riob5             ; no, see if need to wait
        or      bx,bx                   ; in middle of a message?
        jz      short riob8             ; no, return the message
        test    dx,NP_RMESG             ; mesage mode read?
        jz      short riob4             ; no, return with what we have
        jmp     short riob7             ; yes, read more (up to bytcnt)

;       if no bytes were available in the buffer,
;       we must wait for more data if in blocking mode

riob4:  cmp     ax,cx                   ; any bytes read?
        jnz     short riob8             ; yes, return them

riob5:  xor     bx,bx                   ; residual message size = 0
        test    dx,NP_NBLKR             ; non-blocking mode?
        jz      short riob7             ; no, block waiting for data
        test    dx,NP_PEEK              ; peeking?
        jnz     short riob8a            ; yes, don't return error
        mov     ax,ERROR_NO_DATA
        stc
        jmp     short riobx             ; return error

riob7:
        DBPRT   20h,40h, <Readiob - sleeping for data\n>,<>
        lea     bx, [si].npb_rdsem      ; (ds:si) = ptr to read sync. semaphore
        call    np_sem_blk              ; wait for more data
;;      DBPRT   20h,40h, <Readiob - got awakened\n>,<>
        jnc     short riob1             ; go try again

;       We returned from np_sem_blk because of a signal

        jmp     short riobx

riob8:
        test    dx,NP_PEEK              ; peeking data?
        jz      short riob9             ; no, return with success
riob8a:
;       call    rd_chk_msgsize          ; (bx) = number of bytes in message
        call    rd_chk_avail            ; (ax) = data currently in buffer
        jmp     short riob10

riob9:
        test    dx,NP_RMESG             ; message mode read
        jz      short riob10            ; no
        or      bx,bx                   ; residual message?
        jz      short riob10            ; no, no problem
        cmp     bx,-1                   ; read never started?
        je      short riob10            ; yes, not an error
        mov     ax,ERROR_MORE_DATA
        stc
        jmp     short riobx

riob10:
        clc
riobx:
        LeaveProc
        ret

EndProc readiob



;***    nprcb - circular buffer read
;
;       Read as much data out of the circular buffer as we can
;       without blocking.  In any case, we never transfer more
;       bytes than requested.
;
;       This routine deals with message headers in the buffer if
;       the pipe is in message write mode.
;
;       ENTRY   (ds:si) = ptr to npb
;               (bx:di) = ptr to dest of data
;               (cx) = byte count
;               (dx) = pipe mode
;
;       EXIT    (ds:si) = ptr to npb
;               (bx:di) = adjusted dest ptr
;               (cx) = residual byte count
;               (ax) = bytes remaining in current message
;                       -1 if no message in message mode
;
;       USES    AX, BX, CX, DI, ES, Flags
;

Procedure       nprcb,NEAR
        ASSUME  DS:NOTHING,ES:NOTHING

        LocalVar        rcb_dstseg,WORD
        LocalVar        rcb_pmode,WORD
        LocalVar        rcb_bytcnt,WORD
        LocalVar        rcb_msglen,WORD

        EnterProc       <bx,dx,cx>      ; save entry parameters

        DBPRT   20h,80h, <nprcb - entered\n>,<>

        call    rd_chk_avail            ; (ax) = number of bytes in buf

        mov     bx,ds:[si.npb_out]      ; (ds:bx) = source of data
        test    dx,NP_WMESG             ; message headers in buffer?
        jz      short rcb2              ; no, go transfer data
        cmp     ax,MSG_HDR_LEN          ; enough for a message header?
        jb      short rcb1              ; no, don't transfer anything

        mov     al,ds:[bx]              ; msglen = *p++ + (*p++ << 8)
        inc     bx
        call    chk_buf_wrap1
        mov     ah,ds:[bx]
        inc     bx
        call    chk_buf_wrap1
        jmp     short rcb2

rcb1:   mov     ax,-1                   ; msglen = 0
        clc
        DLJMP   rcbx

rcb2:   mov     rcb_msglen,ax

;*      About to copy some data, first compute the max. transfer size
;
;       (ax) = size of current message (avail. data in buf if stream mode)
;       (bx) = pointer to source of data

        cmp     ax,rcb_bytcnt           ; (ax) = min(ax, bytcnt)
        jbe     short rcb4
        mov     ax,rcb_bytcnt

;*      Compute the maximum size of a block transfer into CX.  This
;       will be the minimum of the following quantities:
;
;               the distance from the out pointer to the in pointer
;               the distance from the out pointer to the buffer end
;               the number of bytes requested in the transfer.

rcb4:
        mov     cx, ds:[si].npb_in
        sub     cx,bx
        jz      short rcb7              ; buffer empty, go return
        jnc     short rcb5              ; can transfer to in ptr
        mov     cx, ds:[si].npb_last
        sub     cx,bx                   ; can only xfer to end of buffer
rcb5:
        cmp     cx,ax
        jbe     short rcb6              ; xfer len smaller than byte count
        mov     cx,ax                   ; don't xfer more byte count
rcb6:
        SaveReg <si,ds>                 ; (tos) = npb ptr
        mov     si,bx                   ; (si) = offset part of buffer ptr
                                        ; (ds:si) = ptr to source
                                        ; (di) = offset of ptr to dest for copy
                                        ; (cx) = byte count for this copy
        sub     rcb_msglen,cx           ; adjust bytes remaining in message
        sub     rcb_bytcnt,cx           ; adjust bytes remaining in transfer
        sub     ax,cx                   ; (ax) = bytes remaining after copy
        mov     bx,rcb_dstseg           ; (bx) = destination selector
        DBPRT   20h,80h, <nprcb - xfer len $x\n>,cx
        SaveReg <AX>                    ; SuBuff trashes AX
        CALLFAR SuBuff                  ; (ds:si) is adjusted
                                        ; (bx:di) is adjusted
                                        ; (cx) = zero
        RestoreReg <AX>                 ; (ax) = count of bytes left to transfer
        mov     bx,si                   ; (bx) = offset of new out ptr
        RestoreReg  <ds,si>
        jc      short rcb9              ; fault occurred on copy
        call    chk_buf_wrap1
        or      ax,ax
        jnz     short rcb4              ; more bytes to xfer
rcb7:
        mov     ax,rcb_msglen           ; (ax) = residual message length
        mov     dx,rcb_pmode            ; (dx) = pipe mode
        test    dx,NP_WMESG             ; in message mode?
        jnz     short rcb8              ; yes, may need to stuff msglen back
        xor     ax,ax                   ; no message residual
rcb8:
        test    dx,NP_PEEK              ; peeking buffer?
        jnz     short rcb9              ; yes, don't write buffer or pointer
        or      ax,ax                   ; need to stuff back message length?
        jz      short rcb8a             ; no, save current pointer
        dec     bx                      ; *--p = msglen >> 8
        call    chk_buf_wrap
        mov     ds:[bx],ah
        dec     bx                      ; *--p = msglen & 0xff
        call    chk_buf_wrap
        mov     ds:[bx],al
rcb8a:
        mov     ds:[si.npb_out],bx      ; save current pointer
rcb9:   clc

rcbx:   mov     cx,rcb_bytcnt           ; (cx) = residual byte count
        mov     bx,rcb_dstseg           ; restore entry bx value
        DBPRT   20h,80h, <nprcb exit - $x bytes remaining\n>,cx
;        cmp     ax,-1                   ; -1 represents no message           @6074
;        jnz     rcbx1                   ; if not -1, don't change ax         @6074
;        mov     ax,0                    ; if ax=-1, set it to 0 (msg length) @6074
;rcbx1:                                  ;                                    @6074
        LeaveProc
        ret

EndProc nprcb


;***    rd_chk_avail - check the amount of buffered data
;
;       Compute the size of the data stored in the circular
;       buffer.
;
;       ENTRY   (ds:si) = ptr to npb
;
;       EXIT    (ax) = number of buffered bytes
;
;       USES    AX, Flags
;

Procedure       rd_chk_avail,NEAR
        ASSUME  DS:NOTHING,ES:NOTHING

        mov     ax,ds:[si].npb_in
        sub     ax,ds:[si].npb_out      ; (ax) = (in - out)
        jae     short rca3              ; in >= out, computation done

;*  buffer wrap, add in size of buffer
        add     ax,ds:[si].npb_last
        sub     ax,ds:[si].npb_first
rca3:
        ret
EndProc         rd_chk_avail



;***    chk_buf_wrap - adjust buffer pointer
;
;       chk_buf_wrap will check that to see if an end of the
;       circular buffer has been reached; if so, it will reset
;       the pointer.  The entry point chk_buf_wrap1 may be used
;       when pointers are incremented; it checks only against the
;       high end of the buffer.
;
;       ENTRY   (ds:bx) = pointer in buffer
;               (ds:si) = base of buffer structure
;
;       EXIT    bx is in the range [npb_first : npb_last-1 ]
;
;       USES    BX, Flags
;

Procedure       chk_buf_wrap,NEAR,local
        ASSUME  DS:NOTHING,ES:NOTHING

        cmp     bx,ds:[si.npb_first]            ; went off beginning of buffer?
        jae     chk_buf_wrap1
        mov     bx,ds:[si.npb_last]
        dec     bx
Entry   chk_buf_wrap1,,local
        cmp     bx,ds:[si.npb_last]             ; went off end of buffer?
        jb      short cbw2

IFDEF PIPESTRICT
        je      short cbw1
        InternalError <chk_buf_wrap: beyond end of buffer>
cbw1:
ENDIF

        mov     bx,ds:[si.npb_first]
cbw2:   ret

EndProc         chk_buf_wrap


BREAK   <Writing into pipes>

;***    w_TransactNmPipe - Perform a transaction on a named pipe by handle
;
;       A transactional dialogue on a pipe is attempted.  First, both
;       the write lock and the read lock are acquired.  Once this is
;       done, there should be no buffered data in the pipe (assuming
;       other uses of the pipe have used this call).  The data is
;       written into the pipe and the write lock is released.  Then
;       response data is read from the pipe and returned to the user.
;
;       NOTE:   Careful handling of the pipe read and write selectors is
;               needed here.  Any time the process blocks, selectors
;               in use could be mapped to another one.
;
;       ENTRY   (dx)    = named pipe file handle (w_TransactNmPipe only)
;               (cx)    = length of InBuffer
;               (ax)    = length of OutBuffer
;               (ss:bx) = argument frame; contains pointers to
;                         InBuffer and OutBuffer
;
;
;       EXIT    C flag clear:
;                       (cx) = number of bytes returned
;               C flag set:
;                       (ax) = error code
;
;       USES    AX, BX, CX, DX, SI, DI, DS, ES, Flags
;

Procedure       w_TransactNmPipe,<FARONLY,far16>
        ASSUME  DS:NOTHING,ES:NOTHING

        LocalVar        tnp_ilen,WORD   ; length of user's InBuffer
        LocalVar        tnp_olen,WORD   ; length of user's OutBuffer
        LocalVar        tnp_ibuf,DWORD  ; pointer to user's InBuffer
        LocalVar        tnp_obuf,DWORD  ; pointer to user's OutBuffer

        LocalVar        tnp_req,<size npt_param>        ; remote packet

        CodeLock        NMPIPE_GROUP

        EnterProc       <cx,ax>
        DBPRT   20h,40h, <w_TransactNmPipe entered Pid $x\n>,ss:Pid

        CpuMode 386
        mov     eax,ss:[bx].tnmp_InBuffer
        mov     tnp_ibuf,eax            ; save pointer to input buffer
        mov     eax,ss:[bx].tnmp_OutBuffer
        mov     tnp_obuf,eax            ; save pointer to output buffer
        CpuMode reset

        call    np_handle_io            ; map the handle
        LJC     tnpx                    ; error occurred

        cmp     si,FSC_LOCAL            ; what file system?
        je      short tnp30             ; local

; set up parameter for FSD

        lea     di,tnp_req              ; TASKAREA:di -> request packet
        mov     TASKAREA:[di.npt_len],size npt_param
        mov     ax,tnp_ilen
        mov     TASKAREA:[di.npt_ilen],ax
        mov     ax,tnp_obuf.Offst
        mov     TASKAREA:[di.npt_obuf.Offst],ax
        mov     ax,tnp_obuf.Segmt
        mov     TASKAREA:[di.npt_obuf.Segmt],ax
        mov     ax,tnp_olen
        mov     TASKAREA:[di.npt_olen],ax

; FSD named pipe

        mov     bx,CurrTCB
        les     di,TASKAREA:[bx].TCBThisSFT ; es:di -> sft
        lea     ax,[di].sf_fsi          ; es:ax -> sf_fsi
        push    es
        push    ax                      ; push pointer to sf_fsi
        lea     ax,[di].sf_fsd          ; es:ax -> sf_fsd
        push    es
        push    ax                      ; push pointer to sf_fsd
        push    NMP_TransactPipe        ; Op code
        lea     ax,tnp_req              ; ss:ax -> parameter block
        push    ss
        push    ax                      ; push pointer to parameter block
        les     di,tnp_ibuf             ; es:di -> inbuffer
        push    es
        push    di                      ; pData -> inbuffer
        xor     ax,ax                   ; ax = null pointer
        push    ax
        push    ax                      ; push null pName
        call    ds:[si].FS_NMPIPE
        xor     bx,bx
        cmp     bx,ax                   ; set carry if ax != 0
        mov     cx,tnp_req.npt_nbyt     ; (cx) = number of bytes read
        jmp     short tnpx              ; C set appropriately, jsut return

; local named pipe

tnp30:  push    tnp_ibuf.Segmt
        push    tnp_ibuf.Offst          ; pointer to user's InBuffer
        push    tnp_ilen                ; length of user's InBuffer
        push    tnp_obuf.Segmt
        push    tnp_obuf.Offst          ; pointer to user's OutBuffer
        push    tnp_olen                ; length of user's OutBuffer

        call    np_trans_sft            ; do the transaction
                                        ; C set as appropriate, stack cleaned
                                        ;   by LeaveProc
        DBPRT   20h,40h,<w_TransactNmPipe return count $x Pid $x\n>,<cx,ss:Pid>
tnpx:   LeaveProc                       ; cleans stack
        CodeUnlock NMPIPE_GROUP
        ret

EndProc w_TransactNmPipe


;***    np_trans_sft - Perform a transaction on a named pipe with SFT
;
;       A transactional dialogue on a pipe is attempted.  First, both
;       the write lock and the read lock are acquired.  Once this is
;       done, there should be no buffered data in the pipe (assuming
;       other uses of the pipe have used this call).  The data is
;       written into the pipe and the write lock is released.  Then
;       response data is read from the pipe and returned to the user.
;
;       The np_trans_sft entry point is used by w_CallNmPipe and
;       w_TransactNmPipe.
;
;       NOTE:   Careful handling of the pipe read and write selectors is
;               needed here.  Any time the process blocks, selectors
;               in use could be mapped to another one.
;
;
;       ENTRY   [TCBThisSFT] = pointer to SFT
;               (TOS+12:TOS+10) = pointer to InBuffer
;               (TOS+8) = length of InBuffer
;               (TOS+6:TOS+4) = pointer to OutBuffer
;               (TOS+2) = length of OutBuffer
;
;       EXIT    C flag clear:
;                       (cx) = number of bytes returned
;               C flag set:
;                       (ax) = error code
;
;       USES    AX, BX, CX, DX, SI, DI, DS, ES, Flags
;

WLOCK = 1
RLOCK = 2

Procedure       np_trans_sft,NEAR
        ASSUME  DS:NOTHING,ES:NOTHING

        ArgVar          nts_olen,WORD   ; length of user's OutBuffer
        ArgVar          nts_obuf,DWORD  ; pointer to user's OutBuffer
        ArgVar          nts_ilen,WORD   ; length of user's InBuffer
        ArgVar          nts_ibuf,DWORD  ; pointer to user's InBuffer

        LocalVar        nts_nbyt,WORD   ; number of bytes read
        LocalVar        nts_locks,WORD  ; indicator of locks set
        LocalVar        nts_rdsel,WORD  ; selector of read buffer
        LocalVar        nts_wtsel,WORD  ; selector of write buffer

        EnterProc       <0,0>           ; init nts_locks, nts_nbyt

        mov     di,CurrTCB
        les     di,ss:[di.TCBThisSFT]   ; (es:di) = SFT pointer
        call    np_sft_io
        jc      short ntse              ; error occurred
        test    dx,NP_WMESG             ; message mode pipe?
        jnz     short nts2              ; yes, no problem
        mov     ax,ERROR_BAD_FORMAT
        jmp     short ntse

nts2:   and     dx,NOT NP_NBLK          ; must be blocking mode
        or      dx,NP_RMESG             ; must do read in msg. mode

        or      bx,bx                   ; write buffer allocated?
        jz      short nts3              ; no, error
        or      ax,ax                   ; read buffer allocated?
        jne     short nts4              ; yes, no problem
nts3:   mov     ax,ERROR_ACCESS_DENIED
ntse:   stc
        jmp     ntsx

nts_eip: mov    ax,ERROR_INVALID_PARAMETER
        jmp     short ntse

nts4:   test    nts_ilen,-1             ; non-zero in-buffer length?
        jz      short nts_eip           ; no, return error
        test    nts_olen,-1             ; non-zero out-buffer length?
        jz      short nts_eip           ; no, return error

        SaveReg <ax>                    ; save read buffer selector
        mov     ds,bx                   ; (ds:0) = ptr to pipe write buffer
        mov     bx, npb_wtlck           ; (ds:bx) = ptr to write lock
        call    get_lock_sem            ; entering critical section
        mov     nts_wtsel,ds            ; save write buffer selector
nts4a:  RestoreReg <ds>                 ; (ds) = read buffer selector

        or      nts_locks,WLOCK         ; write buffer is locked
        mov     bx, npb_rdlck           ; (ds:bx) = ptr to read lock
        call    get_lock_sem            ; entering critical section
        mov     nts_rdsel,ds            ; save read buffer selector
        jnc     short nts4c             ; not interrupted, do the write
        mov     ds,nts_wtsel            ; (ds) = write buffer selector
        jmp     short ntsx2             ; interrupted, unlock write buf, unmap
;
;       We've acquired both locks, so no one else can be reading or
;       writing.  Check to see if read buffer is empty.
;
nts4c:  or      nts_locks,RLOCK         ; read buffer is locked
        xor     si,si                   ; (ds:si) = ptr to read pipe buffer
        call    rd_chk_avail            ; (ax) number of bytes in buffer
        or      ax,ax                   ; buffered bytes?
        mov     ds,nts_wtsel            ; (ds:si) = ptr to write buffer
        jz      short nts5              ; no, no problem
        mov     ax,ERROR_PIPE_BUSY      ; there's a usage error somewhere
        stc
        jmp     short ntsx2

;*      write our message into the pipe
;
;       (dx)    = pipe mode
;       (ds:si) = pointer to pipe's write buffer

nts5:
        mov     cx,nts_ilen             ; (cx) = length of write
        SaveReg <es,di>
        mov     ax,di                   ; es:ax -> np structure                 ;AWJ73774
        mov     bx,nts_ibuf.Segmt
        mov     di,nts_ibuf.Offst       ; (bx:di) = ptr to src of data
        call    writeiob                ; get the data
        RestoreReg <di,es>
        pushf
        sub     cx,nts_ilen             ; (cx) == 0 iff no bytes written
        popf
        call    np_poke_user_sem
ntsx2:
        SaveReg <ax,es>                 ; preserve possible error code, np ptr
        pushf                           ; and flags
        mov     bx, npb_wtlck           ; (ds:bx) = ptr to write lock
        call    np_sem_sig              ; leaving critical section

        popf                            ; restore flags (ints are on)
        RestoreReg <es,ax>              ;     and possible error code, np ptr
        mov     ds,nts_rdsel
        jc      short ntsx1             ; error, don't bother with read

        sub     si,si                   ; (ds:si) = ptr to pipe read buffer
        mov     cx,nts_olen             ; (cx) = length of read
        push    cx                      ; preserve byte count
        SaveReg <es,di>
        mov     bx,nts_obuf.Segmt
        mov     di,nts_obuf.Offst       ; (bx:di) = ptr to dest of data
        call    readiob                 ; get the data
        RestoreReg <di,es>
        mov     bx,cx
        pop     cx                      ; (cx) = original byte count
        pushf
        sub     cx, bx                  ; (cx) = number of bytes read
        popf
        mov     nts_nbyt,cx             ; save read size
        call    np_poke_user_sem

;*      Release the read lock and selector if appropriate
;
;       'C' set if error
;       (ax) = possible error code
;       (ds) = buffer selector (can be either read or write)

ntsx1:
        push    ax                      ; preserve possible error code
        pushf                           ; preserve flags
        test    nts_locks,RLOCK         ; holding read lock?
        jz      short ntsx3             ; no, release memory
        mov     bx, npb_rdlck           ; (ds:bx) = ptr to read lock sem
        call    np_sem_sig              ; leaving critical section
ntsx3:
        popf                            ; restore flags (ints are on)
        pop     ax                      ; restore error code
ntsx:
        mov     cx,nts_nbyt             ; (cx) = read size
        DBPRT   20h,40h, <w_TransactNmPipe return count $x Pid $x\n>,<cx, ss:Pid>
        LeaveProc
        ret

EndProc np_trans_sft


;***    w_RawWriteNmPipe - write a pipe in raw mode (ignore msg mode)
;
;       ENTRY   (dx)    = named pipe file handle
;               (cx)    = length of buffer
;               (ss:bx) = argument frame; contains pointer to user buffer
;
;       EXIT    C flag clear:
;                       (cx) = number of bytes written
;               C flag set if error
;                       (ax) = error code
;
;       USES    EAX, BX, CX, DX, SI, DI, DS, ES, Flags
;

Procedure       w_RawWriteNmPipe,<FARONLY,far16>
        ASSUME  DS:NOTHING,ES:NOTHING

        LocalVar        nprw_req,<size nprw_param>
        CodeLock        NMPIPE_GROUP
        EnterProc

        mov     di,CurrTCB
        CpuMode 386
        mov     eax,ss:[bx].rwnp_Buffer
        mov     ss:[di].TCBDMAAdd,eax   ; save pointer to user buffer
        CpuMode reset

        call    np_handle_io
        jc      short nprw3             ; error occurred

        cmp     si,FSC_LOCAL            ; what file system?
        je      short nprw5             ; local named pipe

; set up request packet for FSD

        mov     nprw_req.nprw_len,size nprw_param
        mov     nprw_req.nprw_dlen,cx   ; set data buffer size


; do FSD named pipe

        mov     bx,CurrTCB
        les     di,TASKAREA:[bx].TCBThisSFT ; es:di -> sft
        lea     ax,[di].sf_fsi          ; es:ax -> sf_fsi
        push    es
        push    ax                      ; push pointer to sf_fsi
        lea     ax,[di].sf_fsd          ; es:ax -> sf_fsd
        push    es
        push    ax                      ; push pointer to sf_fsd
        push    NMP_WRITERAW            ; Op code
        lea     ax,nprw_req             ; ss:ax -> parameter block
        push    ss
        push    ax                      ; push pointer to parameter block
        les     di,TASKAREA:[bx].TCBDMAAdd ; es:di -> buffer
        push    es
        push    di                      ; pData -> buffer
        xor     ax,ax                   ; ax = null pointer
        push    ax
        push    ax                      ; push null pName
        call    ds:[si].FS_NMPIPE
        xor     bx,bx
        cmp     bx,ax                   ; set carry if ax != 0
        mov     cx,nprw_req.nprw_nbyt   ; (cx) = bytes written
        jmp     short nprwx             ; return, C set as appropriate

nprw3:  mov     cx,0
        jmp     short nprwx

;  nprw5:  and     dx,NOT (NP_RMESG+NP_WMESG+NP_NBLKW)         @6535
nprw5:  and     dx,NOT (NP_RMESG+NP_WMESG+NP_NBLKW+NP_TIMOUT) ;@6535
        call    nmpipe_write_raw

nprwx:  LeaveProc
        CodeUnlock NMPIPE_GROUP
        ret
EndProc         w_RawWriteNmPipe


;***    NmPipeWrite - write data into a pipe
;***    nmpipe_write_raw - alternate entry point for raw write
;
;       Most of work is getting ready for a call to writeiob,
;       which does the work of getting the data into the
;       circular buffer.
;
;       We lock the pipe for writing; this prevents simultaneous
;       writes of more than the buffer length from 'interspersing'
;       data among the writes.  This interleaving might also
;       occur if the writes are short but block when the buffer
;       becomes full.
;
;       ENTRY   (es:di) = ptr to SFT
;               TCBDMAAdd = source pointer
;               (cx) = byte count
;
;       EXIT    CY: ax = error code,
;               else (cx) = # of bytes written
;
;       USES    AX, BX, CX, DX, SI, DI, ES, Flags
;

Procedure       NmPipeWrite,hybrid
        ASSUME  DS:NOTHING,ES:NOTHING

LocalVar npw_locked,WORD                ; whether code is locked or not

        CodeLock NMPIPE_GROUP
        EnterProc <CODE_IS_LOCKED>      ; indicate code is locked

        DBPRT   20h,40h, <NmPipeWrite entered Pid $x\n>,ss:Pid

        call    np_sft_io
        jnc     short npw1
        jmp     short npwx2

Entry           nmpipe_write_raw,,local
        EnterProc <CODE_NOT_LOCKED>,NoCheck     ; indicate code not locked
npw1:   or      bx,bx                   ; allocated buffer?
        jne     short npw2              ; yes, no problem
        mov     ax,ERROR_ACCESS_DENIED
        stc
        jmp     short npwx2

npw2:   SaveReg <dx,di,ds,es>
        ; di will later be restored into ax: needed for call to writeiob        ; AWJ73774
        SaveReg <di>                    ; save the offset of np structure       ; AWJ73774
        mov     ds,bx                   ; (ds:0) = ptr to pipe buffer

        mov     bx, npb_wtlck           ; (ds:bx) = ptr to write lock
        call    get_lock_sem            ; entering critical section
        jnc     short npw3              ; no error acquiring semaphore

;*      Error acquiring semaphore.  Set up to return the error.
;
;       C set
;       (ax) = error code

        pop     ax                      ; 83633 - restore DI to AX
        mov     bx,cx                   ; indicate no bytes written
        push    ax                      ; save registers as below
        pushf
        push    bx
        jmp     short npw5
npw3:   RestoreReg  <ax>                ; (ax) = offset of np structure         ;AWJ73774
        sub     si,si                   ; (ds:si) = ptr to pipe header
        mov     di,CurrTCB
        mov     bx,ss:[di].TCBDMAAdd.Segmt
        mov     di,ss:[di].TCBDMAAdd.Offst      ; (bx:di) = ptr to src of data
        push    cx                      ; preserve byte count
        call    writeiob                ; get the data
        pop     bx                      ; (bx) = origianl byte count
        push    ax                      ; preserve error code
        pushf                           ; and flags
        push    bx
        mov     bx, npb_wtlck           ; (ds:bx) = ptr to write lock
        call    np_sem_sig              ; leaving critical section
npw5:
        pop     bx
        sub     cx, bx
        neg     cx                      ; (cx) = # of bytes read
        popf                            ; restore flags (ints are on)
        pop     ax                      ; and error code
        RestoreReg <es,ds,di,dx>
        call    np_poke_user_sem        ; let the other guy know about write

        DBPRT   20h,40h, <NmPipeWrite return count $x Pid $x\n>,<cx, ss:Pid>

npwx2:  pushf
        cmp     npw_locked,CODE_IS_LOCKED       ; code locked?
        jne     short npwx1             ; no, don't unlock
        CodeUnlock NMPIPE_GROUP
npwx1:  popf
        LeaveProc
        ret

EndProc NmPipeWrite


;***    NmPipeCommit -- flush write data
;
;       Commit a pipe.  This operation will block until all data
;       written has been read by the other end.
;
;       ENTRY   TCBThisSFT = ES:DI  Points to associated SFT.
;
;       EXIT    NC: ES:DI = points to SFT
;                   DS = TASKAREA
;               CY: ax = error code
;
;       USES    AX, BX, CX, DX, SI, DI, DS, ES, Flags
;

Procedure       NmPipeCommit,hybrid
        ASSUME  DS:NOTHING,ES:NOTHING

        CodeLock NMPIPE_GROUP

        call    np_sft_io
        jc      short npcmx             ; error

        and     dx,NOT (NP_RMESG+NP_WMESG+NP_NBLKW)
        or      dx,NP_PEEK              ; set mode to blocking, peek
        mov     di,CurrTCB              ; save buffer address
        mov     word ptr ss:[di].TCBDMAAdd,0
        mov     word ptr ss:[di].TCBDMAAdd+2,0
        mov     cx,1
        call    nmpipe_write_raw
npcmx:
        CodeUnlock NMPIPE_GROUP
        ret

EndProc NmPipeCommit


;***    writeiob - write to a Name Pipe Buffer
;
;       ENTRY   (ds:si) = ptr to struct npb (in fixed or locked segment)
;               (bx:di) = ptr to source of data
;               (es:ax) = ptr to NP structure                                   ;AWJ73774
;               (cx) = byte count
;               (dx) = pipe flags
;
;       The conditions for the write are:
;          There must be a reader
;          We must be connected
;
;          In msg mode:
;             in blocking mode -
;               *  the write will always commence blocking as necessary.
;             in non-blocking mode -
;               *  If there is space for the header+1byte but the message is
;                  larger than the buffer size the write  will be initialized
;                  but block for buffer synchronization with the reader.
;               *  If the buffer size is larger than the message size, but
;                  there is not space in the buffer for the msg, the write
;                  will return with a buffer overflow error.
;
;          In byte mode:
;             in blocking mode -
;               *  the write will always commence blocking as necessary.
;             in non-blocking mode -
;               *  As much data will be written into the buffer as possible
;                  and the write will return (possibly with bytewritten !=
;                  Bufferlength).
;               *  If no space at all in buf return buffer overflow error
;
;       EXIT    Normal: 'C' clear, (cx) = residual byte count
;               Error:  'C' set
;                       AX =    ERROR_PIPE_NOT_CONNECTED
;                               ERROR_BROKEN_PIPE
;                               ERROR_DISCARDED ; same as BROKEN_PIPE but
;                                               ; pipe data flushed
;
;       USES    AX, BX, CX, SI, DI, ES, Flags
;

Procedure       writeiob,NEAR,local
        ASSUME  DS:NOTHING,ES:NOTHING

        LocalVar        wiob_block,WORD         ; flag indicating can block or not
        LocalVar        wiob_srcseg,WORD        ; selector for source of data
        LocalVar        wiob_pnp,DWORD      ; pointer to name pipe structure    ;AWJ73774
;        EnterProc       <-1,es,bx>         ; flg=can block, source selector    ;AWJ73774
        EnterProc       <-1,bx,es,ax>       ; flg=can block, source selector    ;AWJ73774

        test    dx,NP_NBLKW             ; non-blocking mode?
        jz      short wiob1             ; jmp, if in blocking mode

        call    wrt_chk_avail           ; can we start non-block write?
        mov     [wiob_block],bx         ; save the block indicator
        jnz     short wiob1             ; Yes, there's enough space in buffer

        jmp     wiobx

wiob1:  test    dx,NP_WMESG             ; in write message mode?
        jz      short wiob5             ; no, go write data

;       Before writting the message, we probe the source buffer to make
;       sure that it is valid.

wiob1a: SaveReg <cx, dx, ds, si>
        mov     bx, wiob_srcseg
        mov     si, di                  ; bx:si -> Source buffer
        xor     dx, dx                  ; dx == 0 (ACCTYPE_READ)
        CALLFAR ProbeBuff
        RestoreReg <si, ds, dx, cx>
        jc      wiob11                  ; invalid buffer, get out

wiob2:  cmp     ds:[si].npb_rdcnt,0
        jz      wiob9                   ; no readers, go force error
        cmp     ds:[si].npb_wtcnt,0
        jz      wiob10                  ; jump if we've been disconnected
        call    wrt_chk_avail           ; see if we can start
        jnz     short wiob3             ; there's some space in buffer
        lea     bx, [si].npb_wtsem
        call    np_sem_blk              ; wait for space
        jc      wiob8                   ; interrupted wait - return error
        jmp     short wiob2             ; check for space again

;       write out the message header
;       (cx) = length of message

wiob3:  SaveReg <di>                    ; save current user buffer pointer
        push    cx
        push    ss
        pop     bx
        mov     di,sp                   ; (bx:di) = point to message length
        mov     cx,MSG_HDR_LEN
        call    npwcb                   ; write the message header

IFDEF PIPESTRICT
        jc      short wiob3b            ; write MUST complete
        jcxz    short wiob4             ; write MUST complete
wiob3b: InternalError <writeiob: not enough space in buffer>
wiob4:
ENDIF
        pop     cx                      ; restore size to be written
        RestoreReg <di>                 ; restore user buffer pointer

;       write the text of the message (or the byte data)

wiob5:  cmp     ds:[si].npb_rdcnt,0
        jz      short wiob9             ; no readers, go force error
        cmp     ds:[si].npb_wtcnt,0
        jz      short wiob10            ; jump if we've been disconnected

        test    dx,NP_PEEK              ; peeking?
        jz      short wiob6             ; no, go do I/O

        call    rd_chk_avail            ; anything to be read?
        or      ax,ax                   ; buffered bytes?
        jz      short wiobx             ; no, we're done
        jmp     short wiob7

;       transfer as much data as we can without blocking
;
;       (es:di) = ptr to source of data
;       (ds:si) = ptr to npb
;       (cx) = byte count
;       (dx) = pipe mode

wiob6:  mov     bx,wiob_srcseg
        push    ds:[si].npb_in          ; save npb_in in case npwcb fails
        call    npwcb                   ; (cx) = number of bytes remaining
        pop     ax                      ; ax = original npb_in
        jnc     short wiob6a            ; no error, continue

;       npwcb failed, so we restore npb_in to its original value and
;       return with error.

        mov     ds:[si].npb_in, ax      ; restore npb_in
        jmp     short wiob11            ; return error

;       we've put in some data, so notify any waiting readers

wiob6a: lea     bx, [si].npb_rdsem      ; (ds:bx) = ptr to rdr sem
        call    np_sem_sig              ; wake up reader who may be waiting

; AWJ73774 - this code was picked up from 1.3. It is possible that it was       ;AWJ73774
;            never integrated into 2.0.                                         ;AWJ73774
; Something of liberty is taken here.  The assumption is made that since        ;AWJ73774
; there was space in the buffer and we have the pipe sem, some of the write     ;AWJ73774
; will have succeeded, part of the body, or at least the header for message     ;AWJ73774
; mode pipes.  Also, we set cx to some nonzero value to force the sem clear.    ;AWJ73774

SaveReg <cx,di,es>                                                              ;AWJ73774
les     di,[wiob_pnp]           ; es:di -> name pipe structure                  ;AWJ73774
mov     cx,1                    ; cx != 0, some bytes written                   ;AWJ73774
call    np_poke_user_sem        ; let the other guy know about write            ;AWJ73774
RestoreReg  <es,di,cx>          ;                                               ;AWJ73774

        jcxz    short wiobx             ; no more to write, we're done
        or      [wiob_block],0          ; can we block?
        jz      short wiobx             ; No, so exit with not all written

;       wait for space

wiob7:  DBPRT   20h,40h, <writeiob - sleeping for space\n>,<>

        push    ds                      ; Preserve reg                @0081
        DosContext DS                   ; Change to Shutdown data seg @0081
        cmp     ds:[_ShutdownFlag], 1   ; Get flag                    @0081 150101
        pop     ds                      ; Restore reg                 @0081
;150101 cmp     bx,1                    ; If a Shutdown,              @0081
        jz      short wiobx             ; jump to exit,               @0081
                                        ; instead of blocking         @0081

        lea     bx, [si].npb_wtsem
        call    np_sem_blk              ; wait for space
        jnc     short wiob5             ; np_sem_blk returned normally
        test    dx,NP_WMESG             ; in message mode?
        jnz     short wiob5             ; sorry, can't interrupt msg mode write!

wiob8:  jmp     short wiobx

wiob9:  mov     ax,ERROR_BROKEN_PIPE
        cmp     [si].npb_data,0         ;was data flushed before disconnect?
        je      short wiobe             ;Yes, go return error

        mov     ax,ERROR_DISCARDED      ; set error code
        jmp     short wiobe

wiob10: mov     ax,ERROR_PIPE_NOT_CONNECTED
        jmp     short wiobe

wiob11: mov     ax, ERROR_INVALID_BLOCK

wiobe:  stc                             ; indicate error

wiobx:  LeaveProc
        ret

EndProc writeiob


;***    wrt_chk_avail - check for sufficient available space in buffer
;
;       Check that there is enough space available in the circular
;       buffer to be able to start a data transfer.
;
;
;       ENTRY   (ds:si) = ptr to npb
;               (cx) = size to be written
;               (dx) = pipe mode
;
;       EXIT    (ax) = number of bytes available in buffer
;               (bx)!=0 can block, (bx)=0 can't block
;               Z flag = Z if insufficient space available
;               Z flag = NZ otherwise
;
;       USES    AX, BX, Flags
;

Procedure       wrt_chk_avail,NEAR
        ASSUME  DS:NOTHING,ES:NOTHING

        xor     bx,bx                   ; (bx) = can't block
        test    dx,NP_NBLKW             ; In non block mode?
        jnz     short wca00             ; Yes, Jmp as flag is ok
        not     bx                      ; (bx) = can block

wca00:  mov     ax,ds:[si].npb_out
        sub     ax,ds:[si].npb_in       ; (ax) = (out - in)
        ja      short wca10             ; out>=in, don't account for buf wrap

;*  buffer wrap, add in size of buffer

        add     ax,ds:[si].npb_last
        sub     ax,ds:[si].npb_first

wca10:  dec     ax                      ; can't use the last byte in buffer
        test    dx,NP_WMESG             ; is it in message mode?
        jnz     short wca20             ; Yes, go see if is enough space

;       here we are in byte mode

        or      ax,ax                   ; is the buffer at least 1 byte
        ret                             ; return result in Z flag

;       from here on we know that we are in message mode

wca20:  test    dx,NP_NBLKW             ; is it a non-blocking write?
        jnz     short wca30             ; Yes, go see if is enough space

;       for a blocking msg write is there enough space for the hdr + 1byte?

        cmp     ax,MSG_HDR_LEN
        ja      short wca50             ; Yes, return enough space
        jmp     short wca40             ; Return not enough space

wca30:  push    cx
        add     cx,MSG_HDR_LEN          ; add in the msg header to write size
        cmp     ax,cx                   ; enough space to start non-block write?
        pop     cx
        jae     short wca50             ; Yes, return enough space

;       Here we know that there isn't enough space in the buffer to completely
;       contain the Msg. If this is because the Msg is larger than the buffer
;       then we will treat it as a blocking write.

        mov     bx,ds:[si].npb_last
        sub     bx,ds:[si].npb_first    ; (ax) = buffer length
        sub     bx,MSG_HDR_LEN+1
        cmp     bx,cx
        jb      short wca45             ; Msg>buffer so treat as blocking write
                                        ; ie (bx) = can block

        xor     bx,bx                   ; (bx) = can't block
wca40:  test    ax,0                    ; Return not enough space
        ret

wca45:  or      bx,cx                   ; (bx) = can block
wca50:  or      sp,sp
        ret
EndProc         wrt_chk_avail



;***    npwcb - circular buffer write
;
;       We write as much data into the circular buffer as we can
;       without blocking.  In any case, we never transfer more
;       bytes than requested.
;
;       NOTE:   The source buffer must be validated (ProbeBuff) before
;               calling this function. This function always returns
;               with the carry clear.
;
;       ENTRY   (ds:si) = ptr to npb
;               (bx:di) = ptr to source of data
;               (cx) = byte count
;               (dx) = pipe mode
;
;       EXIT    (ds:si) = ptr to npb
;               (di) = adjusted source ptr offset
;               (cx) = residual byte count
;               CY if invalid source buffer
;
;       USES    AX, BX, CX, SI, DI, ES, Flags
;

Procedure       npwcb,NEAR
        ASSUME  DS:NOTHING,ES:NOTHING

        LocalVar        wcb_srcseg,WORD         ; selector of source
        EnterProc       <bx>

        DBPRT   20h,80h, <npwcb - entered\n>,<>

;       exchange pointers

        push    ds
        pop     es
        xchg    si,di                   ; (bx:si) = ptr to src data
                                        ; (es:di) = ptr to npb
        mov     bx, cx                  ; (bx) = byte count

;*      calculate length of transfer
;       there are two cases:
;       1. in ptr is < out ptr
;               in this case, we can only put (out - in) - 1 bytes in
;       2. in ptr >= out ptr
;               In this case, we can only xfer to end of buffer.
;               If in == out == first, we must leave last byte
;               empty.
wcb00:
        mov     cx, es:[di].npb_in
        mov     ax, es:[di].npb_out
        sub     cx, ax                  ; (cx) = -(out - in)
        jb      short wcb10             ; in < out, can only go to (out - 1)
        add     cx, ax                  ; undo subtraction (cx) = in
        sub     cx, es:[di].npb_last
        cmp     ax, es:[di].npb_first
        jne     short wcb20
wcb10:
        inc     cx                      ; 'subtract' from negated count
wcb20:
        neg     cx                      ; (cx) = transfer count

        jcxz    short wcb50             ; no room, return
        cmp     cx, bx
        jbe     short wcb30             ; xfer len smaller than request
        mov     cx, bx                  ; don't xfer more than requested

;       (es:di) = ptr to npb
;       (cx) = byte count for this xfer
;       (si) = source ptr
;       (bx) = total number of bytes to be xfer'ed

wcb30:
        push    di

        mov     di, es:[di].npb_in      ; si = in ptr
                                        ; (es:di) = dest ptr for copy
                                        ; (cx) = byte count for copy
        sub     bx, cx                  ; (bx) = bytes remaining after copy
        DBPRT   20h,80h, <npwcb - xfer len $x\n>,cx
        SaveReg <es,bx,cx>
        mov     bx,wcb_srcseg
        CALLFAR FuBuff                  ; (bx:si) is adjusted
                                        ; (es:di) is adjusted
                                        ; (cx) = zero (if successful)
        RestoreReg <cx,bx,es>
        jnc     short wcb35             ; no error, continue

;67244  pushf
        pop     di
        pushf                           ;67244;
        add     cx, bx                  ; cx = residual byte cound
        jmp short wcb90                 ; error return

wcb35:  mov     cx, di                  ; (cx) = offset part of new out ptr
        pop     di                      ; (es:di) = ptr to npb
        cmp     cx, es:[di].npb_last
        jb      short wcb40             ; didn't hit end of buffer
        mov     cx, es:[di].npb_first   ; hit end, handle wrap around
wcb40:
        mov     es:[di].npb_in, cx      ; and save the new pointer
        or      bx, bx
        jnz     short wcb00             ; more bytes to xfer
wcb50:
        mov     cx, bx                  ; (cx) = residual byte count
        clc
        pushf
wcb90:  push    es
        pop     ds
        xchg    di,si                   ; swap pointers back
        xor     ax,ax
        mov     es,ax                   ; (es) = null selector
        DBPRT   20h,80h, <npwcb exit - $x bytes remaining\n>,cx
        popf
        LeaveProc
        ret

EndProc npwcb


BREAK   <Semaphore Subroutines>

;***    get_lock_sem - get a lock semaphore
;
;       This subroutine is like np_sem_wait except that it will
;       return immediately if called with a non-blocking pipe
;       mode parameter and the semaphore is not available.
;
;       ENTRY   (ds:bx) = ptr to semaphore word
;               (dx)    = pipe mode
;
;       EXIT    Carry clear
;                   if semaphore owned by caller
;                   semaphore set
;                   interrupts enabled
;               Carry set
;                   ProcBlock interrupted or Semaphore busy
;                   Interrupts enabled
;
;       USES    AX, flags, I-flag
;

Procedure       get_lock_sem,NEAR,local
        ASSUME  DS:NOTHING,ES:NOTHING

        mov     al,1
        xchg    al,BYTE PTR ds:[bx]     ; set semaphore

;       If busyflag was set then its still set and (al) = 1
;       If busyflag was clear then its set now and (al) = 0

        or      al,al                   ; (OR clears carry)
        jz      short glsx              ; done  - we've got it

        test    dx,NP_NBLK              ; non-blocking call?
        jz      short np_sem_wait       ; no, block waiting for it

        xor     ax,ax                   ; don't return specific error
        stc                             ; indicate we couldn't get sem.
glsx:   ret
EndProc         get_lock_sem


;***    np_sem_blk - set a semaphore and wait on it
;
;       ENTRY   (ds:bx) = ptr to semaphore word
;
;       EXIT    thru np_sem_wait
;
;       USES    AX, flags, I-flag
;

Procedure       np_sem_blk,,local
        ASSUME  DS:NOTHING,ES:NOTHING

        mov     byte ptr ds:[bx], 1             ; set the flag


;**     np_sem_wait - Wait for a semaphore
;
;       This routine operates on a semaphore made up of a pair of
;       bytes -- an "in use" flag and a "someone is waiting" flag.
;
;       DB      busyflag
;       DB      waitingflag
;
;       If busyflag is clear then
;               set busyflag
;               return
;       else
;               set waitingflag (to notify owner to do a wakeup when he's done)
;               ProcBlock on address of busyflag
;
;       Note that when ProcBlock returns interrupts will be enabled.
;       Since the entire procblock interval was an "interrupts enabled"
;       window we figure we must be called with interrupts on and
;       thus don't mess with PUSHF/popf but just jam the interrupts
;       on and off as necessary (in keeping with good practice we
;       keep them on as much as possible).
;
;       In order to have a consistent Block/Run key, we use the selector
;       of the npb segment as the high order word of the key.
;
;       It is presumed that the ProcBlock should be interruptible.
;
;       ENTRY   (DS:BX) = address of semaphore word
;
;       EXIT    Carry clear
;                   When semaphore owned by caller
;                   semaphore set
;                   enables interrupts
;               Carry set
;                   (ax) = error code
;                   ProcBlock interrupted
;                   Interrupts enabled
;
;       USES    AX, I-flag, Flags
;


Entry           np_sem_wait
        ASSUME  DS:NOTHING,ES:NOTHING
        LocalVar  TIMOUT,WORD                                             ;@6535
        EnterProc                                                         ;@6535
        SaveReg <dx,bx,es,di,ax>                                          ;@6535
        mov     TIMOUT,-1                                                 ;@6535
        mov     bx,CurrTCB                                                ;@6535
        les     di,TASKAREA:[bx].TCBThisSFT ; es:di -> sft                ;@6535
        mov     ax,es:[di].sf_pipmod    ; look at mode at creation time   ;@6535
        test    ax,NP_TIMOUT            ; see if bits are set for timeout ;@6535
        jz      short swa00             ; no infinite                     ;@6535
        and     ax,NP_TIMOUT            ; mask off bits                   ;@6535
        shr     ax,11                   ; shift bits to be useable        ;@6535
        mov     bx,2000                 ; multiply by 2000 (ms * 1000 * 2 secs) ;@6535
        mul     bx                      ; multiply to get timeout value   ;@6535
        mov     TIMOUT,ax               ; put timeout in TIMOUT           ;@6535
swa00:                                                                    ;@6535
        RestoreReg <ax,di,es,bx,dx>                                       ;@6535

;       test semaphore
;
;       (ds:bx) = address

swa0:   mov     al,1
        xchg    al,BYTE PTR ds:[bx]     ; set semaphore

;       If busyflag was set then it's still set and (al) = 1
;       If busyflag was clear then it's set now and (al) = 0

        or      al,al                   ; (OR clears carry)
;          jz      swa1                    ; done  - we've got it    @6535
        jnz     short swa                                           ;@6535
        jmp     swa1                    ; done  - we've got it      ;@6535
swa:                                                                ;@6535

;*      The thing is set.  We're going to have to sleep on it.  We don't
;       need to "cli" because the semaphore cannot be cleared by any
;       interrupt time activity.  Hence, there is no possibility of a
;       race with the semaphore owner releasing it while we're blocking.
;
;       (al) = 1
;       (ds:bx) = pointer to semaphore

        mov     byte ptr ds:[bx+1],al   ; set waiting

;;;;;;;;;;;;;;;;;;;; Ray Andrade Defect# 79982 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;removed Ray  mov     ax,ds                   ; AX:BX -> semaphore to block.

;;;;;;;;;;;;;;;;;;;; Ray Andrade Defect# 79982 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; This code moves ds to ax and then clears ds 4-lines added.
        push     ds                     ;
        xor      ax,ax                  ; clear ds to prevent trap
        mov      ds,ax                  ; in proc block.
        pop      ax                     ; AX:BX -> semaphore to block
;;;;;;;;;;;;;;;;;;;; Ray Andrade Defect# 79982 END FIX 1 ;;;;;;;;;;;;;;;;;;;;

        SaveReg <ax,cx,dx,di>

;         mov     cx,-1                 ; cx:di = -1 no timeout    @6535
;         mov     di,cx                                            @6535

        mov     cx,TIMOUT               ; cx:di = -1 no timeout   ;@6535
        .if   <cx eq -1>                                          ;@6535
        mov     di,cx                                             ;@6535
        .else                                                     ;@6535
        mov     di,0                                              ;@6535
        .endif                                                    ;@6535

        mov     dh,0                    ; interruptible ProcBlock
        CALLFAR ProcBlock               ; wait till semaphore released
        RestoreReg      <di,dx,cx,ax>


;;;;;;;;;;;;;;;;;;;; Ray Andrade Defect# 79982 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; restore registers and check to see that pipe buffers are still valid.

        SaveReg <es,di,bx>
        pushf

        mov     bx,CurrTCB              ; get the SFT
        les     di,TASKAREA:[bx].TCBThisSFT ; es:di -> SFT
        les     di,es:[di].sf_np        ; get the pipe struct
        mov     bx,es
        cmp     ax,bx                   ;does segment match semaphore?
        jz      short swa2
        cmp     ax,es:[di].np_selector1 ;does segment match semaphore?
        jz      short swa2
        cmp     ax,es:[di].np_selector2 ;does segment match semaphore?
        jz      short swa2
        ;;;; data segment gone - return as if INTERRUPTED
        popf
        RestoreReg    <bx,di,es>
        stc
        jmp     short swa1a

swa2:   popf
;        RestoreReg    <bx,di,es>       ; 82466  wdm
        pop      bx                     ; 82466  wdm
        pop      di                     ; 82466  wdm
        pop      es                     ; 82466  wdm

;;;;;;;;;;;;;;;;;;;; Ray Andrade Defect# 79982 END FIX 2 ;;;;;;;;;;;;;;;;;;;;

        mov     ds,ax                   ; (ds:0) = ptr to header

        jnc     swa0                    ; Loop if normal awakening

;       ProcBlock was interrupted.  Return with carry set.

;;;;;;;;;;;;;;;;;;;; Ray Andrade Defect# 79982 label added ;;;;;;;;;;;;;;;;;;
swa1a:
;;;;;;;;;;;;;;;;;;;; Ray Andrade Defect# 79982 END FIX 3 ;;;;;;;;;;;;;;;;;;;;
        mov     ax,ERROR_INTERRUPT      ; indicate interrupted syscall

swa1:   LeaveProc                       ;@6535
        ret                             ;@6535
;  swa1:   ret                           @6535
EndProc np_sem_blk



;**     np_sem_sig - Signal a semaphore
;
;       np_sem_sig releases a semaphore (it is presumed that the caller indeed
;       owns it!) and, if anyone was waiting, wakes them up.
;
;       ENTRY   (DS:BX) = address of semaphore word (see np_sem_wait, above)
;
;       EXIT    NONE
;
;       USES    AX, Flags
;

Procedure       np_sem_sig
        ASSUME  DS:NOTHING,ES:NOTHING

        xor     ax,ax
        xchg    ax,ds:[bx]              ; clear semaphore and wait flag

;       (al) = semaphore flag
;       (ah) = wait flag

        or      ah,ah
        jz      short ssig2             ; nobody waiting, don't ProcRun

;*      Someone is waiting on this semaphore.  Wake hir up.
;
;       (ds:bx) = semaphore address = block/run code value

        mov     ax,ds                   ; AX:BX -> semaphore to run
        CALLFAR ProcRun                 ; Yes, let them run.

ssig2:  ret

EndProc np_sem_sig

;**     np_sem_clear - Clear a system semaphore
;
;       np_sem_clear releases a user-supplied (system) semaphore.
;
;       ENTRY   (ES:BX) = address of semaphore handle
;
;       EXIT    NONE
;
;       USES    AX, Flags
;

Procedure       np_sem_clear
        ASSUME  DS:NOTHING,ES:NOTHING

        CpuMode 386
;        SaveReg <ebx, ecx, edx, es, ds, fs, gs>
        SaveReg <esi, edi, ebx, ecx, edx, es, ds, fs, gs>      ; 86021 added esi and edi
        mov     eax, es:[bx]
        or      eax, eax                ; existing associated sem?
        jz      short nscx              ; no, just exit

        SaveReg <eax, ebx, es>
        FlatContext <ds,es>,,noassume
        CCALL32 _SEMFindType,<eax>      ; check if valid 32-bit sempahore
        or      eax, eax                ; 32 bit semaphore?
        RestoreReg <es, ebx, eax>
        jz      short nsc5              ; no, assume 16 bit

;   32-bit semaphore   EAX == Semaphore handle
        CALLFAR dh_PostEventSem         ; call 32-bit routine
        jmp     short nscx

;   16-bit semaphore   ES:BX -> Semaphore handle
nsc5:   mov     ax,es:[bx.hiword]
        mov     bx,es:[bx.loword]       ; (ax:bx) = semaphore handle
        CALLFAR SemClear

; nscx:   RestoreReg <gs, fs, ds, es, edx, ecx, ebx>
nscx:   RestoreReg <gs, fs, ds, es, edx, ecx, ebx, edi, esi>   ; 86021 added esi and edi
        CpuMode Reset
        ret
EndProc np_sem_clear

;**     np_poke_user_sem - conditionally clear one of the user semaphores
;
;       np_poke_user_sem will SemClear one the user-supplied semaphores
;       if the following conditions are met:
;
;               - read or write size is non-zero
;               - operation was not a peek or flush
;               - the user semaphore has been set
;
;       ENTRY   (ES:DI) = pointer to NP structure
;               (CX)    = number of bytes read or written
;               (DX)    = pmode flags for operation
;
;       EXIT    NONE
;
;       USES    None (including flags)
;

Procedure       np_poke_user_sem,NEAR,local
        ASSUME  DS:NOTHING,ES:NOTHING

        CpuMode 386
        pushf
        SaveReg <eax, ebx, es>
        jcxz    short npusx
        test    dx,NP_PEEK              ; peek or flush operation?
        jnz     short npusx
        lea     bx,es:[di.np_ssem]      ; assume client side reading/writing
        test    dx,NP_SERVER            ; server side?
        jz      short npus1             ; no
        lea     bx,es:[di.np_csem]      ; server side reading/writing

npus1:  call    np_sem_clear            ; let other guy know about operation

npusx:  RestoreReg <es, ebx, eax>
        popf
        CpuMode Reset
        ret
EndProc         np_poke_user_sem

        PUBLIC _EndNMPIPEGROUP
_EndNMPIPEGROUP LABEL BYTE

NMPIPESWAPCODE   ENDS
        END
