home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / vol_100 / 157_01 / cmodem.c < prev    next >
Text File  |  1987-10-12  |  31KB  |  1,059 lines

  1. /*  HEADER:     CUGaaa.bb;
  2.     FILENAME:   cmodem.c;
  3.     TITLE:      Xmodem communications in c;
  4.     COMPILERS:  C86;
  5.     SYSTEM:     ms-dos;
  6.     AUTHORS:    Bill Rogers;
  7.     VERSION:    5.01;
  8.     DATE:       08/04/1985;
  9.     KEYWORDS:   commnications, xmodem, ms-dos, pc-dos, 8251a, 8530;
  10.     SEE-ALSO:   local.h;
  11.     DESCRIPTION:
  12.         "This program implements an elementary version of xmodem for
  13.         under ms-dos or pc-dos.";
  14.     WARNINGS:
  15.         "The zilog usart option is not yet tested.";
  16.  */
  17. /*      The calling sequence is:
  18.  *
  19.  *          cmodem
  20.  *
  21.  *      The control sequences are:
  22.  *
  23.  *          <ctrl-z><ctrl-z>    send <ctrl-z>
  24.  *          <ctrl-z>b300        set baud rate to 300 baud
  25.  *          <ctrl-z>b1200       set baud rate to 12000 baud
  26.  *          <ctrl-z>d           dial telephone number
  27.  *          <ctrl-z>e           exit without hangup
  28.  *          <ctrl-z>h           hangup without exit
  29.  *          <ctrl-z>i           initialize port & baud rate
  30.  *          <ctrl-z>nx...x      enter telephone number x...x
  31.  *          <ctrl-z>q           quit with hangup
  32.  *          <ctrl-z>rx...x      receive file name x...x
  33.  *          <ctrl-z>sx...x      send file name x...x
  34.  *          <ctrl-z>t           trace file transmissions
  35.  *          <ctrl-z>?           display commands
  36.  *
  37.  *      Revision history: Latest revision first.
  38.  *
  39.  *      12/Dec/84   Changed to C86 (Bill Rogers).
  40.  *      04/Jun/82   Removed PMMI defines and ifdef for Australia.  Much 
  41.  *                  more  extensive  documentation  added.  Modem  port 
  42.  *                  defines now obtained from BDSCIO.H.  Code tidied up 
  43.  *                  a  bit  and VARBAUD  ifdefs  added.  Some  messages 
  44.  *                  changed and numerous small changes ( Bill Bolton).
  45.  *      ??/???/81   Patched together from existing Cnet and YAM code
  46.  *                  (Steve Passe, Cnode SYSOP, USA).
  47.  *  SCCS IDENTIFICATION */
  48.         static char SCCSID[] = "@(#)cmodem.c  5.0.1";
  49. /*--------------------------------------------------------------------*/
  50. /*  INCLUDE */
  51. #define  EXTERN extern
  52. #include <local.h>
  53. #undef   EXTERN
  54. /*--------------------------------------------------------------------*/
  55. /*  DEFINE */
  56.  
  57. /*  Select USART interface type. */
  58.  
  59. #define     OCTAPORT            /* Lomas HAZITALL or Lomas OCTAPORT */
  60.  
  61. /*  ascii characters. */
  62.  
  63. #define     NUL         0x00
  64. #define     SOH         0x01
  65. #define     EOT         0x04
  66. #define     ACK         0x06
  67. #define     HT          0x09
  68. #define     LF          0x0a
  69. #define     CR          0x0d
  70. #define     NAK         0x15
  71. #define     CAN         0x18
  72. #define     SUB         0x1a    /*  ^Z */
  73. #define     ESC         0x1b
  74.  
  75. /*  Escape character for control. */
  76.  
  77. #define     ESCCHAR     0x1a    /*  ^Z */
  78.  
  79. #ifdef      HAZITALL
  80.  
  81. /*  Intel 8253 programmable interval timer definition. */
  82.  
  83. #define     CLOAD       0x8d    /*  load port. */
  84. #define     CCTRL       0x0f    /*  control port. */
  85. #define     CCTRLB      0x76    /*  select counter 1,
  86.                                     load lsb, then msb,
  87.                                     square wave generator,
  88.                                     binary counter. */
  89. #define     B0300       417     /*   300 baud. */
  90. #define     B1200       104     /*  1200 baud. */
  91.  
  92. /*  Intel 8251a serial port definition. */
  93.  
  94. #define     MDATA       0x84    /*  Data port. */
  95. #define     MSTAT       0x85    /*  Status port. */
  96. #define     MOMASK      0x01    /*  Output ready mask. */
  97. #define     MIMASK      0x02    /*  Input ready mask. */
  98.  
  99. #define     MCMD1       0x40    /*  Internal reset. */
  100. #define     MMODE       0x6e    /*  One stop bit,
  101.                                     even parity,
  102.                                     disable parity,
  103.                                     8 bit character length,
  104.                                     16x baud rate factor. */
  105. #define     MCMD2       0x37    /*  NA,
  106.                                     internal reset disabled,
  107.                                     rts enabled,
  108.                                     error reset enabled,
  109.                                     break disabled,
  110.                                     receive enabled,
  111.                                     dtr enabled,
  112.                                     send enabled. */
  113.  
  114. #endif
  115.  
  116. #ifdef      OCTAPORT
  117.  
  118. /*  Zilog 8530 serial port definition, including timer. */
  119.  
  120. #define     B0300       830
  121. #define     B1200       206
  122.  
  123. #define     MDATA       0x20    /*  Data port. */
  124. #define     MSTAT       0x21    /*  Status port. */
  125. #define     MCMND       0x22    /*  Command port. */
  126. #define     MOMASK      0x04    /*  Output ready mask. */
  127. #define     MIMASK      0x01    /*  Input ready mask. */
  128.  
  129. #define     MDEFSNUM    10
  130. static unsigned char MDEFS[MDEFSNUM] = {
  131.     0x09,   /*  Select wr09 - master interrupt control. */
  132.     0xc0,   /*      7-6: force hardware reset. 
  133.                     5-0: - */
  134.     0x0f,   /*  Select wr15 - external status/control. */
  135.     0x40,   /*      7-0: Disable all interrupts. */
  136.     0x04,   /*  Select wr04 - receiver and transmitter control. */
  137.     0x44,   /*      7-6: x16 clock,
  138.                     5-4: 0
  139.                     3-2: 1 stop bits,
  140.                     1-1: even parity,
  141.                     0-0: no parity. */
  142.     0x05,   /*  Select wr05 - transmitter control. */
  143.     0xea,   /*      7-7: assert dtr,
  144.                     6-5: transmit 8 bits,
  145.                     4-4: disable break,
  146.                     3-3: enable transmit. 
  147.                     2-2: 0
  148.                     1-1: assert rts. 
  149.                     0-0: - */
  150.     0x03,   /*  Select wr03 - receiver control. */
  151.     0xc1    /*      7-6: receive 8 bits,
  152.                     5-5: no autoenables,
  153.                     4-1: 0,
  154.                     0-0: enable receive. */
  155. };
  156.  
  157. #define     MBAUDNUM    10
  158. #define     MBAUDLOW    3       /*  Index to baudrate low byte. */
  159. #define     MBAUDHIGH   5       /*  Index to baudrate high byte. */
  160. static unsigned char MBAUD[MBAUDNUM] = {
  161.     0x0e,   /*  Select wr14- miscellaneous control. */
  162.     0x00,   /*      7-1: -
  163.                     0-0: Disable baudrate generator. */
  164.     0x0c,   /*  Select wr12 - low byte of baudrate constant. */
  165.     11,     /*      7-0: 1200 baud. */
  166.     0x0d,   /*  Select wr13 - high byte of baudrate constant. */
  167.     0,      /*      7-0: 1200 baud. */
  168.     0x0b,   /*  Select wr11 - clock mode control. */
  169.     0x56,   /*      7-7: no crystal,
  170.                     6-5: receive clock = baudrate generator output,
  171.                     4-3: transmit clock = baudrate generator output,
  172.                     2-2: -
  173.                     1-0: trxc-out=baudrate generator. */
  174.     0x0e,   /*  Select wr14 - miscellaneous control. */
  175.     0x03    /*      7-2: -
  176.                     1-1: pclk source of clock,
  177.                     0-0: Enable baudrate generator. */
  178. };
  179. #endif
  180.  
  181. /*  Hayes modem command strings. */
  182.  
  183. #define     ESCAPE_STR  "+++"
  184. #define     HANGUP_STR  "AT H0 \r"
  185. #define     DIAL_STR    "AT DT "
  186. #define     CR_STR      "\r"
  187.  
  188. /*  DOS interrupts. */
  189.  
  190. #define     DIR_IO      0x06    /*  DOS function request - direct i/o */
  191. #define     INPUT       0xff    /*  DOS function sub-request - accept */
  192.  
  193. /*  Flag values. */
  194.  
  195. #define     OK          ( 0)
  196. #define     ERROR       (-1)
  197. #define     WCEOT       (-2)
  198. #define     TIMEOUT     (-3)
  199. #define     EMPTY       (-4)
  200.  
  201. #define     SECSIZ      128     /*  Block size. */
  202.  
  203. #define     RETRYMAX    10
  204. #define     PATHLEN     64      /*  Send/receive file pathname length */
  205. #define     CLKMHZ      8       /*  CPU speed in Mhz */
  206. #define     TIMECON     311     /*  Constant to convert CLKMHZ to ticks.
  207.                                  */
  208.  
  209. #define     CONSTAT     2
  210. #define     CONIN       3
  211. #define     NORMAL      0x1c
  212. #define     ORIG        0x01
  213. #define     ANSWER      0x02
  214. #define     READY       0x5f
  215. #define     OPT300      0x20
  216. #define     OPT600      0x00
  217. #define     CLEAR       0x3f
  218. /*--------------------------------------------------------------------*/
  219. static int          BAUDRATE;
  220. static string       PHONENUM[33] = "1,201-866-2258";
  221. static bool         TRACEFLAG    = false;
  222.  
  223. static string       YN[65];
  224. static bool         SENDOPEN;             
  225. static bool         RCVOPEN;          
  226. static string       SENDNAME[PATHLEN + 1];    
  227. static string       RCVNAME[PATHLEN + 1]; 
  228. static file        *SENDFILE;      
  229. static file        *RCVFILE;   
  230.  
  231. static int          T1PAUSE;
  232. static char         CHECKSUM;
  233. static char         LASTRX;
  234. static int          WCJ;
  235. static int          FIRSTCH;
  236.  
  237. extern file        *fopen();
  238. /*--------------------------------------------------------------------*/
  239. static void DELAY(P_DELAY)
  240.  
  241.     int P_DELAY;                /*  In. */
  242.  
  243.     /*  Delay, in units of 0.01 sec. */
  244. {
  245.     static int  TICKS = 465;   /*  For 8mhz 8086. */
  246.     int         I;
  247.     int         J;
  248.     int         X = 0;
  249. /*  begin */
  250.     for (I = 0 ; I < P_DELAY ; I++ ) {
  251.         for (J = 0 ; J < TICKS ; J++) {
  252.             X = X;
  253.         }
  254.     }
  255. }
  256. /*--------------------------------------------------------------------*/
  257. static void PRNALLCH(P_C)
  258.  
  259.     metachar P_C;               /*  In. */
  260.  
  261.     /*  Print character. 
  262.  
  263.         Char      Print
  264.  
  265.         eof       ^* 
  266.         < 0       ^:
  267.         control   ^x  (x = '@'...'_')
  268.         tilde     ^>
  269.         del       ^?
  270.         >del      .
  271.     */
  272. {
  273. /*  begin */
  274.     if (P_C == -1) {
  275.         putchar('^');
  276.         putchar('*');
  277.     } else if (P_C < 0) {
  278.         putchar('^');
  279.         putchar(':');
  280.     } else if (P_C == LF) {
  281.         putchar(P_C);
  282.     } else if (P_C == CR) {
  283.         putchar(P_C);
  284.     } else if (P_C < ' ') {
  285.         putchar('^');
  286.         putchar(P_C + 0x40);
  287.     } else if (P_C <= '}') {
  288.         putchar(P_C);
  289.     } else if (P_C <= 0x7f) {
  290.         putchar('^');
  291.         putchar(P_C + 0x20);
  292.     } else {
  293.         putchar('.');
  294.     }
  295. }
  296. /*--------------------------------------------------------------------*/
  297. static metachar IN_MODEM()
  298.  
  299.     /*  Receive byte from modem port. */
  300. {
  301.     metachar        RTN;
  302.     extern metachar inportb();
  303. /*  begin */
  304.     if (inportb(MSTAT) & MIMASK) {      /*  status & char ready bit */
  305.         RTN = inportb(MDATA) & 0x00ff;  /*  ok, get the char */
  306.     } else {
  307.         RTN = EMPTY;                    /*  return empty */
  308.     }
  309.     return RTN;
  310. }
  311. /*--------------------------------------------------------------------*/
  312. static void OUT_MODEM(P_OUT_CHAR)
  313.  
  314.     char P_OUT_CHAR;
  315.  
  316.     /*  Send byte to modem port. */
  317. {
  318.     extern metachar inportb();
  319.     extern void     outportb();
  320. /*  begin */
  321.     while ( ! (inportb(MSTAT) & MOMASK)) {
  322.         ;                                /* wait */
  323.     }
  324.     outportb(MDATA,P_OUT_CHAR);    /* finally, send it */
  325. }
  326. /*--------------------------------------------------------------------*/
  327. static char GETCH()
  328.  
  329.     /*  Accept character from keyboard,
  330.         no wait, no ^C, no echo, no buffer flush. */
  331. {
  332.     char            RTN;
  333.     extern metachar bdos();
  334. /*  begin */
  335.     RTN = bdos(DIR_IO,INPUT) & 0x007f;
  336.     return RTN;
  337. }
  338. /*--------------------------------------------------------------------*/
  339. static metachar READBYT(P_DELAY)
  340.     
  341.     int P_DELAY;
  342.  
  343.     /*  Receive byte. Delay in units of 0.01 sec. */
  344. {
  345.     metachar        RTN;
  346.     static int      TICKS = 10;     /* For 8mhz 8086. */
  347.     int             I;
  348.     int             J;
  349.     extern metachar inportb();
  350. /*  begin */
  351.     for (I = 0 ; I < P_DELAY ; I++) {
  352.         for (J = 0 ; J < TICKS ; J++) {
  353.             RTN = IN_MODEM();
  354.             if (RTN  != EMPTY) {
  355.                 return RTN;
  356.             }
  357.             if (GETCH()) {      /*  Local forced timeout. */
  358.                 RTN = TIMEOUT;
  359.                 return RTN;
  360.             }
  361.         }
  362.     }
  363.     RTN = TIMEOUT;
  364.     return RTN;
  365. }
  366. /*--------------------------------------------------------------------*/
  367. static void DELAY_ECHO(P_DELAY)
  368.  
  369.     int P_DELAY;
  370.  
  371.     /*  Wait for echo, and echo to screen. Delay in units of 0.01 sec.*/
  372. {
  373.     static int  TICKS = 130;    /*  For 8mhz 8086. */
  374.     int         I;
  375.     int         J;
  376.     int         X = 0;
  377.     metachar    C;
  378.     metachar    IN_MODEM();
  379. /*  begin */
  380.     for (I = 0 ; I < P_DELAY ; I++) {
  381.         for (J = 0 ; J < TICKS ; J++) {
  382.             C = IN_MODEM();
  383.             if (C != EMPTY) {
  384.                 if (C != TIMEOUT) { 
  385.                     PRNALLCH(C);
  386.                 }
  387.             }
  388.         }
  389.     }
  390. }
  391. /*--------------------------------------------------------------------*/
  392. static void OUT_STR(P_OUT_STR)    
  393.  
  394.     string  P_OUT_STR[];
  395.  
  396.     /*  Send string to modem port. */
  397. {
  398.     int         I;
  399.     metachar    C;
  400. /*  begin */
  401.     I = 0;
  402.     while (P_OUT_STR[I] != '\0') {
  403.         OUT_MODEM(P_OUT_STR[I]);
  404.         C = READBYT(10);
  405.         if ( ! ((C == EMPTY) || (C == TIMEOUT))) {
  406.             PRNALLCH(C);
  407.         }
  408.         I++;
  409.     }
  410. }
  411. /*--------------------------------------------------------------------*/
  412. static void PURGELINE()
  413.  
  414.     /*  Drain modem input. */
  415. {
  416.     metachar        TEMP;
  417.     extern metachar inportb();
  418. /*  begin */
  419.     while (inportb(MSTAT) & MIMASK) {
  420.         TEMP = inportb(MDATA);
  421.     }
  422. }
  423. /*--------------------------------------------------------------------*/
  424. static int  FILBUF(P_BUF,P_COUNT)
  425.  
  426.     char       *P_BUF;
  427.     int         P_COUNT;
  428.  
  429.     /*  fill block from send file with count chars padding with ^Z for 
  430.         CPM */
  431. {
  432.     metachar        C;
  433.     int             M;
  434.     extern metachar fgetc();
  435. /*  begin */
  436.     M = P_COUNT;
  437.     while ((C = fgetc(SENDFILE)) != EOF) {
  438.         *P_BUF++ = C;
  439.         if (--M == 0) {
  440.             break;
  441.         }
  442.     }
  443.     if (M == P_COUNT) {
  444.         return 0;
  445.     } else {
  446.         
  447.         /*  won't pad properly under cp/m because of two different EOF's
  448.             (EOF & CPMEOF), and the fact that cp/m doesn't know type of 
  449.             file instinctively, fix later */
  450.  
  451.         while(--M >= 0) {
  452.             *P_BUF++ = SUB;         /*  ^Z. */
  453.         }
  454.     }
  455.     return P_COUNT;
  456. }
  457. /*--------------------------------------------------------------------*/
  458. static int  OPENTX()
  459.  
  460.     /*  Open send file. */
  461. {
  462.     int RTN;
  463. /*  begin */
  464.     SENDFILE = fopen(SENDNAME,opnreadb);
  465.     if (SENDFILE == null) {
  466.         printf("\n**** Send File '%s' Failed to Open ****\n",SENDNAME);
  467.         RTN = ERROR;
  468.     } else {
  469.         printf("\n#### Send File '%s' Opened ####\n",SENDNAME);
  470.         SENDOPEN = true;
  471.         RTN = OK;
  472.     }
  473.     return (RTN);
  474. }
  475. /*--------------------------------------------------------------------*/
  476. static void CLOSETX()
  477.  
  478.     /*  Close send file. */
  479. {
  480.     int FLAG;
  481. /*  begin */
  482.     if (SENDOPEN) {
  483.         FLAG = fclose(SENDFILE);
  484.         if (FLAG == ERROR) {
  485.             printf("\n**** Send File '%s' Failed to Close ****\n",
  486.                 SENDNAME);
  487.             exit(1);
  488.         } else {
  489.             printf("\n#### Send File '%s' Closed ####\n",SENDNAME);
  490.             SENDOPEN = false;
  491.         }
  492.     }
  493. }
  494. /*--------------------------------------------------------------------*/
  495. static int  OPENRX()
  496.  
  497.     /*  Open receive file. */
  498. {
  499.     int RTN;
  500. /*  begin */
  501.     RCVFILE = fopen(RCVNAME,opnreadb);
  502.     if (RCVFILE != null) {
  503.         fclose(RCVFILE);
  504.         printf(
  505.     "\n**** Receive File '%s' Already Exists, Try Another Name ****\n",
  506.             RCVNAME);
  507.         RTN = ERROR;
  508.     } else {
  509.         RCVFILE = fopen(RCVNAME,crewritb);
  510.         if (RCVFILE == null) {
  511.             printf("\n**** Receive File '%s' Cannot Be Created ****\n",
  512.                 RCVNAME);
  513.             RTN = ERROR;
  514.         } else {
  515.             printf("\n#### Receive File '%s' Opened ####\n",
  516.                 RCVNAME);
  517.             RCVOPEN = true;
  518.             RTN = OK;
  519.         }
  520.     }
  521.     return (RTN);
  522. }
  523. /*--------------------------------------------------------------------*/
  524. static void CLOSERX()
  525.  
  526.     /*  Close receive file. */
  527. {
  528.     int FLAG;
  529. /*  begin */
  530.     if (RCVOPEN) {
  531.         fflush(RCVFILE);
  532.         FLAG = fclose(RCVFILE);
  533.         if (FLAG == ERROR) {
  534.             printf("\n**** Receive File '%s' Failed to Close ****\n",
  535.                 RCVNAME);
  536.             exit(1);
  537.         } else {
  538.             printf("\n#### Receive File '%s' Closed ####\n",
  539.                 RCVNAME);
  540.             RCVOPEN = false;
  541.         }
  542.     }
  543. }
  544. /*--------------------------------------------------------------------*/
  545. static int  WCPUTSEC(P_TXBUF,P_SECTNUM)
  546.  
  547.     char   *P_TXBUF;
  548.     int     P_SECTNUM;
  549.  
  550.     /*  Send block. */
  551. {
  552.     char    ATTEMPTS;
  553.     int     I;
  554.     char    XBYT;
  555. /*  begin */
  556.     FIRSTCH = 0;    /* part of logic to detect CAN CAN */
  557.     for (ATTEMPTS = 0 ; ATTEMPTS <= RETRYMAX ; ATTEMPTS++) {
  558.         LASTRX = FIRSTCH;
  559.         OUT_MODEM(SOH);
  560.         OUT_MODEM(P_SECTNUM);
  561.         OUT_MODEM(~P_SECTNUM);  /*  <tilde>P_SECTNUM */
  562.         CHECKSUM = 0;
  563.         for (I = 0 ; I < SECSIZ ; I++) {
  564.             OUT_MODEM(P_TXBUF[I]);
  565.             if (TRACEFLAG) {
  566.                 PRNALLCH(P_TXBUF[I]);
  567.             }
  568.             CHECKSUM = CHECKSUM + P_TXBUF[I];
  569.         }
  570.         OUT_MODEM(CHECKSUM);
  571.         PURGELINE();
  572.  
  573.         FIRSTCH = READBYT(1000);
  574.         if (FIRSTCH == CAN && LASTRX == CAN) {
  575. CANCAN:
  576.             printf("\n#### Receiver CANcelled Sending ####\n");
  577.             return ERROR;
  578.         } else if (FIRSTCH == ACK) {
  579.             return OK;
  580.         } else if (FIRSTCH == TIMEOUT) {
  581.             printf("\n**** Timeout on Block ACK Attempt %4d ****\n",
  582.                 ATTEMPTS);
  583.         } else {
  584.             printf("\n**** Got '%02x' for Block ACK Attempt %4d ****\n",
  585.                 FIRSTCH,ATTEMPTS);
  586.             for( ; ; ) {
  587.                 LASTRX = FIRSTCH;
  588.                 if ((FIRSTCH = READBYT(10)) == TIMEOUT) {
  589.                     break;
  590.                 }
  591.                 if (FIRSTCH == CAN && LASTRX == CAN) {
  592.                     goto CANCAN;
  593.                 }
  594.             }
  595.         }
  596.     }
  597.     printf("\n**** No ACK on Block - Abort ****\n");
  598.     return ERROR;
  599. }
  600. /*--------------------------------------------------------------------*/
  601. static int  WCTX()
  602.  
  603.     /*  Send. */
  604. {
  605.     int     SECTNUM;
  606.     char    ATTEMPTS;
  607.     char    TXBUF[SECSIZ + 1];
  608. /*  begin */
  609.     printf("\n#### Awaiting Initial NAK . . . ####\n");
  610.     while ((FIRSTCH = READBYT(1000)) != NAK     && 
  611.             FIRSTCH                 != TIMEOUT && 
  612.             FIRSTCH                 != CAN) {
  613.         printf("\n**** Got '%02x', not NAK ****\n",FIRSTCH);
  614.                                 /*  let user see it if strange char */
  615.     }
  616.     if (FIRSTCH == CAN) {
  617.         return ERROR;
  618.     }
  619.     if (FIRSTCH == TIMEOUT) {
  620.         printf("\n**** Timeout on Initial NAK! ****\n");
  621.         return ERROR;
  622.     }
  623.     SECTNUM = 1;
  624.     while (FILBUF(TXBUF,SECSIZ)) {
  625.         printf("\n#### Send Block #%4d ####\n",SECTNUM);
  626.         if (WCPUTSEC(TXBUF,SECTNUM) == ERROR) {
  627.             return ERROR;
  628.         } else {
  629.             SECTNUM++;
  630.         }
  631.     }
  632.     ATTEMPTS = 0;
  633.     do {
  634.         printf("\n#### Sending EOT . . . ####\n");
  635.         OUT_MODEM(EOT);
  636.         PURGELINE();            /*  why? */
  637.         ATTEMPTS++;
  638.     } while ((FIRSTCH = (READBYT(1000)) != ACK) && ATTEMPTS < RETRYMAX);
  639.                                 /*  wait for ACK */
  640.  
  641.     if (ATTEMPTS == RETRYMAX) {
  642.         printf("\n**** No ACK on EOT, Abort ****\n");
  643.         return ERROR;
  644.     } else {
  645.         return OK;
  646.     }
  647. }
  648. /*--------------------------------------------------------------------*/
  649. static void UPLOAD()
  650.  
  651.     /*  Send from local computer to remote computer. */
  652. {
  653.     int I;
  654. /*  begin */
  655.     if (OPENTX() == ERROR) {
  656.         ;
  657.     } else {
  658.         if (WCTX() == ERROR) {
  659.             printf("\n**** Abort ****\n");
  660.             for (I = 0 ; I < 10 ; I++) {
  661.                 OUT_MODEM(CAN);
  662.             }
  663.         }
  664.         CLOSETX();
  665.     }
  666. }
  667. /*--------------------------------------------------------------------*/
  668. static int  WCGETSEC(P_RXBUF,P_TIME)
  669.  
  670.     char   *P_RXBUF;
  671.     int     P_TIME;
  672.     
  673.     /*  This routine fetches a Ward Christensen type block.
  674.         Returns block number encountered or ERROR if valid block not 
  675.         received,
  676.         or CAN CAN received
  677.         or WCEOT if eot block
  678.         P_TIME is timeout for first char, set to 4 seconds thereafter
  679.                 ***** NO ACK IS SENT IF SECTOR IS RECEIVED OK ****
  680.         (Caller must do that when he is good and ready to get next 
  681.         block). */
  682. {
  683.     int     SECTCURR;
  684.     int     ERRORS;
  685.     char   *CP;
  686.     int     I;
  687. /*  begin */
  688.     for (LASTRX = ERRORS = 0 ; ERRORS < RETRYMAX ; ERRORS++) {
  689.         do {
  690.             FIRSTCH = READBYT(P_TIME);
  691.         } while (FIRSTCH != SOH     && 
  692.                  FIRSTCH != TIMEOUT && 
  693.                  FIRSTCH != EOT     && 
  694.                  FIRSTCH != CAN);   /*  wait for one of these */
  695.         if (FIRSTCH == SOH) {
  696.             SECTCURR = READBYT(10);
  697.             if ((SECTCURR + READBYT(10)) == 255) {
  698.                 CHECKSUM = 0;
  699.                 for (CP = P_RXBUF,WCJ = 128 ; --WCJ >= 0 ; ) {
  700.                     *CP = READBYT(10);
  701.                     if (TRACEFLAG) {
  702.                         PRNALLCH(*CP);
  703.                     }
  704.                     CHECKSUM += (*CP++);
  705.                 }
  706.                 if  (((CHECKSUM - READBYT(10)) & 0xff) == 0) {
  707.                     return SECTCURR;
  708.                 } else {
  709.                     printf("\n**** Checksum Error - Errors %4d ****\n",
  710.                         ERRORS);
  711.                 }
  712.             } else {
  713.             printf("\n**** Block Number Garbled - Errors %4d ****\n",
  714.                     ERRORS);
  715.             }
  716.         } else if (FIRSTCH == EOT) {
  717.             printf("\n#### Received EOT ####\n");
  718.             return WCEOT;
  719.         } else if (FIRSTCH == CAN) {
  720.             if (LASTRX == CAN) {
  721.                 printf("\n#### Sender CANcelled ####\n");
  722.                 return ERROR;
  723.             } else {
  724.                 LASTRX = CAN;
  725.                 continue;
  726.             }
  727.         } else if (FIRSTCH == TIMEOUT) {
  728.             printf("\n**** SOH Timeout - Errors %4d ****\n",ERRORS);
  729.         }
  730.         LASTRX = 0;
  731.         while (READBYT(10) != TIMEOUT) {
  732.             ;
  733.         }
  734.         OUT_MODEM(NAK);
  735.         P_TIME = 1000;
  736.     }
  737.  
  738.     /* try to stop the bubble machine. */
  739.  
  740.     for (I = 0 ; I < 10 ; I++) {
  741.         OUT_MODEM(CAN);
  742.     }
  743.     return ERROR;
  744. }
  745. /*--------------------------------------------------------------------*/
  746. static int  WCRX()
  747.     
  748.     /*  Receive. */
  749. {
  750.     int             I;
  751.     int             SECTNUM;
  752.     int             SECTCURR;
  753.     char            RXBUF[SECSIZ + 1];
  754.     char            SENDCHAR;
  755.     int             TIMER;
  756.     extern metachar fputc();
  757. /*  begin */
  758.     SECTNUM  = 0;
  759.     SENDCHAR = NAK;
  760.     TIMER    = 1000;
  761.     printf("\n#### Sending Initial NAK . . . ####\n");
  762.     for ( ; ; ) {
  763.         printf("\n#### Receive Block #%4d ####\n",SECTNUM);
  764.         OUT_MODEM(SENDCHAR);    /* send it now, we're ready! */
  765.         SECTCURR = WCGETSEC(RXBUF,TIMER);
  766.         TIMER = 200;
  767.         if (SECTCURR == (SECTNUM + 1 & 0xff)) {    /* mask low byte */
  768.             SECTNUM++;
  769.             for (I = 0 ; I < SECSIZ ; I++) {
  770.                 if (fputc(RXBUF[I],RCVFILE) == ERROR) {
  771.                     printf("\n**** Receive File '%s' Error ****\n",
  772.                         RCVNAME);
  773.                     return ERROR;
  774.                 }
  775.             }
  776.             SENDCHAR = ACK;
  777.         } else if (SECTCURR == SECTNUM) {
  778.             printf("\n**** Received Duplicate Block %4d ****\n",
  779.                 SECTCURR);
  780.             SENDCHAR = ACK;
  781.         } else if (SECTCURR == WCEOT) {
  782.             OUT_MODEM(ACK);
  783.  
  784.             /* don't pad the file any more than it already is */
  785.  
  786.             return OK;
  787.         } else {
  788.             printf("\n**** Synchronization Error ****\n");
  789.             return ERROR;
  790.         }
  791.     }
  792. }
  793. /*--------------------------------------------------------------------*/
  794. static void DOWNLOAD()
  795.  
  796.     /*  Receive from remote computer to local computer. */
  797. {
  798.     int I;
  799. /*  begin */
  800.     if (OPENRX() == ERROR) {
  801.         ;
  802.     } else {
  803.         if (WCRX() == ERROR) {
  804.             printf("\n**** Abort ****\n");
  805.             for (I = 0 ; I < 10 ; I++) {
  806.                 OUT_MODEM(CAN);
  807.             }
  808.         }
  809.         CLOSERX();
  810.     }
  811. }
  812. /*--------------------------------------------------------------------*/
  813. static void DIAL()
  814.  
  815.     /*  Dial telephone number. */
  816. {
  817. /*  begin */
  818.     printf("\n#### Dialing Telephone Number '%s' . . . ####\n",
  819.         PHONENUM);
  820.     DELAY(150);
  821.     OUT_STR(ESCAPE_STR);
  822.     DELAY_ECHO(150);
  823.     OUT_STR(DIAL_STR);
  824.     OUT_STR(PHONENUM);
  825.     OUT_STR(CR_STR);
  826.     DELAY_ECHO(300);
  827. }
  828. /*--------------------------------------------------------------------*/
  829. static void HANGUP()
  830.  
  831.     /*  Hang up telephone. */
  832. {
  833. /*  begin */
  834.     printf("\n#### Hanging Up Telephone . . . ####\n");
  835.     DELAY(150);
  836.     OUT_STR(ESCAPE_STR);
  837.     DELAY_ECHO(150);
  838.     OUT_STR(HANGUP_STR);
  839.     DELAY_ECHO(300);
  840. }
  841. /*--------------------------------------------------------------------*/
  842. static void DOS()
  843.  
  844.     /*  Exit to DOS. Do not hang up telephone. */
  845. {
  846. /*  begin */
  847.     printf("\n#### Exit to DOS. Do not Hang Up Telephone ####\n");
  848.     exit(0);
  849. }
  850. /*--------------------------------------------------------------------*/
  851. static void QUIT()
  852.  
  853.     /*  Exit to DOS. Hang up telephone. */
  854. {
  855. /*  begin */
  856.     printf("\n #### Exit to DOS. Hang Up Telephone ####\n");
  857.     HANGUP();
  858.     exit(0);
  859. }
  860. /*--------------------------------------------------------------------*/
  861. static void BAUD()
  862.  
  863.     /*  Set baud rate. */
  864. {
  865.     unsigned int    LOAD_INT;
  866.  
  867. #ifdef  HAZITALL
  868.     unsigned int    LOAD_LOW_BYTE;
  869.     unsigned int    LOAD_HIGH_BYTE;
  870. #endif
  871.  
  872. #ifdef  OCTAPORT
  873.     int I;
  874. #endif
  875.  
  876. /*  begin */
  877.     printf("\n#### Set Baud Rate ####\n");
  878.     if (BAUDRATE == 300) {
  879.         LOAD_INT = B0300;
  880.     } else {
  881.         LOAD_INT = B1200;
  882.     }
  883.  
  884. #ifdef  HAZITALL
  885.     LOAD_HIGH_BYTE = (LOAD_INT >> 8) & 0x00ff;
  886.     LOAD_LOW_BYTE  =  LOAD_INT       & 0x00ff;
  887.     outportb(CCTRL,CCTRLB);
  888.     outportb(CLOAD,LOAD_LOW_BYTE);
  889.     outportb(CLOAD,LOAD_HIGH_BYTE);
  890. #endif
  891.  
  892. #ifdef  OCTAPORT
  893.     MDEFS[MBAUDLOW]  = (LOAD_INT >> 8) & 0x00ff;
  894.     MDEFS[MBAUDHIGH] =  LOAD_INT       & 0x00ff;
  895.     for (I = 0 ; I < MBAUDNUM ; I++) {
  896.         outportb(MCMND,MBAUD[I]);
  897.     }
  898. #endif
  899.  
  900. }
  901. /*--------------------------------------------------------------------*/
  902. static void INITIALIZE_PORT()
  903.  
  904.     /*  Initialize the modem port. */
  905. {
  906.     extern void outportb();
  907.  
  908. #ifdef  OCTAPORT
  909.     int I;
  910. #endif
  911.  
  912. /*  begin */
  913.     printf("\n#### Initialize Modem Port ####\n");
  914.  
  915. #ifdef  HAZITALL
  916.     outportb(MSTAT,NUL);
  917.     outportb(MSTAT,NUL);
  918.     outportb(MSTAT,NUL);
  919.     outportb(MSTAT,MCMD1);
  920.     outportb(MSTAT,MMODE);   
  921.     outportb(MSTAT,MCMD2);   
  922. #endif
  923.  
  924. #ifdef  OCTAPORT
  925.     for (I = 0 ; I < MDEFSNUM ; I++) {
  926.         outportb(MCMND,MDEFS[I]);
  927.     }
  928. #endif
  929.  
  930. }
  931. /*--------------------------------------------------------------------*/
  932. static void INIT()
  933.  
  934.     /*  Initialize. */
  935. {
  936. /*  begin */
  937.     printf("\n#### Initialize ####\n");
  938.     INITIALIZE_PORT();
  939.     BAUD();
  940. }
  941. /*--------------------------------------------------------------------*/
  942. static void help()
  943.  
  944.     /*  Display COMMANDS. */
  945. {
  946. /*  begin */
  947.     printf("\n    COMMANDS:\n");
  948.     printf("\n        'b'aud       <baudrate>");
  949.     printf("\n        'd'ial       telephone number");
  950.     printf("\n        'e'xit       without hangup");
  951.     printf("\n        'h'angup     without exit");
  952.     printf("\n        'i'nitialize baudrate & port");
  953.     printf("\n        'n'umber     <enter telephone number>");;
  954.     printf("\n        'q'uit       with hangup");
  955.     printf("\n        'r'eceive    <filename>");
  956.     printf("\n        's'end       <filename>");
  957.     printf("\n        't'race      transmissions");
  958.     printf("\n");
  959. }
  960. /*--------------------------------------------------------------------*/
  961. static void COMMANDS(P_CMD)
  962.  
  963.     char P_CMD;
  964.  
  965.     /*  Interpret command. */
  966. {
  967. /*  begin */
  968.     switch (tolower(P_CMD)) {
  969.         case 'b':
  970.             printf("\nBaudrate: ");
  971.             scanf("%d",&BAUDRATE);
  972.             BAUD();
  973.             break;
  974.         case 'd':
  975.             DIAL();
  976.             break;
  977.         case 'e':
  978.             DOS();
  979.             break;
  980.         case 'h':
  981.             HANGUP();
  982.             break;
  983.         case 'i':
  984.             INIT();
  985.             break;
  986.         case 'n':
  987.             printf("\nTelephone Number: ");
  988.             scanf("%s",PHONENUM);
  989.             break;
  990.         case 'q':
  991.             QUIT();
  992.             break;
  993.         case 'r':
  994.             printf("\nReceive File: ");
  995.             scanf("%s",RCVNAME);
  996.             DOWNLOAD();
  997.             break;
  998.         case 's':
  999.             printf("\nSend File: ");
  1000.             scanf("%s",SENDNAME);
  1001.             UPLOAD();
  1002.             break;
  1003.         case 't':
  1004.             printf("\nTrace Transmissions (y,n): ");
  1005.             scanf("%s",YN);
  1006.             if ((YN[0] = 'Y') || (YN[0] = 'y')) {
  1007.                 TRACEFLAG = true;
  1008.             } else if ((YN[0] = 'N') || (YN[0] = 'n')) {
  1009.                 TRACEFLAG = false;
  1010.             }
  1011.             break;
  1012.         case '?':
  1013.             help();
  1014.             break;
  1015.         default:
  1016.             printf("\nBad Command: '%c'\n",P_CMD);
  1017.     }
  1018. }
  1019. /*--------------------------------------------------------------------*/
  1020. main()
  1021. {
  1022.     metachar        RECEIVED;
  1023.     metachar        TO_SEND;
  1024.     bool            ESCFLAG;
  1025. /*  begin */
  1026.     SENDOPEN    = false;
  1027.     RCVOPEN     = false;
  1028.     T1PAUSE     = TIMECON * CLKMHZ;
  1029.     ESCFLAG     = false;
  1030.     BAUDRATE    = 1200;
  1031.     printf(
  1032.     "\n\nCMODEM--A 'Christensen Protocol' File Transfer Program\n\n");
  1033.     while (true) {
  1034.         if ((RECEIVED = IN_MODEM()) != EMPTY) {
  1035.              putchar(RECEIVED);
  1036.         } else if (TO_SEND = GETCH()) {
  1037.             if (TO_SEND == ESCCHAR ) {
  1038.                 if (ESCFLAG) {
  1039.                     ESCFLAG = false;
  1040.                     OUT_MODEM(TO_SEND);
  1041.                 } else {
  1042.                     ESCFLAG = true;
  1043.                 }                
  1044.             } else {
  1045.                 if (ESCFLAG) {
  1046.                     ESCFLAG = false;
  1047.                     COMMANDS(TO_SEND);
  1048.                 } else {
  1049.                     OUT_MODEM(TO_SEND);
  1050.                 }
  1051.             }
  1052.         }
  1053.     }
  1054.     exit(0);
  1055. }
  1056. /*--------------------------------------------------------------------*/
  1057. /*  end cmodem.c */
  1058. /*--------------------------------------------------------------------*/
  1059.