home *** CD-ROM | disk | FTP | other *** search
/ The CIA World Factbook 1992 / k3bimage.iso / sel / 02 / 0035 / jmodem_a.c < prev    next >
Encoding:
Text File  |  1991-12-02  |  31.8 KB  |  529 lines

  1. /****************************************************************************/
  2. /*    FILE JMODEM_A.C                                                       */
  3. /*                                                                          */
  4. /*    The JMODEM protocol            MicroSoft (r)  'C' V5.1                */
  5. /*    Created 03-FEB-1990            Richard B. Johnson                     */
  6. /*                                   405 Broughton Drive                    */
  7. /*                                   Beverly, Massachusetts 01915           */
  8. /*                                   BBS (508) 922-3166                     */
  9. /*                                                                          */
  10. /*    An external protocol for high-speed data transmission.                */
  11. /*                                                                          */
  12. /*    This is the MAIN module                                               */
  13. /*    The required modules are:                                             */
  14. /*    JMODEM.H    (function prototypes and structures)                      */
  15. /*    UART.H      (8250 UART parameters)                                    */
  16. /*    SCREEN.H    (function protypes and structures for the screen)         */
  17. /*    JMODEM_A.C  (this module)                                             */
  18. /*    JMODEM_B.C  (memory allocation and input parsing)                     */
  19. /*    JMODEM_C.C  (all file I/O)                                            */
  20. /*    JMODEM_D.C  (encode/decode and CRC routines)                          */
  21. /*    JMODEM_E.C  (communications I/O routines)                             */
  22. /*    JMODEM_F.C  (the screen I/O routines)                                 */
  23. /*    JMODEM.     (The MAKE file )                                          */
  24. /*                                                                          */
  25. /*    This program requires about 67k of free RAM to execute properly.      */
  26. /*    If you have 66k or less, it will execute, but the screens will        */
  27. /*    not be written or replaced properly. If you have only 64k, the        */
  28. /*    program will exit with an error message.                              */
  29. /*                                                                          */
  30. /*    Revision History:                                                     */
  31. /*    V3.00   Beta test                  11-FEB-1990   Richard B. Johnson   */
  32. /*    V3.01   First release              18-FEB-1990   Richard B. Johnson   */
  33. /*    V3.02   Revised                    19-FEB-1990   Richard B. Johnson   */
  34. /*                                                                          */
  35. /*      (1)   A bug in MicroSoft _calloc()  allocates overlapping           */
  36. /*            buffers so data files were getting corrupted. I had           */
  37. /*            used both _calloc() and _malloc() at the same time and        */
  38. /*            they didn't like it. I changed the memory allocation          */
  39. /*            to _malloc() only and it seems to work okay.                  */
  40. /*                                                                          */
  41. /*      (2)   While debugging, I found some structures I didn't need and    */
  42. /*            removed them. Changed some code to accommodate.               */
  43. /*                                                                          */
  44. /*      (3)   Added a file-size during downloads.                           */
  45. /*                                                                          */
  46. /*      (4)   Changed code in the data encoding (compression) routine       */
  47. /*            in an attempt to speed it up.                                 */
  48. /*                                                                          */
  49. /*    V3.03   Revised                   20-FEB-1990  Richard B. Johnson     */
  50. /*                                                                          */
  51. /*      (5)   Fixed bug in compression routine where the loop wasn't        */
  52. /*            terminating properly, adding random characters. Bug was       */
  53. /*            created during V3.02 change.                                  */
  54. /*                                                                          */
  55. /*    V3.04   Revised                   27-FEB-1990  Richard B. Johnson     */
  56. /*                                                                          */
  57. /*      (1)   Modified the block-size routine and the receive-block         */
  58. /*            routine in an attempt to improve the noise immunity.          */
  59. /*            Does not abort even if you whistle into the telephone         */
  60. /*            during uploads and downloads. Waits 5 seconds to clear        */
  61. /*            the interrupt buffer when a bad block-size is received.       */
  62. /*                                                                          */
  63. /*      (2)   Added a 1/2 second wait for modem status when opening         */
  64. /*            channel. This might accommodate slow modems response to       */
  65. /*            RTS.                                                          */
  66. /*                                                                          */
  67. /*    V3.05   Revised                   22-MAR-1990  Richard B. Johnson     */
  68. /*                                                                          */
  69. /*      (1)   Removed _sprintf() runtime library calls to shorten           */
  70. /*            the code. Saved about 4k.                                     */
  71. /*                                                                          */
  72. /*      (2)   Removed extra spaces in the signon-logo to shorten            */
  73. /*            the program size.                                             */
  74. /*                                                                          */
  75. /*      (3)   Changed the method of creating a fixed-length string          */
  76. /*            for both the block size and cps numbers which saved about     */
  77. /*            800 bytes of program size.                                    */
  78. /*                                                                          */
  79. /*      (4)   Changed numerous array indexes in JMODEM_F.C to pointers      */
  80. /*            to reduce code size. Saved a few hundred bytes and should     */
  81. /*            improve speed of screen output.                               */
  82. /*                                                                          */
  83. /*      (5)   Created a local _puts() routine which saved over 6k from the  */
  84. /*            MicroSoft C runtime library version. (JMODEM_F.C)             */
  85. /*                                                                          */
  86. /*                                                                          */
  87. /*                                                                          */
  88. /****************************************************************************/
  89. #include <stdlib.h>                     /* Used for _free()                 */
  90. #include <stdio.h>                      /* Used for NULL value              */
  91. #include <string.h>                     /* Used for _memcpy()               */
  92. #include <time.h>                       /* Used for absolute time           */
  93. #include "jmodem.h"                     /* JMODEM primatives                */
  94. /****************************************************************************/
  95. /*                   Global pointers and allocation                         */
  96. /****************************************************************************/
  97. unsigned char abrt[]  = "Aborted!";     /* Four messages                    */
  98. unsigned char okay[]  = "Okay    ";
  99. unsigned char retry[] = "Retry   ";
  100. unsigned char done[]  = "Done!   ";
  101. unsigned short user_abort = 0;          /* Global user abort flag           */
  102. unsigned char *int_buffer;              /* Pointer to interrupt buffer      */
  103. SYS syst;                               /* Structure for JMODEM status      */
  104. /****************************************************************************/
  105. /*                               C O D E                                    */
  106. /****************************************************************************/
  107. short main (short argc,  char *argv[])
  108. {
  109.     unsigned char *in_buffer;             /* Pointer to input buffer        */
  110.     unsigned char *out_buffer;            /* Pointer to output buffer       */
  111.     unsigned char *comp_buffer;           /* Pointer to compression buffer  */
  112.     register unsigned char *file_buffer;  /* Pointer to file buffer         */
  113.     register JBUF *buff;                  /* A pointer for the JMODEM block */
  114.     char *file_name;                      /* Filename                       */
  115.     char function;                        /* Receive, Transmit              */
  116.     char com_port;                        /* Communications adapter port    */
  117. #ifdef FTIME                              /* Floating point timer           */
  118.     time_t start;                         /* Start time                     */
  119.     time_t finish;                        /* End time                       */
  120.     double dat_tmp;                       /* Temporary variable for time    */
  121. #else                                     /* Integer timer                  */
  122.     unsigned long start;                  /* Start time                     */
  123.     unsigned long finish;                 /* End time                       */
  124. #endif
  125.     unsigned short status=0;              /* TX and RX status               */
  126.     unsigned short tries;                 /* Attempts to send a file        */
  127.     unsigned short cmp_size;              /* Size after compression         */
  128.     unsigned short data_written;          /* Data written to the file       */
  129.     unsigned short data_read;             /* Data read from the file        */
  130.     short handle;                         /* For file I/O                   */
  131.  
  132.     if (!(file_name = get_inp (argc, argv))) /* Get file name               */
  133.     {
  134.         disp();                              /* Display usage message       */
  135.         return JM_FNF;
  136.     }
  137.     if (!(function = get_fun (argc, argv)))  /* Get function 'R' or 'S'     */
  138.     {
  139.         disp();                              /* Display usage message       */
  140.         return JM_CMD;
  141.     }
  142.     if (!(com_port = get_prt (argc, argv)))  /* Get port '1 to 4 '          */
  143.     {
  144.         disp();                              /* Display usage message       */
  145.         return JM_CMD;
  146.     }
  147.     port = get_port(com_port);               /* Convert port to an offset   */
  148. /****************************************************************************/
  149. /*                          Allocate buffers                                */
  150. /****************************************************************************/
  151.     in_buffer = allocate_memory(DAT_LEN);  /* Get some memory for input     */
  152.     if (!in_buffer)
  153.         return JM_MEM;                     /* No memory available           */
  154.     out_buffer = allocate_memory(DAT_LEN); /* Get some memory for output    */
  155.     if (!out_buffer)
  156.         return JM_MEM;                     /* No memory available           */
  157.     comp_buffer=allocate_memory(DAT_LEN);  /* Get memory for compression    */
  158.     if (!comp_buffer)
  159.         return JM_MEM;                     /* No memory available           */
  160.     file_buffer=allocate_memory(DAT_LEN);  /* Get memory for file buffer    */
  161.     if (!file_buffer)
  162.         return JM_MEM;                     /* No memory available           */
  163.     int_buffer =allocate_memory(DAT_LEN);  /* Memory for interrupt buffer   */
  164.     if (!int_buffer)
  165.         return JM_MEM;                     /* No memory available           */
  166. /****************************************************************************/
  167.     screen (SCR_SGN,NULL,NULL);            /* Write signon screen           */
  168.     syst.s_len = BLK_SIZ;                  /* Set beginning block size      */
  169.     syst.s_byt = 0;                        /* Set bytes handled             */
  170.     syst.s_blk = 0;                        /* Starting block                */
  171.     syst.s_sta = okay;                     /* Starting status               */
  172.     switch(function)                       /* Functions are TX and RX       */
  173.     {
  174. /****************************************************************************/
  175. /*                          Receive JMODEM file                             */
  176. /****************************************************************************/
  177.     case 'R':
  178.         {
  179.             if (!file_io(CREATE, &handle, file_name, NULL) )
  180.             {
  181.                 buff = (JBUF *) in_buffer;            /* Assign type JBUF   */
  182.                 open_chan(port);                      /* Open com channel   */
  183.                 screen (SCR_STA,NULL,NULL);           /* Write status block */
  184.                 status = rx_sync();                   /* Synchronize        */
  185.                 if (!status)
  186.                     screen (SCR_SYR,NULL,NULL);
  187.                 data_written = 0xFFFF;
  188.                 tries = 10;                          /* Attempts to receive */
  189.                 while (    (data_written)            /* Write file okay     */
  190.                         && (!user_abort )            /* No break key        */
  191.                         && (!status     )            /* Recev block okay    */
  192.                         && (tries--)    )            /* 10 retries          */
  193.                 {
  194.                     time(&start);                    /* Get starting time   */
  195.                     screen (SCR_SYS,&syst,NULL);     /* Show status block   */
  196.                     status = recv_blk (              /* Receive data-block  */
  197.                              &syst.s_len,            /* Block length        */
  198.                              in_buffer);             /* Input buffer        */
  199.                     if (status)                      /* If bad              */
  200.                         break;                       /* Abort the WHILE     */
  201.                     if( (!(calc_crc(GET_CRC,         /* Calculate CRC       */
  202.                           syst.s_len,                /* Amount to check     */
  203.                           in_buffer) ))              /* Receiver buffer     */
  204.                       && ( buff->blk_num ==          /* Check block also    */
  205.                          (unsigned char)
  206.                          (syst.s_blk +1)))           /* Block number        */
  207.                     {
  208.                         syst.s_sta = okay;           /* Text pointer        */
  209.                         tries=10;                    /* Reset count         */
  210.                         syst.s_len -= OVRHD;         /* Subtract overhead   */
  211.                         *out_buffer = ACK;           /* Good                */
  212.                         write_chan(1,out_buffer);    /* Send the ACK        */
  213.  
  214.                                                   /* If data was compressed */
  215.                         if ( (buff->blk_typ & COMP) == COMP)
  216.                         {
  217.                              syst.s_len = decode (   /* Decode the data     */
  218.                                       syst.s_len,    /* Data-block length   */
  219.                                      &buff->blk_dat, /* Where to start      */
  220.                                      file_buffer);   /* Where to put data   */
  221.                         }
  222.                         else
  223.                             /* Data was normal (not compressed, just copy ) */
  224.                         {
  225.                             memcpy (file_buffer,&buff->blk_dat,syst.s_len);
  226.                         }
  227.                         /* Write to the file                                */
  228.                         data_written = file_io( WRITE ,  /* Function        */
  229.                                          &handle,        /* File handle     */
  230.                                          file_buffer ,   /* Where data is   */
  231.                                          syst.s_len );   /* Amount to write */
  232.                         syst.s_byt += data_written;      /* Total bytes     */
  233.                         syst.s_blk++;                    /* Block number    */
  234.                         time(&finish);                   /* Get end time    */
  235.                         if (finish - start)              /* Check div/0     */
  236.                         {
  237. #ifdef FTIME
  238.                             dat_tmp = (double) data_written;
  239.                             syst.s_cps = (short) (dat_tmp /
  240.                                                difftime(finish,start));
  241. #else
  242.                             syst.s_cps = (short)         /* Calc Block CPS  */
  243.                             (data_written / (finish - start) );
  244. #endif
  245.                         }
  246.                                                    /* Check for end-of-file */
  247.                         if ( (buff->blk_typ & EOF_) == EOF_)
  248.                         {                       /* This was the end of file */
  249.                             file_io(CLOSE,               /* Function        */
  250.                                    &handle,              /* Open handle     */
  251.                                    file_name,            /* Name not used   */
  252.                                    NULL);                /* Buffer not used */
  253.                             close_chan(port);            /* Close the port  */
  254.                             status = JM_NRM;             /* Set status      */
  255.                             goto cleanup;                /* exit routine    */
  256.                         }
  257.                     }
  258.                     else
  259.                     {
  260.                         *out_buffer = NAK;              /* Bad block        */
  261.                         syst.s_sta = retry;             /* Char pointer     */
  262.                         write_chan(1,out_buffer);       /* Send the NAK     */
  263.                      }
  264.                 }
  265.                 close_chan(port);                        /* Aborted         */
  266.                 file_io(DELETE,                          /* Function        */
  267.                         &handle,                         /* File handle     */
  268.                         file_name,                       /* Name            */
  269.                         NULL);                           /* Buffer not used */
  270.                 status = JM_ABT;
  271.                 break;                                   /* Exit if() {}    */
  272.             }
  273.             else                                       /* Can't create file */
  274.             {
  275.                 status = JM_CRE;
  276.                 break;                                   /* Exit while() {} */
  277.             }
  278.         break;                                           /* Exit case 'R'   */
  279.         }
  280. /****************************************************************************/
  281. /*                          Send JMODEM file                                */
  282. /****************************************************************************/
  283.     case 'S':   /* Send JMODEM file */
  284.         {
  285.             if (!file_io(OPEN_READ, &handle, file_name, NULL) )
  286.             {
  287.                 buff = (JBUF *)out_buffer;            /* Assign type JBUF   */
  288.                 syst.s_byt = 0;                       /* Restore byte count */
  289.                 open_chan(port);                      /* Open COM port      */
  290.                 data_read = 0xFFFF;                   /* Initialize         */
  291.                 screen (SCR_STA,NULL,NULL);           /* Write status block */
  292.                 status = tx_sync();                   /* Synchronize        */
  293.                 if (!status)
  294.                     screen (SCR_SYT,NULL,NULL);
  295.                 while  (  (!user_abort)               /* Ctrl - break       */
  296.                        && (!status) )                 /* sent okay          */
  297.                 {
  298.                     time(&start);                     /* Get starting time  */
  299.                     data_read = file_io( READ       , /* Read a record      */
  300.                                        &handle      , /* File pointer       */
  301.                                        file_buffer ,  /* Where to put       */
  302.                                       syst.s_len );   /* Amount to read     */
  303.                     if (!data_read)                   /* Past end of file   */
  304.                         break;
  305.                     syst.s_byt += (long) data_read;   /* Running count      */
  306.                     screen (SCR_SYS,&syst,NULL);      /* Show status block  */
  307.                     buff->blk_num = (unsigned char)
  308.                                      ++syst.s_blk;    /* Block number       */
  309.                     if (data_read != syst.s_len)      /* Byte request       */
  310.                         buff->blk_typ = EOF_;         /* Into control-byte  */
  311.                     else
  312.                         buff->blk_typ = NORM;         /* Normal block       */
  313.                     
  314.                     cmp_size = encode (data_read,     /* Encode size        */
  315.                                       file_buffer,    /* Source             */
  316.                                       comp_buffer);   /* Destination        */
  317.  
  318.                     if ( cmp_size  < data_read  )     /* If compressed      */
  319.                     {
  320.                         buff->len = (cmp_size+OVRHD); /* Length of block    */
  321.                         buff->blk_typ |= COMP;        /* Show compressed    */
  322.                         memcpy (&buff->blk_dat,       /* Start of data      */
  323.                                    comp_buffer,       /* Copy from here     */
  324.                                    cmp_size);         /* This much          */
  325.                     }
  326.                     else                              /* Not compressed     */
  327.                     {
  328.                         buff->len = (data_read+OVRHD);/* Length of block    */
  329.                         memcpy (&buff->blk_dat,       /* Copy to            */
  330.                                    file_buffer,       /* Copy from          */
  331.                                    data_read);        /* This amount        */
  332.                     }
  333.                     calc_crc(SET_CRC,                 /* Calculate CRC      */
  334.                             buff->len ,               /* Length of block    */
  335.                             out_buffer);              /* Where data is      */
  336.                     status = send_blk(                /* Send the block     */
  337.                              buff->len,               /* Block length       */
  338.                              &syst,                   /* Read block ptr.    */
  339.                              out_buffer);             /* Buffer pointer     */
  340.                     time(&finish);                    /* Get end time       */
  341.                     if (finish - start)               /* Check div/0        */
  342.                     {
  343. #ifdef FTIME
  344.                         dat_tmp = (double) data_read;
  345.                         syst.s_cps = (short) (dat_tmp /
  346.                                          difftime(finish,start));
  347. #else
  348.                         syst.s_cps = (short)          /* Calc Block CPS     */
  349.                         (data_read / (finish - start) );
  350. #endif
  351.                     }
  352.                         (data_read / (finish-start) );
  353.                     if ( buff->blk_typ == EOF_)       /* Last record        */
  354.                         break;
  355.                 }
  356.                 close_chan(port);                     /* Close the port     */
  357.                 if (status)
  358.                     syst.s_sta = abrt;                /* A text pointer     */
  359.                 else
  360.                     syst.s_sta = done;                /* A text pointer     */
  361.  
  362.                 file_io(CLOSE, &handle,
  363.                         file_name, NULL);             /* Close the file     */
  364.                 screen (SCR_SYS,&syst,NULL);          /* Show status block  */
  365.             }
  366.             else                                      /* File not found     */
  367.             {
  368.                 status = JM_FNF;
  369.             }
  370.         break;  /* End of CASE 'S' */
  371.         }
  372.     }
  373.     cleanup:
  374.     free (in_buffer);                                  /* Free  buffers     */
  375.     free (out_buffer);
  376.     free (comp_buffer);
  377.     free (file_buffer);
  378.     /* Five-second timer to display error messages */
  379.     if (status != JM_NRM)
  380.     {
  381.         time(&finish);
  382.         start = 0;
  383.         finish += 2;
  384.         while ( finish > start )
  385.             time(&start);
  386.     }
  387.     screen (SCR_END,NULL,NULL);                         /* Clear the screen */
  388.     return status;                                      /* Normal exit      */
  389. }
  390. /****************************************************************************/
  391. /*                          Send the JMODEM block                           */
  392. /****************************************************************************/
  393. unsigned short send_blk (blk_len, sys_ptr, buffer)
  394. unsigned short blk_len;
  395. SYS *sys_ptr;
  396. unsigned char *buffer;
  397. {
  398.     unsigned char ack_buf;                    /* Buffer for ACK/NAK         */
  399.     unsigned short tries = 10;                /* Attempts to send the block */
  400.     while ((tries--) && (!user_abort))
  401.     {
  402.         write_chan(blk_len,buffer);           /* Send the JMODEM block      */
  403.         flush();                              /* Clear back channel noise   */
  404.         do
  405.         {
  406.             ack_buf = NULL;                   /* Clear the return buffer    */
  407.             read_chan(1,&ack_buf);            /* Receive a response         */
  408.         } while ( (ack_buf != ACK)            /* Stay in loop until we      */
  409.                && (ack_buf != CAN)            /*  ... get something useful  */
  410.                && (ack_buf != NAK)            /* This helps re-sync in noise*/
  411.                && (ack_buf ==NULL)
  412.                && (!user_abort) );
  413.  
  414.         if (ack_buf == CAN)                   /* Check for an abort         */
  415.             break;                            /* User aborted               */
  416.         if (ack_buf == ACK)                   /* If good block              */
  417.         {
  418.             if (tries == 9)                   /* If no retries              */
  419.             {
  420.                 sys_ptr->s_len += 512;        /* Increase block-size        */
  421.                 if (sys_ptr->s_len > DAT_MAX) /* If too large               */
  422.                     sys_ptr->s_len = DAT_MAX;
  423.             }
  424.             else
  425.             {
  426.                 tries = 9 - tries;            /* Use for divisor            */
  427.                 sys_ptr->s_len =              /* Update block length        */
  428.                 sys_ptr->s_len / tries;       /* Div block size             */
  429.             if (sys_ptr->s_len < 0x40)        /* If less than minimum       */
  430.                 sys_ptr->s_len = 0x40;        /* Set to minimum             */
  431.             }
  432.         sys_ptr->s_sta = okay;                /* Show status is okay        */
  433.         return JM_NRM;                        /* Show good                  */
  434.         }
  435.     sys_ptr->s_sta = retry;                   /* Show a retry               */
  436.     screen (SCR_SYS, sys_ptr,NULL);           /* Write to screen            */
  437.     }
  438.         ack_buf = CAN;                        /* Send ^Xes                  */
  439.         for (tries = 0; tries <6; tries++)    /* Six times                  */
  440.             write_chan(1,&ack_buf);
  441.         return JM_ABT;                        /* Abort local program        */
  442.  
  443. }
  444. /****************************************************************************/
  445. /*                        Receive the JMODEM block                          */
  446. /****************************************************************************/
  447. unsigned short recv_blk (blk_len, buffer)
  448. unsigned short *blk_len;                   /* Pointer to the block-length   */
  449. unsigned char *buffer;                     /* Pointer to the buffer         */
  450. {
  451.     JBUF *buff;                            /* Pointer type JBUF             */
  452.     unsigned char nak_buf;                 /* Buffer for ACK/NAK            */
  453.     unsigned short tries = 10;             /* Attempts to receive the block */
  454.     unsigned short ret_val;                /* Block length returned         */
  455.     buff = (JBUF * )buffer;                /* Assign pointer type JBUF      */
  456.  
  457.     while ((tries--) && (!user_abort))
  458.     {
  459.         ret_val = read_chan(2,buffer);     /* Receive the block size        */
  460.         if (ret_val == 2)                  /* If we received the length     */
  461.         {
  462.             *blk_len = buff->len;          /* So caller knows size          */
  463.             if (*blk_len > DAT_LEN)        /* If way out of line            */
  464.                 break;                     /* NAK it                        */
  465.             ret_val = read_chan(           /* Get more data                 */
  466.                       (*blk_len)-2 ,       /* Size to read                  */
  467.                       &buff->blk_typ);     /* Where to put it               */
  468.             if (ret_val == (*blk_len)-2)   /* If we got what we requested   */
  469.                 return JM_NRM;
  470.         }
  471.     read_chan (DAT_LEN,buffer);            /* Make sure other end stops     */
  472.     nak_buf = NAK;                         /* Get a NAK                     */
  473.     write_chan(1,&nak_buf);                /* Send to remote                */
  474.     flush();                               /* Flush the buffer              */
  475.     }
  476.     nak_buf = CAN;                         /* Send ^Xes                     */
  477.     for (tries = 0; tries <6; tries++)
  478.     write_chan(1,&nak_buf);
  479.     return JM_ABT;                         /* Abort local program           */
  480. }
  481. /****************************************************************************/
  482. /*                         Synchronize during receive                       */
  483. /****************************************************************************/
  484. unsigned short rx_sync()
  485. {
  486.     short i;
  487.     unsigned char ack_nak;               /* Single byte buffer for ACK/NAK  */
  488.     flush();                             /* Clear the interrupt buffer      */
  489.     while (!user_abort)
  490.     {
  491.         ack_nak = NULL;                  /* Clear the buffer                */
  492.         read_chan(1,&ack_nak);           /* Receive ACK, NAK, or SYN        */
  493.         if (ack_nak == CAN)              /* If a ^X                         */
  494.             break;
  495.         if ( ack_nak == ACK )            /* If a good response              */
  496.             return JM_NRM;               /* Show handshake                  */
  497.         if ( ack_nak == NAK )            /* If a good response              */
  498.         {
  499.             ack_nak = ACK;
  500.             write_chan(1,&ack_nak);      /* Send a ACK response             */
  501.             return JM_NRM;
  502.          }
  503.          ack_nak = NAK;
  504.          write_chan(1,&ack_nak);         /* Keep sending MAKs               */
  505.     }
  506.     ack_nak = CAN;
  507.     for (i=0; i<8; i++)                  /* Send 8 ^Xes                     */
  508.         write_chan(1,&ack_nak);          /* Send a response                 */
  509.     return JM_ABT;
  510. }
  511. /****************************************************************************/
  512. /*                         Synchronize during transmit                      */
  513. /****************************************************************************/
  514. unsigned short tx_sync()
  515. {
  516.     unsigned short ret_val;
  517.     ret_val = rx_sync();                /* Call same routine for receive    */
  518.     if (ret_val)                        /* If no success                    */
  519.         return ret_val;                 /* Abort routines                   */
  520.     flush();                            /* Else flush the input buffer      */
  521.     timer = 5;                          /* 5 timer-ticks to wait            */
  522.     while (timer)                       /* Wait for timer                   */
  523.         ;
  524.     return JM_NRM;                      /* Normal return                    */
  525. }
  526. /****************************************************************************/
  527. /************************ E N D  O F   M O D U L E **************************/
  528.  
  529.