/*                          *** ibmtty.c ***                         */
/*                                                                   */
/* IBM-PC microsoft "C" under PC-DOS                                 */
/*                                                                   */
/* Terminal emulation program with file upload and download capabili-*/
/* ties.  Optimized to communicate with a DEC VAX 11/780.            */
/*                                                                   */
/* Written by L. Cuthbertson, April 1984.                            */
/*                                                                   */
/*********************************************************************/
/*                                                                   */
 
#include <stdio.h>
 
#define TRUE 1
#define FALSE 0
#define XON '\021'
#define XOFF '\023'
#define ESC '\033'
#define CONZ '\032'
#define DEL '\177'
 
#define LCR 0x3FB	/* 8250 line control register */
#define DLL 0x3F8	/* 8250 least significant divisor latch */
#define DLM 0x3F9	/* 8250 most significant divisor latch */
#define LSR 0x3FD	/* 8250 line status register */
#define IIR 0x3FA	/* 8250 interrupt identification register */
#define IER 0x3F9	/* 8250 interrupt enable register */
#define MCR 0x3FC	/* 8250 modem control register */
#define MSR 0x3FE	/* 8250 modem status register */
#define RBR 0x3F8	/* 8250 receiver buffer register */
#define THR 0x3F8	/* 8250 transmitter holding register */

char *lcr=(char*)LCR, *dll=(char*)DLL, *dlm=(char*)DLM;
char *lsr=(char*)LSR, *iir=(char*)IIR, *ier=(char*)IER;
char *mcr=(char*)MCR, *msr=(char*)MSR, *rbr=(char*)RBR;
char *thr=(char*)THR;
 
struct buf {
	char *fbuf;
	char *wbuf,*rbuf;
	char *lbuf;
};
 
struct buf mem;			/* file receiption/transmission buff */

char combuf[64];		/* transmission buffer */
struct buf com;

char crtbuf[512];		/* receiption buffer */
struct buf crt;
 
char outfil[13];		/* transmit file name */
FILE *outchan;
 
char infil[13];			/* receive file name */
FILE *inchan;
 
char lecho = FALSE;
char gotxoff = FALSE;
char sendxoff = FALSE;
char sentxoff = FALSE;
 
/*********************************************************************/
/*                                                                   */
main(argc,argv)
int argc;
char *argv[];
{
	char *malloc();
	int iret,port;
	unsigned u;
 
	/* initialize file receiption/transmission */
	inchan = FALSE;
	outchan = FALSE;

	/* set up buffer structures */
	com.fbuf = com.wbuf = com.rbuf = &combuf[0];
	com.lbuf = &combuf[0] + sizeof(combuf) - 1;
	crt.fbuf = crt.wbuf = crt.rbuf = &crtbuf[0];
	crt.lbuf = &crtbuf[0] + sizeof(crtbuf) - 1;

	/* allocate as much memory as possible to file buffer */
	for (u=65023;;u -= 512) {
		if (u < (512+(5*_BUFSIZ))) {
			writes("\r\n\007*** not enough memory available ***");
			exit();
		}
		mem.fbuf = malloc(u);
		if (mem.fbuf != 0) {
			free(mem.fbuf);
			u -= 5*_BUFSIZ;
			mem.fbuf = malloc(u);
			break;
		}
	}

	mem.wbuf = mem.rbuf = mem.fbuf;
	mem.lbuf = mem.fbuf + u - 1;

	/* determine communications port to use */
	if (argc > 1) {
		sscanf(argv[1],"%d",&port);
		port--;
		if ((port < 0) || (port > 1)) {
			writes("\r\n\007*** invalid communications port - enter 1 or 2 ***");
			exit();
		}
	} else {
		port = 0;
	}

	/* adjust port addresses if neccessary */
	if (port == 1) {
		lcr -= 0x100;
		dll -= 0x100;
		dlm -= 0x100;
		lsr -= 0x100;
		iir -= 0x100;
		ier -= 0x100;
		mcr -= 0x100;
		msr -= 0x100;
		rbr -= 0x100;
		thr -= 0x100;
	}
 
	/* initialize 8250  - use DOS "MODE" command */
	outp(mcr,3);		/* send DTR and RTS */
	pause(1.);

	/* check for carrier */
	if ((inp(msr)&48) != 48) {
		writes("\r\n\007*** carrier not detected - please check your connections ***");
		exit();
	}

	/* courtesy message */
	cursor(25,1);
	writes("\r\n*** you are now connected ***\r\n");
 
	/* polling loop */
	for (;;) {

		/* check communications port for reception of data */
		if ((readcomm()) != 0)
			;

		/* check if screen output needed */
		if ((wrtscr()) != 0)
			;

		/* check communications port for receiption of data */
		if ((readcomm()) != 0)
			;

		/* write characters to comm port for transmission */
		if ((wrtcomm()) != 0)
			;

		/* check communications port for receiption of data */
		if ((readcomm()) != 0)
			;

		/* check for screen output */
		if ((wrtscr()) != 0)
			;

		/* check communications port for receiption of data */
		if ((readcomm()) != 0)
			;

		/* check for keyboard entry */
		if ((readkey()) != 0)
			;
 
	}
}

