Google
 

Trailing-Edge - PDP-10 Archives - BB-H348C-RM_1982 - swskit-v21/nvt/nrtsrv.mac
There are 20 other files named nrtsrv.mac in the archive. Click here to see a list.
	TITLE	NRTSRV - Program to provide remote network login service




;COPYRIGHT (C) 1979 BY
;DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASS.
;
;
;THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND COPIED
;ONLY  IN  ACCORDANCE  WITH  THE  TERMS  OF  SUCH LICENSE AND WITH THE
;INCLUSION OF THE ABOVE COPYRIGHT NOTICE.  THIS SOFTWARE OR ANY  OTHER
;COPIES THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY
;OTHER PERSON.  NO TITLE TO AND OWNERSHIP OF THE  SOFTWARE  IS  HEREBY
;TRANSFERRED.
;
;
;THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT  NOTICE
;AND  SHOULD  NOT  BE  CONSTRUED  AS A COMMITMENT BY DIGITAL EQUIPMENT
;CORPORATION.
;
;DIGITAL ASSUMES NO RESPONSIBILITY FOR THE USE OR RELIABILITY  OF  ITS
;SOFTWARE ON EQUIPMENT WHICH IS NOT SUPPLIED BY DIGITAL.
	SALL			; Keep listing uncluttered, no macro expansion
	SEARCH	MONSYM,MACSYM	; Universals to search
	.REQUIRE SYS:MACREL	; Make LINK load these automatically

;
; This program will allow a person to login to the system on which it is
; being run from a remote node on a DECnet network. It demonstrates use
; of of the TOPS-20 DECnet functionality in a program which performs a
; real and useful function. The person desiring to login to this system
; must run the NRT20 program, if on a TOPS-20 node, or a program of
; identical functionality on a node with another operating system.
;
	SUBTTL	DECLARED SYMBOLS

DOROUT==1			; If a one, do route through
STATPG==300			; WHERE TO MAP STAT BUFFERS
NETADR==STATPG_^D9		; ADDRESS OF FROM NET STATS
PTYADR==NETADR+1000

	T1==1			; AC definitions
	T2==2
	T3==3
	T4==4
	CX==16
	P==17
	F==0			; Flags

	MAXFRK==^D8		; Maximum number of server sub processes
	OLDFRK==1		; # 200 servers to be used
	SRVOSZ==50		; Output buffer size in words
	MAXOUT==SRVOSZ*4	; Maximum number of output characters
	SRVSIZ==10		; Size of server buffer in words
	NPDL==40		; Size of pushdown list
	DATADR==5000		; Address to map status file into
	SUBTTL	IMPURE STORAGE

FRKIDX:	BLOCK 1			; Index for fork into FORKS
SAVAC:	BLOCK 4			; AC save area
DEVNAM:	BLOCK 2			; For building PTY names
PTYJFN:	BLOCK 1			; The JFN of the PTY
TTYJFN:	BLOCK 1			; TTY designator
SRVJFN:	BLOCK 1			; The JFN of the network connection
OPTDAT:	BLOCK 4			; Optional data buffer
GTBFR:	BLOCK 20		; File name buffer
SRVINP:	BLOCK SRVSIZ		; Holds input characters
OUTPUT:	BLOCK 1			; Output buffer pointer
OUCNT:	BLOCK 1
PC:	BLOCK 1			; The software interrupt PC save area
SRVOUP:	BLOCK SRVOSZ		; Holds ouptut data
HSTNAM:	BLOCK 2			; Host name string
PDL:	BLOCK NPDL		; Pushdown list (stack)

	RELOC 1000-140		; Start on page boundary

FORKS:	BLOCK MAXFRK
STATFL:	BLOCK 1			;HOLD JFN FOR STAT FILE
LEVTAB:	PC			; Where to stash the PC
CHNTAB:	1,,GOTCI		; Network connect received interrupt routine
	1,,GOTDAT		; Network data available interrupt routine
	SUBTTL	BEGIN - Start of NRTSRV program

