lamb@lids.mit.edu.UUCP (04/16/87)
----------------PART 2 of 2 --------------------- Some time ago I wrote a "from-scratch" uucp clone for use on our DG AOS machine (with its brain-damaged uucp, useless to us and DG being no help). Later I ported it to MS-DOS and VAX/VMS and its been working fine for a year (Ive also hacked up a "rmail" for use between machines.) Recently, with the help of John Gilmore, I was able to get the "g" protocol realy workin (window size > 1). Previously all I could figure out was a degenerate "g" proto using 1-window. Im not crazy enough to say its perfect, but it does work. Although this is a two-way (master+slave) uucico clone, compared to John's uuslave, the coding and portibility has much to be desired. (This project started as just an internal attempt to connect our DG to the world). In any case for what its worth, Here it is........... Hope everyone finds it useful. As were not a real unix site, Id appreciate all correspondence thru mail and not NEWS. (It took me most of last week to drum up a way to post stuf to USENET writting a NNTP program). Rick Lamb Lamb@lids.mit.edu or ...ihnp4!mit-eddie!lids!lamb -------------CUT here ---------------------------------------- echo readme cat >readme <<'@@@ Finished readme' -------Notes on dcp.------------------- ---R.H.Lamb 6/86---------------------- Fix dcps.h for right system Fix rmail.c for local mailer or no (#define LCLMAIL) For PC compile comm.asm & dcpasm.asm files Compile the rest of the .c files Link dcp+ .c except rmail.c files + (comm+dcpasm for PC) Link rmail **************Finished creation************** Modify "systems" for your site Modify "userfile" for your site **************Ready to run ****************** To que up mail in current directory type rmail mit-eddie!lids!lamb <CR> or <NL> Enter your message here ending with a line with only a "." (Note: rmail does not presently put things in RFC820 format. A "filter" program piped into rmail could do this easily. With out this format things work ok, but youll see "Apparently to" s in your mail.) To read incomming mail type rmail <CR> or <NL> followed by <CR> or <NL> for each message To send/receive files type dcp master 3 <CR> Where the last number is debug level (0 is least) **** Prog descriptions ***** dcp.c "uucico" dcpsys.c Top level state machine dcpstart.c uucp startup protocol+ dialing algorithm dcpscan.c directory scan for work dcpsend.c send file proto dcprec.c rec file proto dcpio.c general purpose serial I/O interface dcppkt.c,dcpggpkt.c "g" proto Windowsize=1 (Previously tested)) dcpgpkt.c "g" proto Var windowsize (In use) dcptpkt.c "t" proto no-error chk: for virt links dcprpkt.c "r" proto My sliding window proto dcpvms.c VMS support routines dcxqt.c "uuxqt" rmail.c "rmail" comm.asm PC serial drivers (Saltzers pkg) dcpasm.asm PC directory and other asm routines systems "L.sys" userfile site info readme this file format.doc info on msg formats (RFC8??) to be passed to rmail makeobj.* make object files for PC(.bat) DG(.cli) makeexe.* make dcp.exe or dcp.pr for PC or DG call.* execute dcp in master mode spawn.c VMS spawn testing subroutine (Not needed for oper) ------notes----- PC info: MSC 3.0, LINK 3.01, MASM 4.00 DG info: C 3.21, LINK 6.00 VMS info: C 2.0, LINK ? --------VMS notes-------- To send mail in VMS note: rmail "a!b!c" message (Those dammed "."'s screwed up my posting on the first run >.<) To send a file in VMS using redirected I/O (example "ss") ss "rmail ""a!b!c" file nl: In VMS host names with "_,-,?,&" are no good. Make sure who you talk to has a simple name like "dgvax" "userfile" should have TT: for local console 0 for speed (use default speed on logon) BUGS!!!! The spawn command in dcxqt (lib$spawn in dcpvms) seems to work only for small files. Redirection of input file to "rmail" larger than a certain size get lost. Cause is differing record file formats. Presently its a kludge that runs "CONVERT/FDL=VAR" on the input files. Please- an VMS C hacker FIX. @@@ Finished readme echo format.doc cat >format.doc <<'@@@ Finished format.doc' -------A sample message that was received----- -------/** denotes stuff my pgm added, the rest (**/) is what a--- ------d.file should look like before sending--------- ---The message was sent using: % mail ggt@rutgers.UUCP Subject: jeez Hello there (dammed dots >.<) EOF ------------------------------------------------------ ---------after recipt--------------- From uucp Tue Mar 10 16:36:03 1987 EDT remote from rutgers >From tae Sat Mar 14 12:53:36 1987 remote from dspvax Received: by dspvax.MIT.EDU (5.51/MIT.1.01x) on Sat, 14 Mar 87 12:53:36 EST; User rutgers!ggt; Host rutgers Date: Sat, 14 Mar 87 12:53:36 EST From: Tae Joo <dspvax!tae> Message-Id: <8703141753.AA02983@dspvax.MIT.EDU> To: rutgers!ggt Subject: jeez Hello there ---------before send--------------- *****The Drutg115 file From tae Sat Mar 14 12:53:36 1987 remote from dspvax Received: by dspvax.MIT.EDU (5.51/MIT.1.01x) on Sat, 14 Mar 87 12:53:36 EST; User rutgers!ggt; Host rutgers Date: Sat, 14 Mar 87 12:53:36 EST From: Tae Joo <dspvax!tae> Message-Id: <8703141753.AA02983@dspvax.MIT.EDU> To: rutgers!ggt Subject: jeez Hello there ******The Brutg115 file -----note: for forwarding the last line is C rmail ggt!seismo!lamb -----U=user I=input C=command F=general file U uucp rutgers F D.rutgers115 I D.rutgers115 C rmail ggt ******The Crutg115 file ----S=send R=receive(not common) S Drutg115 D.rutgers115 uucp - Drutg115 0666 S Brutg115 X.rutgers115 uucp - Brutg115 0666 ----------------end of document---------------- @@@ Finished format.doc echo mboxuucp cat >mboxuucp <<'@@@ Finished mboxuucp' From uucp Sun Mar 15 22:18:54 1987 EDT remote from lids Received: by lids on Sun Mar 15 22:18:54 1987 EDT >From uucp Tue Apr 7 20:28:41 1987 remote from mit-eddie Received: by EDDIE.MIT.EDU with UUCP with smail2.3 with sendmail-5.31/4.7 id <AA05017@EDDIE.MIT.EDU>; Tue, 7 Apr 87 20:28:30 EDT Received: by lids on Sun Mar 15 22:14:42 1987 EDT Date: 7 Apr 87 19:28:17 EST (Tue) From: uucp@EDDIE.MIT.EDU Message-Id: <8704071928.AA05012@EDDIE.MIT.EDU> To: lids!lamb Hello there. From uucp Sun Mar 15 22:51:39 1987 EDT remote from lids Received: by lids on Sun Mar 15 22:51:39 1987 EDT >From uucp Tue Apr 7 20:55:47 1987 remote from mit-eddie Received: by EDDIE.MIT.EDU with UUCP with smail2.3 with sendmail-5.31/4.7 id <AA05699@EDDIE.MIT.EDU>; Tue, 7 Apr 87 20:55:33 EDT Received: by lids on Sun Mar 15 22:36:58 1987 EDT Date: 7 Apr 87 19:55:25 EST (Tue) From: uucp@EDDIE.MIT.EDU Message-Id: <8704071955.AA05697@EDDIE.MIT.EDU> To: lids!uucp >From uucp Tue Apr 7 20:45:40 1987 remote from mit-eddie Received: by EDDIE.MIT.EDU with UUCP with smail2.3 with sendmail-5.31/4.7 id <AA05435@EDDIE.MIT.EDU>; Tue, 7 Apr 87 20:44:59 EDT Received: by lids on Sun Mar 15 22:26:58 1987 EDT Received: by rutgers.UUCP (5.51/UUCP.1.01x) on Sat, 14 Mar 87 12:53:36 EST; Date: Sat, 14 Mar 87 12:53:36 EST From: UUCP <rutgers!uucp@EDDIE.MIT.EDU> Subject: jeez Message-Id: <8704071944.AA05432@EDDIE.MIT.EDU> To: lids!mit-eddie!lids!uucp Hello there #include "dcp.h" #include "dcp0.h" /*<FF>*/ /* usage dcp (master/slave(D)) (debug level 0=none(D)) (system name(MYNAME))*/ /* (console device) */ main(argc,argv) int argc; char *argv[]; { int ftmp; char line[132]; flog=creat(SYSLOG,0775); close(flog); flog=open(SYSLOG,2); remote=TRUE; debug=3; fp = -1; fw = -1; ftmp=open(USERFILE,0); getline(ftmp,line); sscanf(line,"%s %s %s",myname,username); getline(ftmp,line); sscanf(line,"%s %s",tty,speed); strcpy(spooldir,"."); /* default spooldir is current dir */ close(ftmp); if(argc > 1 && strcmp(argv[1],"master") == 0) remote=FALSE; if(argc > 2) sscanf(argv[2],"%d",&debug); if(argc > 3) strcpy(myname,argv[3]); if(argc > 4) strcpy(tty,argv[4]); if(! remote) { if((fsys=open(SYSTEMS,0)) ==-1) return(FALSE); state='I'; while(TRUE) { if(debug>0) printmsg("Mstate = %c",state); switch(state) { case 'I': state=getsystem(); break; case 'S': state=callup(); break; case 'P': state=startup(); break; case 'D': state=master(); break; case 'Y': state=sysend(); break; case 'G': state='I'; break; } if(state=='A') break; } close(fsys); } else { if(openline(tty,speed)==-1) return(FALSE); state='L'; while(TRUE) { if(debug>0) printmsg("Sstate = %c",state); switch(state) { case 'L':state=login(); break; case 'I':state=startup(); break; case 'R':state=slave(); break; case 'Y':state=sysend(); break; } if(state=='A') break; } closeline(); } if(dcxqt()) if(debug) printmsg("ERROR in DCXQT"); /* scan and process any recieved files */ close(flog); } /*<FF>*/ /* ** ** **master ** ** */ master() { state='I'; while(TRUE) { if(debug>1) printmsg("Top level state (master mode) %c",state); switch(state) { case 'I':state=sinit(); break; case 'B':state=scandir(); break; case 'S':state=send(); break; case 'Q':state=sbreak(); break; case 'G':state=receive(); break; case 'C':state='Y'; break; case 'Y':state=endp(); break; case 'P':return('Y'); case 'A':return('A'); default:return('A'); } } } /*<FF>*/ /* ** ** **slave ** ** */ slave() { state='I'; while(TRUE) { if(debug>1) printmsg("Top level state (slave mode) %c",state); switch(state) { case 'I':state=rinit(); break; case 'F':state=receive(); break; case 'C':state=schkdir(); break; case 'T':state='B'; break; case 'B':state=scandir(); break; case 'S':state=send(); break; case 'Q':state=sbreak(); break; case 'G':return('Y'); case 'Y':state=endp(); break; case 'P':return('Y'); case 'A':return('A'); default:return('A'); } } } /*<FF>*/ /* * r e c e i v e * * This is the state table switcher for receiving files. */ receive() { state = 'F';/* Receive-Init is the start state */ while(TRUE) { if (debug > 2) printmsg(" receive state: %c",state); switch(state)/* Do until done */ { case 'F':state = rfile(); break; /* Receive-File */ case 'D':state = rdata(); break; /* Receive-Data */ case 'C':return('C');/* Complete state */ case 'A':return('Y');/* "Abort" state */ default:return('Y'); } } } /*<FF>*/ /* * s e n d * * Sendsw is the state table switcher for sending files. It loops until * either it finishes, or an error is encountered. The routines called * by sendsw are responsible for changing the state. * */ send() { fp = -1; /* reset file getter/opener */ state = 'F';/* Send initiate is the start state */ while(TRUE)/* Do this as long as necessary */ { if (debug > 2) printmsg("send state: %c",state); switch(state) { case 'F':state = sfile(); break; /* Send-File */ case 'D':state = sdata(); break; /* Send-Data */ case 'Z':state = seof(); break; /* Send-End-of-File */ case 'B':return ('B'); /* Complete */ case 'A':return ('Y'); /* "Abort" */ default:return ('Y'); /* Unknown, fail */ } } } /*<FF>*/ /* * * login (for slave in PC mode) * */ login() { char logmsg[132]; #ifdef PC msgtime= 2*MSGTIME; lretry: wmsg("Username:",0); rmsg(logmsg,0); if(debug > 0) printmsg("Username = %s",logmsg); wmsg("Password:",0); rmsg(logmsg,0); if(debug > 0) printmsg("Password = %s",logmsg); if(strcmp(logmsg,"uucp") != 0) goto lretry; #endif return('I'); } From uucp Mon Mar 16 03:20:48 1987 EDT remote from lids Received: by lids on Mon Mar 16 03:20:48 1987 EDT >From uucp Tue Apr 7 21:03:05 1987 remote from mit-eddie Received: by EDDIE.MIT.EDU with UUCP with smail2.3 with sendmail-5.31/4.7 id <AA05879@EDDIE.MIT.EDU>; Tue, 7 Apr 87 21:02:42 EDT Received: by lids on Sun Mar 15 22:43:44 1987 EDT Date: 7 Apr 87 20:02:39 EST (Tue) From: uucp@EDDIE.MIT.EDU Message-Id: <8704072002.AA05877@EDDIE.MIT.EDU> To: lids!uucp >From uucp Tue Apr 7 20:49:16 1987 remote from mit-eddie Received: by EDDIE.MIT.EDU with UUCP with smail2.3 with sendmail-5.31/4.7 id <AA05507@EDDIE.MIT.EDU>; Tue, 7 Apr 87 20:48:44 EDT Received: by lids on Sun Mar 15 22:34:18 1987 EDT Received: by rutgers.UUCP (5.51/UUCP.1.01x) on Sat, 14 Mar 87 12:53:36 EST; Date: Sat, 14 Mar 87 12:53:36 EST From: UUCP <rutgers!uucp@EDDIE.MIT.EDU> Subject: jeez Message-Id: <8704071948.AA05505@EDDIE.MIT.EDU> To: lids!mit-eddie!lids!uucp Hello there #include "dcp.h" #include "dcp0.h" /*<FF>*/ /* usage dcp (master/slave(D)) (debug level 0=none(D)) (system name(MYNAME))*/ /* (console device) */ main(argc,argv) int argc; char *argv[]; { int ftmp; char line[132]; flog=creat(SYSLOG,0775); close(flog); flog=open(SYSLOG,2); remote=TRUE; debug=3; fp = -1; fw = -1; ftmp=open(USERFILE,0); getline(ftmp,line); sscanf(line,"%s %s %s",myname,username); getline(ftmp,line); sscanf(line,"%s %s",tty,speed); strcpy(spooldir,"."); /* default spooldir is current dir */ close(ftmp); if(argc > 1 && strcmp(argv[1],"master") == 0) remote=FALSE; if(argc > 2) sscanf(argv[2],"%d",&debug); if(argc > 3) strcpy(myname,argv[3]); if(argc > 4) strcpy(tty,argv[4]); if(! remote) { if((fsys=open(SYSTEMS,0)) ==-1) return(FALSE); state='I'; while(TRUE) { if(debug>0) printmsg("Mstate = %c",state); switch(state) { case 'I': state=getsystem(); break; case 'S': state=callup(); break; case 'P': state=startup(); break; case 'D': state=master(); break; case 'Y': state=sysend(); break; case 'G': state='I'; break; } if(state=='A') break; } close(fsys); } else { if(openline(tty,speed)==-1) return(FALSE); state='L'; while(TRUE) { if(debug>0) printmsg("Sstate = %c",state); switch(state) { case 'L':state=login(); break; case 'I':state=startup(); break; case 'R':state=slave(); break; case 'Y':state=sysend(); break; } if(state=='A') break; } closeline(); } if(dcxqt()) if(debug) printmsg("ERROR in DCXQT"); /* scan and process any recieved files */ close(flog); } /*<FF>*/ /* ** ** **master ** ** */ master() { state='I'; while(TRUE) { if(debug>1) printmsg("Top level state (master mode) %c",state); switch(state) { case 'I':state=sinit(); break; case 'B':state=scandir(); break; case 'S':state=send(); break; case 'Q':state=sbreak(); break; case 'G':state=receive(); break; case 'C':state='Y'; break; case 'Y':state=endp(); break; case 'P':return('Y'); case 'A':return('A'); default:return('A'); } } } /*<FF>*/ /* ** ** **slave ** ** */ slave() { state='I'; while(TRUE) { if(debug>1) printmsg("Top level state (slave mode) %c",state); switch(state) { case 'I':state=rinit(); break; case 'F':state=receive(); break; case 'C':state=schkdir(); break; case 'T':state='B'; break; case 'B':state=scandir(); break; case 'S':state=send(); break; case 'Q':state=sbreak(); break; case 'G':return('Y'); case 'Y':state=endp(); break; case 'P':return('Y'); case 'A':return('A'); default:return('A'); } } } /*<FF>*/ /* * r e c e i v e * * This is the state table switcher for receiving files. */ receive() { state = 'F';/* Receive-Init is the start state */ while(TRUE) { if (debug > 2) printmsg(" receive state: %c",state); switch(state)/* Do until done */ { case 'F':state = rfile(); break; /* Receive-File */ case 'D':state = rdata(); break; /* Receive-Data */ case 'C':return('C');/* Complete state */ case 'A':return('Y');/* "Abort" state */ default:return('Y'); } } } /*<FF>*/ /* * s e n d * * Sendsw is the state table switcher for sending files. It loops until * either it finishes, or an error is encountered. The routines called * by sendsw are responsible for changing the state. * */ send() { fp = -1; /* reset file getter/opener */ state = 'F';/* Send initiate is the start state */ while(TRUE)/* Do this as long as necessary */ { if (debug > 2) printmsg("send state: %c",state); switch(state) { case 'F':state = sfile(); break; /* Send-File */ case 'D':state = sdata(); break; /* Send-Data */ case 'Z':state = seof(); break; /* Send-End-of-File */ case 'B':return ('B'); /* Complete */ case 'A':return ('Y'); /* "Abort" */ default:return ('Y'); /* Unknown, fail */ } } } /*<FF>*/ /* * * login (for slave in PC mode) * */ login() { char logmsg[132]; #ifdef PC msgtime= 2*MSGTIME; lretry: wmsg("Username:",0); rmsg(logmsg,0); if(debug > 0) printmsg("Username = %s",logmsg); wmsg("Password:",0); rmsg(logmsg,0); if(debug > 0) printmsg("Password = %s",logmsg); if(strcmp(logmsg,"uucp") != 0) goto lretry; #endif return('I'); } @@@ Finished mboxuucp echo systems cat >systems <<'@@@ Finished systems' ihnp4 Slave lids Slave COM1 DIR 9600 g \n-me:-op\n-word:-dogbridge\ntest\n dlids Slave COM1 DIR 9600 g \n-me:-tuucp\n-word:-ggggg\n mit-eddie Any COM2 ACU 1200 g ATZ\r-OK\r-ATDT88222\r-CONNECT\r-\r-ogin:-uucp\r-assword:-uucp\r hurob1 Slave lids Slave COM2 ACU 1200 g ATZ\r-K\r-ATDT87444\r-CT\r-\n-ame:-op\n-ord:-dogbridge\ntest\n lids Slave COM2 ACU 1200 g ATZ\r-K\r-ATDT87444\r-CT\r-\n-ame:-uucp\n-ord:-uucp\n seismo Slave orly Slave rutgers Slave speedy Slave dspvax Any COM2 ACU 1200 g ATZ\r-K\r-ATDT86666\r-CT\r-\r-ogin:-uucp\r-ord:-what\r mit-atrp Slave COM2 ACU 1200 g ATZ\r-K\r-ATDT87922\r-CT\r-\r-ogin:-uucp\r-ord:-duck\r allegra Slave COM2 ACU 1200 g ATZ\r-K\r-ATDT9,15551212392\r-CT\r-\r-ogin:-uucp\r-ord:-attttt\r @@@ Finished systems echo userfile cat >userfile <<'@@@ Finished userfile' lids uucp COM1 9600 (arghhhhhh >.< want a dot here!!!) /* prototype */ machine-name user-name default-console-for-call-in's default-baud default-spool-directory /************/ @@@ Finished userfile echo comm.h cat >comm.h <<'@@@ Finished comm.h' /* declarations for comm.asm ** ** compilation must use the Ze switch to enable the ** "far" keyword for the small memory model ** ** Robin Rohlicek 3/86 */ void far select_port( int ); /* select active port (1 or 2) */ void far save_com(); /* save the interupt vectors */ void far restore_com(); /* restore those vectors */ int far install_com(); /* install our vectors */ void far open_com( /* open com port */ int, /* baud */ int, /* 'M'odem or 'D'irect */ int, /* Parity 'N'one, 'O'dd, 'E'ven, 'S'pace, 'M'ark */ int, /* stop bits (1 or 2) */ int); /* Xon/Xoff 'E'nable, 'D'isable */ void far close_com(); /* close com port */ void far dtr_off(); /* clear DTR */ void far dtr_on(); /* set DTR */ long far r_count(); /* receive counts */ /* high word = total size of receive buffer */ /* low word = number of pending chars */ #define r_count_size() ( (int) (r_count()>>16) ) #define r_count_pending() ( (int) r_count() ) int far receive_com(); /* get one character */ /* return -1 if none available */ long far s_count(); /* send counts */ /* high word = total size of transmit buffer */ /* low word = number of bytes free in transmit buffer */ #define s_count_size() ( (int) (s_count()>>16) ) #define s_count_free() ( (int) s_count() ) void far send_com(int); /* send a character */ void far send_local(int); /* simulate receive of char */ void far sendi_com(int); /* send immediately */ void far break_com(); /* send a BREAK */ int * far com_errors(); /* pointer to error counts (in static area) */ #define COM_EOVFLOW 0 /* buffer overflows */ #define COM_EOVRUN 1 /* receive overruns */ #define COM_EBREAK 2 /* break chars */ #define COM_EFRAME 3 /* framing errors */ #define COM_EPARITY 4 /* parity errors */ #define COM_EXMIT 5 /* transmit erros */ #define COM_EDSR 6 /* data set ready errors */ #define COM_ECTS 7 /* clear to send errors */ #define COM_NERR 8 /* number of errors */ @@@ Finished comm.h echo dcp.h cat >dcp.h <<'@@@ Finished dcp.h' /* DCP a uucp clone. Copyright Richard H. Lamb 1985,1986,1987 */ #include <stdio.h> /* Standard UNIX definitions */ #include "dcps.h" #define MSGTIME 20 #define MAXPACK 256 #define ACK 4 /* general definitions */ #define NAK 2 #define DATA 0 #define CLOSE 1 #define ERROR 10 #define EMPTY 11 #define TRUE -1 #define FALSE 0 /*<FF>*/ typedef int (*procref)(); typedef struct { char type; procref a; procref b; procref c; procref d; } Proto; /* the various protocols available. Add here for others */ extern procref getpkt,sendpkt,openpk,closepk; extern int ggetpkt(),gsendpkt(),gopenpk(),gclosepk(); extern int kgetpkt(),ksendpkt(),kopenpk(),kclosepk(); extern int rgetpkt(),rsendpkt(),ropenpk(),rclosepk(); extern int tgetpkt(),tsendpkt(),topenpk(),tclosepk(); /*<FF>*/ extern int pktsize; /* packet size for this pro*/ extern int flog; /* system log file */ extern int fw; /* cfile pointer */ extern int fpr,fpw; /* comm dev pointer */ extern char cfile[80]; /* work file pointer */ extern int remote; /* -1 means we're remote*/ extern int debug; /* debugging level */ extern int msgtime; /* timout setting */ extern char fromfile[132]; extern char tofile[132]; extern char state; /* present state */ extern int fp; /* current disk file ptr */ extern int size; /* nbytes in buff */ extern int fsys; extern char tty[20]; extern char myname[20]; extern char username[20]; extern char spooldir[80]; extern char rmtname[20]; extern char cctime[20]; extern char device[20]; extern char type[10]; extern char speed[10]; extern char proto[5]; extern char loginseq[132]; extern unsigned int checksum(); @@@ Finished dcp.h echo dcps.h cat >dcps.h <<'@@@ Finished dcps.h' /* DCP a uucp clone. Copyright Richard H. Lamb 1985,1986,1987 */ /** select system */ #define PC /* */ /*#define DG /* */ /*#define VMS /* */ #define USERFILE "userfile" #define SYSTEMS "systems" #define SYSLOG "dcp.log" @@@ Finished dcps.h echo comm.asm cat >comm.asm <<'@@@ Finished comm.asm' TITLE COM_PKG2 -- Last updated 10/6/85 PAGE 75,132 ; ; modified to use MSC calling sequence. ; jrr 3/86 ; ; Communications Package for the IBM PC, XT, AT and PCjr ; and strict compatibles. ; May be copied and used freely -- This is a public domain program ; Developed by Richard Gillmann, John Romkey, Jerry Saltzer, ; Craig Milo Rogers, Dave Mitton and Larry Afrin. ; ; We'd sure like to see any improvements you might make. ; Please send all comments and queries about this package ; to GILLMANN@USC-ISIB.ARPA ; ; o Supports both serial ports simultaneously ; o All speeds to 19200 baud ; o Compatible with PC, XT, AT and PCjr. ; o Built in XON/XOFF flow control option ; o Assembly language calling conventions ; o Logs all comm errors ; o Direct connect or modem protocol ; ; MAXIMUM BUFFER SIZES R_SIZE EQU 500 ; SIZE OF RECEIVE BUFFERS S_SIZE EQU 500 ; SIZE OF TRANSMIT BUFFERS rhl/4/86 ; INTERRUPT NUMBERS INT_COM1 EQU 0CH ; COM1: FROM 8259 INT_COM2 EQU 0BH ; COM2: FROM 8259 ; 8259 PORTS INTA00 EQU 20H ; 8259A PORT, A0 = 0 INTA01 EQU 21H ; 8259A PORT, A0 = 1 ; COM1: LEVEL 4 IRQ4 EQU 2*2*2*2 ; 8259A OCW1 MASK, M4=1, A0=0 NIRQ4 EQU NOT IRQ4 AND 0FFH ; COMPLEMENT OF ABOVE EOI4 EQU 4 OR 01100000B ; 8259A OCW2 SPECIFIC IRQ4 EOI, A0=0 ; COM2: LEVEL 3 IRQ3 EQU 2*2*2 ; 8259A OCW1 MASK, M3=1, A0=0 NIRQ3 EQU NOT IRQ3 AND 0FFH ; COMPLEMENT OF ABOVE EOI3 EQU 3 OR 01100000B ; 8259A OCW2 SPECIFIC IRQ3 EOI, A0=0 ; FLOW CONTROL CHARACTERS CONTROL_Q EQU 11H ; XON CONTROL_S EQU 13H ; XOFF ; MISC. DOS EQU 21H ; DOS FUNCTION CALLS PAGE ; ; ROM BIOS Data Area ; RBDA SEGMENT AT 40H RS232_BASE DW 4 DUP(?) ; ADDRESSES OF RS232 ADAPTERS RBDA ENDS ; ; ROM PC-Type IDENT ; ROM SEGMENT AT 0F000H ORG 0FFFEH ROMID DB ? ; 0FFH=PC OR EARLY XT, 0FEH=XT, 0FDH=JR ROM ENDS PAGE ; ; TABLE FOR EACH SERIAL PORT ; SP_TAB STRUC PORT DB ? ; 1 OR 2 ; PARAMETERS FOR THIS INTERRUPT LEVEL INT_COM DB ? ; INTERRUPT NUMBER IRQ DB ? ; 8259A OCW1 MASK NIRQ DB ? ; COMPLEMENT OF ABOVE EOI DB ? ; 8259A OCW2 SPECIFIC END OF INTERRUPT ; INTERRUPT HANDLERS FOR THIS LEVEL INT_HNDLR DW ? ; OFFSET TO INTERRUPT HANDLER OLD_COM_OFF DW ? ; OLD HANDLER'S OFFSET OLD_COM_SEG DW ? ; OLD HANDLER'S SEGMENT ; ATTRIBUTES INSTALLED DB ? ; IS PORT INSTALLED ON THIS PC? (1=YES,0=NO) BAUD_RATE DW ? ; 19200 MAX CONNECTION DB ? ; M(ODEM), D(IRECT) PARITY DB ? ; N(ONE), O(DD), E(VEN), S(PACE), M(ARK) STOP_BITS DB ? ; 1, 2 XON_XOFF DB ? ; E(NABLED), D(ISABLED) ; FLOW CONTROL STATE HOST_OFF DB ? ; HOST XOFF'ED (1=YES,0=NO) PC_OFF DB ? ; PC XOFF'ED (1=YES,0=NO) ; ERROR COUNTS ERROR_BLOCK DW 8 DUP(?) ; EIGHT ERROR COUNTERS ; 8250 PORTS DATREG DW ? ; DATA REGISTER IER DW ? ; INTERRUPT ENABLE REGISTER IIR DW ? ; INTERRUPT IDENTIFICATION REGISTER LCR DW ? ; LINE CONTROL REGISTER MCR DW ? ; MODEM CONTROL REGISTER LSR DW ? ; LINE STATUS REGISTER MSR DW ? ; MODEM STATUS REGISTER ; BUFFER POINTERS START_TDATA DW ? ; INDEX TO FIRST CHARACTER IN X-MIT BUFFER END_TDATA DW ? ; INDEX TO FIRST FREE SPACE IN X-MIT BUFFER START_RDATA DW ? ; INDEX TO FIRST CHARACTER IN REC. BUFFER END_RDATA DW ? ; INDEX TO FIRST FREE SPACE IN REC. BUFFER ; BUFFER COUNTS SIZE_TDATA DW ? ; NUMBER OF CHARACTERS IN X-MIT BUFFER SIZE_RDATA DW ? ; NUMBER OF CHARACTERS IN REC. BUFFER ; BUFFERS TDATA DB S_SIZE DUP(?) ; SEND BUFFER RDATA DB R_SIZE DUP(?) ; RECEIVE BUFFER SP_TAB ENDS ; SP_TAB EQUATES ; WE HAVE TO USE THESE BECAUSE OF PROBLEMS WITH STRUC EOVFLOW EQU ERROR_BLOCK ; BUFFER OVERFLOWS EOVRUN EQU ERROR_BLOCK+2 ; RECEIVE OVERRUNS EBREAK EQU ERROR_BLOCK+4 ; BREAK CHARS EFRAME EQU ERROR_BLOCK+6 ; FRAMING ERRORS EPARITY EQU ERROR_BLOCK+8 ; PARITY ERRORS EXMIT EQU ERROR_BLOCK+10 ; TRANSMISSION ERRORS EDSR EQU ERROR_BLOCK+12 ; DATA SET READY ERRORS ECTS EQU ERROR_BLOCK+14 ; CLEAR TO SEND ERRORS DLL EQU DATREG ; LOW DIVISOR LATCH DLH EQU IER ; HIGH DIVISOR LATCH PAGE ; put the data in the DGROUP segment ; far calls enter with DS pointing to DGROUP ; DGROUP GROUP _DATA _DATA SEGMENT PUBLIC 'DATA' ; DIV50PC EQU 2304 ; DIVISOR FOR 50 BAUD (PC,XT) DIV50JR EQU 2237 ; DIVISOR FOR 50 BAUD (JR) DIV50 DW 2304 ; ACTUAL DIVISOR FOR 50 BAUD IN USE CURRENT_AREA DW AREA1 ; CURRENTLY SELECTED AREA ; DATA AREAS FOR EACH PORT AREA1 SP_TAB <1,INT_COM1,IRQ4,NIRQ4,EOI4> ; COM1 DATA AREA AREA2 SP_TAB <2,INT_COM2,IRQ3,NIRQ3,EOI3> ; COM2 DATA AREA _DATA ENDS PAGE COM_TEXT SEGMENT PARA PUBLIC 'CODE' ASSUME CS:COM_TEXT,DS:DGROUP,ES:NOTHING PUBLIC _select_port PUBLIC _save_com PUBLIC _install_com PUBLIC _restore_com PUBLIC _open_com PUBLIC _close_com PUBLIC _dtr_on PUBLIC _dtr_off PUBLIC _r_count PUBLIC _s_count PUBLIC _receive_com PUBLIC _send_com PUBLIC _sendi_com PUBLIC _send_local PUBLIC _break_com PUBLIC _com_errors PAGE ; ; SELECT WHICH PORT IS TO BE "ACTIVE" ; [bp+6] = port number _select_port PROC FAR push bp mov bp,sp mov AX,[bp+6] ; get aguement TEST AL,1 ; FIRST PORT? JZ SP1 ; IF NOT, IT MUST BE SECOND PORT MOV AX,OFFSET DGROUP:AREA1 ; SELECT COM1 DATA AREA JMP SHORT SPX ; CONTINUE SP1: MOV AX,OFFSET DGROUP:AREA2 ; SELECT COM2 DATA AREA SPX: MOV CURRENT_AREA,AX ; SET SELECTION IN MEMORY mov sp,bp pop bp RET ; DONE _select_port ENDP PAGE ; ; SAVE ORIGINAL COM VECTOR ; _save_com PROC FAR push bp mov bp,sp push si MOV SI,CURRENT_AREA ; SI POINTS TO DATA AREA PUSH ES ; SAVE EXTRA SEGMENT MOV AREA1.INT_HNDLR,OFFSET INT_HNDLR1 MOV AREA2.INT_HNDLR,OFFSET INT_HNDLR2 MOV AH,30H ; GET DOS VERSION NUMBER INT DOS ; DOS FUNCTION CMP AL,2 ; AT LEAST DOS 2? JB SVC1 ; JUMP IF DOS 1 ; SAVE OLD VECTOR WITH DOS 2 CALLS MOV AH,35H ; FETCH INTERRUPT VECTOR CONTENTS MOV AL,INT_COM[SI] ; INTERRUPT NUMBER INT DOS ; DOS 2 FUNCTION MOV OLD_COM_OFF[SI],BX ; SAVE MOV BX,ES ; ES:BX MOV OLD_COM_SEG[SI],BX ; FOR LATER RESTORATION JMP SHORT SVCX ; DONE ; SAVE OLD VECTOR WITH DOS 1 CALLS SVC1: MOV AX,0 ; ZERO SEGMENT MOV ES,AX ; ES POINTS TO INTERRUPT VECTORS MOV BL,INT_COM[SI] ; OUR INTERRUPT NUMBER MOV BH,0 ; BL -> BX SHL BX,1 ; TIMES FOUR SHL BX,1 ; IS OFFSET OF VECTOR IN MEMORY MOV AX,WORD PTR ES:[BX] ; SAVE MOV OLD_COM_OFF[SI],AX ; THE ADD BX,2 ; OLD MOV AX,WORD PTR ES:[BX] ; INTERRUPT MOV OLD_COM_SEG[SI],AX ; VECTOR SVCX: POP ES ; RESTORE ES pop si mov sp,bp pop bp RET ; DONE _save_com ENDP PAGE ; ; INSTALL_COM: INSTALL THE ACTIVE PORT ; ; SET 8250 PORTS FROM RS-232 BASE IN ROM BIOS DATA AREA ; INITIALIZE PORT CONSTANTS AND ERROR COUNTS ; INSTALL INTERRUPT VECTOR ; ; return ax=1 on success ax=0 on failure ; _install_com PROC FAR push bp mov bp,sp push si MOV SI,CURRENT_AREA ; SI POINTS TO DATA AREA PUSH ES ; SAVE EXTRA SEGMENT CMP INSTALLED[SI],1 ; ALREADY INSTALLED? JNE INSTOK ; NO, CONTINUE JMP INSTX ; ELSE JUMP IF ALREADY INSTALLED ; CLEAR ERROR COUNTS INSTOK: MOV WORD PTR EOVFLOW[SI],0 ; BUFFER OVERFLOWS MOV WORD PTR EOVRUN[SI],0 ; RECEIVE OVERRUNS MOV WORD PTR EBREAK[SI],0 ; BREAK CHARS MOV WORD PTR EFRAME[SI],0 ; FRAMING ERRORS MOV WORD PTR EPARITY[SI],0 ; PARITY ERRORS MOV WORD PTR EXMIT[SI],0 ; TRANSMISSION ERRORS MOV WORD PTR EDSR[SI],0 ; DATA SET READY ERRORS MOV WORD PTR ECTS[SI],0 ; CLEAR TO SEND ERRORS ; SENSE PC TYPE AND SET DIVISOR ACCORDINGLY MOV BX,ROM ; HIGH ROM SEGMENT MOV ES,BX ; TO ES ASSUME ES:ROM MOV DIV50,DIV50PC ; ASSUME PC OR XT CMP ROMID,0FDH ; IS IT A PCjr? JNE INST0 ; JUMP IF NOT MOV DIV50,DIV50JR ; ELSE SET JR DIVISOR ; SET 8250 PORT ADDRESSES INST0: MOV BX,RBDA ; ROM BIOS DATA AREA MOV ES,BX ; TO ES ASSUME ES:RBDA TEST PORT[SI],1 ; PORT 1? JZ INST1 ; JUMP IF NOT MOV AX,3F8H ; COM1 BASE PORT ADDRESS JMP SHORT INST2 ; CONTINUE INST1: MOV AX,2F8H ; COM2 BASE PORT ADDRESS INST2: CMP AX,RS232_BASE ; INSTALLED? JE INST2A ; JUMP IF SO CMP AX,RS232_BASE+2 ; INSTALLED? JNE INST666 ; JUMP IF NOT INST2A: MOV BX,DATREG ; OFFSET OF TABLE OF PORTS MOV CX,7 ; LOOP SIX TIMES INST3: MOV WORD PTR [SI][BX],AX ; SET PORT ADDRESS INC AX ; NEXT PORT ADD BX,2 ; NEXT WORD ADDRESS LOOP INST3 ; RS232 BASE LOOP ; RESET VECTOR TO POINT TO OUR HANDLER MOV AREA1.INT_HNDLR,OFFSET INT_HNDLR1 MOV AREA2.INT_HNDLR,OFFSET INT_HNDLR2 MOV AH,25H ; SET INTERRUPT VECTOR CONTENTS MOV AL,INT_COM[SI] ; INTERRUPT NUMBER MOV DX,OFFSET DGROUP:INT_HNDLR[SI] ; OUR INTERRUPT HANDLER PUSH DS ; SAVE DATA SEGMENT PUSH CS ; COPY CS POP DS ; TO DS INT DOS ; DOS FUNCTION POP DS ; RECOVER DATA SEGMENT ; PORT INSTALLED INSTX: MOV INSTALLED[SI],1 ; PORT INSTALLED POP ES ; RESTORE ES mov ax,1 pop si mov sp,bp pop bp RET ; DONE ; PORT NOT INSTALLED INST666:MOV INSTALLED[SI],0 ; PORT NOT INSTALLED ON THIS PC POP ES ; RESTORE ES mov ax,0 pop si mov sp,bp pop bp RET ; DONE _install_com ENDP PAGE ; ; RESTORE ORIGINAL INTERRUPT VECTOR ; _restore_com PROC FAR push bp mov bp,sp push si MOV SI,CURRENT_AREA ; SI POINTS TO DATA AREA MOV INSTALLED[SI],0 ; PORT IS NO LONGER INSTALLED MOV AH,25H ; SET INTERRUPT VECTOR FUNCTION MOV AL,INT_COM[SI] ; INTERRUPT NUMBER MOV DX,OLD_COM_OFF[SI] ; OLD OFFSET TO DX MOV BX,OLD_COM_SEG[SI] ; OLD SEG PUSH DS ; SAVE DS MOV DS,BX ; TO DS INT DOS ; DOS FUNCTION POP DS ; RECOVER DS pop si mov sp,bp pop bp RET ; DONE _restore_com ENDP PAGE ; ; OPEN_COM ON CURRENT PORT ; ; CLEAR BUFFERS ; RE-INITIALIZE THE INTEL 8250 UART ; ENABLE INTERRUPTS ON THE INTEL 8259 INTERRUPT CONTROL CHIP ; ; [bp+6] = BAUD RATE ; [bp+8] = CONNECTION: M(ODEM), D(IRECT) ; [bp+10] = PARITY: N(ONE), O(DD), E(VEN), S(PACE), M(ARK) ; [bp+12] = STOP BITS: 1, 2 ; [bp+14] = XON/XOFF: E(NABLED), D(ISABLED) ; _open_com PROC FAR push bp mov bp,sp push si MOV SI,CURRENT_AREA ; SI POINTS TO DATA AREA CLI ; INTERRUPTS OFF mov ax,[bp+6] MOV BAUD_RATE[SI],AX ; SET mov bh,[bp+8] MOV CONNECTION[SI],BH ; ARGS mov bl,[bp+10] MOV PARITY[SI],BL ; IN mov ch,[bp+12] MOV STOP_BITS[SI],CH ; MEMORY mov cl,[bp+14] MOV XON_XOFF[SI],CL ; RESET FLOW CONTROL MOV HOST_OFF[SI],0 ; HOST FLOWING MOV PC_OFF[SI],0 ; PC FLOWING ; RESET BUFFER COUNTS AND POINTERS MOV START_TDATA[SI],0 MOV END_TDATA[SI],0 MOV START_RDATA[SI],0 MOV END_RDATA[SI],0 MOV SIZE_TDATA[SI],0 MOV SIZE_RDATA[SI],0 TEST INSTALLED[SI],1 ; PORT INSTALLED? JNZ OC1 ; SKIP IF SO JMP OCX ; ELSE ABORT OC1: ; RESET THE 8250 MOV AL,0 MOV DX,MCR[SI] OUT DX,AL JMP $+2 ; I/O DELAY FOR JR MOV DX,LSR[SI] ; RESET LINE STATUS CONDITION IN AL,DX JMP $+2 ; I/O DELAY FOR JR MOV DX,DATREG[SI] ; RESET RECSIVE DATA CONDITION IN AL,DX JMP $+2 ; I/O DELAY FOR JR MOV DX,MSR[SI] ; RESET MODEM DELTAS AND CONDITIONS IN AL,DX ; CONVERT PASSED BAUD RATE TO 8250 DIVISOR MOV AX,50 ; 50 BAUD MUL DIV50 ; TIMES ITS DIVISOR DIV BAUD_RATE[SI] ; OTHER SPEEDS ARE PROPORTIONAL MOV BX,AX ; RESULT TO BX ; SET 8250 DIVISOR MOV DX,LCR[SI] ; LINE CONTROL REGISTER MOV AL,80H ; HI BIT ON OUT DX,AL ; SET DLAB = 1 JMP $+2 ; I/O DELAY FOR JR MOV DX,WORD PTR DLL[SI] ; LEAST SIGNIFICANT BYTE MOV AL,BL ; LSB FROM TABLE OUT DX,AL ; SET LSB ON 8250 JMP $+2 ; I/O DELAY FOR JR MOV DX,WORD PTR DLH[SI] ; MOST SIGNIFICANT BYTE MOV AL,BH ; MSB FROM TABLE OUT DX,AL ; SET MSB ON 8250 JMP $+2 ; I/O DELAY FOR JR ; SET PARITY AND NUMBER OF STOP BITS MOV AL,03H ; NONE OR SPACE PARITY IS THE DEFAULT CMP PARITY[SI],'O' ; ODD PARITY REQUESTED? JNE P1 ; JUMP IF NOT MOV AL,0AH ; SELECT ODD PARITY JMP SHORT P3 ; CONTINUE P1: CMP PARITY[SI],'E' ; EVEN PARITY REQUESTED? JNE P2 ; JUMP IF NOT MOV AL,1AH ; SELECT EVEN PARITY JMP SHORT P3 ; CONTINUE P2: CMP PARITY[SI],'M' ; MARK PARITY REQUESTED? JNE P3 ; JUMP IF NOT MOV AL,2AH ; SELECT MARK PARITY P3: TEST STOP_BITS[SI],2 ; 2 STOP BITS REQUESTED? JZ STOP1 ; NO OR AL,4 ; YES STOP1: MOV DX,LCR[SI] ; LINE CONTROL REGISTER OUT DX,AL ; SET 8250 PARITY MODE AND DLAB=0 ; ENABLE INTERRUPTS ON 8259 AND 8250 IN AL,INTA01 ; SET ENABLE BIT ON 8259 AND AL,NIRQ[SI] OUT INTA01,AL MOV DX,IER[SI] ; ENABLE INTERRUPTS ON 8250 MOV AL,5 ; RECEIVE & LINE ERROR OUT DX,AL JMP $+2 ; I/O DELAY FOR JR MOV DX,MCR[SI] ; SET DTR AND ENABLE INT DRIVER MOV AL,0BH OUT DX,AL OCX: STI ; INTERRUPTS ON pop si mov sp,bp pop bp RET ; DONE _open_com ENDP PAGE ; ; CLOSE_COM - TURNS OFF INTERRUPTS FROM THE COMMUNICATIONS PORT ; _close_com PROC FAR push bp mov bp,sp push si MOV SI,CURRENT_AREA ; SI POINTS TO DATA AREA TEST INSTALLED[SI],1 ; PORT INSTALLED? JZ CCX ; ABORT IF NOT ; TURN OFF 8250 MOV DX,IER[SI] MOV AL,0 OUT DX,AL ; TURN OFF 8259 MOV DX,INTA01 IN AL,DX OR AL,IRQ[SI] JMP $+2 ; DELAY FOR AT OUT DX,AL CCX: pop si mov sp,bp pop bp RET _close_com ENDP PAGE ; ; DTR_OFF - TURNS OFF DTR TO TELL MODEMS THAT THE TERMINAL HAS GONE AWAY ; AND TO HANG UP THE PHONE ; _dtr_off PROC FAR push bp mov bp,sp push si PUSHF ; SAVE FLAGS PUSH AX ; SAVE REGS PUSH DX PUSH SI MOV SI,CURRENT_AREA ; SI POINTS TO DATA AREA TEST INSTALLED[SI],1 ; PORT INSTALLED? JZ DFX ; ABORT IF NOT MOV DX,MCR[SI] MOV AL,08H ; DTR OFF, RTS OFF, OUT2 ON OUT DX,AL DFX: POP SI ; RECOVER REGS POP DX POP AX POPF ; RECOVER FLAGS pop si mov sp,bp pop bp RET _dtr_off ENDP ; ; DTR_ON - TURNS DTR ON ; _dtr_on PROC FAR push bp mov bp,sp push si PUSHF ; SAVE FLAGS PUSH AX ; SAVE REGS PUSH DX PUSH SI MOV SI,CURRENT_AREA ; SI POINTS TO DATA AREA TEST INSTALLED[SI],1 ; PORT INSTALLED? JZ DNX ; ABORT IF NOT MOV DX,MCR[SI] MOV AL,0BH OUT DX,AL DNX: POP SI ; RECOVER REGS POP DX POP AX POPF ; RECOVER FLAGS pop si mov sp,bp pop bp RET ; DONE _dtr_on ENDP PAGE ; ; R_COUNT - RETURNS NUMBER OF BYTES IN THE RECEIVE BUFFER IN AX ; total in DX ; _r_count PROC FAR push bp mov bp,sp push si PUSHF ; SAVE FLAGS PUSH SI ; SAVE SI MOV SI,CURRENT_AREA ; SI POINTS TO DATA AREA MOV AX,0 ; NOTHING RECEIVED IF NOT INSTALLED mov dx,R_SIZE TEST INSTALLED[SI],1 ; PORT INSTALLED? JZ RCX ; ABORT IF NOT MOV AX,SIZE_RDATA[SI] ; GET NUMBER OF BYTES USED RCX: POP SI ; RESTORE SI POPF ; RESTORE FLAGS pop si mov sp,bp pop bp RET _r_count ENDP PAGE ; ; RECEIVE - RETURNS THE NEXT CHARACTER FROM THE RECEIVE BUFFER IN AL ; AND REMOVES IT FROM THE BUFFER ; THE PARITY BIT IS STRIPPED OFF ; _receive_com PROC FAR push bp mov bp,sp push si PUSHF ; SAVE FLAGS PUSH BX ; SAVE REGS PUSH SI MOV SI,CURRENT_AREA ; SI POINTS TO DATA AREA mov ax,-1 ; -1 if bad call TEST INSTALLED[SI],1 ; PORT INSTALLED? JZ RCVX ; ABORT IF NOT CMP SIZE_RDATA[SI],0 ; ANY CHARACTERS? JE RCVX ; ABORT IF NOT ; GOOD CALL mov ah,0 ; good call MOV BX,START_RDATA[SI] ; GET POINTER TO OLDEST CHAR MOV AL,RDATA[SI][BX] ; GET CHAR FROM BUFFER ; MOD BY LBA, 10/6/85 FOR PROPER SUPPORT OF NO PARITY COMMUNICATIONS CMP PARITY[SI],'N' ; ARE WE RUNNING WITH NO PARITY? JE L11 ; IF SO, DON'T STRIP HIGH BIT ; END OF MOD AND AL,7FH ; STRIP PARITY BIT L11: INC BX ; BUMP START_RDATA CMP BX,R_SIZE ; SEE IF PAST END JB L12 ; IF NOT THEN SKIP MOV BX,0 ; ADJUST TO BEGINNING L12: MOV START_RDATA[SI],BX ; SAVE THE NEW START_RDATA VALUE DEC SIZE_RDATA[SI] ; ONE LESS CHARACTER CMP XON_XOFF[SI],'E' ; FLOW CONTROL ENABLED? JNE RCVX ; DO NOTHING IF DISABLED CMP HOST_OFF[SI],1 ; HOST TURNED OFF? JNE RCVX ; JUMP IF NOT CMP SIZE_RDATA[SI],R_SIZE/20 ; RECEIVE BUFFER NEARLY EMPTY? JGE RCVX ; DONE IF NOT MOV HOST_OFF[SI],0 ; TURN ON HOST IF SO PUSH AX ; SAVE RECEIVED CHAR MOV AL,CONTROL_Q ; TELL HIM TO TALK CLI ; TURN OFF INTERRUPTS CALL SENDII ; SEND IMMEDIATELY INTERNAL STI ; INTERRUPTS BACK ON POP AX ; RESTORE RECEIVED CHAR RCVX: POP SI ; RECOVER REGS POP BX POPF ; RECOVER FLAGS pop si mov sp,bp pop bp RET ; DONE _receive_com ENDP PAGE ; ; S_COUNT - RETURNS IN AX THE AMOUNT OF FREE SPACE ; REMAINING IN THE TRANSMIT BUFFER ; DX total size ; _s_count PROC FAR push bp mov bp,sp push si PUSHF ; SAVE FLAGS PUSH SI ; SAVE SI MOV SI,CURRENT_AREA ; SI POINTS TO DATA AREA MOV AX,0 ; NO SPACE LEFT IF NOT INSTALLED mov dx,S_SIZE TEST INSTALLED[SI],1 ; PORT INSTALLED? JZ SCX ; ABORT IF NOT MOV AX,S_SIZE ; GET THE SIZE OF THE X-MIT BUFFER SUB AX,SIZE_TDATA[SI] ; SUBTRACT THE NUMBER OF BYTES USED SCX: POP SI ; RECOVER SI POPF ; RESTORE FLAGS pop si mov sp,bp pop bp RET _s_count ENDP PAGE ; ; SEND - SEND A CHARACTER ; [bp+6] = char ; _send_com PROC FAR push bp mov bp,sp push si mov al,[bp+6] PUSHF ; SAVE FLAGS PUSH AX ; SAVE REGS PUSH BX PUSH DX PUSH SI MOV SI,CURRENT_AREA ; SI POINTS TO DATA AREA TEST INSTALLED[SI],1 ; PORT INSTALLED? JZ L44 ; ABORT IF NOT CMP SIZE_TDATA[SI],S_SIZE ; BUFFER FULL? JL L4A ; JUMP IF NOT INC WORD PTR EOVFLOW[SI] ; BUMP ERROR COUNT JMP SHORT L44 ; PUNT L4A: MOV BX,END_TDATA[SI] ; BX POINTS TO FREE SPACE MOV TDATA[SI][BX],AL ; MOVE CHAR TO BUFFER INC BX ; INCREMENT END_TDATA CMP BX,S_SIZE ; SEE IF PAST END JL L4 ; IF NOT THEN SKIP MOV BX,0 ; ADJUST TO BEGINNING L4: MOV END_TDATA[SI],BX ; SAVE NEW END_TDATA INC SIZE_TDATA[SI] ; ONE MORE CHARACTER IN X-MIT BUFFER MOV DX,IER[SI] ; INTERRUPT ENABLE REGISTER IN AL,DX ; GET IT TEST AL,2 ; SEE IF TX INTERRUPTS ARE ENABLED JNZ L44 ; JUMP IF SO MOV AL,7 ; IF NOT THEN RCV, TX, LINE ERROR OUT DX,AL ; ARE ENABLED L44: POP SI ; RESTORE REGS POP DX POP BX POP AX POPF ; RESTORE FLAGS pop si mov sp,bp pop bp RET ; DONE _send_com ENDP PAGE ; ; SENDI - SEND A CHARACTER IMMEDIATELY ; [bp+6] = char to send ; _sendi_com PROC FAR push bp mov bp,sp push si mov al,[bp+6] PUSHF ; SAVE FLAGS PUSH AX ; SAVE REGS PUSH BX PUSH DX PUSH SI MOV SI,CURRENT_AREA ; SI POINTS TO DATA AREA TEST INSTALLED[SI],1 ; PORT INSTALLED? JZ LQ44 ; ABORT IF NOT CLI ; MASK INTERRUPTS CALL SENDII ; CALL INTERNAL SEND IMMEDIATE STI ; INTERRRUPTS BACK ON LQ44: POP SI ; RESTORE REGS POP DX POP BX POP AX POPF ; RESTORE FLAGS pop si mov sp,bp pop bp RET ; DONE _sendi_com ENDP PAGE ; ; INTERNAL ROUTINE ; DEPENDS ON CALLER TO KEEP INTERRUPTS CLEARED AND SET SI ; SENDI - SEND A CHARACTER IMMEDIATELY (PUT AT BEGINNING OF QUEUE) ; AL = CHAR TO WRITE ; SENDII PROC NEAR PUSH DX ; SAVE DX CMP SIZE_TDATA[SI],S_SIZE ; BUFFER FULL? JB LI4A ; JUMP IF NOT INC WORD PTR EOVFLOW[SI] ; BUMP ERROR COUNT MOV BX,START_TDATA[SI] ; BX POINTS TO FIRST CHAR IN BUFFER MOV TDATA[SI][BX],AL ; CLOBBER FIRST CHAR IN BUFFER JMP SHORT LI4B ; CONTINUE LI4A: MOV BX,START_TDATA[SI] ; BX POINTS TO FIRST CHAR IN BUFFER DEC BX ; BACKUP THE PTR CMP BX,-1 ; BEFORE BEGINNING? JNE LI4 ; JUMP IF NOT MOV BX,S_SIZE-1 ; POINT TO END IF SO LI4: MOV TDATA[SI][BX],AL ; MOVE CHAR TO BUFFER MOV START_TDATA[SI],BX ; SAVE NEW START_TDATA INC SIZE_TDATA[SI] ; ONE MORE CHARACTER IN X-MIT BUFFER LI4B: MOV DX,IER[SI] ; INTERRUPT ENABLE REGISTER IN AL,DX ; GET IT TEST AL,2 ; SEE IF TX INTERRUPTS ARE ENABLED JNZ LI44 ; JUMP IF SO MOV AL,7 ; IF NOT THEN RCV, TX, LINE ERROR OUT DX,AL ; ARE ENABLED LI44: POP DX ; RECOVER DX RET ; DONE SENDII ENDP PAGE ; ; S_LOCAL ; _send_local PROC FAR push bp mov bp,sp push si mov al,[bp+6] PUSHF ; SAVE FLAGS PUSH AX ; SAVE REGS PUSH BX PUSH SI MOV SI,CURRENT_AREA ; SI POINTS TO DATA AREA TEST INSTALLED[SI],1 ; PORT INSTALLED? JZ SLX ; ABORT IF NOT CLI ; INTERRUPTS OFF CMP SIZE_RDATA[SI],R_SIZE ; SEE IF ANY ROOM JB L13A ; SKIP IF ROOM INC WORD PTR EOVFLOW[SI] ; BUMP OVERFLOW COUNT JMP SHORT L14 ; PUNT L13A: MOV BX,END_RDATA[SI] ; BX POINTS TO FREE SPACE MOV RDATA[SI][BX],AL ; SEND DATA TO BUFFER INC BX ; INCREMENT END_RDATA POINTER CMP BX,R_SIZE ; SEE IF GONE PAST END JL L13 ; IF NOT THEN SKIP MOV BX,0 ; ELSE ADJUST TO BEGINNING L13: MOV END_RDATA[SI],BX ; SAVE VALUE INC SIZE_RDATA[SI] ; GOT ONE MORE CHARACTER L14: STI ; INTERRUPTS BACK ON SLX: POP SI ; RECOVER REGS POP BX POP AX POPF ; RECOVER FLAGS pop si mov sp,bp pop bp RET ; DONE _send_local ENDP PAGE ; ; BREAK - CAUSES A BREAK TO BE SENT OUT ON THE LINE ; _break_com PROC FAR push bp mov bp,sp push si PUSHF ; SAVE FLAGS PUSH AX ; SAVE REGS PUSH CX PUSH DX MOV SI,CURRENT_AREA ; SI POINTS TO DATA AREA TEST INSTALLED[SI],1 ; PORT INSTALLED? JZ BRX ; ABORT IF NOT MOV DX,LCR[SI] ; LINE CONTROL REGISTER IN AL,DX ; GET CURRENT SETTING JMP $+2 ; I/O DELAY FOR JR OR AL,40H ; TURN ON BREAK BIT OUT DX,AL ; SET IT ON THE 8250 MOV CX,0C000H ; WAIT APPROX. 1/4 SEC. BREAK1: LOOP BREAK1 ; BUSY WAIT AND AL,0BFH ; TURN OFF BREAK BIT OUT DX,AL ; RESTORE LINE CONTROL REGISTER BRX: POP DX ; RECOVER REGS POP CX POP AX POPF ; RECOVER FLAGS pop si mov sp,bp pop bp RET ; DONE _break_com ENDP PAGE ; ; COM_ERRORS - RETURN POINTER IN dx:ax TO ERROR COUNTS ; _com_errors PROC FAR push bp mov bp,sp mov ax,OFFSET DGROUP:CURRENT_AREA add ax,ERROR_BLOCK mov dx,ds mov sp,bp pop bp RET ; DONE _com_errors ENDP PAGE ; ; INTERNAL ROUTINE ; BUMP ERROR COUNTS FROM LINE STATUS IN AL ; E_BUMP PROC NEAR TEST AL,2 ; OVERRUN ERROR? JZ LSI1 ; JUMP IF NOT INC WORD PTR EOVRUN[SI] ; ELSE BUMP ERROR COUNT LSI1: TEST AL,4 ; PARITY ERROR? JZ LSI2 ; JUMP IF NOT INC WORD PTR EPARITY[SI] ; ELSE BUMP ERROR COUNT LSI2: TEST AL,8 ; FRAMING ERROR? JZ LSI3 ; JUMP IF NOT INC WORD PTR EFRAME[SI] ; ELSE BUMP ERROR COUNT LSI3: TEST AL,16 ; BREAK RECEIVED? JZ LSI4 ; JUMP IF NOT INC WORD PTR EBREAK[SI] ; ELSE BUMP ERROR COUNT LSI4: RET ; DONE E_BUMP ENDP PAGE ; ; INTERNAL ROUTINE ; MODEM SEND PROTOCOL ; M_PROTOCOL PROC NEAR CMP CONNECTION[SI],'M' ; MODEM CONNECTION? JNE S3 ; IF NOT, SKIP DSR & CTS PROTOCOL ; TELL MODEM WE'RE READY TO SEND MOV DX,MCR[SI] ; MODEM CONTROL REGISTER MOV AL,00001011B ; OUT 2, RTS, DTR OUT DX,AL ; TERMINAL READY, REQUEST TO SEND JMP $+2 ; I/O DELAY FOR JR ; WAIT UNTIL MODEM SAYS DATA SET READY MOV CX,1000 ; TIMEOUT COUNT MOV DX,MSR[SI] ; MODEM STATUS REGISTER S1: IN AL,DX ; GET MODEM STATUS TEST AL,20H ; DATA SET READY? JNZ S1X ; YES, TEST CLEAR TO SEND LOOP S1 ; NO, BUSY WAIT INC WORD PTR EDSR[SI] ; BUMP ERROR COUNT JMP SHORT S3 ; WE TIMED OUT S1X: ; WAIT UNTIL MODEM SAYS IT'S CLEAR TO SEND MOV CX,1000 ; TIMEOUT COUNT S2: IN AL,DX ; GET MODEM STATUS TEST AL,10H ; CLEAR TO SEND? JNZ S2X ; YES LOOP S2 ; NO, KEEP TRYING INC WORD PTR ECTS[SI] ; BUMP ERROR COUNT - WE TIMED OUT S2X: ; TEST FOR TRANSMITTER READY S3: MOV DX,LSR[SI] ; LINE STATUS REGISTER IN AL,DX ; GET LINE STATUS TEST AL,20H ; TRANSMITTER READY? JNZ S4 ; SKIP IF SO INC WORD PTR EXMIT[SI] ; ELSE BUMP ERROR COUNT S4: RET ; DONE M_PROTOCOL ENDP PAGE ; ; INTERNAL ROUTINES FOR FLOW CONTROL ; ; FLOW_IN - RESPOND TO FLOW CONTROL COMMANDS FROM HOST ; FLOW_OUT - ISSUE FLOW CONTROL COMMANDS TO HOST ; FLOW_IN PROC NEAR PUSH AX ; SAVE CHAR CMP XON_XOFF[SI],'E'; FLOW CONTROL ENABLED? JNE FI_2 ; DO NOTHING IF DISABLED AND AL,7FH ; STRIP PARITY CMP AL,CONTROL_S ; STOP COMMAND RECEIVED? JNE FI_1 ; JUMP IF NOT MOV PC_OFF[SI],1 ; WE MUST SHUT UP JMP SHORT FI_2 ; CONTINUE FI_1: CMP AL,CONTROL_Q ; GO COMMAND RECEIVED? JNE FI_2 ; NO, MUST BE NORMAL CHAR MOV PC_OFF[SI],0 ; WE START TALKING AGAIN FI_2: POP AX ; RESTORE CHAR RET ; DONE FLOW_IN ENDP ; FLOW_OUT PROC NEAR CMP XON_XOFF[SI],'E'; FLOW CONTROL ENABLED? JNE FO_X ; DO NOTHING IF DISABLED CMP HOST_OFF[SI],1 ; HOST TURNED OFF? JE FO_X ; JUMP IF SO CMP SIZE_RDATA[SI],R_SIZE/2 ; RECEIVE BUFFER NEARLY FULL? JLE FO_X ; DONE IF NOT MOV AL,CONTROL_S ; TURN OFF HOST IF SO CALL SENDII ; SEND IMMEDIATELY INTERNAL MOV HOST_OFF[SI],1 ; HOST IS NOW OFF FO_X: RET ; DONE FLOW_OUT ENDP PAGE ; ; INT_HNDLR1 - HANDLES INTERRUPTS GENERATED BY COM1: ; INT_HNDLR1 PROC FAR PUSH SI ; SAVE SI MOV SI,OFFSET AREA1 ; DATA AREA FOR COM1: JMP SHORT INT_COMMON ; CONTINUE ; ; INT_HNDLR2 - HANDLES INTERRUPTS GENERATED BY COM2: ; INT_HNDLR2 PROC FAR PUSH SI ; SAVE SI MOV SI,OFFSET AREA2 ; DATA AREA FOR COM2: ; ; BODY OF INTERRUPT HANDLER ; INT_COMMON: PUSH AX ; SAVE REGS PUSH BX PUSH CX PUSH DX PUSH BP PUSH DI PUSH DS PUSH ES MOV AX,SEG _DATA ; OUR DATA SEG MOV DS,AX ; TO DS ; CLEAR THE INTERRUPT CONTROLLER FLAG MOV DX,INTA00 ; 8259 CONTROL PORT MOV AL,EOI[SI] ; SPECIFIC END OF INTERRUPT OUT DX,AL ; CLEAR FLAG ; FIND OUT WHERE INTERRUPT CAME FROM AND JUMP TO ROUTINE TO HANDLE IT REPOLL: MOV DX,IIR[SI] ; READ INTERRUPT STATUS REGISTER IN AL,DX CMP AL,4 JE RX_INT ; IF FROM THE RECEIVER CMP AL,2 JE TX_INT ; IF FROM THE TRANSMITTER CMP AL,6 JE LSTAT_INT ; INTERRUPT BECAUSE OF LINE STATUS CMP AL,0 JE MSTAT_INT ; INTERRUPT BECAUSE OF MODEM STATUS JMP FAR PTR INT_END ; DONE, EXIT (DON'T FIX FAR PTR STUFF) LSTAT_INT: MOV DX,LSR[SI] ; READ AND IGNORE LINE STATUS IN AL,DX ; CALL E_BUMP ; JUST BUMP ERROR COUNTS JMP REPOLL ; SEE IF ANY MORE INTERRUPTS MSTAT_INT: MOV DX,MSR[SI] ; READ AND IGNORE MODEM STATUS IN AL,DX ; JMP REPOLL ; SEE IF ANY MORE INTERRUPTS TX_INT: CMP PC_OFF[SI],1 ; HAVE WE BEEN TOLD TO SHUT UP? JNE GOODTX1 ; JUMP IF NOT CMP HOST_OFF[SI],1 ; HAS HOST ALSO SHUT UP? JNE SEND_NO_MORE ; JUMP IF NOT ; CLEAR XON/XOFF DEADLOCK MOV PC_OFF[SI],0 ; WE SPEAK MOV HOST_OFF[SI],0 ; THEY REPLY MOV AL,CONTROL_Q ; BUT ONLY WHEN CALL SENDII ; WE LET THEM GOODTX1: CMP SIZE_TDATA[SI],0 ; SEE IF ANY MORE DATA TO SEND JG HAVE_DATA ; IF POSITIVE THEN THERE IS DATA TO SEND ; IF NO DATA TO SEND THEN RESET TX INTERRUPT AND RETURN SEND_NO_MORE: MOV DX,IER[SI] ; MOV AL,5 ; JUST RCV AND LINE ERROR OUT DX,AL ; ARE SET JMP REPOLL ; HAVE_DATA: CALL M_PROTOCOL ; DO MODEM PROTOCOL IF NECESSARY MOV BX,START_TDATA[SI] ; BX POINTS TO NEXT CHAR. TO BE SENT MOV AL,TDATA[SI][BX] ; GET DATA FROM BUFFER MOV DX,DATREG[SI] ; DX EQUALS PORT TO SEND DATA TO OUT DX,AL ; SEND DATA INC BX ; INCREMENT START_TDATA CMP BX,S_SIZE ; SEE IF GONE PAST END JB NTADJ ; IF NOT THEN SKIP MOV BX,0 ; RESET TO BEGINNING NTADJ: MOV START_TDATA[SI],BX ; SAVE START_TDATA DEC SIZE_TDATA[SI] ; ONE LESS CHARACTER IN X-MIT BUFFER JMP REPOLL RX_INT: MOV DX,DATREG[SI] ; 8250 DATA REGISTER IN AL,DX ; GET DATA CALL FLOW_IN ; RESPOND TO F.C. COMMANDS FROM HOST CMP SIZE_RDATA[SI],R_SIZE ; SEE IF ANY ROOM JL GOOD_RX1 ; CONTINUE IF SO INC WORD PTR EOVFLOW[SI] ; BUMP OVERFLOW ERROR COUNT JMP REPOLL ; PUNT GOOD_RX1: MOV BX,END_RDATA[SI] ; BX POINTS TO FREE SPACE MOV RDATA[SI][BX],AL ; MOVE DATA TO BUFFER INC SIZE_RDATA[SI] ; GOT ONE MORE CHARACTER INC BX ; INCREMENT END_RDATA POINTER CMP BX,R_SIZE ; SEE IF GONE PAST END JB NRADJ ; IF NOT THEN SKIP MOV BX,0 ; ELSE ADJUST TO BEGINNING NRADJ: MOV END_RDATA[SI],BX ; SAVE VALUE CALL FLOW_OUT ; ISSUE FLOW CONTROL COMMANDS TO HOST JMP REPOLL ; INT_END: POP ES ; RESTORE REGS POP DS POP DI POP BP POP DX POP CX POP BX POP AX POP SI ; RESTORE SI, TOO IRET INT_HNDLR2 ENDP INT_HNDLR1 ENDP COM_TEXT ENDS END @@@ Finished comm.asm echo dcpasm.asm cat >dcpasm.asm <<'@@@ Finished dcpasm.asm' ; Support rotuines for DCP a uucp clone. ; Copyright Richard H. Lamb, 1985,1986,1987 ; dos dir scan for a file ;"""""""""""""""""""""""""""""" _text segment byte public 'code' _text ends _data segment byte public 'data' _data ends const segment byte public 'const' const ends _bbs segment byte public 'bbs' _bbs ends dgroup group const,_bbs,_data assume cs:_text,ds:dgroup,ss:dgroup,es:dgroup _text SEGMENT public _dir_open _dir_open proc near ;_dir_open: push bp mov bp,sp push ds push dx push cx ; set DTA mov dx,word ptr [bp+6] mov ah,01ah int 21h ; find first file mov dx,word ptr [bp+4] mov ah,04eh mov cx,010h int 21h ; return 0=found sub ah,ah pop cx pop dx pop ds pop bp ret _dir_open endp ;******************************* _text ends end @@@ Finished dcpasm.asm echo call.bat cat >call.bat <<'@@@ Finished call.bat' dcp master 3 @@@ Finished call.bat echo makeexe.bat cat >makeexe.bat <<'@@@ Finished makeexe.bat' link dcp+dcpstart+dcpscan+dcpsys+dcprec+dcpsend+dcpgpkt+dcprpkt+dcptpkt+dcpkpkt+dcpio+dcxqt+dcpvms+comm+dcpasm; @@@ Finished makeexe.bat echo makeobj.bat cat >makeobj.bat <<'@@@ Finished makeobj.bat' msc dcp; msc dcpsys; msc dcpstart; msc -Ze dcpio; msc -Ze dcpscan; msc dcpsend; msc dcprec; msc dcpgpkt; msc dcprpkt; msc dcptpkt; msc dcpkpkt; msc -Ze dcxqt; msc dcpvms; masm comm; masm dcpasm; msc rmail; link rmail; @@@ Finished makeobj.bat echo call.cli cat >call.cli <<'@@@ Finished call.cli' xe dcp master 3 @@@ Finished call.cli echo makeexe.cli cat >makeexe.cli <<'@@@ Finished makeexe.cli' ccl dcp,dcpsys,dcpstart,dcpscan,dcprec,dcpsend,dcpgpkt,dcptpkt,dcpio,dcprpkt, & dcpkpkt,dcxqt,dcpvms @@@ Finished makeexe.cli echo makeobj.cli cat >makeobj.cli <<'@@@ Finished makeobj.cli' cc dcp cc dcpsys cc dcpstart cc dcpscan cc dcprec cc dcpsend cc dcpgpkt cc dcptpkt cc dcpio cc dcxqt cc dcprpkt cc dcpkpkt cc dcpvms cc rmail ccl rmail @@@ Finished makeobj.cli echo clink.vms cat >clink.vms <<'@@@ Finished clink.vms' $ link 'p1,sys$library:crtlib/lib @@@ Finished clink.vms echo makeexe.vms cat >makeexe.vms <<'@@@ Finished makeexe.vms' $ clink dcp,dcpstart,dcpsys,dcpscan,dcpsend,dcprec,dcpgpkt,dcptpkt,dcprpkt,dcpkpkt,dcpio,dcxqt,dcpvms @@@ Finished makeexe.vms echo makeobj.vms cat >makeobj.vms <<'@@@ Finished makeobj.vms' $ cc dcp $ cc dcpstart $ cc dcpsys $ cc dcpscan $ cc dcpsend $ cc dcprec $ cc dcpgpkt $ cc dcptpkt $ cc dcprpkt $ cc dcpkpkt $ cc dcpio $ cc dcxqt $ cc dcpvms $ cc rmail $ clink rmail @@@ Finished makeobj.vms echo setup.vms cat >setup.vms <<'@@@ Finished setup.vms' $ rmail :== $sys$user:[lids.lamb.c.dcp2]rmail $ dcp :== $sys$user:[lids.lamb.c.dcp2]dcp $ ss :== $sys$user:[lids.lamb.c.dcp2]spawn @@@ Finished setup.vms exit 0 End of Listing