/* MODEM7.C 26-May-81 final 12-Mar-83 DJ Travis */ #define TITLE "\t\tMODEM7 -:- 12-Mar-83\n\n" /* Written for RT-11 DECUS-C by Dale J. Travis May 1981 If you intend to use this program for high speed (i.e, greater than 300 baud) data transfers, such as over RS-232 lines between two machines directly, then the speed of transfer will be limited by the processors involved instead of the baud rate; UNDER SUCH CIRCUMSTANCES, A TRANSFER WILL ONLY WORK IF THESE TWO CONDITIONS ARE MET: 1) The transfer must always performed in BINARY mode, never in TEXT mode, and 2) The receiving processor must be as fast or FASTER than the transmitting processor. ************************************************* * MODEM7 assumes that your console I/O device * * is much faster than your modem. * ************************************************* "MODEM7" is a program which interacts with a modem to turn your computer into a very versatile terminal. Special commands are entered to the program by typing the character you designate as "SPECIAL", i.e, some character (such as ^A ) which you wouldn't be likely to need transmitted, and then entering the appropriate command letter. Incoming data may be buffered up in RAM memory and dumped to disk when- ever you desire (via the "o", "d", "c" and "k" commands), data may be transmitted from disk to modem (via "t" and "a"), or files can be formally transferred in an alternate "checksum" mode which handles handshaking and buffering automatically when interacting with the same program on the other end of the line. During file transfers, you may temporarily pause and later resume the transmission (via the "p" and "r" commands.) There are also various options you can control (see "n", "7", "h" and "l") to adapt operation toward the type of file you wish to transfer. The "q" command closes the output file (if open) and quits to RT-11. The "s" command displays the status of the program. "z" clears the console screen. Any other command letter (such as, for example, "?") causes a list of legal commands to be displayed. In order to transmit or receive files in the checksum mode, both parties must make sure that their modems are operating in FULL-DUPLEX. When you are in full duplex, then what you type will NOT come right back at you from the modem; the only input you see from the modem is the data transmitted by the machine on the OTHER end of the line. This program considers "half duplex" to be any situation in which the data you transmit comes right back at you; whether it is your modem that is performing the ehoing or a computer system far away doesn't really matter. In any case, checksumming and handshaking is not allowed under half-duplex operation, since erroneous characters would be received. If you switch from half to full or vice-versa while running the program, use the "h" option to inform MODEM7 of the fact. To perform checksummed file transfer, a connection must first be estab- lished between the two parties. If both parties are operating in full duplex, one originating and the other answering, then MODEM7 will both display what each types to the console and send it to the modem. If a file then needs to be transferred, then one user would give the "t" command (to transmit) and the other would give the "o" command (to open an output file.) If both users indicate checksum mode (rather than only one specifing checksum mode which will abort almost immediatly), then MODEM7 will take it from there and perform the transfer. If the sender (transmitter) wants to suspend the transfer temporarily and continue later, he can use the "p" command. When the receiver sees that trans- mission has been suspended (when no data has been sent for a long time), then he gives the "p" command also, and both users may type to each other. When ready to resume, the "r" command must be given by the RECEIVER first, and then the sender, to prevent data from being lost. */ int $$narg=1; /* Suppress input prompt */ #include /* System I/O */ FILE *rfd, *tfd; /* File descriptors */ #define SPECIAL 01 /* Special char to signal MODEM7 command */ /* The following #defines need not be changed: */ #define ACK 006 /* Ascii ACK for handshaking */ #define NAK 025 /* Ascii NAK for handshaking */ #define EOT 004 /* End of transmission (EOT) */ #define ETX 003 /* Abort Transmission (ETX) */ #define CTRLC 003 /* CTRL-C character */ #define SOH 001 /* Start of text (SOH) */ #define SECSIZ 128 /* Sector size (CPM) */ /* External variable declarations: */ char answ; /* ACK or NAK on last record */ char rflag; /* receiving file open flag */ char tflag; /* transmitting file open flag */ char chflag; /* checksumming enabled flag */ char cflag; /* text-collection enabled flag */ char pflag; /* pausing flag */ char spflag; /* stripping parity bit flag */ char nflag; /* recognizing nulls flag */ char fflag; /* true if changing CR-LF's into just CR when transmitting */ char lastc; /* last char xmitted */ char dodflag; /* true if displaying outging data */ char didflag; /* true if displaying incoming data */ char hdflag; /* true if working in half-duplex. ie. local echo & no echo from the modem */ char cpuflag; /* true if emulating a CPU. ie. does local echo & echos to the modem */ char dec; /* true if using as DEC computer console to issue X-ON/X-OFF while write to disk*/ char abortf; /* true when file I/O aborted */ char sbuf[SECSIZ+10]; /* sector buffer */ char rname[20]; /* name of receiving file */ char tname[20]; /* name of transmitting file */ char *cptr; /* pointer to free space in buf */ unsigned bfree; /* number of bytes free in buf */ unsigned *mistat; /* modem input status word */ unsigned *midat; /* modem input data buffer */ unsigned *mostat; /* modem output status word */ unsigned *modat; /* modem output data buffer */ int bcount; /* counts bytes in current block when checksumming */ int scount; /* Number of sectors sent/received */ int sc; /* MSB sector count (# 255's) */ int checksum; /* the checksum value itself */ char timoutf; /* true if time-out happens while waiting for modem data */ char *i; /* odd-job char pointer */ int dod_sav, did_sav; /* scratch variables */ int hdf_sav; /* " */ unsigned bufspace; /* # of bytes available for text collection buffer in ram */ char *buf; /* text collection pointer; will point to text buffer */ main() { char c, c2; FILE *fopen(); if (tsxis()) { /* If TSX is running */ tsxrt(); /* Map I/O page in */ printf ("\035S\035U"); /* Single char activation */ } init(); /* Initialize all the stuff */ loop: if (abortf) { /* Want to abort? */ if (rflag) rclose(); /* Close read file */ if (tflag) tabort(); /* Abort transmit file */ abortf = 0; /* Clear the abort flag */ } if (tflag && xmit()) { /* Transmit a block */ printf("\nTransmission of %s complete.\n", tname); fclose(tfd); /* Close the file */ reset(); /* Reset all the flags */ } if (abortf) goto loop; if (rflag && chflag && recv()) {/* Receive a block */ printf("\nReception of %s complete.\n", rname); rclose(); /* Close the file */ } if (abortf) goto loop; if (miready()) { /* Is char on modem? */ c = c2 = getmod(); /* Get the char from modem */ if (spflag) c &= '\177'; /* Use lower 7 bits only */ if (tflag && (c == ETX)) { /* Has receiver aborted? */ printf("Reciever has aborted.\n"); abortf = 1; /* Receiver has aborted */ goto loop; /* Loop-de-loop */ } if (didflag && (c || nflag)) display(c); /* Char to console */ if (cpuflag) outmod(c); /* Echo char back to modem */ if (cpuflag && (c == '\r')) { display('\n'); /* Echo LF for CR if CPU mode */ outmod ('\n'); /* Echo it to modem too */ } if (cflag && !pflag) { /* Are we collecting data? */ if (c || nflag) { tobuf(c2); /* Char to buffer */ if (cpuflag && (c == '\r')) tobuf('\n'); /* The CPU inserts the LF's */ } } } if (cins()) { /* Check for char on console */ c = cind(); /* Get char from console */ if (c != SPECIAL) { /* Want to input a command? */ if (c == '\177' && !dec) c = '\10'; /* Convert rub to backspace*/ if (pflag || (!tflag && !(rflag && chflag))) { if (!(fflag && (c == '\n') && (lastc == '\r'))) outmod(c); /* Char to modem */ lastc = c; /* Save as last char to modem */ if (hdflag || cpuflag) display(c); /* Echo to console */ } if (!tflag && rflag && !pflag && cpuflag) tobuf(c); /* Char to buffer */ } else special(); /* Go do special char processing */ } goto loop; /* Loop-de-loop */ } /* Routine to return true if input is present on the modem: */ miready() { return (*mistat & 0200); } /* Routine to return true if modem is ready to output a byte: */ moready() { return (*mostat & 0200); } /* Put the char into the buffer & dump the buffer when needed. Handle all the checksumming if needed, etc. */ tobuf(c) char c; { char cbuf[10]; /* Character buffer to save modem chars */ int j, nc; /* Number of chars we save */ *cptr++ = c; /* Plunk char in buffer */ bcount++; /* Inc buffer count */ bfree--; /* Dec free counter */ if (bcount == SECSIZ) { /* Need to dump buffer? */ /*******************************************/ outmod (023); /* CTRL-S to the modem */ nc = 0; /* Init # chars saved */ for (j=0; j<4000; j++) { /* Set for modem time out */ if (miready()) /* Char ready from modem */ cbuf[nc++] = getmod(); /* Get char from modem */ } /*******************************************/ bcount = 0; /* Yep, re-init count */ rdump(0); /* Write block on disk */ /*******************************************/ for (j=0; j= ' ') { putchar(c); /* Printables gets printed */ return; } if ((c == '\r') || (c == '\n') || (c == 033) || (c == 010) || (c == 007)) putchar(c); /* Also CR, LF, ESC, BKSP, BELL gets printed */ } /* Receive a checksummed block of data: 1st byte = SOH, 2nd byte = block #, 3rd byte = 1's compliment of block #, 128 bytes of data follow, then 1 byte checksum. If file is done, EOT instead of SOH. */ recv() { char c, c2; int psec, n; outmod(answ); /* Send response to other end */ if (hang(1, SOH, EOT) == -1) return(0); /* Transmitter aborted */ if (i == EOT) { outmod(ACK); /* ACK the EOT */ return(1); /* That's all there is */ } n = SOH; /* Start here */ psec = scount; /* Save for sector check */ scount = getmod(); /* Sector # */ n += scount; /* Keep checksum */ n = ((n+getmod()) & 0377); /* + 1's compliment sector # */ checksum = n; /* Save sum so far */ cptr = buf; /* Init buffer pointer */ for (bcount=0; bcount < SECSIZ; bcount++){/*SECSIZ bytes of data now*/ c = c2 = getmod(); /* Get a char */ if (abortf) return(1); /* Someone might play with kybd */ if (spflag) c &= 0177; /* Lower 7 bits only */ if (didflag && (c || nflag)) display(c); *cptr++ = c2; /* Plunk char in buffer */ checksum += c2; /* Keep checksum */ } i = getmod(); /* Get the checksum from modem */ if (!n && ((checksum & 0377) == i) && (psec == scount)) { rdump(0); /* Write block to disk */ if (!didflag) printf("Good sector <%d>\n", sc+scount); answ = ACK; /* ACK says hunky-dory */ if (++scount > 255) { /* Next sequential sector # */ scount = 0; /* Sector count back to 0 */ sc += 256; /* Next round-de-round */ } } else{ printf("\nChecksum error. Retrying <%d>\n", sc+scount); answ = NAK; /* Send it to me again! */ } return(0); /* Return not done */ } /* Transmit a block of data. Append header as follows: SOH, 8 bit block #, 8 bit 1's compliment of block #, 128 data bytes, 8 bit sum of previous 131 bytes. */ xmit() { char c; cptr = buf; /* Init buffer pointer */ if (chflag) { /* If we're checksumming */ outmod(SOH); /* SOH first */ outmod(++scount); /* Block # */ outmod(~scount); /* 1's compliment block # */ checksum = 0; /* Init the checksum */ } for (bcount=0; bcount < SECSIZ; bcount++) { c = *cptr++; /* Char from buffer */ checksum += c; /* Keep checksum */ if (chflag || !(fflag && (c == '\n'))) outmod(c); /* Char to modem */ if (dodflag) display(c); /* Char to console?? */ } if (!chflag) return (!read1()); outmod(checksum); /* Send the checksum */ if (hang(0, ACK, NAK) == -1) return(0); /* Receiver aborted */ if (i == ACK) { if (!dodflag) printf("Good sector <%d>\n", scount); if (read1()) return(0); /* Keep sending */ outmod(EOT); /* Done with file */ return(1); } /* Assume NAK here */ printf("\n\7Error. Resending sector %d.\n", scount--); return (0); } /* If parameter 1 from modem return 1; if parameter 2, return 0. Do 10 reads on the modem. If something other that par 1 or par 2 is seen on the modem, it will be thrown away. If after 10 tries and neither item is seen on the modem, return -1. */ hang(x, y, z) char x, y, z; { int m, n; for (m=0; m<10; m++) { if (x && m) outmod(NAK); /* If we're looping, NAK*/ for (n=0; n<5000; n++) { if (miready()) { if ((i=getmod()) == y) return(1); /* Wait for ACK */ if (i == z) return(0); /* or NAK */ } else if (kbabort()) m = 10; /* Quit */ } printf("wait %d\n", m); } printf("aborting.\n"); abortf = 1; /* Abort */ return(-1); } /* Read a sector of the file to be transmitted. Must return 0 for EOF, SECSIZ for good read. */ read1() { int j; if ((j=read()) < 0) { printf("\nRead error from %s; Aborting.\n", tname); tabort(); j = 0; /* Make look like EOF now */ } return(j); } /* Put SECSIZ bytes into sbuf. Return # of bytes in sbuf if read was ok, otherwise return 0 for EOF or -1 for error. */ read() { int j; for (j=0; j