.TITLE REMOTE - Connect to remote CPU .IDENT /3.2/ .ENABL LC .NLIST BEX ;--------------------------------------------------------------------------- ; ; REMOTE is a program that allows the user to communicate with ; a remote computer through a serial port on a PDP 11 running RSX. ; ; Program usage: ; ; MCR>REMOTE [TTn:] ; ; This command logically connects the user's terminal with ; the serial port specified (TTn: if given, otherwise the TKB ; defined port). Commands to this program must be prefixed with ; a control character (or prefix character), a control A (^A). ; The next character is assumed to be a command character, and ; the specified action is taken. ; There are two special cases to consider. To transmit a ^A to ; the second port, two ^A's must be typed. ^B, the command to ; transmit a line or suspend/resume file transmission, does not ; need a prefix, assuming that a transmit file is open. ; ; The known commands are as follows: ; ; ^A prefix character ; ^B send a line or suspend/resume transmission ; A Abort file transmission ; B Baud rate selection 110/300/1200 ; D Delay time for characters, lines ; E Exit ; F Full file or line-by-line transfer ; H Help text ; I Initialize Hayes modem and dial out ; K Kill Hayes modem and exit ; P Parity: none/even/odd ; R Recieve a file ; S Status of files, etc ; T Transmit a file ; Z close received file ; ;----------------------------------------------------------------------------- ;----------------------------------------------------------------------------- ; ; Modification history: ; ; Version 1.00 m heller october 1979 ; DUPLEX created. ; 1.05 m heller december 1979 ; version submitted to the DECUS library. ; 2.0 h gregg 7 july 1982 ; changed command structure, allow file spec for in/out, ; specify any port, generally cleaned up and reorginized ; 2.1 h gregg december 1983 ; added parity, speed, delays (as per g everhart's mods) ; dial-out for V4.1, HAYES modem support ; 2.2 h gregg march 1984 ; cleaned up a bit more ; 2.3 h gregg april 1984 ; added multibuffered output for disk file ; 3.0 h gregg april 1984 ; changed modem handling - no longer an AST per ; character, now notified, length of typeahead ; checked, and QIO issued, and then characters ; processed. ; 3.1 h gregg april 1984 ; added wait in modem input - allow a few characters ; to be input at a time. At 1200 baud, with 1 tic ; wait, about 7 chars max in typeahead buffer. So ; at 300 baud, wait of 4 tics, should also be about ; the same number of characters. At 2400 baud, no ; waiting gives about 6 characters in the buffer. ; At 4800 baud (11/23), about 30 char in the buffer ; as seen by RMD, but some characters are lost. ; 3.2 h gregg may 1984 ; allowed receive file to be reused, cleaned up ; intercharacter delays ; ; To build: ; ; MAC REMOTE=REMOTE ; TKB>REMOTE/-CP/PR:0=REMOTE,LB:[1,1]SYSLIBMBF/DL ; ; needs priv to set chars on modem, use multibuffered SYSLIB ; TKB>/ ; better performance if /-CP ; TKB>UNITS=4 ; only four ; TKB>ASG=TI:1,SY:3:4 ; TI:, in and out files ; TKB>ASG=TT1:2 ; default modem port ; TKB>PRI=110 ; better performance ; TKB>// ; ; ;--------------------------------------------------------------------------- ;--------------------------------------------------------------------------- ; ; ; Program internals: ; ; Logical unit assignments: ; ; 1 user's terminal, command terminal ; 2 second port, usually connected to a modem ; 3 transmit file LUN ; 4 receive file LUN ; ; Event flag structure: ; ; 1 user's terminal output (Remote's messages, ; not info from other port) ; 2 second port - mainly for initialization ; 3 flag from AST routines that there is a ; command to be processed. ; 4 flag that indicated a line of text has finished ; transmission to the second port. ; 5 flag that indicates that there is a buffer of ; text to write to the disk. ; 6 flag that line delay is done (also for characters) ; 7 flag for writing to receive file. ; 10 flag that the modem has some characters ; 11 flag for wait in MODIN routine. ; 12 flag for intercharacter delay is done ; ; Both transmit and receive file names are user specified, the ; receive file defaulting to RECEIVED.DAT. The receive file ; is set as a 'normal' text file with variable length records and ; no funky FORTRAN characters. ; ; Each character from either port is received with an AST routine, ; which is 'slow', but effective. All character transmissions ; are done in the AST routines; the character fetched, put in ; a circular buffer (for different terminal speeds), check for ; a command (unit 1 only), queued to the other port, and if ; necessary, put into a buffer for the received file. All ; command processing is done out of the ASTs - the AST routine ; specifies an event that wakes the main routine up to do the ; work. ; ; For version 3.0, the modem port is no longer an unsolicited AST - ; instead its a notification AST. This sets event flag 10, and ; the main program go and picks up characters from the type-ahead ; buffer. ; ; Delay added (see revision history) for V3.1 to aid system performance. ; The terminal handler can buffer things easier than us continually ; asking it for a single character. ; ;----------------------------------------------------------------------------- .SBTTL Definitions and macros ; ; MACRO definitions ; .PSECT DATA .MCALL QIO$S,QIOW$S,QIO$,QIOW$,DIR$,GMCR$,ASTX$S,EXIT$S,ALUN$ .MCALL CLEF$S,SETF$S,WTLO$S,WTSE$S,RDAF$S,MRKT$S,CMKT$S .MCALL FCSMC$,CSI$,CSI$1,CSI$2 ; ; MACRO to PRINT out a line of text, with CRs and LFs ; .MACRO PRINT,STRING,?TEMP1,?TEMP2 .NCHR LENGTH, ; get how long it is MOV #TEMP1,PRNT+Q.IOPL ; put buffer in place MOV #LENGTH+4,PRNT+Q.IOPL+2 ; stuff len + extra in place DIR$ #PRNT ; do it BR TEMP2 ; branch about the ascii TEMP1: .ASCII @STRING@ .EVEN TEMP2: .ENDM ; ; MACRO to PRInt text, with no carriage control ; .MACRO PRI,STRING,?TEMP1,?TEMP2 .NCHR LENGTH, ; get the length MOV #TEMP1,PRNT+Q.IOPL ; buffer to place MOV #LENGTH,PRNT+Q.IOPL+2 ; and length DIR$ #PRNT ; and print it BR TEMP2 ; and continue TEMP1: .ASCII @STRING@ .EVEN TEMP2: .ENDM ; ; MACRO to READ a line of text, after a prompt ; .MACRO READ,STRING,?TEMP1,?TEMP2 .NCHR LENGTH, ; get the length of our message MOV #TEMP1,QRPR+Q.IOPL+6 ; put the buffer into its place MOV #LENGTH+2,QRPR+Q.IOPL+10 ; and its length CLRB SETBIN+1 ; first, we must turn off read pass all DIR$ #QBIN ; no binary, until after the read DIR$ #QRPR ; and ask us the question INCB SETBIN+1 ; set the characteristic again DIR$ #QBIN ; DIR$ #PRNTLF ; send out a line feed to clean up BR TEMP2 ; branch around TEMP1: .ASCII @STRING@ .EVEN TEMP2: .ENDM ; LF = 12 CR = 15 ESC= 33 .SBTTL Get/Set multiple characteristics buffers ; ; get/set multiple characteristic parameters ; ; Note that TC.PAR, TC.EPA, and TC.DLU=2 are undocumented features ; of the full duplex handler, and will work only for interfaces that ; support these characteristics, such as DZ-11's and DH-11's. ; SETMC: .BYTE TC.BIN,1 ; for setting read-pass-all .BYTE TC.FDX,1 ; full duplex .BYTE TC.ACR,0 ; no wraps .BYTE TC.RAT,1 ; make sure the typeahead is on LSETMC= . - SETMC ; RSETMC: .BYTE TC.BIN,0 ; clear read-pass-all .BYTE TC.FDX,0 ; back to half duplex .BYTE TC.ACR,1 ; wrap the lines LRSETM= . - RSETMC ; SETBIN: .BYTE TC.BIN,0 ; read pass all ; OLDCHR: ; original characteristics, to be returned OLDDLU: .BYTE TC.DLU,0 ; 0 = local, 1 = remote, 2 = dial-out OLDRSP: .BYTE TC.RSP,0 ; receiver speed OLDXSP: .BYTE TC.XSP,0 ; transmitter speed OLDASP: .BYTE TC.ASP,0 ; answer speed OLDABD: .BYTE TC.ABD,0 ; autobaud OLDPAR: .BYTE TC.PAR,0 ; PAR = parity OLDEPA: .BYTE TC.EPA,0 ; EPA = even/odd LOLDCH= . - OLDCHR ; SETSPD: .BYTE TC.ABD,0 ; turn off autobaud .BYTE TC.RSP,S.300 ; set receive speed to 300 baud .BYTE TC.XSP,S.300 ; transmit speed .BYTE TC.ASP,S.300 ; answer speed LSETSP= . - SETSPD ; DIALOU: .BYTE TC.DLU,2 ; set line to dial-out LDIALO= . - DIALOU ; SETPAR: ; set parity SPAR: .BYTE TC.PAR,0 ; enable/disable parity SEPA: .BYTE TC.EPA,0 ; even/odd parity LSETPA= . - SETPAR ; TYPAHD: .BYTE TC.TBF,0 ; read size of typeahead buffer LTYPAH= .-TYPAHD .SBTTL Buffers and pointers ; ; Buffers and associated pointers ; CIRSIZ = 256. BUFSIZ = 136. B1PTR: .WORD BUF1 ; pointer into TI:'s input buffer B2PTR: .WORD BUF2 ; pointer into TTn:'s input buffer RPTR: .WORD RBUF1 ; pointer into received file buffer RBBEG: .WORD RBUF1 ; beginning of the receive buffer RBEND: .WORD RBUF1+BUFSIZ ; and the end of the buffer IOSB: .BLKW 2 ; In/Out Status Block EFLAGS: .BLKW 4 ; buffer for event flags ; CREQ: .ASCII /=/ ; general , and '=' for input filespec INBUF: .BLKB 80. ; a nice sized input buffer BUF1: .BLKB CIRSIZ ; circle buffer for TI:'s input BUF2: .BLKB CIRSIZ+36. ; buffer for TTn:'s input (+typeahead) RBUF1: .BLKB BUFSIZ ; receive file buffer number 1 RBUF2: .BLKB BUFSIZ ; buffer number 2 ; .SBTTL Flags and their definitions ; ; Flags and their meanings ; PCMD: .BYTE 0 ; 0 = no prefix; 1 = prefix received XCMD: .BYTE 0 ; command byte XFMODE: .BYTE -1 ; Xfer: -1=full suspend; 0=full; 1=lines RCVFIL: .BYTE 0 ; 0 = no file; 1 = receive file open XACTIV: .BYTE 0 ; 0 = no file; 1 = transmit file open FPARTY: .BYTE 0 ; 0 = no parity, 1 = odd, 2 = even FSPEED: .BYTE S.300 ; S.110, S.300, S.1200 baud .EVEN ; following must be word aligned CDELAY: .WORD 0 ; no character delay in progress (# chrs) LINDEL: .WORD 1 ; line delay, ticks CHRDEL: .WORD 0 ; character delay, ticks .SBTTL FCS initialization FCSMC$ ; define FCS macros FSRSZ$ 3 ; initialize FCS record buffer CSI$ ; define CSI control block .PSECT DATA ; get back to our own PSECT ; XMTFDB: FDBDF$ ; for file to transmit FDBF$A 4 ; use event flag 4 for fetching a line FDRC$A ,XMTBUF,BUFSIZ ; FDOP$A 3,XMTCSI+C.DSDS,,FO.RD!FA.SHR ; filename will be parsed by CSI XMTBUF: .BLKB BUFSIZ ; I/O buffer XMTCSI: .BLKB C.SIZE ; space for CSI to work ; RCVFDB: FDBDF$ ; for file to receive FDOP$A 4,RCVCSI+C.DSDS,RCVDEF,FO.WRT,FA.DLK ; CSI filename + default RCVDEF: NMBLK$ RECEIVED,DAT ; default to SY:RECEIVED.DAT RCVCSI: .BLKB C.SIZE ; CSI's buffer ; .SBTTL Directives for later use ; ; Directives ; AQ1: QIO$ IO.WVB,2,,,,,<,1,0> ; input on unit 1, output to 2 AQ2: QIO$ IO.WVB,1,,,,,<,1,0> ; input on unit 2, output to 1 PRNT: QIOW$ IO.WVB,1,1,,IOSB,,<,,0> ; print a message to the user QRPR: QIOW$ IO.RPR,1,1,,IOSB,, ; read after prompt PRNTLF: QIOW$ IO.WVB,1,1,,,, ; just a simple line feed LINEFD: .ASCIZ ALUN: ALUN$ 2,TT,0 ; assign unit 2 to another terminal GMCR: GMCR$ ; directive and it's buffer QSMC: QIOW$ SF.SMC,1,1,,IOSB,, ; set terminal characteristics QGMC: QIOW$ SF.GMC,2,2,,IOSB,, ; get terminal characteristics QBIN: QIOW$ SF.SMC,1,1,,,, ; to toggle binary read on/off SIZBUF: QIOW$ SF.GMC,2,2,,IOSB,, ; get size of typeahead buffer RMODEM: QIOW$ IO.RAL!TF.RNE,2,2,,IOSB,,<,,> ; read characters from buffer QXMIT: QIO$ IO.WVB,2,4 ; write request for transmit file ; .SBTTL Entry point and initialization ; .PSECT REMOTE REMOTE::QIOW$S #IO.ATA,#1,#1,,#IOSB,,<#AST1>; attach device 1 DIR$ #QSMC ; set the terminal characteristics ; ; get command line - look for first space ; 10$: DIR$ #GMCR,35$ ; get a command line, if present MOV #GMCR+G.MCRB,R0 ; get the address of the buffer 15$: MOVB (R0)+,R1 ; get a character CMPB #40,R1 ; check for printing character BLT 15$ ; still have one (no space yet) BGT 35$ ; terminator - no TTn: given ; ; now check for TT, and try to pull in a number ; CMPB #'T,(R0)+ ; first T? BNE 30$ ; nope - trash line CMPB #'T,(R0)+ ; second T? BNE 30$ ; nope - trash CLR R1 ; build the number here 20$: MOVB (R0)+,R2 ; get a character CMPB #':,R2 ; end of number? BEQ 25$ ; yup - got it SUB #60,R2 ; make binary of the ascii BLT 30$ ; oops - not a number. error. CMP #7,R2 ; better be 7 or less ... BLT 30$ ; error ASL R1 ; make room in our number for ASL R1 ; the new octal digit ASL R1 ; ADD R2,R1 ; make the new number BR 20$ ; and try to make it bigger ; 25$: MOV R1,ALUN+A.LUNU ; put in the unit number DIR$ #ALUN,30$ ; and assign it CMPB #IS.SUC,$DSW ; did it work? BEQ 35$ ; yup - go attach it 30$: PRINT JMP EXIT1 ; and leave this dump ; ; loon 2 assigned to terminal, now attach it; do some initialization ; 35$: MRKT$S #6,#5,#2,#30$ ; give us 5 seconds to attach QIOW$S #IO.ATA!TF.NOT,#2,#2,,#IOSB,,<#AST2> ; attach with AST notify CMKT$S #6 ; we're attached, line is available MOV #2,QSMC+Q.IOLU ; set loon 2 DIR$ #QSMC ; and set the characteristics FINIT$ ; init FCS MOV #BUF1,B1PTR ; terminal's input buffer MOV #BUF2,B2PTR ; port 2's input buffer CLEF$S #3 ; clean our flags CLEF$S #4 ; CLEF$S #5 ; CLEF$S #6 ; .SBTTL Modem port initialization ; ; First, get current parameters so they can be returned ; DIR$ #QGMC ; get current parity, speed, etc MOVB OLDRSP+1,FSPEED ; set our internal variables, speed MOVB OLDEPA+1,FPARTY ; parity INCB FPARTY ; make inout our standards (1=odd,2=even) TSTB OLDPAR+1 ; was parity enabled? BNE 40$ ; yup - we're set CLRB FPARTY ; inform us of no parity ; ; if autobaud, set speed to 300 ; 40$: TSTB OLDABD+1 ; autobaud enabled? BEQ 45$ ; nope - use current speed MOVB #S.300,FSPEED ; use 300 baud MOV #SETSPD,QSMC+Q.IOPL ; get instructions MOV #LSETSP,QSMC+Q.IOPL+2 ; and their length DIR$ #QSMC ; and set the speed ; ; are we set local, remote or dial-out? ; 45$: CMPB #1,OLDDLU+1 ; check DLU BNE 50$ ; 0 = local, 2 = dial-out. both OK MOV #DIALOU,QSMC+Q.IOPL ; line is remote - set for dial-out MOV #LDIALO,QSMC+Q.IOPL+2 ; DIR$ #QSMC ; set dial-out ; ; All set - now tell us ; 50$: PRINT .SBTTL Wait and command dispatcher ; ; We wait here, until we're someone wakes us up ; WAIT: WTLO$S 0,#1274 ; wait for a signal (flags 3,4,5,6,10,12) RDAF$S #EFLAGS ; someone woke us - who? BIT #4,EFLAGS ; check for EF #3 BNE 30$ ; thats it - EF #3 ==> command BIT #200,EFLAGS ; check for EF #10 BNE 4$ ; thats it - EF #10 ==> modem has input BIT #20,EFLAGS ; check for EF #5 BNE 10$ ; thats it - EF #5 ==> must write buffer BIT #10,EFLAGS ; check for EF #4 BNE 20$ ; thats it - EF #4 ==> line trans. done BIT #40,EFLAGS ; check for EF #6 BNE 5$ ; thats it - EF #6 ==> line delay finished ; ; event flag #12 woke us up - character delay is done ; CLEF$S #12 ; clear the flag CALL XMITC ; and transmit another character BR WAIT ; and go wait for more ; ; event flag #10 woke us up - modem has input characters ; 4$: CLEF$S #10 ; clear the flag CALL MODIN ; get input from the modem BR WAIT ; and wait for more to do ; ; event flag #6 woke us up - line delay must be done ; 5$: CLEF$S #6 ; clear the flag CALL XMITL ; go transmit the line BR WAIT ; and wait for something else to do ; ; event flag #5 woke us up - put buffer to disk ; 10$: CLEF$S #5 ; clear the flag for the future PUT$ #RCVFDB ; and ship out the line BCC WAIT ; no errors PRINT CALL RECVCL ; go close the file BR WAIT ; and go back to sleep ; ; event flag 4 woke us up - transmit another line of the file? ; 20$: CLEF$S #4 ; first arm the flag again TST CDELAY ; character delay in progress? BEQ 25$ ; nope - check other possibilities CALL XMITCD ; go delay for the next character BR WAIT ; and wait until it's done 25$: TSTB XACTIV ; still have the file open? BEQ WAIT ; nope - just go to sleep TSTB XFMODE ; file open - full file or line BNE WAIT ; line-by-line or suspended. wait. CALL XMITLN ; doing a file transfer - do a line JMP WAIT ; and go back to sleep ; ; event flag 3 woke us up - we've got a command ; 30$: CLEF$S #3 ; first clear it MOVB XCMD,R0 ; get the command BIC #177640,R0 ; making sure its clean MOV #TABLE,R1 ; get the base of our table ; ; loop thru the table, looking for a matching entry ; 50$: MOV (R1)+,R2 ; get the command's address BEQ 60$ ; if zero, end of table CMP (R1)+,R0 ; is this our command? BNE 50$ ; nope - check more CALL (R2) ; got a valid command - do it JMP WAIT ; then go and wait ; ; bad command - tell us ; 60$: PRINT JMP WAIT ; and go wait for more ; .SBTTL TABLE - the list of known commands ; ; The table format is an address, and a word with the letter ; to match. A zero address is the end of the table. ; TABLE: .WORD XMTLIN, 2 ; ^B is transmit a line .WORD XMITAB, 'A ; Abort a file transfer .WORD SPEED, 'B ; Baud rate selection .WORD DELAY, 'D ; Delay time parameters .WORD EXIT, 'E ; Exit .WORD FULLIN, 'F ; Full file/line-by-line transfer .WORD HELP, 'H ; Help .WORD INIHAY, 'I ; Initialize the Hayes modem and dial .WORD KILHAY, 'K ; Kill Hayes modem and exit .WORD PARITY, 'P ; Parity .WORD RECV, 'R ; Receive a file .WORD STATUS, 'S ; Status of files, flags .WORD XMIT, 'T ; Transmit a file .WORD RECVCL, 'Z ; close receive file .WORD 0 ; thats all folks ... .SBTTL XMIT - Open a transmit file ; ; get file descripter, parse it, open input file ; XMIT: READ INC IOSB+2 ; bump the length to count the '=' CSI$1 #XMTCSI,#INBUF-1,IOSB+2 ; compress line BCS 30$ ; error handling CSI$2 #XMTCSI,INPUT ; parse file descriptor BCS 30$ ; error handling OPEN$R #XMTFDB ; open file for reading BCS 40$ ; any errors? ; ; file is open, set flags and send it ; CLEF$S #4 ; clear our flag CLR CDELAY ; set no character delay in progress INCB XACTIV ; flag file active TSTB XFMODE ; full file Xfer, or line-by-line? BGT 20$ ; line-by-line - tell us how. BLT 10$ ; file transmissions is supresses, tell us JMP XMITLN ; full file, transmission not suppresses 10$: PRINT RETURN ; 20$: PRINT RETURN ; and wait for a command ; ; error opening file -- say so ; 30$: PRINT RETURN 40$: CMPB #IE.NSF,XMTFDB+F.ERR ; check for No Such File BNE 50$ ; that's not the error ... PRINT BR 60$ ; 50$: PRINT 60$: BR XMITCL ; go 'close' the file ; .SBTTL XMITCL - Close transmit file ; ; either error or end-of-file; don't care which, just close the file ; XMITAB: PRINT XMITCL: CLOSE$ #XMTFDB ; close the file CLRB XACTIV ; flag it closed CLEF$S #4 ; and clear our flag TSTB XFMODE ; check file transmission mode BNE 10$ ; in line-by-line or suspended DECB XFMODE ; full file, suspended 10$: RETURN ; and head home .SBTTL XMTLIN - transmit a line of the file (^B), resume transmission ; ; check validity of the command ; XMTLIN: TSTB XACTIV ; file open? BNE 10$ ; yup. PRINT RETURN ; and go wait for more things to happen 10$: TSTB XFMODE ; line-by-line transmission? BGT XMITLN ; yup. BLT 20$ ; we are stopped- go restart DECB XFMODE ; we're transmitting - suspend RETURN ; and head for more commands 20$: CLRB XFMODE ; tell us we can send lines out BR XMITLN ; and start now. ; .SBTTL XMITLN - Transmit a line of the file ; ; read a record from the file and send to the other unit ; ; ; now check for any delays that we must do ; XMITLN: CLR CDELAY ; no character delay in progress now TSTB LINDEL ; any inter-line delay? BEQ XMITL ; nope go look for character delay MRKT$S #6,LINDEL,#1 ; delay (LINDEL) ticks, event flag 6 RETURN ; and go wait for it XMITL: GET$ #XMTFDB ; get a line BCC 30$ ; no error CLEF$S #4 ; clear our flag CMPB #IE.EOF,XMTFDB+F.ERR ; check for a valid end of file BNE 10$ ; this isn't one ... PRINT BR 20$ ; and go close the file 10$: PRINT 20$: JMP XMITCL ; go close (?) the file ; ; get rid of leading line feeds ; 30$: TST XMTFDB+F.NRBD ; check the length of the buffer BEQ 40$ ; zero - go send a only CMP #LF,@XMTFDB+F.NRBD+2 ; check the first character BNE 40$ ; different - let it slide INC XMTFDB+F.NRBD+2 ; point past the DEC XMTFDB+F.NRBD ; and decrement the count BR 30$ ; and do it again 40$: TSTB CHRDEL ; any inter-character delay? BNE 11$ ; yea - too bad TST XMTFDB+F.NRBD ; check the length of the buffer BNE 5$ ; not zero - send the line out JMP XMITCR ; and send out a for blank lines 5$: MOV XMTFDB+F.NRBD+2,QXMIT+Q.IOPL ; get address of the input buffer MOV XMTFDB+F.NRBD,QXMIT+Q.IOPL+2 ; get length of the input buffer MOV #'+,QXMIT+Q.IOPL+4 ; put the carriage control character in 6$: DIR$ #QXMIT ; and issure the request RETURN ; send line, done with flag 4 up. ; ; Must send the line with inter-character delay. Very slow. ; 11$: MOV XMTFDB+F.NRBD+2,QXMIT+Q.IOPL ; address of the buffer to send DEC QXMIT+Q.IOPL ; point before the first character MOV XMTFDB+F.NRBD,CDELAY ; and its length INC CDELAY ; and up the count (don't forget ) MOV #1,QXMIT+Q.IOPL+2 ; ship out one character at a time CLR QXMIT+Q.IOPL+4 ; no carriage control until the last XMITCD: MRKT$S #12,CHRDEL,#1 ; wait (CHRDEL) ticks RETURN ; and wait until we're ready XMITC: CMP #1,CDELAY ; are we through with the line? BEQ XMITCR ; yup - go give us a INC QXMIT+Q.IOPL ; point to the character to be sent DIR$ #QXMIT ; and ship it out DEC CDELAY ; decrement the count (delay flag) RETURN ; and go wait until we're done XMITCR: MOV #CREQ,QXMIT+Q.IOPL ; get a carriage return MOV #1,QXMIT+Q.IOPL+2 ; and a length of 1 CLR QXMIT+Q.IOPL+4 ; and no carriage control DIR$ #QXMIT ; and send it out CLR CDELAY ; done with the delays for this line RETURN ; all done with this line .SBTTL RECV - Open received data file ; ; get file name, parse it, open output file ; RECV: READ CSI$1 #RCVCSI,#INBUF,IOSB+2 ; compress the line BCS 10$ ; error - go tell us CSI$2 #RCVCSI,OUTPUT ; parse the filespec as an output file BCS 10$ ; error - go tell us FDBF$R #RCVFDB,#7,,#2 ; event flag 7, multibuffered FDAT$R #RCVFDB,#R.VAR,#FD.CR,,#-10.,#-10. ; var length ASCII files OPEN$W #RCVFDB ; and open the file BCS 20$ ; any errors? INCB RCVFIL ; mark file open MOV RBUF1,RBBEG ; use buffer 1 first MOV RBBEG,RPTR ; get pointer into receive buffer MOV RBBEG,RBEND ; calculate the end of the buffer ADD #BUFSIZ,RBEND ; RETURN ; ; error opening file -- say so and wait ; 10$: PRINT RETURN 20$: PRINT RETURN ; .SBTTL RECVCL - Close the receive file ; ; no problems, just requested to close the file ; RECVCL: CLOSE$ #RCVFDB ; close it CLRB RCVFIL ; mark it closed PRINT RETURN ; and go home .SBTTL STATUS - return our status to the user ; ; full file [suspended] or line-by-line ; STATUS: PRINT TSTB XFMODE ; -1 = full, suspended, 0 = full, 1 = line BGT 10$ ; line by line PRI < Full file transfer mode> TSTB XFMODE ; suspended? BEQ 15$ ; nope PRI < [suspended]> ; BR 15$ ; 10$: PRI < Line-by-line file transmission mode> 15$: CALL CRLF ; ; Delay times ; PRI < Delay times between lines/characters: > MOV #INBUF,R0 ; get a buffer for us to use MOV LINDEL,R1 ; get the delay time CLR R2 ; no leading zeros CALL $CBDMG ; convert to decimal MOVB #'/,(R0)+ ; put in a spacer MOV CHRDEL,R1 ; get the delay time CLR R2 ; no leading zeros CALL $CBDMG ; convert it to decimal also SUB #INBUF,R0 ; calculate the length MOV R0,PRNT+Q.IOPL+2 ; MOV #INBUF,PRNT+Q.IOPL ; DIR$ #PRNT ; and print it out CALL CRLF ; ; speed ; PRI < Line characteristics: > CMPB #S.110,FSPEED ; 110 baud? BNE 20$ ; PRI <110 baud, > BR 35$ ; 20$: CMPB #S.300,FSPEED ; 300 baud? BNE 25$ ; PRI <300 baud, > BR 35$ ; 25$: CMPB #S.1200,FSPEED ; 1200 baud? BNE 30$ ; PRI <1200 baud, > BR 35$ ; 30$: BGT 32$ ; better than 1200 baud? PRI BR 35$ ; 32$: PRI ; ; parity ; 35$: TSTB FPARTY ; check parity BNE 40$ ; > 0 ==> parity PRI BR 50$ ; 40$: CMPB #1,FPARTY ; odd? BNE 45$ ; PRI BR 50$ ; 45$: PRI 50$: CALL CRLF ; ; receive/transmit file ; TSTB RCVFIL ; 0 ==> no receive file BNE 55$ ; PRI < No receive file active> BR 60$ ; 55$: PRI < Receive file open> 60$: CALL CRLF TSTB XACTIV ; 0 ==> no transmit file BNE 62$ ; PRI < No transmit file active> BR 63$ ; 62$: PRI < Transmit file open> 63$: CALL CRLF RETURN ; and go wait for more commands ; .SBTTL CRLF - get to a new line ; ; subroutine to send out a CR and LF ; CRLF: MOV #CRLFA,PRNT+Q.IOPL ; put buffer in place MOV #2,PRNT+Q.IOPL+2 ; and its length DIR$ #PRNT ; and print it RETURN ; and continue on with whatever ; CRLFA: .ASCII .SBTTL FULLIN - full file or line-by-line transfer mode ; ; ask us which and set the flag ; FULLIN: READ MOVB INBUF,R0 ; get our answer BIC #177640,R0 ; bad lower case, parity MOVB #1,XFMODE ; assume line-by-line CMPB #'L,R0 ; Line-by-line? BEQ 10$ ; yup. CMPB #'F,R0 ; Full file? BNE FULLIN ; neither - try again NEGB XFMODE ; set full file, suspended 10$: RETURN ; and wait for more commands ; .SBTTL PARITY setting routine ; ; Ask about parity, and do as told ; PARITY: READ MOVB INBUF,R0 ; get our asnwer BIC #177640,R0 ; bag lower case, parity CLRB FPARTY ; assume no parity CLRB SPAR+1 ; CMPB #'N,R0 ; no parity? BEQ 10$ ; INCB FPARTY ; assume odd parity INCB SPAR+1 ; CLRB SEPA+1 ; CMPB #'O,R0 ; odd parity? BEQ 10$ ; INCB FPARTY ; assume even parity INCB SEPA+1 ; CMPB #'E,R0 ; even parity? BNE PARITY ; nope - try again 10$: MOV #SETPAR,QSMC+Q.IOPL ; set the commands up MOV #LSETPA,QSMC+Q.IOPL+2 ; and the length of the command DIR$ #QSMC ; and change the parity RETURN .SBTTL SPEED setting routine ; ; Ask for speed ans set it ; SPEED: READ MOVB INBUF,R0 ; get the answer BIC #177640,R0 ; bag lower case, parity MOV #S.110,R1 ; assume 110 baud CMPB #'A,R0 ; BEQ 10$ ; MOV #S.300,R1 ; assume 300 baud CMPB #'B,R0 ; BEQ 10$ ; MOV #S.1200,R1 ; assume 1200 baud CMPB #'C,R0 ; BNE SPEED ; none of the above - try again ; ; got the speed, now set it ; 10$: MOVB R1,FSPEED ; save the speed for our use MOVB R1,SETSPD+3 ; receive speed MOVB R1,SETSPD+5 ; transmit speed MOVB R1,SETSPD+7 ; answer speed MOV #SETSPD,QSMC+Q.IOPL ; put the command in place MOV #LSETSP,QSMC+Q.IOPL+2 ; and its length DIR$ #QSMC ; and set the speed RETURN ; .SBTTL DELAY time setting ; ; Ask for and store delay times ; DELAY: READ MOV #INBUF,R0 ; get the address of the buffer CALL $CDTB ; get the first number MOV R1,LINDEL ; set the line delay CALL $CDTB ; get the character delay MOV R1,CHRDEL ; and set it RETURN ; its that easy. now go home and wait. .SBTTL INIHAY - initialize Hayes and dial out ; ; tell us what's available, and get our answer ; INIHAY: MOV #35$,PRNT+Q.IOPL ; get our menu MOV #40$-35$,PRNT+Q.IOPL+2 ; and its length MOV #1,PRNT+Q.IOLU ; make sure we talking to the terminal DIR$ #PRNT ; and let us see it READ MOVB INBUF,R0 ; get our answer BIC #177640,R0 ; get rid of parity, lower case ; ; prepare to talk to the modem ; MOV #2,PRNT+Q.IOLU ; talk to the modem MOV #40$,PRNT+Q.IOPL ; get the address of our message MOV #50$-40$,PRNT+Q.IOPL+2 ; and its length ; ; start checking the options ; TST IOSB+2 ; anything typed? BEQ 30$ ; nope - leave CMPB #'I,R0 ; init only command? BNE 10$ ; nope - check others MOVB #CR,45$ ; terminate command to the Hayes DIR$ #PRNT ; this is all we wanted BR 30$ ; and leave 10$: CMPB #'D,R0 ; check for a dial command BNE 15$ ; not a dial - check the table READ MOV #INBUF,R0 ; R0 = buffer pointer MOV IOSB+2,R1 ; R1 = length BEQ 30$ ; if zero - no number entered - leave MOV R0,R2 ; get the base address ADD R1,R2 ; and point to the end MOVB #CR,(R2) ; end with a carriage return INC R1 ; and bump the length BR 25$ ; and go dial 15$: MOV #DIAL,R2 ; get the lookup table 20$: MOVB (R2)+,R3 ; get the character to match BNE 22$ ; if we couldn't find a match, try again JMP INIHAY ; and go around 22$: MOVB (R2)+,R1 ; the length MOV (R2)+,R4 ; and the pointer to the number CMP R0,R3 ; is this the requested character? BNE 20$ ; nope try another entry MOV R4,R0 ; get the pointer to the number 25$: MOVB #'D,45$ ; get a dial command for the Hayes DIR$ #PRNT ; MOV R0,PRNT+Q.IOPL ; put address of the number in place MOV R1,PRNT+Q.IOPL+2 ; and its length also DIR$ #PRNT ; dial the number 30$: MOV #1,PRNT+Q.IOLU ; reset the loon for our print RETURN ; and leave .SBTTL Phone numbers ; ; Menu for selecting the number to dial ; 35$: .ASCII @Remote - known phone numbers:@ .ASCII @ C Computer somewhere@ .ASCII @ D Dial any number@ .ASCII @ I Initialize only, return to modem command state@ .ASCII @ M MSU cyber computer@ 40$: .ASCII /AT E1 Q0 S2=1 S7=15/ ; echo cmd & rslt,esc=^A,15s no carrier 45$: .ASCII ; for init only, 'D' for dial 50$: .EVEN DIAL: .BYTE 'C,8. ; Crouch lab .BYTE char to match .WORD 10$ ; .BYTE length of number .BYTE 'M,6 ; MSU Cyber .WORD address of number .WORD 40$ ; .WORD 0 ; end of table 10$: .ASCII /1234567/ ; Crouch lab 40$: .ASCII /38500/ ; MSU Cyber (on campus number) .EVEN ; .SBTTL KILHAY - kill Hayes modem and exit ; ; get the modem's attention and reset it ; KILHAY: MOV #2,PRNT+Q.IOLU ; set up to talk to the modem MRKT$S #6,#2,#2 ; wait for 2 seconds (lead in time) WTSE$S #6 ; and really wait MOV #2,PRNT+Q.IOLU ; set up to talk to the modem MOV #10$,PRNT+Q.IOPL ; get our message (three ^A's) MOV #3,PRNT+Q.IOPL+2 ; and length of the message DIR$ #PRNT ; and get the modem's attention MRKT$S #6,#2,#2 ; wait for 2 seconds (lead out time) WTSE$S #6 ; and really wait MOV #20$,PRNT+Q.IOPL ; get our message (hang up and reset) MOV #30$-20$,PRNT+Q.IOPL+2 ; and length of the message DIR$ #PRNT ; hang up and reset MRKT$S #6,#2,#2 ; wait for 2 seconds before detatching WTSE$S #6 ; and really wait JMP EXIT ; and clean things up ; 10$: .BYTE 1,1,1 ; escape code as set up earlier 20$: .ASCII /ATH0Z/ ; hang up and reset 30$: .EVEN .SBTTL HELP - the user needs help ; ; just print our message ; HELP: CLRB SETBIN+1 ; first, turn off RPA DIR$ #QBIN ; no binary mode until after write QIOW$S #IO.WVB,#1,#1,,#IOSB,,<#10$,#20$-10$,#0> INCB SETBIN+1 ; set to pass all mode again DIR$ #QBIN ; RETURN ; all done - go and wait ; 10$: .ASCII @Remote - valid commands:@ .ASCII @ ^A command prefix character@ .ASCII @ ^B send a line or suspend/resume of transmission@ .ASCII @ A Abort the file transmission@ .ASCII @ B Baud rate: 110/300/1200@ .ASCII @ D Delay time for characters and lines@ .ASCII @ E Exit@ .ASCII @ F Full file or Line-by-line transfer@ .ASCII @ H Help (this text)@ .ASCII @ I Initialize Hayes modem and dial out@ .ASCII @ K Kill Hayes modem and exit@ .ASCII @ P Parity: none/even/odd@ .ASCII @ R Receive a file@ .ASCII @ S Status of open files, flags, etc@ .ASCII @ T Transmit a file@ .ASCII @ Z cloZe the receive file@ 20$: .EVEN .SBTTL EXIT - requested to leave - lets go away ; ; bag outstanding I/O, release things, unit 2 first ; EXIT: MOV #2,PRNT+Q.IOLU ; specify loon 2 EXLOOP: MOV #IO.KIL,PRNT+Q.IOFN ; get a kill directive DIR$ #PRNT ; and do it MOV #RSETMC,QSMC+Q.IOPL ; get the reset codes MOV #LRSETM,QSMC+Q.IOPL+2 ; and their lengths DIR$ #QSMC ; and reset the line characteristics MOV #IO.DET,PRNT+Q.IOFN ; get the detach function, and hold it ; ; the above is identical for units 1 and 2, the specific stuff is here ; CMP #1,PRNT+Q.IOLU ; have we done unit 1 already? BEQ CLFILE ; yup - go detach, then do the files ; ; unit 2 also needs to have other things reset ; ; reset modem itself MOV #OLDCHR,QSMC+Q.IOPL ; set the old line characteristics MOV #LOLDCH,QSMC+Q.IOPL+2 ; length of the characteristics DIR$ #QSMC ; speed, remote, parity reset DIR$ #PRNT ; and detatch it EXIT1: MOV #2,PRNT+Q.IOLU ; specify loon 2 MOV #IO.KIL,PRNT+Q.IOFN ; get a kill directive DIR$ #PRNT ; and do it (maybe second time) MOV #1,PRNT+Q.IOLU ; now set up for loon 1 MOV #1,QSMC+Q.IOLU ; BR EXLOOP ; and release this loon also ; ; if any files open, close them ; CLFILE: DIR$ #PRNT ; and detatch unit 1 TSTB RCVFIL ; receive file open? BEQ 10$ ; nope ... CLOSE$ #RCVFDB ; close it 10$: TSTB XACTIV ; transmit file open? BEQ 20$ ; nope ... CLOSE$ #XMTFDB ; and close it 20$: EXIT$S ; and die .SBTTL AST1 - Asynchronous trap handler, unit 1 (TI:) ; ; ast service routine for terminal 1 ; AST1: MOV R0,-(SP) ; save our working register MOVB 2(SP),R0 ; get character recieved BIC #177600,R0 ; bag any parity bits TSTB PCMD ; do we have a prefix from before? BEQ 20$ ; no previous prefix - check character ; ; we have a previous prefix character - either ^A, ^B or a command ; CMPB #3,R0 ; ^A or ^B? BHI 40$ ; yup - send it out 10$: MOVB R0,XCMD ; put command where we'll see it SETF$S #3 ; flag us BR 50$ ; and leave, clearing the prefix ; ; no previous prefix. maybe ^A, ^B or just something to ship out ; 20$: CMPB #1,R0 ; ^A? BNE 30$ ; not ^A, check other possibilities INCB PCMD ; set the prefix flag BR 60$ ; and head home 30$: CMPB #2,R0 ; ^B? BEQ 10$ ; yup - give us the command ; ; normal pass characters thru to other port ; 40$: MOVB R0,@B1PTR ; put character in our buffer MOV B1PTR,AQ1+Q.IOPL ; get address for output request DIR$ #AQ1 ; queue the request INC B1PTR ; maintain circular buffer CMP B1PTR,#BUF1+CIRSIZ ; past end of buffer? BMI 50$ ; not yet ... MOV #BUF1,B1PTR ; past end - make a circle 50$: CLRB PCMD ; clear the prefix command 60$: MOV (SP)+,R0 ; restore our register ADD #2,SP ; get our character off the stack ASTX$S ; return from AST service RETURN ; just to be sure ... ; .SBTTL AST2 - Asynchronous trap handler for unit 2 (modem) ; ; ast handler for 2nd terminal - tell us we have input waiting ; AST2: SETF$S #10 ; set our event flag ADD #2,SP ; pop the term # off the stack ASTX$S ; return from ast service RETURN ; just to be sure ... .SBTTL MODIN - get modem input, and send to terminal, file ; ; we've got modem input - figure how much, but first delay a bit ; MODIN: TSTB FSPEED ; check for unknown speed (DL line) BEQ 3$ ; no known speed - go fast CMPB #S.1200,FSPEED ; check output baud rate BLO 3$ ; better than 1200 baud, go fast BHI 2$ ; less than 1200, go real slow MRKT$S #11,#1,#1 ; wait a tic for 1200 baud (~2 chars) BR 4$ ; go wait for more input 2$: MRKT$S #11,#4,#1 ; wait 4 tics for less, (@300, ~2 chars) 4$: WTSE$S #11 ; and really wait 3$: DIR$ #SIZBUF ; get the number of characters in buffer MOVB TYPAHD+1,R5 ; this is the size (where RMD can see) BIC #177400,R5 ; make sure we've less than 255 chars CMP R4,R5 ; compare sizes of current/previous BGE 1$ ; current (R5) >= last (R4) MOV R5,R4 ; show us the max count (RMD can see) 1$: MOV B2PTR,RMODEM+Q.IOPL ; get our buffer address MOV R5,RMODEM+Q.IOPL+2 ; and length in the read request DIR$ #RMODEM ; and read the input character(s) ; ; Ok - characters are now in - send them out ; MOV B2PTR,AQ2+Q.IOPL ; now get output buffer address MOV R5,AQ2+Q.IOPL+2 ; and length DIR$ #AQ2 ; and queue up the output request TSTB RCVFIL ; file open to receive data? BEQ 10$ ; nope - continue on CALL RCVCRS ; yes - put characters into file 10$: ADD R5,B2PTR ; update our pointer CMP B2PTR,#BUF2+CIRSIZ ; past end of the buffer? BMI 20$ ; nope - continue on MOV #BUF2,B2PTR ; yup - make a circle of the buffer 20$: RETURN ; all done - leave .SBTTL RCVCRS - file received characters ; ; a string of (R5) characters pointed to by (B2PTR) must be filed ; RCVCRS: MOV R5,R1 ; get a copy of our counter MOV B2PTR,R2 ; and the pointer to the characters 10$: MOVB (R2)+,R0 ; get a character into R0 ; CMPB #CR,R0 ; CR? BEQ 20$ ; yes - put buffer CMPB #LF,R0 ; LF? BEQ 55$ ; ignore LF - more characters? MOVB R0,@RPTR ; stuff char into buffer INC RPTR ; maintain pointer CMP RPTR,RBEND ; check for buffer full BLE 55$ ; not full - continue ; ; either buffer full or CR received ; 20$: MOV RPTR,R0 ; compute record length SUB RBBEG,R0 ; BLT 50$ ; negative chars would be an error MOV R0,RCVFDB+F.NRBD ; set length MOV RBBEG,RCVFDB+F.NRBD+2 ; and the start address CMP RBUF1,RBBEG ; are we using the first buffer? BEQ 30$ ; yup - switch to the second MOV #RBUF1,RBBEG ; get the address of the first buffer BR 40$ ; 30$: MOV #RBUF2,RBBEG ; get the address of the second buffer 40$: MOV RBBEG,RBEND ; calculate the end of the buffer ADD #BUFSIZ,RBEND ; SETF$S #5 ; tell us to write it out 50$: MOV RBBEG,RPTR ; set up for new buffer ; ; any more characters to file? ; 55$: SOB R1,10$ ; yup - more. Go get them. RETURN ; no more - finished here .END REMOTE