C	DRMASTER
C	Version 'V04-000'
C
C
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 FACILITY:	DRCOPY -- EXAMPLE PROGRAM FOR DR32
C
C ABSTRACT:
C	This set of routines constitutes the Master portion of the
C	DRCOPY file transfer example program.
C	For more information on the DR32 and how it is supported by
C	VAX/VMS, see Chapter 11 of the VAX/VMS I/O Users' Guide.
C
C ENVIRONMENT:	
C	These programs run in User mode; no privileges are needed.
C
C AUTHOR:  Steve Beckhardt,	CREATION DATE: July, 1979
C
C MODIFIED BY:
C
C	, : VERSION
C 01	- 
C--

C *****************************************************************************
C	TO RUN DRCOPY:
C *****************************************************************************
C
C	DRCOPY requires two CPUs and two DR32s; the DR32s form the
C communications path between the two CPUs.
C	To run DRCOPY, type the following commands on BOTH CPUs:
C
C	$ SET DEFAULT SYS$SYSDISK:[SYSHELP.EXAMPLES]
C	$ @DRCOPYBLD	! if necessary, to create image file.
C	$ RUN DRCOPY
C
C	A prompt, "DRCOPY>", should appear.  To get a description of the valid
C DRCOPY file transfer commands, type "HELP" in response to the prompt.
C	In order to use DRCOPY, both CPUs must be running the DRCOPY program
C (i.e. a terminal on each CPU should be waiting at the "DRCOPY>" prompt).
C
C *****************************************************************************
C	THE FOLLOWING SECTIONS ARE INCLUDED AS AN AID TO UNDERSTANDING THE
C	IMPLEMENTATION OF THE DRCOPY PROGRAM.
C *****************************************************************************
C
C	This set of routines is used to implement a CPU - to - CPU file
C transfer protocol using the DR32.  The goals are to implement the
C protocol (excluding the data source and data sink routines) in
C FORTRAN, using the library of high-level support routines provided
C in VMS Release 2 for the DR780.**** Please read Chapter 11 of the VAX/VMS
C I/O User's Guide before trying to understand this material. ****
C
C
C		THE MASTER ROUTINES AND THE SLAVE ROUTINES
C
C	In DRCOPY's model of the world, there exists a Master program
C in one CPU and a Slave program in the other.  The Master always
C initiates file transfers, and the direction of the file transfers are
C defined from the Master's point of view (i.e. a 'read' or a 'get'
C operation means 'transfer a file from the Slave to the Master', while
C a 'put' or a 'write' operation transfers a file from the Master to the
C Slave).  
C	While it is convenient to think of one CPU as the "Master", and the
C other as the "Slave", in reality both images of DRCOPY contain a set of
C routines that are collectively called the Master routines and a set of
C routines called the Slave routines.  During discussions of a transfer, the
C Master CPU is the CPU currently executing the Master routines; the Slave CPU
C is currently executing the Slave routines.  But since both images contain
C both sets of routines, either CPU can potentially be the Master or the Slave;
C in fact, both CPUs can be Master and Slave simultaneously.
C
C	DRCOPY on CPU A				DRCOPY on CPU B
C	+---------------+			+---------------+
C	|   MASTER	|---------    ----------|   MASTER	|
C	|   routines	|	   \/		|   routines	|
C	|---------------|	   /\		|---------------|
C	|   SLAVE	|	  /  \		|   SLAVE	|
C	|   routines	|<--------    --------->|   routines	|
C	+---------------+			+---------------+

C
C	The files being tranferred are assumed to be on disk, and too big
C (or too something) to be locked down into memory during the transfer, so
C they are buffered in main memory.  The 'source' side of any given transfer
C (the Master during a PUT and the Slave during a GET) is involved in two
C asynchronous processes: (1) filling its ring of buffers from disk; and
C (2) shipping filled buffers via the DR32 to the far-end CPU.
C	The receiving side is in turn: (1) obtaining buffers full of data from
C the far-end CPU and (2) emptying the buffers back out to its own disk.  The
C transfer of data between disk and memory will be called the RMS process;
C the transfer of data from one CPU's memory to the other's will be called the
C DR-transfer process.
C
C
C			THE MAIN ROUTINES
C
C
C	PARSE	 \
C	GET_TOKEN - command interface routines
C	HELP	 /
C
C	DO_PUT  -Top level Master routine for copying a file from local CPU
C		 to remote CPU.
C
C	DO_GET	-Top level Master routine for copying a file from remote CPU
C		 to local CPU.
C
C
C			THE AST ROUTINES
C
C	MRMS_AST -Master RMS AST completion routine
C	SLV_BUFDONE -Slave RMS AST completion routine
C
C	PKT_AST	-Called when a completed DR32 command packet is placed on an
C		 empty termination queue.  Call XF$GETPKT until TERMQ is empty.
C		 XF$GETPKT will call the action routine associated with each
C		 packet as it removes that packet from TERMQ.
C
C
C			THE ACTION ROUTINES
C
C	When building command packets, the Master routines specify different
C	action routines depending on the function this command packet will
C	perform. The Master also specifies a special action routine for
C	command packets that it loads on the free queue.
C
C	ACT_NOPPKT -Called when a command packet specifying a NOP function
C		completes.
C	ACT_RWPKT -Called when a read or write packet completes.
C
C	ACT_FREQUE -This is the action routine address built into packets
C		released onto the free queue; it is called after the DR32
C		stores a command/device message from the far-end device
C		into a packet from the free queue and then inserts that
C		packet onto the termination queue.

C	According to the protocol defined for DRCOPY, the first longword of all
C	device messages is a type code.  ACT_FREQUE dispatches to the routine
C	associated with each type code.  The type codes fall into two main
C	categories: those whose names begin with MS_MSG are messages from the
C	Master to the Slave;  those whose names begin with SM_MSG are messages
C	from the Slave to the Master.  For instance,  the type code
C	MS_MSG_STARTPUT is a message from the Master informing the Slave that
C	a PUT operation is to be initiated.
C
C	Slave routines are only invoked in response to device messages from the
C	Master side.  (There is one exception to this: after a message from the
C	Master starts up the Slave's RMS process, that process proceeds without
C	coordination from the Master while there are buffers available to it.)
C	The Slave routines respond to device messages from the far-end Master
C	routines, but require the local Master routines to remove the packet
C	(containing the far-end device message) from the termination queue
C	and to call the appropriate Slave routine according to the type of
C	device message.
C
C
C			SLAVE FREE QUEUE ROUTINES (SFQ_)
C
C	SFQ_STARTGET -Called when the Slave receives an MS_MSG_STARTGET
C		message;  Slave opens the file and sends its attributes
C		back to the Master.  Slave also sends the addresses of
C		its buffers.
C	SFQ_GOGET -Called when Master signals that he received file attributes,
C		opened the file, and is ready to accept data; Slave issues
C		an RMS read to get things going.
C	SFQ_STARTPUT -Called when the Slave receives an MS_MSG_STARTPUT
C		message; Slave creates the file, sends back the addresses
C		of its buffers, and waits for data from Master.
C	SFQ_PNXTBFR -Called when the Slave receives a "process your next
C		buffer" message.
C	SFQ_PLSTBFR -This message is only sent during a PUT operation; it means
C		the last buffer to be written to disk has arrived.
C
C
C			MASTER FREE QUEUE ROUTINES (MFQ_)
C
C	MFQ_BFRADS -Process list of buffer addresses sent by Slave.
C	MFQ_FILEATTR -Copy attributes of file opened by Slave.
C	MFQ_PNXTBFR -Called when Slave sends message that it has processed
C		another buffer; this means another buffer is available
C		to the Master.
C	MFQ_PLSTBFR -Called when Slave sends a message that it has processed
C		its last buffer;  if GET, read last buffer; if PUT,
C		transfer is complete - wake main level.
C	MFQ_ERROR -Called when Slave sends error message.

C
C DRMASTER -- the Master portion of the DRCOPY example program
C
	INCLUDE 'SYS$LIBRARY:XFDEF.FOR/NOLIST'	! DR32 definitions
	INCLUDE 'DRCOPY.PRM'			! Parameters

C
C	Local Variables
C
	INTEGER*4 STATUS

C
C	Common variables and areas
C
	CHARACTER*80  INPLINE			! Input line
	CHARACTER*64  LOC_FNAME			! Local file name
	CHARACTER*64  REM_FNAME			! Remote file name

	COMMON  /CHARS/  INPLINE,LOC_FNAME,REM_FNAME

	INTEGER*2  LOC_FNSIZE			! Local file name size
	INTEGER*2  REM_FNSIZE			! Remote file name size
	INTEGER*2  SPOS				! Starting token pos.
	INTEGER*2  EPOS				! Ending token pos.

	COMMON  /SIZES/  LOC_FNSIZE,REM_FNSIZE,SPOS,EPOS

	INTEGER*4 XFDATA(30)			! Context array
	BYTE MBFRS(BUFSIZ,NUM_MBFRS)		! Master buffers
	BYTE SBFRS(BUFSIZ,NUM_SBFRS)		! Slave buffers

	COMMON /MS_SHARE/  XFDATA,MBFRS,SBFRS

	INTEGER*4 IDEVMSG(32)			! Incoming device messages
	INTEGER*4 ODEVMSG(32)			! Outgoing device messages
	INTEGER*4 REM_BFRADS(25)		! Remote buffer addresses
	INTEGER*4 FILEATTR(6)			! File attributes
	INTEGER*4 CSTATUS			! Common status
	INTEGER*4 LASTBFRSIZ			! Last buffer size
	INTEGER*4 DDIDIS			! DDI disable
	INTEGER*2 MRMS_CNT			! Master RMS count
	INTEGER*2 MRMS_IDX			! Master RMS index
	INTEGER*2 QPKT_CNT			! Queue packet count
	INTEGER*2 QPKT_IDX			! Queue packet index
	INTEGER*2 REM_CNT			! Remote buffer count
	INTEGER*2 REM_IDX			! Remote buffer index
	INTEGER*2 NUMREM_BFRS			! Number of remote buffers
	LOGICAL*1 GPFLAG			! Get/put flag
	LOGICAL*1 LASTBFR			! Last buffer flag
	LOGICAL*1 EOFFLAG			! End of file flag
	LOGICAL*1 ERRFLAG			! Error flag
	LOGICAL*1 REMFLAG			! Remote error flag

	COMMON /MDATA/  IDEVMSG,ODEVMSG,REM_BFRADS,FILEATTR,CSTATUS,
	1		LASTBFRSIZ,DDIDIS,MRMS_CNT,MRMS_IDX,QPKT_CNT,
	2		QPKT_IDX,REM_CNT,REM_IDX,NUMREM_BFRS,GPFLAG,
	3		LASTBFR,EOFFLAG,ERRFLAG,REMFLAG

	INTEGER*4 SYS$CLREF
	INTEGER*4 SYS$SETEF
	INTEGER*4 SYS$WAITFR

	EXTERNAL ACT_FREQUE
	EXTERNAL ACT_NOPPKT
	EXTERNAL PKT_AST