BEGIN:	RESET			; Initialize ourselves
	MOVE T1,[SIXBIT /NRTSRV/] ; Our name in SIXBIT
	SETNM			; Set jobs private name
	HRROI T1,[ASCIZ /SRV/]	; See if network exists
	STDEV			; By checking for SRV device
	 JRST [	SETOM T1		; Doesn't exist, so go away
		LGOUT			; By logging out this job
		 JFCL			; Should always work, but...
		HALTF]			; Either way, go away
	MOVEI T1,.FHSLF		; This process
	RPCAP			; Get our capabilities
	MOVE T3,T2		; Copy them
	EPCAP			; Enable all capabilities we have
	MOVX T1,GJ%SHT
	HRROI T2,[ASCIZ /SYSTEM:NRTSRV-STATS.BIN/]
	GTJFN			;GET STAT FILE HANDLE
	 JRST NOSTAT		;IF FAILED, GIVE IT UP
	MOVEM T1,STATFL		;SAVE JFN
	MOVX T2,OF%RD!OF%WR!OF%THW ;ACCESS BITS
	OPENF			;OPEN 'ER UP
	 ERJMP [MOVE T1,STATFL	;OOPS
		RLJFN
		 NOP
		SETZM STATFL	;NO STATS
		JRST .+1]	;CONTINUE
NOSTAT:	MOVEI F,OLDFRK
	MOVSI T4,-MAXFRK	; Maximum processes to create
FRKDO:	MOVX T1,CR%CAP		; Give them the same capabilities we have
	SETZM T2		; No AC's
	CFORK			; Create a process
	 ERJMP WTFRK		; If no more, done
	MOVEM T1,FORKS(T4)	; Save process handle
	SOSL F			; Want an old server?
	HRROS FORKS(T4)		; Yes, this is one of them
	MOVEI T2,0		; The ACs to load
	SFACS			; Set the ACs
	HRLZ T2,T1		; Process handle of destination
	HRRI T2,1		; Start at page 1 in destination process
	MOVE T1,[.FHSLF,,1]	; Map this process to it
	MOVEI T3,<DATADR/1000>	; Number of pages to do
	TXO T3,PM%CNT!PM%RD!PM%WR ; Make PMAP argument
	PMAP			; Map the process' address space
	HLRZ T1,T2		; Get process handle
	MOVEI T2,START0		; Start address
	SFORK			; Start the process up
FRKLOP:	AOBJN T4,FRKDO		; Do all of them
WTFRK:	MOVX T1,GJ%SHT		; Get ready
	HRROI T2,[ASCIZ/SYSTEM:NRTSRV-CONNECTIONS.DATA;P777752/]
	GTJFN			; Get a JFN for status file
	 ERJMP DATDON		; Failed, skip onward
	MOVE T3,T1		; Save in case of failure
	MOVX T2,OF%RD!OF%WR	; Want read and write access
	OPENF			; Open it up
	 ERJMP DATDON		; Failed, go on
	TXO T1,CO%NRJ		; Preserve the JFN
	CLOSF			; Close file so it appears in a directory
	 ERJMP DATDON		; Failed
	MOVE T1,T3		; Restore JFN
	OPENF			; Open it again for real
	 ERJMP DATDON		; Failed
	MOVSI T1,(T1)		; JFN in left half and page 0 in right half
	MOVE T2,[.FHSLF,,DATADR/1000]	; Set up core page
	MOVX T3,PM%RD!PM%WR	; Want to read and write the page
	PMAP			; Map the file into core
	 ERJMP DATDON		; Failed
