C		DRSLAVE
C****************************************************************************
C*									    *
C*  COPYRIGHT (c) 1978, 1980, 1982, 1984 BY				    *
C*  DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASSACHUSETTS.		    *
C*  ALL RIGHTS RESERVED.						    *
C* 									    *
C*  THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND COPIED   *
C*  ONLY IN  ACCORDANCE WITH  THE  TERMS  OF  SUCH  LICENSE  AND WITH THE   *
C*  INCLUSION OF THE ABOVE COPYRIGHT NOTICE. THIS SOFTWARE OR  ANY  OTHER   *
C*  COPIES THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY   *
C*  OTHER PERSON.  NO TITLE TO AND OWNERSHIP OF  THE  SOFTWARE IS  HEREBY   *
C*  TRANSFERRED.							    *
C* 									    *
C*  THE INFORMATION IN THIS SOFTWARE IS  SUBJECT TO CHANGE WITHOUT NOTICE   *
C*  AND  SHOULD  NOT  BE  CONSTRUED AS  A COMMITMENT BY DIGITAL EQUIPMENT   *
C*  CORPORATION.							    *
C* 									    *
C*  DIGITAL ASSUMES NO RESPONSIBILITY FOR THE USE  OR  RELIABILITY OF ITS   *
C*  SOFTWARE ON EQUIPMENT WHICH IS NOT SUPPLIED BY DIGITAL.		    *
C* 									    *
C*									    *
C****************************************************************************
C
C
C			  COPYRIGHT (c) 1978 BY
C	      DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASS.
C
C THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND  COPIED
C ONLY  IN  ACCORDANCE  WITH  THE  TERMS  OF  SUCH  LICENSE AND WITH THE
C INCLUSION OF THE ABOVE COPYRIGHT NOTICE.  THIS SOFTWARE OR  ANY  OTHER
C COPIES  THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY
C OTHER PERSON.  NO TITLE TO AND OWNERSHIP OF  THE  SOFTWARE  IS  HEREBY
C TRANSFERRED.
C
C THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE  WITHOUT  NOTICE
C AND  SHOULD  NOT  BE  CONSTRUED  AS  A COMMITMENT BY DIGITAL EQUIPMENT
C CORPORATION.
C
C DIGITAL ASSUMES NO RESPONSIBILITY FOR THE USE OR  RELIABILITY  OF  ITS
C SOFTWARE ON EQUIPMENT WHICH IS NOT SUPPLIED BY DIGITAL.
C

C++
C FACILITY:	DRCOPY -- DR32 example file transfer program
C
C ABSTRACT:
C	This set of routines constitutes the Slave portion of the
C	DRCOPY file transfer example program.
C
C ENVIRONMENT:
C	These routines run in User mode; no privileges are necessary.
C
C AUTHOR:  Trudy Matthews,	CREATION DATE: July, 1979
C
C MODIFIED BY:
C
C	, : VERSION
C 01	- 
C--

	SUBROUTINE	SFQ_STARTGET(DEVMSG)

C 
C This subroutine is called when the Slave receives a "start read
C operation" control message.  This routine opens an existing file.
C If the OPEN is successful, then send a confirmation control message
C to the Master program and initialize the buffer management variables.
C
C INPUTS:
C	A Device Message of the format:
C		31			      0
C		+-------------------------------+
C		|   type = MS_MSG_STARTGET	|
C		+-------------------------------+
C		|   number of master buffers	|
C		+-------------------------------+
C		|				|
C		//	file attribute		//
C		|	 information		|
C		+-------------------------------+
C
C IMPLICIT INPUTS:
C IMPLICIT OUTPUTS:
C	The file specified by the file attribute information is opened,
C	and an RMS read from the file into the first buffer is issued.
C

	INCLUDE	'SYS$LIBRARY:XFDEF.FOR/NOLIST'
	INCLUDE	'DRCOPY.PRM'
	
	BYTE		MBFRS(BUFSIZ, NUM_MBFRS)
	BYTE		SBFRS(BUFSIZ, NUM_SBFRS)

	INTEGER*2	PTR		!index into ring of buffers
	INTEGER*2	RMSCOUNT	!number of buffers in queue
	INTEGER*4	ADDRMSG(NUM_SBFRS+2)	!"Here are my bfr addrs"
						!device message
	INTEGER*4	CONTXT(30)	!context array
	INTEGER*4	DEVMSG(32)	!input device message
	INTEGER*4	LASTCNT		!# bytes in last buffer
	INTEGER*4	STATUS
	INTEGER*4	SYS$CLREF	!integer function
	LOGICAL*1	FLAG, SLVDONE, ENDISNEAR

	COMMON	/MS_SHARE/ CONTXT, MBFRS, SBFRS
	COMMON	/SLV/	SLVDONE
	COMMON	/SLAVE/	RMSCOUNT, PTR, FLAG
	COMMON	/SLVWRT/ LASTCNT, ENDISNEAR

	EXTERNAL	SLV_OPEN	!macro routine; does RMS OPEN
	EXTERNAL	SLV_COPYFAB	!macro routine; alters FAB
	EXTERNAL	XF$PKTBLD	!DR32 support routine
	EXTERNAL	SLV_FINISH	!called to end transfer
	EXTERNAL	SYS$CLREF, SS$_IVBUFLEN

