Google
 

Trailing-Edge - PDP-10 Archives - SRI_NIC_PERM_SRC_1_19910112 - 7/ft3/monitor/stanford/pipe.mac
There are 4 other files named pipe.mac in the archive. Click here to see a list.
;<6-1-MONITOR>PIPE.MAC.10, 26-Mar-85 11:09:11, Edit by LOUGHEED
; Prevent spurious PIPRLX buginfs due to GTJFN% parsing failures
;<6-MONITOR>PIPE.MAC.9, 25-Jun-84 16:55:05, Edit by LOUGHEED
; Fix version lookup under Release 6
;<6-MONITOR>PIPE.MAC.8, 21-Jun-84 02:06:23, Edit by LOUGHEED
;<6-MONITOR>PIPE.MAC.7, 19-Jun-84 20:26:23, Edit by LOUGHEED
; Conditional assembly for Release 6
;<5-3-MONITOR>PIPE.MAC.6,  1-Jan-84 22:03:41, Edit by LOUGHEED
;<5-3-MONITOR>PIPE.MAC.5,  1-Jan-84 19:25:45, Edit by LOUGHEED
; If both ends of a pipe are opened in mode 1 (.GSSMB) no buffering
;  is done.  Makes interactive applications more viable.
;<5-3-MONITOR>PIPE.MAC.4, 26-Nov-83 23:31:58, Edit by LOUGHEED
; Release pipe data structures if GTJFN% version lookup fails
;<5-3-MONITOR>PIPE.MAC.3, 29-Oct-83 02:21:22, Edit by LOUGHEED
; PIPCLZ doesn't try to flush a non-existent queued buffer
;<5-3-MONITOR>PIPE.MAC.2, 29-Oct-83 01:57:59, Edit by LOUGHEED
; Make PIPNAM and PIPEXT a bit smarter about rejecting alphabetic strings
;<5-3-MONITOR>PIPE.MAC.1, 29-Oct-83 00:04:51, Edit by LOUGHEED
; Initial production version
SEARCH PROLOG
TTITLE(PIPE,PIPE,< -- I/O PIPES>)
SUBTTL Kirk Lougheed / Stanford University / 13-October-1983

; Copyright (c) 1983, 1984, 1985 by
; The Board of Trustees, Leland Stanford Junior University, Stanford, CA 94305
; All Rights Reserved.

; The information in this software is subject to change without notice
; and should not be construed as a commitment by Stanford University.
; Stanford assumes no responsibility for the use or reliability of
; this software.
SUBTTL Definitions and Storage

IFNDEF REL6,<REL6==1>		;Default to assembling for Release 6.0

;AC redefinitions

DEFAC (PIP,Q1)			;Index into pipe device data structures
DEFAC (IOS,Q2)			;I/O status bits
DEFAC (STS,P1)			;File status flags
DEFAC (JFN,P2)			;The current JFN
DEFAC (DEV,P4)			;Device bits,,DTB address
DEFAC (F1,P5)			;GTJFN% flags

DEFSTR FILPIP,FILOFN,35,18	;Pipe device number (index into PIPSTS)

EXTN <NPIPES,PIPSTS,PIPST1,PIPBUF,PIPBF1>	;JSB storage defined in STG

;NPIPES==^D10			;Maximum number of pipe devices per job
DEFWSZ==^D100			;Default word size of a pipe buffer
MAXWSZ==777			;Maximum word size of a pipe buffer

;JS PIPSTS,NPIPES
	PIASGF==1B0		;Assigned by GTJFN%
	PIREDF==1B1		;Open for read
	PIWRTF==1B2		;Open for write  
	PIQEDF==1B3		;Queued buffer contains valid data
	PIWCLF==1B4		;EOF (write side was open, is now closed)
	PIRCLF==1B5		;EOF (read side was open, is now closed)
	PILCKF==1B6		;Pipe is locked 
	PIINTF==1B7		;Interactive pipe (.GSSMB open mode)
	DEFSTR PIPBSZ,PIPSTS,23,6	;Byte size of pipe
	DEFSTR PIPWSZ,PIPSTS,35,12	;Number of words in buffer
;JS PIPST1,NPIPES
	DEFSTR PIPBCT,PIPST1,17,18	;Bytes/buffer for this byte size
	DEFSTR PIPWTB,PIPST1,26,9	;Wait bit for PIQEDF flag
	DEFSTR PIPCNT,PIPST1,35,9	;JFN share count for this pipe
;JS PIPBUF,NPIPES
	DEFSTR PIPINP,PIPBUF,35,18	;Input buffer pointer
	DEFSTR PIPOUT,PIPBUF,17,18	;Output buffer pointer
;JS PIPBF1,NPIPES
	DEFSTR PIPQED,PIPBF1,35,18	;Queued buffer pointer
	DEFSTR PIPQCT,PIPBF1,17,18	;Byte count of queued data

;This storage used by all jobs.

NUMWTW==^D10			;Number of words of wait bits
RS PIPBTB,NUMWTW		;Table of wait bits (must be resident)
RS PIPBFF,NUMWTW		;Table of assigned wait bits (must be resident)
NR PIPWTF,1			;-1 if wait bit pool initialized
SUBTTL Pipe Device Dispatch Table

	SWAPCD

