.TITLE CON - DECNET telnet user task .IDENT /V01/ .ENABL LC ;+ ; Title: CON - DECNET telent connection program ; Version: 01 ; By: Greg Thompson (Informatics/PMI) ; Date: 2/21/78 ; ; This task is the user initiated telnet connection process. ; It talks, via DECNET, to the TELNET task on the destination ; machine and implements virtual terminals over DECNET. ; ; The network messages received 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 ; !_______________________________! ; ! ! ; ; To assemble CON issue: ; ; MAC CON,CON=[1,1]EXEMC/ML,[6,6]CON ; ; To build CON issue: ; ; TKB ; TKB>CON/PR:0=CON ; TKB>/ ; ENTER OPTIONS: ; TKB>TASK=...CON ; TKB>PRI=70 ; TKB>// ;- .MCALL DIR$,QIOW$,QIO$,ALUN$,COMDF$,GMCR$,CLEF$S .MCALL EXIT$S,SETF$S,WTSE$S,ASTX$S,GTSK$,SPND$S,UCBDF$ COMDF$ ; DECNET definitions UCBDF$ ; UCB - U2.CW2 bit definitions ; ; Local equates ; NTBS = 2*71. ; Size for network buffer (bytes) M$$CRI = 80. ; Max buffer size (bytes) for MCR command buffer LUNNT = 1 ; NT: LUN LUNTI = 5 ; TI: LUN EFNT = 1 ; Event flag for NT: IO.CON, IO.DIS EFTI = 2 ; Event flag for TI: IO.ATA, IO.DET, IO.KIL EFBUF1 = 3 ; Event flag for first NT: buffer EFBUF2 = 4 ; Event flag for second NT: buffer EFBUF3 = 5 ; Event flag for third NT: buffer EFDONE = 6 ; Event flag to exit task EFIN = 7 ; Event flag for unsolicited character IO.XMT EFMCR = 10 ; Event flag for sending MCR command line buffer EFECHO = 11 ; Event flag for echoing characters ; ; Directive Parameter Blocks ; ASNNT: ALUN$ LUNNT,NT,0 ; Assign for DECNET CONNT: QIOW$ IO.CON,LUNNT,EFNT,,IOSB,, GCMBLK: GMCR$ ; Get command line from MCR GTSK: GTSK$ TSKBUF ; Get task name WRTTI: QIOW$ IO.CCO,LUNTI,EFTI,,TISB,,<0,0,0> ATATI: QIOW$ IO.ATA,LUNTI,EFTI,,TISB,, WRINT: QIO$ IO.XMT,LUNNT,EFIN,,,, MCRQIO: QIO$ IO.XMT,LUNNT,EFMCR,,,, GTS: QIOW$ SF.GMC,LUNTI,,,TISB,, WRMCR: QIO$ IO.CCO,LUNTI,,,,, ECHO: QIO$ IO.CCO!TF.WAL,LUNTI,EFECHO,,,,<0,0,0> DONL: QIO$ IO.CCO,LUNTI,,,,, ; GTSBUF: .BYTE TC.ESQ,0 ; Escape sequences .BYTE TC.HLD,0 ; Hold screen mode .BYTE TC.NEC,0 ; No echo mode SCOPE: .BYTE TC.SCP,0 ; Scope LOWER: .BYTE TC.SMR,0 ; Upper-case conversion .BYTE TC.TTP,0 ; Terminal type GTSSIZ = .-GTSBUF ; Size of GTS buffer ; MCRMSG: .WORD IO.CTL ; Message buffer to send MCR command .WORD 0 ; Zero sequence number .WORD 0 ; No status block word 1 .WORD 0 ; Status word 2 (byte count) MCRBUF: .BLKB M$$CRI ; Buffer for MCR input MCRPMT: .ASCII <15><12>/XXX>/ ; MCR prompt string (use remote host name) .EVEN ; ECOBUF: .BLKB 4. ; Buffer to echo characters MCRCNT: .WORD 0 ; Number of bytes in MCR command buffer INCHAR: .WORD 0 ; Buffer to hold unsolicited input character TSKBUF: .BLKW 16. ; Buffer to hold task info TISB: .BLKW 2 ; Misc TI: I/O status block IOSB: .BLKW 2 ; Misc NT: I/O status block ERRFLG: .WORD 0 ; Shutdown on error flag WQUEUE: .WORD 0 ; Queue of buffers held up by MCR input STATUS: .WORD 0 ; Status word ; ; Status word bit settings ; ST.CON = 1 ; Connection open flag ST.ATI = 2 ; TI attached flag ST.VAT = 4 ; VT: driver has task with IO.ATA ST.RIP = 10 ; Rubout in process ST.WAI = 20 ; Hold up output MCR input ST.DNL = 40 ; Do a on first char of MCR input ST.DLF = 100 ; Do a before issuing a read ; ; Logical link buffer ; LNKBUF: .BYTE 0 ; Object type code - 0 .BYTE 0 ; Device unit number .WORD 0 ; Logical link connection number .BLKW 2 ; Remote node name .RAD50 /TELNET/ ; Task we wish to talk to .WORD 1,1 ; Remote task UIC .WORD 0 ; Number of user arguments .BLKW 8. ; User arguments ; ; Network buffers and QIOs ; TIQIO1: QIO$ IO.WLB,LUNTI,EFBUF1,,NET1SB,TIAST,<0,0,0,0,0,0> TIQIO2: QIO$ IO.WLB,LUNTI,EFBUF2,,NET2SB,TIAST,<0,0,0,0,0,0> TIQIO3: QIO$ IO.WLB,LUNTI,EFBUF3,,NET3SB,TIAST,<0,0,0,0,0,0> RCQIO1: QIO$ IO.RCV,LUNNT,EFBUF1,,NET1SB,RCVAST, RCQIO2: QIO$ IO.RCV,LUNNT,EFBUF2,,NET2SB,RCVAST, RCQIO3: QIO$ IO.RCV,LUNNT,EFBUF3,,NET3SB,RCVAST, XMQIO1: QIO$ IO.XMT,LUNNT,EFBUF1,,NET1SB,XMTAST, XMQIO2: QIO$ IO.XMT,LUNNT,EFBUF2,,NET2SB,XMTAST, XMQIO3: QIO$ IO.XMT,LUNNT,EFBUF3,,NET3SB,XMTAST, ; NET1SB: .BLKW 2 ; IO.RCV I/O status block 1 .WORD 0 ; Current QIO ptr .WORD RCQIO1 ; Ptr to IO.RCV for this buffer .WORD TIQIO1 ; Ptr to TI: QIO for this buffer .WORD XMQIO1 ; Ptr to IO.XMT for this buffer BUF1: .BLKB NTBS ; Buffer number 1 ; NET2SB: .BLKW 2 ; IO.RCV I/O status block 2 .WORD 0 ; Current QIO ptr .WORD RCQIO2 ; Ptr to IO.RCV for this buffer .WORD TIQIO2 ; Ptr to TI: QIO for this buffer .WORD XMQIO2 ; Ptr to IO.XMT for this buffer BUF2: .BLKB NTBS ; Buffer number 2 ; NET3SB: .BLKW 2 ; IO.RCV I/O status block 3 .WORD 0 ; Current QIO ptr .WORD RCQIO3 ; Ptr to IO.RCV for this buffer .WORD TIQIO3 ; Ptr to TI: QIO for this buffer .WORD XMQIO3 ; Ptr to IO.XMT for this buffer BUF3: .BLKB NTBS ; Buffer number 3 ; ; Offsets from I/O Status Block to various ptrs and buffer ; N.CUR = 4 ; Offset to current QIO ptr N.RCV = 6 ; Offset to IO.RCV QIO ptr N.TI = 10 ; Offset to TI: QIO ptr N.XMT = 12 ; Offset to IO.XMT QIO ptr N.BUF = 14 ; 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 ; ; Error messages ; ERR1: .ASCIZ <15><12>/CON - Network NT: device not in system./<15> ERR2: .ASCIZ <15><12>/CON - Unknown node name./<15> ERR3: .ASCIZ <15><12>/CON - Network NT: device dismounted./<15> ERR4: .ASCIZ <15><12>/CON - No logical links available./<15> ERR5: .ASCIZ <15><12>/CON - No virtual terminals available on node./<15> ERR6: .ASCIZ <15><12>/CON - No connects to node available./<15> ERR7: .ASCIZ <15><12>/CON - No connects to TELNET task available./<15> ERR8: .ASCIZ <15><12>/CON - TELNET task not installed on remote node./<15> ERR9: .ASCIZ <15><12>/CON - Node shutting down./<15> ERR10: .ASCIZ <15><12>/CON - Could not connect to specified node./<15> ERR11: .ASCIZ <15><12>/CON - Could not attach TI: device./<15> ERR12: .ASCIZ <15><12>/CON - Connection lost on network link./<15> ERR13: .ASCIZ <15><12>/CON - Node name missing or invalid./<15> ERR14: .ASCIZ <15><12>/CON - Remote TELNET task closed connection./<15> ERR15: .ASCIZ <15><12>/CON - Node not active./<15> ERR16: .ASCIZ <15><12>/CON - Timeout on connection request./<15> ERR17: .ASCIZ <15><12>/CON - VT: driver not loaded on remote node./<15> DONE: .ASCIZ <15><12>/CON - Connection closed./<15> RCON: .ASCIZ <15><12>/CON - Reconnected./<15> RCON2: .ASCIZ <15><12>/CON - Reconnected./<15><12>/>/ NOTCM: .ASCIZ <15><12>/CON - Not a valid local command./<15><12>/>/ .EVEN PMESS: .ASCII <15><12>/CON - Pausing, enter "RES / PTASK: .BLKB 6 ; Our task name for message .ASCIZ /" to continue./<15><12>/>/ .EVEN .PAGE ; ; CONNECT command code ; START: CLR ERRFLG ; Initialize flags and variables CLR STATUS ; CLR MCRCNT ; CLR WQUEUE ; SETF$S #EFTI ; Initially set the three EFs SETF$S #EFBUF1 ; that we will use later SETF$S #EFBUF2 ; SETF$S #EFIN ; Set unsolicited input Event Flag SETF$S #EFMCR ; And MCR buffer availablity Event Flag SETF$S #EFECHO ; Event flag for echoing characters CLEF$S #EFDONE ; Clear the DONE Event Flag ; ; Get node name from command line ; DIR$ #GCMBLK ; Get command line BCS 20$ ; Issue error message if error MOV #G.MCRB+GCMBLK,R0 ; Get address of line MOV $DSW,R1 ; Get length of line ADD R0,R1 ; Point at end of line MOVB #' ,R2 ; Put a space in R2 MOVB R2,(R1)+ ; Pad name on end with spaces MOVB R2,(R1)+ ; MOVB R2,(R1)+ ; MOVB R2,(R1)+ ; MOVB R2,(R1)+ ; MOV $DSW,R2 ; Put length of line in R2 10$: DEC R2 ; Are there any characters? BGE 30$ ; Yes, see if its a space 20$: MOV #ERR13,R0 ; In case of error 25$: JMP ERROR ; Issue "Node name missing." error ; 30$: CMPB (R0)+,#' ; Look for first space BNE 10$ ; Not it, try next character ; ; Convert node name and assign NT: device ; MOVB (R0),MCRPMT+2 ; Put host name in prompt string MOVB 1(R0),MCRPMT+3 ; MOVB 2(R0),MCRPMT+4 ; CALL CAT5B ; Convert nodename to RAD50 BCS 20$ ; Branch if invalid RAD50 name MOV R1,B.RN+LNKBUF ; Put first half of name in LLB CALL CAT5B ; Convert next 3 characters BCS 20$ ; Branch if invalid RAD50 name MOV R1,B.RN+2+LNKBUF ; Put second half of name in LLB CLR B.RC+LNKBUF ; Make sure LL connection number is zero DIR$ #ASNNT ; Assign NT: to a logical unit number BCC 31$ ; Branch if successful MOV #ERR1,R0 ; Unable to assign network device BR 25$ ; Issue error message ; ; Create U.CW2 word for our terminal ; 31$: CLR B.UA+LNKBUF ; Init U.CW2 word MOV #2,B.NA+LNKBUF ; Init number of parameters (bytes) DIR$ #GTS ; Get characteristics of our terminal BCS 40$ ; Couldn't get it TSTB IOSB ; Was there an error? BMI 40$ ; Yes, couldn't get characteristics CLR R0 ; Got characteristics, create U.CW2 word MOV #GTSBUF+1,R1 ; UGH, why couldn't have TT: returned it TSTB (R1)+ ; in U.CW2 format directly BEQ 32$ ; Branch if no Escape sequences BIS #U2.ESC,R0 ; Indicate escape sequences allowed 32$: INC R1 ; Point at Hold Screen mode TSTB (R1)+ ; Hold Screen mode? BEQ 33$ ; No BIS #U2.HLD,R0 ; Yes, set the bit 33$: INC R1 ; Point at No Echo mode set TSTB (R1)+ ; No Echo mode? BEQ 34$ ; No BIS #U2.NEC,R0 ; Yes, don't echo 34$: INC R1 ; Point at Scope bit TSTB (R1)+ ; Is terminal a scope? BEQ 35$ ; No BIS #U2.CRT,R0 ; Yes, set CRT bit 35$: INC R1 ; Point at case conversion bit TSTB (R1)+ ; Upper-case conversion disabled? BEQ 36$ ; No BIS #U2.LWC,R0 ; Yes, set Lower-case bit 36$: INC R1 ; Point at terminal type CMPB #4,(R1) ; LA30? BNE 37$ ; No BIS #U2.L3S,R0 ; Yes, make it a LA30S 37$: CMPB #7,(R1) ; VT05? BNE 38$ ; No BIS #U2.VT5,R0 ; Yes, make it a VT05 38$: CMPB #14,(R1) ; LA180S? BNE 39$ ; No BIS #U2.L8S,R0 ; Yes, make it a LA180S 39$: MOV R0,B.UA+LNKBUF ; Put the U.CW2 word in the link buffer ; ; Connect to remote node ; 40$: MOV #IO.CON,Q.IOFN+CONNT ; Set function to connect MOV #LNKBUF,Q.IOPL+CONNT ; Set address of link buffer DIR$ #CONNT ; Connect to remote TELNET task BCS 50$ ; If directive rejected, issue error TSTB IOSB ; Was there an error? BPL CONWRK ; Branch if connect worked MOV #ERR2,R0 ; Set up for error CMPB #IE.NNN,IOSB ; Is there such a node name? BEQ ERROR ; Yes, issue error MOV #ERR3,R0 ; Set up for next error CMPB #IE.PRI,IOSB ; Device dismounted? BEQ ERROR ; Yes, issue error MOV #ERR15,R0 ; Next error CMPB #IE.OFL,IOSB ; Node not active? BEQ ERROR ; Yes, issue error MOV #ERR16,R0 ; Next error CMPB #IE.TMO,IOSB ; Timeout error? BEQ ERROR ; Yes, issue error MOV #ERR4,R0 ; Next error CMPB #IE.RSU,IOSB ; No more links available? BEQ ERROR ; Yes, issue error MOV #ERR17,R0 ; Next error CMPB #CR.UR,IOSB+2 ; Who did the rejecting? BNE 45$ ; Wasn't the TELNET task CMPB #-1,IOSB+3 ; TELNET task rejected it BEQ ERROR ; Was it because VT: wasn't loaded? MOV #ERR5,R0 ; No, assume no terminals available BR ERROR ; Issue error ; 45$: CMPB #CR.NR,IOSB+2 ; Did NSP reject us? BNE 50$ ; Issue general error if not MOV #ERR6,R0 ; Next error CMPB #CR.N1,IOSB+3 ; Too many connects to node? BEQ ERROR ; Yes, issue error MOV #ERR7,R0 ; Next error CMPB #CR.N2,IOSB+3 ; Too many connects to task? BEQ ERROR ; Yes, issue error MOV #ERR8,R0 ; Next error CMPB #CR.N3,IOSB+3 ; TELNET task not found? BEQ ERROR ; Yes, issue error CMPB #CR.N0,IOSB+3 ; General reject? BEQ ERROR ; Assume telnet task not found MOV #ERR9,R0 ; Next error CMPB #CR.N6,IOSB+3 ; Node shutting down? BEQ ERROR ; Yes, issue error 50$: MOV #ERR10,R0 ; Other error BR ERROR ; Issue error message ; ; Output an error message, close up and exit ; ; Enter with R0 -> ; ERROR: BIT #ST.CON,STATUS ; Is there a connection opened? BEQ 10$ ; Branch if not MOV #IO.DIS,Q.IOFN+CONNT ; Disconnect from TELNET CLR Q.IOPL+CONNT ; Clear number of parameters DIR$ #CONNT ; Disconnect 10$: MOV #IO.KIL,Q.IOFN+ATATI ; Kill any terminal I/O DIR$ #ATATI ; Do it CALL MESSAG ; Issue our error message BIT #ST.ATI,STATUS ; Is the TI: attached BEQ 20$ ; Branch if not MOV #IO.DET,Q.IOFN+ATATI ; Detach terminal DIR$ #ATATI ; Detach 20$: EXIT$S ; Done ; ; Output a message ; MESSAG: MOV R0,WRTTI+Q.IOPL ; Set buffer address MOV R0,R1 ; 10$: TSTB (R1)+ ; Figure out length BNE 10$ ; Find end yet? SUB R0,R1 ; Difference in addresses DEC R1 ; minus 1 is length MOV R1,WRTTI+Q.IOPL+2 ; Set buffer length DIR$ #WRTTI ; Write error message to terminal RETURN ; Return ; ; Connected to TELNET task on remote node ; Attach TI: device ; CONWRK: BIS #ST.CON,STATUS ; Indicate connection open MOV #IO.ATA,Q.IOFN+ATATI ; Set function to attach DIR$ #ATATI ; Attach with AST BCS 10$ ; If error, branch TSTB TISB ; Error code in IOSB? BPL 20$ ; No, continue on 10$: MOV #ERR11,ERRFLG ; "Unable to attach TI:" error BR ERGET ; Issue error message ; ; Put up IO.RCVs and go to sleep (everything is now AST driven) ; 20$: BIS #ST.ATI,STATUS ; Indicate TI: attached DIR$ #RCQIO1 ; Put up IO.RCVs on network BCS ERRCV ; Shutdown on error DIR$ #RCQIO2 ; Put other IO.RCV up too BCS ERRCV ; Shutdown on error DIR$ #RCQIO3 ; Put another IO.RCV up BCS ERRCV ; Shutdown on error WTSE$S #EFDONE ; Wait till we are done TST ERRFLG ; Was there an error? BNE ERGET ; Yes, output the error message MOV #DONE,ERRFLG ; No, write disconnected message BR ERGET ; Note: ERRFLG must be set, after ; CONWRK, before mainline code may ; use registers since ASTs do not ; save them. ERRCV: MOV #ERR12,ERRFLG ; Issue link disconnected error ERGET: MOV ERRFLG,R0 ; Also put it in R0 for ERROR code BR ERROR ; Issue error message ; ; Unsolicited Input from terminal AST ; TIUINT: TST ERRFLG ; Shutting down? BEQ 10$ ; No, so far so good TST (SP)+ ; Yes, ignore character BR 20$ ; and ignore AST ; 10$: MOV (SP)+,R0 ; Put the character in R0 TST MCRCNT ; Any MCR input yet? BNE 40$ ; Yes, then this char is also MCR input BIT #ST.WAI,STATUS ; Output held up? BNE 30$ ; Yes, MCR input then BIT #ST.VAT,STATUS ; Does remote VT: driver want input? BEQ 30$ ; No, so read it in as new MCR input WTSE$S #EFIN ; Wait till last unsol. char sent MOV R0,INCHAR ; Put the character in buffer to send it DIR$ #WRINT ; Send char via interupt message 20$: ASTX$S ; Thats all folks ; ; First character for a MCR command line. ; 30$: CMPB #177,R0 ; Delete character? BEQ 20$ ; Ignore it if so BIS #ST.WAI,STATUS ; Hold up other output WTSE$S #EFMCR ; Wait for MCR buffer to free up CMPB #3,R0 ; Ctrl-C? BNE 40$ ; No DIR$ #WRMCR ; Yes, issue MCR> prompt BIC #ST.DNL,STATUS ; Don't do on MCR input BR 20$ ; And exit AST ; ; Input for MCR command line, add it to buffer ; 40$: CMPB #3,R0 ; Ctrl-C? BEQ 20$ ; Yes, ignore it at this point WTSE$S #EFECHO ; Wait for last term func to complete MOV #ECOBUF,ECHO+Q.IOPL ; Set echo buffer address ; ; Handle Ctrl-Z (EOF) ; CMPB #32,R0 ; Ctrl-Z? BNE 45$ ; No MOV #15,R0 ; Yes, use CR instead MOV #"^Z,ECOBUF ; and echo "^Z" MOV #5015,ECOBUF+2 ; MOV #4,ECHO+Q.IOPL+2 ; Set count DIR$ #ECHO ; Send string to terminal BR 100$ ; Now put CR in buffer and process it ; ; Handle Ctrl-U (cancel line) ; 45$: CMPB #25,R0 ; Ctrl-U? BNE 60$ ; No CLR MCRCNT ; Yes, reset char count to zero MOV #"^U,ECOBUF ; Yes, send a "^U" to terminal MOV #5015,ECOBUF+2 ; MOV #4,ECHO+Q.IOPL+2 ; Set byte count 47$: BIC #ST.RIP,STATUS ; Don't complete any rubout 50$: DIR$ #ECHO ; Send string to terminal BR 20$ ; Return from AST ; ; Handle Ctrl-R (retype line) ; 60$: CMPB #22,R0 ; Ctrl-R? BNE 70$ ; No MOV #5015,MCRBUF-2 ; Yes, send "" MOV #MCRBUF-2,ECHO+Q.IOPL ; Set buffer address MOV #2,ECHO+Q.IOPL+2 ; Figure message count 65$: ADD MCRCNT,ECHO+Q.IOPL+2 ; Correct length BR 47$ ; Send string to terminal ; ; Handle Delete or Rubout (delete last character) ; 70$: CMPB #177,R0 ; Delete (rubout)? BNE 100$ ; No DEC MCRCNT ; Yes, decrement count of chars in buffer MOV MCRCNT,R1 ; New count to R1 TSTB SCOPE+1 ; Is this a CRT terminal? BNE 80$ ; Yes, go handle it for a scope MOVB MCRBUF(R1),ECOBUF ; Put deleted char in echo buffer MOV #1,ECHO+Q.IOPL+2 ; Set count of echo BIT #ST.RIP,STATUS ; Is the echo already started? BNE 50$ ; Yes, no "\" needed BIS #ST.RIP,STATUS ; No, Indicate "\" now sent SWAB ECOBUF ; Put "\" in from of character MOVB #'\,ECOBUF ; MOV #2,ECHO+Q.IOPL+2 ; Change count to 2 bytes BR 50$ ; Echo the characters ; ; Handle Delete for Scope type terminal ; 80$: CMPB MCRBUF(R1),#40 ; Did we delete a ctrl character? BLO 90$ ; Yes, resend line MOV #20010,ECOBUF ; No, send MOV #10,ECOBUF+2 ; MOV #3,ECHO+Q.IOPL+2 ; Set count BR 50$ ; Send the string ; 90$: MOVB #15,MCRBUF-1 ; Put a on front of line MOV #MCRBUF-1,ECHO+Q.IOPL ; Set buffer address MOV #1,ECHO+Q.IOPL+2 ; Figure count to send BR 65$ ; Re-send line ; ; Non special character - check for folding to upper-case ; 100$: TSTB LOWER+1 ; Should case be made upper? BNE 103$ ; No CMPB R0,#'a ; Is it in the range for folding? BLO 103$ ; No CMPB R0,#'z ; Check upper boundry too BHI 103$ ; Not a lower-case character BICB #40,R0 ; Make character upper-case ; ; Non special character - echo it to terminal ; 103$: MOVB R0,ECOBUF ; Set to echo the character MOV MCRCNT,R1 ; Put character in MCR command buffer MOVB R0,MCRBUF(R1) ; INC MCRCNT ; Bump count of chars in buffer CMPB #33,R0 ; Was it an ESC? BEQ 120$ ; Yes, don't echo it back CMP #1,MCRCNT ; No, was it the first char in the buffer? BNE 105$ ; No BIT #ST.DNL,STATUS ; Yes, should we include a ? BEQ 105$ ; No BIC #ST.DNL,STATUS ; Yes, only do it once MOVB ECOBUF,ECOBUF+2 ; Move the character MOV #5015,ECOBUF ; So we can put a on front of it MOV #3,ECHO+Q.IOPL+2 ; Set the count to 3 bytes BR 110$ ; Continue ; 105$: MOV #1,ECHO+Q.IOPL+2 ; Set count to echo BIT #ST.RIP,STATUS ; Echo in progress? BEQ 110$ ; No BIC #ST.RIP,STATUS ; Yes, clear the flag SWAB ECOBUF ; And put "\" in front of char MOVB #'\,ECOBUF ; to indicate end of echo string MOV #2,ECHO+Q.IOPL+2 ; Change count to 2 bytes 110$: DIR$ #ECHO ; Echo the character(s) ; ; See if time to send MCR command to VT: ; CMPB #15,R0 ; Was char a CR? BEQ 115$ ; Yes CMP #M$$CRI,MCRCNT ; Buffer full? BEQ 120$ ; Yes, send MCR command ASTX$S ; Thats all for now ; 115$: BIS #ST.DLF,STATUS ; Do a NL if read is put up ; ; See if local command entered ; 120$: BIS #ST.DNL,STATUS ; Do a new line at this point MOV #MCRBUF,R0 ; Set ptr to buffer in R0 CLR R1 ; Clear R1 to indicate no buffer ptr MOV MCRCNT,R2 ; Put length of string in R2 CMPB #'#,(R0)+ ; Is the first character a "#"? BNE SNDMCR ; Not a local command if it isnt DEC R2 ; Decrement count of characters BEQ SNDMCR ; Not local command if nothing else CMPB #'#,(R0) ; But is the second byte one too? BNE LOCAL ; No, process local command 125$: MOVB (R0),-1(R0) ; Make the two "#"s a single "#". INC R0 ; Move down the line DEC R2 ; For the entire line BNE 125$ ; Loop till done DEC MCRCNT ; Reflect change in count in MCRCNT ; ; Send MCR command line to VT: driver ; SNDMCR: MOV MCRCNT,MCRBUF-2 ; Put count of line in buffer MOV MCRCNT,MCRQIO+Q.IOPL+2 ; Set count for NT: IO.XMT ADD #N.MES-N.BUF,MCRQIO+Q.IOPL+2 ; Add length of header DIR$ #MCRQIO ; Send the MCR command line to VT: ; ; Start any held-up terminal functions ; MRSTRT: BIC #ST.WAI,STATUS ; Don't hold anything else up CLR MCRCNT ; Reset MCR line count MOV WQUEUE,R2 ; Now start up any queued terminal functions CLR WQUEUE ; Clear queue word 130$: MOV R2,R1 ; Copy buffer pointer BEQ 140$ ; Nothing more queued MOV N.CUR(R1),R2 ; Get next buffer address MOV N.TI(R1),R3 ; Get address of TI: QIO CALL QIOTI ; Qio to Ti: BCC 130$ ; If no error, start next one MOV #IE.BAD&377,N.SB1(R1) ; Return IE.BAD error CLRB N.SIZ(R1) ; Zero return size MOV N.XMT(R1),R3 ; Get addr of NT: IO.XMT QIO MOV #N.MES-N.BUF,Q.IOPL+2(R3) ; Set count for transfer MOV R3,N.CUR(R1) ; Indicate IO.XMT active DIR$ R3 ; Start it back over net BR 130$ ; ; 140$: ASTX$S ; Return from AST ; ; QIO a buffer to TI: and set ST.DNL correctly ; ; Enter with: ; ; R1 -> buffer ; R3 -> TI: QIO ; QIOTI: MOV R3,N.CUR(R1) ; Set current function to TI: QIO BIT #ST.WAI,STATUS ; MCR input active? BNE 70$ ; Yes, don't touch ST.DNL MOV R2,-(SP) ; Save R2 MOV Q.IOFN(R3),R2 ; Put function code in R2 BIC #377,R2 ; Clear sub-function bits CMP #IO.KIL,R2 ; I/O kill? BEQ 60$ ; Yes, don't affect the flag CMP #IO.RPR,R2 ; Read with prompt? BEQ 50$ ; Yes, set flag CMP #IO.RLB,R2 ; Read logical? BEQ 10$ ; Yes, check flag CMP #IO.RVB,R2 ; Read virtual? BNE 20$ ; No, continue checking 10$: BIT #ST.DLF,STATUS ; Is the first bit set? BEQ 50$ ; No DIR$ #DONL ; Yes, output first BR 50$ ; Then do the read ; 20$: CMP #IO.WVB,R2 ; Write virtual? BEQ 30$ ; Yes, check for prompt CMP #IO.WLB,R2 ; Write logical? BNE 60$ ; No, don't affect flag then 30$: CMP #44,Q.IOPL+4(R3) ; Is the VFC prompt mode? BEQ 40$ ; Yes, clear the flag MOV Q.IOPL(R3),R2 ; Create pointer to last byte ADD Q.IOPL+2(R3),R2 ; DEC R2 ; CMPB #'>,(R2) ; Last char a ">"? BNE 50$ ; No, set flag 40$: BIC #ST.DNL!ST.DLF,STATUS ; Yes, clear flag BR 60$ ; Continue ; 50$: BIS #ST.DNL,STATUS ; We will want a new line BIC #ST.DLF,STATUS ; No LF before read 60$: MOV (SP)+,R2 ; Restore R2 70$: DIR$ R3 ; Start QIO going to TI: RETURN ; Return to caller ; ; Check for a local command ; LOCAL: CMPB #'C,(R0) ; CLOSE command? BEQ CLOSE ; Yes, go do close CMPB #'E,(R0) ; How about END or EXIT? BEQ CLOSE ; Yes, they will do too CMPB #'D,(R0) ; DISCONNECT? BEQ CLOSE ; Yes, do close CMPB #'P,(R0) ; Is it the PAUSE command? BEQ PAUSE ; Yes, go do pause CMPB #'S,(R0) ; SUSPEND? BEQ PAUSE ; Yes, go do pause CMPB #'c,(R0) ; CLOSE command? BEQ CLOSE ; Yes, go do close CMPB #'e,(R0) ; How about END or EXIT? BEQ CLOSE ; Yes, they will do too CMPB #'d,(R0) ; DISCONNECT? BEQ CLOSE ; Yes, do close CMPB #'p,(R0) ; Is it the PAUSE command? BEQ PAUSE ; Yes, go do pause CMPB #'s,(R0) ; SUSPEND? BEQ PAUSE ; Yes, go do pause MOV R1,-(SP) ; Save buffer ptr on stack MOV #NOTCM,R0 ; Issue not a local command error JMP RETI ; Re-issue read after ; ; Local command entered ; ; #CLOSE - close connection and return to local MCR ; #PAUSE or #SUSPEND - temporarily exit ...CON program ; #END or #EXIT or #DISCONNECT - same as #CLOSE ; CLOSE: MOV #DONE,ERRFLG ; Disconnect and close down SETF$S #EFDONE ; Shutdown ASTX$S ; Return from AST ; ; Pause (return to MCR until RESume command) ; PAUSE: MOV R1,-(SP) ; Save ptr to current buffer on stack DIR$ #GTSK ; Get the name of our task MOV #PTASK,R0 ; Point at where to put task name MOV TSKBUF,R1 ; Get first half of task name (RAD50) CALL $C5TA ; Convert name to ASCII MOV TSKBUF+2,R1 ; Get second half of task name CALL $C5TA ; Convert it too to ASCII 10$: MOV #PMESS,R0 ; Get address of message to write CALL MESSAG ; Write the message MOV #IO.DET,Q.IOFN+ATATI ; Disconnect TI: DIR$ #ATATI ; SPND$S ; Return to MCR MOV #IO.ATA,Q.IOFN+ATATI ; Reconnect to TI: DIR$ #ATATI ; BCS 10$ ; If error, suspend again MOV #RCON,R0 ; Issue reconnected message TST (SP) ; Did we come from MCR input code? BEQ RETI ; Yes, don't issue it with prompt MOV #RCON2,R0 ; No, issue prompt with message RETI: CALL MESSAG ; Issue message BIC #ST.DNL,STATUS ; We don't want at this point MOV (SP)+,R1 ; Retrieve original buffer ptr BNE 5$ ; If non-zero continue JMP MRSTRT ; If zero, we came from MCR input code ; 5$: MOV N.TI(R1),R3 ; R3 -> TI: QIO CMPB #IO.RPR/400,Q.IOFN+1(R3) ; Was the function read prompt? BNE 10$ ; No MOV #IO.RLB,Q.IOFN(R3) ; Reset function to just read 10$: CALL QIOTI ; Re-issue TI: read QIO BCS 20$ ; Error on directive? ASTX$S ; No, Exit AST 20$: JMP TIBAD ; Yes, return IE.BAD on IO.XMT to NT: ; ; Terminal operation completed AST ; TIAST: CALL ASTSET ; R1 -> IOSB MOV N.FUN(R1),R2 ; Get I/O function code CMP #IO.KIL,R2 ; Was the function kill I/O ? BNE 20$ ; No MOV #NET1SB,R4 ; Yes, check each buffer for killed I/O CALL KILCHK ; because when I/O gets killed MOV #NET2SB,R4 ; AST doesn't get executed CALL KILCHK ; MOV #NET3SB,R4 ; CALL KILCHK ; BR 60$ ; Complete IO.KIL ; 20$: BIC #377,R2 ; Ignore subfunction bits CMP #IO.RLB,R2 ; Was the function Read Logical Block? BEQ 30$ ; Yes, check for local command CMP #IO.RVB,R2 ; How about Read Virtual Block? BEQ 30$ ; Yes, check for local command CMP #IO.RPR,R2 ; Last chance, Read after PRompt? BNE 60$ ; No, just return I/O status, etc 30$: MOV 2(R1),R2 ; Get length of line in R2 BEQ 50$ ; No characters read, just return read MOV R1,R0 ; Generate a ptr to the buffer ADD #N.MES,R0 ; R0 points at message buffer CMPB #'#,(R0)+ ; Is the first character a "#"? BNE 50$ ; Not a local command if it isnt DEC R2 ; Decrement count of characters BEQ 50$ ; If single "#" just send it CMPB #'#,(R0) ; But is the second byte one too? BEQ 40$ ; Yes, remove the extra #. JMP LOCAL ; No, check for local command ; 40$: MOVB (R0),-1(R0) ; Make the two "#"s a single "#". INC R0 ; Move down the line DEC R2 ; For the entire line BNE 40$ ; Loop till done DEC 2(R1) ; Line is one character shorter now 50$: MOVB 2(R1),N.SIZ(R1) ; Yes, return only number of characters read 60$: MOV (R1),N.SB1(R1) ; Return I/O status block in buffer MOV 2(R1),N.SB2(R1) ; Second word too MOV N.XMT(R1),R3 ; Get address of IO.XMT QIO MOVB N.SIZ(R1),Q.IOPL+2(R3) ; Figure length of return buffer CLRB Q.IOPL+3(R3) ; Clear high order bits ADD #N.MES-N.BUF,Q.IOPL+2(R3) ; Add in 4 header words MOV R3,N.CUR(R1) ; Indicate IO.XMT active on buffer DIR$ R3 ; Return to sender BCS LNKERR ; Branch if error ASTX$S ; Exit terminal AST ; ; Check for terminal I/O killed by IO.KIL ; (AST gets flushed by IO.KIL) ; ; Enter with: ; ; R4 -> buffer to check ; R1 -> IO.KIL buffer ; KILCHK: CMP R4,R1 ; Is it the IO.KIL buffer? BEQ 20$ ; Yes, it doesn't kill itself TST N.CUR(R4) ; Is I/O active on the buffer? BEQ 20$ ; No, couldn't have killed it then MOV R1,-(SP) ; Save old buffer pointer MOV R2,-(SP) ; Save R2 also MOV N.CUR(R4),R2 ; Get ptr to current QIO CMPB #LUNTI,Q.IOLU(R2) ; Did QIO go to TI:? BNE 10$ ; No, IO.KIL didn't kill it CMPB #IE.ABO,(R4) ; Did IE.ABO get returned? BNE 10$ ; No, IO.KIL didn't kill it CLRB N.SIZ(R4) ; Yes, found an aborted I/O function MOV (R4),N.SB1(R4) ; Move I/O status block results MOV 2(R4),N.SB2(R4) ; MOV N.XMT(R4),N.CUR(R4) ; Set current QIO to IO.XMT DIR$ N.XMT(R4) ; Send aborted results back to caller BCC 10$ ; Branch if no error MOV #ERR12,ERRFLG ; Indicate error occured SETF$S #EFDONE ; Shutdown 10$: MOV (SP)+,R2 ; Restore registers MOV (SP)+,R1 ; 20$: RETURN ; Return to caller ; ; Message received (IO.RCV completed) from network AST ; RCVAST: CALL ASTSET ; R1 -> IOSB BPL RCVOK ; Branch if no error LNKERR: MOV #ERR12,ERRFLG ; Indicate error occured SETF$S #EFDONE ; Shutdown ASTX$S ; Exit IO.RCV AST ; ; Fill in terminal QIO DPB ; RCVOK: MOV N.FUN(R1),R2 ; Get I/O function code MOV N.TI(R1),R3 ; Get addr of TI: QIO MOV R2,Q.IOFN(R3) ; Put function code in QIO MOV R1,Q.IOPL(R3) ; Set up parameters for QIO ADD #N.MES,Q.IOPL(R3) ; Put buffer address in 1st parameter MOVB N.SIZ(R1),Q.IOPL+2(R3) ; Put size in 2nd parameter CLRB Q.IOPL+3(R3) ; Clear high order bits MOV N.VFC(R1),Q.IOPL+4(R3) ; Put VFC code in 3rd parameter MOV Q.IOPL(R3),Q.IOPL+6(R3) ; Copy buffer address (for IO.RPR) MOV N.PSZ(R1),Q.IOPL+10(R3) ; Put prompt size in MOV N.VFC(R1),Q.IOPL+12(R3) ; Put VFC code in again CLR N.SB1(R1) ; Init return values CLR N.SB2(R1) ; ; ; Check for IO.ATA and IO.DET functions ; CMP #IO.ATA,R2 ; IO.ATA - to indicate remote task wants BNE 10$ ; unsolicited input? BIS #ST.VAT,STATUS ; Yes, indicate unsolicited input to be sent BR TIRTN ; Return message to VT: ; 10$: CMP #IO.DET,R2 ; IO.DET - to indicate remote task no longer BNE 20$ ; wants input? BIC #ST.VAT,STATUS ; Yes, clear the flag BR TIRTN ; And return message now ; ; Check for other functions and issue QIO to TI: ; 20$: BIC #377,R2 ; Clear subfunction bits CMP #IO.WLB,R2 ; Was function a write logical block? BEQ 30$ ; Yes, don't need to return data portion CMP #IO.WVB,R2 ; Was function a write virtual block? BEQ 30$ ; Yes, dothing to return CMP #SF.SMC,N.FUN(R1) ; Is function set multiple characteristics? BNE 40$ ; No, return entire buffer CLRB N.SIZ(R1) ; Don't return data portion with message BR 40$ ; Continue ; 30$: CLRB N.SIZ(R1) ; Don't return data portion with message BITB #TF.WBT,Q.IOFN(R3) ; Is a write break through? BEQ 40$ ; No CALL QIOTI ; Start the write break through BCS TIBAD ; If directive error, handle it now TST MCRCNT ; Is there any MCR input active? BEQ ASTX ; No MOV #22,-(SP) ; Yes, simulate a Ctrl-R JMP TIUINT ; ; 40$: BIT #ST.WAI,STATUS ; Should we hold up stuff to terminal? BEQ 70$ ; No, give it to terminal CLR N.CUR(R1) ; Yes, clear forward chain pointer MOV #WQUEUE-N.CUR,R2 ; And queue the buffer on wait chain 50$: TST N.CUR(R2) ; Queue them FIFO on chain, found end yet? BEQ 60$ ; Found end MOV N.CUR(R2),R2 ; Not end, follow chain BR 50$ ; ; 60$: MOV R1,N.CUR(R2) ; Add us to end of chain waiting on flag BR ASTX ; And exit AST ; 70$: CALL QIOTI ; QIO to TI: BCS TIBAD ; If directive error, branch ASTX: ASTX$S ; No error, just return ; ; Directive error on TI: functon - return it over NT: now ; TIBAD: MOV #IE.BAD&377,N.SB1(R1) ; If directive error, return IE.BAD TIRTN: CLRB N.SIZ(R1) ; Zero size parameter MOV N.XMT(R1),R3 ; Get address of IO.XMT QIO MOV #N.MES-N.BUF,Q.IOPL+2(R3) ; Only 4 header words MOV R3,N.CUR(R1) ; Indicate IO.XMT active on this buffer DIR$ R3 ; Return to sender BCC ASTX ; Exit AST if no error JMP LNKERR ; Go processes error ; ; Message sent (IO.XMT completed) from network AST ; XMTAST: CALL ASTSET ; Set up everything BMI 10$ ; Shutdown on error MOV N.RCV(R1),R3 ; Get ptr to IO.RCV QIO MOV R3,N.CUR(R1) ; Indicate IO.RCV active on this buffer DIR$ R3 ; Put IO.RCV up for the buffer BCC ASTX ; Exit AST if no error 10$: JMP LNKERR ; Go process error ; ; Initial set up for I/O ASTs ; ASTSET: TST ERRFLG ; Are we shutting down? BEQ 10$ ; No CMP (SP)+,(SP)+ ; Yes, pop stack (don't change registers!) ASTX$S ; And ignore AST ; 10$: MOV (SP)+,R5 ; Get return address MOV (SP)+,R1 ; Get address of I/O status block CLR N.CUR(R1) ; Indicate no I/O active TSTB (R1) ; Was there an error? JMP (R5) ; Return with R1 -> IOSB & carry set correctly ; ; DECNET exception AST ; (Remote TELNET task disconnected or aborted) ; EXAST: CMP (SP)+,(SP)+ ; Clean stack TST ERRFLG ; Are we already quitting? BNE 10$ ; Yes, ignore AST MOV #ERR14,ERRFLG ; No, set error message SETF$S #EFDONE ; Start shutdown 10$: ASTX$S ; Exit exception AST ; ; Convert ASCII to RAD50 (including blanks) ; ; R0 - Address of next char in input buffer ; R1 - Packed RAD50 characters ; ; Carry set if invalid character found ; CAT5B: MOV R3,-(SP) ; Save R3 on stack MOV R4,-(SP) ; Save R4 on stack MOV R5,-(SP) ; Save R5 on stack MOV #CVTBL,R3 ; Get address of conversion table CLR R1 ; Clear accumulation 10$: MOVB (R0)+,R5 ; Get a character MOV #CNTRL,R4 ; Point at ctrl byte string CMPB (R4)+,R5 ; RAD50 character? BLO 60$ ; If lo no CMPB (R4)+,R5 ; Alphabetic? BLOS 50$ ; If los yes CMPB (R4)+,R5 ; RAD50 character? BLO 60$ ; If lo no CMPB (R4)+,R5 ; Numeric? BLOS 40$ ; If los yes CMPB (R4)+,R5 ; "$" ? BEQ 30$ ; If eq yes CMPB (R4)+,R5 ; " " ? BEQ 20$ ; If eq yes CMPB (R4)+,R5 ; "." ? BEQ 40$ ; If eq yes 60$: SEC ; Not RAD50 characters 70$: MOV (SP)+,R5 ; Restore R5 MOV (SP)+,R4 ; Restore R4 MOV (SP)+,R3 ; Restore R3 RETURN ; Error return ; 20$: SUB #' -11,R5 ; Space 30$: SUB #11-22,R5 ; "$" 40$: SUB #22-100,R5 ; Period/digit 50$: SUB #100,R5 ; Alphabetic MUL (R3)+,R5 ; Scale RAD50 character ADD R5,R1 ; Accumulate result CMP R3,#CVTBL+6 ; Converted three yet? BLO 10$ ; If lo no CLC ; Return result in R1 BR 70$ ; Successful return ; CVTBL: .WORD 50*50 ; RAD50 pack multiplication table .WORD 50 .WORD 1 CNTRL: .BYTE 'Z,'A,'9,'0,'$,' ,'. ; Control byte string .EVEN .END START ; End of code