C
C Check that master buffers and slave buffers agree in size
C
	IF (DEVMSG(2) .NE. BUFSIZ) THEN
		CALL SLV_FINISH(SM_MSG_ERROR, %LOC(SS$_IVBUFLEN))
		RETURN
	END IF

C
C Open the file
C
	CALL SLV_OPEN(DEVMSG, STATUS)	!contains FAB
	IF (.NOT. STATUS) THEN
		CALL SLV_FINISH(SM_MSG_ERROR, STATUS)
		RETURN
	END IF

C
C Send a packet notifying Master of successful open
C
	ADDRMSG(1) = SM_MSG_BFRADRS
	ADDRMSG(2) = NUM_SBFRS
	DO 10	I = 1, NUM_SBFRS		!build device msg that conveys
		ADDRMSG(I+2) = %LOC(SBFRS(1,I))	!buffer addresses
10	CONTINUE

	CALL XF$PKTBLD 
	1      (CONTXT,
	1	XF$K_PKT_WRTCM,		!write control message function
	1	,,			!default index & difsize
	1	ADDRMSG,		!send addresses of buffers
	1	(NUM_SBFRS + 2) * 4,	!size of ADDRMSG in bytes
	1	,			!no logmsg
	1	256,			!modes = insert pkt at head of q
	1	,,			!no action, actparm
	1	STATUS)

	IF (.NOT. STATUS) THEN
		CALL SLV_FINISH(SM_MSG_ERROR, STATUS)
		RETURN
	END IF

C
C Send a control message to Master containing File Attributes
C
	DEVMSG(1) = SM_MSG_FAB
	CALL SLV_COPYFAB(DEVMSG)	!put attributes in same devmsg
	CALL XF$PKTBLD
	1      (CONTXT,
	1	XF$K_PKT_WRTCM,		!write control message function
	1	,,			!no index, difsize
	1	DEVMSG,			!send file attributes
	1	128,			!size of device message
	1	,			!no log message
	1	256,			!modes = insert packet at head
	1	,,			!no action, actparm
	1	STATUS)

	IF (.NOT. STATUS) THEN
		CALL SLV_FINISH(SM_MSG_ERROR, STATUS)
		RETURN
	END IF

C
C Initialize the buffer management variables
C
	FLAG = GET
	RMSCOUNT = NUM_SBFRS			!# empty bfrs available for 
					!slave to fill
	PTR = 1				!index of next bfr to fill

	STATUS = SYS$CLREF(%VAL(SLVEF))	!clear slave event flag

	SLVDONE = .FALSE.
	ENDISNEAR = .FALSE.

	RETURN
	END

	SUBROUTINE	SFQ_GOGET
C
C This routine is called during a GET operation when the Master routine
C signals that his initialization is complete and he is ready to accept
C buffers of data.
C

	INCLUDE	'DRCOPY.PRM/NOLIST'

	BYTE		MBFRS(BUFSIZ, NUM_MBFRS)
	BYTE		SBFRS(BUFSIZ, NUM_SBFRS)

	INTEGER*2	PTR
	INTEGER*2	RMSCOUNT
	INTEGER*4	CONTXT(30)
	INTEGER*4	STATUS

	LOGICAL*1	FLAG

	EXTERNAL	SLV_CHKRMS	!RMS completion routine

	COMMON	/MS_SHARE/	CONTXT, MBFRS, SBFRS
	COMMON	/SLAVE/		RMSCOUNT, PTR, FLAG

