Trailing-Edge
-
PDP-10 Archives
-
BB-R598A-RM_1983
-
swskit-v3/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