  Print symbiont for terminal-attached printer? 
 The Question is:
 
How do I create a run/execute que that will do a passthru function for
printing.  I.E.  I want to print from an application that only supports
printing to a que, but I am dialed in with a VT terminal with a printer port
via a modem connection.
 
 The Answer is:
 
  The OpenVMS Wizard is aware of freeware/shareware OpenVMS print symbionts
  available that can route printer output to a VT terminal with an attached
  printer.
 
  Effectively, this print symbiont allocates a connection to the target
  terminal device (typically using SHARE privilege), sends the terminal
  escape sequence to route output to the attached printer, sends the
  contents of the file to the terminal and thus through to the printer,
  then switches the output back to the terminal via an escape sequence.
 
  No, the OpenVMS Wizard does not have any immediate suggestions nor
  sources for this print symbiont.
 
  The attached symbiont was acquired from a posting on CompuServe many
  years ago, author unknown -- two versions are posted, the original
  OpenVMS VAX and an OpenVMS Alpha port.  (If you are or know of the
  author of this, please contact the OpenVMS Wizard.)  To build this
  symbiont, please follow the instructions in the OpenVMS manual set for
  the creation of customization of print symbionts, and the steps to
  configure the symbiont.
 
 
--------------------------------------------------------------------------------
	.TITLE SLAVE_SMB - An symbiont for terminal slave printers
 
;+
; This printer symbiont routes print jobs back to the user's terminal
; for printing on the terminal connected slave printer (if there is one
; and if the user is still logged on).  This enables slave printing to
; be seamlessly integrated into the VMS system as a queue.  If it is
; likely that more than one user will want to use this facility, then
; several such queues should be set up, all fed from the same generic
; queue.  The symbiont is multithreaded, supporting up to 16 streams.
 
; Note, however, that there is a complication in the case of clusters.
; The generic and multiple execution queues should (in the homogeneous
; case) be duplicated on each node with cluster unique names.  The slave
; queue complex should therefore be referenced through a cluster common
; logical pointing to a node's specific slave generic queue.
;-
 
;+
; Modification history
 
;  24-JAN-1990 : Created.
 
;  08-FEB-1990 : Single threaded version to named device.
 
;  09-FEB-1990 : Single threaded version to device named in /PARAMETER.
 
;  10-FEB-1990 : Legal device checking &amp; terminal state control added.
 
;  18-FEB-1990 : Modified to delay (in EXEC mode) between keyboard lock
;                and terminal -&gt; transparent print mode.
;		 Note _strange_ system bug fix.
 
;  02-JUN-1990 : LIB$WAIT replaced by $SETIMR &amp; $WAITFR_S to avoid kludge.
 
;  21-NOV-1990 : Set term to VT200-8bit and reset to VT100 mode, to allow
;                for printing of graphics character set.
 
;-
 
	.LIBRARY	'SYS$LIBRARY:LIB'
 
;+
; System definitions
;-
	$PSMDEF					; PSM definitions
	$SMBDEF					; SMB definitions
	$DSCDEF					; DSC definitions
	$OPCDEF					; OPC definitions
	$PRVDEF					; PRV definitions
	$DVIDEF					; GETDVI item codes
	$IODEF					; I/O function codes
	$SSDEF					; Status codes
	$DCDEF					; Device class symbols
 
;+
; Define argument offsets for user supplied services called by symbiont
;-
 
	CONTEXT		= 04			; Symbiont context
	WORK_AREA	= 08			; User context
	FUNC		= 12			; Function code
	FUNC_DESC	= 16			; Function dependent descriptor
	FUNC_ARG	= 20			; Function dependent argument
 
;+
; D_DESC - Macro to create dynamic descriptors
;-
 
	.MACRO	D_DESC
		.WORD	0			; DSC$W_LENGTH  = 0
		.BYTE	DSC$K_DTYPE_T		; DSC$B_DTYPE   = STRING
		.BYTE	DSC$K_CLASS_D		; DSC$B_CLASS   = DYNAMIC
		.LONG	0			; DSC$A_POINTER = 0
	.ENDM
 
;+
; STR_DESC - Macro to create string descriptors
;-
 
	.MACRO	STR_DESC	TEXT,?STR
	.WORD	%LENGTH(TEXT)
	.BYTE	DSC$K_DTYPE_T
	.BYTE	DSC$K_CLASS_D
	.LONG	STR
STR:	.ASCII	/TEXT/
	.ENDM
 
;+
; Symbiont data
;-
 
TT_CHAN:	.BLKW	1			; Channel to user's TT
 
TT_NAME:	 D_DESC				; TT name from PARAMETER_1
 
TT_TYPE:	.BLKL	1			; Device class from LIB$GETDVI
 
NL_NAME:	.ASCID	/NL:/			; Null device for junking
 
IO_STATUS:	.BLKQ	1			; I/O status block
 
;+
; Symbiont buffer pointers
;-
 
	B_ADDR:		.LONG	0
	B_SIZE:		.LONG	0
 
;+
; Create form feed string descriptor
;-
 
	FF = 12
 
	FORM_FEED:	.WORD	1
			.BYTE	DSC$K_DTYPE_T
			.BYTE	DSC$K_CLASS_D
			.LONG	FF_ADDR
	FF_ADDR:	.ASCII	&lt;FF&gt;
 