C
C Issue READ to get things going
C
	CALL SLV_READ(SBFRS(1,PTR),BUFSIZ,SLV_CHKRMS,SLV_CHKRMS,STATUS)
	IF (.NOT. STATUS) CALL SLV_FINISH(SM_MSG_ERROR, STATUS)

	RETURN
	END

	SUBROUTINE	SFQ_STARTPUT(DEVMSG)
C
C This subroutine is called when the Slave receives a "start write
C operation" control message.  This routine must create a file.
C If the file is successfully created, then send a confirmation
C message to Master, initialize the buffer management variables, and
C return.  The process waits for a control message from Master to
C activate.

C
C INPUTS:
C	Device message from Master of the format:
C		31			      0
C		+-------------------------------+
C		|   type = MS_MSG_STARTPUT	|
C		+-------------------------------+
C		|   size of master buffers	|
C		+-------------------------------+
C		|				|
C		//     file attribute		//
C		|	information		|
C		+-------------------------------+
C
C IMPLICIT INPUTS:
C IMPLICIT OUTPUTS:
C	The file specified by the device message is created.
C
	INCLUDE	'SYS$LIBRARY:XFDEF.FOR/NOLIST'
	INCLUDE	'DRCOPY.PRM/NOLIST'

	BYTE		MBFRS(BUFSIZ, NUM_MBFRS)
	BYTE		SBFRS(BUFSIZ, NUM_SBFRS)

	INTEGER*2	RMSCOUNT, PTR
	INTEGER*4	CONTXT(30)
	INTEGER*4	ADDRMSG(NUM_SBFRS+2)
	INTEGER*4	DEVMSG(32)	!input device message
	INTEGER*4	LASTCNT		!# bytes in last buffer
	INTEGER*4	STATUS
	INTEGER*4	SYS$CLREF	!integer function
	LOGICAL*1	FLAG, SLVDONE, ENDISNEAR
	
	COMMON	/MS_SHARE/ CONTXT, MBFRS, SBFRS
	COMMON	/SLV/	SLVDONE
	COMMON	/SLAVE/	RMSCOUNT, PTR, FLAG
	COMMON	/SLVWRT/ LASTCNT, ENDISNEAR

	EXTERNAL	SLV_CREATE	!macro routine; does RMS CREATE
	EXTERNAL	XF$PKTBLD	!DR32 support routine
	EXTERNAL	SLV_FINISH	!called to end transfer
	EXTERNAL	SYS$CLREF, SS$_IVBUFLEN

C
C Check that the sizes of Master and Slave buffers agree
C
	IF (DEVMSG(2) .NE. BUFSIZ) THEN
		CALL SLV_FINISH (SM_MSG_ERROR, %LOC(SS$_IVBUFLEN))
		RETURN
	END IF

C
C Create the file
C
	CALL SLV_CREATE (DEVMSG, STATUS)
	IF (.NOT. STATUS) THEN
		CALL SLV_FINISH(SM_MSG_ERROR, STATUS)
		RETURN
	END IF

C
C Send a packet notifying Master of a successful open
C
	ADDRMSG(1) = SM_MSG_BFRADRS
	ADDRMSG(2) = NUM_SBFRS
	DO 10	I = 1, NUM_SBFRS	!for all buffers do
		ADDRMSG(I+2) = %LOC(SBFRS(1,I))	!get address
10	CONTINUE

	CALL XF$PKTBLD
	1      (CONTXT,
	1	XF$K_PKT_WRTCM,		!write command message function
	1	,,			!no index,difsiz
	1	ADDRMSG,		!send addresses of buffers
	1	(NUM_SBFRS+2)*4,	!size of ADDRMSG in bytes
	1	,			!no log message
	1	256,			!modes = insert packet at head
	1	,,			!no action, actparm
	1	STATUS)

	IF (.NOT. STATUS) THEN
		CALL SLV_FINISH(SM_MSG_ERROR, STATUS)
		RETURN
	END IF

