.TITLE TELNET - TELNET FOR TCP .SBTTL GLOBAL DECLARATIONS .INSRT ../../mos/moscnf-1.sml .INSRT mosdev-1.sml .INSRT ../../mos/mosmac.sml .INSRT ../../mos/mostbl.sml .NLIST ME .LIST MEB .ENABL ISD .CSECT TELNTI .GLOBL $TELNT ;TELNET ENTRY POINT .GLOBL $HTTBL ;HOST (TERMINAL) NAME/ID TABLE .GLOBL $NTTBL ;NETOWRK NAME TABLE .GLOBL $SKTBL ;SERVICE SOCKET NAME TABLE .GLOBL $NETID,$HSTID ;LOCAL NET,TERMINAL ID .GLOBL $SEND,$RECV ;TCP INTERFACE ROUTINES .GLOBL $OPEN,$CLOSE .SBTTL DATA BASE EQUATES $CNFIG ;DEFINE CONFIGURATION OF SYSTEM SNDBL = 190. ;LENGTH OF SEND BUFFER RECBL = 190. ;LENGTH OF RECEIVE BUFFER ECOBL = 20. ;LENGTH OF ECHO BUFFER CMDBL = 40. ;LENGTH OF USER COMMAND BUFFER MSGBL = 20. ;LENGTH OF TELNET MESSAGE QUEUE $DFIOR ;FORMAT OF I/O REQUEST BLOCK ;DEFINE QUEUE MANAGEMENT ENTRIES ..OFST=0 WORD HEAD ;HEAD POINTER (NEXT CHAR TO TAKE) WORD TAIL ;TAIL POINTER (NEXT EMPTY CELL) WORD UPLIM ;UPPER LIMIT OF QUEUE +1 WORD LOLIM ;LOWER LIMIT OF QUEUE WORD BYTCNT ;NUMBER OF ENQUEUED CHARS WORD ORIG ;START OF QUEUE ;DEFINE DATA BASE (TELNET CONTROL TABLE) ..OFST=0 WORD STATE ;STATE WORD WORD DSTATE ;DEFAULT STATES BYTE TTLID ;TELNET ID (used as local socket) BYTE TCPCID ;TCP connection ID BYTE TNCNT ;NUMBER BYTES OF TELNET CMD STREAM RCVD BYTE TNCMD ;TELNET COMMAND RECEIVED BYTE TNOPT ;TELNET OPTION RECEIVED BYTE INTCHR ;COMMAND INTERCEPT CHARACTER WORD SNDCNT ;NUMBER OF BYTES IN THE LAST $SEND WORD SNDURG ;OFFSET INTO SNDBUF FOR END OF URGENT DATA BYTE INPBUF ;INPUT BYTE BUFFER BYTE OUTBUF ;OUTPUT BYTE BUFFER BYTE TRFCHR ;NEXT TRAFFIC CHARACTER TO GENERATE BYTE TRFCNT ;NUMBER OF TRAFFIC CHARS GENERATED WORD CANVT ;CANNED MESSAGE INTERVAL OPNBLK = ..OFST ;START OF TCP OPEN PARAMETER BLOCK WORD LPORT ;LOCAL TCP PORT ID WORD FNET ;FOREIGN TCP NET ID WORD FTCP ;FOREIGN TCP TCP ID WORD FPORTH ;FOREIGN PORT HIGH ID WORD FPORTL ;FOREIGN PORT LOW ID BUFFER INIORB,IORBL ;CHARACTER INPUT IORB BUFFER OTIORB,IORBL ;CHARACTER OUTUT IORB BUFFER SNDBUF,ORIG+SNDBL ;SEND TO NETWORK BUFFER BUFFER RECBUF,ORIG+RECBL ;NET RECEIVE BUFFER BUFFER ECOBUF,ORIG+ECOBL+2 ;LOCAL ECHO BUFFER BUFFER CMDBUF,ORIG+CMDBL ;USER COMMAND BUFFER BUFFER MSGBUF,ORIG+MSGBL ;TELNET MESSAGE QUEUE TCTLEN = ..OFST ;LENGTH OF TELNET CONTROL TABLE .PAGE .SBTTL GENERAL EQUATES ;STATUS WORD EQUATES NEWDAT = 200 ;TCP RECEIVE DATA AVAILABLE SNDRDY = 400 ;TCP SEND COMLETED CHRSND = 1000 ;DATA (CHARS) READY TO BE SENT RNGBEL = 2000 ;TTY BUFFER(S) FULL TTYIDL = 10000 ;TTY OUTPUT DEVICE IDLE CONOPN = 20000 ;VALID CONNECTION OPEN ;SIGNAL EQUATES ; SG.INP = 10 ;INPUT CHAR COMPLETION SG.OUT = 11 ;OUTPUT CHARACTER COMPLETION SG.ECO = 12 ;ECHO AND STATUS MSG OUTPUT COMPLETION SG.TIM = 13 ;TIMER SIGNAL SG.CMT = 14 ;CANNED MESSAGE TIMER SIGNAL SG.MAX = 15 ;STATE (AND DSTATE) WORD EQUATES RTCEMD = 1 ;ECHO & TRANSMISSION CONTROL MODE ; (0= LOCAL, 1= REMOTE) EMODE = 2 ;ECHO MODE (0=LOCAL, 1=REMOTE) REMODE = 4 ;REQ. ECHO MODE (0=LOCAL, 1=REMOTE) TMODE = 30 ;TRANSMISSION MODE TLMODE = 10 ; =1, LINE AT A TIME TRANSMISSION MODE TWMODE = 20 ; =1, WORD TRANSMISSION MODE PRTCOL = 100 ;PROTOCOL (0=TELNET, 1=XPARENT) STKLSN = 200 ;STICKY LISTEN MODE OPNISS = 400 ;INDICATES THAT AN OPEN HAS BEEN ISSUED GENTRF = 1000 ;INDICATES TRAFFIC GENERATOR ACTIVE USRIDL = 2000 ;USER IDLE, HASN'T TYPED CHAR BINMOD = 4000 ;BINARY TRANSMISSION MODE PRIVLG = 10000 ;PRIVILEGED USER (0=NO, 1=YES) ;TELNET COMMAND AND OPTIONS EQUATES IAC = 377 ;INTERPRET AS COMMAND WILL = 373 WONT = 374 DO = 375 DONT = 376 ECHO = 1 SUPRGA = 3 ;SUPPRESS GO-AHEAD IP = 364 ;INTERRUPT PROCESS DM = 362 ;DATA MARK ;ASCII CHARACTERS NULL = 0 CNTRLG = 7 LF = 12 CR = 15 SPACE = 40 COMMA = 54 DEL = 177 UPPER = 40 ;MASK FOR FOLDING TO UPPER CASE ;CHARACTER TYPE TABLE ENTRIES (LOW 4 BITS, RCTE CHAR CLASS. HI 4 BITS, FLAGS) CHCLS1 = 1 ;RCTE CHAR CLASS 1 - UPPER-CASE LETTERS CHCLS2 = 2 ;RCTE CHAR CLASS 2 - LOWER-CASE LETTERS CHCLS3 = 3 ;RCTE CHAR CLASS 3 - NUMBERS CHCLS4 = 4 ;RCTE CHAR CLASS 4 - FORMAT EFFECTORS CHCLS5 = 5 ;RCTE CHAR CLASS 5 - OTHER CONTROL CHARS CHCLS6 = 6 ;RCTE CHAR CLASS 6 - .,;:?! CHCLS7 = 7 ;RCTE CHAR CLASS 7 - [(<>)] CHCLS8 = 10 ;RCTE CHAR CLASS 8 - '"/\%@$&#+=*=^_ CHCLS9 = 11 ;RCTE CHAR CLASS 9 - CHNECO = 1*20 ;DON'T ECHO THIS CHAR WHEN IN LOCAL MODE CHXMT = 2*20 ;TRANSMIT ON THIS CHAR WHEN IN LINE MODE ;DEFAULT PARAMETERS DFMODE = REMODE ;DEFAULT MODES = REMOTE ECHO, ; TRANSMIT ON CHAR, DFTSK1 = 23. ;DEFAULT FOREIGN SOCKET NUMBER, LOW DFTSK2 = 0 ;DEFAULT FOREIGN SOCKET NUMBER, HI LCLSOK = 0. ;LOCAL SOCKET FOR TELNET ;If 0, use TELNET counter NULLRQ = 5. ;NUMBER NULLS REQUIRED FOR PADDING CR'S .PAGE .SBTTL MACRO DEFINITIONS ;MACROS TO CALL TCP .MACRO $RECV BR ;ISSUE RECEIVE MOV BR,R0 ;SET BYTES REQUESTED JSR PC,$RECV ;CALL THE TCP .ENDM .MACRO $SEND ;ISSUE SEND JSR PC,$SEND ;CALL THE TCP .ENDM .MACRO $OPEN ;ISSUE OPEN JSR PC,$OPEN ;CALL THE TCP .ENDM .MACRO $CLOSE ;ISSUE CLOSE JSR PC,$CLOSE ;CALL THE TCP .ENDM ;MACROS FOR LOCAL I/O .MACRO $TOUT CHAR ;OUTPUT A CHARACTER .IF NB CHAR ;SKIP NEXT IF NO ARGUMENT MOVB CHAR,R0 .ENDC JSR PC,$TOUT .ENDM .MACRO $READ DEST ;READ IN A CHARACTER MOVB INPBUF(TCT),DEST ;GET CHAR FROM I/O BUFFER JSR PC,$READ ;AND START ANOTHER INPUT .ENDM .MACRO $TYPE MSG MOV MSG,R0 ;GET INDEX OF MSG ENQC #MSGBUF ;AND PUT IN MSG QUEUE .ENDM ;MACROS FOR MANIPULATING DATA BASE .MACRO IBUF BUF,LEN ;INITIALIZE A BUFFER MOV BUF,R1 ;SET BUFFER OFFSET MOV LEN,R0 ;SET BUFFER LENGTH JSR PC,IBUF ;CALL BUFFER INITIALIZER .ENDM .MACRO ENQC BUFFER ;ENQUEUE A CHARACTER IN A BUFFER MOV BUFFER,R1 ;SET BUFFER OFFSET JSR PC,ENQ1 ;CALL ENQUEUE ROUTINE .ENDM .MACRO DEQ BUFFER ;DEQUEUE A CHARACTER FROM A BUFFER MOV BUFFER,R1 ;SET BUFFER OFFSET JSR PC,DEQ ;CALL DEQUEUE ROUTINE .ENDM ; ; REGISTER USAGE ; TCT = %5 ;ADDR OF CURRENT TELNET CONTROL TABLE STATUS = %4 ;CURRENT CONNECTION STATUS .PAGE .SBTTL TELNET INITIALIZATION ;TELNET INITIALIZATION ROUTINE. INITIALIZE ALL BUFFERS AND SET ALL ;PROGRAM CONSTANTS TO DEFAULT OR INITIAL VALUES. $TELNT: $AVS #TCTLEN MOV R2,TCT ;GET TCT ADDRESS $GAPID ;Get our PID TSTB TTLFLG ;TELNET table initialized BEQ 1$ ;If not, do it CMPB R0,TTLFLG ;Did we init it last time BNE 2$ ;If not, skip 1$: MOVB R0,TTLFLG ;Set PID of initializer (us) CLRB TTLCNT ;and clear count of telnets 2$: INCB TTLCNT ;Bump TELNET count MOVB TTLCNT,TTLID(TCT) ;and remember as local ID ;INITIALIZE BUFFERS AND CONSTANTS IBUF #MSGBUF,#MSGBL ;INIT MESSAGE QUEUE IBUF #CMDBUF,#CMDBL ;INIT COMMAND BUFFER IBUF #ECOBUF,#ECOBL+2 ;INIT ECHO BUFFER IBUF #RECBUF,#RECBL ;INIT NET RECEIVE BUFFER IBUF #SNDBUF,#SNDBL ;INIT NET SEND BUFFER CLR SNDCNT(TCT) ;RESET SEND BYTE COUNT CLR SNDURG(TCT) ;RESET URGENT DATA PTR CLRB TNCNT(TCT) ;INIT TELNET STREAM COUNT MOV #DFMODE,STATE(TCT) ;INIT STATE WORD MOV #DFMODE,DSTATE(TCT) ; AND DEFAULT STATE WORD MOV #TTYIDL!SNDRDY,STATUS ;INIT STATUS WORD $TYPE #MSGHDR ;OUTPUT HEADER MESSAGE MOVB #'&,INTCHR(TCT) ;SET DEFAULT INTERCEPT CHAR MOV TCT,R0 ;GET TCT ADDR ADD #INIORB,R0 ; ADVANCE TO INPUT IORB MOV #-1,IRDEV(R0) ;SET DEVICE TO PRIMARY INPUT MOVB #SG.INP,IROPC(R0) ;INPUT SIGNAL OPCODE MOV TCT,IRUVA(R0) ;SET UP BUFFER POINTER ADD #INPBUF,IRUVA(R0) ; INTO TCT MOV #1,IRBR(R0) ;AND ASK FOR 1 BYTE MOV TCT,R0 ;GET TCT ADDR ADD #OTIORB,R0 ; ADVANCE TO OUTPUT IORB MOV #-2,IRDEV(R0) ;SET DEVICE TO PRIMARY INPUT $READ R0 ;START CHARACTER INPUT GOING JSR PC,HNDOUT ;HANDLE TTY OUTPUT DATA .PAGE .SBTTL MAIN LOOP - EVENT WAIT ;THE MAIN LOOP STARTS WITH AN EVENT WAIT, FOLLOWED BY TESTS FOR ;THE OCCURENCE OF EACH OF THE POSSIBLE EVENT CLASSES: ; 1. STATUS CHANGE EVENT--REQUIRES USER NOTIFICATION OF CHANGES. ; 2. NET INPUT EVENT--REQUIRES DISPATCH OF ROUTINE TO ACCEPT ; THE DATA. ; 3. TTY INPUT EVENT--REQUIRES DISPATCH OF ROUTINES TO ACCEPT THE ; DATA AND HANDLE IT ACCORDING TO TYPE (USER DATA OR USER ; COMMAND). ; 4. TCP SEND COMPLETION EVENT--REQUIRES SENDING OF ANY AVAILABLE DATA ; TO NET VIA ANOTHER TCP $SEND CALL. ; 5. TTY OUTPUT COMPLETION EVENT--REQUIRES OUTPUT OF NEXT BYTE ; IN OUTPUT STREAM TO TTY. ;FLAGS FOR EACH OF THESE EVENTS ARE 'NEWDAT', 'TTYCHR', ;'SNDRDY', AND 'TTYIDL' FOR 2 THROUGH 5 RESPECTIVELY. ;THERE ARE 8 FLAGS CORRESPONDING TO EVENT 1, WHICH ARE LISTED BELOW. ;CONDITIONAL TESTS WITHIN THE MAIN LOOP INHIBIT THE DISPATCH OF ROUTINES ;TO HANDLE ANY EVENT UNLESS THOSE ROUTINES WILL ALL BE ABLE TO ;RUN TO COMPLETION AT THAT TIME. THUS, EVENTS ARE HELD IN ;ABEYANCE UNTIL THEY CAN BE HANDLED IN A STRAIGHT THROUGH TO COMPLETION ;FASHION. NOTE THAT MOST OF THE SERVICE ROUTINES ARE INLINE WITH THE ;MAIN LOOP. MAINLP: $WAIT ;WAIT FOR AN EVENT BIC #377*400,R0 ;ZAP PID, LEAVING OPCODE CMPB R0,#SG.MAX ;ILLEGAL SIGNAL BHIS 1$ ;IF SO, ERROR ASL R0 ;CONVERT TO WORD INDEX JSR PC,@TELOPC(R0) ;AND CALL SIGNAL HANDLER ROUTINE JSR PC,HNDOUT ;HANDLE TTY OUTPUT DATA JSR PC,HNDRCV ;HANDLE NET RECEIVE DATA STREAM JSR PC,HNDSND ;HANDLE NET SEND DATA STREAM JSR PC,HNDOUT ;HANDLE TTY OUTPUT DATA BR MAINLP ;AND GET NEXT SIGNAL 1$: $ERROR TELOPC: .WORD TCPOPN ;CONNECTION OPEN .WORD TCPSTS ;(CONNECTION ERROR) .WORD TCPSTS ;(TCP NOT RESPONDING) .WORD TCPCLS ;CONNECTION CLOSED .WORD TCPRCV ;TCP RECEIVE DATA READY .WORD TCPSTS ;(CONNECTION REFUSED) .WORD TCPSND ;TCP SEND COMPLETION .WORD TCPSTS ;(REMOTE CLOSE) .WORD TTYIN ;TTY INPUT COMPLETE .WORD TTYOUT ;TTY OUTPUT COMPLETE .WORD ECHOUT ;ECHO & STATUS MSG OUTPUT COMPLETION .WORD TIMER ;ONCE-A-SEC TIMER EXPIRATION .WORD CNTIME ;CANNED MSG TIMER EXPIRATION .PAGE .SBTTL TCPOPN - TCP CONNECTION OPEN SIGNAL TCPOPN: BIS #CONOPN!SNDRDY,STATUS ;INDICATE CONNECTION OPEN & TCP READY BR TCPSTS ;AND OUTPUT STATUS CHANGE MSG TCPCLS: MOV DSTATE(TCT),STATE(TCT) ;RESET ALL STATE INFO TO DEFAULTS BIC #^C,STATE(TCT) ;AND ;ONLY THOSE THAT DEFAULT BIC #CONOPN!NEWDAT,STATUS ;INDICATE CONNECTION CLOSED BIT #STKLSN,STATE(TCT) ;IN STICKY LISTEN MODE? BEQ TCPSTS ;IF NOT, SKIP $PUSH R0 ;SAVE SIGNAL $TYPE #MSGRPN ;TELL USER WE ARE RE-OPENING JSR PC,REOPEN ;TRY TO RE-OPEN THE CONNECTION NOP ;IGNORE ERROR CONDITIONS $POP R0 RTS PC TCPSTS: $TYPE R0 ;ENQUEUE STATUS MESSAGE RTS PC .PAGE .SBTTL TCPSND - TCP SEND COMPLETE SIGNAL TCPSND: BIS #SNDRDY,STATUS ;INDICATE SEND SIDE IDLE SUB SNDCNT(TCT),SNDURG(TCT) ;ACCOUNT FOR BYTES SENT BGE 1$ CLR SNDURG(TCT) 1$: SUB SNDCNT(TCT),SNDBUF+BYTCNT(TCT) ;ACCOUNT FOR BYTES SENT BNE 2$ ;IF BUFFER NOT EMPTY, SKIP BIC #CHRSND,STATUS ;INDICATE NOTHING TO SEND MOV SNDBUF+LOLIM(TCT),SNDBUF+HEAD(TCT) ;RESET HEAD, TAIL MOV SNDBUF+LOLIM(TCT),SNDBUF+TAIL(TCT) ; TO START OF BUFFER RTS PC 2$: ADD SNDCNT(TCT),SNDBUF+HEAD(TCT) ;ADVANCE HEAD COUNTER CMP SNDBUF+HEAD(TCT),SNDBUF+UPLIM(TCT) ;REACHED END? BLO 3$ ;IF NOT, SKIP SUB #SNDBL,SNDBUF+HEAD(TCT) ;ELSE, WRAP AROUND TO FRONT 3$: RTS PC .PAGE .SBTTL TCPRCV - NET INPUT EVENT HANDLER ;DATA FROM THE NET IS AVAILABLE. IF RECBUF IS EMPTY, RECEIVE A NEW BUFFER ; FULL, ELSE THIS EVENT CANNOT BE HANDLED NOW. TCPRCV: TST RECBUF+BYTCNT(TCT) ;RECEIVE BUFFER EMPTY? BNE 1$ ;IGNORE SIGNAL BIC #NEWDAT,STATUS ;CLEAR TCP DATA READY FLAG MOV RECBUF+LOLIM(TCT),R1 ;GET LOW LIMIT OF RECEIVE QUEUE MOV R1,RECBUF+HEAD(TCT) ;RESET HEAD $RECV #RECBL ;ISSUE THE RECEIVE MOV R0,RECBUF+BYTCNT(TCT) ;SET BYTES XFERRED RTS PC 1$: BIS #NEWDAT,STATUS ;INDICATE NET RECV DATA WAITING RTS PC .PAGE .SBTTL TIMER - ONCE-A-SEC TIMER SIGNAL EVENT HANDLER TIMER: BIT #TWMODE,STATE(TCT) ;TRANSMIT WORD MODE? BEQ 1$ ;IF NOT, SKIP $STIME #SG.TIM,#0,#60. ;RESET THE TIMER BIT #USRIDL,STATE(TCT) ;HAS THE USER BEEN IDLE BEQ 1$ ;IF NOT, DON'T FORCE TRANSMISSION TST SNDBUF+BYTCNT(TCT) ;ANY CHARS ENQUEUED? BEQ 1$ ;IF NOT, SKIP BIS #CHRSND,STATUS ;ELSE, DECIDE TO TRANSMIT THE LINE 1$: BIS #USRIDL,STATE(TCT) ;SET FLAG RTS PC .PAGE .SBTTL CNTIME - PROCESS CANNED MESSAGE TIMER SIGNAL ; ;THIS EVENT HANDLER RESETS THE INTER-CANNED MESSAGE TIMER AND DELIVER ;ONE CANNED MESSAGE TO THE TTY INPUT EVENT HANDLER ONE CHARACTER AT ;A TIME. THE FORMAT OF THE MSG IS (CANNED MSG). THIS MSG ;IS SENT DEPENDING ON THE TRANMISSION MODE IN EFFECT (CHAR,WORD, OR LINE). ; CNTIME: MOV CANVT(TCT),R2 ; GET INTER-MSG TIME BEQ 20$ ; QUIT IF ZERO TIME $STIME #SG.CMT,#0,R2 ; RESTART INTER-MSG TIMER MOV #CANBUF,R2 ; R2 = A(CANNED MSG) 10$: MOVB (R2)+,R0 ; PICK-UP NEXT CHAR OF MSG BEQ 20$ ; MSG ENDS WITH ZERO BYTE $PUSH R0,R2 ; SAVE REGISTERS JSR PC,INSCHR ; INSERT CHAR IN TELNET STREAM $POP R2,R0 ; RESTORE REGISTERS BR 10$ ; LOOP FOR MORE CHARS 20$: RTS PC ; RETURN TO CALLER .PAGE .SBTTL TTYIN - TTY INPUT EVENT HANDLER ;THIS CHARACTER MAY EITHER BE PART OF THE DATA STREAM TO THE NET, ;OR PART OF A USER COMMAND STREAM TO THE TELNET; IT IS THE FORMER ;IF THE 'BYTCNT' FIELD OF 'CMDBUF' IS =0, AND THE LATTER IF >0. ;THE TTY INPUT EVENT CANNOT BE HANDLED AT THIS TIME IF THERE IS ;NO BUFFER ROOM FOR THE NEW CHARACTER. IF THIS IS THE CASE, A FLAG, ;'RNGBEL', IS SET AS A SIGNAL TO THE TTY OUTPUT ROUTINE TO WARN ;THE USER WITH A BELL. TTYIN: CMP #ECOBL,ECOBUF+BYTCNT(TCT) ;ECHO BUFFER FULL? BHI GETCHR ;NO, HANDLE THE EVENT BUFFUL: BIS #RNGBEL,STATUS ;ELSE CAN'T, WARN USER BIS #USRIDL,STATE(TCT) ;FORCE SENDING IF IN WORD MODE BEVNT4: RTS PC GETCHR: $READ R0 ;READ IN THE CHAR INSCHR: BIT #BINMOD,STATE(TCT) ;IN EIGHT BIT BINARY MODE? BNE 1$ ;IF SO, SKIP BIC #200,R0 ;ELSE, STRIP PARITY BIT 1$: BIC #USRIDL!GENTRF,STATE(TCT) ;INDICATE USER NOT IDLE ;AND TURN OFF TRAFFIC GENERATOR MOVB R0,R1 ;DUPLICATE INPUT CHAR SWAB R0 ;PUT ORIGINIAL IN UPPER BYTE CLRB R0 BICB #200,R1 ;STRIP OFF PARITY BISB R1,R0 ;AND PUT STRIPPED CHAR INTO LOW BYTE TST CMDBUF+BYTCNT(TCT) ;COMMAND OR DATA STREAM? BNE CMDSTM ;BRANCH IF COMMAND STREAM .PAGE .SBTTL TTY INPUT EVENT: NET-DESTINED DATA HANDLER ;THE CURRENT TTY INPUT BYTE IS EITHER A NET-DESTINED DATA BYTE, ;OR THE COMMAND STREAM INTERCEPT CHARACTER. IN THE FORMER CASE, ;THE CHARACTER IS ENQUEUED IN THE CURRENT SEND BUFFER, AND ALSO ;IN THE ECHO BUFFER, IF LOCAL ECHO IS SET. ALSO, IF CHARACTER AT A ;TIME TRANSMIT MODE IS SET, IF THE CHARACTER IS SPECIAL, OR IF THE ;SEND BUFFER IS FULL, THE FLAG 'CHRSND' IS SET TO AWAKEN THE NET ;TRANSMISSION ROUTINE. ;IF THE CHARACTER IS THE COMMAND INTERCEPT, IT IS ENQUEUED FOR ECHOING, ;AND A COMMAND STREAM IS STARTED. CMPB R0,INTCHR(TCT) ;COMMAND INTERCEPT? BEQ FNDINT ;YES, START COMMAND STREAM NETCHR: BIT #EMODE,STATE(TCT) ;LOCAL ECHO SET? BNE 1$ ;NOPE, DONT ECHO IT CLR R1 BISB R0,R1 BITB #CHNECO,$CHTBL(R1) ;SHOULD WE NOT ECHO THIS CHARATER? BNE 1$ ;IF SO, SKIP ENQC #ECOBUF ;ELSE ECHO IT CMPB R0,#CR ;ECHOING A CARRIAGE RETURN? BNE 1$ ;IF NOT, SKIP $PUSH R0 MOVB #LF,R0 ;IF SO, ALSO ECHO A LINE FEED JSR PC,ENQ2 $POP R0 ;RECOVER CHAR 1$: BIT #OPNISS,STATE(TCT) ;HAS AN OPEN BEEN ISSUED? BEQ JEVNT4 ;NO, ON TO NEXT EVENT BIT #STKLSN,STATE(TCT) ;IN SERVER MODE (STICKY LISTEN)? BEQ 9$ ;IF NOT, SKIP BIT #CONOPN,STATUS ;CONNECTION OPEN? BEQ JEVNT4 ;IF NOT, EXIT 9$: CMP SNDBUF+BYTCNT(TCT),#SNDBL-1 ;Room for 2 more characters? BHIS BUFFUL ;If not, warn user SWAB R0 ;PUT FULL CHAR INTO LOW BYTE ENQC #SNDBUF ;ENQUEUE THE CHAR FOR SENDING CMPB R0,#IAC ;Telnet IAC character? BNE 4$ ;If not, skip CALL ENQ2 ;Else, duplicate IAC character 4$: CLRB R0 ;DISCARD FULL CHAR SWAB R0 ;AND THEN PUT STRIPPED CHAR IN LO BYTE CMPB #CR,R0 ;WAS IT A CR? BNE 10$ ;NOPE, ONWARD MOVB #LF,R0 ;YES, ALSO SEND A LF JSR PC,ENQ2 ;TO THE NET 10$: BIT #TMODE,STATE(TCT) ;CHAR AT A TIME TRANSMIT MODE? BEQ 2$ ;YES, SIGNAL FOR TRANSMISSION BITB #CHXMT,$CHTBL(R0) ;FORCE TRANSMISSION ON THIS CHARACTER? BNE 2$ ;IF SO, SIGNAL 'CHARS TO SEND' BIT #TLMODE,STATE(TCT) ;LINE-AT-A-TIME XMIT MODE? BNE 11$ ;IF SO, SKIP TEST MOVB $CHTBL(R0),R2 ;GET CHAR CLASS NUMBER BIC #177760,R2 ;AND CLEAR OF OUTHER FLAGS BEQ 2$ ;IF CLASS 0, TRANSIT NOW CMP #CHCLS4,R2 ;COMPARE VS FORMAT EFFECTOR CLASS BLOS 2$ ;IF ANY CLASS BUT ALPHANUMERICS, XMIT 11$: CMP SNDBUF+BYTCNT(TCT),#SNDBL ;SEND BUFFER FULL? BHIS 2$ ;YES, SIGNAL FOR SENDING BR JEVNT4 ;ELSE ON TO NEXT EVENT 2$: BIS #CHRSND,STATUS ;FLAG CHARACTERS TO SEND TO NET BR JEVNT4 ;AND ON TO NEXT EVENT ;FOUND AN INTERCEPT CHARACTER. ENQUEUE IT FOR ECHOING AND START THE ;COMMAND STREAM. FNDINT: MOV CMDBUF+LOLIM(TCT),CMDBUF+TAIL(TCT) ;RESET CMDBUF TAIL INC CMDBUF+BYTCNT(TCT) ;START COMMAND STREAM ENQC #ECOBUF ;QUEUE CHAR FOR ECHOING JEVNT4: RTS PC .PAGE .SBTTL TTY INPUT EVENT: COMMAND STREAM HANDLER ;THE CURRENT TTY INPUT BYTE IS PART OF A COMMAND STREAM. IF IT IS ;A SECOND INTERCEPT CHARACTER IN A ROW, ABORT THE COMMAND STREAM AND ATTEMPT ;TO SEND THE CHARACTER TO THE NET BY TRANSFERRING TO THE NET-DESTINED ;DATA HANDLER. OTHERWISE, THE CHARACTER IS PART OF THE COMMAND STREAM ;AND IS FOLDED TO UPPER CASE. ;A COMMAND STREAM IS ABORTED BY A DELETE CHARACTER, AND TERMINATED ;BY A CR. TERMINATION CAUSES IMMEDIATE PARSING AND EXECUTION OF THE COMMAND. ;BAD SYNTAX (INCLUDING COMMAND BUFFER OVERFLOW) IS SIGNALLED THROUGH THE ;'BAD' FLAG. CMDSTM: CMP #1,CMDBUF+BYTCNT(TCT) ;2ND BYTE OF STREAM? BNE 1$ ;NO, CANT BE DOUBLED INTERCEPT CMPB R0,INTCHR(TCT) ;ELSE MIGHT BE BNE 1$ ;NOPE, BRANCH CLR CMDBUF+BYTCNT(TCT) ;YUP, ABORT COMMAND STREAM BR NETCHR ;AND SEND INTERCEPT TO NET ;NORMAL COMMAND BYTE. IF THERE IS ROOM IN CMDBUF, FOLD TO UPPER CASE ;AND ENQUEUE IN CMDBUF AND ECOBUF, THEN ON TO NEXT EVENT HANDLER ;UNLESS THE CHAR IS A CR (COMMAND STREAM TERMINATOR), IN WHICH CASE, ;PARSE AND EXECUTE THE COMMAND. ;COMMANDS ARE SINGLE LETTERS, FOLLOWED BY AN ARBITRARY NUMBER OF ;SPACES, AND THEN A SINGLE ONE-LETTER ARGUMENT OR ONE OR MORE NUMERICAL ;ARGUMENTS SEPARATED BY COMMAS. 1$: CMPB #DEL,R0 ;CHARACTER A DELETE? BNE 2$ ;NO, ONWARD MOVB #'X,R0 ENQC #ECOBUF ;ECHO DEL AS 3 X'S AND A SPACE JSR PC,ENQ2 JSR PC,ENQ2 MOVB #SPACE,R0 JSR PC,ENQ2 MOVB #CR,R0 ;TERMINATE THIS LINE JSR PC,ENQ2 ;WITH A CR MOVB #LF,R0 ;YES, ALSO SEND A LF JSR PC,ENQ2 BR EXTCMD ;AND ON TO NEXT EVENT 2$: CMPB #CR,R0 ;IF THIS CHAR IS BEQ 3$ ;BR, TO ECHO CMP #1,CMDBUF+BYTCNT(TCT) ;IF THE CMD BYTE IS NOT STORED BEQ 3$ ;BR, TO ECHO CMD BYTE CMPB #125,@CMDBUF+LOLIM(TCT) ;IF CMD BYTE UPPER "U" BEQ 4$ ;BR, DON'T ECHO ARGUMENT CMPB #165,@CMDBUF+LOLIM(TCT) ;IF CMD BYTE LOWER "u" BEQ 4$ ;BR, DON'T ECHO ARGUMENT 3$: ENQC #ECOBUF ;ENQUEUE THE CHAR FOR ECHOING 4$: CMPB #SPACE,R0 ;A SPACE? BEQ JEVNT4 ;YES, IGNORE IT CMP #CMDBL+1,CMDBUF+BYTCNT(TCT);CMDBUF FULL? BLOS BADCMD ;YES, BAD COMMAND CMPB #141,R0 ;CHECK IF LOWER CASE ALPHA BHI 5$ ;NOPE, DONT FOLD CMPB #172,R0 ;CHECK OTHER BOUND BLO 5$ ;NOPE BICB #UPPER,R0 ;ELSE FOLD TO UPPER CASE 5$: ENQC #CMDBUF ;ELSE PLACE IN CMDBUF CMPB #CR,R0 ;A CR? BNE JEVNT4 ;NO, ON TO NEXT EVENT MOVB #LF,R0 ;YES, ALSO SEND A LF ENQC #ECOBUF ;ECHO CR AS CR-LF .PAGE .SBTTL PRSCMD - PARSE USER COMMAND PRSCMD: MOV #TCMDTB,R2 ;GET ADDRESS OF COMAND TABLE MOV #CMDBUF,R1 ;GET INDEX OF COMMAND BUFFER ADD TCT,R1 ;CALCULATE ITS ADDRESS MOVB ORIG(R1),R0 ;GET THE COMMAND 1$: CMPB (R2),R0 ;FIRST CHARACTER MATCH? BEQ HNDCMD ;IF SO, GREAT ADD #6,R2 ;ADVANCE TO NEXT ENTRY TST (R2) ;ZERO ENTRY MEANS END-OF-TABLE BNE 1$ ;IF MORE, TRY THEM BADCMD: $TYPE #MSGBAD ;BAD COMMAND SYNTAX, INFORM USER BR EXTCMD ;AND EXIT HNDCMD: MOV 2(R2),R0 ;GET ADDRESS OF COMMAND HANDLER MOV 4(R2),R2 ;GET ADDR OF CMD CMPL STRING TSTB -1(R2) ;BYTE IN FRONT OF STR IS PRIV LEV BEQ 10$ ;BR IF LEVEL IS ZERO BIT #PRIVLG,STATE(TCT) ;CK IF USER IS PRIVILEGED BEQ BADCMD ;BR IF NOT 10$: MOV R1,R2 ;R2 TO POINTS TO COMMAND STRING ADD #ORIG+1,R2 JSR PC,@R0 ;CALL COMMAND HANDLER BR BADCMD ;IF ERROR, COMPLAIN TO USER EXTCMD: CLR CMDBUF+BYTCNT(TCT) ;TERMINATE COMMAND STREAM RTS PC .PAGE .SBTTL TTYOUT - TTY OUTPUT COMPLETION EVENT HANDLER ;IF THE TELETYPE SIGNALS OUTPUT COMPLETION, PICK A NEW CHARACTER FOR ;OUTPUT. THERE ARE FOUR CLASSES OF CHARACTERS AVAILABLE ;FOR OUTPUT. EACH CLASS HAS A DIFFERENT PRIORITY, AND CHARACTERS OF ;HIGHER PRIORITY ARE OUTPUT BEFORE THOSE OF LOWER PRIORITY. FROM ;HIGHEST TO LOWEST PRIORITY, THE CLASSES ARE: ; 1. BELL--TYPE A 'CONTROL-G IF 'RNGBEL' IS 'TRUE'. ; 2. ECHO BUFFER--TYPE THE NEXT CHARACTER FROM 'ECOBUF' IF NONEMPTY. ; 3. MSG BUFFER--TYPE THE NEXT STATUS MSG FROM 'MSGBUF' IF NONEMPTY. ; 4. RECEIVE BUFFER--TYPE THE NEXT CHARACTER FROM 'RECBUF' IF NONEMPTY, ; AND IF CURRENT INPUT NOT PART OF A TELNET COMMAND STREAM. TTYOUT: SUB OTIORB+IRBX(TCT),RECBUF+BYTCNT(TCT) ;ACCOUNT FOR BYTES ADD OTIORB+IRBX(TCT),RECBUF+HEAD(TCT) ; OUTPUTED CMP RECBUF+HEAD(TCT),RECBUF+UPLIM(TCT) ;REACHED UPPER END? BLO ECHOUT ;IF NOT, SKIP MOV RECBUF+LOLIM(TCT),RECBUF+HEAD(TCT) ;ELSE, RESET HEAD PTR ECHOUT: BIS #TTYIDL,STATUS ;INDICATE OUTPUT COMPLETED RTS PC HNDOUT: BIT #TTYIDL,STATUS ;OUTPUT IDLE? BEQ 4$ ;IF NOT, EXIT BIT #RNGBEL,STATUS ;A BELL TO RING? BEQ 1$ ;NOPE, CHECK FOR CHARS TO ECHO BIC #RNGBEL,STATUS ;YES, RESET THIS FLAG MOV #CNTRLG,R0 ;RING HIS CHIMES BR 2$ ;AND OUTPUT IT 1$: TST ECOBUF+BYTCNT(TCT) ;ECHO BUFFER EMPTY? BEQ 3$ ;YES, SKIP DEQ #ECOBUF ;GET NEXT ECOBUF CHAR 2$: MOVB R0,OUTBUF(TCT) ;PUT CHARACTER IN I/O BUFFER MOV TCT,R0 ADD #OUTBUF,R0 ;CALCULATE ADDR OF I/O BUFFER MOV #1,R1 ;SENDING ONLY 1 CHAR JSR PC,$COUT ;OUTPUT CHARACTER RTS PC 3$: TST MSGBUF+BYTCNT(TCT) ;MSG BUFFER EMPTY? BEQ 4$ ;YES, EXIT DEQ #MSGBUF ;GET NEXT MSG INDEX BIC #377*400,R0 MOV MSGTBL(R0),R0 ;GET ADDRESS OF MSG STRING MOVB (R0)+,R1 ;AND ITS LENGTH JSR PC,$COUT ;OUTPUT CHARACTER 4$: RTS PC ;AND RETURN .PAGE .SBTTL HNDRCV - HANDLE NETWORK RECEIVE DATA/TELNET STREAM ; ; CALLED WITH: R5 - ADDR OF ACTIVE TCT ; R4 - CONNECTION STATUS FLAGS ; ; PROCESS DATA RECEIVED FROM THE NETWORK. ; HNDRCV: BIT #TTYIDL,STATUS ;OUTPUT IDLE? BEQ 3$ ;IF NOT, DEFER PROCESSING TST RECBUF+BYTCNT(TCT) ;REC BUFFER EMPTY? BEQ 2$ ;IF SO, SEE IF DATA WAITING 1$: MOVB TNCNT(TCT),R0 ;GET STREAM BYTE COUNT ASL R0 ;FORMAT AS JUMP INDEX JMP @RCVJTB(R0) ;AND FIND PROPER ENTRY POINT 2$: BIT #NEWDAT,STATUS ;DATA READY TO BE RECEIVED? BEQ 1$ ;IF NOT, SKIP JSR PC,TCPRCV ;ELSE, DO A RECEIVE BR 1$ 3$: RTS PC RCVJTB: .WORD HNDDAT ;HANDLE REGULAR DATA .WORD TNCIAC ;HANDLE TELNET IAC RECEPTION .WORD TNC12 ;HANDLE TELNET COMMAND RECEPTION .WORD TNC3 ;HANDLE TELNET OPTION PROCESSING .PAGE .SBTTL HNDDAT - PROCESSES REGULAR DATA RECEIVED FROM NETWORK HNDDAT: MOV RECBUF+HEAD(TCT),R3 ;GET ADDR OF FIRST BYTE OF DATA MOV RECBUF+BYTCNT(TCT),R2 ; AND BYTE COUNT BEQ 4$ ;IF NONE, SKIP 1$: CMPB #IAC,(R3)+ ;START OF TELNET COMMAND? BEQ 5$ ;IF SO, CHECK IF TRANSPARENT MODE 2$: $LOOP R2,1$ ;ELSE, SCAN ALL OF RECEIVED DATA 3$: MOV RECBUF+BYTCNT(TCT),R1 ;GET NUMBER OF BYTES SUB R2,R1 ;ACCOUNT FOR NUMBER NOT YET SCANNED BEQ HNDRCV ;IF NONE, SKIP MOV TCT,R0 ADD #OTIORB,R0 ;CALCULATE ADDR OF IORB MOV RECBUF+HEAD(TCT),IRUVA(R0) ;SET BUFFER ADDRESS MOV R1,IRBR(R0) ; NUMBER OF BYTES MOVB #SG.OUT,IROPC(R0) ; AND OPCODE BIC #TTYIDL,STATUS ;MARK TTY AS BUSY $SIO R0 4$: RTS PC ;AND EXIT 5$: BIT #PRTCOL,STATE(TCT) ;XPARENT PROTOCOL MODE? BNE 2$ ;YES, IAC TREATED AS ORDINARY DATA INCB TNCNT(TCT) ;ELSE, FLAG START OF TELNET COMMAND BR 3$ ;AND EXIT LOOP .PAGE .SBTTL TLNCMD - PROCESS TELNET COMMAND STREAM ;IF THE CURRENT NET INPUT IS A TELNET COMMAND STREAM, PROCESS IT. ;THIS ROUTINE HAS TWO ENTRY POINTS, ONE FOR RECEPTION OF THE ;COMMAND BYTE OF THE STREAM AND FOR RECEPTION OF THE OPTION, AND ONE ;FOR ACTING ON THE COMPLETED TELNET OPTION MESSAGE. DISPATCH OF THE ;FIRST TWO IS INHIBITED IF THE RESPECTIVE BYTES ARE NOT YET ;AVAILABLE (RECBUF EMPTY), AND THE LAST IS INHIBITED IF THERE IS NOT ;ROOM IN THE CURRENT SNDBUF FOR SENDING A TELNET OPTION MESSAGE IN RESPONSE. ;ENTRY POINT FOR COMMAND AND OPTION BYTE RECEPTION. ;IF RECBUF EMPTY, CAN'T DO IT YET. ELSE, READ IN THE BYTE AND ;BRANCH TO PROPER SUB-ENTRY POINT (ONE FOR COMMAND BYTE, OTHER FOR OPTION). TNCIAC: DEQ #RECBUF ;REMOVE IAC FROM NET RECEIVE BUFFER TNC12: TST RECBUF+BYTCNT(TCT) ;RECBUF EMPTY? BEQ RCVEXT ;YES, ON TO NEXT EVENT DEQ #RECBUF ;ELSE GET NEXT BYTE INCB TNCNT(TCT) ;INCREMENT STREAM COUNT CMPB #3,TNCNT(TCT) ;COMMAND OR OPTION BYTE? BEQ TNC2 ;OPTION BYTE ;JUST GOT THE COMMAND BYTE. IF NOT A LEGAL COMMAND, ABORT TELNET COMMAND ;STREAM, ELSE IF A DOUBLED 'IAC', TYPE IT AND ABORT THE STREAM. OTHERWISE, ;JUST SAVE THE COMMAND BYTE. TNC1: CMPB #373,R0 ;LEGAL COMMAND? BHI 1$ ;NO, ABORT STREAM MOVB R0,TNCMD(TCT) ;ELSE SAVE THE COMMAND CMPB #IAC,R0 ;ANOTHER IAC? BNE TNC12 ;NO, TRY TO GET OPTION ENQC #ECOBUF ;YES, ENQUEUE IT FOR TYPING 1$: CLRB TNCNT(TCT) ;ABORT THE COMMAND STREAM RCVEXT: RTS PC ;AND ONWARD ;JUST GOT THE OPTION BYTE. SAVE AND TRY TO ACT ON THE COMMAND. TNC2: MOVB R0,TNOPT(TCT) ;SAVE THE OPTION ;ENTRY POINT FOR ACTING ON THE COMMAND. IF THERE IS NO ROOM IN SNDBUF ;FOR A TELNET REPLY, CAN'T DO THIS NOW. TNC3: CMP #SNDBL-3,SNDBUF+BYTCNT(TCT) ;ROOM IN SNDBUF? BLO RCVEXT ;NO CLRB TNCNT(TCT) ;ELSE TERMINATE TELNET COMMAND STREAM MOVB TNCMD(TCT),R0 ;GET THE COMMAND SUB #177773,R0 ;FORMAT AS JUMP INDEX ASL R0 JMP @JMPTB2(R0) ;AND GO TO PROPER HANDLER JMPTB2: .WORD WILLR .WORD WONTR .WORD DOR .WORD DONTR ;WILL COMMAND. IF REMOTE ECHO WAS REQUESTED, SET NEW MODE IF THE OPTION ;IS ECHO, ELSE REPLY 'DONT'. WILLR: CMPB #ECHO,TNOPT(TCT) ;OPTION=ECHO? BNE 1$ ;NO, SKIP BIT #REMODE,STATE(TCT) ;REMOTE ECHO REQUESTED? BEQ 2$ ;NO, 'DONT' BIS #EMODE,STATE(TCT) ;ELSE SET REMOTE ECHO BR HNDRCV ;AND SCAN RECV DATA 1$: CMPB #SUPRGA,TNOPT(TCT) ;OPTION=SUPPRESS GA? BEQ HNDRCV ;YES, ACCEPT IT 2$: MOVB #DONT,R0 ;SET UP TO SAY 'DONT' JSR PC,TNREP ;SEND IT JMP HNDRCV ;AND ONWARD ;DO COMMAND. SAY 'WONT'. DOR: MOVB #WONT,R0 ;SET 'WONT' JSR PC,TNREP ;SAY IT JMP HNDRCV ;AND ONWARD ;WONT RECEIVED. IF OPTION=ECHO AND REMOTE ECHO REQUESTED, INFORM ;USER WITH 'CANT', ELSE SET LOCAL ECHO. WONTR: CMPB #ECHO,TNOPT(TCT) ;OPTION=ECHO? BNE 2$ ;NO, ONWARD BIT #REMODE,STATE(TCT) ;REMOTE ECHO REQUESTED? BEQ 1$ ;NO, LOCAL REQUESTED $TYPE #MSGCNT ;CAN'T NEGOTIATE TELET OPTION BIC #REMODE,STATE(TCT) ;RESET TO LOCAL ECHO REQUESTED 1$: BIC #EMODE,STATE(TCT) ;SET LOCAL ECHO 2$: JMP HNDRCV ;AND ONWARD ;DONT COMMAND RECEIVED. SEND 'WONT'. DONTR: MOVB #WONT,R0 ;SET 'WONT' JSR PC,TNREP ;SEND IT JMP HNDRCV .PAGE .SBTTL HNDSND - HANDLE NETWORK SEND DATA/TELNET STREAM ;IF THE TCP SEND IS IDLE, IF 'CONOPN' INDICATES THAT A CONNECTION EXISTS, ; AND IF 'CHRSND' INDICATES THAT THERE ARE CHARACTERS TO SEND, THEN SEND ; THE CONTENTS OF THE CURRENT SNDBUF TO THE NET ;IF THE CONNECTION IS OPEN AND THE TRAFFIC GENERATOR IS ACTIVE, 'GENTRF' ; =1, THEN FILL THE SEND BUFFER WITH GENERATOR TRAFFIC HNDSND: BIT #CONOPN,STATUS ;AN OPEN CONNECTION? BEQ 3$ ;NOPE BIT #SNDRDY,STATUS ;SEND COMPLETION? BEQ 3$ ;NOPE BIT #GENTRF,STATE(TCT) ;TRAFFIC GENERATOR ACTIVE? BNE 4$ ;YES, GENERATE TRAFFIC BIT #CHRSND,STATUS ;TIME TO SEND DATA? BEQ 3$ ;NOPE 1$: BIC #SNDRDY,STATUS ;RESET FLAGS MOV SNDBUF+HEAD(TCT),R1 ;GET ADDR OF HEAD OF BUFFER MOV SNDBUF+UPLIM(TCT),R0 ;GET UPPER LIMIT OF RING BUFFER SUB R1,R0 ;CALCULATE NUMBER OF CHARS TILL END CMP R0,SNDBUF+BYTCNT(TCT) ;THIS LESS THAN NUMBER OF CHARS? BLOS 2$ ;IF SO, SKIP MOV SNDBUF+BYTCNT(TCT),R0 ;SET BYTES REQUESTED 2$: MOV R0,SNDCNT(TCT) ;REMEMBER HOW MANY WE SENT MOV SNDURG(TCT),R2 ;OFFSET TO END OF URGENT DATA OR 0 $SEND ;ISSUE THE SEND 3$: RTS PC ; FILL SNDBUF WITH INTERNALLY GENERATED TRAFFIC 4$: MOV #SNDBL,R0 ;GET SIZE OF BUFFER SUB SNDBUF+BYTCNT(TCT),R0 ; AND CALCULATE SPACE REMAINING BEQ 1$ ;IF FULL, SEND & EXIT MOV R0,R2 ;GET COUNT INTO FREE REGISTER 5$: MOVB TRFCHR(TCT),R0 ;GET NEXT TRAFFIC CHAR INCB TRFCNT(TCT) ;BUMP COUNT OF CHARS GENERATED CMPB #80.,TRFCNT(TCT) ;SENT A LINE FULL? BNE 6$ ;IF NOT, OK MOVB #SPACE,R0 ;ELSE, SEND A SPACE CLRB TRFCNT(TCT) ;RESET COUNTER BR 7$ 6$: INCB TRFCHR(TCT) ;GENERATE NEXT TRAFFIC CHAR CMPB TRFCHR(TCT),#71 ;TIME TO WRAP TO ZERO? BLOS 7$ ;NOPE, ONWARD MOVB #60,TRFCHR(TCT) ;YES, WRAP BACK TO "0" 7$: ENQC #SNDBUF ;PLACE CHAR INTO SEND BUFFER $LOOP R2,5$ ;FILL UP THE BUFFER W/ TRAFFIC BR 1$ ;AND SEND IT .PAGE .SBTTL .SBTTL TELNET COMMAND HANDLERS .SBTTL . OPEN CONNECTION (L,O,S) ;AN OPEN COMMAND HAS BEEN RECEIVED. PARSE THE ARGUMENTS AND ISSUE THE ;OPEN IF THE ARGUMENTS ARE OK. THE ARGUMENTS FOR THE OPEN COMMAND ARE ;RADIX-10 NUMBERS OF THE FORM: ',,'. ;THE NET AND SOCKET NUMBERS HAVE DEFAULTS, AND SO MAY BE OMITTED. ;IF THE OPEN FAILS, THE 'CANT' FLAG IS SET TO NOTIFY THE USER. OPENS: BIS #STKLSN,STATE(TCT) ;INDICATE STICKY CONNECTION BR OPENC2 ;AND OPEN THE CONNECTION OPENC: BIC #STKLSN,STATE(TCT) ;INDICATE CONNECTION NOT STICKY OPENC2: BIT #OPNISS,STATE(TCT) ;HAS AN OPEN ALREADY BEEN ISSUED? BEQ 10$ ;IF NOT, ACCEPT ANOTHER MOV #1,R0 ;INDICATE CONNECTION ALREADY OPEN ERROR BR OPNER ;AND INFORM USER 10$: MOV TCT,R1 ADD #OPNBLK,R1 ;ADVANCE TO TCP OPEN PARAMETER BLK .IF NE, LCLSOK MOV #LCLSOK,(R1)+ ;INITIALIZE WITH DEFAULT VALUES .IFF MOVB TTLID(TCT),(R1)+ ;Local socket = TELNET number CLRB (R1)+ .ENDC MOVB $NETID,(R1)+ ;DEFAULT TO LOCAL NET CLRB (R1)+ ;DEFAULT IS LISTENING CLR (R1)+ MOV #DFTSK2,(R1)+ ;DEFAULT IS TELNET SERVER SOCKET MOV #DFTSK1,(R1)+ CMPB #43,(R2) ; CH FIRST CHAR FOR "#" BNE 15$ ; BR IF NOT "#" INC R2 ; ADVANCE R2 PAST "#" JSR PC,HEXBIN ; CNVERT HEX CHAR'S TO BIN BR OPNBAD ; IF HEX OVERFLOW GIVE 'BAD' MSG BIT #177400,R0 ; CK IF FULL TIU ADDR GIVEN BNE 13$ ; BR IF GIVEN BIS #10000,R0 ; FORM FULL TIU ADDR 13$: MOV R0,FTCP(TCT) ; SET TIU/TCP NUMBER BR 20$ ; CONTINUE PARSING WITH NETID 15$: MOV #$HTTBL,R1 ;GET TCP ID TABLE JSR PC,PARSE ;GET TCP IDENTIFIER BR OPNBAD ;IF PARSING ERROR, GIVE 'BAD' MSG MOV R1,FTCP(TCT) ;SET TCP NUMBER TST R0 ;NETWORK NUMBER SPECIFIED? BEQ 20$ ;IF NOT, SKIP MOV R0,FNET(TCT) ; AND DEFAULT NETWORK 20$: MOVB (R2)+,R0 ;GET DELIMITER CMPB #CR,R0 ;ONLY TCP SUPPLIED? BEQ REOPEN ;IF SO, SKIP. USE DEFAULT NET & SOCK CMPB #COMMA,R0 ;A COMMA? BNE OPNBAD ;NOPE, BAD SYNTAX MOV #$NTTBL,R1 ;ADDR OF NETWORK NAME TABLE JSR PC,PARSE ;GET NETWORK IDENTIFIER BR OPNBAD BCS 25$ ;IF NULL, USE DEFAULT MOV R0,FNET(TCT) ;SET NET NUMBER 25$: MOVB (R2)+,R0 ;GET DELIMITER CMPB #CR,R0 ;WAS A SOCKET SUPPLIED? BEQ REOPEN ;NO, USE DEFAULT CMPB #COMMA,R0 ;DELIMITER A COMMA? BNE OPNBAD ;NOPE, BAD SYNTAX MOV #$SKTBL,R1 ;ADDR OF SERVICE SOCKET NAME TABLE JSR PC,PARSE ;CONVERT SOCKET TO BINARY BR OPNBAD CMPB (R2),#CR ;DELIMITER A CR? BNE OPNBAD ;NOPE, BAD SYNTAX MOV R0,FPORTL(TCT) ; SET SOCKET, LOW WORD MOV R1,FPORTH(TCT) ; AND SET HI WORD REOPEN: MOV TCT,R1 ;SET A(OPEN BLOCK) ADD #OPNBLK,R1 $OPEN ;ISSUE THE OPEN OPNER: ASL R0 ;CONVERT RETURN CODE TO WORD INDEX $PUSH R0 $TYPE ERRTBL(R0) ;AND OUTPUT MESSAGE $POP R0 ;DID OPEN COMPLETE OK? BNE OPNEX ;NO, THEN JUST EXIT OPEN ROUTINE ;THE TCP ACCEPTED THE OPEN. FLUSH ALL CURRENT NET ;SEND AND RECEIVE DATA (INCLUDING ANY RECEIVED TELNET COMMAND STREAM) ;AND IF APPROPRIATE, ENQUEUE A 'DO ECHO' REQUEST IN SNDBUF. MOV STATE(TCT),DSTATE(TCT) ;REMEMBER DEFAULT STATE BIS #OPNISS,STATE(TCT) ;FLAG OPEN ISSUED CLRB TNCNT(TCT) ;ABORT ANY TELNET COMMAND STREAM CLR RECBUF+BYTCNT(TCT) ;FLUSH RECEIVE DATA MOV RECBUF+LOLIM(TCT),RECBUF+HEAD(TCT);RE-INIT RECBUF CLR SNDBUF+BYTCNT(TCT) ;FLUSH SEND DATA MOV SNDBUF+LOLIM(TCT),SNDBUF+TAIL(TCT) ;AND RE-INIT SNDBUF MOV SNDBUF+LOLIM(TCT),SNDBUF+HEAD(TCT) CLR SNDCNT(TCT) ;FLUSH SENT TO TCP COUNT CLR SNDURG(TCT) ;FLUSH SEND URGENT PTR BIT #PRTCOL,STATE(TCT) ;TRANSPARENT PROTOCOL? BNE OPNEX ;YES, EXIT MOVB #DO,R0 ;SET COMMAND DO MOVB #SUPRGA,TNOPT(TCT) ;AND SUPPRESS GO-AHEAD OPTION JSR PC,TNREP ;BUILD THE OPTION MESSAGE BIT #REMODE,STATE(TCT) ;REMOTE ECHOING REQUESTD? BEQ OPNEX ;IF NOT, EXIT MOVB #ECHO,TNOPT(TCT) ;SET OPTION=ECHO JSR PC,TNREP ;ENQUEUE THE OPTION REQUEST FOR SENDING OPNEX: ADD #2,(SP) ;AND EXIT OPNBAD: RTS PC .PAGE .SBTTL . CLOSE CONNECTION (C) ;A CLOSE COMMAND HAS BEEN ISSUED BY THE USER. SIMPLY ISSUE A $CLOSE ;CALL TO THE TCP AND SET THE 'CONOPN' ;FLAG TO FALSE TO SIGNAL THE CONNECTION LOGICALLY CLOSED. TELCLS: CMPB #CR,(R2) ;PROPER SYNTAX? BNE CLSBAD ;NOPE CLSIT: BIT #OPNISS,STATE(TCT) ;OPEN BEEN ISSUED? BEQ 2$ ;IF NOT, IGNORE CLOSE BIT #CONOPN,STATUS ;CONNECTION NOW OPENED? BNE 1$ ;IF SO, SKIP BIC #OPNISS,STATE(TCT) ;ELSE, ABORT THE CONNECTION $TYPE #MSGAB ;AND INFORM THE USER 1$: $CLOSE ;ELSE CLOSE THE CONNECTION BIC #CONOPN!CHRSND,STATUS ;AND FLAG IT CLOSED 2$: ADD #2,(SP) ;AND EXIT CLSBAD: RTS PC .PAGE .SBTTL . SEND TELNET INTERRUPT PROCESS SEQUENCE SNDIP: CMPB #CR,(R2) ;PROPER SYNTAX? BNE IPBAD ;NO BIT #CONOPN,STATUS ;CONNECTION OPEN? BEQ IPBAD ;NO CMP #SNDBL-4,SNDBUF+BYTCNT(TCT) ;ROOM FOR IP SEQUENCE? BLO IPBAD ;NO MOVB #IAC,R0 ;PUT IAC, IP, IAC, DM INTO SNDBUF ENQC #SNDBUF MOVB #IP,R0 JSR PC,ENQ2 MOVB #IAC,R0 JSR PC,ENQ2 MOVB #DM,R0 JSR PC,ENQ2 BIS #CHRSND,STATUS ;INDICATE CHARS TO SEND MOV SNDBUF+BYTCNT(TCT),SNDURG(TCT) ;OFFSET INTO SNDBUF FOR END OF URGENT MSG ADD #2,(SP) ;GOOD RETURN IPBAD: RTS PC .PAGE .SBTTL . RESET CONNECTION STATE (R) RESET: CMPB #CR,(R2) ;PROPER SYNTAX? BNE CLSBAD ;IF NOT, COMPLAIN MOV #DFMODE,DSTATE(TCT) ;RESET TO DEFAULTS MOVB #DFMODE,STATE(TCT) MOVB #'@,INTCHR(TCT) ;RESET INTERCEPT CHAR TO '@' CLR R1 ;ZERO CANNED MSG INTERVAL JSR PC,CANEND ;KILL CANNED MSGS NOP ; COVER FOR ERR RTN JSR PC,VRSN ;OUTPUT SOFTWARE VERSION & IDS BR CLSIT ;AND CLOSE ANY OPEN CONNECTIONS .PAGE .SBTTL . OUTPUT SYSTEM HERALD (V) VERSON: CMPB #CR,(R2) ;PROPER SYNTAX? BNE 1$ ;IF NOT, GIVE ERROR JSR PC,VRSN ;OUTPUT MESSAGE ADD #2,(SP) ;GIVE GOOD RETURN 1$: RTS PC VRSN: $PUSH R0,R1,R2 $TYPE #MSGHDR ;OUTPUT SYSTEM HERALD MSG .IF EQ, 1 $TYPE #MSGTRM ;FOLLOWED BY TERMINAL ID $AVS #IORBL ;GET A BLOCK FOR THE MESSAGE $PUSH R2 ; AND SAVE ADDR FOR LATER INC R2 ;RESERVE SPACE FOR STRING LENGTH MOV $HSTID,R0 ;GET TERMINAL ID CLR R1 ;SET BYTE COUNT TO ZERO JSR PC,NOUT ;CONVERT TO DECIMAL ASCII STRING $POP R2 ;RECOVER STRING ADDRESS MOVB R1,(R2) ;PUT BYTE COUNT IN STRING $TYPE R2 $TYPE #MSGNET $AVS #IORBL $PUSH R2 INC R2 CLR R0 BISB $NETID,R0 ;GET NET ID CLR R1 ;LENGTH INITIALLY ZERO JSR PC,NOUT ;CONVERT NET ID TO STRING MOVB #'.,(R2)+ ;TERMINAL WITH A PERIOD MOVB #12,(R2)+ ; A CR MOVB #15,(R2) ; AND A LF ADD #3,R1 $POP R2 MOVB R1,(R2) $TYPE R2 ;AND OUTPUT IT .ENDC $POP R2,R1,R0 RTS PC .PAGE .SBTTL . SET ECHO MODE (E) ;A NEW ECHO MODE IS REQUESTED. IF THERE IS NO ROOM IN THE CURRENT ;SNDBUF FOR A TELNET COMMAND SEQUENCE, FORGET IT AND PUT THE USER OFF ;WITH A 'BAD' MESSAGE. IF THE REQUESTED AND CURRENT MODES ARE ;THE SAME, IGNORE THE COMMAND, OTHERWISE, REMEMBER THE REQUESTED MODE ;FOR LATER AND SEND OUT A TELNET COMMAND SEQUENCE IN SNDBUF REQUESTING ;THE NEW MODE. ECOMOD: CMPB #CR,ORIG+2(R1) ;GOOD SYNTAX? BNE 8$ ;NOPE MOVB ORIG+1(R1),R0 ;GET PARAMETER BIT #PRTCOL,STATE(TCT) ;XPARENT PROTOCOL? BNE 5$ ;YES, SPECIAL ECHO HANDLING BIT #CONOPN,STATUS ;AN OPEN CONNECTION? BEQ 3$ ;NOPE, SET A DEFAULT ECHO MODE CMP #SNDBL-3,SNDBUF+BYTCNT(TCT) ;ROOM FOR A TELNET SEQUENCE? BLO 8$ ;NOPE, DELAY THE USER CMPB #'R,R0 ;REMOTE ECHO REQUESTED? BNE 1$ ;NOPE, CHECK IF LOCAL BIT #EMODE,STATE(TCT) ;YES, IS CURRENT MODE REMOTE? BNE 7$ ;YES, NOTHING TO DO BIS #REMODE,STATE(TCT) ;NO, FLAG REMOTE REQUESTED MOVB #DO,R0 ;PREPARE TO SEND 'DO ECHO' BR 2$ ;GO DO IT 1$: CMPB #'L,R0 ;LOCAL ECHO REQUESTED? BNE 8$ ;NOPE, BAD BIT #EMODE,STATE(TCT) ;ELSE IS CURRENT MODE LOCAL? BEQ 7$ ;YES, NOTHING TO DO BIC #REMODE,STATE(TCT) ;ELSE FLAG LOCAL ECHO REQUESTED MOVB #DONT,R0 ;SET UP TO SEND 'DONT ECHO' 2$: $PUSH TNOPT(TCT) ;SAVE CONTENTS OF THIS BYTE MOVB #ECHO,TNOPT(TCT) ;SET OPTION JSR PC,TNREP ;SEND OPTION REQUEST $POP TNOPT(TCT) ;RESTORE THE OPTION BYTE 7$: ADD #2,(SP) ;TAKE GOOD RETURN 8$: RTS PC ;AN ECHO MODE HAS BEEN REQUESTED, BUT THERE IS NO CONNECTION. IN THIS CASE, ;WE SET THE DEFAULT ECHO MODE WITH WHICH TO ATTEMPT OPENING ALL SUBSEQUENT ;CONNECTIONS. 3$: CMPB #'R,R0 ;REMOTE ECHO REQUESTED? BNE 4$ ;NOPE, CHECK IF LOCAL BIS #REMODE,STATE(TCT) ;YES, SET IT AS DEFAULT MODE BR 7$ ;AND EXIT 4$: CMPB #'L,R0 ;LOCAL ECHO REQUESTED? BNE 8$ ;NOPE, BAD ARGUMENT BIC #REMODE,STATE(TCT) ;YES, SET IT AS DEFAULT MODE BR 7$ ;AND EXIT 5$: CMPB #'R,R0 ;REMOTE ECHO? BNE 6$ ;NOPE, CHECK IF LOCAL REQUESTED BIS #EMODE!REMODE,STATE(TCT) ;YES, SET REMOTE ECHO BR 7$ ;AND EXIT 6$: CMPB #'L,R0 ;LOCAL ECHO? BNE 8$ ;NOPE, BAD BIC #EMODE!REMODE,STATE(TCT) ;YES, SET IT BR 8$ ;AND EXIT .PAGE .SBTTL . SET TRANSMIT MODE (T) ;A NEW TRANSMIT MODE IS REQUESTED. IF SYNTAX IS OK, SET THE NEW MODE. XMTMOD: CMPB #CR,ORIG+2(R1) ;GOOD SYNTAX? BNE 4$ ;NOPE CMPB #'L,ORIG+1(R1) ;LINE AT A TIME REQUESTED? BNE 1$ ;NO BIS #TLMODE,STATE(TCT) ;YES, SET IT BIC #TWMODE,STATE(TCT) ;AND CLEAR WORD-AT-A-TIME FLAG BR 3$ ;AND EXIT 1$: CMPB #'C,ORIG+1(R1) ;CHARACTER AT A TIME? BNE 2$ ;NO BIC #TMODE,STATE(TCT) ;ELSE SET MODE BR 3$ ;AND EXIT 2$: CMPB #'W,ORIG+1(R1) ;WORD-AT-A-TIME MODE? BNE 4$ ;NOPE, BAD COMMAND BIT #TWMODE,STATE(TCT) ;ALREADY IN WORD MODE? BNE 3$ ;IF SO, SKIP $STIME #SG.TIM,#0,#60. ;START THE TIMER BIS #TWMODE,STATE(TCT) ;SET WORD-AT-ATIME FLAG BIC #TLMODE,STATE(TCT) ; AND CLEAR LINE-AT-A-TIME FLAG 3$: ADD #2,(SP) 4$: RTS PC .PAGE .SBTTL . CHANGE INTERCEPT CHARACTER (I) ;A NEW INTERCEPT CHARACTER IS REQUESTED. CONVERT THE ARGUMENT TO BINARY ;AND IF SYNTAX OK, SET THE NEW INTERCEPT CHARACTER. INTCPT: CMPB #CR,(R2) ; CK FOR "I" BEQ 1$ ; BAD SYNTAX IF NO ARGUMENT JSR PC,CVTBIN ;CONVERT TO BINARY CMPB #CR,(R2) ;TERMINATOR A CR? BNE 1$ ;NOPE, BAD CMP #177,R0 ;CHECK FOR OUT-OF-RANGE VALUE BMI 1$ ;BR IF TOO BIG MOVB R0,INTCHR(TCT) ;SET NEW INTERCEPT CHARACTER ADD #2,(SP) 1$: RTS PC ;AND EXIT .PAGE .SBTTL . SET PROTOCOL MODE (P) PROCOL: CMPB #CR,ORIG+2(R1) ;TERMINATOR A CR? BNE 3$ ;NOPE, BAD CMPB #'T,ORIG+1(R1) ;TELNET PROTOCOL REQUESTED? BNE 1$ ;NOPE, CHECK IF XPARENT BIC #PRTCOL,STATE(TCT) ;ELSE SET TELNET MODE BR 2$ ;AND EXIT 1$: CMPB #'X,ORIG+1(R1) ;XPARENT PROTOCOL REQUESTED? BNE 3$ ;NOPE, BAD BIS #PRTCOL,STATE(TCT) ;ELSE SET THAT MODE 2$: ADD #2,(SP) 3$: RTS PC .PAGE .SBTTL . TRAFFIC GENERATOR ENABLE (G) TRFGEN: CMPB #CR,(R2) ;TERMINATOR A CR? BNE 1$ ;NOPE, BAD BIS #GENTRF,STATE(TCT) ;FLAG TRAFFIC GENERATOR ALIVE MOVB #60,TRFCHR(TCT) ;SET FIRST CHAR TO GENERATE CLRB TRFCNT(TCT) ;CLEAR TRAFFIC GEN CHAR COUNTER ADD #2,(SP) ;BUMP TO GOOD RETURN ADDRESS 1$: RTS PC ;AND EXIT .PAGE .SBTTL . BINARY TRANSFER (B) BNMODE: CMPB #CR,(R2) ;TERMINATOR A CR? BNE 1$ ;NOPE, BAD BIS #BINMOD,STATE(TCT) ;INDCATE BINARY TRANSMISSION MODE ADD #2,(SP) ;BUMP TO GOOD RETURN ADDRESS 1$: RTS PC ;AND EXIT .PAGE .SBTTL . ESCAPE TO DDT (D) ; ;THIS ROUTINE PROVIDES AN ESCAPE MECHANISM TO DDT. IT IS CALLED ;TO SERVICE THE TELNET (D) COMMAND. ; DDTBRK: CMPB #CR,(R2) ; CK FOR "D" BNE DCMDNG ; BR IF NOT PROPER SYNTAX BPT ; BREAK POINT PLANTED ADD #2,(SP) ; ADJUST FOR GOOD RETURN DCMDNG: RTS PC ; RETURN .PAGE .SBTTL . HELP ;LIST TELNET COMMANDS IN TCMDTB ; HELP: ADD #2,(SP) ;NULL COMMAND RTS PC ;RETURN .PAGE .SBTTL . USER LEVEL SPECIFICATION (U) ; ;THIS ROUTINE ESTABLISHES THE USERS PRIVILEGE LEVEL ; CURRENT IMPLIMENTATION ONLY SUPPORTS TWO LEVELS ; PRIVILEGED (NON-ZERO) ; UNPRIVILEGED (ZERO) ;THE PRIVILEGE LEVEL OF EACH TELNET COMMAND IS SPECIFIED IN THE ;CMD MACROS IN THE COMMAND TABLE ; USERLV: CMPB #CR,(R2) ; IF CMD IS "U" BEQ 1$ ; BR, TO CLR PRIVLG CMPB #120,(R2) ; IF PASSWORD IS OK BNE UCMDER CMPB #122,1(R2) BNE UCMDER BIS #PRIVLG,STATE(TCT) ; SET PRIVILEGE MODE BR UCMDEX ; EXIT NORMALLY 1$: BIT #PRIVLG,STATE(TCT) ; IF PRIVLG NOT SET BEQ UCMDER ; BR, SYNTAX ERROR BIC #PRIVLG,STATE(TCT) ; CLEAR PRIVILEGE MODE UCMDEX: ADD #2,(SP) ; ADJ FOR NORM RTN UCMDER: RTS PC ; RETURN .PAGE .SBTTL . SEND CANNED MESSAGE AT REGULAR INTERVALS (Z) ; ;THIS ROUTINE STARTS OR ENDS THE TRANSMISSION OF A CANNED MESSAGE ;AT REGULAR INTERVALS. NORMAL TRAFFIC CAN BE SENT AND RECEIVED INBETWEEN ;THE CANNED MESSAGES. THIS ROUTINE IS CALLED TO SERVICE THE TELNET ;(Z) COMMAND. TO TURN ON THIS FUNCTION THE Z COMMAND IS TYPED WITH ;ONE ARGUMENT WHICH SPECIFIES IN DECIMAL THE NUMBER OF SECONDS INBETWEEN ;CANNED MESSAGES. THE VALID RANGE FOR THIS ARGUMENT IS 1 TO 900 SECONDS. ;ANY OTHER VALUES IS CONSIDERED INVALID SYNTAX AND NO ACTION IS TAKEN. ;TO TURN OFF THIS FUNCTION THE Z COMMAND IS TYPED WITHOUT AN ARGUMENT ;WHERE A IMMEDIATELY FOLLOWES THE Z. ; CANMSG: CLR R1 ;CLEAR TIME INTERVAL CMPB #CR,(R2) ;IF "Z" BEQ CANEND ;BR, ENDING CANNED MSG JSR PC,CVTBIN ;PICK UP CANNED MSG INTERVAL CMPB #CR,(R2) ;IF NXT CHAR NOT BNE CANERR ;BR, SYNTAX ERROR TST R0 ;IF THE INTERVAL IS NOT POS. BLE CANERR ;BR, SYNTAX ERROR CMP #600.,R0 ;IF THE INTERVAL IS GT 600 SEC BMI CANERR ;BR, SYNTAX ERROR MOV R0,R1 ;CONV. SECS TO TICKS MUL #60.,R1 ;SECS * 60. = TICKS ;R1 = NEW INTERVAL IN TICKS CANEND: $PUSH R0,R1 ;SAVE REGS $STIME #SG.CMT,#0,#0 ;STOP CURRENT TIMER $POP R1,R0 ;RESTORE REGS MOV R1,CANVT(TCT) ;STORE NEW CAN MSG TIMER MOV R1,R2 ;R2 = NEW TIME INTERVAL BEQ CANRTN ;BR IF TURNING OFF IE. 0 $STIME #SG.CMT,#0,R2 ;BEGIN NEW TIMER CANRTN: ADD #2,(SP) ;ADJUST FOR GOOD RETURN CANERR: RTS PC ;RETURN .PAGE .SBTTL .SBTTL TELNET UTILITY ROUTINES .SBTTL . IBUF - ROUTINE TO INITIALIZE A BUFFER ;IBUF--ROUTINE TO INITIALIZE A BUFFER ;CALL-- R0: BUFFER LENGTH ; R1: OFFSET OF BUFFER ;RETURN-- R1: A(BUFFER) IBUF: ADD TCT,R1 ;CALC A(BUFFER) IBUF2: MOV R1,R2 ;A(BUFFER) TO R2 ADD #ORIG,R2 ;CALC START OF CHAR QUEUE MOV R2,LOLIM(R1) ;SET LOWER BOUND OF QUEUE MOV R2,HEAD(R1) ;INITIALIZE HEAD POINTER MOV R2,TAIL(R1) ;AND INIT TAIL CLR BYTCNT(R1) ;INIT BYTE COUNT ADD R0,R2 ;CALC UPPER LIMIT MOV R2,UPLIM(R1) ;SET UPPER LIMIT RTS PC ;AND RETURN .PAGE .SBTTL . QUEUE MANAGEMENT ROUTINES--DEQ AND ENQ ;DEQ--THIS ROUTINE IS PASSED THE OFFSET OF THE BUFFER IN R1 AND RETURNS ;WITH THE DEQUEUED CHARACTER IN R0 AND THE ADDRESS OF THE BUFFER IN R1. DEQ: ADD TCT,R1 ;CALC A(BUFFER) DEQ2: MOVB @HEAD(R1),R0 ;GET THE CHARACTER INC HEAD(R1) ;UPDATE THE HEAD CMP HEAD(R1),UPLIM(R1) ;WRAP AROUND? BLO 1$ ;NO, ONWARD MOV LOLIM(R1),HEAD(R1) ;ELSE WRAP 1$: DEC BYTCNT(R1) ;UPDATE BYTE COUNT RTS PC ;AND THATS IT ;ENQ--THIS ROUTINE IS PASSED THE CHARACTER TO ENQUEUE IN R0 AND THE ;OFFSET OF THE BUFFER IN R1. IT RETURNS THE ADDRESS OF THE BUFFER ;IN R1. ENQ1: ADD TCT,R1 ;CALC A(BUFFER) ENQ2: MOVB R0,@TAIL(R1) ;STICK CHAR IN BUFFER INC TAIL(R1) ;BUMP TAIL CMP TAIL(R1),UPLIM(R1) ;WRAP? BLO 1$ ;NOPE MOV LOLIM(R1),TAIL(R1) ;YES, DO IT 1$: INC BYTCNT(R1) ;INCREMENT BYTE COUNT RTS PC ;AND GO HOME .PAGE .SBTTL . TNREP--ROUTINE TO ENQUEUE A TELNET OPTION MESSAGE ;TNREP--THIS ROUTINE ENQUEUES A THREE BYTE TELNET OPTION ;NEGOTIATION SEQUENCE IN THE CURRENT SNDBUF, AND SETS 'CHRSND' TO ;TRUE. ;CALL-- R0: TELNET COMMAND TO SEND ;R1 IS CLOBBERED ON RETURN TNREP: $PUSH R0 MOVB #IAC,R0 ;ENQUEUE 'IAC' ENQC #SNDBUF MOV (SP),R0 JSR PC,ENQ2 ;ENQUEUE VERB MOVB TNOPT(TCT),R0 ;ENQUEUE THE OPTION JSR PC,ENQ2 BIS #CHRSND,STATUS ;FLAG CHARACTERS TO SEND $POP R0 RTS PC ;RETURN .PAGE .SBTTL . PARSE TABLE FORMAT PRSTNG = 0 ;ADDRESS OF TEXT STRING PRSLEN = 2 ;LENGTH OF STRING PRSMIN = 3 ;MINIMUM NEEDED FOR RECOGNITION PRSDAT = 4 ;DATA WORD #1 PRSSIZ = 10 ;SIZE OF PARSE TABLE ENTRY .PAGE .SBTTL . PARSE - PARSES PARAMETER FIELD WITH OPTIONAL LOOKUP TABLE ; ; CALLED WITH: R1 - ADDR OF TEXT LOOK-UP TABLE ; R2 - ADDR OF PARAMTER TEXT STRING ; ; RETURNS WITH: R0 - LO VALUE OF PARAMETER ; R1 - HI VALUE OF PARAMETER ; R2 - ADDR OF DELIMITER ; CARRY SET - IF NULL PARAMETER FIELD ; ; RETURNS AT: CALL+2 - IF TABLE ENTRY NOT FOUND ; CALL+4 - NORMAL RETURN, OR NULL PARAMETER ; PARSE: MOVB (R2),R0 ;GET FIRST CHARACTER CMPB R0,#'0 ;A DELIMITER? BLO 6$ ;IF SO, GIVE NULL PARAMETER RETURN CMPB R0,#'A ;NUMER OR ALPHA STRING? BLO 4$ ;IF NUMBER, CONVERT TO BINARY TST R1 ;STRING TABLE SUPPLIED? BEQ 7$ ;IF NOT, GIVE ERROR $PUSH R3,R4,R5 MOV R1,R5 ;R5 - ADDR OF COMMAND TABLE ENTRY MOV R2,R4 ;R4 - START OF PARAMETER STRING CLR R3 ;RESET LENGTH OF PARAMETER STRING INC R2 ;SKIP FIRST CHAR 8$: INC R3 ;ACCOUNT FOR EACH CHAR IN STRING CMPB (R2)+,#'0 ;TERMINATOR CHAR? BHIS 8$ ;IF NOT, COUNT IT ; ; R5 - ADDR OF PARSING TABLE ENTRY ; R4 - ADDR OF COMMAND STRING START ; R3 - LENGTH OF COMMAND STRING ; 1$: CMPB R3,PRSMIN(R5) ;PARAMETER STRING LONGER THAN MINIMUM? BLO 3$ ;IF NOT, CAN'T MATCH CMPB R3,PRSLEN(R5) ;PARAMETER STRING LONGER THAN MAX? BHI 3$ ;IF SO, NO MATCH MOV R3,R0 MOV PRSTNG(R5),R1 ;GET ADDR OF STRING MOV R4,R2 2$: CMPB (R2)+,(R1)+ ;CHARACTER MATCH? BNE 3$ ;IF NOT, ADVANCE TO NEXT ENTRY $LOOP R0,2$ ;COMPARE ALL OF STRING MOV PRSDAT(R5),R0 ;GET PARAMETERS MOV PRSDAT+2(R5),R1 $POP R5,R4,R3 BR 5$ ;AND GIVE GOOD EXIT 3$: ADD #PRSSIZ,R5 ;ADVANCE TO NEXT ENTRY TST (R5) ;TABLE TERMINATOR? BNE 1$ ;IF NOT, SCAN ENTRY $POP R5,R4,R3 RTS PC ;AND TAKE ERROR RETURN 4$: JSR PC,CVTBIN ;CONVERT TEXT TO NUMBER 5$: ADD #2,(SP) ;INDICATE GOOD RETURN CLC ;WITH VALID PARAMETER RTS PC 6$: ADD #2,(SP) ;BUMP TO GOOD RETURN SEC ;INDICATE NULL PARAMETER 7$: RTS PC ;AND RETURN .PAGE .SBTTL . HEXBIN - CONVERT A HEX STRING TO BINARY ; ; THIS ROUTINE SCANS AN ASCII STRING LEFT TO RIGHT CONVERTING CHARACTERS ; TO BINARY ASUMING THE CHARS "0"-"9","A"-"F","a"-"f" TO BE VALID HEXIDECIMAL ; DIGITS. THE CONVERSION ENDS AS THE FIRST NON-HEX CHARACTER IS DISCOVERED, ; OR THE BINARY VALUE OVERFLOWS A 16 BIT WORD. ; ; CALLED WITH R2: A(STRING) ; ; RETURNS WITH R0: BINARY VALUE ; R2: A(NON-HEX CHAR) ; ; RETURNS AT PC IF OVERFLOW OCCURED ; PC+2 FOR NORMAL RETURN ; HEXBIN: $PUSH R1 ; SAVE CALLERS REGISTERS CLR R0 ; INITIALIZE RESULTS NEXTCH: MOVB (R2),R1 ; GET CHAR FROM STRING BIC #177400,R1 ; CLEAR UPPER BYTE SUB #60,R1 ; ADJUST TO "0" BMI NONHEX ; BR IF NONHEX BELOW "0" CMP #9.,R1 ; CK IF CHAR GT "9" BPL HEXOK ; BR IF CHAR "0"-"9" SUB #7,R1 ; ADJUST TO "A" BMI NONHEX ; BR IF NONHEX BETWEEN "9"-"A" CMP #15.,R1 ; CK IF CHAR GT "F" BPL HEXOK ; BR IF CHAR "A"-"F" SUB #40,R1 ; ADJUST TO "a" BMI NONHEX ; BR IF NONHEX BETWEEN "F"-"a" CMP #15.,R1 ; CK IF CHAR GT "f" BMI NONHEX ; BR IF CHAR GT "f" HEXOK: BIT #170000,R0 ; CK FOR POSSIBLE OVERFLOW BNE HEXOFL ; BR IF OVERFLOW ASH #4,R0 ; MULT RESULT BY 16 ADD R1,R0 ; ADD IN NEW HEX VALUE INC R2 ; ADVANCE A(STRING) 1 CHAR BR NEXTCH ; CONTINUE CONVERTING CHARACTERS HEXOFL: $POP R1 ; RESTORE CALLERS REGS RTS PC ; RETURN TO CALLER (ERROR RTN) NONHEX: $POP R1 ; RESTORE CALLERS REGS ADD #2,(SP) ; ADJUST RETURN RTS PC ; RETURN TO CALLER (NORMAL RTN) .PAGE .SBTTL . CVTBIN - CONVERT A RADIX-10 STRING TO BINARY ;CVTBIN IS CALLED FROM THE OPEN COMMAND HANDLER. IT SCANS AN ASCII STRING ;FROM LEFT TO RIGHT CONVERTING TO BINARY AS IT GOES, AND STOPS WHEN IT ;COMES TO A NON-NUMERAL, WHICH IS ASSUMED TO BE A DELIMITER. ;CALL-- R2: A(STRING) ;RETURN-- R0: LOW ORDER WORD OF NUMBER IN BINARY ; R1: HI ORDER WORD OF NUMBER IN BINARY ; R2: A(DELIMITER) ; OTHER REGISTERS UNAFFECTED. CVTBIN: $PUSH R3,R4,R5 ;SAVE CALLER CLR R0 ;INIT LOW WORD OF NUMBER CLR R1 ;INIT HI WORD OF NUMBER CLR R3 ;INIT TEMP STORAGE CLC ;CLEAR CARRY BIT ;ACCUMULATE THE NUMBER IN R0,R1 BY LOOPING. EACH ACCUMULATION OPERATION ;CONSISTS OF MULTIPLYING THE NUMBER BY 10, AND THEN ADDING IN THE NEXT ;DIGIT IN THE STRING. EXIT THE LOOP WHEN A DELIMITER IS FOUND. 1$: MOVB (R2)+,R3 ;GET NEXT CHAR TO R3 SUB #60,R3 ;CONVERT TO BINARY CMPB #9.,R3 ;A NUMERAL? BLO 2$ ;NO, MUST BE DELIMITER, EXIT MOV R0,R4 ;SAVE NUMBER LOW WORD MOV R1,R5 ;SAVE NUMBER HI WORD ROL R0 ;DOUBLE PRECISION ROL R1 ;MULTIPLY BY 2 ROL R0 ;DOUBLE PRECISION ROL R1 ;MULT BY 4 ADD R4,R0 ;ADD BACK IN ADC R1 ;ORIGINAL NUMBER TO ADD R5,R1 ;GET D.P. MULT BY 5 ROL R0 ;MULT BY 2 ROL R1 ;TO GET MULT BY 10 ADD R3,R0 ;ADD IN NEXT DIGIT ADC R1 ;TO THE RESULT BR 1$ ;AND LOOP TILL DONE 2$: DEC R2 ;LEAVE A(DELIMITER) IN R3 $POP R5,R4,R3 ;RESTORE CALLER RTS PC ;AND RETURN .PAGE .SBTTL . NOUT - NUMBER TO STRING CONVERSION ; ; CALLED WITH: R0 - NUMBER ; R1 - # CHARS CURRENTLY IN STRING ; R2 - ADDRESS OF STRING BUFFER ; ; RETURNS WITH: R1 - BUMPPED BY # OF CHARS IN NUMER STRING ; R2 - POINTING 1 PAST END OF STRING ; NOUT: $PUSH R0,R1,R3,R4 MOV R0,R1 ;GET IN LOW WORD FOR DIVIDE CLR R0 CLR R3 ;INIT CHAR COUNTER 1$: DIV #10.,R0 ;DIVIDE BY 10 ADD #60,R1 ;CONVERT REMAINDER TO ASCII $PUSH R1 ;AND SAVE ON STACK INC R3 ;BUMP CHAR COUNTER TST R0 ;QUOTENT ZERO? BNE 1$ ;IF NOT, CONTINUE DIVIDE MOV R3,R4 ;REMEMBER NUMBER OF CHARS 2$: MOVB (SP)+,(R2)+ ;TRANSFER CHAR FROM STACK TO STRING $LOOP R3,2$ ADD R4,4(SP) ;BUMP CHARACTER COUNT $POP R4,R3,R1,R0 RTS PC .PAGE .SBTTL . $READ - CHARACTER INPUT $READ: $PUSH R0 MOV TCT,R0 ADD #INIORB,R0 ;GET INPUT IORB ADDRESS $SIO R0 $POP R0 RTS PC .PAGE .SBTTL . $COUT - OUTPUT CHARACTERS TO TTY ; ; CALLED WITH: R0 - ADDRESS OF START OF MESSAGE ; R1 - MESSAGE LENGTH ; $COUT: $PUSH R0 MOV TCT,R0 ADD #OTIORB,R0 ;GET ADDR OF OUTPUT IORB MOV (SP),IRUVA(R0) ;SET-UP BUFFER ADDRESS MOV R1,IRBR(R0) ;BYTE COUNT MOVB #SG.ECO,IROPC(R0) ;INDICATE ECHO OR MSG OUTPUT BIC #TTYIDL,STATUS ;FLAG TTY BUSY $SIO R0 ;START OUTPUT $POP R0 RTS PC .PAGE .SBTTL .SBTTL LOCAL STORAGE AND MESSAGE TABLES .CSECT TELNTD, STATIC .NLIST MEB .MACRO CMD CMDLTR,HNDLR,PRIV,MSG,?A,?B .ASCIZ /CMDLTR/ ;COMMAND LETTER .WORD HNDLR ;HANDLER TRANSFER ADDRESS .CSECT TELSTR, STATIC .BYTE PRIV ;PRIVILEGE LEV (0 OR 1-7) ..TMP=. .BYTE B-A A: .ASCII /MSG/ B: .CSECT TELNTD, STATIC .WORD ..TMP ;ADDR OF COMMAND COMPLETION STRING .ENDM ; ; COMMAND TABLE ; TCMDTB: CMD ?,HELP,0,< help> ;LIST TELNET COMMANDS CMD B,BNMODE,1, ;BINARY TRANSMISSION MODE CMD C,TELCLS,0, ;CLOSE CONNECTION .IF NE KF.DDT CMD D,DDTBRK,7,<- break to DDT> ;BREAK TO DDT .ENDC CMD E,ECOMOD,1, ;SET ECHO MODE CMD G,TRFGEN,1, ;ENABLE TRAFFIC GENERATOR CMD I,INTCPT,1, ;CHANGE INTERCEPT CHARACTER CMD L,OPENC,0, ;OPEN CONNECTION CMD O,OPENC,0, ;OPEN CONNECTION CMD P,PROCOL,1, ;SET PROTOCOL MODE CMD R,RESET,0, ;RESET TELNET STATE CMD S,OPENS,1, ;OPEN STICKY CONNECTION CMD T,XMTMOD,1, ;SET TRANSMIT MODE CMD U,USERLV,0, ;USER PRIVILEGE LEVEL CMD V,VERSON,0, ;SOFTWARE VERSION & IDS CMD Z,CANMSG,1,<-canned msg> ;SEND CANNED MSG AT REG INTER. CMD K,SNDIP,0, ;SEND TELNET IP SEQUENCE .WORD 0 ;STATUS MESSAGE TABLE .MACRO MSG NAME,STRNG,?A,?B .IF NB, NAME NAME = ..CNT .CSECT TELSTG, STATIC ..TMP = . .BYTE B-A A: .ASCII STRNG B: .CSECT TELNTD, STATIC .WORD ..TMP .IFF .WORD 0 .ENDC ..CNT=..CNT+2 .ENDM ..CNT=0 MSGTBL: MSG MSGOPN,<\ Open\> MSG MSGERR,<\ Connection error\> MSG MSGHNR,<\ Host not responding\> MSG MSGCLS,<\ Closed\> MSG MSG MSGRFS,<\ Connection refused\> MSG MSG MSGRC,<\ Remote disconnect\> MSG MSGHDR,<\LCSNET-TIU TELNET 8-8-79.\> MSG MSGBAD,<\ Bad command syntax\> MSG MSGRPN,<\ Closed, reopening connection\> MSG MSGC1,<\ Can't, connection already open\> MSG MSGC2,<\ Can't, IMP/PR not ready\> MSG MSGC3,<\ Can't, Station not responding\> MSG MSGTRY,<\Trying...\> MSG MSGCNT,<\ Can't negotiate TELNET REMOTE ECHO option\> MSG MSGAB,<\ Connection aborted\> ERRTBL: .WORD MSGTRY ;IF RETURN CODE =0, TYPE 'TRYING' .WORD MSGC1 ; =1, TYPE 'CANT, ALREADY OPEN' .WORD MSGBAD ; =2, TYPE 'BAD SYNTAX' .WORD MSGC2 ; =3, TYPE 'CANT, IMP/PR NOT READY' .WORD MSGC3 ; =4, TYPE 'CANT STATION NOT RESPONDING' ; CANBUF: .ASCII \;CANNED MSG.\ .EVEN .PAGE .SBTTL CHARACTER TYPE TABLE ; ; THE CHARACTER TYPE TABLE IS INDEXED BY THE 8 BIT CHARACTER CODE AND THE ; RESULTANT BYTE INDICATES IN THE LOW-ORDER 4 BITS THE TELNET RCTE ; CHARACTER CLASS, THE HIGH ORDER 4 BITS ARE USED AS VARIOUS FLAGS INDICATING ; IF THE CHARACTER SHOULD BE ECHOED IN LOCAL ECHO MODE OR IF THE CHARACTER ; SIGNALS TRANSMISSION IN LINE-AT-A-TIME MODE. ; $CHTBL: .REPT 7. ;FOR CHARACTERS 'NULL' THROUGH 'ACK' .BYTE CHCLS5!CHXMT!CHNECO ; CHAR CLASS 5, DON'T ECHO AND XMIT .ENDR .BYTE CHCLS5!CHXMT ;FOR 'BELL', CLASS 5, ECHO AND XMIT .REPT 2. ;FOR CHARACTERS 'BS' THROUGH 'TAB' .BYTE CHCLS4!CHXMT!CHNECO ; CHAR CLASS 4, DON'T ECHO AND XMIT .ENDR .BYTE CHCLS4!CHXMT ;FOR 'LF', CLASS 4, ECHO AND XMIT .REPT 2 ;FOR CHARACTERS ''VT' THROUGH 'FF' .BYTE CHCLS4!CHXMT!CHNECO ; CHAR CLASS 4, DON'T ECHO AND XMIT .ENDR .BYTE CHCLS4!CHXMT ;FOR 'CR', CLASS 4, ECHO AND XMIT .REPT 18. ;FOR CHARACTERS 'SO' THROUGH 'US' .BYTE CHCLS5!CHXMT!CHNECO ; CHAR CLASS 5, DON'T ECHO AND XMIT .ENDR .BYTE CHCLS9 ;FOR 'SPACE', CLASS 9, ECHO & DON'T XMIT .BYTE CHCLS6 ;FOR '!', CLASS 6 .REPT 6 ;FOR CHARACTERS '"' THROUGH ''' .BYTE CHCLS8 ; CHAR CLASS 8 .ENDR .REPT 2 ;FOR CHARACTERS '(' AND ')' .BYTE CHCLS7 ; CHAR CLASS 7 .ENDR .REPT 2 ;FOR CHRACTERS '*' AND '+' .BYTE CHCLS8 ; CHAR CLASS 8 .ENDR .BYTE CHCLS6 ;FOR ',', CLASS 6 .BYTE CHCLS8 ;FOR '-', CLASS 8 .BYTE CHCLS6 ;FOR '.', CLASS 6 .BYTE CHCLS8 ;FOR '/', CLASS 8 .REPT 10. ;FOR CHARACTERS '0' THROUGH '9' .BYTE CHCLS3 ; CHAR CLASS 3 .ENDR .REPT 2 ;FOR CHARACTERS ':' THROUGH ';' .BYTE CHCLS6 ; CHAR CLASS 6 .ENDR .BYTE CHCLS7 ;FOR '<', CLASS 7 .BYTE CHCLS8 ;FOR '=', CLASS 8 .BYTE CHCLS7 ;FOR '>', CLASS 7 .BYTE CHCLS6 ;FOR '?', CLASS 6 .BYTE CHCLS8 ;FOR '@', CLASS 8 .REPT 26. ;FOR CHARACTERS 'A' THROUGH 'Z' .BYTE CHCLS1 ; CHAR CLASS 1 .ENDR .BYTE CHCLS7 ;FOR '[', CLASS 7 .BYTE CHCLS8 ;FOR '\', CLASS 8 .BYTE CHCLS7 ;FOR ']', CLASS 7 .REPT 3 ;FOR CHARACTERS '^' THROUGH '`' .BYTE CHCLS8 ; CHAR CLASS 8 .ENDR .REPT 26. ;FOR CHARACTERS 'a' THROUGH 'z' .BYTE CHCLS2 ; CHAR CLASS 2 .ENDR .BYTE CHCLS7 ;FOR '{', CLASS 7 .BYTE CHCLS8 ;FOR '|', CLASS 8 .BYTE CHCLS7 ;FOR '}', CLASS 7 .BYTE CHCLS8 ;FOR '~', CLASS 8 .BYTE CHCLS5!CHXMT!CHNECO ;FOR 'DEL', CLASS 5, NO ECHO & XMIT TTLFLG: .BYTE 0 ;TELNET initializer flag TTLCNT: .BYTE 0 ;TELNET counter .END