;+
; Terminal control sequences
;-
 
	ESC = 27
 
	LOCK_1:		.BYTE	ESC
			.ASCII	/[2h/		; lock keyboard
 
	LOCK_1_LEN	= &lt; . - LOCK_1 &gt;
 
	LOCK_0:		.BYTE	ESC
			.ASCII	/[2l/		; unlock keyboard
 
	LOCK_0_LEN	= &lt; . - LOCK_0 &gt;
 
	XPRINT_1:	.BYTE	ESC
			.ASCII	/[5i/		; transparent print on
 
	XPRINT_1_LEN	= &lt; . - XPRINT_1 &gt;
 
	XPRINT_0:	.BYTE	ESC
			.ASCII	/[4i/		; transparent print off
 
	XPRINT_0_LEN	= &lt; . - XPRINT_0 &gt;
 
	VT200_8BIT:	.BYTE	ESC
			.ASCII	/[62;2"p/	; VT200 8-bit Terminal mode on
			.BYTE	ESC
			.ASCII	/ G/		; 8-bit Trasmission mode on VT200
 
	VT200_8BIT_LEN	= &lt; . - VT200_8BIT &gt;
 
	VT100:		.BYTE	ESC
			.ASCII	/[61"p/		; VT100 Terminal mode on
 
	VT100_LEN	= &lt; . - VT100 &gt;
 
 
;+
; OPR_DESC - Macro to create operator message descriptors
;-
 
	.MACRO	OPR_DESC	TEXT,?OPR
	.WORD	%LENGTH(TEXT)+8
	.BYTE	DSC$K_DTYPE_T
	.BYTE	DSC$K_CLASS_D
	.LONG	OPR
OPR:	.BYTE	OPC$_RQ_RQST
	.BYTE	OPC$M_NM_CENTRL
	.WORD	0
	.LONG	0
	.ASCII	/TEXT/
	.ENDM
 
;+
; Create operator messages
;-
 
	BAD_TT:	OPR_DESC	&lt;Slave Symbiont - Invalid TERM device&gt;
 
;+
; Process name descriptor
;-
 
	PROC_NAME:	.ASCID	/Slave Symbiont/
 
;+
; Own storage for values passed by reference
;-
 
	CODE:		.LONG	0		; Service or item code
	STREAMS:	.LONG	1		; Number of simultaneous streams
	BUFSIZ:		.LONG	512		; Output buffer size
 
;+
; Own storage for symbiont status
;-
 
	COUNT:		.LONG	0		; # calls to COMPLETE
 
;+
; Privilege mask - NETMBX + BYPASS + LOG_IO + PHY_IO + CMEXEC
;-
 
	PRVMSK:		.LONG	PRV$M_NETMBX ! -
				PRV$M_BYPASS ! -
				PRV$M_LOG_IO ! -
				PRV$M_PHY_IO ! -
				PRV$M_CMEXEC
			.LONG	0
 
;+
; Main routine - Invoked at image startup
;-
 
	.ENTRY	SLAVE_SMB,^M&lt;&gt;			; Save nothing
 
;+
; Supply private output routine - PSM$K_OUTPUT
;-
 
	MOVZBL	#PSM$K_OUTPUT,CODE		; Set the service code
	PUSHAL	OUTPUT				; Address of modified routine
	PUSHAL	CODE				; Address of service code
	CALLS	#2,G^PSM$REPLACE		; Replace the routine
	BLBC	R0,10$				; Exit if any errors
 
;+
; Supply private input routine - PSM$K_JOB_COMPLETION
;-
 
	MOVZBL	#PSM$K_JOB_COMPLETION,CODE	; Set the service code
	PUSHAL	COMPLETE			; Address of modified routine
	PUSHAL	CODE				; Address of service code
	CALLS	#2,G^PSM$REPLACE		; Replace the routine
	BLBC	R0,10$				; Exit if any errors
 
;+
; Set privileges required for symbiont operation
;-
 
	$SETPRV_S -
		ENBFLG = #1, -
		PRVADR = PRVMSK
	BLBC	R0,10$
 
;+
; Set process name
;-
 
	$SETPRN_S -
		PRCNAM = PROC_NAME
	BLBC	R0,10$
 
;+
; Transfer control to the standard symbiont
;-
 
	PUSHAL	BUFSIZ				; Address of output buffer size
	PUSHAL	STREAMS				; Address of number of streams
	CALLS	#2,G^PSM$PRINT			; Invoke standard symbiont
	BLBC	R0,10$				; Exit if any errors
 
10$:	RET
 
;+
; Output routine - PSM$K_OUTPUT
;-
 
OUTPUT:		.WORD	0			; Save nothing
 
;+
; Check function code
;-
 
	CMPL	#PSM$K_START_TASK,@FUNC(AP)	; Start of task ?
	BNEQ	100$
	BRW	140$
100$:	CMPL	#PSM$K_CANCEL,@FUNC(AP)		; Cancel task ?
	BNEQ	110$
	BRW	150$
110$:	CMPL	#PSM$K_WRITE,@FUNC(AP)		; Write ?
	BNEQ	120$
	BRW	160$
120$:	CMPL	#PSM$K_WRITE_NOFORMAT,@FUNC(AP) ; Write unformatted ?
	BNEQ	130$
	BRW	170$
130$:	MOVL	#PSM$_FUNNOTSUP,R0		; Unsupported function
	RET					; Return to symbiont
 
;+
; PSM$K_START_TASK
;-
 
140$:	BSBW	OPEN_TERM			; Set up terminal chan, etc
	MOVL	#SS$_NORMAL,R0			; Errors handled in OPEN_TERM
	RET					; Return to symbiont
 
;+
; PSM$K_CANCEL
;-
 
150$:	BSBW	CLOSE_TERM			; Close down terminal chan, etc
	MOVL	#SS$_NORMAL,R0			; Errors handled in CLOSE_TERM
	RET					; Return to symbiont
 
;+
; PSM$K_WRITE
;-
 
160$:	PUSHAL	B_ADDR				; Address of symbiont buffer
	PUSHAL	B_SIZE				; Size of symbiont buffer
	PUSHL	FUNC_DESC(AP)			; Descriptor for string to send
	CALLS	#3,G^STR$ANALYZE_SDESC		; Get address &amp; size of string
	MOVZWL	B_SIZE,R1			; Get size
	$QIOW_S -				; QIO to terminal
		CHAN = TT_CHAN,-
		FUNC = #IO$_WRITEVBLK,-
		IOSB = IO_STATUS,-
		P1   = @B_ADDR,-
		P2   = R1,-
		P4   = #^X00000000		; No carriage control
	BSBW	IO_ERROR			; Check for IO errors
	CMPL	#0,COUNT			; Is this the last write
	BNEQ	161$				; No - branch round ...
	BSBW	CLOSE_TERM			; Close down terminal chan, etc
161$:	MOVL	#SS$_NORMAL,R0
	RET					; Return to symbiont
 
;+
; PSM$K_WRITE_NOFORMAT
;-
 
170$:	PUSHAL	B_ADDR				; See comments above ....
	PUSHAL	B_SIZE
	PUSHL	FUNC_DESC(AP)
	CALLS	#3,G^STR$ANALYZE_SDESC
	MOVZWL	B_SIZE,R1
	$QIOW_S -
		CHAN = TT_CHAN,-
		FUNC = #IO$_WRITEVBLK!IO$M_NOFORMAT,-
		IOSB = IO_STATUS,-
		P1   = @B_ADDR,-
		P2   = R1
	BSBW	IO_ERROR
	CMPL	#0,COUNT
	BNEQ	171$
	BSBW	CLOSE_TERM
171$:	MOVL	#SS$_NORMAL,R0
	RET					; Return to symbiont
 
;+
; Subroutine OPEN_TERM - Sets up terminal chan, etc.
;-
 
OPEN_TERM:
 
 
; Get PARAMETER_1 -&gt; TT_NAME.
 
 
	MOVZBL	#SMBMSG$K_PARAMETER_1,CODE	; Set item code
	PUSHAL	TT_NAME				; Address of descriptor
	PUSHAL	CODE				; Address of item code
	PUSHAL	@CONTEXT(AP)			; Address of symbiont ctx value
	CALLS	#3,G^PSM$READ_ITEM_DX		; Read PARAMETER_1
	BSBW	ERROR				; Check system service
 
 
; If TT_NAME is a terminal, assign a channel to it.
; If it isn't, send an OPCOM message and open a channel to NL:
 
 
	MOVZBL	#DVI$_DEVCLASS,CODE		; Item code to get device type
	PUSHAL	TT_TYPE				; Receives device type
	PUSHAL	TT_NAME				; Device name from /PARAMETER
	PUSHL	#0				; Channel not available yet
	PUSHAL	CODE				; Item code
	CALLS	#4,G^LIB$GETDVI			; Get device information
	BLBC	R0,190$				; Any errors, open NL:
	CMPL	#DC$_TERM,TT_TYPE		; Is device a terminal
	BNEQ	190$				; If not, open NL:
	BRW	191$				; OK - go on to next bit ...
 
190$:	BRW	195$
 
 
; Assign channel to TT:
 
 
191$:	$ASSIGN_S	DEVNAM	= TT_NAME,-	; TT:
			CHAN	= TT_CHAN	; TT channel
	BSBW	ERROR				; Check system service
 
 
; Set terminal to VT200 8bit mode
 
 
	$QIOW_S -
		CHAN = TT_CHAN,-
		FUNC = #IO$_WRITEVBLK!IO$M_NOFORMAT,-
		IOSB = IO_STATUS,-
		P1   = VT200_8BIT,-
		P2   = #VT200_8BIT_LEN
	BSBW	IO_ERROR			; Check for IO errors
 
 
; Lock keyboard.
 
 
	$QIOW_S -
		CHAN = TT_CHAN,-
		FUNC = #IO$_WRITEVBLK!IO$M_NOFORMAT,-
		IOSB = IO_STATUS,-
		P1   = LOCK_1,-
		P2   = #LOCK_1_LEN
	BSBW	IO_ERROR			; Check for IO errors
 
 
; Wait 3 seconds before setting up transparent printing.
 
 
	BSBW	DELAY_3
 
 
; Enter transparent print mode.
 
 
	$QIOW_S -
		CHAN = TT_CHAN,-
		FUNC = #IO$_WRITEVBLK!IO$M_NOFORMAT,-
		IOSB = IO_STATUS,-
		P1   = XPRINT_1,-
		P2   = #XPRINT_1_LEN
	BSBW	IO_ERROR			; Check for IO errors
 
	RSB
 
 
; Assign channel to NL: and send OPCOM message.
 
 
195$:	$ASSIGN_S	DEVNAM	= NL_NAME,-	; NL:
			CHAN	= TT_CHAN	; TT channel
 
	BSBW	ERROR				; Check system service
 
	$SNDOPR_S	MSGBUF	= BAD_TT,-	; Send OPCOM message
			CHAN	= #0
	BSBW	ERROR				; Check system service
 
	RSB
 
;+
; Subroutine CLOSE_TERM - Closes down terminal chan, etc.
;-
 
CLOSE_TERM:
 
; Exit transparent print mode.
 
 
	$QIOW_S -
		CHAN = TT_CHAN,-
		FUNC = #IO$_WRITEVBLK!IO$M_NOFORMAT,-
		IOSB = IO_STATUS,-
		P1   = XPRINT_0,-
		P2   = #XPRINT_0_LEN
	BSBW	IO_ERROR			; Check for IO errors
 
 
; Unlock keyboard.
 
 
	$QIOW_S -
		CHAN = TT_CHAN,-
		FUNC = #IO$_WRITEVBLK!IO$M_NOFORMAT,-
		IOSB = IO_STATUS,-
		P1   = LOCK_0,-
		P2   = #LOCK_0_LEN
	BSBW	IO_ERROR			; Check for IO errors
 
 
; Set term to VT100 mode
 
	$QIOW_S -
		CHAN = TT_CHAN,-
		FUNC = #IO$_WRITEVBLK!IO$M_NOFORMAT,-
		IOSB = IO_STATUS,-
		P1   = VT100,-
		P2   = #VT100_LEN
	BSBW	IO_ERROR			; Check for IO errors
 
 
; Deassign channel to TT
 
 
	$DASSGN_S	CHAN = TT_CHAN		; Deassign TT channel
 
	BSBW	ERROR				; Check system service
 
	RSB
 
 
;+
; Subroutine DELAY_3 - Waits 3 seconds.
;-
 
DELAY_DELTA:	.ASCID	/0 00:00:03.00/		; 3 seconds delta
 
DELAY_TIME:	.BLKQ	1			; Binary rep. of above
 
DELAY_3:
 
 
; Convert ASCII representation into 64 bit binary
 
 
	$BINTIM_S -
		TIMBUF	= DELAY_DELTA, -
		TIMADR	= DELAY_TIME
	BSBW	ERROR				; Check system service
 
 
; Set time to wait
 
 
	$SETIMR_S -
		EFN	= #2, -
		DAYTIM	= DELAY_TIME
	BSBW	ERROR				; Check system service
 
 
; Do the actual wait
 
 
	$WAITFR_S -
		EFN	= #2
	BSBW	ERROR				; Check system service
 
	RSB
 
;+
; Subroutine IO_ERROR / ERROR - Checks for errors
;-
 
IO_ERROR:				; Check system service and I/O status
	BLBC	R0,ERR			; If low bit clear then error
	MOVZWL	IO_STATUS,R0		; Test I/O status
 
ERROR:					; Check system service status (R0)
	BLBC	R0,ERR			; If low bit clear then error
	RSB				; No errors so return
 
ERR:
	$EXIT_S	-			; Error occurred so exit
		CODE = R0		;  with status
 
 
;+
; Input routine - PSM$K_JOB_COMPLETION
;-
 
COMPLETE:	.WORD	0			; Save nothing
 
;+
; Check function code
;-
 
	CMPL	#PSM$K_START_TASK,@FUNC(AP)	; Start of task ?
	BNEQ	200$
	BRW	220$
200$:	CMPL	#PSM$K_READ,@FUNC(AP)		; Read ?
	BNEQ	210$
	BRW	230$
210$:	MOVL	#PSM$_FUNNOTSUP,R0		; Unsupported function
	RET					; Return to symbiont
 
;+
; PSM$K_START_TASK
;-
 
220$:	MOVZBL	#2,COUNT			; Reset READ call count
	MOVL    #SS$_NORMAL,R0			; Set success exit code
	RET					; Return to symbiont
 
;+
; PSM$K_READ
;-
 
230$:	DECL	COUNT				; Decrement READ call count
	BEQL	240$				; Branch if second
 
 
; Send a &lt;FF&gt; to flush output
 
 
	PUSHAB	FORM_FEED			; &lt;FF&gt; descriptor
	PUSHL	FUNC_DESC(AP)			; Symbiont descriptor
	CALLS	#2,G^STR$COPY_DX		; Copy to symbiont buffer
	RET					; Return to symbiont
 
 
; Done - return PSM$_EOF
 
 
240$:	MOVL	#PSM$_EOF,R0			; Set EOF exit code
	RET					; Return to symbiont
 
;+
; End of SLAVE_SMB
;-
 
	.END	SLAVE_SMB
 
--------------------------------------------------------------------------------
	.TITLE SLAVE_SMB - An symbiont for terminal slave printers
 
;+
; This printer symbiont routes print jobs back to the user's terminal
; for printing on the terminal connected slave printer (if there is one
; and if the user is still logged on).  This enables slave printing to
; be seamlessly integrated into the VMS system as a queue.  If it is
; likely that more than one user will want to use this facility, then
; several such queues should be set up, all fed from the same generic
; queue.  The symbiont is multithreaded, supporting up to 16 streams.
 
; Note, however, that there is a complication in the case of clusters.
; The generic and multiple execution queues should (in the homogeneous
; case) be duplicated on each node with cluster unique names.  The slave
; queue complex should therefore be referenced through a cluster common
; logical pointing to a node's specific slave generic queue.
;-
 
;+
; Modification history
 
;  24-JAN-1990 : Created.
 
;  08-FEB-1990 : Single threaded version to named device.
 
;  09-FEB-1990 : Single threaded version to device named in /PARAMETER.
 
;  10-FEB-1990 : Legal device checking &amp; terminal state control added.
 
;  18-FEB-1990 : Modified to delay (in EXEC mode) between keyboard lock
;                and terminal -&gt; transparent print mode.
;		 Note _strange_ system bug fix.
 
;  02-JUN-1990 : LIB$WAIT replaced by $SETIMR &amp; $WAITFR_S to avoid kludge.
 
;  21-NOV-1990 : Set term to VT200-8bit and reset to VT100 mode, to allow
;                for printing of graphics character set.
 
;  1999-04-15	John Simmonds
;		Port to OpenVMS Alpha.
;-
 
	.LIBRARY	'SYS$LIBRARY:LIB'
 
;+
; System definitions
;-
	$PSMDEF					; PSM definitions
	$SMBDEF					; SMB definitions
	$DSCDEF					; DSC definitions
	$OPCDEF					; OPC definitions
	$PRVDEF					; PRV definitions
	$DVIDEF					; GETDVI item codes
	$IODEF					; I/O function codes
	$SSDEF					; Status codes
	$DCDEF					; Device class symbols
 
;+
; Define argument offsets for user supplied services called by symbiont
;-
 
	CONTEXT		= 04			; Symbiont context
	WORK_AREA	= 08			; User context
	FUNC		= 12			; Function code
	FUNC_DESC	= 16			; Function dependent descriptor
	FUNC_ARG	= 20			; Function dependent argument
 
;+
; D_DESC - Macro to create dynamic descriptors
;-
 
	.MACRO	D_DESC
		.WORD	0			; DSC$W_LENGTH  = 0
		.BYTE	DSC$K_DTYPE_T		; DSC$B_DTYPE   = STRING
		.BYTE	DSC$K_CLASS_D		; DSC$B_CLASS   = DYNAMIC
		.LONG	0			; DSC$A_POINTER = 0
	.ENDM
 
;+
; STR_DESC - Macro to create string descriptors
;-
 
	.MACRO	STR_DESC	TEXT,?STR
	.WORD	%LENGTH(TEXT)
	.BYTE	DSC$K_DTYPE_T
	.BYTE	DSC$K_CLASS_D
	.LONG	STR
STR:	.ASCII	/TEXT/
	.ENDM
 
;+
; Symbiont data
;-
.psect SMB_DATA NOEXE,RD,WRT,QUAD
 
TT_CHAN:	.BLKW	1			; Channel to user's TT
 
TT_NAME:	 D_DESC				; TT name from PARAMETER_1
 
TT_TYPE:	.BLKL	1			; Device class from LIB$GETDVI
 
NL_NAME:	.ASCID	/NL:/			; Null device for junking
 
IO_STATUS:	.BLKQ	1			; I/O status block
 
;+
; Symbiont buffer pointers
;-
 
	B_ADDR:		.LONG	0
	B_SIZE:		.LONG	0
 
;+
; Create form feed string descriptor
;-
 
	FF = 12
 
	FORM_FEED:	.WORD	1
			.BYTE	DSC$K_DTYPE_T
			.BYTE	DSC$K_CLASS_D
			.LONG	FF_ADDR
	FF_ADDR:	.ASCII	&lt;FF&gt;
 
;+
; Terminal control sequences
;-
 
	ESC = 27
 
	LOCK_1:		.BYTE	ESC
			.ASCII	/[2h/		; lock keyboard
 
	LOCK_1_LEN	= &lt; . - LOCK_1 &gt;
 
	LOCK_0:		.BYTE	ESC
			.ASCII	/[2l/		; unlock keyboard
 
	LOCK_0_LEN	= &lt; . - LOCK_0 &gt;
 
	XPRINT_1:	.BYTE	ESC
			.ASCII	/[5i/		; transparent print on
 
	XPRINT_1_LEN	= &lt; . - XPRINT_1 &gt;
 
	XPRINT_0:	.BYTE	ESC
			.ASCII	/[4i/		; transparent print off
 
	XPRINT_0_LEN	= &lt; . - XPRINT_0 &gt;
 
	VT200_8BIT:	.BYTE	ESC
			.ASCII	/[62;2"p/	; VT200 8-bit Terminal mode on
			.BYTE	ESC
			.ASCII	/ G/		; 8-bit Trasmission mode on VT200
 
	VT200_8BIT_LEN	= &lt; . - VT200_8BIT &gt;
 
	VT100:		.BYTE	ESC
			.ASCII	/[61"p/		; VT100 Terminal mode on
 
	VT100_LEN	= &lt; . - VT100 &gt;
 
 
;+
; OPR_DESC - Macro to create operator message descriptors
;-
 
	.MACRO	OPR_DESC	TEXT,?OPR
	.WORD	%LENGTH(TEXT)+8
	.BYTE	DSC$K_DTYPE_T
	.BYTE	DSC$K_CLASS_D
	.LONG	OPR
OPR:	.BYTE	OPC$_RQ_RQST
	.BYTE	OPC$M_NM_CENTRL
	.WORD	0
	.LONG	0
	.ASCII	/TEXT/
	.ENDM
 
;+
; Create operator messages
;-
 
	BAD_TT:	OPR_DESC	&lt;Slave Symbiont - Invalid TERM device&gt;
 
;+
; Process name descriptor
;-
 
	PROC_NAME:	.ASCID	/Slave Symbiont/
 
;+
; Own storage for values passed by reference
;-
 
	CODE:		.LONG	0		; Service or item code
	STREAMS:	.LONG	1		; Number of simultaneous streams
	BUFSIZ:		.LONG	512		; Output buffer size
 
;+
; Own storage for symbiont status
;-
 
	COUNT:		.LONG	0		; # calls to COMPLETE
 
;+
; Privilege mask - NETMBX + BYPASS + LOG_IO + PHY_IO + CMEXEC
;-
 
	PRVMSK:		.LONG	PRV$M_NETMBX ! -
				PRV$M_BYPASS ! -
				PRV$M_LOG_IO ! -
				PRV$M_PHY_IO ! -
				PRV$M_CMEXEC
			.LONG	0
 
;+
; Main routine - Invoked at image startup
;-
.psect SMB_CODE EXE,RD,NOWRT,QUAD
 
;	.ENTRY	SLAVE_SMB,^M&lt;&gt;			; Save nothing
SLAVE_SMB::
	.call_entry home_args=&lt;TRUE&gt;,max_args=&lt;6&gt;,preserve=&lt;&gt;
 
;+
; Supply private output routine - PSM$K_OUTPUT
;-
 
	MOVZBL	#PSM$K_OUTPUT,CODE		; Set the service code
	PUSHAL	OUTPUT				; Address of modified routine
	PUSHAL	CODE				; Address of service code
	CALLS	#2,G^PSM$REPLACE		; Replace the routine
	BLBC	R0,10$				; Exit if any errors
 
;+
; Supply private input routine - PSM$K_JOB_COMPLETION
;-
 
	MOVZBL	#PSM$K_JOB_COMPLETION,CODE	; Set the service code
	PUSHAL	COMPLETE			; Address of modified routine
	PUSHAL	CODE				; Address of service code
	CALLS	#2,G^PSM$REPLACE		; Replace the routine
	BLBC	R0,10$				; Exit if any errors
 
;+
; Set privileges required for symbiont operation
;-
 
	$SETPRV_S -
		ENBFLG = #1, -
		PRVADR = PRVMSK
	BLBC	R0,10$
 
;+
; Set process name
;-
 
	$SETPRN_S -
		PRCNAM = PROC_NAME
	BLBC	R0,10$
 
;+
; Transfer control to the standard symbiont
;-
 
	PUSHAL	BUFSIZ				; Address of output buffer size
	PUSHAL	STREAMS				; Address of number of streams
	CALLS	#2,G^PSM$PRINT			; Invoke standard symbiont
	BLBC	R0,10$				; Exit if any errors
 
10$:	RET
 
;+
; Output routine - PSM$K_OUTPUT
;-
 
OUTPUT:		;.WORD	0			; Save nothing
	.call_entry home_args=&lt;TRUE&gt;,max_args=&lt;6&gt;,preserve=&lt;&gt;
 
;+
; Check function code
;-
 
	CMPL	#PSM$K_START_TASK,@FUNC(AP)	; Start of task ?
	BNEQ	100$
	BRW	140$
100$:	CMPL	#PSM$K_CANCEL,@FUNC(AP)		; Cancel task ?
	BNEQ	110$
	BRW	150$
110$:	CMPL	#PSM$K_WRITE,@FUNC(AP)		; Write ?
	BNEQ	120$
	BRW	160$
120$:	CMPL	#PSM$K_WRITE_NOFORMAT,@FUNC(AP) ; Write unformatted ?
	BNEQ	130$
	BRW	170$
130$:	MOVL	#PSM$_FUNNOTSUP,R0		; Unsupported function
	RET					; Return to symbiont
 
;+
; PSM$K_START_TASK
;-
 
140$:	BSBW	OPEN_TERM			; Set up terminal chan, etc
	MOVL	#SS$_NORMAL,R0			; Errors handled in OPEN_TERM
	RET					; Return to symbiont
 
;+
; PSM$K_CANCEL
;-
 
150$:	BSBW	CLOSE_TERM			; Close down terminal chan, etc
	MOVL	#SS$_NORMAL,R0			; Errors handled in CLOSE_TERM
	RET					; Return to symbiont
 
;+
; PSM$K_WRITE
;-
 
160$:	PUSHAL	B_ADDR				; Address of symbiont buffer
	PUSHAL	B_SIZE				; Size of symbiont buffer
	PUSHL	FUNC_DESC(AP)			; Descriptor for string to send
	CALLS	#3,G^STR$ANALYZE_SDESC		; Get address &amp; size of string
	MOVZWL	B_SIZE,R1			; Get size
	$QIOW_S -				; QIO to terminal
		CHAN = TT_CHAN,-
		FUNC = #IO$_WRITEVBLK,-
		IOSB = IO_STATUS,-
		P1   = @B_ADDR,-
		P2   = R1,-
		P4   = #^X00000000		; No carriage control
	BSBW	IO_ERROR			; Check for IO errors
	CMPL	#0,COUNT			; Is this the last write
	BNEQ	161$				; No - branch round ...
	BSBW	CLOSE_TERM			; Close down terminal chan, etc
161$:	MOVL	#SS$_NORMAL,R0
	RET					; Return to symbiont
 
;+
; PSM$K_WRITE_NOFORMAT
;-
 
170$:	PUSHAL	B_ADDR				; See comments above ....
	PUSHAL	B_SIZE
	PUSHL	FUNC_DESC(AP)
	CALLS	#3,G^STR$ANALYZE_SDESC
	MOVZWL	B_SIZE,R1
	$QIOW_S -
		CHAN = TT_CHAN,-
		FUNC = #IO$_WRITEVBLK!IO$M_NOFORMAT,-
		IOSB = IO_STATUS,-
		P1   = @B_ADDR,-
		P2   = R1
	BSBW	IO_ERROR
	CMPL	#0,COUNT
	BNEQ	171$
	BSBW	CLOSE_TERM
171$:	MOVL	#SS$_NORMAL,R0
	RET					; Return to symbiont
 
;+
; Subroutine OPEN_TERM - Sets up terminal chan, etc.
;-
 
OPEN_TERM:
	.jsb_entry
 
; Get PARAMETER_1 -&gt; TT_NAME.
 
 
	MOVZBL	#SMBMSG$K_PARAMETER_1,CODE	; Set item code
	PUSHAL	TT_NAME				; Address of descriptor
	PUSHAL	CODE				; Address of item code
	PUSHAL	@CONTEXT(AP)			; Address of symbiont ctx value
	CALLS	#3,G^PSM$READ_ITEM_DX		; Read PARAMETER_1
	BSBW	ERROR				; Check system service
 
 
; If TT_NAME is a terminal, assign a channel to it.
; If it isn't, send an OPCOM message and open a channel to NL:
 
 
	MOVZBL	#DVI$_DEVCLASS,CODE		; Item code to get device type
	PUSHAL	TT_TYPE				; Receives device type
	PUSHAL	TT_NAME				; Device name from /PARAMETER
	PUSHL	#0				; Channel not available yet
	PUSHAL	CODE				; Item code
	CALLS	#4,G^LIB$GETDVI			; Get device information
	BLBC	R0,190$				; Any errors, open NL:
	CMPL	#DC$_TERM,TT_TYPE		; Is device a terminal
	BNEQ	190$				; If not, open NL:
	BRW	191$				; OK - go on to next bit ...
 
190$:	BRW	195$
 
 
; Assign channel to TT:
 
 
191$:	$ASSIGN_S	DEVNAM	= TT_NAME,-	; TT:
			CHAN	= TT_CHAN	; TT channel
	BSBW	ERROR				; Check system service
 
 
; Set terminal to VT200 8bit mode
 
 
	$QIOW_S -
		CHAN = TT_CHAN,-
		FUNC = #IO$_WRITEVBLK!IO$M_NOFORMAT,-
		IOSB = IO_STATUS,-
		P1   = VT200_8BIT,-
		P2   = #VT200_8BIT_LEN
	BSBW	IO_ERROR			; Check for IO errors
 
 
; Lock keyboard.
 
 
	$QIOW_S -
		CHAN = TT_CHAN,-
		FUNC = #IO$_WRITEVBLK!IO$M_NOFORMAT,-
		IOSB = IO_STATUS,-
		P1   = LOCK_1,-
		P2   = #LOCK_1_LEN
	BSBW	IO_ERROR			; Check for IO errors
 
 
; Wait 3 seconds before setting up transparent printing.
 
 
	BSBW	DELAY_3
 
 
; Enter transparent print mode.
 
 
	$QIOW_S -
		CHAN = TT_CHAN,-
		FUNC = #IO$_WRITEVBLK!IO$M_NOFORMAT,-
		IOSB = IO_STATUS,-
		P1   = XPRINT_1,-
		P2   = #XPRINT_1_LEN
	BSBW	IO_ERROR			; Check for IO errors
 
	RSB
 
 
; Assign channel to NL: and send OPCOM message.
 
 
195$:	$ASSIGN_S	DEVNAM	= NL_NAME,-	; NL:
			CHAN	= TT_CHAN	; TT channel
 
	BSBW	ERROR				; Check system service
 
	$SNDOPR_S	MSGBUF	= BAD_TT,-	; Send OPCOM message
			CHAN	= #0
	BSBW	ERROR				; Check system service
 
	RSB
 
;+
; Subroutine CLOSE_TERM - Closes down terminal chan, etc.
;-
 
CLOSE_TERM:
	.jsb_entry
 
; Exit transparent print mode.
 
 
	$QIOW_S -
		CHAN = TT_CHAN,-
		FUNC = #IO$_WRITEVBLK!IO$M_NOFORMAT,-
		IOSB = IO_STATUS,-
		P1   = XPRINT_0,-
		P2   = #XPRINT_0_LEN
	BSBW	IO_ERROR			; Check for IO errors
 
 
; Unlock keyboard.
 
 
	$QIOW_S -
		CHAN = TT_CHAN,-
		FUNC = #IO$_WRITEVBLK!IO$M_NOFORMAT,-
		IOSB = IO_STATUS,-
		P1   = LOCK_0,-
		P2   = #LOCK_0_LEN
	BSBW	IO_ERROR			; Check for IO errors
 
 
; Set term to VT100 mode
 
	$QIOW_S -
		CHAN = TT_CHAN,-
		FUNC = #IO$_WRITEVBLK!IO$M_NOFORMAT,-
		IOSB = IO_STATUS,-
		P1   = VT100,-
		P2   = #VT100_LEN
	BSBW	IO_ERROR			; Check for IO errors
 
 
; Deassign channel to TT
 
 
	$DASSGN_S	CHAN = TT_CHAN		; Deassign TT channel
 
	BSBW	ERROR				; Check system service
 
	RSB
 
 
;+
; Subroutine DELAY_3 - Waits 3 seconds.
;-
.save_psect
.psect SMB_DATA NOEXE,RD,WRT,QUAD
DELAY_DELTA:	.ASCID	/0 00:00:03.00/		; 3 seconds delta
 
DELAY_TIME:	.BLKQ	1			; Binary rep. of above
.restore_psect
 
DELAY_3:
	.jsb_entry
 
; Convert ASCII representation into 64 bit binary
 
 
	$BINTIM_S -
		TIMBUF	= DELAY_DELTA, -
		TIMADR	= DELAY_TIME
	BSBW	ERROR				; Check system service
 
 
; Set time to wait
 
 
	$SETIMR_S -
		EFN	= #2, -
		DAYTIM	= DELAY_TIME
	BSBW	ERROR				; Check system service
 
 
; Do the actual wait
 
 
	$WAITFR_S -
		EFN	= #2
	BSBW	ERROR				; Check system service
 
	RSB
 
;+
; Subroutine IO_ERROR / ERROR - Checks for errors
;-
 
IO_ERROR:				; Check system service and I/O status
	.jsb_entry
	BLBC	R0,ERR			; If low bit clear then error
	MOVZWL	IO_STATUS,R0		; Test I/O status
 
ERROR:					; Check system service status (R0)
	.jsb_entry
	BLBC	R0,ERR			; If low bit clear then error
	RSB				; No errors so return
 
ERR:
	.jsb_entry input=&lt;R0&gt;
	$EXIT_S	-			; Error occurred so exit
		CODE = R0		;  with status
	rsb
 
 
;+
; Input routine - PSM$K_JOB_COMPLETION
;-
 
COMPLETE:	;.WORD	0			; Save nothing
	.call_entry preserve=&lt;&gt;
 
;+
; Check function code
;-
 
	CMPL	#PSM$K_START_TASK,@FUNC(AP)	; Start of task ?
	BNEQ	200$
	BRW	220$
200$:	CMPL	#PSM$K_READ,@FUNC(AP)		; Read ?
	BNEQ	210$
	BRW	230$
210$:	MOVL	#PSM$_FUNNOTSUP,R0		; Unsupported function
	RET					; Return to symbiont
 
;+
; PSM$K_START_TASK
;-
 
220$:	MOVZBL	#2,COUNT			; Reset READ call count
	MOVL    #SS$_NORMAL,R0			; Set success exit code
	RET					; Return to symbiont
 
;+
; PSM$K_READ
;-
 
230$:	DECL	COUNT				; Decrement READ call count
	BEQL	240$				; Branch if second
 
 
; Send a &lt;FF&gt; to flush output
 
 
	PUSHAB	FORM_FEED			; &lt;FF&gt; descriptor
	PUSHL	FUNC_DESC(AP)			; Symbiont descriptor
	CALLS	#2,G^STR$COPY_DX		; Copy to symbiont buffer
	RET					; Return to symbiont
 
 
; Done - return PSM$_EOF
 
 
240$:	MOVL	#PSM$_EOF,R0			; Set EOF exit code
	RET					; Return to symbiont
 
;+
; End of SLAVE_SMB
;-
 
	.END	SLAVE_SMB
 
 