C
C Initialize the buffer management varaibles
C
	STATUS = SYS$CLREF (%VAL(SLVEF))
	IF (.NOT. STATUS) THEN
		CALL SLV_FINISH(SM_MSG_ERROR, STATUS)
		RETURN
	END IF
	SLVDONE = .FALSE.		!transfer is not complete
	ENDISNEAR = .FALSE.		!set when Master sends
					!"end of transfer" cntrl msg
	FLAG = PUT
	RMSCOUNT = 0			!# bfrs available for slave
					!to empty
	PTR = 1				!next bfr to empty

	RETURN
	END

	SUBROUTINE	SFQ_PNXTBFR(DEVMSG)
C
C This routine is called when the Slave receives a device message
C "process your next buffer"
C
C
C INPUTS:
C	Device Message; contains message type MS_MSG_PNXTBFR.
C
C IMPLICIT INPUTS
C IMPLICIT OUTPUTS:
C	Buffer management data updated to reflect the fact that the
C	Master program has completed a DR-transfer of another Slave
C	buffer, and now the buffer is available to the RMS process.
C

	INCLUDE	'DRCOPY.PRM/NOLIST'

	BYTE		MBFRS(BUFSIZ, NUM_MBFRS)
	BYTE		SBFRS(BUFSIZ, NUM_SBFRS)

	INTEGER*2	RMSCOUNT	!if GET, # bfrs for slave to fil
					!if PUT, # bfrs to empty
	INTEGER*2	PTR		!PTR = current buffer
	INTEGER*4	DEVMSG(32)	!not used
	INTEGER*4	CONTXT(30)
	INTEGER*4	STATUS
	LOGICAL*1	FLAG, SLVDONE

	COMMON	/MS_SHARE/ CONTXT, MBFRS, SBFRS
	COMMON /SLV/	SLVDONE
	COMMON	/SLAVE/	RMSCOUNT, PTR, FLAG

	EXTERNAL	SLV_READ	!macro routine; does RMS READ
	EXTERNAL	SLV_WRITE	!macro routine; does RMS WRITE
	EXTERNAL	SLV_CHKRMS	!RMS success completion routine;
					!checks for end of file on read
	EXTERNAL	SLV_RMSERR	!RMS error completion routine
	EXTERNAL	SLV_BUFDONE	!RMS completion routine
	EXTERNAL	SLV_FINISH	!called to end transfer

C
C If transfer has prematurely aborted, simply return
C
	IF (SLVDONE) RETURN

C
C Else record the fact that there exists another buffer for RMS to
C operate on
C
	RMSCOUNT = RMSCOUNT + 1		!another bfr to fill/empty
	IF (RMSCOUNT .EQ. 1) THEN		!start or restart RMS
	    IF (FLAG .EQ. PUT) THEN
		CALL SLV_WRITE (SBFRS(1, PTR), BUFSIZ,
	1			SLV_BUFDONE, SLV_RMSERR, STATUS)
		IF (.NOT. STATUS) CALL SLV_FINISH (SM_MSG_ERROR, STATUS)
	    ELSE
		CALL SLV_READ (SBFRS (1, PTR), BUFSIZ, 
	1			SLV_CHKRMS, SLV_CHKRMS, STATUS)
		IF (.NOT. STATUS) CALL SLV_FINISH (SM_MSG_ERROR, STATUS)
	    END IF
	END IF

	RETURN
	END

	SUBROUTINE	SLV_BUFDONE
