.TITLE TELNET - DECNET TELNET server task .IDENT /V01/ .ENABL LC ;+ ; Title: TELNET - DECNET remote terminal server task ; Version: 01 ; By: Greg Thompson (Informatics/PMI) ; Date: 2/27/78 ; ; This task is the server task for the DECNET remote terminal ; support. It talks, via DECNET, to ...CON tasks on various ; nodes. This task job is to hand-off messages of the following ; formats between the ...CON task and a RTx: terminal driver. ; ; The network messages sent to CON are of the following format: ; _______________________________ ; ! ! ! ; ! Function code ! Modifier ! 0 ; !_______________!_______________! ; ! ! ! ; !Sequence number! Size (bytes) ! 2 ; !_______________!_______________! ; ! ! ; ! VFC parameter ! 4 ; !_______________________________! ; ! ! ; ! Prompt size parameter ! 6 ; !_______________________________! ; ! ! ; ! First word of message buffer ! 10 ; !_______________________________! ; ! ! ; ; The network messages returned are of the following format: ; _______________________________ ; ! ! ! ; ! Function code ! Modifier ! 0 ; !_______________!_______________! ; ! ! ! ; !Sequence number! Size (bytes) ! 2 ; !_______________!_______________! ; ! ! ; ! First word of I/O Status Block! 4 ; !_______________________________! ; ! ! ; ! Second word of I/O Status Blk ! 6 ; !_______________________________! ; ! ! ; ! First word of message buffer ! 10 ; !_______________________________! ; ! ! ; ; An IO.XMT with a buffer is queued to RT: driver for it ; to construct a message of the above format. When the RT ; driver receives a terminal driver QIO it constructs the ; message and calls $IOFIN on the IO.XMT in order to return ; the message to this task, which then sends it on the the ; CON task where the equivalent QIO finally occurs to the ; real TT driver. The I/O status block and any read data ; is returned on the IO.RCV which is then QIOed to the RT ; driver allowing it to call $IOFIN on the original terminal ; QIO which started the message. The driver then $IOFINs ; the IO.RCV to return the buffer to the buffer pool in this ; task. IO.CON is used instead of IO.XMT to the RT driver ; in order to indicate that the user has just connected to ; the terminal. IO.DIS is used to indicate that the user ; has disconnected. ; ; To assemble this module use: MAC TELNET=TELNET ; ; To build it use: ; ; TKB ; TKB>TELNET/SL/SE/-CP=TELNET ; TKB>/ ; TKB>; UNITS SHOULD BE: 1 + ( 2 * THE NUMBER OF RT PORTS ) ; TKB>UNITS=17 ; TKB>PRI=70 ; TKB>// ;- .MCALL DIR$,QIOW$,QIO$,ALUN$S,COMDF$,CLEF$S .MCALL EXIT$S,SETF$S,WTSE$S,ASTX$S,RCVD$,SRDA$S .MCALL DSAR$S,ENAR$S COMDF$ ; DECNET definitions .PSECT TELNET ; ; Local equates ; NTERMS = 8. ; Number of terminals supported NBUFS = 3.*NTERMS ; Number of message buffers NTBS = 2*71. ; Size for network buffer (bytes) LUNNT = 1. ; NT: LUN reserved for IO.CRJ, IO.CON NTLUN0 = 2. ; NT: LUN for RT0: RTLUN0 = NTERMS+NTLUN0 ; RT0: LUN EFNT = 1. ; Event flag for NT: connects EFDONE = 2. ; Event flag to exit task ; ; Directive Parameter Blocks ; RCVD: RCVD$ ,RCVBUF ; Receive connection request CONNT: QIOW$ IO.CON,LUNNT,EFNT,,IOSB,, UNSOL: QIO$ IO.CTL,,,,,,<0> ; QIO to pass unsol. input to RTx: IOSB: .BLKW 2 ; Misc NT: I/O status block ; ; Logical link buffer ; RCVBUF: .BLKW 2 ; Task name for RCVD$ LNKBUF: .BYTE 0 ; Object type code - 0 .BYTE 0 ; Device unit number .WORD 0 ; Logical link connection number .BLKW 2 ; Remote node name .BLKW 2 ; Task we wish to talk to .WORD 1,1 ; Remote task UIC .WORD 0 ; Number of user arguments .BLKW 8. ; User arguments ; RTCW2: .WORD 0 ; Place to hold U.CW2 word from a connection ; Note: If a second connection comes in fast ; enough and we are short of buffers this ; word could get changed before it is used. BFNEED: .WORD 0 ; Count of buffers wanted CONCNT: .WORD 0 ; Number of terminals connected ; ; remote terminal status bytes ; RTSTAT: .REPT NTERMS ; One for each remote terminal .BYTE 0 ; Terminal Status Byte .ENDR ; .EVEN ; ; ; Terminal status byte bit definitions ; V.ALOC = 200 ; Terminal allocated (must be 200) V.XNED = 100 ; Need a buffer for IO.XMT to RTx: V.RNED = 40 ; Need a buffer for IO.RCV to NT: V.INIT = 20 ; Terminal initted ; ; ; Offsets from I/O Status Block to various ptrs and buffer ; N.STA = 4 ; Offset to buffer status byte N.TRM = 5 ; Offset to owning terminal byte N.BUF = 6 ; Offset to network buffer N.FUN = N.BUF ; Offset to function code in buffer N.SIZ = N.BUF+2 ; Offset to size parameter in buffer N.SEQ = N.BUF+3 ; Offset to sequence number N.VFC = N.BUF+4 ; Offset to VFC parameter N.SB1 = N.BUF+4 ; Offset to 1st word of returned IOSB N.PSZ = N.BUF+6 ; Offset to prompt size parameter N.SB2 = N.BUF+6 ; Offset to 2nd word of returned IOSB N.MES = N.BUF+10 ; Offset to message (TI) buffer N.QIO = N.BUF+NTBS ; Offset to QIO ; ; Message buffers ; MBUFF: .REPT NBUFS ; Generate the required number of buffers $$$=. .BLKW 2 ; IO.RCV I/O status block .BYTE 0,0 ; Buffer status, terminal number .BLKB NTBS ; Buffer QIO$ IO.CON,RTLUN0,,,$$$,TXMAST,<$$$+N.BUF,NTBS,0> .ENDR ; MBUFFL = .-$$$ ; Length of message buffer ; ; Buffer status byte bit definations ; B.ALOC = 200 ; Buffer allocated (must be 200) B.IOAC = 100 ; I/O active on buffer .PAGE ; ; TELNET server task code ; START: CLR BFNEED ; Initialize variables CLR CONCNT ; CLEF$S #EFDONE ; Initialize finished Event Flag MOV #MBUFF,R0 ; Free up all buffers MOV #NBUFS,R1 ; 10$: CLRB N.STA(R0) ; by clearing status word ADD #MBUFFL,R0 ; Point at next buffer DEC R1 ; Clear them all? BNE 10$ ; No, clear next one MOV #NTERMS-1,R0 ; Now indicate all terminals are free 20$: CLRB RTSTAT(R0) ; Free a terminal DEC R0 ; Clear them all? BGE 20$ ; No, free next one ALUN$S #LUNNT,#"NT,#0 ; Assign for DECNET connection LUN BCS 50$ ; Exit if error DSAR$S ; No ASTs while using CONREQ at this level CALL CONREQ ; Process initial connection requests ENAR$S ; Ok to allow ASTs now SRDA$S #CRQAST ; Use receive data AST for further requests 30$: WTSE$S #EFDONE ; Wait for all terminals to disconnect TST CONCNT ; Are all terminals disconnected? BLE 40$ ; Yes, exit CLEF$S #EFDONE ; Reset finished Event Flag BR 30$ ; Keep waiting ; 40$: SRDA$S ; No more receive data ASTs 50$: EXIT$S ; Exit task ; ; Receive connection request messages from DECNET AST ; CRQAST: CALL CONREQ ; Go process connection requests ASTX$S ; Return from AST when done ; ; Process queued connection requests ; CONREQ: DIR$ #RCVD ; Is there a connection request? BCC 10$ ; Yes, go process it RETURN ; No, return from AST ; ; Find a free terminal to allocate ; 10$: MOV B.UA+LNKBUF,RTCW2 ; Save U.CW2 parameter MOV #1,B.NA+LNKBUF ; Set one byte return parameter CLR B.UA+LNKBUF ; Init return paramater CLR R0 ; Look for a free terminal 20$: TSTB RTSTAT(R0) ; Is this terminal available? BPL 40$ ; Yes, we can use it INC R0 ; No, bump terminal number CMP R0,#NTERMS ; Is that all the terminals? BNE 20$ ; No, try this new one ; ; No free terminal, reject the request ; 30$: MOV #IO.CRJ,Q.IOFN+CONNT ; Reject connection MOVB #LUNNT,Q.IOLU+CONNT ; Use reserved LUN MOV #LNKBUF,Q.IOPL+CONNT ; Set address of logical link buffer DIR$ #CONNT ; Reject request SETF$S #EFDONE ; Check to see if all terminals disconnected BR CONREQ ; See if there are other requests ; ; Found a terminal, assign RT and NT devices ; 40$: MOV #RTLUN0,R1 ; Figure which RT LUN to use ADD R0,R1 ; R1 is RTx: LUN for terminal ALUN$S R1,#"RT,R0 ; Assign RTx: pseudo terminal BCS 50$ ; Branch if error on assign MOV #NTLUN0,R1 ; Now figure out which NT LUN to use ADD R0,R1 ; R1 is NT: LUN for terminal ALUN$S R1,#"NT,#0 ; Assign NT0: device for terminal BCS 30$ ; Reject request on error ; ; Put up IO.CON to RT: driver ; BICB #V.INIT!V.XNED!V.RNED,RTSTAT(R0) ; Insure we do a IO.CON to it CALL RTXMT ; Queue IO.CON buffer to RT: BCC 60$ ; First IO.CON worked, accept connection CMPB #IE.HWR,$DSW ; Is the RT: driver unloaded? BNE 30$ ; No, just say no terminal available 50$: MOVB #-1,B.UA+LNKBUF ; Yes, so return RT: unloaded error BR 30$ ; ; ; Accept connection ; 60$: MOV #IO.CON,Q.IOFN+CONNT ; Accept connection MOVB R1,Q.IOLU+CONNT ; Set LUN to use for connection MOV #LNKBUF,Q.IOPL+CONNT ; Set address of logical link buffer DIR$ #CONNT ; Accept connection BCS 30$ ; Reject again if error TSTB IOSB ; Was there an error? BMI 30$ ; Reject connection if yes ; ; Allocate terminal ; BISB #V.ALOC,RTSTAT(R0) ; Indicate terminal allocated INC CONCNT ; Bump count of terminals allocated ; ; Put up IO.RCV on NT: ; CALL NTRCV ; Queue up first IO.RCV on network BCC CONREQ ; See if any more requests if no error CALL CLEAN ; Flush connection if error BR CONREQ ; See if anymore requests ; ; Exception condition on network AST ; (Unsolicited input or connection aborted) ; EXCAST: CMP #IA.ISM,(SP)+ ; Is it unsolicited input? BEQ 20$ ; Yes, get character to pass to RT: MOV (SP)+,R0 ; No, connection must have failed BIC #177400,R0 ; Clear all bits but LUN number CMP R0,#LUNNT ; Is it the reserved LUN? BEQ 10$ ; Yes, ignore AST SUB #NTLUN0,R0 ; Figure terminal number that quit CALL CLEAN ; Disconnect that terminal 10$: ASTX$S ; Return from AST ; ; Unsolicited character or for terminal ; 20$: MOV (SP)+,R0 ; Get LUN number BIC #177400,R0 ; Clear all bits but LUN number CMP R0,#LUNNT ; Is it the reserved LUN? BEQ 10$ ; Yes, just exit I guess (shouldn't happen) ADD #NTERMS,R0 ; Compute RT: LUN from NT: LUN MOVB R0,Q.IOLU+UNSOL ; Set LUN in QIO MOV (SP)+,Q.IOPL+UNSOL ; Put unsolicited character in QIO DIR$ #UNSOL ; Issue IO.CTL to pass character to RT: driver ASTX$S ; It will complete on its own ; ; IO.XMT or IO.CON operation to RTx: completed AST, pass it over NT: ; TXMAST: CALL ASTSET ; Set up registers CALL RTXMT ; Put up another IO.XMT on terminal BCS CLNXT ; Cancel terminal if error MOV #IO.XMT,Q.IOFN(R2) ; Now send completed IO.XMT to CON task MOVB #NTLUN0,Q.IOLU(R2) ; Figure LUN to talk on ADD R0,Q.IOLU(R2) ; MOV #XMTAST,Q.IOAE(R2) ; Set AST address MOVB N.SIZ(R1),Q.IOPL+2(R2) ; Figure size of message to send CLRB Q.IOPL+3(R2) ; Clear high order byte CMPB #IO.RLB/400,N.FUN+1(R1) ; Is function a read logical? BEQ 10$ ; Yes, don't send any data CMPB #IO.RVB/400,N.FUN+1(R1) ; Is function a read virtual? BEQ 10$ ; Yes, don't send any data CMP #IO.GTS,N.FUN(R1) ; Get terminal support? BEQ 10$ ; Yes, no data to send CMPB #IO.RPR/400,N.FUN+1(R1) ; Read after prompt? BNE 20$ ; No, assume write type MOV N.PSZ(R1),Q.IOPL+2(R2) ; Yes, use prompt size instead BR 20$ ; Continue ; 10$: CLRB Q.IOPL+2(R2) ; Dont need to send data on a read 20$: ADD #N.MES-N.BUF,Q.IOPL+2(R2) ; Add in size of header CLR Q.IOPL+4(R2) ; Not an interupt message ISSUE: BISB #B.IOAC,N.STA(R1) ; Indicate I/O active on buffer DIR$ R2 ; Issue IO.XMT on NT: BCC ASTX ; Branch if no error CLNXT: CALL CLEAN ; Clean up if error ASTX: ASTX$S ; Exit AST ; ; Message transmitted over network AST (IO.XMT completed), release buffer ; XMTAST: CALL ASTSET ; Set up registers CALL RELB ; Release the buffer BR ASTX ; Return ; ; Message received from network AST (IO.RCV completed), return it to RTx: ; RCVAST: CALL ASTSET ; Set up registers CALL NTRCV ; Put up another IO.RCV BCS CLNXT ; Close up on error MOVB #RTLUN0,Q.IOLU(R2) ; Figure LUN for RTx: ADD R0,Q.IOLU(R2) ; Return IOSB and read data to RT: MOV #TRCAST,Q.IOAE(R2) ; Set AST address JMP ISSUE ; Issue IO.RCV to RTx: ; ; IO.RCV operation to RTx: terminal completed AST, release buffer ; TRCAST: CALL ASTSET ; Set up registers CALL RELB ; Release the buffer BR ASTX ; Return ; ; Release a buffer, start up terminals waiting for a buffer ; ; Enter with R1 -> buffer ; RELB: BICB #B.ALOC,N.STA(R1) ; Free up the buffer TST BFNEED ; Does any terminal need a buffer? BEQ 30$ ; No, thats all folks DEC BFNEED ; Yes, we have one for him CLR R0 ; R0 is terminal number 10$: BITB #V.RNED!V.XNED,RTSTAT(R0) ; Find someone who is waiting BNE 20$ ; Found someone INC R0 ; No, Try next terminal CMP R0,#NTERMS ; Any more terminals to check? BNE 10$ ; Yes, check it BR 30$ ; No, I don't know where it went ; 20$: BITB #V.RNED,RTSTAT(R0) ; Buffer for IO.RCV? BNE 40$ ; Yes BICB #V.XNED,RTSTAT(R0) ; Clear waiting bit BITB #V.ALOC,RTSTAT(R0) ; Is he still active? BEQ 30$ ; Forget him if not CALL RTXMT ; Still alive, give him his buffer BCS CLNXT ; Clean up on error 30$: RETURN ; Return if no error ; 40$: BICB #V.RNED,RTSTAT(R0) ; Clear waiting bit BITB #V.ALOC,RTSTAT(R0) ; Is he still active? BEQ 50$ ; Forget him if not CALL NTRCV ; Still alive, give him his buffer BCS CLNXT ; Clean up on error 50$: RETURN ; Return if no error ; ; QIO a IO.XMT buffer to RTx: ; ; Enter with R0 = terminal number ; (Does not harm registers) ; Carry set on return if error ; RTXMT: MOV R1,-(SP) ; Save registers MOV R2,-(SP) ; MOV #MBUFF,R1 ; Look for a free buffer MOV #NBUFS,R2 ; 10$: BITB #B.ALOC!B.IOAC,N.STA(R1) ; Is the buffer free? BEQ 20$ ; Yes, found one for us ADD #MBUFFL,R1 ; No, point at next buffer DEC R2 ; Was that the last buffer? BNE 10$ ; No, check next one BISB #V.XNED,RTSTAT(R0) ; Indicate it wants a buffer INC BFNEED ; Bump count of terminals waiting CLC ; Make like no error BR 40$ ; Just return now, QIO it later ; 20$: BISB #B.ALOC,N.STA(R1) ; Allocate the buffer to us MOVB R0,N.TRM(R1) ; Set owning terminal number MOV R1,R2 ; Get a pointer to our QIO DPB ADD #N.QIO,R2 ; MOV #IO.XMT,Q.IOFN(R2) ; Queue a buffer for RT's use BITB #V.INIT,RTSTAT(R0) ; Should we send IO.CON instead? BNE 30$ ; No, already initialized MOV #IO.CON,Q.IOFN(R2) ; Yes, use IO.CON MOV RTCW2,Q.IOPL+4(R2) ; Put U.CW2 word after length BISB #V.INIT,RTSTAT(R0) ; Indicate RTx initialized 30$: MOVB #RTLUN0,Q.IOLU(R2) ; Figure LUN to talk on ADD R0,Q.IOLU(R2) ; MOV #TXMAST,Q.IOAE(R2) ; Set AST address MOV #NTBS,Q.IOPL+2(R2) ; Set buffer size in QIO CLR N.FUN(R1) ; Clear function word MOVB #NTBS-,N.SIZ(R1) ; Put size in buffer CLRB N.SEQ(R1) ; Clear sequence number CLR N.VFC(R1) ; Clear VFC parameter CLR N.PSZ(R1) ; Clear prompt size parameter BISB #B.IOAC,N.STA(R1) ; Indicate I/O active on buffer DIR$ R2 ; Issue IO.XMT or IO.CON QIO to RTx: 40$: MOV (SP)+,R2 ; Restore registers and Carry status MOV (SP)+,R1 ; RETURN ; Return to caller ; ; QIO a IO.RCV buffer to NT: for a terminal ; ; Enter with R0 = terminal number ; (Does not harm registers) ; Carry set on return if error ; NTRCV: MOV R1,-(SP) ; Save registers MOV R2,-(SP) ; MOV #MBUFF,R1 ; Look for a free buffer MOV #NBUFS,R2 ; 10$: BITB #B.ALOC!B.IOAC,N.STA(R1) ; Is the buffer free? BEQ 20$ ; Yes, found one for us ADD #MBUFFL,R1 ; No, point at next buffer DEC R2 ; Was that the last buffer? BNE 10$ ; No, check next one BISB #V.RNED,RTSTAT(R0) ; Indicate it wants a buffer INC BFNEED ; Bump count of terminals waiting CLC ; Make like no error BR 30$ ; Just return now, QIO it later ; 20$: BISB #B.ALOC,N.STA(R1) ; Allocate the buffer to us MOVB R0,N.TRM(R1) ; Set owning terminal number MOV R1,R2 ; Get a pointer to our QIO DPB ADD #N.QIO,R2 ; MOV #IO.RCV,Q.IOFN(R2) ; Queue a buffer for NT's use MOVB #NTLUN0,Q.IOLU(R2) ; Figure LUN to talk on ADD R0,Q.IOLU(R2) ; MOV #RCVAST,Q.IOAE(R2) ; Set AST address MOV #NTBS,Q.IOPL+2(R2) ; Set buffer size in QIO CLR Q.IOPL+4(R2) ; No inhibiting link status CLR N.FUN(R1) ; Clear function word MOVB #NTBS-,N.SIZ(R1) ; Put size in buffer CLRB N.SEQ(R1) ; Clear sequence number CLR N.VFC(R1) ; Clear VFC parameter CLR N.PSZ(R1) ; Clear prompt size parameter BISB #B.IOAC,N.STA(R1) ; Indicate I/O active on buffer DIR$ R2 ; Issue IO.RCV to NT: 30$: MOV (SP)+,R2 ; Restore registers and Carry status MOV (SP)+,R1 ; RETURN ; Return to caller ; ; Setup for QIO ASTs, check for terminal disconnected ; ; Return with: ; ; R0 = Terminal number ; R1 -> Buffer ; R2 -> QIO DPB for the buffer ; ASTSET: MOV (SP)+,R5 ; Put return address in R5 MOV (SP)+,R1 ; Get ptr to I/O status block (buffer) BICB #B.IOAC,N.STA(R1) ; Indicate I/O completed BITB #B.ALOC,N.STA(R1) ; Is our buffer still active? BEQ 20$ ; No, ignore and exit AST MOVB N.TRM(R1),R0 ; Yes, get terminal number TSTB RTSTAT(R0) ; Is terminal still active? BPL 20$ ; Just exit if not TSTB (R1) ; Did we get an error? BMI 10$ ; Yes, disconnect terminal MOV R1,R2 ; Set up pointer to QIO ADD #N.QIO,R2 ; For use by AST routine JMP (R5) ; Success so far, return to caller ; 10$: CALL CLEAN ; Yes, abort connection 20$: ASTX$S ; And exit AST ; ; Disconnect a terminal ; ; Enter with R0 = terminal number ; CLEAN: TSTB RTSTAT(R0) ; Is the terminal connected? BPL 30$ ; Nothing to do if not CLRB RTSTAT(R0) ; Indicate terminal released DEC CONCNT ; Decrement count of connected terminals BGT 5$ ; Is the count now zero? SETF$S #EFDONE ; Yes, we can exit task now 5$: MOV #IO.DIS,Q.IOFN+CONNT ; Set function to disconnect CLR Q.IOPL+CONNT ; Set no parameters to return MOV #NTLUN0,R1 ; Now figure out which NT LUN to use ADD R0,R1 ; R1 is NT: LUN for terminal MOVB R1,Q.IOLU+CONNT ; Set LUN to use for disconnect DIR$ #CONNT ; Issue disconnect to NT network MOV #RTLUN0,R1 ; Now figure out which RT LUN to use ADD R0,R1 ; R1 is RTx: LUN for the terminal MOVB R1,Q.IOLU+CONNT ; Set LUN for disconnect DIR$ #CONNT ; IO.DIS to RT driver too MOV #MBUFF,R1 ; Scan buffers for ones owned by him MOV #NBUFS,R2 ; 10$: TSTB N.STA(R1) ; Is this buffer allocated? BPL 20$ ; No, try next one CMPB R0,N.TRM(R1) ; Does this terminal own it? BNE 20$ ; No, try next one BICB #B.ALOC,N.STA(R1) ; Yes, so deallocate buffer 20$: ADD #MBUFFL,R1 ; Point at next buffer DEC R2 ; Is that all of the buffers? BNE 10$ ; No, check this one too 30$: RETURN ; Yes, all done ; .END START ; End of code