	SUBROUTINE WRITE_REMOTE (BUFFER, NBYTES)
C
C	This subroutine is used to write a buffer to the remote.
C
	INCLUDE 'COM.INC/NOLIST'

	LOGICAL*1 BUFFER(1)
	INTEGER BYTES, NBYTES, STATUS
	CHARACTER*(*) MODULE_NAME

	PARAMETER (MODULE_NAME = 'WRITE_REMOTE')

	BYTES = NBYTES + 1		! Adjust the byte count.
	BUFFER(BYTES) = CR		! Append Terminator.
	GO TO 100			! And continue ...
C
C	The next entry is used to write the buffer without appending
C	a carriage return to the end of the message.
C
	ENTRY WRITE_BYTE (BUFFER, NBYTES)
	BYTES = NBYTES			! Copy the byte count.

100	CALL WRITE_DEBUG (MODULE_NAME, BUFFER, BYTES)
	STATUS = SYS$QIOW (%VAL(REFN_OUT),%VAL(RCHAN_OUT),
	1		%VAL(IO$_WRITELBLK + IO$M_NOFORMAT),
	1		XIOSB,,,BUFFER,%VAL(BYTES),,,,)
	CALL CHECK_STATUS (MODULE_NAME, STATUS)
	RETURN
	END

	INTEGER FUNCTION READ_REMOTE (BUFFER, NBYTES)
C
C	This function is called to read a line from the remote.
C
	INCLUDE 'COM.INC/NOLIST'

	CHARACTER*(*) MODULE_NAME
	PARAMETER (MODULE_NAME = 'READ_REMOTE')

	LOGICAL*1 BUFFER(1)
	INTEGER NBYTES, STATUS

100	IF (CONTROLC_TYPED) RETURN	! Return if aborted.
	BUFFER(1) = 0			! Initialize the first byte.
	STATUS = SYS$QIOW(%VAL(REFN_IN),%VAL(RCHAN_IN),
	1		%VAL(IO$_READLBLK + IO$M_NOECHO + IO$M_TIMED),
	1		RIOSB,,,BUFFER,%VAL(NBYTES),
	1		%VAL(TIMEOUT_COUNT),TERMPTR,,)

	READ_REMOTE = STATUS		! Copy the directive status.
	RBYTE_COUNT = RIOSB(2)		! Save the byte count.
	IF (.NOT. CHECK_STATUS (MODULE_NAME, STATUS)) RETURN
	READ_REMOTE = RIOSB(1)		! Pass back I/O status.
	IF (READ_REMOTE) THEN
	    CALL WRITE_DEBUG (MODULE_NAME, BUFFER, RBYTE_COUNT+1)
	ELSE
	    CALL WRITE_DEBUG (MODULE_NAME, BUFFER, RBYTE_COUNT)
	ENDIF
C
C	Check for various errors:
C
	IF     (RIOSB(1) .EQ. SS$_TIMEOUT) THEN		! Timeout error ?
		TIMEOUTS = TIMEOUTS + 1			! Yes, count it.
		GO TO 200				! And continue ...
	ELSEIF (RIOSB(1) .EQ. SS$_PARITY) THEN		! Parity error ?
		PARITY_ERRORS = PARITY_ERRORS + 1	! Yes, count it,
		GO TO 200				! And continue ...
	ELSEIF (RIOSB(1) .EQ. SS$_DATAOVERUN) THEN	! Data overrun ?
		OVERRUN_ERRORS = OVERRUN_ERRORS + 1	! Yes, count it.
		GO TO 200				! And continue ...
	ELSE
		IF (RIOSB(1) .NE. SS$_ABORT) THEN
		    CALL CHECK_STATUS (MODULE_NAME, READ_REMOTE)
		ENDIF
	ENDIF
	RETURN
C
C	Here for timeout and hardware errors.
C
200	IF (.NOT. DUMP_MODE) THEN
	    CALL WAIT_TILL_IDLE (MODULE_NAME,BUFFER) ! Wait until idle.
	    BUFFER(1) = 0			! Force bad transmission
	    RBYTE_COUNT = 0			!  by clearing buffer & BC.
	ENDIF
	RETURN
	END

	LOGICAL FUNCTION GET_RESPONSE (CODE, DISPLAY)
C
C	This subroutine gets a response from the host, makes sure it's
C	valid, then passes the code back for the subroutine to do further
C	processing.
C
C	Inputs:
C		CODE - Buffer to return code to.
C		DISPLAY - Controls whether the error should be displayed.
C
C	Outputs:
C		Returns success if a valid code was received,
C		  else returns failure if CTRL/C was typed, the read
C		  function was canceled, a CANcel code was received,
C		  or the retry limit was exceeded.
C
C	Action taken:
C	o  if code is ACK, NAK, ENQ, CAN, EOF, or EOT, return code.
C	o  else presume the response was garbled, send ENQ to the
C	   host, and loop to get retransmitted code.
C
	INCLUDE 'COM.INC/NOLIST'

	CHARACTER*(*) MODULE_NAME

	PARAMETER (MODULE_NAME = 'GET_RESPONSE')
	LOGICAL DISPLAY, REPORT_ERROR
	LOGICAL*1 CODE(1)

	GET_RESPONSE = .FALSE.			! Presume bad response.
100	IF (CONTROLC_TYPED) RETURN		! Return bad status.
	IF (READ_REMOTE (RBUFFER,READ_SIZE) .EQ. SS$_ABORT) RETURN
	CODE(1) = RBUFFER(1)			! Copy the received code.
	IF (CODE(1) .EQ. ACK .OR. CODE(1) .EQ. NAK) GO TO 200
	IF (CODE(1) .EQ. ENQ .OR. CODE(1) .EQ. CAN) GO TO 200
	IF (CODE(1) .EQ. EOF .OR. CODE(1) .EQ. EOT) GO TO 200