C
C	Set event flag 2.  This is used by the slave half to indicate
C	when it is active so that we don't exit while the slave is active.
C
	STATUS = SYS$SETEF(%VAL(5))
	IF (.NOT. STATUS) CALL FATAL_ERROR(STATUS)

C
C	Start the DR32.  This involves three calls.  One to set
C	up everything, one to initialize the free queue with empty
C	packets, and one to actually start the DR32.
C
	CALL XF$SETUP(XFDATA,			! Context array
	1             MBFRS,			! Data buffers
	2             BUFSIZ,			! Data buffer size
	3             NUM_MBFRS + NUM_SBFRS,	! Number of data buffers
	4             IDEVMSG,128,		! Incoming device msg array and size
	5             ,,,			! No log area
	6             STATUS)			! Status
	IF (.NOT. STATUS) CALL FATAL_ERROR(STATUS)

	CALL XF$FREESET(XFDATA,			! Context array
	1               NUM_MBFRS + NUM_SBFRS,	! Number of Free Q packets
	2               1,			! AST if Term Q empty
	3               ACT_FREQUE,,		! Action routine, no param.
	4               STATUS)			! Status
	IF (.NOT. STATUS) CALL FATAL_ERROR(STATUS)

	CALL XF$STARTDEV(XFDATA,		! Context array
	1                'XFA0:',		! Device name
	2                PKT_AST,,,,		! AST routine
	3                DATARATE,		! Data rate
	4                STATUS)		! Status
	IF (.NOT. STATUS) CALL FATAL_ERROR(STATUS)

C
C	Enable random access mode (device initiated transfers)
C
	STATUS = SYS$CLREF(%VAL(1))		! Clear event flag
	IF (.NOT. STATUS) CALL FATAL_ERROR(STATUS)

	CALL XF$PKTBLD(XFDATA,
	1	       XF$K_PKT_SETRND,		! Set random enable
	2	       ,,,,,
	3	       64+256,			! Ins @ head, Int. if empty
	4	       ACT_NOPPKT,,		! Action routine, parm
	5	       STATUS)			! Status
	IF (.NOT. STATUS) CALL FATAL_ERROR(STATUS)
	STATUS = SYS$WAITFR(%VAL(1))		! Wait for packet
	IF (.NOT. STATUS) CALL FATAL_ERROR(STATUS)

C
C	Get the command
C
500	WRITE(6,600)				! Prompt for input
600	FORMAT(' DRCOPY> ',$)
	READ(5,650,END=8000)INPLINE		! Get input line
650	FORMAT(A80)
	CALL PARSE(GPFLAG)			! Parse it
	IF(.NOT. GPFLAG) GO TO 500		! No command, repeat
D	WRITE(6,700)GPFLAG,LOC_FNAME(1:LOC_FNSIZE),REM_FNAME(1:REM_FNSIZE)
D 700	FORMAT(1X,'GPFLAG = ',I1,',  LOCAL FILE NAME = ',A,
D	1  ',  REMOTE FILE NAME = ',A)

C
C	Do the requested operation
C
	IF (GPFLAG .EQ. 1) THEN
	    CALL DO_GET
	ELSE
	    CALL DO_PUT
	END IF
	GO TO 500

C
C	Wait until slave half finishes before exiting
C
8000	STATUS = SYS$WAITFR(%VAL(5))
	IF (.NOT. STATUS) CALL FATAL_ERROR(STATUS)

9000	END

	SUBROUTINE PARSE(GPFLAG)

C	This subroutine parses the input line into a command,
C	a local filename, and a remote filename.

C
C	Local Variables
C
	INTEGER*4 GPFLAG			! Get/Put flag

C
C	Common variables and areas
C
	CHARACTER*80  INPLINE			! Input line
	CHARACTER*64  LOC_FNAME			! Local file name
	CHARACTER*64  REM_FNAME			! Remote file name

	COMMON  /CHARS/  INPLINE,LOC_FNAME,REM_FNAME

	INTEGER*2  LOC_FNSIZE			! Local file name size
	INTEGER*2  REM_FNSIZE			! Remote file name size
	INTEGER*2  SPOS				! Starting token pos.
	INTEGER*2  EPOS				! Ending token pos.

	COMMON  /SIZES/  LOC_FNSIZE,REM_FNSIZE,SPOS,EPOS


C
C	Raise lowercase characters to uppercase
C
	DO 1000 I = 1,80
	J = ICHAR(INPLINE(I:I))			! Get next character
	IF (J.GE.'61'X .AND. J.LE.'7A'X) THEN	! If its between a and z
	    J = J - '20'X			! make it between A and Z
	    INPLINE(I:I) = CHAR(J)		! Replace it in input line
	END IF
1000	CONTINUE

C
C	Get command 
C
	SPOS = 1				! Starting position
	CALL GET_TOKEN				! Get next token
	IF (SPOS .LT. 0) GO TO 8000		! Nothing on line
	IF(INPLINE(SPOS:EPOS-1) .EQ. 'GET') THEN
	    GPFLAG = 1
	ELSE IF (INPLINE(SPOS:EPOS-1) .EQ. 'PUT') THEN
	    GPFLAG = 3
	ELSE IF (INPLINE(SPOS:EPOS-1) .EQ. 'HELP') THEN
	    CALL HELP
	    GO TO 8000
	ELSE
	    GO TO 7000				! Syntax error
	END IF
C
C	Get local filename
C
	SPOS = EPOS
	CALL GET_TOKEN
	IF (SPOS .LT. 0) GO TO 7000		! Syntax error
	LOC_FNAME = INPLINE(SPOS:EPOS-1)	! Extract filename
	LOC_FNSIZE = EPOS - SPOS		! and get size
C
C	Process 'TO' or 'FROM'
C
	SPOS = EPOS
	CALL GET_TOKEN				! Get next token
	IF (SPOS .LT. 0) GO TO 4000		! Remote filename defaulted
	IF (GPFLAG .EQ. 1  .AND.  INPLINE(SPOS:EPOS-1) .NE. 'FROM')
	1   GO TO 7000				! Syntax error
	IF (GPFLAG .EQ. 3  .AND.  INPLINE(SPOS:EPOS-1) .NE. 'TO')
	1   GO TO 7000				! Syntax error

C
C	Get remote filename
C
	SPOS = EPOS
	CALL GET_TOKEN				! Get next token
	IF (SPOS .LT. 0) GO TO 7000		! Syntax error
	REM_FNAME = INPLINE(SPOS:EPOS-1)	! Extract filename
	REM_FNSIZE = EPOS - SPOS		! and get size

C
C	Make sure rest of line is empty
C
	SPOS = EPOS
	CALL GET_TOKEN
	IF(SPOS .GE. 0) GO TO 7000		! Syntax error

C
C	If either filename is '*', use the other name
C
	IF (REM_FNAME(1:REM_FNSIZE) .EQ. '*') GO TO 4000
	IF (LOC_FNAME(1:LOC_FNSIZE) .NE. '*') GO TO 9000

	LOC_FNAME = REM_FNAME			! Local filename = *
	LOC_FNSIZE = REM_FNSIZE
	GO TO 9000

4000	IF (LOC_FNAME(1:LOC_FNSIZE) .EQ. '*') GO TO 7000
	REM_FNAME = LOC_FNAME			! Remote filename = *
	REM_FNSIZE = LOC_FNSIZE
	GO TO 9000

C
C	Syntax error
C
7000	WRITE(6,7100)
7100	FORMAT(1X,'%DRCOPY-E-SYNTAX, syntax error on command line')

8000	GPFLAG = 0

9000	RETURN
	END


	SUBROUTINE GET_TOKEN

C
C	This subroutine gets the next token on the input line.
C
C	Inputs:
C		SPOS - Starting character position
C
C	Outputs:
C		SPOS - Starting position of token
C		EPOS - One character after end of token
C
C	If there are no more tokens on the line SPOS is set to -1
C

C
C	Common variables and areas
C
	CHARACTER*80  INPLINE			! Input line
	CHARACTER*64  LOC_FNAME			! Local file name
	CHARACTER*64  REM_FNAME			! Remote file name

	COMMON  /CHARS/  INPLINE,LOC_FNAME,REM_FNAME

	INTEGER*2  LOC_FNSIZE			! Local file name size
	INTEGER*2  REM_FNSIZE			! Remote file name size
	INTEGER*2  SPOS				! Starting token pos.
	INTEGER*2  EPOS				! Ending token pos.

	COMMON  /SIZES/  LOC_FNSIZE,REM_FNSIZE,SPOS,EPOS


C
C	Return immediately if SPOS is past end of line
C
	IF (SPOS .GT. 80) GO TO 400

C
C	Skip leading blanks
C
	DO 100 SPOS = SPOS,80
	IF (INPLINE(SPOS:SPOS) .NE. ' ')GO TO 200
100	CONTINUE
	GO TO 400				! No more tokens

C
C	SPOS points to start of token.  Now find first blank after token
C
200	DO 300 EPOS = SPOS,80
	IF (INPLINE(EPOS:EPOS) .EQ. ' ') GO TO 500
300	CONTINUE
	GO TO 500

400	SPOS = -1				! No more tokens

500	RETURN
	END

	SUBROUTINE HELP

C
C	This subroutine prints out the HELP message
C

	WRITE(6,100)
	WRITE(6,200)
	WRITE(6,300)
	WRITE(6,400)
