home *** CD-ROM | disk | FTP | other *** search
- /****************************************************************************/
- /* FILE JMODEM_A.C */
- /* */
- /* The JMODEM protocol MicroSoft (r) 'C' V6.00 */
- /* Created 03-FEB-1990 Richard B. Johnson */
- /* 405 Broughton Drive */
- /* Beverly, Massachusetts 01915 */
- /* BBS (508) 922-3166 */
- /* */
- /* An external protocol for high-speed data transmission. */
- /* */
- /* This is the MAIN module */
- /* The required modules are: */
- /* JMODEM.H (function prototypes and structures) */
- /* UART.H (8250 UART parameters) */
- /* SCREEN.H (function protypes and structures for the screen) */
- /* JMODEM_A.C (this module) */
- /* JMODEM_B.C (memory allocation and input parsing) */
- /* JMODEM_C.C (all file I/O) */
- /* JMODEM_D.C (encode/decode and CRC routines) */
- /* JMODEM_E.C (communications I/O routines) */
- /* JMODEM_F.C (the screen I/O routines) */
- /* JMODEM_G.ASM (Interrupt service routines after V3.10) */
- /* JMODEM. (The MAKE file ) */
- /* */
- /* This program requires about 67k of free RAM to execute properly. */
- /* If you have 66k or less, it will execute, but the screens will */
- /* not be written or replaced properly. If you have only 64k, the */
- /* program will exit with an error message. */
- /* */
- /* Revision History: */
- /* V3.00 Beta test 11-FEB-1990 Richard B. Johnson */
- /* V3.01 First release 18-FEB-1990 Richard B. Johnson */
- /* V3.02 Revised 19-FEB-1990 Richard B. Johnson */
- /* */
- /* (1) A bug in MicroSoft _calloc() allocates overlapping */
- /* buffers so data files were getting corrupted. I had */
- /* used both _calloc() and _malloc() at the same time and */
- /* they didn't like it. I changed the memory allocation */
- /* to _malloc() only and it seems to work okay. */
- /* */
- /* (2) While debugging, I found some structures I didn't need and */
- /* removed them. Changed some code to accommodate. */
- /* */
- /* (3) Added a file-size during downloads. */
- /* */
- /* (4) Changed code in the data encoding (compression) routine */
- /* in an attempt to speed it up. */
- /* */
- /* V3.03 Revised 20-FEB-1990 Richard B. Johnson */
- /* */
- /* (5) Fixed bug in compression routine where the loop wasn't */
- /* terminating properly, adding random characters. Bug was */
- /* created during V3.02 change. */
- /* */
- /* V3.04 Revised 27-FEB-1990 Richard B. Johnson */
- /* */
- /* (1) Modified the block-size routine and the receive-block */
- /* routine in an attempt to improve the noise immunity. */
- /* Does not abort even if you whistle into the telephone */
- /* during uploads and downloads. Waits 5 seconds to clear */
- /* the interrupt buffer when a bad block-size is received. */
- /* */
- /* (2) Added a 1/2 second wait for modem status when opening */
- /* channel. This might accommodate slow modems response to */
- /* RTS. */
- /* */
- /* V3.05 Revised 22-MAR-1990 Richard B. Johnson */
- /* */
- /* (1) Removed _sprintf() runtime library calls to shorten */
- /* the code. Saved about 4k. */
- /* */
- /* (2) Removed extra spaces in the signon-logo to shorten */
- /* the program size. */
- /* */
- /* (3) Changed the method of creating a fixed-length string */
- /* for both the block size and cps numbers which saved about */
- /* 800 bytes of program size. */
- /* */
- /* (4) Changed numerous array indexes in JMODEM_F.C to pointers */
- /* to reduce code size. Saved a few hundred bytes and should */
- /* improve speed of screen output. */
- /* */
- /* (5) Created a local _puts() routine which saved over 6k from the */
- /* MicroSoft C runtime library version. (JMODEM_F.C) */
- /* */
- /* V3.06 Revised 07-APR-1990 Richard B. Johnson */
- /* */
- /* (1) Put the filename text into the syst structure as a pointer */
- /* to char. This allowed me to save 56 bytes of code and now */
- /* only two parameters are passed to the _screen() function. */
- /* */
- /* (2) Modified the syst structure and supporting code. */
- /* */
- /* (3) Moved all external data and functions to the JMODEM.H file. */
- /* */
- /* (4) Moved _disp() "usage" module to JMODEM_F.C */
- /* */
- /* (5) Changed arrays in JMODEM_B.C to pointers to reduce code- */
- /* size. Eliminated _strcpy() from the command-line parsing */
- /* routines. Brought the code-size to less than 12,000 bytes. */
- /* */
- /* (6) Reduced the code-size in the _encode(), _decode(), and */
- /* _crc() routines in JMODEM_D.C. Removed shifts to improve */
- /* speed and replaced the shifts with pointers for altering */
- /* portions of the strings. */
- /* */
- /* (7) Made a _cancel() routine in JMODEM_A.C to send ^Xes upon */
- /* abort. */
- /* */
- /* (8) Removed the bit being set "OUT 1" via the modem-control */
- /* register in the "open" routine in JMODEM_E.C. This was */
- /* causing some internal modems to lock up as they use this */
- /* bit for something. "OUT 2" is used to enable IRQ on most */
- /* clone RS-232 boards and modems. The Heathkit HZ-100 boards */
- /* will probably not work anymore because they use "OUT 1". */
- /* */
- /* */
- /* V3.07 Revised 03-MAY-1990 Richard B. Johnson */
- /* */
- /* (1) Rewrote code to remove the requirement for a file buffer. */
- /* This means that this buffer does not need to be allocated, */
- /* saving about 8k of RAM at run-time. ( JMODEM_A.C ) */
- /* */
- /* Program now only requires 52k of free RAM to execute okay. */
- /* */
- /* (2) Changed the header file, JMODEM.H, and function calling */
- /* procedures to file_io() and screen() to allow variable- */
- /* length parameter-lists. This eliminates the requirement */
- /* to pass a NULL as a place-holder on procedures that don't */
- /* always require all possible parameters to be passed. This */
- /* saved about 50 bytes of code. */
- /* */
- /* (3) Changed the "Usage" prompt and code to reduce program size. */
- /* Saved about 60 bytes. */
- /* */
- /* (4) Changed keyboard break interrupt in JMODEM_E.C so it sets */
- /* the global timer to zero as well as setting the abort flag. */
- /* */
- /* V3.08 Revised 01-DEC-1990 Richard B. Johnson */
- /* */
- /* (1) Changed the code to compile without warning errors when */
- /* using Microsoft Version 6.0. They saw fit to change the */
- /* ANSI standards for declaring objects passed to functions. */
- /* The new "standards" were called to my attention by */
- /* Jeff Jevnisek who provided modified source. */
- /* */
- /* (2) Changed the method of determining a memory allocation */
- /* failure. The code used to check for a NULL pointer returned */
- /* from _malloc() if memory was not available. Microsoft does */
- /* not allow NULL to be used for that anymore! Instead I have */
- /* to either use a cast or check for (!ptr). I chose the latter. */
- /* */
- /* V3.09 Revised 27-JUN-1991 Richard B. Johnson */
- /* */
- /* (1) Changed a typo in the previous revision record that showed */
- /* the date to be 01-DEC-1991 when it should be 01-DEC-1990 */
- /* */
- /* (2) Changed the method of checking the interrupt buffer pointer */
- /* in JMODEM_E.C so that only one compare and no arithmetic */
- /* has to be done. JMODEM now works at 38,400 baud with a */
- /* 33 MHz '386 machine. */
- /* */
- /* (3) Added support for using any communications port address */
- /* and IRQ. */
- /* */
- /* JMODEM R(3F8:4) filename.typ */
- /* JMODEM R(2F8:3) filename.typ */
- /* JMODEM S(3F8:4) filename.typ */
- /* JMODEM S(2F8:3) filename.typ */
- /* JMODEM S(20E:7) filename.typ ... etc. */
- /* */
- /* V3.10 Revised 19-NOV-1991 Richard B. Johnson */
- /* */
- /* (1) Changed the memory allocation method. Used to allocate 4 */
- /* buffers with 4 calls to _malloc(). Now I allocate one large */
- /* block and cut it up into 4 pieces. This is more efficient. */
- /* */
- /* (2) Called _malloc() directly without a separate allocate_mem() */
- /* routine because machine differences can be handled with */
- /* definitions for different compilers and platforms. */
- /* */
- /* (3) Moved the receive and timer interrupt service routines to */
- /* assembly-language modules in JMODEM_G.ASM */
- /* */
- /* V3.11 Revised 25-JAN-1992 Richard B. Johnson */
- /* */
- /* (1) Added variable "tries" to force a timeout should the user */
- /* attempt to receive from a disconnected RS-232C line. This */
- /* will time-out in about a minute. */
- /* */
- /* */
- /****************************************************************************/
- #include <stdlib.h> /* Used for _free() */
- #include <stdio.h> /* Used for NULL value */
- #include <string.h> /* Used for _memcpy() */
- #include <time.h> /* Used for absolute time */
- #include "jmodem.h" /* JMODEM primatives */
- #if defined (TURBOC)
- #include <alloc.h>
- #else
- #include <malloc.h> /* Used for _malloc(); */
- #endif
-
- /****************************************************************************/
- /* Global pointers and allocation */
- /****************************************************************************/
- word user_abort = 0; /* Global user abort flag */
- byte *int_buffer; /* Pointer to interrupt buffer */
- SYS syst; /* Structure for JMODEM status */
- /****************************************************************************/
- /* C O D E */
- /****************************************************************************/
- short main (short argc, char *argv[])
- {
- byte *in_buffer; /* Pointer to input buffer */
- byte *out_buffer; /* Pointer to output buffer */
- byte *comp_buffer; /* Pointer to compression buffer */
- register byte *io_ptr; /* Select buffers to use for I/O */
- register JBUF *buff; /* A pointer for the JMODEM block */
- byte *file_name; /* Filename */
- byte function; /* Receive, Transmit */
- time_t start; /* Start time */
- time_t finish; /* End time */
- #ifdef FTIME /* Floating point timer */
- double dat_tmp; /* Temporary variable for time */
- #endif
- word status=0; /* TX and RX status */
- word tries; /* Attempts to send a file */
- word cmp_size; /* Size after compression */
- word data_written; /* Data written to the file */
- word data_read; /* Data read from the file */
- short handle; /* For file I/O */
-
- if (!(file_name = get_inp (argc, argv))) /* Get file name */
- {
- disp(); /* Display usage message */
- return JM_FNF;
- }
- if (!(function = get_fun (argc, argv))) /* Get function 'R' or 'S' */
- {
- disp(); /* Display usage message */
- return JM_CMD;
- }
- if (!(port = get_port (argc, argv))) /* Get port '1 to 4 ' */
- {
- disp(); /* Display usage message */
- return JM_CMD;
- }
- /****************************************************************************/
- /* Allocate buffers */
- /* Note: Here I allocate one large buffer that is 4 times the size of */
- /* DAT_LEN. Then I partition it into 4 sections for the 4 buffers. */
- /* This is more efficient that multiple calls to _malloc() and I */
- /* can free the entire buffer with one call to _free(). */
- /****************************************************************************/
- in_buffer = (byte *) malloc(ALC_MEM); /* Get some memory for input */
- if (!in_buffer)
- return JM_MEM; /* No memory available */
- out_buffer = in_buffer + DAT_LEN; /* Get some memory for output */
- comp_buffer= out_buffer + DAT_LEN; /* Get memory for compression */
- int_buffer = comp_buffer + DAT_LEN; /* Memory for interrupt buffer */
- /****************************************************************************/
- screen (SCR_SGN); /* Write signon screen */
- syst.s_len = BLK_SIZ; /* Set beginning block size */
- syst.s_byt = 0; /* Set bytes handled */
- syst.s_blk = 0; /* Starting block */
- syst.s_sta = okay; /* Starting status */
- switch(function) /* Functions are TX and RX */
- {
- /****************************************************************************/
- /* Receive JMODEM file */
- /****************************************************************************/
- case 'R':
- {
- if (!file_io(CREATE, &handle, file_name) )
- {
- buff = (JBUF *) in_buffer; /* Assign type JBUF */
- open_chan(port); /* Open com channel */
- screen (SCR_STA); /* Write status block */
- status = rx_sync(); /* Synchronize */
- if (!status)
- screen (SCR_SYR);
- data_written = 0xFFFF;
- tries = 10; /* Attempts to receive */
- while ( (data_written) /* Write file okay */
- && (!user_abort ) /* No break key */
- && (!status ) /* Recev block okay */
- && (tries--) ) /* 10 retries */
- {
- time(&start); /* Get starting time */
- screen (SCR_SYS,&syst); /* Show status block */
- status = recv_blk ( /* Receive data-block */
- &syst.s_len, /* Block length */
- in_buffer); /* Input buffer */
- if (status) /* If bad */
- break; /* Abort the WHILE */
- if( (!(calc_crc( GET_CRC, /* Calculate CRC */
- syst.s_len, /* Amount to check */
- in_buffer) )) /* Receiver buffer */
- && ( buff->blk_num == /* Check block also */
- (byte)
- (syst.s_blk +1))) /* Block number */
- {
- syst.s_sta = okay; /* Text pointer */
- tries=10; /* Reset count */
- syst.s_len -= OVRHD; /* Subtract overhead */
- *out_buffer = ACK; /* Good */
- write_chan(1,out_buffer); /* Send the ACK */
- io_ptr = &buff->blk_dat; /* Assume normal data */
-
- if (buff->blk_typ & COMP)
- { /* If data compressed */
- syst.s_len = decode ( /* Decode the data */
- syst.s_len, /* Data-block length */
- &buff->blk_dat, /* Where to start */
- comp_buffer); /* Where to put data */
- io_ptr = comp_buffer; /* Point to data */
- }
- data_written = file_io(WRITE, /* Write to file */
- &handle, /* File handle */
- io_ptr , /* Where data is */
- syst.s_len); /* Amount to write */
- syst.s_byt += data_written; /* Total bytes */
- syst.s_blk++; /* Block number */
- time(&finish); /* Get end time */
- if (finish - start) /* Check div/0 */
- {
- #ifdef FTIME
- dat_tmp = (double) data_written;
- syst.s_cps = (short) (dat_tmp /
- difftime(finish,start));
- #else
- syst.s_cps = (short) /* Calc Block CPS */
- (data_written / (finish - start) );
- #endif
- }
- /* Check end-of-file */
- if (buff->blk_typ & EOF_)
- {
- file_io(CLOSE,&handle); /* Close file */
- close_chan(port); /* Close the port */
- status = JM_NRM; /* Set status */
- goto cleanup; /* exit routine */
- }
- }
- else
- {
- *out_buffer = NAK; /* Bad block */
- syst.s_sta = retry; /* Char pointer */
- write_chan(1,out_buffer); /* Send the NAK */
- }
- }
- close_chan(port); /* Aborted */
- file_io( DELETE, &handle, file_name); /* Delete bad file */
- status = JM_ABT;
- break; /* Exit if() {} */
- }
- else /* Can't create file */
- {
- status = JM_CRE;
- break; /* Exit while() {} */
- }
- }
- /****************************************************************************/
- /* Send JMODEM file */
- /****************************************************************************/
- case 'S': /* Send JMODEM file */
- {
- if (!file_io(OPEN_READ, &handle, file_name) )
- {
- buff = (JBUF *)out_buffer; /* Assign type JBUF */
- syst.s_byt = 0; /* Restore byte count */
- open_chan(port); /* Open COM port */
- screen (SCR_STA); /* Write status block */
- status = tx_sync(); /* Synchronize */
- if (!status)
- screen (SCR_SYT);
- while ( (!user_abort) /* Ctrl - break */
- && (!status) ) /* sent okay */
- {
- time(&start); /* Get starting time */
- data_read = file_io( READ, /* Read a record */
- &handle, /* File pointer */
- &buff->blk_dat, /* Where to put data */
- syst.s_len ); /* Amount to read */
- if (!data_read) /* Past end of file */
- break;
- syst.s_byt += (long) data_read; /* Running count */
- screen (SCR_SYS,&syst); /* Show status block */
- buff->blk_num = (byte)
- ++syst.s_blk; /* Block number */
- buff->blk_typ = NORM; /* Assume Normal */
- buff->len = (data_read+OVRHD); /* Length of block */
- if (data_read != syst.s_len) /* Less than request */
- buff->blk_typ |= EOF_; /* Its end of file */
- cmp_size = encode (data_read, /* Encode size */
- &buff->blk_dat, /* Source */
- comp_buffer); /* Destination */
- if ( cmp_size < data_read ) /* If compressed */
- {
- buff->len = (cmp_size+OVRHD); /* Length of block */
- buff->blk_typ |= COMP; /* Show compressed */
- memcpy (&buff->blk_dat, /* Start of data */
- comp_buffer, /* Copy from here */
- cmp_size); /* This much */
- }
- calc_crc(SET_CRC, /* Calculate CRC */
- buff->len , /* Length of block */
- out_buffer); /* Where data is */
- status = send_blk( /* Send the block */
- buff->len, /* Block length */
- &syst, /* Read block ptr. */
- out_buffer); /* Buffer pointer */
- time(&finish); /* Get end time */
- if (finish - start) /* Check div/0 */
- {
- #ifdef FTIME
- dat_tmp = (double) data_read;
- syst.s_cps = (short) (dat_tmp /
- difftime(finish,start));
- #else
- syst.s_cps = (short) /* Calc Block CPS */
- (data_read / (finish - start) );
- #endif
- }
- if ( buff->blk_typ == EOF_) /* Last record */
- break;
- }
- syst.s_sta = done; /* Assume normal */
- if (status)
- {
- cancel(); /* Send ^Xes */
- syst.s_sta = abrt; /* Was aborted */
- }
- close_chan(port); /* Close the port */
- file_io(CLOSE, &handle); /* Close the file */
- screen (SCR_SYS,&syst); /* Show status block */
- }
- else /* File not found */
- {
- status = JM_FNF;
- }
- break; /* End of CASE 'S' */
- }
- }
- cleanup:
- free (in_buffer); /* Free buffers */
- /* Two-second timer to display error messages */
- if (status != JM_NRM)
- {
- time(&finish); /* Get system clock */
- finish += 2; /* Add two seconds */
- start = 0;
- while ( finish > start ) /* Wait until the same */
- time(&start);
- }
- screen (SCR_END); /* Clear the screen */
- return status; /* Normal exit */
- }
- /****************************************************************************/
- /* Send the JMODEM block */
- /****************************************************************************/
- word send_blk (word blk_len, register SYS *sys_ptr, register byte *buffer)
- {
- byte ack_buf; /* Buffer for ACK/NAK */
- word tries = 10; /* Attempts to send the block */
- while ((tries--) && (!user_abort))
- {
- write_chan(blk_len,buffer); /* Send the JMODEM block */
- flush(); /* Clear back channel noise */
- do
- {
- ack_buf = (char) 0x00; /* Clear the return buffer */
- read_chan(1,&ack_buf); /* Receive a response */
- } while ( (ack_buf != ACK) /* Stay in loop until we */
- && (ack_buf != CAN) /* ... get something useful */
- && (ack_buf != NAK) /* This helps re-sync in noise */
- && (ack_buf == (char) 0x00)
- && (!user_abort) );
-
- if ( (ack_buf == CAN)
- || user_abort ) /* Check for an abort */
- break; /* User aborted */
- if (ack_buf == ACK) /* If good block */
- {
- if (tries == 9) /* If no retries */
- {
- sys_ptr->s_len += 512; /* Increase block-size */
- if (sys_ptr->s_len > DAT_MAX) /* If too large */
- sys_ptr->s_len = DAT_MAX;
- }
- else
- {
- tries = 9 - tries; /* Use for divisor */
- sys_ptr->s_len = /* Update block length */
- sys_ptr->s_len / tries; /* Div block size */
- if (sys_ptr->s_len < 0x40) /* If less than minimum */
- sys_ptr->s_len = 0x40; /* Set to minimum */
- }
- sys_ptr->s_sta = okay; /* Show status is okay */
- return JM_NRM; /* Show good */
- }
- sys_ptr->s_sta = retry; /* Show a retry */
- screen (SCR_SYS, sys_ptr); /* Write to screen */
- }
- cancel(); /* Send cancel (^Xes) */
- return JM_ABT; /* Abort local program */
- }
- /****************************************************************************/
- /* Receive the JMODEM block */
- /****************************************************************************/
- word recv_blk (word *blk_len, register byte *buffer)
- {
- register JBUF *buff; /* Pointer type JBUF */
- byte nak_buf; /* Buffer for ACK/NAK */
- word tries = 10; /* Attempts to receive the block */
- word ret_val; /* Block length returned */
- buff = (JBUF * )buffer; /* Assign pointer type JBUF */
-
- while ((tries--) && (!user_abort))
- {
- ret_val = read_chan(2,buffer); /* Receive the block size */
- if (ret_val == 2) /* If we received the length */
- {
- *blk_len = buff->len; /* So caller knows size */
- if (*blk_len > DAT_LEN) /* If way out of line */
- break; /* NAK it */
- ret_val = read_chan( /* Get more data */
- (*blk_len)-2 , /* Size to read */
- &buff->blk_typ); /* Where to put it */
- if (ret_val == (*blk_len)-2) /* If we got what we requested */
- return JM_NRM;
- }
- if (buff->blk_typ == CAN) /* If transmitter sent ^Xes */
- break; /* The other side has aborted */
- read_chan (DAT_LEN,buffer); /* Make sure other end stops */
- nak_buf = NAK; /* Get a NAK */
- write_chan(1,&nak_buf); /* Send to remote */
- flush(); /* Flush the buffer */
- }
- cancel(); /* Send cancel (^Xes) */
- return JM_ABT; /* Abort local program */
- }
- /****************************************************************************/
- /* Synchronize during receive */
- /****************************************************************************/
- word rx_sync()
- {
- word tries; /* Attempts to synchronize */
- byte ack_nak; /* Single byte buffer for ACK/NAK */
- flush(); /* Clear the interrupt buffer */
- tries = TRIES; /* Attempts to synchronize */
- while ((!user_abort) && (tries--))
- {
- ack_nak = (char) 0x00; /* Clear the buffer */
- read_chan(1,&ack_nak); /* Receive ACK, NAK, or SYN */
- if (ack_nak == CAN) /* If a ^X */
- break;
- if ( ack_nak == ACK ) /* If a good response */
- return JM_NRM; /* Show handshake */
- if ( ack_nak == NAK ) /* If a good response */
- {
- ack_nak = ACK;
- write_chan(1,&ack_nak); /* Send a ACK response */
- return JM_NRM;
- }
- ack_nak = NAK;
- write_chan(1,&ack_nak); /* Keep sending NAKs */
- }
- cancel(); /* Send cancel (^Xes) */
- return JM_ABT;
- }
- /****************************************************************************/
- /* Send ^Xes to cancel */
- /****************************************************************************/
- void cancel()
- {
- byte buffer = CAN;
- short xes = 6;
- user_abort=0; /* Reset flag so write_chan works */
- while(xes--)
- write_chan(1,&buffer);
- }
- /****************************************************************************/
- /* Synchronize during transmit */
- /****************************************************************************/
- word tx_sync()
- {
- word ret_val;
- ret_val = rx_sync(); /* Call same routine for receive */
- if (!ret_val) /* If success */
- {
- flush(); /* Flush the input buffer */
- timer = 5; /* 5 timer-ticks to wait */
- while (timer); /* Wait for timer */
- }
- return ret_val; /* Return status */
- }
- /****************************************************************************/
- /* Dummy _setenvp procedure to replace the large library module */
- /****************************************************************************/
- #ifdef NOENV /* If a compiler command */
- void _setenvp(void); /* Dummy routine prototype */
- void _setenvp() /* Dummy routine */
- {}
- #endif
- /****************************************************************************/
- /************************ E N D O F M O D U L E **************************/