PIPDTB::
IFN REL6,<PIPDTL>		;Length of dispatch table
	DTBDSP PIPSET		;*Directory setup routine
	DTBDSP PIPNAM		;*Name lookup
	DTBDSP PIPEXT		;*Extension lookup
	DTBDSP PIPVER		;*Version lookup
	DTBBAD (DESX9)		; Protection insertion
	DTBBAD (DESX9)		; Account insertion
	DTBBAD (DESX9)		; Status insertion
	DTBDSP PIPOPN		;*Open file
	DTBDSP PIPSQI		;*Byte input
	DTBDSP PIPSQO		;*Byte output
	DTBDSP PIPCLZ		;*Close file
	DTBBAD (DESX9)		; Rename
	DTBBAD (DESX9)		; Delete file
	DTBBAD (DESX9)		; Dump mode input
	DTBBAD (DESX9)		; Dump mode output
	DTBBAD (DESX9)		; Mount device
	DTBBAD (DESX9)		; Dismount device
	DTBBAD (DESX9)		; Initialize directory
	DTBBAD (DESX9)		; Do mtape operation
	DTBDSP PIPGTD		;*Get device status (GDSTS%)
	DTBBAD (DESX9)		; Set device status
	DTBDSP PIPREC		;*Force record out (SOUTR%)
	DTBDSP RFTADN 		; Read file time and date
	DTBDSP SFTADN 		; Set file time and date
	DTBDSP BIOINP 		; Set JFN for input
	DTBDSP BIOOUT 		; Set JFN for output
	DTBDSP PIPATR		;*Check attribute
	DTBDSP PIPRJF		;*Release JFN
PIPDTL==.-PIPDTB
SUBTTL GTJFN% Parsing

COMMENT	|

To create an instance of a pipe device, the user should give a string such as
"PIP:.;RECORD-SIZE:500" to GTJFN%.  The record size attribute is optional and
controls the number of words in the pipe's buffers.  The important thing is
that both the file name and file extension for the first GTJFN% string are
null strings.  GTJFN% will create the pipe and use the pipe index as the file
name for that JFN.  The user is then expected to use JFNS% to create a second
string of the form "PIP:#.#" where "#" is a small number, the file name
created by the first GTJFN%.  It doesn't matter which JFN is used for read or
write.  You may have multiple JFN's on a pipe, but only two may be opened, one
for read and one for write.  When the last JFN on the pipe is released, the
pipe is destroyed.  The buffer size attribute need only be specified once, but
if it is specified twice, it must be the same as the first time. The default
size is 100 (decimal) words.

|

;PIPSET - Set up directory field

PIPSET:	TQNE <STEPF>		;Want to step?
	 RETBAD(GJFX17)		;Yes, error
	NOINT			;Match the OKINT at return
IFN REL6,<RETSKP>		;Success return>
IFE REL6,<JRST SK2RET		;Success return>

;PIPVER - version lookup
;If we fail here we must decrement the share count *now* since RELJFX doesn't
;invoke the device dependent release JFN routine.

PIPVER:
IFE REL6,<JUMPE T1,GJSRET>	;Success if no version information.
IFN REL6,<JUMPE T1,GJ2RET>	;Success if no version information.
	LOAD T1,FILPIP,(JFN)	;Don't want versions, get our pipe number
	LOAD T2,PIPCNT,(T1)	;Get pipe share count
	SUBI T2,2		;Decrement it twice
	STOR T2,PIPCNT,(T1)	;Store back updated count
	MOVX T1,GJFX20		;"No such generation number"
	JRST GJERRX		;Take failure return
;PIPNAM - name lookup routine

;If the name string is non-null and numeric, we verify that the resulting
;number corresponds to a valid, assigned pipe device.  If the name string is
;null, we need to create an instance of a pipe.  This involves setting up the
;pipe data structure and creating a numeric name string for the JFN.  Using
;the name string, the user can then get a second JFN on the pipe.  The name
;string parsing is quite stupid, e.g. "2FOO" is treated the same as "2".

PIPNAM:	SKIPN T1		;Wildcard name?
	 RETERR (GJFX18)	;Yes, return an error
	LDB T3,[POINT 7,1(T1),6] ;Get first character of string
	JUMPE T3,PIPNA0		;Null string means create a pipe
	HRLI T1,(POINT 7,,35)	;Make string pointer
	MOVEI T3,^D8		;Pipes are octal
	NIN%			;Read a number 
	 RETERR(GJFX4)		;"Invalid character in filename"
	SKIPLE T2		;Zero or negative pipe number not allowed
	 CAIL T2,NPIPES		;Range check the pipe number
	  RETERR(ARGX19)	;"Invalid unit number"
	MOVE T1,PIPSTS(T2)	;Get pipe status word
	TXNN T1,PIASGF		;Already assigned?
	 RETERR(DIAGX2)		;"Device is not assigned"
	STOR T2,FILPIP,(JFN)	;Remember our pipe number
	JRST GJ2RET		;Take success return

;This code handles the case of creating a new pipe device.