DATDON:	MOVE T1,[DATADR,,DATADR+1]	; Set up for BLT
	SETZM DATADR		; Zero first word of file
	BLT T1,DATADR+777	; And the rest to indicate no connections yet
	MOVEI T1,.FHSLF		; This fork
	SETZM T3		; No capabilities
	EPCAP			; Disable all capabilities
	MOVEI T1,.FHINF		; All the inferior processes
	RFORK			; Resume them
	WFORK			; Wait for any inferior to halt
	; ..
	SUBTTL	RSPROC - Restart inferior process after it halted

RSPROC:	MOVEI T1,.FHINF		; All the inferior processes
	FFORK			; Freeze them
	MOVSI T4,-MAXFRK	; Scan process table
DEDLOP:	SKIPN T1,FORKS(T4)	; This one exist?
	JRST DEDLO1		; No, go on
	HRRZS T1
	RFSTS			; Yes, get its status
	LOAD T1,RF%STS,T1	; Get its status
	CAIE T1,.RFHLT		; Halted?
	CAIN T1,.RFFPT		; Or error?
	JRST [	MOVE T1,FORKS(T4) 	; Yes, get process handle again
		SETZM FORKS(T4)		; Clear entry
		TLZN T1,-1
		TDZA F,F
		MOVEI F,1
		KFORK			; Kill the process
		HRRZS T4		; Create one more process
		MOVEI T1,.FHSLF		; Our process
		RPCAP			; Get our capabilities
		MOVE T3,T2		; Copy them
		EPCAP			; Enable all capabilities we have
		JRST FRKDO]		; Go back and create another process
DEDLO1:	AOBJN T4,DEDLOP		; Look at them all
	JRST WTFRK		; Wait some more
	SUBTTL	START - Start of code for inferior process'

;
; Note: This code runs as an inferior process to be the DECnet server
;	for NRT20. Any node on the network can make a connection to this
;	server to login to the system on which this program is running. A
;	separate copy of this code is run for each process which will be
;	a server. The DECnet object number of this server is 200. A TOPS-20
;	program can access the server with the DECnet file specification -
;	'DCN:node-200' where node is the node name to be logged in on. When
;	the server gets a connection from the network it obtains a PTY, and
;	sends a ^C character on it. It then accepts the network connection
;	and waits for data to arrive on the network link. Data arriving on
;	the network link is then output to the PTY and any data from the 
;	PTY is sent on the network link.
;

START0:	MOVEM T4,FRKIDX		; Stash the index away
	HRLZ T1,STATFL		;GET JFN OF STAT FILE
	JUMPE T1,START		;IF NOT FILE, DON'T MAP
	MOVE T2,[.FHSLF,,STATPG]
	MOVX T3,PM%CNT!PM%RD!PM%WR
	PMAP			;GET PAGES HERE
START:	CIS			; Clear interrupt system
	MOVE T4,FRKIDX		; Remember our index
	SKIPN T1,SRVJFN		; Have a server JFN yet?
	JRST START1		; No...
	TXO T1,CZ%ABT		; Yes, abort it
	CLOSF			; Close and abort the network connection
	 JFCL			; Ignore errors
	SETZM SRVJFN		; Clear the server JFN
START1:	SKIPN T1,PTYJFN		; Have a PTY yet?
	JRST START2		; No...
	DVCHR			; Yes, get characteristics
	 ERJMP NSTSTC		; Failed, go on
	HRRZ T1,T3		; Get unit number
	IMULI T1,2		; Two words per PTY in data file
	SETZB T2,T3		; Get a couple of zero words
	CAIGE T1,1000		; Verify unit number is reasonable
	DMOVEM T2,DATADR(T1)	; Yes, remove old host name from file
NSTSTC:	MOVE T1,PTYJFN		; Restore JFN
	TXO T1,CZ%ABT		; Abort it
	CLOSF			; Close the PTY
	 JFCL			; Ignore errors
