/*This program implements the Ward Christensen assembly language program in C. All of the original modes are available, and a capture buffer was added to the terminal and computer modes. The UNIX and PDP10 modes were added to facilitate sending files to those systems. Note that the original version of this program was written for the D.C. Hayes modem board - however, this version is for the PMMI modem. original by Jack M. Wierda modified by Roderick W. Hart */ #include #define FALSE 0 #define TRUE 1 #define NUL 0 #define SOH 1 #define CTRLB 2 #define CTRLC 3 #define EOT 4 #define ERRORMAX 5 #define RETRYMAX 5 #define CTRLE 5 #define ACK 6 #define TAB 9 #define LF 10 #define CR 13 #define CTRLQ 17 #define SPS 2750 /*loops per second*/ #define CTRLS 19 #define NAK 21 #define CTRLZ 26 #define SPACE 32 #define DELETE 127 #define TIMEOUT -1 #define SECSIZ 0x80 #define DATAMASK 0x7f #define BUFSIZ 0x7f80 /*maximum size, 0x8000 will not work with with BDS C, reduce buffer size as necessary for smaller systems*/ char cntrlw0, cntrlw1, cntrlw2, cntrlw3; char hangup, option, option2, mode, baudrate, display, system; char asciiflg, showtrans, showrecv, view, dtrr; char buffer[BUFSIZ]; moready() { return (inp(STATPORT) & TBMT) == (MAHI ? TBMT : 0); } miready() { return (inp(STATPORT) & DAV) == (MAHI ? DAV : 0); } ctsready() { return (inp(MODEMCP2) & CTS) == (CTSHI ? CTS : 0); } sendline(data) char data; { while(!MOREADY()); outp(DATAPORT,data); if(showtrans) { if(asciiflg) if(((data >= ' ') && (data <= 0x7f)) || data == LF || data == CR || data == TAB) putchar(data); else printf("[%0x]",data); if(option2 == 'H') printf("[%0x]",data); } } readline(seconds) int seconds; { char data; seconds = seconds * SPS; while (!MIREADY() && seconds--); if(seconds < 0) return(TIMEOUT); data = inp(DATAPORT); if(showrecv) { if(asciiflg) if(((data >= ' ') && (data <= 0x7f)) || data == LF || data == CR || data == TAB) putchar(data); else printf("[%0x]",data); if(option2 == 'H') printf("<%0x>",data); } return(data); } purgeline() { while (MIREADY()) inp(DATAPORT); /*PURGE THE RECEIVE REGISTER*/ } initializemodem() { char cdflg; cdflg = TRUE; if((option == 'R') || (option == 'S')) { cntrlw0 = ORIG + NB1 + NB2 + NP; cntrlw3 = dtrr; } else { cntrlw0 = ORIG + NB2 + EPS; cntrlw3 = dtrr; } if(!CTSREADY()) { printf("Waiting for carrier\n"); cdflg = FALSE; } while(!CTSREADY() && !bios(2)) { outp(STATPORT,cntrlw0); outp(MODEMCP2,cntrlw2); outp(MODEMCP3,cntrlw3); outp(DATAPORT,cntrlw1); } if(CTSREADY() && !cdflg) printf("Carrier detected\n"); outp(STATPORT, (cntrlw0 & 0xfe)); purgeline(); } sendstr(str) char *str; { while(*str) if(option == 'U') sendline(tolower(*str++)); else sendline(*str++); } termcomp() { char bflag, kbdata, moddata; int fd; int bufctr; bflag = FALSE; bufctr = 0; initializemodem(); while((CTSREADY()) && (kbdata !=CTRLE)) { if(bios(2)) switch(kbdata = bios(3)) { case CTRLB: bflag = ~bflag; if(bflag) printf("Capture initiated"); else printf("Capture terminated"); printf(", %u bytes free\n",BUFSIZ - bufctr); break; case CTRLE: break; default: if(option == 'C') { putchar(kbdata); if(bflag && (bufctr < BUFSIZ)) buffer[bufctr++] = kbdata; } outp(DATAPORT,kbdata); break; } if(MIREADY()) { moddata = inp(DATAPORT); if(option == 'C') outp(DATAPORT,moddata); if(bflag && (bufctr < BUFSIZ)) buffer[bufctr++] = moddata; else if(bflag) printf("Buffer overflow\n"); putchar(moddata); } } if(bufctr) { buffer[bufctr] = CTRLZ; fd = creat("cmodem.tmp"); if(fd == -1) { printf("Cannot create cmodem.tmp\n"); exit(); } write(fd,buffer,1 + (bufctr/SECSIZ)); close(fd); } } putbuffer(buffer,sectors) char *buffer; int sectors; { char ch, moddata; unsigned k; ch = 0; k = 0; while((ch != CTRLZ) && (k < sectors * SECSIZ) && (CTSREADY())) { k = k+1; if((ch = *buffer++) == LF) /*don't send LF's*/ putchar(LF); else if(ch != CTRLZ || option != 'U') /*don't send CTRLZ to UNIX*/ sendline(ch); if(!(k & 0xff)) /*let other end catch-up*/ while(readline(1) != TIMEOUT); } } pdp10(file) char *file; { char sectors; int fd; showrecv = FALSE; asciiflg = showtrans = TRUE; fd = open(file,0); if(fd == -1) { printf("Cannot open %s\n",file); exit(); } initializemodem(); if(option == 'P') { sendstr("\nR PIP\n"); while((readline(1) != '*') && !bios(2)); if(*(++file) != ':') sendstr(--file); else sendstr(++file); sendstr("=TTY:\n"); } else { sendstr("\ncat > "); if(*(++file) != ':') sendstr(--file); else sendstr(++file); sendstr("\n"); } while((sectors = read(fd,buffer,BUFSIZ/SECSIZ)) == BUFSIZ/SECSIZ) putbuffer(buffer,sectors); if(sectors) putbuffer(buffer,sectors); close(fd); while(readline(1) != TIMEOUT); if(option == 'P') sendline(CTRLC); else { sendline(CR); sendline(LF); sendline(EOT); } while(readline(10) != TIMEOUT); sendline(CR); sendline(LF); } readfile(file) char *file; { int j, firstchar, sectnum, sectcurr, sectcomp, errors; int checksum; int errorflag, fd; int bufctr; if(view) { showrecv = TRUE; showtrans = FALSE; } option2 = 'A'; fd = creat(file); if(fd == -1) { printf("Cannot create %s\n",file); exit(); } sectnum = 0; errors = 0; bufctr = 0; initializemodem(); sendline(NAK); do { errorflag = FALSE; do firstchar = readline(6); while(firstchar != SOH && firstchar != EOT && firstchar != TIMEOUT); if(firstchar == TIMEOUT) { errorflag = TRUE; printf("SOH timeout\n"); } if(firstchar == SOH) { sectcurr = readline(1); sectcomp = readline(1); if((sectcurr + sectcomp) == 255) { if(sectcurr == sectnum + 1) { checksum = 0; if(option2 == 'A') asciiflg = TRUE; for(j = bufctr;j < (bufctr + SECSIZ);j++) { buffer[j] = readline(1); checksum = (checksum + buffer[j]) & 0xff; } asciiflg = FALSE; if(checksum == readline(1)) { errors = 0; sectnum = sectcurr; bufctr = bufctr + SECSIZ; if(bufctr == BUFSIZ) { bufctr = 0; write(fd,buffer,BUFSIZ/SECSIZ); } if(!showrecv) printf("Received sector %d\n",sectcurr); sendline(ACK); } else { printf("Checksum error, expected <%0x>\n",checksum); errorflag = TRUE; } } else if(sectcurr == sectnum) { do; while(readline(1) != TIMEOUT); printf("Received duplicate sector %d\n", sectcurr); sendline(ACK); } else { printf("Synchronization error\n"); errorflag = TRUE; } } else { printf("Sector number error\n"); errorflag = TRUE; } } if(errorflag == TRUE) { errors++; printf("Error %d\n",errors); while(readline(1) != TIMEOUT); sendline(NAK); } } while(firstchar != EOT && errors != ERRORMAX); if((firstchar == EOT) && (errors < ERRORMAX)) { sendline(ACK); write(fd,buffer,1 + (bufctr/SECSIZ)); close(fd); printf("Transfer complete\n"); } else printf("Aborting\n"); } sendfile(file) char *file; { int j, sectnum, sectors, attempts; int checksum; int bufctr, fd; if(view) { showrecv = FALSE; showtrans = TRUE; } option2 = 'A'; fd = open(file,0); if(fd == -1) { printf("Cannot open %s\n",file); exit(); } initializemodem(); attempts = 0; sectnum = 1; while((sectors = read(fd,buffer,BUFSIZ/SECSIZ)) && (attempts != RETRYMAX)) { if(sectors == -1) printf("\nFile read error\n"); else { bufctr = 0; do { attempts = 0; do { if(!showtrans) printf("\nSending sector %d\n",sectnum); sendline(SOH); sendline(sectnum); sendline(-sectnum-1); checksum = 0; if(option2 == 'A') asciiflg = TRUE; for(j = bufctr;j < (bufctr + SECSIZ);j++) { sendline(buffer[j]); checksum = (checksum + buffer[j]) & 0xff; } asciiflg = FALSE; sendline(checksum); purgeline(); attempts++; } while((readline(10) != ACK) && (attempts != RETRYMAX)); bufctr = bufctr + SECSIZ; sectnum++; sectors--; } while((sectors != 0) && (attempts != RETRYMAX)); } } if(attempts == RETRYMAX) printf("\nNo ACK on sector, aborting\n"); else { attempts = 0; do { sendline(EOT); purgeline(); attempts++; } while((readline(10) != ACK) && (attempts != RETRYMAX)); if(attempts == RETRYMAX) printf("\nNo ACK on EOT, aborting\n"); else printf("\nTransfer complete\n"); } close(fd); } ckfile(argc) int argc; { if(!argc) { printf("File required\n"); exit(); } } main(argc,argv) int argc; char **argv; { char *s; printf("Cmodem (Pmmi) ver 1.3 13-Apr-81\n"); printf(" mod. by Rod Hart\n"); cntrlw2 = 0; asciiflg = hangup = showrecv = showtrans = view = FALSE; if(**++argv != '-') { printf("Command line format: cmodem -options file1 file2 ... file14\n"); printf("Available options are:\n"); printf("\ta: answer t: terminal\n"); printf("\tc: computer u: UNIX\n"); printf("\th: auto-hangup v: view data\n"); printf("\to: originate 1: 110 baud\n"); printf("\tp: PDP10 3: 300 baud\n"); printf("\tr: receive file 4: 450 baud\n"); printf("\ts: send file 6: 600 baud\n"); printf("The p, r, s, and u options require one or more files.\n"); printf("Ctrl-B initiates and terminates data capture in file\n"); printf("cmodem.tmp in the terminal and computer modes.\n"); printf("Ctrl-E is used to initiate file transfers in the UNIX and\n"); printf("PDP10 modes. In the terminal mode Ctrl-E escapes to the\n"); printf("hangup question or CP/M.\n"); printf("Any character escapes the no carrier condition.\n"); exit(); } --argv; while(--argc && **++argv == '-') for(s = *argv+1;*s;s++) switch(*s) { case 'C': case 'P': case 'R': case 'S': case 'T': case 'U': option = *s; break; case 'A': cntrlw0 = cntrlw0 + ANS; break; case 'H': hangup = TRUE; break; case 'V': view = TRUE; break; case 'O': cntrlw0 = cntrlw0 + ORIG; break; case '1': cntrlw2 = BR110; dtrr = UND300; break; case '3': cntrlw2 = BR300; dtrr = UND300; break; case '4': cntrlw2 = BR450; dtrr = OVR300; break; case '6': cntrlw2 = BR600; dtrr = OVR300; break; default: printf("Unimplemented option %c\n\n",*s); exit(); break; } switch(option) { case 'C': case 'T': termcomp(); break; case 'P': case 'U': ckfile(argc); termcomp(); while(argc--) { printf("\nSending %s\n",*argv); pdp10(*argv++); } termcomp(); break; case 'R': ckfile(argc); while(argc--) { printf("\nReceiving %s\n",*argv); readfile(*argv++); } break; case 'S': ckfile(argc); while(argc--) { printf("\nSending %s\n",*argv); sendfile(*argv++); } break; } if(hangup) outp(MODEMCP3,0x00); else { do { printf("\nHangup : Y(es), N(o) ? "); hangup = tolower(getchar()); printf("\n"); } while((hangup != 'y') && (hangup != 'n')); if(hangup == 'y') outp(MODEMCP3,0x00); } }