C
C This routine is called after RMS has completed a read/write operation
C to/from disk, making another slave buffer available to the Master
C process.  This routine must send a control message to the Master
C informing him the next buffer is available, then issue another RMS
C read/write.
C
C
C IMPLICIT INPUTS:
C	RMS process has completed the transfer of a buffer.
C
C IMPLICIT OUTPUTS:
C	Buffer management updated: another buffer available to
C	DR-transfer process.
C	If possible, start RMS processing next buffer in RMS queue.
C
	INCLUDE	'SYS$LIBRARY:XFDEF.FOR/NOLIST'
	INCLUDE	'DRCOPY.PRM/NOLIST'

	BYTE		MBFRS(BUFSIZ, NUM_MBFRS)
	BYTE		SBFRS(BUFSIZ, NUM_SBFRS)

	INTEGER*2	RMSCOUNT
	INTEGER*2	PTR
	INTEGER*4	CONTXT(30)
	INTEGER*4	LASTCNT
	INTEGER*4 	STATUS
	LOGICAL*1	FLAG, SLVDONE, ENDISNEAR


	COMMON	/MS_SHARE/ CONTXT, MBFRS, SBFRS
	COMMON	/SLV/	SLVDONE
	COMMON	/SLAVE/	RMSCOUNT, PTR, FLAG
	COMMON	/SLVWRT/ LASTCNT, ENDISNEAR

	EXTERNAL	SLV_READ	!;macro routine; does RMS READ
	EXTERNAL	SLV_WRITE	!macro routine; does RMS WRITE
	EXTERNAL	SLV_CHKRMS	!RMS success completion routine;
					!checks for end of file on read
	EXTERNAL	SLV_RMSERR	!RMS error completion routine
	EXTERNAL	XF$PKTBLD	!DR32 support routine
	EXTERNAL	CALL_BUFDONE	!RMS success completion routine
	EXTERNAL	SLV_FINISH	!called to end transfer
	EXTERNAL	SLV_NORMAL	!RMS completion routine;
					!successful end of PUT operation
	EXTERNAL	SS$_NORMAL

C
C If transfer has prematurely aborted, simply return
C
	IF (SLVDONE) RETURN


C
C Send control message "process my next buffer"
C Insert this command packet at the head of the input queue
C
	CALL XF$PKTBLD
	1      (CONTXT,
	1	XF$K_PKT_WRTCM,		!write control message function
	1	,,			!no index,difsize
	1	SM_MSG_PNXTBFR,		!slave "next buffer" devmsg
	1	2,			!size of device message
	1	,			!no logmsg
	1	256,			!modes = insert pkt at head
	1	,,			!no action, actparm
	1	STATUS)

	IF (.NOT. STATUS) THEN
		CALL SLV_FINISH(SM_MSG_ERROR, STATUS)
		RETURN
	END IF

	PTR = PTR + 1			!step to next buffer
	IF (PTR .GT. NUM_SBFRS) PTR = 1	!increment mod(NUM_SBFRS)
	RMSCOUNT = RMSCOUNT - 1		!slave finished a buffer
	
C
C The ENDISNEAR flag is set only during a PUT operation, when the 
C device message "this is the last buffer" is received.  If the last
C buffer has been received and is also the only buffer left in the
C queue (RMSCOUNT = 1), then issue the last write.

	IF (ENDISNEAR .AND. RMSCOUNT .EQ. 1) THEN	!this is last buffer
		IF (LASTCNT .GT. 0) THEN
			CALL SLV_WRITE (SBFRS(1,PTR), LASTCNT,
	1			SLV_NORMAL, SLV_RMSERR, STATUS)
			IF (.NOT. STATUS)
	1			CALL SLV_FINISH (SM_MSG_ERROR, STATUS)
		ELSE		!0 bytes in last buffer transferred
			CALL SLV_FINISH(SM_MSG_PLSTBFR, 1)
		END IF

	ELSE					!this is not last buffer
		IF (RMSCOUNT .NE. 0) THEN	
			IF (FLAG .EQ. PUT) THEN
				CALL SLV_WRITE (SBFRS(1,PTR), BUFSIZ,
	1				CALL_BUFDONE,SLV_RMSERR,STATUS)
				IF (.NOT. STATUS) CALL SLV_FINISH
	1					(SM_MSG_ERROR, STATUS)
			ELSE
				CALL SLV_READ (SBFRS(1,PTR), BUFSIZ,
	1				SLV_CHKRMS,SLV_CHKRMS,STATUS)
				IF (.NOT. STATUS) CALL SLV_FINISH
	1					(SM_MSG_ERROR, STATUS)
			END IF
		END IF
	END IF
		

	RETURN
	END

	SUBROUTINE	CALL_BUFDONE
C
C This subroutine exists because
C	(1) SLV_BUFDONE must specify itself as its success
C		RMS completion routine, and
C	(2) FORTRAN subroutines may not reference themselves.
C So SLV_BUFDONE specifies this routine as its completion routine.
C

	CALL SLV_BUFDONE
	RETURN
	END

	SUBROUTINE	SLV_CHKRMS

	INCLUDE	'DRCOPY.PRM/NOLIST'

	PARAMETER	RMS$_NORMAL = '10001'X
	PARAMETER	RMS$_EOF = '1827A'X

	INTEGER*4	XFRCNT
	INTEGER*4	RMSTAT
	INTEGER*4	GETBYTCNT
	INTEGER*4	GETRMSTAT