/*********************************************************************/
/* Check the comm port for receiption of data and puts recieved data */
/* into buffers.  Returns a -1 if error detected, 0 if no data, or   */
/* the character received.                                           */
/*                                                                   */
int readcomm()
{
	static char oldc = NULL;
	static char c;
	static int iret;

	/* check comm port for data receiption */
	iret = inp(lsr);
	if ((iret&1) != 0) {

		/* ignore data overrun errors */
		/* expect to loose padd characters at high baud rate */
		if ((iret&2) != 0) {
			;

		/* framing or parity error */
		} else if ((iret&12) != 0) {
			writes("\007*** data reception error ***");
			return(-1);

		/* break currenly being sent */
		} else if ((iret&16) != 0) {
			return(0);	/* break detect */
		}

		/* read character */
		c = inp(rbr)&127;

		/* process character */
		if (c == XOFF) {
			gotxoff = TRUE;
		} else if (c == XON) {
			gotxoff = FALSE;
		} else if (c != NULL) {

			/* prevent CR CR LF line endings */
			if ((c != '\r') || (oldc != '\r')) {

				/* store character in receiption buffer */
				if ((putbuf(&crt,c)) == (-1)) {
					writes("\007*** screen buffer overflow ***");
					return(-1);
				}

				/* store character if file receiption buffer */
				if (inchan) {
					if ((putbuf(&mem,c)) == (-1)) {
						writes("\007*** file receiption buffer overflow ***");
						inchan = FALSE;
						return(-1);
					}
				}
			}
			oldc = c;
		}
		return(c);
	}
 
	/* no character found */
	return(0);
}

/*********************************************************************/
/* write characters from the screen buffer to the screen.  Returns a */
/* -1 if error occured, 0 if no characters in buffer, or character   */
/* written.                                                          */
/*                                                                   */
int wrtscr()
{
	static char c;
	static int i,iret;

	/* get character from screen buffer */
	if ((iret = getbuf(&crt)) != (-1)) {
		c = iret&127;	/* strip parity */

		/* if line feed then scroll screen */
		if (c == '\n') {
			if ((iret = doscr()) == (-1)) {
				return(-1);
			}

		/* handle tabs seperatly */
		} else if (c == '\t') {
			if ((iret = dotab()) == (-1)) {
				return(-1);
			}

		/* output other characters */
		} else {
			biostty(c);
		}
		return(c);
	}

	/* no character in buffer */
	return(0);
}

/*********************************************************************/
/* scroll the screen up one line at a time while checking the comm   */
/* port for data receiption.  Returns a -1 if an error occured.      */
/*                                                                   */
int doscr()
{
	static char fillchar;
	static int trow,tlcol,brow,brcol;
	static int iret;

	/* initialize */
	fillchar = 0x20;	/* fill opened line with blanks */
	tlcol = 0;		/* top left column of window */
	brcol = 79;		/* bottom right column of window */

	/* begin check and scroll loop */
	for(trow=0;trow<24;trow++) {

		/* check comm port for data receiption */
		if ((iret = readcomm()) == (-1)) {
			return(-1);
		}

		/* scroll 1 line up */
		brow = trow + 1;
		biosup(1,trow,tlcol,brow,brcol,fillchar);
	}

	return(0);
}