PIPNA0:	NOSKED			;No races
	CALL GETPIP		;Get a new pipe device
	 RETERR(MONX01,<OKSKED>) ;"Insufficient system resources" 
	MOVX T1,PIASGF		;Get assignment flag
	IORM T1,PIPSTS(T2)	;Make sure pipe is flagged as owned
	OKSKED			;Reallow scheduling
	STOR T2,FILPIP,(JFN)	;Remember our pipe number
	HRRZ T1,FILTMP(JFN)	;Get address of temporary storage string
	SETZM 1(T1)		;Clear first word (avoid storage header word)
	MOVEI T2,2(T1)		;Form blt pointer in T2
	HRLI T2,1(T1)		; ...
	BLT T2,MAXLW(T1)	;Zero the temporary string, just in case
	HRRZ T1,FILTMP(JFN)	;Address of temporary name string buffer
	HRLI T1,(POINT 7,,35)	;Set up byte pointer
	LOAD T2,FILPIP,(JFN)	;Get pipe number
	MOVEI T3,^D8		;Octal radix
	NOUT%			;Replace null name with pipe name
	 RETERR(MONX03)		;"Monitor internal error"
	MOVEM T1,FILOPT(JFN)	;Remember pointer to last byte
	JRST GJ2RET		;Take success return
;PIPEXT - extension lookup

;Here we ensure that we have JFN's of the form "PIP:#." or "PIP:#.#" pointing
;to pipe number "#".  If the tests are passed, the pipe share count is
;incremented.

PIPEXT:	SKIPN T1		;Wildcard name?
	 RETERR (GJFX18)	;Yes, return an error
	LOAD T4,FILPIP,(JFN)	;Get pipe number we think we should have
	LDB T3,[POINT 7,1(T1),6] ;Get first character of string
	JUMPE T3,PIPEX0		;Null string is okay
	HRLI T1,(POINT 7,,35)	;Make string pointer
	MOVEI T3,^D8		;Pipes are octal
	NIN%			;Read a number
	 RETERR(GJFX4)		;Invalid character in filename
	CAME T4,T2		;Extension matches name?
	 RETERR(GJFX16)		;No, "No such device"
PIPEX0:	INCR PIPCNT,(T4)	;Increment share count
	JRST GJ2RET		;Take success return
;PIPATR - check pipe attribute
;We use the "RECORD-LENGTH" attribute for magtapes to specify the number of
; words in a pipe buffer.  This is currently the only attribute of a pipe
; that a user can specify.
;Takes	T1/ address of attribute value string
;	T2/ attribute code
;Returns +1 failure, T1/ error code
;	 +2 success

PIPATR:	CALL SAVPIP		;Preserve our special AC's
	CAIE T2,.PFRLN		;Is this the record length attribute?
	 RETBAD(GJFX49)		;"Invalid attribute for this device"
	SKIPN T1		;Pointer exist?
	 RETBAD(GJFX46)		;"Attribute value is required"
	HRLI T1,010700		;Fix up byte pointer
	MOVEI T3,^D10		;Want a decimal number
	NIN%			;Read it
	 SETZ T2,		;Failed, pretend an illegal value was read
	SKIPLE T2		;Range check the value
	 CAILE T2,MAXWSZ	; ...
	  RETBAD(GJFX50)	;"Invalid argument for attribute"
	LOAD PIP,FILPIP,(JFN)	;Get pipe index
	CALL LCKPIP		;Lock the pipe
	LOAD T1,PIPWSZ,(PIP)	;Get present word size
	SKIPN T1		;Skip if set
	 STOR T2,PIPWSZ,(PIP)	;Else save the new word size
	CAME T1,T2		;Sizes had better match
	 JUMPN T1,[RETBAD(GJFX50,<CALL ULKPIP>)] ;They don't, return error
	IORB IOS,PIPSTS(PIP)	;Make sure IOS and PIPSTS(PIP) agree
	CALL ULKPIP		;Unlock the pipe
	RETSKP			;Skip return to caller
;GETPIP - find a free pipe device index
;Returns +1 failure, no free pipes
;	 +2 success, T2/ pipe index
;Note that zero is always an invalid pipe index
;Caller must be NOSKED to avoid races
;Clobbers T1-T2

GETPIP:	MOVE T2,[XWD -NPIPES+1,1] ;Form aobjn pointer, avoid zero
GETPI0:	MOVE T1,PIPSTS(T2)	;Get status bits
	TXNE T1,PIASGF		;Assigned?
	AOBJN T2,GETPI0		;Yes, try next pipe
	JUMPGE T2,R		;Maybe take failure return
	HRRZS T2		;Clear extraneous bits
	RETSKP			;Return success, T2/ index

;Common GTJFN% returns

IFE REL6,<
GJ2RET:	AOS 0(P)		;Double skip
GJSRET:	AOS 0(P)		;Single skip
>;IFE REL6
IFN REL6,<
GJ2RET:	AOS 0(P)		;Skip return
GJSRET:				;Single return
>;IFN REL6
	TQNE <UNLKF>		;Should we unlock?
	 RET			;No
GJERRX:	OKINT			;Yes
	RET    
SUBTTL OPENF% Routines

;PIPOPN - device dependent OPENF% code
;Returns +1 failure, T1/ error code
;	 +2 success, one side of pipe opened

