home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The C Users' Group Library 1994 August
/
wc-cdrom-cusersgrouplibrary-1994-08.iso
/
vol_100
/
140_01
/
cmodem.c
< prev
next >
Wrap
Text File
|
1985-03-10
|
14KB
|
611 lines
/*
CMODEM.C
Revision history: Latest revision first.
04/Jun/82 Removed PMMI defines and ifdef for Australia, much
more extensive documentation added, modem port
defines now obtained from BDSCIO.H. Code tidied up
a bit and VARBAUD ifdefs added, some messages
changed and numerous small changes. Bill Bolton
??/???/81 Patched together from existing Cnet and YAM code by
Steve Passe, Cnode SYSOP, USA.
This program uses the "Christensen" protocol as (used in
YAM/MODEM7) for file transfers.
When you execute CMODEM it signs on and immediately goes into
the terminal mode (without any messages) and is ready to send
ASCII text from your console keyboard out your modem port and
receive ASCII text from your modem port and display it on your
console. An "esc" convention allows file transfers without
leaving the program.
To send a file:
type '<esc> s <cr>',
To receive one:
type '<esc> r <cr>'
(you will be prompted for the file name in both cases)
For other commands:
type '<esc> ? <cr>'
(<esc> is the ASCII escape character)
If you should need to send an <esc> to the remote system simply
hit <esc> twice in a row for each <esc> that you wish to
send. While transfering files CMODEM echos everything, from the
file it sends or receives, to the console so be prepared to see
some weird things when transfering object files. It doesn't
send non-ASCII data (i.e. control codes) to the console but
replaces them with a full stop.
This is really just a very minimal implementation of a program
to send and receive files in using the "Christensen" protocols.
For a "full feature" communications program you should
definitely look at YAM.
Add whatever code necessary to the initialize_port() function
to setup your port for:
8 bits,
1 stop bit,
no parity,
baud rate of 300 if necessary,
etc.).
If you are certain that your modem port will always be
initialised in the correct data format before entry to CMODEM
(perhaps initialised at cold boot time) you can just leave this
function empty.
If your modem UART has programmable baud rates you (2651, 8250
or whatever) and you want to be able to set them from CMODEM
you should insert code in the baud() function and define
VARBAUD. This is probably not really worthwhile for Australian
conditions but I left the code in place in case anyone wants to
try it. It is easier to leave your UART set at 300 baud as the
CCITT V21 modems currently available in Australia just wont run
any faster than 300 baud....maybe that will change with digital
filtering in the future.
If you are using a direct connect modem and you want to be able
to drop the line when finished you should insert code in the
hangup() function.
The defines for the modem port must be set up in the BDSCIO.H
file for your system. Note that this program uses BDOS call 6
(direct console I/O) for all console access and therefore
should be easily usable by those with odd consoles that are not
easily defined in BDSCIO.H (such as Sorcerer or memory mapped).
If you have TELNET running you should be able to compile CMODEM
and run it without any modifications (make sure thae data
format as described above is correct).
This program uses large chunks of the YAM package for file
transfers (thanks Chuck).
*/
#include "bdscio.h"
#define SOH 0x01
#define EOT 0x04
#define ACK 0x06
#define NAK 0x15
#define CAN 0x18
#define RETRYMAX 10
#define TIMEOUT (-1)
#define PATHLEN 20
#define WCEOT (-2)
#define CLKMHZ 4 /* CPU speed in Mhz */
#define CONSTAT 2
#define CONIN 3
#define DIR_IO 0x06
#define INPUT 0xff
#define NORMAL 0x1c
#define ORIG 0x01
#define ANSWER 0x02
#define READY 0x5f
#define OPT300 0x20
#define OPT600 0X00
#define CLEAR 0x3f
#define FLAG char
int Baud;
FLAG Tfile, Rfile;
char Tname[PATHLEN], Rname[PATHLEN];
unsigned T1pause, Timeout;
char File_buf[BUFSIZ];
char Checksum, Lastrx;
int Wcj, Firstch;
main()
{
char received, to_send, in_modem(), getch(), escflag;
Tfile = Rfile = FALSE;
T1pause = 311*CLKMHZ;
escflag = NULL;
Baud = 300;
initialize_port();
printf("\n\CMODEM a 'Christensen' protocol file transfer program\n\n");
while (TRUE) {
if (received = in_modem()) {
putch(received);
}
else if (to_send = getch()) {
if (to_send == ESC) {
if (escflag) {
escflag = NULL;
out_modem(to_send);
}
else escflag = TRUE;
}
else {
if (escflag) {
escflag = NULL;
commands(to_send);
}
else out_modem(to_send);
}
}
}
}
char /* get incoming byte from modem */
in_modem()
{
if (inp(MSTAT) & MIMASK) { /* status & char ready bit */
return inp(MDATA); /* ok, get the char */
} /* or */
else return FALSE; /* return empty */
}
out_modem(out_char) /* send byte to modem */
char out_char;
{
while (!(inp(MSTAT) & MOMASK))
; /* wait */
outp(MDATA, out_char); /* finally, send it */
}
char /* keyboard 'hook' and 'filter' */
getch()
{
return bdos(DIR_IO, INPUT);
}
commands(cmd)
char cmd;
{
int baudrate;
switch (tolower(cmd)) {
#ifdef VARBAUD
case 'b':
printf("\n\n\tbaudrate: ");
scanf("%d", &baudrate);
baud(baudrate);
break;
#endif
case 'q':
hangup();
break;
case 'r':
printf("\n\n\treceive: ");
scanf("%s", Rname);
download(Rname);
break;
case 's':
printf("\n\n\tsend: ");
scanf("%s", Tname);
upload(Tname);
break;
case '?':
case 'h':
help();
break;
default:
printf("\nbad command: '%c'\n", cmd);
}
}
/*
Most of the following functions taken from YAM package by
Chuck Forsberg, BDS "C" UG disk Utilities #, some modifications made for
compatibility with cnode code.
*/
upload(filename)
char *filename;
{
int err_flag;
if(opentx(Tname)==ERROR) {
printf("\ncan't open '%s'\n", filename);
return ERROR;
}
printf("\n'%s' open for transmission\n", Tname);
if(wctx()==ERROR) {
abort('t');
}
return OK;
}
wctx()
{
int sectnum, attempts;
char txbuf[SECSIZ];
printf("Awaiting initial NAK - ");
while((Firstch=readbyt(400))!=NAK && Firstch!=TIMEOUT && Firstch!=CAN)
{
printf("\n\tgot 0x%2x, not NAK", Firstch);
/* let user see it if strange char */
}
if(Firstch==CAN)
return ERROR;
if (Firstch == TIMEOUT) {
printf("\ntimeout on initial NAK!");
return ERROR;
}
sectnum=1;
while(filbuf(txbuf, SECSIZ)) {
if(wcputsec(txbuf, sectnum)==ERROR) {
return ERROR;
}
else {
sectnum++;
}
}
closetx();
attempts=0;
do {
out_modem(EOT);
purgeline(); /* why? */
attempts++;
}
while((Firstch=(readbyt(100)) != ACK) && attempts < RETRYMAX)
; /* wait for ACK */
if(attempts == RETRYMAX) {
printf("\nNo ACK on EOT; Aborting... ");
return ERROR;
}
else
return OK;
}
wcputsec(txbuf, sectnum)
char *txbuf;
int sectnum;
{
char attempts, *cp, xbyt;
Firstch=0; /* part of logic to detect CAN CAN */
for(attempts=0; attempts <= RETRYMAX; attempts++) {
Lastrx= Firstch;
out_modem(SOH);
out_modem(sectnum);
out_modem(~sectnum);
Checksum=0;
for(Wcj=SECSIZ,cp=txbuf; --Wcj>=0; ) {
out_modem(*cp);
isprint(*cp) ? putchar(*cp) : putchar('.');
Checksum += *cp++;
}
out_modem(Checksum);
purgeline();
Firstch=readbyt(100);
if(Firstch==CAN && Lastrx==CAN) {
cancan:
printf("\nReceiver CANcelled transmission ");
return ERROR;
}
else if(Firstch==ACK)
return OK;
else if(Firstch==TIMEOUT)
printf("\nTimeout on sector ack attempt %d", attempts);
else {
printf("\nGot %2x for sector ACK attempt %d", Firstch, attempts);
for(;;) {
Lastrx=Firstch;
if((Firstch=readbyt(1))==TIMEOUT)
break;
if(Firstch==CAN && Lastrx==CAN)
goto cancan;
}
}
}
printf("\nNo ACK on sector; Abort ");
return ERROR;
}
opentx(name)
char *name;
{
printf("'%s' ", name);
if(fopen(name, File_buf)==ERROR){
return ERROR;
}
Tfile= TRUE;
return OK;
}
/* fill buf with count chars padding with ^Z for CPM */
filbuf(buf, count)
char *buf;
int count;
{
int c, m;
m=count;
while((c=getc(File_buf))!=EOF) {
*buf++ =c;
if(--m == 0)
break;
}
if(m==count)
return 0;
else /* won't pad properly under cp/m because of */
/* two different EOF's (EOF & CPMEOF), and the */
/* fact that cp/m doesn't know type of file */
/* instinctively, fix later */
while(--m>=0)
*buf++ = 0x1a; /* control z */
return count;
}
download(filename)
char *filename;
{
printf("ready to receive '%s'\n", Rname);
if(wcrx(Rname)==ERROR) {
abort('r');
}
return OK;
}
/*
* Adapted from CMODEM13.C, written by
* Jack M. Wierda and Roderick W. Hart
*/
wcrx(name)
char *name;
{
int sectnum, sectcurr, sectcomp;
char *cp, rxbuf[128], sendchar;
if(openrx(name)==ERROR)
return ERROR;
sectnum=0;
sendchar=NAK;
for(;;) {
out_modem(sendchar); /* send it now, we're ready! */
sectcurr=wcgetsec(rxbuf, (sectnum & 0x7f) ? 50 : 130);
if(sectcurr==(sectnum+1 & 0xff)) { /* mask low byte */
sectnum++;
for(cp=rxbuf,Wcj=128; --Wcj>=0; )
if(putc(*cp++, File_buf)==ERROR) {
printf("\nDisk Full\n");
return ERROR;
}
sendchar=ACK;
}
else if(sectcurr==sectnum) {
printf("\nReceived dup Sector %d",sectcurr);
sendchar=ACK;
}
else if(sectcurr==WCEOT) {
out_modem(ACK);
/* don't pad the file any more than it already is */
closerx(FALSE);
return OK;
}
else {
printf(" Sync Error\n");
return ERROR;
}
}
}
/*
* wcgetsec fetches a Ward Christensen type sector.
* Returns sector number encountered or ERROR if valid sector not received,
* or CAN CAN received
* or WCEOT if eot sector
* time is timeout for first char, set to 4 seconds thereafter
***************** NO ACK IS SENT IF SECTOR IS RECEIVED OK **************
* (Caller must do that when he is good and ready to get next sector)
*/
wcgetsec(rxbuf, time)
char *rxbuf;
int time;
{
int sectcurr,errors;
char *cp;
for(Lastrx=errors=0; errors<RETRYMAX; errors++) {
do {
Firstch=readbyt(time);
}
while(Firstch != SOH && Firstch != TIMEOUT && Firstch != EOT
&& Firstch != CAN); /* wait for one of these */
if(Firstch==SOH) {
sectcurr=readbyt(1);
if((sectcurr+readbyt(1))==255) {
Checksum=0;
for(cp=rxbuf,Wcj=128; --Wcj>=0; ) {
isprint(*cp = readbyt(1)) ? putchar(*cp) : putchar('.');
Checksum += (*cp++);
}
if(((Checksum-readbyt(1))& 0xff)==0)
return sectcurr;
else
printf("Checksum Error #%d", errors);
}
else
printf("Sector number garbled #%d", errors);
}
else if(Firstch==EOT)
return WCEOT;
else if(Firstch==CAN) {
if(Lastrx==CAN) {
printf("\nSender CANcelled");
return ERROR;
} else {
Lastrx=CAN;
continue;
}
}
else if(Firstch==TIMEOUT)
printf("\nSOH Timeout #%d", errors);
Lastrx=0;
while(readbyt(1)!=TIMEOUT)
;
out_modem(NAK);
time=40;
}
/* try to stop the bubble machine. */
out_modem(CAN);out_modem(CAN);out_modem(CAN);
return ERROR;
}
openrx(name)
char *name;
{
printf("\nsaving it as '%s'", name); /* show the name right away */
if(fopen(name, File_buf) != ERROR) {
fclose(File_buf);
printf("\nI already have one, try another name");
return ERROR;
}
if(fcreat(name, File_buf)==ERROR){
printf("\nCan't create '%s'", name);
return ERROR;
}
Rfile= TRUE;
return OK;
}
readbyt(decisecs)
int decisecs;
{
if (inp(MSTAT) & MIMASK)
return inp(MDATA);
while (--decisecs >= 0) {
if (inp(MSTAT) & MIMASK)
return inp(MDATA);
if (bdos(DIR_IO, INPUT)) /* local forced timeout */
return TIMEOUT;
if (inp(MSTAT) & MIMASK)
return inp(MDATA);
for (Timeout = T1pause; --Timeout; )
if (inp(MSTAT) & MIMASK)
return inp(MDATA);
}
return TIMEOUT;
}
abort(flag)
char flag;
{
flag == 't' ? closetx() : closerx();
out_modem(CAN);out_modem(CAN);out_modem(CAN);
return ERROR;
}
closetx()
{
if(Tfile) {
fclose(File_buf);
printf("\n\n%s closed", Tname);
Tfile=FALSE;
}
}
/* for the upld overlay command */
closerx()
{
if(Rfile) {
fflush(File_buf);
fclose(File_buf);
printf("\n\n%s closed", Rname);
Rfile=FALSE;
}
}
purgeline()
{
while (inp(MSTAT) & MIMASK)
inp(MDATA);
}
/* end of code from yam */
help()
{
printf("\n\tCommands:");
#ifdef VARBAUD
printf("\n\t\t\t'b'aud <baudrate>");
#endif
printf("\n\t\t\t'r'eceive <filename>");
printf("\n\t\t\t's'end <filename>");
printf("\n\t\t\t'q'uit");
printf("\n");
}
initialize_port()
{
/* put code to init your modem port here */
/* this is for initializing an 8251 uart...
outp(MSTAT,0xaa); /* insure next port access of 'command' type */
outp(MSTAT,0x40); /* 'command' a reset */
outp(MSTAT,0x8e); /* set the 'mode', 1-stop bit, sync 16x */
/* eight bits, no parity */
outp(MSTAT,0x27); /* trans, DTR, rec, RTS */
*/
}
isprint(c)
char c;
{
return (c>=' ' && c <= '~') || (c == 0x0d) || (c == 0x0a) || (c==0x09);
}
hangup()
{
/* insert code to hang up line (using CCITT 108)
if you have a direct connect modem */
printf("\nhanging up...\n");
exit();
}
#ifdef VARBAUD
baud(baudrate)
int baudrate;
{
/* Insert code to set the baud rate of your UART here */
}
#endif