START2:	MOVE P,[IOWD NPDL,PDL]	; Initialize the stack pointer
	MOVEI T1,.FHSLF		; Our process
	MOVE T2,[LEVTAB,,CHNTAB] ; Address of level and channel table
	SIR			; Set software interrupt table addresses
	MOVX T2,3B1		; Channel 0 and 1
	AIC			; Activate software interrupt channels
	EIR			; Enable software interrupt system
	MOVE T1,[POINT ^D8,SRVOUP] ; Initial output pointer
	MOVEM T1,OUTPUT		; Initialize the pointer
	MOVEI T1,MAXOUT		; Maximum output characters
	MOVEM T1,OUCNT		; Initialize the output count
	MOVEI T1,.FHSLF		; Our process
	RPCAP			; Get our capabilities
	MOVE T3,T2		; Copy them
	EPCAP			; Enable all capabilities we have
	MOVX T1,GJ%SHT		; Short form GTJFN
	HRROI T2,[ASCIZ /SRV:23/] ; The NRT server file spec
	SKIPGE FORKS(T4)
	HRROI T2,[ASCIZ /SRV:200/]
	GTJFN			; Get a JFN for it
	 JRST TRYAGN			; Failed, go try to recover
	MOVEM T1,SRVJFN		; Save the server JFN
	MOVX T2,<FLD(^D8,OF%BSZ)!OF%RD!OF%WR> ; 8 bit bytes, read/write access
	OPENF			; Open the server JFN
	 JRST [	MOVE T1,SRVJFN		; Failed, get server JFN
		RLJFN			; Release the JFN
		 JFCL			; Ignore errors
		SETZM SRVJFN		; Clear the server JFN
		JRST TRYAGN]		; And go try to recover
	MOVEI T1,.FHSLF		; Our process
	SETZB T2,T3		; No capabilities
	EPCAP			; Disable all capabilities
	MOVE T1,SRVJFN		; Get the server JFN
	MOVEI T2,.MOACN		; Assign interrupt system channel numbers
	MOVX T3,<FLD(0,MO%CDN)!FLD(1,MO%DAV)!FLD(.MOCIA,MO%INA)> ; Connect event on 0, data available on 1
	MTOPR			; Perform the device operation
	WAIT			; Wait for something to happen

CAPON:	MOVX T1,.FHSLF
	RPCAP
	MOVE T3,T2
	EPCAP
	RET

CAPOFF:	MOVX T1,.FHSLF
	SETZB T2,T3
	EPCAP
	RET
	SUBTTL	TRYAGN - Wait before trying to become a server again

TRYAGN:	MOVX T1,^D<1000*60*2>	; Two minutes in milliseconds
	DISMS			; Dismiss the process until time expires
	JRST START		; And go back to try again
	SUBTTL	GOTCI - Got a connect from the network

;
; Note: This routine is run at software interrupt level when a
;	remote node attempts to make a connection to us.

GOTCI:	SETZM F
	MOVE T1,SRVJFN		; GEt the JFN of the server
	HRROI T3,OPTDAT		; Where to put the optional data
	MOVEI T2,.MORDA
	MTOPR			; Read the optional data
	ERJMP START
	JUMPN T4,ROUTE		; If data, routing required
	CALL GETPTY		; Get a PTY to use
	JRST [	CIS			; Failed, clear the software interrupt system
		HRROI T1,[ASCIZ /
?No more PTYs on remote host
/]					; Point to message to tell what happened
		MOVE T2,SRVJFN		; Get the server JFN
		SETZM T3		; All of the string
		SOUTR			; Send message on network link
		 ERJMP .+1		; Ignore errors
		MOVE T1,SRVJFN		; Get the server JFN
		CLOSF			; Close the network connection
		 JRST START			; Failed, go back to recover
		SETZM SRVJFN		; Clear the server JFN
		JRST START]		; Go back to recover
	MOVEM T1,PTYJFN		; Save the PTY JFN
	MOVEM T2,TTYJFN		; Save the TTY designator
	MOVEI T2,"C"-100	; Get a Control-C (^C)
	BOUT			; Output it to PTY to start up job
