[net.sources] CP/M paging program for netnews readers

karn (06/10/82)

/* The number of requests for my terminal paging program has been
 * sufficient to warrant posting it on net.sources.
 * This version is set up for the Heathkit H-19;
 * three constants can be changed to configure it for other
 * terminals.
 * Note that no attempt is made to keep track of cursor movement by
 * means other than printing characters, carriage return or line feed
 * characters; hence, this program will get very confused if you try
 * to run emacs, vi, etc., through it.
 *
 * Use BDS-C to compile, and clink with the cio terminal I/O
 * package. Note that I make reference to the terminal and modem port
 * definitions in bdscio.h.
 */
/*
 * Modem buffering program.  A large chunk of memory is used as a circular
 * buffer for incoming modem data, which is sent out to the terminal
 * a crt page at a time.  High/low water marks are used to flow control
 * the host when the buffer is filled.
 *
 * The program is NOT interrupt driven.  Frequent polls are made on
 * both the keyboard and modem ports to avoid losing characters.
 *		March 1982, Phil Karn
 */
#include <a:bdscio.h>
/* Terminal dependent parameters */
#define	ROWS	24
#define	COLS	80
#define	ERASE	"\033E"

#define	BSIZE	32767
#define	HIWATER	32000
#define	LOWATER	31000
#define	CTLS	19
#define	CTLQ	17

char buffer[BSIZE];
char *in,*out;
int count;
int stopped;

main()
{
	register char c;
	int row,col;		/* Cursor position */

	stopped = count = col = row = 0;
	in = out = buffer;
	TTYMode(0);	/* Really raw mode */
	puts(ERASE);	/* Erase screen */

loop:	scanmod();
	scankbd();
	c = getb();
	switch(c & 0x7f){
	case '\n':
		row++;
		break;
	case '\r':
		col = 0;
		break;
	default:
		/* Normal character; check for end of line wraparound */
		col++;
		if(col == COLS){
			col = 0;
			row++;
		}
		break;
	}
	/* Screen full. Wait for any character before proceeding */
	while(row == ROWS){
		while(!kbhit())
			scanmod();
		getchar();
		col = row = 0;
		puts(ERASE);
	}
	/* Don't waste the top line of the screen with the newline */
	if(c != '\n' || row != 0 )
		putchar(c);
	goto loop;
}
scanmod()
{
	/* Scan modem input for activity */
	if(inp(MSTAT) & MIMASK){
		*in++ = inp(MDATA);
		count++;
		if(in == &buffer[BSIZE])
			in = buffer;
		if(!stopped && count > HIWATER){
			stopped = 1;
			putmod(CTLS);
		}
	}
}
scankbd()
{
	/* Do same for keyboard */
	if(kbhit())
		putmod(getchar());
}
/* Fetch char from modem input buffer */
char
getb()
{
	char c;

	while(count == 0){
		scanmod();
		scankbd();
	}
	c = *out++;
	count--;
	if(stopped && count < LOWATER){
		stopped = 0;
		putmod(CTLQ);
	}
	if(out == &buffer[BSIZE])
		out = buffer;
	return(c);
}
/* Send character to modem */
putmod(c)
char c;
{
	while((inp(MSTAT) & MOMASK) == 0)
		scanmod();
	outp(MDATA,c);
}