PIPOPN:	CALL SAVPIP		;Preserve PIP and IOS 
	TQNN <XCTF,RNDF>	;Execute and append are illegal
	 TQNN <READF,WRTF>	;Must be reading or writing
	  RETBAD(OPNX13)	;Illegal access
	TQC <READF,WRTF>	;A pipe end is either read or write
	 TQCN <READF,WRTF>	; ....
	  RETBAD(OPNX13)	;Both read and write is illegal
	LOAD PIP,FILPIP,(JFN)	;Get index into pipe data structures
	CALL LCKPIP		;Lock pipe data structures
	MOVX T1,PIREDF		;Assume trying for read access
	TQNN <READF>		;True?
	MOVX T1,PIWRTF		;No, write access
	TDNE T1,IOS		;Don't open an end of a pipe twice
	 RETBAD(OPNX9,<CALL ULKPIP>)	;"Invalid simultaneous access"

;Determine data mode.
	LDB T1,[POINT 4,STS,35]	;Get four bits of open mode
	CAIE T1,.GSNRM		;Normal (mode 0)
	 CAIN T1,.GSSMB		;Small buffer (mode 1).  Interactive use.
	  TRNA			;Good mode
	   RETBAD(OPNX14,<CALL ULKPIP>)	;"Invalid mode requested"
	TXNN IOS,PIINTF		;Other side opened in interactive mode?
	IFSKP.
	  CAIE T1,.GSSMB	;Yes, make sure we are doing same
	   RETBAD(OPNX14,<CALL ULKPIP>)	;"Invalid mode requested"
	ENDIF.
	CAIN T1,.GSSMB		;Opening in interactive mode?
	 TXO IOS,PIINTF		;Yes, make sure flag set

;Determine buffer and byte sizes.
	LOAD T1,PIPWSZ,(PIP)	;Get word size of buffers
	MOVEI T2,DEFWSZ		;Get default buffer size
	SKIPN T1		;If buffer size not specified
	 STOR T2,PIPWSZ,(PIP)	;Then use the default
	LDB T1,PBYTSZ		;Get OPENF% byte size
	LOAD T2,PIPBSZ,(PIP)	;Get pipe's byte size
	SKIPN T2		;Any byte size set yet?
	 STOR T1,PIPBSZ,(PIP)	;No, set the pipe's byte size
	CAME T1,T2		;OPENF% and pipe must match
	 JUMPN T2,[RETBAD(SFBSX1,<CALL ULKPIP>)] ;Return error otherwise
	MOVEI T2,^D36		;Bits per word
	IDIVI T2,(T1)		;Divide by bits/byte to get bytes/word
	LOAD T3,PIPWSZ,(PIP)	;Get words/buffer
	IMULI T2,(T3)		;Multiply by words/buffer to get bytes/buffer
	TXNE IOS,PIINTF		;Opened for interactive use?
	 SETZ T2,		;Yes, make FILCNT always be zero
	STOR T2,PIPBCT,(PIP)	;Remember for later
	IORB IOS,PIPSTS(PIP)	;Make sure byte info gets set in IOS as well
;Assign wait bit and buffers
	LOAD T1,PIPWTB,(PIP)	;Do we have a wait bit yet?
	IFE. T1
	  CALL ASNBIT		;Assign a wait bit
	   RETBAD(MONX01,<CALL ULKPIP>)	;"Insufficient system resources"
	  STOR T1,PIPWTB,(PIP)	;Store the wait bit
	ENDIF.
	XMOVEI T1,OPNRED	;Assume opening read side
	TQNN <READF>		;Well?
	XMOVEI T1,OPNWRT	;Opening write side
	CALL (T1)		;Call appropriate routine
	IFNSK.
	  IFXE. IOS,PIWRTF!PIREDF ;Failure, discard wait bit if no side is open
	    LOAD T1,PIPWTB,(PIP) ;Get our wait bit back
	    SETZRO PIPWTB,(PIP)	;Forget we ever had it
	    CALL RELBIT		;Release it
	  ENDIF.
	  RETBAD(MONX02,<CALL ULKPIP>) ;"Insufficient system resources"
	ENDIF.
	CALL ULKPIP		;Unlock pipe
	RETSKP			;Success return
;OPNRED - open read side of pipe
;Returns +1 failure, T1/ error code
;	 +2 success

OPNRED:	LOAD T2,PIPWSZ,(PIP)	;Get size of pipe buffer
	ADDI T2,1		;Count storage header
	CALL ASGJFR		;Get some JSB free space
	 RET			;None, take failure return
	STOR T1,PIPINP,(PIP)	;Save pointer
	CALL SETBYT		;Set up FILBYT byte pointer
	SETZM FILBYN(JFN)	;Current byte number is zero
	SETZM FILCNT(JFN)	;No bytes in current buffer
	TXO IOS,PIREDF		;Set "pipe open for read" flag
	TXZ IOS,PIRCLF		;Clear "closed after open for read" flag
	RETSKP			;Success return

;OPNWRT - open write side of pipe
;Returns +1 failure, T1/ error code
;	 +2 success