C
C SLV_CHKRMS is only called during GET operations.  It is called to
C determine if end-of-file was encountered during the read by
C comparing the requested transfer byte count (BUFSIZ) to the actual
C transfer count (returned by function subroutine GETBYTCNT).
C

	RMSTAT = GETRMSTAT()		!returns RMS completion status
	XFRCNT = GETBYTCNT()		!returns # of bytes transferred

	IF (RMSTAT .EQ. RMS$_NORMAL) THEN
		IF (XFRCNT .EQ. BUFSIZ) THEN	!not finished; read more
			CALL SLV_BUFDONE
			RETURN
		END IF

	ELSE IF (RMSTAT .NE. RMS$_EOF) THEN	!error

		CALL SLV_FINISH (SM_MSG_ERROR, RMSTAT)
		RETURN

	END IF

C
C Only get here if end-of-file was found, either by reading less than
C a full buffer of data or by receiving RMS$_EOF status code.
C Notify far-end that we just read the last buffer.
C

	CALL SLV_LASTRD (XFRCNT)

	RETURN
	END

	SUBROUTINE	SFQ_PLSTBFR(DEVMSG)
C
C This subroutine is called when "last buffer" control message
C is received from Master.  (Only during a PUT operation)
C

	INCLUDE	'SYS$LIBRARY:XFDEF.FOR/NOLIST'
	INCLUDE	'DRCOPY.PRM/NOLIST'

	BYTE		MBFRS(BUFSIZ, NUM_MBFRS)
	BYTE		SBFRS(BUFSIZ, NUM_SBFRS)

	INTEGER*2	RMSCOUNT
	INTEGER*2	PTR
	INTEGER*4	CONTXT(30)
	INTEGER*4	DEVMSG(32)		!input device message
	INTEGER*4	LASTCNT			!holds last byte count
	INTEGER*4	STATUS
	LOGICAL*1	FLAG, SLVDONE, ENDISNEAR

	COMMON	/MS_SHARE/ CONTXT, MBFRS, SBFRS
	COMMON	/SLV/	SLVDONE
	COMMON	/SLAVE/	RMSCOUNT, PTR, FLAG
	COMMON	/SLVWRT/ LASTCNT, ENDISNEAR

	EXTERNAL	XF$PKTBLD,SLV_CLOSE, SLV_RMSERR, SLV_FINISH
	EXTERNAL	SLV_NORMAL

C
C If transfer has prematurely aborted, simply return
C
	IF (SLVDONE) RETURN

C
C Since this is a PUT operation, the "last buffer" control message simply
C means that the last buffer to be written to disk has arrived and is
C on the end of the queue of buffers waiting for the RMS routine to
C write them to disk.  If this buffer is the only one left in the queue 
C (i.e. if RMSCOUNT = 1) then call SLV_WRITE to write the last buffer
C to disk.  If there are other buffers to be written before this
C one, simply return -- the ENDISNEAR flag signals SLV_BUFDONE to
C notice when it is about to write out the last buffer, and it will
C finish up the transfer instead.
C

	LASTCNT = DEVMSG(2)	!save last byte count
	ENDISNEAR = .TRUE.	!signal last buffer in
	RMSCOUNT = RMSCOUNT + 1	!last buffer
	IF (RMSCOUNT .EQ. 1) THEN
		CALL SLV_WRITE (SBFRS(1,PTR), LASTCNT, 
	1		SLV_NORMAL, SLV_RMSERR, STATUS)
		IF (.NOT. STATUS) CALL SLV_FINISH (SM_MSG_ERROR, STATUS)
	END IF

	RETURN
	END

	SUBROUTINE	SLV_LASTRD (XFRCNT)