C
C	Invalid control code received.
C
	RETRY = REPORT_ERROR (DISPLAY)	! Report the error.
C***	CALL WRITE_DEBUG (MODULE_NAME, RBUFFER, RBYTE_COUNT)
	CALL WAIT_TILL_IDLE (MODULE_NAME, RBUFFER) ! Wait until remote is idle.
	IF (RETRY) THEN
		CALL SEND_ENQ()			! Send remote an ENQuire,
		GO TO 100			!   and try again.
	ENDIF

150	RETURN
C
C	The received code can optionally be followed by a text message.
C	If we received more than the code, then we output the message.
C
200	IF (RBYTE_COUNT .GT. 1)
	1	CALL WRITE_BUFFER (RBUFFER(2),RBYTE_COUNT-1)
	IF (CODE(1) .NE. CAN) THEN
		GET_RESPONSE = .TRUE.	! Show good response.
	ENDIF
	RETURN
	END

	SUBROUTINE WRITE_BUFFER (BUFFER, BYTES)
C
C	This subroutine generates a string descriptor from the input
C	buffer and byte count and then writes the buffer to the terminal
C	and the log file.
C
	IMPLICIT NONE

	LOGICAL*1 BUFFER(1)
	INTEGER*4 BYTES, BUFF_DESC(2)

	IF (BYTES .EQ. 0) RETURN
C
C	Fill in the string descriptor with the address and byte count.
C
	BUFF_DESC(1) = BYTES		! Initialize the count
	BUFF_DESC(2) = %LOC(BUFFER)	! 	and the address.
	CALL WRITE_BUFF (BUFF_DESC)	! Write buffer to terminal/log file.
	RETURN
	END

	SUBROUTINE WRITE_DEBUG (MODULE, BUFFER, BYTES)
C
C	This subroutine writes the contents of the buffer received
C	from the remote to the terminal and to the log file.
C
	INCLUDE 'COM.INC/NOLIST'

	CHARACTER*(*) MODULE
	LOGICAL*1 BUFFER(1)
	INTEGER*4 BYTES, BUFF_DESC(2)

	IF (.NOT. DEBUG_MODE) RETURN		! Debug mode is disabled.
C
C	Write some debugging information:
C
	CALL SYS$FAO ('*** Module name: !AS, byte count = !UW ***!/',
	1		SIZE, SCRATCH,
	1		MODULE, %VAL(BYTES) )
C
C	If the protocol is XMODEM, write to the log file only since the
C	protocol has binary data in it that screws up the terminal screen.
C
	IF (PROTOCOL .EQ. XMODEM) THEN
	    CALL WRITE_LOGFILE (%REF(SCRATCH), SIZE)
	ELSE
	    CALL WRITE_BUFF (SCRATCH(1:SIZE))
	ENDIF
C
C	Fill in the string descriptor with the address and byte count.
C
	IF (PROTOCOL .EQ. XMODEM) THEN
	    CALL WRITE_LOGFILE (BUFFER, BYTES)	! Write to the log file.
	ELSE
	    BUFF_DESC(1) = BYTES		! Initialize the count
	    BUFF_DESC(2) = %LOC(BUFFER)		! 	and the address.
	    CALL WRITE_BUFF (BUFF_DESC)		! Write to terminal/log file.
	ENDIF
C
C	Show end of this buffer.
C
	CALL SYS$FAO ('!/*** End of Buffer ***!/', SIZE, SCRATCH)
	IF (PROTOCOL .EQ. XMODEM) THEN
	    CALL WRITE_LOGFILE (%REF(SCRATCH), SIZE)
	ELSE
	    CALL WRITE_BUFF (SCRATCH(1:SIZE))
	ENDIF
	RETURN
	END

	SUBROUTINE WAIT_TILL_IDLE (MODULE, BUFFER)
C
C	This subroutine is called to wait until the remote port becomes idle.
C	This routine is used instead of clearing the typeahead buffer so we
C	can dump the remote input to the terminal and logfile if debug mode
C	is enabled.
C
	INCLUDE 'COM.INC/NOLIST'

	CHARACTER*(*) MODULE, MODULE_NAME
	LOGICAL*1 BUFFER(1)

	PARAMETER (MODULE_NAME = 'WAIT_TILL_IDLE')
	INTEGER TMO, STATUS, BYTES
	DATA TMO /2/
C
C	Read data from the remote until we timeout.
C
	STATUS = SYS$QIOW (%VAL(REFN_IN),%VAL(RCHAN_IN),
	1		%VAL(IO$_TTYREADALL + IO$M_NOECHO + IO$M_TIMED),
	1	RIOSB,,,BUFFER(1),%VAL(READ_SIZE),%VAL(TMO),NOTERM,,)
	IF (.NOT. CHECK_STATUS (MODULE_NAME, STATUS)) RETURN
	STATUS = RIOSB(1)		! Copy the I/O status code.
	BYTES = RIOSB(2)		! Copy the byte count.
	IF (.NOT. STATUS) THEN		! Timeout is only error expected.
	    IF ((STATUS .NE. SS$_TIMEOUT) .AND. (STATUS .NE. SS$_ABORT)) THEN
		CALL CHECK_STATUS (MODULE_NAME, STATUS)
	    ENDIF
	ENDIF
	IF (BYTES .GT. 0) CALL WRITE_DEBUG (MODULE, BUFFER, BYTES)
	RETURN
	END