OPNWRT:	LOAD T2,PIPWSZ,(PIP)	;Get size of pipe buffer
	ADDI T2,1		;Count storage header
	CALL ASGJFR		;Get some JSB free space
	 RET			;None, take failure return
	STOR T1,PIPQED,(PIP)	;Save pointer to queued buffer
	LOAD T2,PIPWSZ,(PIP)	;Get size of pipe buffer
	ADDI T2,1		;Count storage header
	CALL ASGJFR		;Get some JSB free space
	IFNSK.
	  LOAD T2,PIPQED,(PIP)	;Can't.  Get back pointer to queued buffer
	  MOVEI T1,JSBFRE	;Return it to JSB free pool
	  CALL RELFRE		; ...
	  SETZRO PIPQED,(PIP)	;Say we have no queued pipe
	  RET			;Take failure return
	ENDIF.
	STOR T1,PIPOUT,(PIP)	;Save pointer to output buffer
	CALL SETBYT		;Set up FILBYT byte pointer
	LOAD T1,PIPBCT,(PIP)	;Get bytes/buffer
	MOVEM T1,FILCNT(JFN)	;Set buffer byte count
	SETZM FILBYN(JFN)	;Current byte number is zero
	TXO IOS,PIWRTF		;Set "open for write" flag
	TXZ IOS,PIWCLF		;Clear "closed after open for write" flag
	RETSKP			;Success return
Subttl CLOSF% and RLJFN% Routines

;PIPCLZ - device dependent CLOSF% code
;Returns +1 error, BLKF set if need to block, or T1/ error code 
;	 +2 success, one side of pipe device closed

PIPCLZ:	CALL SAVPIP		;Co-routine to preserve PIP and IOS
	LOAD PIP,FILPIP,(JFN)	;Get pipe index
	JUMPE PIP,RSKP		;Success if no pipe
	CALL LCKPIP		;Lock out use of the pipe
	UMOVE T1,1		;Get flags from caller
	TXNN T1,CZ%ABT		;Is this an abort?
	 TQNE <READF>		;Or closing the read side?
	  JRST PIPCL0		;Yes to either, go flush this side of the pipe
	TXNN IOS,PIREDF		;Is read end already closed?
	 JRST PIPCL0		;Yes, don't bother forcing output
	CALL DMPPIP		;Force output side of pipe
	 JRST PIPCL1		;Must block until write side empties
PIPCL0:	TQNN <READF>		;Open for read?
	IFSKP.
	  CALL CLRQDF		;Invalidate any buffered data
	  LOAD T2,PIPINP,(PIP)	;Get pointer to input buffer 
	  SETZRO PIPINP,(PIP)	;Zero it
	  TXZ IOS,PIREDF	;Clear "open for read"
	  TXO IOS,PIRCLF	;Set "closed after open for read"
	ELSE.
	  LOAD T2,PIPOUT,(PIP)	;Writing, get pointer to output buffer 
	  SETZRO PIPOUT,(PIP)	;Zero it
	  TXZ IOS,PIWRTF	;Clear "open for write"
	  TXO IOS,PIWCLF	;Set "closed after open for write"
	ENDIF.
 	MOVEI T1,JSBFRE		;Address of free storage header
	CALL RELFRE		;Release buffer
	TXNE IOS,PIWRTF!PIREDF	;Open at any end?
	IFSKP.
	  LOAD T2,PIPQED,(PIP)	;No, flushed queued buffer
	  SETZRO PIPQED,(PIP)	; ...
	  MOVEI T1,JSBFRE	;Belongs to JSB free space
	  SKIPE T2		;Do nothing if never assigned that buffer
	   CALL RELFRE		;Release last buffer
	  LOAD T1,PIPWTB,(PIP)	;Get back our wait bit
	  CALL RELBIT		;Release it
	  SETZRO PIPWTB,(PIP)	;Say wait bit flushed
	ENDIF.
	CALL ULKPIP		;Release pipe lock 
	RETSKP			;Success return
;Here when we can't dump the write side of the pipe. We block until the 
; PIQEDF flag is reset for this pipe.

PIPCL1:	CALL ULKPIP		;Unlock the pipe 
	SETONE <BLKF>		;We need to block
	LOAD T1,PIPWTB,(PIP)	;Get our wait bit
	HRLI T1,WTBBZT		;Block until PIQEDF is reset
	MOVSS T1		;Canonicalize scheduler test
	RET			;Take failure return
;PIPRJF - device dependent RLJFN% code
;Returns +1 error
;	 +2 success, pipe assignment released