DOACC:	MOVE T1,SRVJFN		; Get the server JFN
	MOVE T2,FRKIDX		; Get index
	SKIPGE FORKS(T2)	; Old or new?
	JRST [	MOVEI T2,.MOCC	; Old
		SETZB T3,T4
		MTOPR		; Just accept it
		ERJMP START
		JRST DOACC0]
	MOVE T2,[POINT 8,[BYTE (8)1,1,0,0,10,0,10,0]]
	MOVNI T3,10
	SOUTR
	ERJMP START
DOACC0:	MOVEI T1,PTYIN		; Address of PTY input routine
	MOVEM T1,PC		; Change the interrupt PC value to start
	SETZM HSTNAM		; Clear host name buffer
	MOVE T1,SRVJFN		; Get the server JFN
	MOVEI T2,.MORHN		; Read network host name
	HRROI T3,HSTNAM		; Pointer to buffer
	MTOPR			; Do the device operation
	 ERJMP NSTATS		; Go on if errors
	MOVE T1,PTYJFN		; Get PTY JFN
	DVCHR			; Return device characteristics
	 ERJMP NSTATS		; Failed
	HRRZ T1,T3		; Obtain PTY unit number
	IMULI T1,2		; File contains two words per PTY
	DMOVE T2,HSTNAM		; Get the HOST name
	CAIGE T1,1000		; Verify unit number is reasonable
	DMOVEM T2,DATADR(T1)	; Yes, place host name into file
	 ERJMP .+1		; Shouldn't fail
NSTATS:	GJINF			; Get current job information
	JUMPL T4,DONCI		; If terminal not attached to job, done
	HRROI T1,[ASCIZ /
NRTSRV- CONNECTION FROM /]	; Point to message to say who's connecting
	PSOUT			; Output to TTY
	HRROI T1,HSTNAM		; Pointer to host name buffer
	PSOUT			; Output the host name to TTY
	HRROI T1,[ASCIZ / ON /]	; Pointer to continuation of message text
	PSOUT			; Output to TTY
	MOVEI T1,.PRIOU		; TTY designator
	MOVE T2,PTYJFN		; Get the PTY JFN
	SETZM T3		; Full file specification format
	JFNS			; Output file spec associated with PTY JFN
	HRROI T1,[ASCIZ /
/]
	PSOUT			; End the message nicely
DONCI:	DEBRK			; Dismiss interrupt and resume processing PTY input
; Here if routing requested. First determine the next "hop"
; and then try to connect to that node while passing
; on the remaining routing string

ROUTE:	SKIPN [DOROUT]		; Want to honor route-through?
	JRST [	MOVEI T3,.DCX39	; No. say no path then
		JRST CLZCON]	; And refuse the connection
	SETZM T1
	IDPB T1,T3		; Tie off the optional data string
	MOVX F,1B0		; Note it is routed
	MOVE T4,[POINT 7,OPTDAT] ; Point to optional data
ROUTE1:	ILDB T1,T4		; Get next byte
	JUMPE T1,NONEXT		; If a null, no next string
	CAIE T1,"-"		; A device terminator?
	JRST ROUTE1		; No. Go on then
	SETZM T1
	DPB T1,T4		; Tie off the node name
	MOVE T3,T4		; Save sp
	ILDB T1,T3		; Get next
	JUMPE T1,NONEXT		; If a null, no next
	SKIPA
NONEXT:	SETZM T4		; No next

; Build GTJFN string to destination node

	HRROI T2,GTBFR		; The buffer
	SETZM T3
	HRROI T1,[ASCIZ /DCN:/]
	SIN
	HRROI T1,OPTDAT
	SIN
	HRROI T1,[ASCIZ /-23/]
	SIN
	JUMPE T4,ROUTE2
	HRROI T1,[ASCIZ /;DATA:/]
	SIN
	MOVE T1,T4
	SIN