/*********************************************************************/
/* Output a horizontal tab to screen while checking communications   */
/* port for transmission.  Returns a -1 if an error occured.         */
/*                                                                   */
int dotab()
{
	static int iret,irow,icol;

	/* check comm port for receiption of data */
	if ((iret = readcomm()) == (-1)) {
		return(-1);
	}

	/* find cursor position */
	iret = biospos();
	icol = iret & 255;	/* column returned in low order bits */
	irow = iret >> 8;	/* row returned in high order bits */

	/* check comm port for receiption of data */
	if ((iret = readcomm()) == (-1)) {
		return(-1);
	}

	/* calculate tab position */
	icol++;
	while ((icol % 8) != 0) {
		icol++;
	}

	/* set cursor to that position */
	biosset(irow,icol);

	return(0);
}

/*********************************************************************/
/* write characters to communications port for transmission.  Return */
/* a -1 if error, 0 if no characters written, or character written.  */
/*                                                                   */
int wrtcomm()
{
	static char c,pushback;
	static int iret,lstat;

	/* write characters to communications port for transmission */
	lstat=inp(lsr);
	if (((lstat&32) != 0) && (!gotxoff)) {

		/* write characters received from keyboard */
		if ((iret=getbuf(&com)) != (-1)) {
			c = iret&127;
			outp(thr,c);
			if (lecho) putbuf(&crt,c);
			return(c);

		/* write characters from file */
		} else if (outchan) {
			if (pushback != NULL) {
				outp(thr,pushback);
				pushback = NULL;
				if (lecho) putbuf(&crt,pushback);
				return(pushback);
			}

			if ((iret=getbuf(&mem)) == (-1)) {
				outp(thr,CONZ);
				fclose(outchan);
				outchan = FALSE;
				mem.wbuf = mem.rbuf = mem.fbuf;
				return(0);
			}

			c = iret&127;
			if (c == '\n') {
				outp(thr,'\r');
				if (lecho) {
					putbuf(&crt,'\r');
					putbuf(&crt,'\n');
				} else {
					dowait();
				}
			} else {
				outp(thr,c);
				if (lecho) putbuf(&crt,c);
			}
			return(c);
		}
	}

	return(0);
}

/*********************************************************************/
/* Function to wait until the receiption of a line feed after the    */
/* transmission of a carriage return.                                */
/*                                                                   */
int dowait()
{
	int iret;

	/* loop */
	while ((iret=readcomm()) != '\n') {
		if (iret == -1) break;
	}

	return(0);
}

/*********************************************************************/
/* Check the keyboard for receipt of a character and buffer it.      */
/* Returns a -1 if error occured, 0 if no character at keyboard, or  */
/* character typed.                                                  */
/*                                                                   */
/* kbhit() is a special IBM-PC microsoft "C" function.               */
/*                                                                   */
int readkey()
{
	static char c;

	/* check for keyboard entry */
	if (kbhit()) {
		c = readc();

		/* display menu if ESCAPE entered */
		if (c == ESC) {
			doesc();
			return(c);
		}

		/* character mapping */
		if (c == '\b') {
			c = DEL;
		} else if (c == '\n') {
			c = '\r';
		}

		/* store character in keyboard buffer */
		if (putbuf(&com,c) == (-1)) {
			writes("\007");		/* buffer full */
			return(-1);
		}
		return(c);
	}
 
	return(0);
}

/*********************************************************************/
/* Write a character into a buffer.  Returns a -1 if overflow.       */
/*                                                                   */
int putbuf(p,c)
struct buf *p;
char c;
{
	*p->wbuf = c;
 
	/* increment (possible wrap-around) pointer */
	if (++p->wbuf > p->lbuf) p->wbuf = p->fbuf;
 
	/* if overflow, indicate */
	if (p->wbuf == p->rbuf) {
		if (--p->wbuf < p->fbuf) p->wbuf = p->lbuf;
		return(-1);
	}
 
	return(0);
}
 