C
C This subroutine is called when EOF is detected while reading from
C disk.  SBFRS(PTR) is the last buffer filled; XFRCNT is the number of 
C bytes of good data it contains.
C Send "process my next(last) buffer" control msg to Master;
C Close the file;
C Set "slave tranfer complete" flag (SLVDONE)
C

	INCLUDE	'SYS$LIBRARY:XFDEF.FOR/NOLIST'
	INCLUDE	'DRCOPY.PRM/NOLIST'

	BYTE		MBFRS(BUFSIZ, NUM_MBFRS)
	BYTE		SBFRS(BUFSIZ, NUM_SBFRS)

	INTEGER*2	RMSCOUNT
	INTEGER*2	PTR
	INTEGER*4	CONTXT(30)
	INTEGER*4	ENDMSG(2)	!"process last bfr; bufsiz" msg
	INTEGER*4	XFRCNT		!bytes of data in last buffer
	INTEGER*4	STATUS
	LOGICAL*1	FLAG, SLVDONE

	COMMON	/MS_SHARE/ CONTXT, MBFRS, SBFRS
	COMMON	/SLV/	SLVDONE
	COMMON	/SLAVE/	RMSCOUNT, PTR, FLAG

	EXTERNAL	XF$PKTBLD,SLV_CLOSE

C
C If transfer has prematurely aborted, simply return
C
	IF (SLVDONE) RETURN


	ENDMSG(1) = SM_MSG_PLSTBFR		!"last buffer"
	ENDMSG(2) = XFRCNT		!# of bytes of good data

	CALL	XF$PKTBLD
	1      (CONTXT,
	1	XF$K_PKT_WRTCM,		!write control message function
	1	,,			!no index, difsize
	1	ENDMSG,
	1	8,			!size in bytes of ENDMSG
	1	,			!no log message
	1	,			!modes = insert packet at tail
	1				!(last buffer must be in order)
	1	,,			!no action, actparm
	1	STATUS)

	CALL SLV_SHUTDOWN

	RETURN
	END

	SUBROUTINE	SLV_NORMAL
C
C This routine is called when RMS completes the transfer of the last
C slave buffer to disk during a PUT operation.
C


C
C Finish up, sending success code (=1) to Master
C
	INCLUDE	'DRCOPY.PRM/NOLIST'

	CALL SLV_FINISH (SM_MSG_PLSTBFR, 1)

	RETURN
	END

	SUBROUTINE	SLV_FINISH (MSGCODE, MSG)
C
C This routine is called to send a status message to Master and then
C halt and clean up the slave transfer.
C


	INTEGER*4	MSGCODE
	INTEGER*4	MSG

	EXTERNAL	SLV_SENDSTAT, SLV_SHUTDOWN

	CALL SLV_SENDSTAT (MSGCODE, MSG)	!send status to Master

	CALL SLV_SHUTDOWN			!end of slave transfer

	RETURN
	END

	SUBROUTINE	SLV_SENDSTAT (MSGCODE, MSG)
C
C This routine provides a centralized routine to call to send status 
C packets to  Master routine.
C

	INCLUDE	'DRCOPY.PRM/NOLIST'
	INCLUDE	'SYS$LIBRARY:XFDEF.FOR/NOLIST'

	BYTE	MBFRS(BUFSIZ, NUM_MBFRS)
	BYTE	SBFRS(BUFSIZ, NUM_SBFRS)

	INTEGER*2	MSGCODE
	INTEGER*4	MSG
	INTEGER*4	CONTXT(30)
	INTEGER*4	DEVMSG(2)

	COMMON	/MS_SHARE/ CONTXT, MBFRS, SBFRS

	EXTERNAL	XF$PKTBLD

	DEVMSG(1) = MSGCODE
	DEVMSG(2) = MSG

	CALL XF$PKTBLD (
	1	CONTXT,
	1	XF$K_PKT_WRTCM,		!write control message function
	1	,,			!no index, size
	1	DEVMSG,			!device message
	1	8,,			!size in bytes of devmsg
	1	256)			!modes = insert at head
C
C If this fails, there is no way to signal Master
C
	RETURN
	END

	SUBROUTINE	SLV_SHUTDOWN
C
C End of transfer
C
	INCLUDE	'DRCOPY.PRM/NOLIST'

	LOGICAL*1	SLVDONE

	COMMON	/SLV/	SLVDONE

	CALL SLV_CLOSE

	SLVDONE = .TRUE.
	CALL SYS$SETEF(%VAL(SLVEF))

	RETURN
	END