100	FORMAT('0','The commands to DRCOPY are:')
200	FORMAT('0','	GET filespec1 [FROM filespec2]'/
	1  ' ','	PUT filespec1 [TO filespec2]')
300	FORMAT('0','	filespec1 is always the local filename'/
	1  ' ','	filespec2 is always the remote filename')
400	FORMAT('0','If either filespec is specified as *, the other ',
	1  'filespec is used for both.'/' If the second half of the ',
	2  'command is omitted, filespec1 is used for filespec2.'/)

	RETURN
	END

	SUBROUTINE DO_PUT

C
C	This routine is the top level routine for copying a file
C	from the local cpu to the remote cpu.
C

	INCLUDE 'SYS$LIBRARY:XFDEF.FOR/NOLIST'	! DR32 definitions
	INCLUDE 'DRCOPY.PRM/NOLIST'		! Parameters

C
C	Local Variables
C
	INTEGER*4 STATUS			! Local status

C
C	Common variables and areas
C
	CHARACTER*80  INPLINE			! Input line
	CHARACTER*64  LOC_FNAME			! Local file name
	CHARACTER*64  REM_FNAME			! Remote file name

	COMMON  /CHARS/  INPLINE,LOC_FNAME,REM_FNAME

	INTEGER*2  LOC_FNSIZE			! Local file name size
	INTEGER*2  REM_FNSIZE			! Remote file name size
	INTEGER*2  SPOS				! Starting token pos.
	INTEGER*2  EPOS				! Ending token pos.

	COMMON  /SIZES/  LOC_FNSIZE,REM_FNSIZE,SPOS,EPOS

	INTEGER*4 XFDATA(30)			! Context array
	BYTE MBFRS(BUFSIZ,NUM_MBFRS)		! Master buffers
	BYTE SBFRS(BUFSIZ,NUM_SBFRS)		! Slave buffers

	COMMON /MS_SHARE/  XFDATA,MBFRS,SBFRS

	INTEGER*4 IDEVMSG(32)			! Incoming device messages
	INTEGER*4 ODEVMSG(32)			! Outgoing device messages
	INTEGER*4 REM_BFRADS(25)		! Remote buffer addresses
	INTEGER*4 FILEATTR(6)			! File attributes
	INTEGER*4 CSTATUS			! Common status
	INTEGER*4 LASTBFRSIZ			! Last buffer size
	INTEGER*4 DDIDIS			! DDI disable
	INTEGER*2 MRMS_CNT			! Master RMS count
	INTEGER*2 MRMS_IDX			! Master RMS index
	INTEGER*2 QPKT_CNT			! Queue packet count
	INTEGER*2 QPKT_IDX			! Queue packet index
	INTEGER*2 REM_CNT			! Remote buffer count
	INTEGER*2 REM_IDX			! Remote buffer index
	INTEGER*2 NUMREM_BFRS			! Number of remote buffers
	LOGICAL*1 GPFLAG			! Get/put flag
	LOGICAL*1 LASTBFR			! Last buffer flag
	LOGICAL*1 EOFFLAG			! End of file flag
	LOGICAL*1 ERRFLAG			! Error flag
	LOGICAL*1 REMFLAG			! Remote error flag

	COMMON /MDATA/  IDEVMSG,ODEVMSG,REM_BFRADS,FILEATTR,CSTATUS,
	1		LASTBFRSIZ,DDIDIS,MRMS_CNT,MRMS_IDX,QPKT_CNT,
	2		QPKT_IDX,REM_CNT,REM_IDX,NUMREM_BFRS,GPFLAG,
	3		LASTBFR,EOFFLAG,ERRFLAG,REMFLAG

	CHARACTER*64 OFNA
	BYTE ODEVMSGB(128)

	EQUIVALENCE (ODEVMSGB,ODEVMSG)
	EQUIVALENCE (OFNA,ODEVMSGB(33))

	INTEGER*4 SYS$CLREF
	INTEGER*4 SYS$WAITFR


C
C	Initialize flags
C
	LASTBFR = .FALSE.			! Last buffer flag
	EOFFLAG = .FALSE.			! End of file flag
	ERRFLAG = .FALSE.			! Error flag
	REMFLAG = .FALSE.			! Remote error flag

C
C	Queue a NOP packet to the DR32.  The purpose of this
C	is to examine the DDI disable bit in the DSL to determine
C	if the DR32 at the other end is ready to go.
C
	CALL QUEUE_NOP(STATUS)
	IF (.NOT. STATUS) RETURN

C
C	Open local file and copy file attributes into device 
C	message array.
C
	CALL OPEN_FILE(LOC_FNAME,LOC_FNSIZE,ODEVMSG(3),STATUS)
	IF (.NOT. STATUS) THEN
	    CALL ERROR(STATUS,.FALSE.)
	    RETURN
	END IF

C
C	Finish building device message and send it.
C
	ODEVMSG(1) = 1				! Packet type
	ODEVMSG(2) = BUFSIZ			! Buffer size
	ODEVMSGB(32) = REM_FNSIZE		! Remote filename size
	OFNA = REM_FNAME			! Remote filename
	STATUS = SYS$CLREF(%VAL(1))		! Clear event flag
	IF (.NOT. STATUS) CALL FATAL_ERROR(STATUS)
	CALL XF$PKTBLD(XFDATA,			! Context array
	1              XF$K_PKT_WRTCM,		! Function = write ctrl msg
	2              ,,			! No index or size
	3              ODEVMSG,			! Device message
	4              96,			! Device message size
	5              ,			! No log area
	6              64+256,			! Ins. @ head, int. if Q empty
	7              ,,			! No action routine or parm
	8              STATUS)			! Status
	IF (.NOT. STATUS) CALL FATAL_ERROR(STATUS)
	STATUS = SYS$WAITFR(%VAL(1))		! Wait for event flag
	IF (.NOT. STATUS) CALL FATAL_ERROR(STATUS)
	IF (.NOT. CSTATUS) THEN			! Error from remote system
	    CALL ERROR(CSTATUS,REMFLAG)
	    GO TO 8000
	END IF

C
C	Set up buffer counters and indexes
C
	MRMS_CNT = NUM_MBFRS - 1		! # of avl RMS buffers
	MRMS_IDX = 2				! Next RMS buffer
	QPKT_CNT = 0				! # of buffers to be queued
	QPKT_IDX = 1				! Next buffer to be queued
	REM_CNT = NUMREM_BFRS			! # of remote buffers
	REM_IDX = 1				! Next remote buffer to use

C
C	Start the transfer going by starting an RMS read and then
C	wait until it completes.
C
	STATUS = SYS$CLREF(%VAL(3))		! Clear event flag
	IF (.NOT. STATUS) CALL FATAL_ERROR(STATUS)
	CALL START_RMS(MBFRS(1,1),BUFSIZ,GPFLAG)! Start RMS
	STATUS = SYS$WAITFR(%VAL(3))		! Wait for event flag
	IF (.NOT. STATUS) CALL FATAL_ERROR(STATUS)
	IF (.NOT. CSTATUS) CALL ERROR(CSTATUS,REMFLAG)

8000	CALL CLOSE_FILE(STATUS)
	IF (.NOT. STATUS) CALL ERROR(STATUS,.FALSE.)

	RETURN
	END

	SUBROUTINE DO_GET

C
C	This routine is the top level routine for copying a file
C	from the remote cpu to the local cpu.
C

	INCLUDE 'SYS$LIBRARY:XFDEF.FOR/NOLIST'	! DR32 definitions
	INCLUDE 'DRCOPY.PRM/NOLIST'		! Parameters

C
C	Local Variables
C
	INTEGER*4 STATUS			! Local status

C
C	Common variables and areas
C
	CHARACTER*80  INPLINE			! Input line
	CHARACTER*64  LOC_FNAME			! Local file name
	CHARACTER*64  REM_FNAME			! Remote file name

	COMMON  /CHARS/  INPLINE,LOC_FNAME,REM_FNAME

	INTEGER*2  LOC_FNSIZE			! Local file name size
	INTEGER*2  REM_FNSIZE			! Remote file name size
	INTEGER*2  SPOS				! Starting token pos.
	INTEGER*2  EPOS				! Ending token pos.

	COMMON  /SIZES/  LOC_FNSIZE,REM_FNSIZE,SPOS,EPOS

	INTEGER*4 XFDATA(30)			! Context array
	BYTE MBFRS(BUFSIZ,NUM_MBFRS)		! Master buffers
	BYTE SBFRS(BUFSIZ,NUM_SBFRS)		! Slave buffers

	COMMON /MS_SHARE/  XFDATA,MBFRS,SBFRS

	INTEGER*4 IDEVMSG(32)			! Incoming device messages
	INTEGER*4 ODEVMSG(32)			! Outgoing device messages
	INTEGER*4 REM_BFRADS(25)		! Remote buffer addresses
	INTEGER*4 FILEATTR(6)			! File attributes
	INTEGER*4 CSTATUS			! Common status
	INTEGER*4 LASTBFRSIZ			! Last buffer size
	INTEGER*4 DDIDIS			! DDI disable
	INTEGER*2 MRMS_CNT			! Master RMS count
	INTEGER*2 MRMS_IDX			! Master RMS index
	INTEGER*2 QPKT_CNT			! Queue packet count
	INTEGER*2 QPKT_IDX			! Queue packet index
	INTEGER*2 REM_CNT			! Remote buffer count
	INTEGER*2 REM_IDX			! Remote buffer index
	INTEGER*2 NUMREM_BFRS			! Number of remote buffers
	LOGICAL*1 GPFLAG			! Get/put flag
	LOGICAL*1 LASTBFR			! Last buffer flag
	LOGICAL*1 EOFFLAG			! End of file flag
	LOGICAL*1 ERRFLAG			! Error flag
	LOGICAL*1 REMFLAG			! Remote error flag

	COMMON /MDATA/  IDEVMSG,ODEVMSG,REM_BFRADS,FILEATTR,CSTATUS,
	1		LASTBFRSIZ,DDIDIS,MRMS_CNT,MRMS_IDX,QPKT_CNT,
	2		QPKT_IDX,REM_CNT,REM_IDX,NUMREM_BFRS,GPFLAG,
	3		LASTBFR,EOFFLAG,ERRFLAG,REMFLAG


	CHARACTER*64 OFNA
	BYTE ODEVMSGB(128)

	EQUIVALENCE (ODEVMSGB,ODEVMSG)
	EQUIVALENCE (OFNA,ODEVMSGB(33))

	INTEGER*4 SYS$CLREF
	INTEGER*4 SYS$WAITFR
	INTEGER*4 SYS$WFLAND