ROUTE2:	CALL CAPON		; CAPs on
	MOVX T1,GJ%SHT
	HRROI T2,GTBFR
	GTJFN			; Get a JFN for it
	 ERJMP START
	MOVEM T1,PTYJFN
	MOVX T2,OF%RD!OF%WR!FLD(^D8,OF%BSZ)
	OPENF			; Open the connection
	 ERJMP [MOVE T1,PTYJFN	; Get the JFN
		RLJFN 
		 NOP
		SETZM PTYJFN
		MOVEI T3,.DCX39
		JRST CLZCON]
	MOVEM T1,PTYJFN		; SAVE JFN
	CALL CAPOFF
	JRST DOACC
	SUBTTL	GOTDAT - Got data available from network connection

;
; Note: This routine is run at software interrupt level when data is
;	available to be read from the network connection.
;

GOTDAT:	DMOVEM T1,SAVAC		; Save AC's
	DMOVEM T3,SAVAC+2
GOTDA1:	MOVE T1,SRVJFN		; Get the server JFN
	MOVEI T2,.MORLS		; Read link's status
	MTOPR			; Do the device operation
	TXNN T3,MO%CON		; Connected?
	JRST START		; No, go start over
	SIBE			; Skip if input buffer empty
	 SKIPA			; Not empty, have some data
	JRST [	DMOVE T1,SAVAC		; Empty, no data, restore AC's
		DMOVE T3,SAVAC+2
		DEBRK]			; Dismiss interrupt and resume processing
	CAILE T2,SRVSIZ*5	; Buffer larger than number of bytes available?
	MOVEI T2,SRVSIZ*5	; No, get maximum buffer full
	AOS NETADR(T2)		; COUNT DATA IN
	MOVN T3,T2		; Get byte count
	MOVE T4,T3		; Save it
	HRROI T2,SRVINP		; Pointer to server input buffer
	SIN			; Read from network link
	 ERJMP START			; Failed, link broken, start again
	SUB T4,T3		; Calculate number of bytes read
	MOVE T3,T4		; Get byte count
	MOVE T1,PTYJFN		; Get PTY JFN
	HRROI T2,SRVINP		; Pointer to server input buffer
	SOUTR			; Output data to PTY
	ERJMP CHKERR
	JRST GOTDA1		; And go back to do more
; Here when couldn't do I/O to destination


CHKERR:	TXNN F,1B0		; Doing routing?
	JRST [	MOVS T1,TTYJFN	; Get TTY designator
		TXZ T1,1B0
		HRRI T1,.TTYJO	; In the TTYJOB table
		GETAB		; Get information
		 JRST START
		JUMPGE T1,GOTDA1 ; If still logged in, proceed
		JRST START]	; No longer logged in
	MOVE T1,PTYJFN
	MOVEI T2,.MORLS
	MTOPR			; Get status of the routed link
	ERJMP START
	ANDI T3,-1		; Isolate error
	JUMPE T3,START		; If no error, give up
CLZCON:	MOVS T2,T3
	HRRI T2,.MOCLZ
	SETZM T4
	MOVE T3,[POINT 7,OPTDAT]
	ILDB T1,T3
	SKIPE T1
	AOJA T4,.-2
	MOVE T3,[POINT 7,OPTDAT]
	MOVE T1,SRVJFN		; Close down the server
	MTOPR			; Abort the link
	ERJMP START
	JRST START
	SUBTTL	PTYIN - Pseudo TTY input routine

;
; Note: This routine gets run at background level by the GOTCI routine
;	changing the PC value at interrupt level when a network connection
;	is accepted.

