/************************************************
*        Terminal "Emulator" for RSX-11M        *
* Environment: DECUS C, RSX-11M Ver. 4.0	*
* Written: 03/14/83,  D. Soldahl  PMG		*
*						*
* Purpose: Pass all characters through a port	*
*	   to another computer (& back)		*
*						*
* Notes: Uses Qio's with AST's to asynchonously	*
*	 Input from both ports and output to	*
*	 both ports.				*
*	 Terminals should be set to typeahead,	*
*	 full duplex before running.		*
************************************************/



#include <stdio.h>
#include <clang.c>
#include <cx.h>

/*** #define debug 1 ***/

#define BSIZ 4096

#define IS_TMO 002

#define IO_ATT 01400
#define IO_RAL 01010
#define IO_WVB 011000
#define IO_WAL 0410
#define IO_KIL 012
#define IO_DET 02000

#define TF_ESQ 020
#define TF_NOT 002
#define TF_RNE 020
#define TF_TMO 0200

#define TC_TBF 57


extern int qio();
extern wtse();
extern wtlo0();
extern int $dsw;

extern rdast();	/* forward reference */
extern wrast();


    int fnc;	/* parm definitions for qio */
    int eflag;
    int iosb[2];
    int devpar[6];

    struct bufsta {		/* Tied to IOSB so AST gets address */
		int	iosb0;
		int	iosb1;
		int	lun;
		boolean	iordy;		/* Flag to indicate ready for I/O */
		int	iocnt;		/* I/O in progress char count 	*/
		int	*count;		/* Ptr to char count for buffer	*/
		char	*bufptr;	/* Ptr to next place for I/O	*/
		char	*bufstart;	/* Ptr to start of ring buffer	*/
		char	*bufend;	/* Ptr to end of ring buffer	*/
		}    bstat[4];		/* 0 & 1 = Input status blocks	*/
					/* 2 & 3 = Output status blocks	*/

    char    buff[2][BSIZ];		/* Ring Buffers for I/O */
    int	    count[2];			/* Count of characters in buffer */
    static  char *term[2]= {"TI0: ","TI0: "};	/* Port Device Names */
    boolean abort;


main(argc,argv)

char *argv[];

{


    int		mask;
    register	int c;
    register	int i;


#ifdef debug
    iff argc == 1 then
    {
	cpystr(term[0],argv[0]);
	error("Arg = %s, Num = %o\n",term[0],otoi(term[0]));
    }
#endif

    for (i = 1; i<argc; i++)
    {
	iff (i > 2) or ((c = strlen(argv[i])) > 5) or (c < 2) then
	    error("Format is: TERM TTi: [TTj:]");

	cpystr(term[i-1],argv[i]);
    }


	/* Initialize I/O Buffers */

    count[0] = 0;			/* Character Count to 0 */
    count[1] = 0;

    for (i = 0; i < 4; i++)
    {
	bstat[i].iordy = true;	/* Buffer ready for input or output flag */

	/* Init buffer pointers etc. */
	bstat[i].bufptr = bstat[i].bufstart = &buff[i & 1];
	bstat[i].count = &count[i & 1];
	bstat[i].bufend = &(buff[i & 1][BSIZ - 1]);

#ifdef debug
    	printf("%d: IOSB = %o, Ptr = %o, &count = %o, end = %o\n",i,
		&bstat[i].iosb0,bstat[i].bufptr,bstat[i].count,
		bstat[i].bufend);
#endif
	}

    mask = 074;			/* Event flags 3 - 6 */


    attach(term[0],4);		/* Attach Terminals (lun was 2,3 not 4,5 */
    bstat[0].lun = 4;		/* input 0 eflag = 3 */
    bstat[3].lun = 4;		/* output 1 eflag = 5 */
    attach(term[1],5);
    bstat[1].lun = 5;		/* input 1 eflag = 4 */
    bstat[2].lun = 5;		/* output 0 eflag = 6 */


    iosb[0] = 0;			/* Clear ast error flag */

    repeat				/* Cycle for each way, In & Out */
    {
	for (i = 0; i < 2; i++)
	{
	    iff (bstat[i].iordy == true) then
		readbuf(&bstat[i]);
	    iff (bstat[i+2].iordy == true) and (count[i] > 0) then
		writebuf(&bstat[i+2]);
	    else clef(bstat[i+2].lun + 1);	/* Clear output pending flag */
	}

	wtlo0(mask);	/* Wait for I/O Done before Retesting */
#ifdef debug
	printf("Counts = %d, %d.  Rdy flags = %d,%d,%d,%d\n",
	    count[0],count[1],bstat[0].iordy,bstat[1].iordy,bstat[2].iordy,
	    bstat[3].iordy);
#endif

    }
    untill(abort));

    iff iosb[0] != 0 then
	error("I/O exception, iosb = %o, %o, dsw = %d.\n",iosb[0],iosb[1],$dsw);

    detach(bstat[0].lun);
    detach(bstat[1].lun);

} /* end main */



/*** Attach Named Terminal ***/