C
C	Initialize flags
C
	LASTBFR = .FALSE.			! Last buffer flag
	EOFFLAG = .FALSE.			! End of file flag
	ERRFLAG = .FALSE.			! Error flag
	REMFLAG = .FALSE.			! Remote error flag

C
C	Queue a NOP packet to the DR32.  The purpose of this is to
C	examine the DDI disable bit in the DSL to determine if
C	the DR32 at the other end is ready to go.
C
	CALL QUEUE_NOP(STATUS)
	IF (.NOT. STATUS) RETURN

C
C	Build a message to send to the remote system indicating we
C	want to GET a file.  Send the remote filename.
C
	ODEVMSG(1) = 3				! Message type
	ODEVMSG(2) = BUFSIZ			! Buffer size
	ODEVMSGB(32) = REM_FNSIZE		! Remote filename size
	OFNA = REM_FNAME			! Remote filename

C
C	Send the message and wait for 2 packets in response:
C	1)  the file attributes and  2)  the remote buffer addresses.
C
	STATUS = SYS$CLREF(%VAL(1))
	IF (.NOT. STATUS) CALL FATAL_ERROR(STATUS)
	STATUS = SYS$CLREF(%VAL(2))
	IF (.NOT. STATUS) CALL FATAL_ERROR(STATUS)

	CALL XF$PKTBLD(XFDATA,			! Context array
	1              XF$K_PKT_WRTCM,,,	! Function, no index or size
	2              ODEVMSG,92,,		! Device msg, size, no log area
	3              64+256,,,		! Ins. @ head, int. if Q empty
	4              STATUS)			! Status
	IF (.NOT. STATUS) CALL FATAL_ERROR(STATUS)

	STATUS = SYS$WFLAND(%VAL(1),%VAL(6))	 ! Wait for both responses
	IF (.NOT. STATUS) CALL FATAL_ERROR(STATUS)
	IF (.NOT. CSTATUS) THEN
	    CALL ERROR(CSTATUS,REMFLAG)
	    RETURN
	END IF

C
C	Create the local file using the file attributes sent by
C	the remote system.
C
	CALL CREATE_FILE(LOC_FNAME,LOC_FNSIZE,FILEATTR,STATUS)
	IF (.NOT. STATUS) THEN
	    CALL RMS_ERROR(STATUS)
	    CALL ERROR(STATUS,.FALSE.)
	    RETURN
	END IF

C
C	Set up buffer counters and indexes
C
	MRMS_CNT = -1				! RMS is not going
	MRMS_IDX = 1				! Next RMS buffer
	QPKT_CNT = NUM_MBFRS			! # of buffers to be queued
	QPKT_IDX = 1				! Next buffer to be queued
	REM_CNT = 0				! # of remote buffers
	REM_IDX = 1				! Next remote buffer

C
C	Start the transfer going and wait until it completes.
C
	STATUS = SYS$CLREF(%VAL(3))
	IF (.NOT. STATUS) CALL FATAL_ERROR(STATUS)

	ODEVMSG(1) = 11				! Start remote sys. going
	CALL XF$PKTBLD(XFDATA,			! Context array
	1	       XF$K_PKT_WRTCM,,,	! Function, no index or size
	2	       ODEVMSG,4,,		! Device msg, size, no log area
	3	       64+256,,,		! Ins. @ head, int. if Q empty
	4	       STATUS)
	IF (.NOT. STATUS) CALL FATAL_ERROR(STATUS)

	STATUS = SYS$WAITFR(%VAL(3))
	IF (.NOT. STATUS) CALL FATAL_ERROR(STATUS)
	IF (.NOT. CSTATUS) CALL ERROR(CSTATUS,REMFLAG)

	CALL CLOSE_FILE(STATUS)
	IF (.NOT. STATUS) CALL ERROR(STATUS,.FALSE.)
	RETURN
	END

	SUBROUTINE QUEUE_NOP(DSTATUS)

C
C	This routine queues a NOP packet to the DR32 to determine
C	if the remote cpu is ready to start a transfer.  This is
C	accomplished by testing the DDI disable bit in the DSL
C	in the packet.  The actual testing of the bit is done at
C	AST level by an action routine and the result is returned
C	in the variable DDIDIS.
C
C	DSTATUS is returned as follows:
C
C		0	Remote CPU not ready (this routine prints
C			error message)
C		1	Remote CPU ready (success)
C

	INCLUDE 'SYS$LIBRARY:XFDEF.FOR/NOLIST'	! DR32 definitions
	INCLUDE 'DRCOPY.PRM/NOLIST'		! Parameters

C
C	Local Variables
C
	INTEGER*4 STATUS			! Local status

C
C	Common variables and areas
C
	INTEGER*4 XFDATA(30)			! Context array
	BYTE MBFRS(BUFSIZ,NUM_MBFRS)		! Master buffers
	BYTE SBFRS(BUFSIZ,NUM_SBFRS)		! Slave buffers

	COMMON /MS_SHARE/  XFDATA,MBFRS,SBFRS

	INTEGER*4 IDEVMSG(32)			! Incoming device messages
	INTEGER*4 ODEVMSG(32)			! Outgoing device messages
	INTEGER*4 REM_BFRADS(25)		! Remote buffer addresses
	INTEGER*4 FILEATTR(6)			! File attributes
	INTEGER*4 CSTATUS			! Common status
	INTEGER*4 LASTBFRSIZ			! Last buffer size
	INTEGER*4 DDIDIS			! DDI disable
	INTEGER*2 MRMS_CNT			! Master RMS count
	INTEGER*2 MRMS_IDX			! Master RMS index
	INTEGER*2 QPKT_CNT			! Queue packet count
	INTEGER*2 QPKT_IDX			! Queue packet index
	INTEGER*2 REM_CNT			! Remote buffer count
	INTEGER*2 REM_IDX			! Remote buffer index
	INTEGER*2 NUMREM_BFRS			! Number of remote buffers
	LOGICAL*1 GPFLAG			! Get/put flag
	LOGICAL*1 LASTBFR			! Last buffer flag
	LOGICAL*1 EOFFLAG			! End of file flag
	LOGICAL*1 ERRFLAG			! Error flag
	LOGICAL*1 REMFLAG			! Remote error flag

	COMMON /MDATA/  IDEVMSG,ODEVMSG,REM_BFRADS,FILEATTR,CSTATUS,
	1		LASTBFRSIZ,DDIDIS,MRMS_CNT,MRMS_IDX,QPKT_CNT,
	2		QPKT_IDX,REM_CNT,REM_IDX,NUMREM_BFRS,GPFLAG,
	3		LASTBFR,EOFFLAG,ERRFLAG,REMFLAG

	INTEGER*4 DSTATUS

	INTEGER*4 SYS$CLREF
	INTEGER*4 SYS$WAITFR

	EXTERNAL ACT_NOPPKT



	STATUS = SYS$CLREF(%VAL(1))		! Clear event flag
	IF (.NOT. STATUS) CALL FATAL_ERROR(STATUS)
	CALL XF$PKTBLD(XFDATA,			! Context array
	1              XF$K_PKT_NOP,		! Function = NOP
	2              ,,,,,			! No index, size, dev. msg, log area
	3              64+256,			! Ins. @ head, int. if Q empty
	4              ACT_NOPPKT,,		! Action routine, parm.
	5              STATUS)			! Status
	IF (.NOT. STATUS) CALL FATAL_ERROR(STATUS)
	STATUS = SYS$WAITFR(%VAL(1))		! Wait for completion
	IF (.NOT. STATUS) CALL FATAL_ERROR(STATUS)

C
C	Test DDIDIS which was set at AST level by the action 
C	routine ACT_NOPPKT.  If non-zero, then print an error message.
C
	IF (DDIDIS .NE. 0) THEN
	    WRITE(6,100)
100	    FORMAT(1X,'%DRCOPY-E-REMNRDY, remote DR32 not ready')
	    DSTATUS = 0
	ELSE
	    DSTATUS = 1				! Success
	END IF

	RETURN
	END

	SUBROUTINE MRMS_AST(RAB)

C
C	This subroutine is the Master RMS completion routine.  It
C	is called at AST level to start the next RMS operation
C	and to queue a packet to the DR32 to read or write the next
C	buffer.
C

	INCLUDE 'DRCOPY.PRM/NOLIST'		! Parameters
	PARAMETER RAB$K_BLN = '44'X
	PARAMETER RAB$W_RSZ = '22'X
	PARAMETER RAB$L_STS = '8'X
	PARAMETER RMS$_EOF = '1827A'X

C
C	Local Variables
C
	INTEGER*4 STATUS			! Local status
	INTEGER*4 RAB(RAB$K_BLN/4+1)
	INTEGER*4 SIZE
	INTEGER*4 BFRSIZE

