Category : Recently Uploaded Files
Archive   : JMD311YF.ZIP
Filename : JMODEM_A.C
/* 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
#include
#include
#include
#include "jmodem.h" /* JMODEM primatives */
/* #define DEBUG */
#ifdef __TURBOC__
#include "jtime.h"
#else
#define OLD_TIME
#endif
#ifdef __TURBOC__ /* 3.11y */
#include
#else
#include
#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 */
/****************************************************************************/
#ifdef __TURBOC__
int main(int argc, char *argv[])
#else
short main(short argc, char *argv[])
#endif
{
register byte *io_ptr; /* Select buffers to use for I/O */
register JBUF *buff; /* A pointer for the JMODEM block */
/* items above moved to give compiler a chance at using registers 3.11y(b)*/
byte *in_buffer; /* Pointer to input buffer */
byte *out_buffer; /* Pointer to output buffer */
byte *comp_buffer; /* Pointer to compression buffer */
byte *file_name; /* Filename */
byte function; /* Receive, Transmit */
#ifdef OLD_TIME /* 3.11y(e) */
time_t start; /* Start time */
time_t finish; /* End time */
#else
ulong start; /* 3.11y(e) */
ulong finish; /* 3.11y(e) */
uint t_temp; /* 3.11y(f) */
#endif
#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 */
#ifdef USE_ENCODE /* 3.11y(b) */
word cmp_size; /* Size after compression */
#endif
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 */
{
#ifdef OLD_TIME
time(&start); /* Get starting time */
#else
start = peekL(0x0040,0x006C); /* 3.11y(f) */
#endif
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 */
#ifdef OLD_TIME /* 3.11y(e) */
time(&finish); /* Get end time */
#else
finish = peekL(0x0040,0x006C); /* 3.11y(f) */
if(finish < start)
start -= 86400L;
#endif
if (finish - start) /* Check div/0 */
{
#ifdef FTIME
dat_tmp = (double) data_written;
syst.s_cps = (short) (dat_tmp /
difftime(finish,start));
#endif
#ifdef OLD_TIME
syst.s_cps = (short)
(data_written / finish-start);
#else
t_temp = (uint) (((finish-start)*100L)/182L);
if(t_temp == 0) t_temp++;
syst.s_cps = (short)( (data_written/t_temp) );
syst.s_cps *= 100;
#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 */
{
#ifdef OLD_TIME
time(&start); /* Get starting time */
#else
start = peekL(0x0040,0x006C);
#endif
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 */
#ifdef USE_ENCODE /* 3.11y(b) killed */
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 */
}
#endif
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 */
#ifdef OLD_TIME /* 3.11y(e) */
time(&finish); /* Get end time */
#else
finish = peekL(0x0040,0x006C);
if(finish < start)
start -= 86400L;
#endif
if (finish - start) /* Check div/0 */
{
#ifdef FTIME
dat_tmp = (double) data_read;
syst.s_cps = (short) (dat_tmp /
difftime(finish,start));
#endif
#ifdef OLD_TIME
syst.s_cps = (short) /* Calc Block CPS */
(data_read / (finish - start) );
#else
t_temp = (uint) (((finish-start)*100L)/182L);
if(t_temp == 0) t_temp++;
syst.s_cps = (short)( (data_read/t_temp) );
syst.s_cps *= 100;
#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)
{
#ifdef OLD_TIME
time(&finish); /* Get system clock */
finish += 2;
start = 0;
while ( finish > start ) /* Wait until the same */
time(&start);
#else
delay(2000);
#endif
}
screen(SCR_END,NULL); /* 7-2-94 3.11y(a) added NULL Clear the screen */
return status; /* Normal exit */
} /* main() */
/****************************************************************************/
/* Send the JMODEM block */
/* 3.11y(b) 7-2-94 reduced max send size to 4k bytes less problem when hits */
/* SDAT_MAX. DAT_MAX remains at _8K for compatability with other versions */
/* of jmodem including the .asm versions. */
/****************************************************************************/
#define SDAT_MAX 4096 /* 3.11y(b) */
word send_blk (word blk_len, register SYS *sys_ptr, register byte *buffer)
{
static byte ack_buf; /* Buffer for ACK/NAK 3.11y(c) static */
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 > SDAT_MAX) /* If too large */
sys_ptr->s_len = SDAT_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 */
static byte nak_buf; /* Buffer for ACK/NAK 3.11y(c) */
word tries = 10; /* Attempts to receive the block */
static word ret_val; /* Block length returned 3.11y(c) */
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 **************************/
Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!
This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.
But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/