PTYIN:	MOVE T1,PTYJFN		; Get PTY JFN
	BIN			; Gte a byte
	 ERJMP CHKERR
	TXNE F,1B0		; A real PTY?
	JRST [	IDPB T2,OUTPUT
		SOS OUCNT
		SIBE		; No. See what we have
		 SKIPA		; Got data
		JRST PTYIN1
		CAILE T2,MAXOUT-1 ; Enough room?
		MOVEI T2,MAXOUT-1 ; No.
		SUBM T2,OUCNT
		MOVNS OUCNT
		MOVN T3,T2
		MOVE T4,T3
		MOVE T2,[POINT ^D8,SRVOUP,7]
		SIN		; Get data
		ERJMP CHKERR
		JRST PTYIN1]
	IDPB T2,OUTPUT		; Store byte in output buffer and save pointer
	SOSG OUCNT		; Output buffer full?
	JRST PTYIN1		; Yes, write it then
	MOVE T1,TTYJFN		; Get TTY designator
	SOBE			; Skip if TTY output buffer empty
	 JRST PTYIN			; Not empty, go get more characters from PTY
PTYIN1:	MOVNI T3,MAXOUT		; Get negative maximum output count
	ADD T3,OUCNT		; Compute number of bytes in buffer
	MOVE T1,SRVJFN		; Get the server JFN
	MOVN T2,T3		;GET POS OF COUNT
	AOS PTYADR(T2)		;DO STATS
	MOVE T2,[POINT ^D8,SRVOUP] ; Get pointer to output buffer
	SOUTR			; Output data to network link
	 ERJMP START			; Failed, link died, start over
	MOVE T1,[POINT ^D8,SRVOUP] ; Get pointer to output buffer
	MOVEM T1,OUTPUT		; Reinitialize output buffer pointer
	MOVEI T2,MAXOUT		; Get maximum output count
	MOVEM T2,OUCNT		; Reinitialize output count
	JRST PTYIN		; Go back and get more characters from PTY
	SUBTTL	GETPTY - Get a pseudo TTY designator and return opened PTY JFN

GETPTY:	STKVAR <SAVCHR>		; Get stack variable storage
	MOVE T1,[SIXBIT /PTYPAR/] ; Pseudo TTY parameter table name
	SYSGT			; Get system table information for PTY
	HRRZM T1,SAVCHR		; Save TTY number of first pseudo TTY 
	HLRZ T4,T1		; Get number of PTY's
	MOVNS T4		; Get negative of count
	HRLZS T4		; Make an index loop pointer
GETPT1:	MOVSI T1,.DVDES+.DVPTY	; Get device designator for PTY
	HRR T1,T4		; Get PTY number
	DVCHR			; Get device characteristics of PTY
	TXNN T2,1B5		; Available?
	JRST GETPT2		; No, try next PTY then
	MOVE T2,T1		; Get device designator
	HRROI T1,DEVNAM		; Pointer to device name string buffer
	DEVST			; Translate device designator to string
	 JRST GETPT2			; Failed, try next PTY
	MOVEI T2,":"		; Get device string terminator character
	IDPB T2,T1		; Store it after device name string
	SETZM T2		; Get a null byte
	IDPB T2,T1		; Make device name string ASCIZ
	MOVX T1,GJ%SHT		; Short form GTJFN
	HRROI T2,DEVNAM		; Get pointer to PTY name string
	GTJFN			; Get a JFN for PTY
	 JRST GETPT2			; Failed, try next PTY
	MOVE T3,T1		; Save the PTY JFN
	MOVX T2,<FLD(^D8,OF%BSZ)!OF%RD!OF%WR> ; 8 bit bytes, read/write access
	OPENF			; Open the PTY
	 JRST [	MOVE T1,T3		; Failed, get PTY JFN
		RLJFN			; Release the PTY JFN
		 JFCL				; Ignore errors
		JRST GETPT2]		; And try next PTY
	MOVEI T2,.TTDES(T4)	; Get PTY index
	ADD T2,SAVCHR		; Get PTY designator
	RETSKP			; Done, return skip for success

; Step through all PTY's

GETPT2:	AOBJN T4,GETPT1		; Increment PTY number and go back if more to do
	RET			; No, give up, return no skip for failure

	END BEGIN