C
C	Common variables and areas
C
	INTEGER*4 XFDATA(30)			! Context array
	BYTE MBFRS(BUFSIZ,NUM_MBFRS)		! Master buffers
	BYTE SBFRS(BUFSIZ,NUM_SBFRS)		! Slave buffers

	COMMON /MS_SHARE/  XFDATA,MBFRS,SBFRS

	INTEGER*4 IDEVMSG(32)			! Incoming device messages
	INTEGER*4 ODEVMSG(32)			! Outgoing device messages
	INTEGER*4 REM_BFRADS(25)		! Remote buffer addresses
	INTEGER*4 FILEATTR(6)			! File attributes
	INTEGER*4 CSTATUS			! Common status
	INTEGER*4 LASTBFRSIZ			! Last buffer size
	INTEGER*4 DDIDIS			! DDI disable
	INTEGER*2 MRMS_CNT			! Master RMS count
	INTEGER*2 MRMS_IDX			! Master RMS index
	INTEGER*2 QPKT_CNT			! Queue packet count
	INTEGER*2 QPKT_IDX			! Queue packet index
	INTEGER*2 REM_CNT			! Remote buffer count
	INTEGER*2 REM_IDX			! Remote buffer index
	INTEGER*2 NUMREM_BFRS			! Number of remote buffers
	LOGICAL*1 GPFLAG			! Get/put flag
	LOGICAL*1 LASTBFR			! Last buffer flag
	LOGICAL*1 EOFFLAG			! End of file flag
	LOGICAL*1 ERRFLAG			! Error flag
	LOGICAL*1 REMFLAG			! Remote error flag

	COMMON /MDATA/  IDEVMSG,ODEVMSG,REM_BFRADS,FILEATTR,CSTATUS,
	1		LASTBFRSIZ,DDIDIS,MRMS_CNT,MRMS_IDX,QPKT_CNT,
	2		QPKT_IDX,REM_CNT,REM_IDX,NUMREM_BFRS,GPFLAG,
	3		LASTBFR,EOFFLAG,ERRFLAG,REMFLAG

	INTEGER*4 SYS$SETEF

	EXTERNAL SS$_NORMAL



C
C	If ERRFLAG is set, then set event flag 3 to wake up main level
C	and return.
C
	IF (ERRFLAG) THEN
	    STATUS = SYS$SETEF(%VAL(3))
	    IF (.NOT. STATUS) CALL FATAL_ERROR(STATUS)
	    RETURN
	END IF

C
C	Check for success or failure of last operation.  If success
C	see if an entire buffer was transferred.  If not, set the
C	end of file flag.  If error is RMS$_EOF and doing a PUT, then
C	also set end of file flag.
C
	STATUS = RAB(RAB$L_STS/4+1)		! Get status from RAB
	IF (STATUS) GO TO 400			! Success
	IF (STATUS .EQ. RMS$_EOF .AND. GPFLAG .EQ. 3) GO TO 450
	CALL RMS_ERROR(STATUS)
	RETURN

400	IF (GPFLAG .EQ. 1) GO TO 500		! Only check EOF for PUT
450	BFRSIZE = RAB(RAB$W_RSZ/4+1)/65536	! Get size of buffer
	IF (BFRSIZE .NE. BUFSIZ) THEN
	    EOFFLAG = .TRUE.			! Set end of file flag
	    LASTBFRSIZ = BFRSIZE
	    GO TO 700
	END IF

C
C	Decrement the count of the number of buffers available
C	for an RMS operation.  If the count goes negative, then
C	we ran out of buffers temporarily and can't start an RMS
C	operation (the next RMS operation will get started in the
C	ACT_RWPKT routine which makes buffers available for RMS
C	operations.)  Otherwise, start the next RMS operation now.
C
500	MRMS_CNT = MRMS_CNT - 1			! Decr. RMS buffer count
	IF (MRMS_CNT .GE. 0) THEN
	    SIZE = BUFSIZ			! Assume not last buffer
	    IF (LASTBFR .AND. MRMS_CNT .EQ. 0)
	1		SIZE = LASTBFRSIZ	! This is the last buffer
	    CALL START_RMS(MBFRS(1,MRMS_IDX),SIZE,GPFLAG) ! Start RMS operation
	    MRMS_IDX = MRMS_IDX + 1		! Advance next buffer index
	    IF (MRMS_IDX .GT. NUM_MBFRS) MRMS_IDX = 1  ! modulo NUM_MBFRS
	END IF

C
C	If MRMS_CNT is less than 0 and LASTBFR is .TRUE. then we
C	are finished doing a GET.  Wake up main level.
C
600	IF (MRMS_CNT .LT. 0 .AND. LASTBFR) THEN
	    CSTATUS = %LOC(SS$_NORMAL)		! Indicate success
	    STATUS = SYS$SETEF(%VAL(3))		! Wake up main level
	    IF (.NOT. STATUS) CALL FATAL_ERROR(STATUS)
	END IF

C
C	Increment the number of buffers available to be queued to the
C	DR32.  If we have a matching remote buffer then queue an
C	operation to the DR32.
C
700	QPKT_CNT = QPKT_CNT + 1			! Incr. QPKT buffer count
	IF (REM_CNT .GT. 0) CALL QUEUE_PKT

	RETURN
	END

	SUBROUTINE QUEUE_PKT

C
C	This routine queues read or write data packets to the DR32.
C	It is called from either MRMS_AST, MFQ_PNXTBFR, or MFQ_PLSTBFR
C	only when both a local and a remote buffer are available.
C

	INCLUDE 'SYS$LIBRARY:XFDEF.FOR/NOLIST'	! DR32 definitions
	INCLUDE 'DRCOPY.PRM/NOLIST'		! Parameters

C
C	Local Variables
C
	INTEGER*4 STATUS
	INTEGER*4 FUNC
	INTEGER*4 SIZE
	INTEGER*2 BFRCNT
C
C	Common variables and areas
C
	INTEGER*4 XFDATA(30)			! Context array
	BYTE MBFRS(BUFSIZ,NUM_MBFRS)		! Master buffers
	BYTE SBFRS(BUFSIZ,NUM_SBFRS)		! Slave buffers

	COMMON /MS_SHARE/  XFDATA,MBFRS,SBFRS

	INTEGER*4 IDEVMSG(32)			! Incoming device messages
	INTEGER*4 ODEVMSG(32)			! Outgoing device messages
	INTEGER*4 REM_BFRADS(25)		! Remote buffer addresses
	INTEGER*4 FILEATTR(6)			! File attributes
	INTEGER*4 CSTATUS			! Common status
	INTEGER*4 LASTBFRSIZ			! Last buffer size
	INTEGER*4 DDIDIS			! DDI disable
	INTEGER*2 MRMS_CNT			! Master RMS count
	INTEGER*2 MRMS_IDX			! Master RMS index
	INTEGER*2 QPKT_CNT			! Queue packet count
	INTEGER*2 QPKT_IDX			! Queue packet index
	INTEGER*2 REM_CNT			! Remote buffer count
	INTEGER*2 REM_IDX			! Remote buffer index
	INTEGER*2 NUMREM_BFRS			! Number of remote buffers
	LOGICAL*1 GPFLAG			! Get/put flag
	LOGICAL*1 LASTBFR			! Last buffer flag
	LOGICAL*1 EOFFLAG			! End of file flag
	LOGICAL*1 ERRFLAG			! Error flag
	LOGICAL*1 REMFLAG			! Remote error flag

	COMMON /MDATA/  IDEVMSG,ODEVMSG,REM_BFRADS,FILEATTR,CSTATUS,
	1		LASTBFRSIZ,DDIDIS,MRMS_CNT,MRMS_IDX,QPKT_CNT,
	2		QPKT_IDX,REM_CNT,REM_IDX,NUMREM_BFRS,GPFLAG,
	3		LASTBFR,EOFFLAG,ERRFLAG,REMFLAG

	EXTERNAL ACT_RWPKT

C
C	Decrement buffer counters
C
	REM_CNT = REM_CNT - 1			! Remote buffer count
	QPKT_CNT = QPKT_CNT - 1			! QPKT buffer count

C
C	Queue packet to DR32.
C
	ODEVMSG(1) = REM_BFRADS(REM_IDX)	! Device msg is remote bfr. addr.
	IF (GPFLAG .EQ. 1) THEN
	    FUNC = XF$K_PKT_RD			! Doing a GET
	    BFRCNT = REM_CNT
	ELSE
	    FUNC = XF$K_PKT_WRT			! Doing a PUT
	    BFRCNT = QPKT_CNT
	END IF

	IF (BFRCNT.EQ.0 .AND. EOFFLAG) THEN
	    SIZE = LASTBFRSIZ			! This is the last buffer
	ELSE
	    SIZE =BUFSIZ			! Not the last buffer
	END IF

	CALL XF$PKTBLD(XFDATA,			! Context array
	1              FUNC,			! Function
	2              QPKT_IDX,		! Buffer index
	3              SIZE,			! Size of transfer
	4              ODEVMSG,			! Device message
	5              4,			! Size of device message
	6              ,			! No log area
	7              24+64,			! Send all, int. on Q empty
	8              ACT_RWPKT,,		! Action routine, no param.
	9              STATUS)			! Status

C
C	Adjust buffer indexes
C
	REM_IDX = REM_IDX + 1			! Advance remote buffer index
	IF (REM_IDX .GT. NUMREM_BFRS) REM_IDX=1	! modulo # of remote buffers
	QPKT_IDX = QPKT_IDX +1			! Advance QPKT buffer index
	IF (QPKT_IDX .GT. NUM_MBFRS) QPKT_IDX=1	! modulo # of local buffers

C
C	Check for success from XF$PKTBLD
C
	IF (.NOT. STATUS) CALL FATAL_ERROR(STATUS)

	RETURN
	END

	SUBROUTINE PKT_AST

C
C	This routine is called whenever a DR32 command packet is
C	placed on an empty termination queue.  This routine calls
C	XF$GETPKT which removes the packet at the head of the
C	termination queue and calls the action routine, if there is 
C	one.  This routine must process all packets on the
C	termination queue until the queue is empty.
C

	INCLUDE 'DRCOPY.PRM/NOLIST'		! Parameters
	PARAMETER SHR$_QEMPTY = '1280'X

C
C	Local Variables
C
	INTEGER*4 STATUS

C
C	Common variables and areas
C
	INTEGER*4 XFDATA(30)			! Context array
	BYTE MBFRS(BUFSIZ,NUM_MBFRS)		! Master buffers
	BYTE SBFRS(BUFSIZ,NUM_SBFRS)		! Slave buffers

	COMMON /MS_SHARE/  XFDATA,MBFRS,SBFRS

100	CALL XF$GETPKT(XFDATA,1,,,,,STATUS)	! Calls action routine
	IF (STATUS) GO TO 100			! Repeat until q empty
	IF (STATUS .EQ. SHR$_QEMPTY) GO TO 9000

C
C	Have a fatal error - print error and IOSB
C
	CALL DR32_ERROR				! Doesn't return

9000	RETURN
	END

	SUBROUTINE ACT_NOPPKT