/*********************************************************************/
/* Read a character from a buffer.  Returns a -1 if buffer is empty. */
/*                                                                   */
int getbuf(p)
struct buf *p;
{
	static int c;
 
	/* empty */
	if (p->rbuf == p->wbuf) return(-1);
 
	/* get char, increment (possible wrap-around) pointer */
	c = *p->rbuf;
	if (++p->rbuf > p->lbuf) p->rbuf = p->fbuf;
 
	return(c);
}
 
/*********************************************************************/
/* Function to do special features.                                  */
/*                                                                   */
int doesc()
{
	char ans[3],memsiz[6];
	char c;
	int iret,ians;

	/* output special information */
	loop:
	escreen(2);		/* erase screen */

	writes("\r\n memory buffer is : ");
	sprintf(memsiz,"%u",(mem.wbuf-mem.fbuf));
	writes(memsiz);
	writes("/");
	sprintf(memsiz,"%u",(mem.lbuf-mem.fbuf));
	writes(memsiz);

	writes("\r\n receive file is  : ");
	writes((inchan)?infil:"<OFF>");
 
	writes("\r\n transmit file is : ");
	writes((outchan)?outfil:"<OFF>");
 
	writes("\r\n local echo is    : ");
	writes((lecho)?"<ON>":"<OFF>");

	/* output menu and accept choice */
	if ((ians=chosit("comm",ans,sizeof(ans))) == (-1)) {
		writes("\r\n\007*** error displaying menu ***");
		ians = 4;
	}

	/* execute command */
	switch (ians) {
 
		case 5:
			if (inchan) {
				wrtbuf();
				fclose(inchan);
			}
			if (outchan) fclose(outchan);
			outp(mcr,0);	/* drop DTR and RTS */
			writes("\r\n");
			exit();
			break;

		case 4:
			cursor(25,1);
			writes("\r\n*** you are now back as a terminal ***\r\n");
			return(0);
			break;
 
		case 3:
			lecho = !lecho;
			break;
 
		case 2:
			if (outchan) {
				fclose(outchan);
				outchan = FALSE;
				mem.wbuf = mem.rbuf = mem.fbuf;
			} else {
				if (inchan) {
					wrtbuf();
					fclose(inchan);
					inchan = FALSE;
				}
				sendfile:
				writes("\r\n\nFilename? ");
				reads(outfil,sizeof(outfil));
				if (outfil[0] == NULL) goto done;

				if ((outchan=fopen(&outfil[0],"r")) == NULL) {
					writes("\007  *** can't open file ***");
					goto sendfile;
				}

				readbuf();
			}
			break;
 
		case 1:
			if (inchan) {
				wrtbuf();
				fclose(inchan);
				inchan = FALSE;
			} else {
				if (outchan) {
					fclose(outchan);
					outchan = FALSE;
					mem.wbuf = mem.rbuf = mem.fbuf;
				}
				savefile:
				writes("\r\n\nFilename? ");
				reads(infil,sizeof(infil));
				if (infil[0] == NULL) goto done;

				if ((inchan=fopen(&infil[0],"w")) == NULL) {
					writes("\007  *** can't open file ***");
					goto savefile;
				}
			}
			break;
 
	}
	done:
	goto loop;
}
 
/*********************************************************************/
/* Function to write the data receiption buffer to output file.      */
/*                                                                   */
int wrtbuf()
{
	for (mem.rbuf=mem.fbuf;mem.rbuf<mem.wbuf;mem.rbuf++) {
		fputc(*mem.rbuf,inchan);
	}
 
	mem.wbuf = mem.rbuf = mem.fbuf;
	return(0);
}

/*********************************************************************/
/* Function to read a file into the file transmission buffer.        */
/* Returns a 0 if successful, or a -1 if buffer overflow.            */
/*                                                                   */
int readbuf()
{
	static char c;
	static int iret;

	/* loop until end-of-file is found */
	while ((iret=getc(outchan)) != EOF) {
		c = iret&127;
		if ((putbuf(&mem,c)) == (-1)) {
			writes("\r\n\007*** file transmission buffer overflow ***");
			return(-1);
		}
	}

	return(0);
}