proc attach(devnam,lun)

    char *devnam;
    int	 lun;    
{
    union {
	   int	dev;
	   char name[2];
	  } dnam;

    int	 unit;
    register int i,c;

    /* assign device to given lun */

	for (i = 0; i < 2; i++)
	    dnam.name[i] = ifx islower(c = *devnam++) thenx
		c - ('a' - 'A') elsex c;
	
	unit = otoi(devnam);

#ifdef debug
	printf("Dev = '%c%c', Unit = %d\n",dnam.name[0],dnam.name[1],unit);
#endif

	alun(lun,dnam.dev,unit);


    /* Attach terminal to capture all characters & allow typeahead */

    fnc = IO_ATT;
    eflag = 2;
    iosb[0] = 0;
    devpar[0] = 0;
    devpar[1] = 0;
    devpar[2] = 0;


    iff qio(fnc,lun,eflag,iosb,0,devpar) < 0
        then error("Attach error dsw = %d\n",$dsw);


    /* Wait for Attach */

    wtse(eflag);

} /* end attach */



	/*** Detach terminal & kill outstanding I/O ***/

proc detach(lun)

    int	lun;
{
	iff qio(IO_KIL,lun,0,iosb,0,0) < 0
	    then error("I/O Kill error, lun %d",lun);

	iff qio(IO_DET,lun,0,iosb,0,0) < 0
	    then error("Detach error, lun %d",lun);

} /* end detach */




	/*** Convert Octal Ascii number to Integer ***/

int
proc otoi(octnum)

    char *octnum;
{
    register 	int i, c;

	for (i = 0; ((c = *octnum++) >= '0') and ((c -= '0') < 8); i += c)
	    i <<=3;


	return(i);

} /* end otoi */



	/*** Readbuf:	Use Qio to Read all characters to buffer ***/
	/***		Clear Iordy and set for RDAST routine	 ***/

proc readbuf(buf)

    struct bufsta  *buf;
{
	/*** Do qio IO.RAL with timeout, 1 character at a time.
		Possible inhancements: use G.MCS to determine number of
		chars in typeahead buffer (TC.TBF = unprocessed char count);
		If buffer > 7/8 full send XOFF, at 1/2 full send XON. ***/

	eflag = buf->lun - 1;		/* flags 3 & 4 for input */

	devpar[0] = buf->bufptr;	/* Set start of I/O */
	devpar[1] = buf->iocnt = 1;	/* Number of characters */
	devpar[2] = 10;			/* Timeout, 10 sec intervals */

	fnc = IO_RAL | TF_TMO;

	buf->iordy = false;
	iff qio(fnc,buf->lun,eflag,&buf->iosb0,rdast,devpar) < 0
	    then error("Read error, lun %d, dsw = %d",buf->lun,$dsw);

} /* end readbuf */




	/* Writebuf: Use qio IO.WAL to write as many characters as
	   possible from buffer, Clear iordy, set for WRAST routine */

proc writebuf(buf)

    struct bufsta  *buf;
{

	eflag = buf->lun + 1;		/* flags 5 & 6 for output */

	buf->iocnt = *buf->count;	/* I/O count = count unless
					   ring buffer wraps */

	iff (buf->bufptr + buf->iocnt) > buf->bufend then
	    buf->iocnt = buf->bufend - buf->bufptr;

	devpar[0] = buf->bufptr;	/* Set start of I/O */
	devpar[1] = buf->iocnt;		/* Number of characters */
	devpar[2] = 0;			/* No VFC added to output */

	fnc = IO_WAL;
	
	buf->iordy = false;
	iff qio(fnc,buf->lun,eflag,&buf->iosb0,wrast,devpar) < 0
	    then error("Write error, lun %d, dsw = %d",buf->lun,$dsw);

} /* end writebuf */



/*** rdast:  entered when read is done,  Sets count, buffer
	     pointer, input ready flag, and abort	    ***/

proc rdast()
{
    register struct bufsta  *buf;
    register int   stat;

	astset();	/* Save registers */

	buf = gtdp(0);	/* Get IOSB addr from stack */

	iff (stat = buf->iosb0 & 0377) == IS_SUC then
	{
	    buf->iocnt = buf->iosb1;
	    iff *buf->bufptr == '\03' then
		abort = true;
	    *buf->count += buf->iocnt;
	    buf->bufptr += buf->iocnt;
	    iff buf->bufptr > buf->bufend then
		buf->bufptr = buf->bufstart;	/* Wrap ring buffer */
	}
	else iff stat != IS_TMO then
	{
	    buf->iocnt = 0;
	    iosb[0] = buf->iosb0;
	    iosb[1] = buf->iosb1;
	    abort = true;
	}
	buf->iordy = true;			/* Ready for more input */

	astx(1);			/* remove iosb from stack on exit */

} /* end rdast */



/*** wrast:  Entered when write is done,  Sets count,
	     buffer pointer, and output ready flag.	    ***/

proc wrast()
{
    register struct bufsta  *buf;

	astset();	/* Save registers */

	buf = gtdp(0);	/* Get IOSB addr from stack */

	iff (buf->iosb0 & 0377) == IS_SUC then
	{
	    *buf->count -= buf->iocnt;
	    buf->bufptr += buf->iocnt;
	    iff buf->bufptr > buf->bufend then
		buf->bufptr = buf->bufstart;	/* Wrap ring buffer */
	}
	else
	{
	    iosb[0] = buf->iosb0;
	    iosb[1] = buf->iosb1;
	    abort = true;
	}
	buf->iordy = true;			/* Ready for more output */

	astx(1);			/* remove iosb from stack on exit */

} /* end wrast */