C
C	This routine is the action routine for NOP packets.  It
C	tests the DDI disable bit in the DSL and sets DDIDIS.
C

	INCLUDE 'SYS$LIBRARY:XFDEF.FOR/NOLIST'	! DR32 definitions
	INCLUDE 'DRCOPY.PRM/NOLIST'		! Parameters

C
C	Local Variables
C
	INTEGER*4 STATUS

C
C	Common variables and areas
C
	INTEGER*4 XFDATA(30)			! Context array
	BYTE MBFRS(BUFSIZ,NUM_MBFRS)		! Master buffers
	BYTE SBFRS(BUFSIZ,NUM_SBFRS)		! Slave buffers

	COMMON /MS_SHARE/  XFDATA,MBFRS,SBFRS

	INTEGER*4 IDEVMSG(32)			! Incoming device messages
	INTEGER*4 ODEVMSG(32)			! Outgoing device messages
	INTEGER*4 REM_BFRADS(25)		! Remote buffer addresses
	INTEGER*4 FILEATTR(6)			! File attributes
	INTEGER*4 CSTATUS			! Common status
	INTEGER*4 LASTBFRSIZ			! Last buffer size
	INTEGER*4 DDIDIS			! DDI disable
	INTEGER*2 MRMS_CNT			! Master RMS count
	INTEGER*2 MRMS_IDX			! Master RMS index
	INTEGER*2 QPKT_CNT			! Queue packet count
	INTEGER*2 QPKT_IDX			! Queue packet index
	INTEGER*2 REM_CNT			! Remote buffer count
	INTEGER*2 REM_IDX			! Remote buffer index
	INTEGER*2 NUMREM_BFRS			! Number of remote buffers
	LOGICAL*1 GPFLAG			! Get/put flag
	LOGICAL*1 LASTBFR			! Last buffer flag
	LOGICAL*1 EOFFLAG			! End of file flag
	LOGICAL*1 ERRFLAG			! Error flag
	LOGICAL*1 REMFLAG			! Remote error flag

	COMMON /MDATA/  IDEVMSG,ODEVMSG,REM_BFRADS,FILEATTR,CSTATUS,
	1		LASTBFRSIZ,DDIDIS,MRMS_CNT,MRMS_IDX,QPKT_CNT,
	2		QPKT_IDX,REM_CNT,REM_IDX,NUMREM_BFRS,GPFLAG,
	3		LASTBFR,EOFFLAG,ERRFLAG,REMFLAG

	INTEGER*4 SYS$SETEF

	INTEGER*4 DSL				! DR32 status longword

	EQUIVALENCE (DSL,XFDATA(8))



	DDIDIS = DSL .AND. XF$M_PKT_DDIDIS
	STATUS = SYS$SETEF(%VAL(1))
	IF (.NOT. STATUS) CALL FATAL_ERROR(STATUS)
	RETURN
	END

	SUBROUTINE ACT_RWPKT

C
C	This subroutine is the action routine for a Read or Write
C	data packet which has just completed.
C

	INCLUDE 'SYS$LIBRARY:XFDEF.FOR/NOLIST'	! DR32 definitions
	INCLUDE 'DRCOPY.PRM/NOLIST'		! Parameters

C
C	Local Variables
C
	INTEGER*4 STATUS
	INTEGER*4 SIZE

C
C	Common variables and areas
C
	INTEGER*4 XFDATA(30)			! Context array
	BYTE MBFRS(BUFSIZ,NUM_MBFRS)		! Master buffers
	BYTE SBFRS(BUFSIZ,NUM_SBFRS)		! Slave buffers

	COMMON /MS_SHARE/  XFDATA,MBFRS,SBFRS

	INTEGER*4 IDEVMSG(32)			! Incoming device messages
	INTEGER*4 ODEVMSG(32)			! Outgoing device messages
	INTEGER*4 REM_BFRADS(25)		! Remote buffer addresses
	INTEGER*4 FILEATTR(6)			! File attributes
	INTEGER*4 CSTATUS			! Common status
	INTEGER*4 LASTBFRSIZ			! Last buffer size
	INTEGER*4 DDIDIS			! DDI disable
	INTEGER*2 MRMS_CNT			! Master RMS count
	INTEGER*2 MRMS_IDX			! Master RMS index
	INTEGER*2 QPKT_CNT			! Queue packet count
	INTEGER*2 QPKT_IDX			! Queue packet index
	INTEGER*2 REM_CNT			! Remote buffer count
	INTEGER*2 REM_IDX			! Remote buffer index
	INTEGER*2 NUMREM_BFRS			! Number of remote buffers
	LOGICAL*1 GPFLAG			! Get/put flag
	LOGICAL*1 LASTBFR			! Last buffer flag
	LOGICAL*1 EOFFLAG			! End of file flag
	LOGICAL*1 ERRFLAG			! Error flag
	LOGICAL*1 REMFLAG			! Remote error flag

	COMMON /MDATA/  IDEVMSG,ODEVMSG,REM_BFRADS,FILEATTR,CSTATUS,
	1		LASTBFRSIZ,DDIDIS,MRMS_CNT,MRMS_IDX,QPKT_CNT,
	2		QPKT_IDX,REM_CNT,REM_IDX,NUMREM_BFRS,GPFLAG,
	3		LASTBFR,EOFFLAG,ERRFLAG,REMFLAG

	INTEGER*4 DSL				! DR32 status longword

	EQUIVALENCE (DSL,XFDATA(8))



	IF (.NOT. DSL) CALL DR32_ERROR		! Error in DSL

C
C	Send a message to the remote system telling it that it's
C	next buffer has been processed (filled or emptied).  If
C	the size of the transfer is not equal to the buffer size,
C	then it must have been the last transfer.
C
	IF (XFDATA(4) .NE. BUFSIZ) THEN	
	    MODE = 64				! Last transfer - insert at tail of Q
	    ODEVMSG(1) = 7			! Last buffer message
	    ODEVMSG(2) = LASTBFRSIZ		! Send size
	    LASTBFR = .TRUE.			! Set flag
	ELSE					! Not last transfer
	    MODE = 64 + 256			! Insert at head of Q
	    ODEVMSG(1) = 5			! Next buffer message
	END IF

	IF (LASTBFR .AND. GPFLAG .EQ. 1) GO TO 5000  ! Don't send if last buffer and GET

	CALL XF$PKTBLD(XFDATA,			! Context array
	1              XF$K_PKT_WRTCM,,,	! Function, no index or size
	2              ODEVMSG,8,,		! Device msg, size, no log area
	3              MODE,,,			! Mode, no action routine
	4              STATUS)			! Status
	IF (.NOT. STATUS) CALL FATAL_ERROR(STATUS)

C
C	Increment the count of the number of buffers available for
C	an RMS operation.  If the count equals zero, then we 
C	previously ran out of RMS buffers and therefore there is 
C	no RMS operation in progress.  In this case, we start the
C	next RMS operation.  If the count is greater than zero, then
C	there is already an RMS operation in progress.
C
5000	MRMS_CNT = MRMS_CNT + 1			! Incr. RMS buffer count
	IF (MRMS_CNT .EQ. 0) THEN
	    SIZE = BUFSIZ			! Assume not last buffer
	    IF (LASTBFR) SIZE = LASTBFRSIZ	! This is the last buffer (GET only)
	    CALL START_RMS(MBFRS(1,MRMS_IDX),SIZE,GPFLAG)  ! Start RMS
	    MRMS_IDX = MRMS_IDX + 1		! Advance RMS buffer index
	    IF (MRMS_IDX .GT. NUM_MBFRS) MRMS_IDX = 1  ! modulo NUM_MBFRS
	END IF

	RETURN
	END

	SUBROUTINE ACT_FREQUE

C
C	This routine is the action routine for packets that were
C	on the free queue.  First it puts another packet on the free queue,
C	and then calls the appropriate routine based on the type code
C	in the device message.
C

	INCLUDE 'DRCOPY.PRM/NOLIST'		! Parameters
	PARAMETER SHR$_VALERR = '11E8'X

C
C	Local Variables
C
	INTEGER*4 STATUS

C
C	Common variables and areas
C
	INTEGER*4 IDEVMSG(32)			! Incoming device messages
	INTEGER*4 ODEVMSG(32)			! Outgoing device messages
	INTEGER*4 REM_BFRADS(25)		! Remote buffer addresses
	INTEGER*4 FILEATTR(6)			! File attributes
	INTEGER*4 CSTATUS			! Common status
	INTEGER*4 LASTBFRSIZ			! Last buffer size
	INTEGER*4 DDIDIS			! DDI disable
	INTEGER*2 MRMS_CNT			! Master RMS count
	INTEGER*2 MRMS_IDX			! Master RMS index
	INTEGER*2 QPKT_CNT			! Queue packet count
	INTEGER*2 QPKT_IDX			! Queue packet index
	INTEGER*2 REM_CNT			! Remote buffer count
	INTEGER*2 REM_IDX			! Remote buffer index
	INTEGER*2 NUMREM_BFRS			! Number of remote buffers
	LOGICAL*1 GPFLAG			! Get/put flag
	LOGICAL*1 LASTBFR			! Last buffer flag
	LOGICAL*1 EOFFLAG			! End of file flag
	LOGICAL*1 ERRFLAG			! Error flag
	LOGICAL*1 REMFLAG			! Remote error flag

	COMMON /MDATA/  IDEVMSG,ODEVMSG,REM_BFRADS,FILEATTR,CSTATUS,
	1		LASTBFRSIZ,DDIDIS,MRMS_CNT,MRMS_IDX,QPKT_CNT,
	2		QPKT_IDX,REM_CNT,REM_IDX,NUMREM_BFRS,GPFLAG,
	3		LASTBFR,EOFFLAG,ERRFLAG,REMFLAG



	CALL FREESET				! Put another packet on FREQ

	GO TO (100,200,300,400,500,600,700,800,900,1000,1100),IDEVMSG(1)

C
C	Invalid packet 
C
	CALL FATAL_ERROR(SHR$_VALERR)

C
C	Type code = 1    Start a PUT    M -> S
C
100	CALL SFQ_STARTPUT(IDEVMSG)
	GO TO 9000

C
C	Type code = 2    Slave buffer addresses    S -> M
C
200	CALL MFQ_BFRADS
	GO TO 9000