PIPRJF:	CALL SAVPIP		;Preserve our special AC's
	LOAD PIP,FILPIP,(JFN)	;Get pipe number
	JUMPE PIP,RSKP		;No pipe, do nothing
	CALL LCKPIP		;Lock the pipe
	LOAD T1,PIPCNT,(PIP)	;Get our share count
	SUBI T1,2		;GTJFN% normally double increments the count
	SKIPGE T1		;Count is good if non-negative 
	 SETZ T1,		;Negative count means GTJFN% failure
	STOR T1,PIPCNT,(PIP)	;Update pipe data structure
	IFN. T1
	  CALL ULKPIP		;Still owners.  Unlock pipe
	  RETSKP		;Good return
	ENDIF.
	LOAD T1,PIPINP,(PIP)	;Get pointer to input buffer 
	JUMPN T1,PIPRJ0
	LOAD T1,PIPOUT,(PIP)	;Get pointer to output buffer
	JUMPN T1,PIPRJ0
	LOAD T1,PIPQED,(PIP)	;Get pointer to queued buffer
	JUMPN T1,PIPRJ0
	TXNE IOS,PIREDF!PIWRTF!PIQEDF	;Any read/write bits lit?
	 JRST PIPRJ0
	LOAD T1,PIPWTB,(PIP)	;Get wait bit
	JUMPN T1,PIPRJ0
	SETZM PIPSTS(PIP)	;Flush pipe assignment (don't call ULKPIP)
	RETSKP			;Return to caller

PIPRJ0:	BUG.(INF,PIPRLX,PIPE,SOFT,<Pipe not properly closed>,<<PIP,INDEX>>)
	CALLRET ULKPIP		;Unlock pipe and take error return
SUBTTL Sequential I/O

COMMENT	|

The sequential I/O code uses three buffers to pass data from the write end of
a pipe to the read end.  The buffers are allocated at OPENF% time; one is
associated with each end and the third is referred to in the code as the
"queued" buffer.  The third buffer is present to avoid complicated, if not
impossible, messing around with the data structures of the two pipe JFN's.

If both ends of the pipe are opened in mode 1 (.GSSMB), no data buffering is
done, i.e., the output buffer takes only one byte.  This mode allows for
interactive applications.

When the write side fills a buffer, it checks if the queued buffer is
available, i.e., if the PIQEDF flag is reset, indicating that that buffer
contains no data for the read side.  If the queued buffer is not available and
the read side is open, the caller blocks waiting for PIQEDF to be reset.
Otherwise, we swap the output and queued buffers, fix up flags and counts, and
continue to do output.  If the queued buffer is unavailable and the read side
is closed after being open, we assume the pipe is broken and generate an
error.  If the read side has never been open, we block instead, hoping it will
eventually be opened.

When the read side needs input, it checks if PIQEDF is set, indicating that
the queued buffer contains valid data.  If valid data is present, the input
and queued buffers are swapped and various flags and counts are fixed up.  If
there is no further data and the write end has been closed normally after
being open, we set the EOF flag.  If the write side has never been opened, we
block waiting for it to open.

|
;PIPSQO - pipe sequential output
;Returns +1 always

PIPSQO:	TXNN STS,.GSSMB		;Interactive pipe?
	IFSKP.
	  IDPB T1,FILBYT(JFN)	;Yes, deposit byte
	  AOS FILBYN(JFN)	;Count the byte
	  CALL PIPREC		;Force pipe now
	   RET			;Need to block or error, return to caller
	  RET			;Data transfered, return to caller
	ENDIF.
	SOSL FILCNT(JFN)	;Buffered pipe, decrement and test byte count
	IFSKP.
	  PUSH P,T1		;Buffer full, save byte on stack
	  SETZM FILCNT(JFN)	;Fixup value of FILCNT
	  CALL PIPREC		;Give buffer to read side (SOUTR%)
	  IFNSK.
	    ADJSP P,-1		;No room or error, trim stack
	    RET			;Return to caller to block or err
	  ENDIF.
	  POP P,T1		;Get back byte
	  JRST PIPSQO		;Try again
	ENDIF.
	IDPB T1,FILBYT(JFN)	;Store byte
	AOS FILBYN(JFN)		;Advance byte number
	RET    			;Return
;PIPREC - force a buffer - SOUTR% support
;Returns +1 failure, T1/ scheduler test or ERRF set
;	 +2 success, write side of pipe emptied

PIPREC:	CALL SAVPIP		;Preserve our favorite AC's
	LOAD PIP,FILPIP,(JFN)	;Get pipe index
	CALL LCKPIP		;Lock the pipe
	CALL DMPPIP		;Force output now
	IFNSK.
	  TXNN IOS,PIRCLF	;No go, is read side closed after being open?
	  IFSKP.
	    SETONE <ERRF>	;Yes, say error encountered
	  ELSE.
	    SETONE <BLKF>	;We need to block
	    LOAD T1,PIPWTB,(PIP) ;Get our wait bit
	    HRLI T1,WTBBZT	;Block until PIQEDF is reset
	    MOVSS T1		;Canonicalize scheduler test
	  ENDIF.
	  JRST ULKPIP		;Unlock pipe and take single return to caller 
	ENDIF.
	CALL ULKPIP		;Write side empty, unlock the pipe
	RETSKP			;Skip return to caller
;DMPPIP - dump the write side of a pipe
;Takes	PIP/ pipe index
;	IOS/ pipe status bits
;Returns +1 failure, unable to dump write side
;	 +2 success, write side of pipe is empty

DMPPIP:	TXNE IOS,PIINTF		;Buffered mode?
	IFSKP.
	  MOVE T1,FILCNT(JFN)	;Yes, get bytes remaining in buffer
	  LOAD T2,PIPBCT,(PIP)	;Get max bytes/buffer
	  SUBI T2,(T1)		;Calculate bytes in this buffer
	  JUMPE T2,RSKP		;Buffer is empty, return now
	ELSE.
	  MOVEI T2,1		;Always one byte if interactive
	ENDIF.
	TXNE IOS,PIQEDF		;Is queued buffer available?
	 RET			;No, caller must decide to block or err
	STOR T2,PIPQCT,(PIP)	;Store count of bytes in queued buffer
	LOAD T1,PIPQED,(PIP)	;Get queued pointer
	LOAD T2,PIPOUT,(PIP)	;Get output pointer
	STOR T2,PIPQED,(PIP)	;Swap buffers
	STOR T1,PIPOUT,(PIP)	; ....
	CALL SETBYT		;Set up new FILBYT(JFN)
	LOAD T1,PIPBCT,(PIP)	;Get max bytes/buffer
	MOVEM T1,FILCNT(JFN)	;Reset FILCNT(JFN)
	CALL SETQDF		;Set queued buffer valid flag
	RETSKP			;Return to caller, output side dumped
;PIPSQI - pipe sequential input
;Returns +1 always

PIPSQI:	SOSL FILCNT(JFN)	;Decrement and test byte count
	IFSKP.
	  CALL PIPINB		;Get some more input
	   RET			;Have to block, return now
	  JRST PIPSQI		;Try again 
	ENDIF.
	ILDB T1,FILBYT(JFN)	;Bytes remain, load next into T1
	AOS FILBYN(JFN)		;Advance byte number
	RET    			;Return

;PIPINB - work routine for PIPSQI
;Returns +1 no input, return to caller to block or err
;	 +2 more input available

PIPINB:	CALL SAVPIP		;Preserve our AC's
	LOAD PIP,FILPIP,(JFN)	;Get pipe index
	CALL LCKPIP		;Lock pipe data structure
	TXNE IOS,PIQEDF		;Queued input valid?
	IFSKP.
	  TXNN IOS,PIWCLF	;No, was write side once open, now closed?
	  IFSKP.
	    SETONE <EOFF>	;Yes, we have reached EOF. Set flag.
	  ELSE.
	    SETONE <BLKF>	;We must block for input
	    LOAD T1,PIPWTB,(PIP) ;Get our wait bit
	    HRLI T1,WTBBOT	;Block until PIQEDF is set
	    MOVSS T1		;Canonicalize scheduler test
	  ENDIF.
	  JRST ULKPIP		;Unlock pipe data structures and return
	ENDIF.
	LOAD T1,PIPQED,(PIP)	;Load pointer to queued input buffer
	LOAD T2,PIPINP,(PIP)	;Load pointer to (empty) input buffer
	STOR T1,PIPINP,(PIP)	;Swap buffer pointers
	STOR T2,PIPQED,(PIP)	; ...
	CALL SETBYT		;Set new FILBYT pointer
	LOAD T1,PIPQCT,(PIP)	;Get byte count of queued buffer
	MOVEM T1,FILCNT(JFN)	;Set it in JFN database
	ADDM T1,FILLEN(JFN)	;Update file length
	CALL CLRQDF		;Clear queued input valid flag
	CALL ULKPIP		;Unlock pipe data structure 
	RETSKP			;Take success return
SUBTTL JSYS Support Routines

;PIPGTD - GDSTS% support.  Returns PIPSTS word for pipe.

PIPGTD:	LOAD T1,FILPIP,(JFN)	;Get pipe index 
	MOVE T1,PIPSTS(T1)	;Get our status bits
	RET			;Return to caller

;SETBYT - Set up a byte pointer
;Takes	PIP/ pipe index
;	JFN/ JFN on one end of pipe
;	T1/ address of buffer
;Returns +1 always, FILBYT(JFN) updated

SETBYT:	LOAD T2,PIPBSZ,(PIP)	;Get our byte size
	ROT T2,-^D12		;Get S field into place (P field is zero)
	HLL T1,T2		;Create the byte pointer
	MOVEM T1,FILBYT(JFN)	;Stash pointer
	RET			;Return to calller

;SAVPIP - AC saving co-routine
;Supports +1 and +2 returns

SAVPIP:	PUSH P,PIP		;We use PIP for the pipe index
	PUSH P,IOS		;We use IOS for pipe status bits
	CALL @-2(P)		;Call our co-routine 
	 TRNA			;Single return
	  AOS -3(P)		;Fix up skip return
	POP P,IOS		;Restore preserved AC
	POP P,PIP		; ...
	ADJSP P,-1		;Return one deeper in stack
	RET			;Return
;LCKPIP - get exclusive use lock on a pipe
;Takes	PIP/ pipe index
;Returns +1 always, IOS/ pipe status word

LCKPIP:	NOSKED			;Disallow scheduling
	MOVE IOS,PIPSTS(PIP)	;Get status bits
	TXON IOS,PILCKF		;Locked?
	IFSKP.
	  OKSKED		;Yes, someone else has it.  Reallow scheduling.
	  TRNA			;Skip into the short term block
	   JRST LCKPIP		;CBLK1 returns here
	  CBLK1			;Block for a short time
	ENDIF.
	IORB IOS,PIPSTS(PIP)	;Now have lock, update in-core copy of status
	OKSKED			;Reallow scheduling
	RET			;Return to caller

;ULKPIP - release lock on pipe
;Takes	PIP/ pipe index
;Returns +1 always, PIPSTS(PIP) updated

ULKPIP:	TXZ IOS,PILCKF		;Clear lock bit
	MOVEM IOS,PIPSTS(PIP)	;Store updated status bits
	RET			;Return to caller
Subttl Wait Bits

COMMENT	|

	Because we often need to block based on the state of non-resident data
and locks, we use a resident pool of "wait bits" for our scheduler testing.  A
process assigns a wait bit and then sets or clears that bit as appropriate to
signal other processes to unblock.  The code for the wait bit management was
taken largely from the TOPS-20 TCP code.  The code duplication was necessary
because 1.) we can't assume that everyone has TCP in their monitor, and 2.) it
is desirable to minimize the interaction of the TCP and pipe code.