C
C	Type code = 3    Start a GET    M -> S
C
300	CALL SFQ_STARTGET(IDEVMSG)
	GO TO 9000

C
C	Type code = 4    File attributes    S -> M
C
400	CALL MFQ_FILEATTR
	GO TO 9000

C
C	Type code = 5    Processed next buffer    M -> S
C
500	CALL SFQ_PNXTBFR(IDEVMSG)
	GO TO 9000

C
C	Type code = 6    Processed next buffer    S -> M
C
600	CALL MFQ_PNXTBFR
	GO TO 9000

C
C	Type code = 7    Processed last buffer    M -> S
C
700	CALL SFQ_PLSTBFR(IDEVMSG)
	GO TO 9000

C
C	Type code = 8    Processed last buffer    S -> M
C
800	CALL MFQ_PLSTBFR
	GO TO 9000

C
C	Type code = 9    Error    M -> S
C
900	CALL SLV_SHUTDOWN
	GO TO 9000

C
C	Type code = 10    Error    S -> M
C
1000	CALL MFQ_ERROR
	GO TO 9000

C
C	Type code = 11	Start sending data (Get only)	M -> S
C
1100	CALL SFQ_GOGET


9000	RETURN
	END

	SUBROUTINE FREESET

C
C	This routine puts an empty packet on the FREQ.  It is called from
C	ACT_FREQUE and the only reason it's a subroutine is that ACT_FREQUE
C	can't be an external in ACT_FREQUE.
C

	INCLUDE 'DRCOPY.PRM/NOLIST'

C
C	Local variables
C
	INTEGER*4 STATUS

C
C	Common variables and areas
C
	INTEGER*4 XFDATA(30)			! Context array
	BYTE MBFRS(BUFSIZ,NUM_MBFRS)		! Master buffers
	BYTE SBFRS(BUFSIZ,NUM_SBFRS)		! Slave buffers

	COMMON /MS_SHARE/  XFDATA,MBFRS,SBFRS

	EXTERNAL ACT_FREQUE

	CALL XF$FREESET(XFDATA,
	1               1,			! Number of packets
	2               1,			! AST if TERMQ empty
	3               ACT_FREQUE,,		! Action routine and parameter
	4               STATUS)			! Status
	IF (.NOT. STATUS) CALL FATAL_ERROR(STATUS)

	RETURN
	END

	SUBROUTINE MFQ_BFRADS

C
C	This routine is called to process the list of buffer
C	addresses sent over by the slave.
C

C
C	Local Variables
C
	INTEGER*4 STATUS

C
C	Common variables and areas
C
	INTEGER*4 IDEVMSG(32)			! Incoming device messages
	INTEGER*4 ODEVMSG(32)			! Outgoing device messages
	INTEGER*4 REM_BFRADS(25)		! Remote buffer addresses
	INTEGER*4 FILEATTR(6)			! File attributes
	INTEGER*4 CSTATUS			! Common status
	INTEGER*4 LASTBFRSIZ			! Last buffer size
	INTEGER*4 DDIDIS			! DDI disable
	INTEGER*2 MRMS_CNT			! Master RMS count
	INTEGER*2 MRMS_IDX			! Master RMS index
	INTEGER*2 QPKT_CNT			! Queue packet count
	INTEGER*2 QPKT_IDX			! Queue packet index
	INTEGER*2 REM_CNT			! Remote buffer count
	INTEGER*2 REM_IDX			! Remote buffer index
	INTEGER*2 NUMREM_BFRS			! Number of remote buffers
	LOGICAL*1 GPFLAG			! Get/put flag
	LOGICAL*1 LASTBFR			! Last buffer flag
	LOGICAL*1 EOFFLAG			! End of file flag
	LOGICAL*1 ERRFLAG			! Error flag
	LOGICAL*1 REMFLAG			! Remote error flag

	COMMON /MDATA/  IDEVMSG,ODEVMSG,REM_BFRADS,FILEATTR,CSTATUS,
	1		LASTBFRSIZ,DDIDIS,MRMS_CNT,MRMS_IDX,QPKT_CNT,
	2		QPKT_IDX,REM_CNT,REM_IDX,NUMREM_BFRS,GPFLAG,
	3		LASTBFR,EOFFLAG,ERRFLAG,REMFLAG

	INTEGER*4 SYS$SETEF

	EXTERNAL SS$_NORMAL


	NUMREM_BFRS = IDEVMSG(2)		! Number of remote buffers
	REM_CNT = NUMREM_BFRS

	DO 100 I = 1,NUMREM_BFRS
100	REM_BFRADS(I) = IDEVMSG(I+2)		! Store each address

	CSTATUS = %LOC(SS$_NORMAL)
	STATUS = SYS$SETEF(%VAL(1))		! Set event flag
	IF (.NOT. STATUS) CALL FATAL_ERROR(STATUS)

	RETURN
	END

	SUBROUTINE MFQ_FILEATTR

C
C	This routine copies the file attributes sent by the remote
C	system (at the start of a GET) from the input device message array
C	to the file attributes array and then sets an event flag.
C

C
C	Local Variables
C
	INTEGER*4 STATUS

C
C	Common variables and areas
C
	INTEGER*4 IDEVMSG(32)			! Incoming device messages
	INTEGER*4 ODEVMSG(32)			! Outgoing device messages
	INTEGER*4 REM_BFRADS(25)		! Remote buffer addresses
	INTEGER*4 FILEATTR(6)			! File attributes
	INTEGER*4 CSTATUS			! Common status
	INTEGER*4 LASTBFRSIZ			! Last buffer size
	INTEGER*4 DDIDIS			! DDI disable
	INTEGER*2 MRMS_CNT			! Master RMS count
	INTEGER*2 MRMS_IDX			! Master RMS index
	INTEGER*2 QPKT_CNT			! Queue packet count
	INTEGER*2 QPKT_IDX			! Queue packet index
	INTEGER*2 REM_CNT			! Remote buffer count
	INTEGER*2 REM_IDX			! Remote buffer index
	INTEGER*2 NUMREM_BFRS			! Number of remote buffers
	LOGICAL*1 GPFLAG			! Get/put flag
	LOGICAL*1 LASTBFR			! Last buffer flag
	LOGICAL*1 EOFFLAG			! End of file flag
	LOGICAL*1 ERRFLAG			! Error flag
	LOGICAL*1 REMFLAG			! Remote error flag

	COMMON /MDATA/  IDEVMSG,ODEVMSG,REM_BFRADS,FILEATTR,CSTATUS,
	1		LASTBFRSIZ,DDIDIS,MRMS_CNT,MRMS_IDX,QPKT_CNT,
	2		QPKT_IDX,REM_CNT,REM_IDX,NUMREM_BFRS,GPFLAG,
	3		LASTBFR,EOFFLAG,ERRFLAG,REMFLAG

	INTEGER*4 SYS$SETEF


	DO 100 I = 1,6
	FILEATTR(I) = IDEVMSG(I+2)
100	CONTINUE

	STATUS = SYS$SETEF(%VAL(2))
	IF (.NOT. STATUS) CALL FATAL_ERROR(STATUS)

	RETURN
	END

	SUBROUTINE MFQ_PNXTBFR

C
C	This subroutine is called when the slave sends a message
C	indicating that it has processed its next buffer.
C

C
C	Common variables and areas
C
	INTEGER*4 IDEVMSG(32)			! Incoming device messages
	INTEGER*4 ODEVMSG(32)			! Outgoing device messages
	INTEGER*4 REM_BFRADS(25)		! Remote buffer addresses
	INTEGER*4 FILEATTR(6)			! File attributes
	INTEGER*4 CSTATUS			! Common status
	INTEGER*4 LASTBFRSIZ			! Last buffer size
	INTEGER*4 DDIDIS			! DDI disable
	INTEGER*2 MRMS_CNT			! Master RMS count
	INTEGER*2 MRMS_IDX			! Master RMS index
	INTEGER*2 QPKT_CNT			! Queue packet count
	INTEGER*2 QPKT_IDX			! Queue packet index
	INTEGER*2 REM_CNT			! Remote buffer count
	INTEGER*2 REM_IDX			! Remote buffer index
	INTEGER*2 NUMREM_BFRS			! Number of remote buffers
	LOGICAL*1 GPFLAG			! Get/put flag
	LOGICAL*1 LASTBFR			! Last buffer flag
	LOGICAL*1 EOFFLAG			! End of file flag
	LOGICAL*1 ERRFLAG			! Error flag
	LOGICAL*1 REMFLAG			! Remote error flag

	COMMON /MDATA/  IDEVMSG,ODEVMSG,REM_BFRADS,FILEATTR,CSTATUS,
	1		LASTBFRSIZ,DDIDIS,MRMS_CNT,MRMS_IDX,QPKT_CNT,
	2		QPKT_IDX,REM_CNT,REM_IDX,NUMREM_BFRS,GPFLAG,
	3		LASTBFR,EOFFLAG,ERRFLAG,REMFLAG


	IF (ERRFLAG) RETURN			! Return if ERRFLAG is set

C
C	Increment the number of remote buffers available.  If
C	we have a matching local buffer, then queue an operation
C	to the DR32.
C
	REM_CNT = REM_CNT +1
	IF (QPKT_CNT .GT. 0) CALL QUEUE_PKT

	RETURN
	END

	SUBROUTINE MFQ_PLSTBFR

C
C	This routine is called when the slave sends a message
C	indicating that it has processed its last buffer.
C

C
C	Local Variables
C
	INTEGER*4 STATUS

C
C	Common variables and areas
C
	INTEGER*4 IDEVMSG(32)			! Incoming device messages
	INTEGER*4 ODEVMSG(32)			! Outgoing device messages
	INTEGER*4 REM_BFRADS(25)		! Remote buffer addresses
	INTEGER*4 FILEATTR(6)			! File attributes
	INTEGER*4 CSTATUS			! Common status
	INTEGER*4 LASTBFRSIZ			! Last buffer size
	INTEGER*4 DDIDIS			! DDI disable
	INTEGER*2 MRMS_CNT			! Master RMS count
	INTEGER*2 MRMS_IDX			! Master RMS index
	INTEGER*2 QPKT_CNT			! Queue packet count
	INTEGER*2 QPKT_IDX			! Queue packet index
	INTEGER*2 REM_CNT			! Remote buffer count
	INTEGER*2 REM_IDX			! Remote buffer index
	INTEGER*2 NUMREM_BFRS			! Number of remote buffers
	LOGICAL*1 GPFLAG			! Get/put flag
	LOGICAL*1 LASTBFR			! Last buffer flag
	LOGICAL*1 EOFFLAG			! End of file flag
	LOGICAL*1 ERRFLAG			! Error flag
	LOGICAL*1 REMFLAG			! Remote error flag

	COMMON /MDATA/  IDEVMSG,ODEVMSG,REM_BFRADS,FILEATTR,CSTATUS,
	1		LASTBFRSIZ,DDIDIS,MRMS_CNT,MRMS_IDX,QPKT_CNT,
	2		QPKT_IDX,REM_CNT,REM_IDX,NUMREM_BFRS,GPFLAG,
	3		LASTBFR,EOFFLAG,ERRFLAG,REMFLAG

	INTEGER*4 SYS$SETEF

	EXTERNAL SS$_NORMAL

	IF (ERRFLAG) RETURN			! Return if ERRFLAG is set
C
C	If this is a GET then we have to read the last buffer.  If
C	this is a PUT, then we are all done.
C
	IF (GPFLAG .EQ. 1) THEN			! Doing a GET
	    EOFFLAG = .TRUE.			! Set end of file flag
	    LASTBFRSIZ = IDEVMSG(2)		! Save last buffer size
	    REM_CNT = REM_CNT + 1		! Inc. remote bfr count
	    IF (QPKT_CNT .GT. 0) CALL QUEUE_PKT	! Queue a read if possible
	ELSE					! Doing a PUT
	    CSTATUS = %LOC(SS$_NORMAL)		! Set success status
	    STATUS = SYS$SETEF(%VAL(3))		! Wake up main level
	    IF (.NOT. STATUS) CALL FATAL_ERROR(STATUS)
	END IF

	RETURN
	END

	SUBROUTINE MFQ_ERROR

C
C	This subroutine is called when the remote system sends
C	an error message
C

C
C	Local Variables
C
	INTEGER*4 STATUS

C
C	Common variables and areas
C
	INTEGER*4 IDEVMSG(32)			! Incoming device messages
	INTEGER*4 ODEVMSG(32)			! Outgoing device messages
	INTEGER*4 REM_BFRADS(25)		! Remote buffer addresses
	INTEGER*4 FILEATTR(6)			! File attributes
	INTEGER*4 CSTATUS			! Common status
	INTEGER*4 LASTBFRSIZ			! Last buffer size
	INTEGER*4 DDIDIS			! DDI disable
	INTEGER*2 MRMS_CNT			! Master RMS count
	INTEGER*2 MRMS_IDX			! Master RMS index
	INTEGER*2 QPKT_CNT			! Queue packet count
	INTEGER*2 QPKT_IDX			! Queue packet index
	INTEGER*2 REM_CNT			! Remote buffer count
	INTEGER*2 REM_IDX			! Remote buffer index
	INTEGER*2 NUMREM_BFRS			! Number of remote buffers
	LOGICAL*1 GPFLAG			! Get/put flag
	LOGICAL*1 LASTBFR			! Last buffer flag
	LOGICAL*1 EOFFLAG			! End of file flag
	LOGICAL*1 ERRFLAG			! Error flag
	LOGICAL*1 REMFLAG			! Remote error flag

	COMMON /MDATA/  IDEVMSG,ODEVMSG,REM_BFRADS,FILEATTR,CSTATUS,
	1		LASTBFRSIZ,DDIDIS,MRMS_CNT,MRMS_IDX,QPKT_CNT,
	2		QPKT_IDX,REM_CNT,REM_IDX,NUMREM_BFRS,GPFLAG,
	3		LASTBFR,EOFFLAG,ERRFLAG,REMFLAG

	INTEGER*4 SYS$SETEF


	ERRFLAG = .TRUE.			! Set error flag
	REMFLAG = .TRUE.			! Set remote error flag
	CSTATUS = IDEVMSG(2)			! Get error status

C
C	Set event flags 1 and 2 and conditionally 3
C
	STATUS = SYS$SETEF(%VAL(1))
	IF (.NOT. STATUS)CALL FATAL_ERROR(STATUS)
	STATUS = SYS$SETEF(%VAL(2))
	IF (.NOT. STATUS) CALL FATAL_ERROR(STATUS)

	IF (GPFLAG.EQ.1 .AND. MRMS_CNT .EQ. -1)	! Get
	1    STATUS = SYS$SETEF(%VAL(3))
	IF (GPFLAG.EQ.3 .AND. EOFFLAG)		! Put
	1    STATUS = SYS$SETEF(%VAL(3))
	IF (.NOT. STATUS) CALL FATAL_ERROR(STATUS)

	RETURN
	END

	SUBROUTINE ERROR(CSTATUS,REMFLAG)

C
C	This routine prints error messages and returns.  If REMFLAG
C	is set the error message is preceded by a message saying
C	that the error is from the remote system.
C

	INTEGER*2 LENGTH

	INTEGER*4 STATUS,CSTATUS

	LOGICAL REMFLAG

	CHARACTER*256 MSGBFR

	INTEGER*4 SYS$GETMSG


	IF (REMFLAG) WRITE(6,100)
100	FORMAT(1X,'%DRCOPY-E-REMERROR, error from remote system:')

	STATUS=SYS$GETMSG(%VAL(CSTATUS),LENGTH,MSGBFR,%VAL(15),)
	IF (.NOT. STATUS) CALL FATAL_ERROR(STATUS)

	WRITE(6,200)MSGBFR(1:LENGTH)
200	FORMAT(1X,A)

	RETURN
	END

	SUBROUTINE RMS_ERROR(ISTATUS)

C
C	This subroutine sends an error packet to the remote system.
C

	INCLUDE 'SYS$LIBRARY:XFDEF.FOR/NOLIST'	! DR32 definitions
	INCLUDE 'DRCOPY.PRM/NOLIST'		! Parameters

C
C	Local Variables
C
	INTEGER*4 ISTATUS,STATUS

C
C	Common variables and areas
C
	INTEGER*4 XFDATA(30)			! Context array
	BYTE MBFRS(BUFSIZ,NUM_MBFRS)		! Master buffers
	BYTE SBFRS(BUFSIZ,NUM_SBFRS)		! Slave buffers

	COMMON /MS_SHARE/  XFDATA,MBFRS,SBFRS

	INTEGER*4 IDEVMSG(32)			! Incoming device messages
	INTEGER*4 ODEVMSG(32)			! Outgoing device messages
	INTEGER*4 REM_BFRADS(25)		! Remote buffer addresses
	INTEGER*4 FILEATTR(6)			! File attributes
	INTEGER*4 CSTATUS			! Common status
	INTEGER*4 LASTBFRSIZ			! Last buffer size
	INTEGER*4 DDIDIS			! DDI disable
	INTEGER*2 MRMS_CNT			! Master RMS count
	INTEGER*2 MRMS_IDX			! Master RMS index
	INTEGER*2 QPKT_CNT			! Queue packet count
	INTEGER*2 QPKT_IDX			! Queue packet index
	INTEGER*2 REM_CNT			! Remote buffer count
	INTEGER*2 REM_IDX			! Remote buffer index
	INTEGER*2 NUMREM_BFRS			! Number of remote buffers
	LOGICAL*1 GPFLAG			! Get/put flag
	LOGICAL*1 LASTBFR			! Last buffer flag
	LOGICAL*1 EOFFLAG			! End of file flag
	LOGICAL*1 ERRFLAG			! Error flag
	LOGICAL*1 REMFLAG			! Remote error flag

	COMMON /MDATA/  IDEVMSG,ODEVMSG,REM_BFRADS,FILEATTR,CSTATUS,
	1		LASTBFRSIZ,DDIDIS,MRMS_CNT,MRMS_IDX,QPKT_CNT,
	2		QPKT_IDX,REM_CNT,REM_IDX,NUMREM_BFRS,GPFLAG,
	3		LASTBFR,EOFFLAG,ERRFLAG,REMFLAG

	INTEGER*4 SYS$SETEF


	ERRFLAG = .TRUE.			! Set error flag
	CSTATUS = ISTATUS			! Store status

	ODEVMSG(1) = 9				! Message type
	ODEVMSG(2) = ISTATUS			! Status

	CALL XF$PKTBLD(XFDATA,			! Send packet
	1              XF$K_PKT_WRTCM,,,	! Func.,index,size
	2              ODEVMSG,8,,		! Msg, size, log area
	3              64,,,			! Int. if Q empty, no action routine
	4              STATUS)			! Status
	IF (.NOT. STATUS) CALL FATAL_ERROR(STATUS)

	STATUS=SYS$SETEF(%VAL(3))		! Wake up main level
	IF (.NOT. STATUS) CALL FATAL_ERROR(STATUS)

	RETURN
	END

	SUBROUTINE DR32_ERROR

C
C	This subroutine prints the I/O status block for DR32 errors
C	Note that this routine does not return
C

	INCLUDE 'DRCOPY.PRM/NOLIST'		! Parameters

C
C	Common variables and areas
C
	INTEGER*4 XFDATA(30)			! Context array
	BYTE MBFRS(BUFSIZ,NUM_MBFRS)		! Master buffers
	BYTE SBFRS(BUFSIZ,NUM_SBFRS)		! Slave buffers

	COMMON /MS_SHARE/  XFDATA,MBFRS,SBFRS

	INTEGER*4 IOSB(2)

	EQUIVALENCE(IOSB,XFDATA)

	WRITE(6,100)
100	FORMAT(1X,'%DRCOPY-F-DR32ERR, DR32 error')
	WRITE(6,200)IOSB(2)
200	FORMAT(1X,'IOSB(2) = ',Z8,' (Hex)')
	CALL LIB$STOP(%VAL(IOSB(1)))
	END

	SUBROUTINE FATAL_ERROR(STATUS)

C
C	This routine signals fatal errors and exits
C

	INTEGER*4 STATUS

	CALL LIB$STOP(%VAL(STATUS))
	END