|

;INIBIT - initialize pool of wait bits
;Returns +1 always

INIBIT:	MOVSI T1,-NUMWTW	;Number of words in the pool
	HRLOI T2,377777		;First word, omitting index 0
	MOVEM T2,PIPBFF(T1)	;Clear free flags
	SETO T2,		;Use all bits in succeeding words
	AOBJN T1,.-2		;Loop over entire table
	SETOM PIPWTF		;Say that pool has been initialized
	RET			;Return to caller
;ASNBIT - assign a (cleared) wait bit index.
;Returns +1 failure, no available wait bits
;	 +2 success, T1/ wait bit index

ASNBIT:	NOSKED			;Only one process at a time
	SKIPN PIPWTF		;Tables initialized?
	 CALL INIBIT		;No, do so now
	MOVSI T3,-NUMWTW	;Number of words in bit table
	SKIPE T1,PIPBFF(T3)	;Any free bits in this word?
	JFFO T1,ASNBT1		;Yes.  Get bit number
	AOBJN T3,.-2		;No.  Try next
	OKSKED			;No free buffer bits, reallow scheduling
	RET			;Return to caller

ASNBT1:	MOVE T1,BITS(T2)	;Get the corresponding bit mask
	ANDCAM T1,PIPBFF(T3)	;Make it not free
	ANDCAM T1,PIPBTB(T3)	;Zero corresponding wait bit
	OKSKED			;Reallow scheduling
	HRRZ T1,T3		;Get word offset
	IMULI T1,^D36		;Convert to bits
	ADD T1,T2		;Add bit within last word
	RETSKP			;Return to caller

;RELBIT - release a wait bit assignment.
;Takes 	T1/ wait bit index
;Returns +1 always 

RELBIT:	JUMPE T1,R		;Ignore attempts to release zero
	IDIVI T1,^D36		;Convert to word and bit
	MOVE T2,BITS(T2)	;Get corresponding bit mask
	TDNE T2,PIPBFF(T1)	;Better be in use right now.
	 BUG.(CHK,PIPWA0,PIPE,SOFT,<PIPE - bit table fouled up>)
	IORM T2,PIPBFF(T1)	;Free it
	RET
;SETBIT - set a wait bit to one state.
;Takes	T1/ wait bit index
;Returns +1 always

SETBIT:	SETZ T2,		;Make sure T2 is something harmless
	JUMPE T1,SETBT0		;Bug if trying to set bit zero
	IDIVI T1,^D36		;Compute offsets
	MOVE T2,BITS(T2)	;Get bit mask
	TDNE T2,PIPBFF(T1)	;Check that it is assigned
SETBT0:	 BUG.(CHK,PIPWA1,PIPE,SOFT,<PIPE - wait bit not assigned>)
	IORM T2,PIPBTB(T1)	;Set the bit
	RET			;Return to caller

;CLRBIT - clear a wait bit to zero state.
;Takes	T1/ wait bit index
;Returns +1 always

CLRBIT:	SETZ T2,		;Make sure T2 is harmless
	JUMPE T1,CLRBT0		;Bug if trying to clear bit zero
	IDIVI T1,^D36		;Compute offsets
	MOVE T2,BITS(T2)	; ...
	TDNE T2,PIPBFF(T1)	;Make sure bit assigned 
CLRBT0:	 BUG.(CHK,PIPWA2,IPIPIP,SOFT,<PIPE - wait bit not assigned>)
	ANDCAM T2,PIPBTB(T1)	;Clear the bit 
	RET			;Return to caller
;WTBBZT - scheduler test for a wait bit clear or unassigned
;Takes	T1/ wait bit index

	RESCD

WTBBZT:	JUMPE T1,1(T4)		;Beware bit 0
	IDIVI T1,^D36		;Convert to word and bit numbers
	MOVE T2,BITS(T2)	;Get bit mask
	TDNN T2,PIPBFF(T1)	;Bit unassigned? 
	TDNN T2,PIPBTB(T1)	;Zero yet?
	 JRST 1(T4)		;Yes, time to unblock
	  JRST 0(T4)		;No

	SWAPCD


;WTBBOT - scheduler test for a wait bit set or unassigned
;Takes	T1/ wait bit index

	RESCD

WTBBOT:	JUMPE T1,1(T4)		;Ignore bit zero
	IDIVI T1,^D36		;Get word and bit numbers
	MOVE T2,BITS(T2)	;Get bit mask
	TDNN T2,PIPBFF(T1)	;Bit unassigned?
	TDNE T2,PIPBTB(T1)	;Is bit set yet?
	 JRST 1(T4)		;Yes, wakeup
	  JRST 0(T4)		;No, keep on blocking


	SWAPCD
;SETQDF - set queued buffer flag and wait bit
;Takes	PIP/ pipe index
;	IOS/ pipe status word
;Returns +1 always 

SETQDF:	TXO IOS,PIQEDF		;Set queued buffer valid flag
	LOAD T1,PIPWTB,(PIP)	;Get our wait bit
	CALLRET SETBIT		;Set it and return

;CLRQDF - clear queued buffer flag and wait bit
;Takes	PIP/ pipe index
;	IOS/ pipe status word
;Returns +1 always 

CLRQDF:	TXZ IOS,PIQEDF		;Clear queued buffer valid flag
	LOAD T1,PIPWTB,(PIP)	;Get our wait bit
	CALLRET CLRBIT		;Clear it and return
	TNXEND
	END