/**************************************************************************
 *									  *
 * 		 Copyright (C) 1983, Silicon Graphics, Inc.		  *
 *									  *
 *  These coded instructions, statements, and computer programs  contain  *
 *  unpublished  proprietary  information of Silicon Graphics, Inc., and  *
 *  are protected by Federal copyright law.  They  may  not be disclosed  *
 *  to  third  parties  or copied or duplicated in any form, in whole or  *
 *  in part, without the prior written consent of Silicon Graphics, Inc.  *
 *									  *
 **************************************************************************/

/*
**		C i/o primitives for remote graphics library
*/
#include <stdio.h>
#include <signal.h>
#include <sgtty.h>
#include "rpc.h"
#include <setjmp.h>
#include <sys/ioctl.h>
#include "Xnsioctl.h"

#define DEC11                   1

/*
**	System specific defines for unix on dec 11/780
**
*/
#ifdef DEC11
#define longwordelement0        3
#define longwordelement1        2
#define longwordelement2        1
#define longwordelement3        0

#define shortwordelement0       1
#define shortwordelement1       0

#define F2IEEE(value)	D2IEEE(value)
#define IEEE2F(value)   IEEE2DEC(value)
#endif

#ifdef M68000
#define longwordelement0        0
#define longwordelement1        1
#define longwordelement2        2
#define longwordelement3        3

#define shortwordelement0       0
#define shortwordelement1       1

#define F2IEEE(value)	(value)		/* these are not quite right */
#define IEEE2F(value)   (value)
#endif

long D2IEEE();
float IEEE2DEC();

/*
**	End of system specific defines for unix on dec 11/780
*/

unsigned char recO();
unsigned char recB();
unsigned short recS();
unsigned long  recL();

static short fastmode = 0;
static short netinited = 0;
static short haveether = 0;
static short offbuffend= 0;

#define OUTBUFFSIZE		1024
#define LONGESTBYTEGROUP 	50

static char outbuff[OUTBUFFSIZE];
static char *bp = outbuff;
static char *highp = outbuff+OUTBUFFSIZE-8;
static char *checkp = outbuff+OUTBUFFSIZE-LONGESTBYTEGROUP;

static struct sgttyb ttydat;
static short tty_save;

#define rec6()		((getgchar() - ' ') & 077)
#define send6(n)	putgchar( ((n)&077) + ' ' )
#define send8(n)	putgchar( n )
#define putgchar(n)	addchar( n )
#define dosync(i)	(((i) & 7) == 0)

#define addchar(n)	(*bp++ = (n))		
#define fullcheck();	{ if(bp>highp) { flushg(); offbuffend++; } }

#ifdef PROTOCOLDEBUG
#define NONE 0
#define READER 1
#define WRITER 2
static isopen = 0, current = NONE, column = 0;
FILE *fopen(), *fp;

char getgchar()
{
unsigned char ch;
ch = getc(stdin);
if (current == WRITER)
    {
    fprintf(fp, "end");
    fprintf(fp, "\nGot from iris:\n");
    current = READER;
    column = 0;
    }
print6char(ch);
return(ch);
}

print6char(ch)
unsigned char ch;
{
if (ch == 0)
    fprintf(fp, " \\0 ");
else if (ch < ' ')
    fprintf(fp, "0%o ", ch);
else if (ch == ' ')
    fprintf(fp, "    ");
else
    fprintf(fp, "  %c ", ch);
if (++column == 16)
    {
    fprintf(fp, "\n");
    column = 0;
    }
}
#else
#define getgchar()	getc(stdin)
#endif

/*
**	gcmd - send the graphics escape and the specified command code
**	
*/
gcmd( comcode )
register short comcode;
{
    if(!netinited)
	netinit();
    outcheck();
    putgchar(TESC);
    send6(comcode);
    send6(comcode>>6);
}


/*
**	sendo - send a boolean 
**
*/
sendo( value )
unsigned char value;
{
    sendb(value);
}


/*
** 	sendos - send an array of bytes
**
*/
sendos( values, nvals )
char values[];
long nvals;
{
    sendbs(values,nvals);
}


/*
**	sendb - send a byte 
**
*/
sendb( value )
register unsigned char value;
{
    if(fastmode)
	send8(value);
    else {
        send6(value);
        send6(value>>6);
    }
    fullcheck();
}


/*
** 	sendbs - send an array of bytes
**
*/
sendbs( values, nvals )
register char values[];
long nvals;
{
    register long i, nlongs;
    long along; 
    register char *ptr = (char *)&along;

    nlongs = (nvals+3)>>2;
    sendl(nvals);
    for(i=0; i<nlongs; i++) {
	if (dosync(i))
            putgchar(AESC);
        if(fastmode) {
	    send8(*values++);
	    send8(*values++);
	    send8(*values++);
	    send8(*values++);
	    fullcheck();
	} else {
	    ptr[longwordelement0] = *values++;	
	    ptr[longwordelement1] = *values++;	
	    ptr[longwordelement2] = *values++;	
	    ptr[longwordelement3] = *values++;	
	    sendl(along);
        }
    }
    arrayflush();
}


/*
** 	sendqs - send an array of Fontchars
**
*/
sendqs( values, nvals )
register char values[];
long nvals;
{
    register long i, nlongs;
    long along; 
    register char *ptr = (char *)&along;

    sendl(nvals<<3);
    nlongs = nvals<<1;
    for(i=0; i<nlongs; i++) {
	if (dosync(i))
            putgchar(AESC);
	ptr[longwordelement1] = *values++;	
	ptr[longwordelement0] = *values++;	
	ptr[longwordelement2] = *values++;	
	ptr[longwordelement3] = *values++;	
	sendl(along);
	i++;
	if (dosync(i))
            putgchar(AESC);
	ptr[longwordelement0] = *values++;	
	ptr[longwordelement1] = *values++;	
	ptr[longwordelement3] = *values++;	
	ptr[longwordelement2] = *values++;	
	sendl(along);
    }
    arrayflush();
}


/*
**	sendc - send a string
**
*/
sendc( str )
register char *str;
{
    sendbs(str,strlen(str)+1);
}


/*
**	sends - send a short
**
*/
sends( value ) 
register unsigned short value;
{
    if(fastmode) {
  	send8(value>>8);
  	send8(value);
    } else {
        send6(value);
        send6(value>>6);
        send6(value>>12);
    }
    fullcheck();
}


/*
** 	sendss - send an array of shorts
**
*/
sendss( values, nvals )
register short values[];
long nvals;
{
    register long i, nlongs;
    long along; 
    register short *ptr = (short *)&along;

    nlongs = (nvals+1)>>1;
    sendl(nvals<<1);
    for(i=0; i<nlongs; i++) {
	if (dosync(i))
            putgchar(AESC);
	ptr[shortwordelement0] = *values++;	
	ptr[shortwordelement1] = *values++;	
	sendl(along);
    }
    arrayflush();
}


/*
**	sendl - send a long
**
*/
sendl( value ) 
unsigned long value;
{
    register unsigned long val;
    register char *ptr;

    if(fastmode) {
	ptr = (char *)&value;
	send8(ptr[longwordelement0]);
	send8(ptr[longwordelement1]);
	send8(ptr[longwordelement2]);
	send8(ptr[longwordelement3]);
    } else {
	val = value;
        send6(val);
        send6(val>>6);
        send6(val>>12);
        send6(val>>18);
        send6(val>>24);
        send6(val>>30);
    }
    fullcheck();
}


/*
** 	sendls - send an array of longs
**
*/
sendls( values, nvals )
register long values[];
register long nvals;
{
    register long i;

    sendl(nvals<<2);
    for(i=0; i<nvals; i++) {
	if (dosync(i))
            putgchar(AESC);
	sendl(*values++);
    }
    arrayflush();
}


/*
**	sendf - send a float
**
*/
sendf( value ) 
float value;
{
    sendl(F2IEEE(value));
}


/*
** 	sendfs - send an array of floats
**
*/
sendfs( values, nvals )
register float values[];
register long nvals;
{
    register long i;

    sendl(nvals<<2);
    for(i=0; i<nvals; i++) {
	if (dosync(i))
            putgchar(AESC);
	sendl(F2IEEE(*values++));
    }
    arrayflush();
}


/*
**	recO - receive a boolean
**
*/
unsigned char recO()
{
    return recB();
}


/*
**	recOs - receive an array of booleans
**
*/
recOs( values )
char *values;
{
    recBs( values );
}


/*
**	recB - receive a byte
**
*/
unsigned char recB()
{
    unsigned char val;

    while(getgchar() != RESC) 
	;
    val = rec6();
    val |= rec6()<<6;
    return val;
}


/*
**	recBs - receive an array of bytes
**
*/
recBs( values )
register char *values;
{
    register long i, nlongs;
    register unsigned long val;
    long nbytes;
    unsigned long along;
    register char *ptr = (char *)&along;

    nlongs = ((nbytes=recL())+3)>>2;
    if(getgchar() != RESC) {
	printf("recBs: error in array transport\n");
	return;
    }
    for(i=0; i<nlongs; i++) {
        val = rec6();
        val |= rec6()<<6;
        val |= rec6()<<12;
        val |= rec6()<<18;
        val |= rec6()<<24;
        val |= rec6()<<30;
	if(dosync(i)) {
            getgchar();
	    putgchar(AESC);
	    flushg();
	}
	along = val;
	if (i < nlongs-1) {
    	    *values++ = ptr[longwordelement0];
    	    *values++ = ptr[longwordelement1];
    	    *values++ = ptr[longwordelement2];
    	    *values++ = ptr[longwordelement3];
	} else {
	    switch (nbytes & 3) {
		case 0:
	    	    *values++ = ptr[longwordelement0];
	    	    *values++ = ptr[longwordelement1];
    		    *values++ = ptr[longwordelement2];
		    *values++ = ptr[longwordelement3];
		    break;
		case 3:
	    	    *values++ = ptr[longwordelement0];
	    	    *values++ = ptr[longwordelement1];
    		    *values++ = ptr[longwordelement2];
		    break;
		case 2:
	    	    *values++ = ptr[longwordelement0];
	    	    *values++ = ptr[longwordelement1];
		    break;
		case 1:
	    	    *values++ = ptr[longwordelement0];
		    break;
	    }
	}
    }
    getgchar();
}


/*
**	recF - receive a float
**
*/
float recF()
{
    char onechar;
    long val;

    return IEEE2F(recL());
}


/*
**	recFs - receive an array of floats
**
*/
recFs( values )
register float *values;
{
    register long i, nlongs;
    register unsigned long val;

    nlongs = recL();
    if(getgchar() != RESC) {
	printf("recFs: error in array transport\n");
	return;
    }
    for(i=0; i<nlongs; i++) {
        val = rec6();
        val |= rec6()<<6;
        val |= rec6()<<12;
        val |= rec6()<<18;
        val |= rec6()<<24;
        val |= rec6()<<30;
	if(dosync(i)) {
            getgchar();
	    putgchar(AESC);
	    flushg();
	}
        *values++ = IEEE2F(val);
    }
    getgchar();
}


/*
**	recL - receive a long
**
*/
unsigned long recL()
{
    register unsigned long val;

    while(getgchar() != RESC) 
	;
    val = rec6();
    val |= rec6()<<6;
    val |= rec6()<<12;
    val |= rec6()<<18;
    val |= rec6()<<24;
    val |= rec6()<<30;
    return val;
}


/*
**	recLs - receive an array of longs
**
*/
recLs( values )
register unsigned long *values;
{
    register long i, nlongs;
    register unsigned long val;

    nlongs = recL();
    if(getgchar() != RESC) {
	printf("recLs: error in array transport\n");
	return;
    }
    for(i=0; i<nlongs; i++) {
        val = rec6();
        val |= rec6()<<6;
        val |= rec6()<<12;
        val |= rec6()<<18;
        val |= rec6()<<24;
        val |= rec6()<<30;
	if(dosync(i)) {
	    getgchar();
	    putgchar(AESC);
	    flushg();
	}
    	*values++ = val;
    }
    getgchar();
}


/*
**	recS - receive a short
**
*/
unsigned short recS()
{
    register unsigned short val;

    while(getgchar() != RESC) 
	;
    val = rec6();
    val |= rec6()<<6;
    val |= rec6()<<12;
    return val;
}


/*
**	recSs - receive an array of shorts
**
*/
recSs( values )
register short *values;
{
    register long i, nlongs;
    register unsigned long val;
    long nshorts;
    unsigned long along;
    register short *ptr = (short *)&along;

    nlongs = ((nshorts=recL())+1)>>1;
    if(getgchar() != RESC) {
	printf("recSs: error in array transport\n");
	return;
    }
    for(i=0; i<nlongs; i++) {
        val =  rec6();
        val |= rec6()<<6;
        val |= rec6()<<12;
        val |= rec6()<<18;
        val |= rec6()<<24;
        val |= rec6()<<30;
	if(dosync(i)) {
            getgchar();
	    putgchar(AESC);
	    flushg();
	}
	along = val;
	if (i < nlongs - 1) {
    	    *values++ = ptr[shortwordelement0];
    	    *values++ = ptr[shortwordelement1];
	} else
	    switch (nshorts & 1) {
		case 1:
	    	    *values++ = ptr[shortwordelement0];
		    break;
		case 0:
	    	    *values++ = ptr[shortwordelement0];
    		    *values++ = ptr[shortwordelement1];
		    break;
	    }
    }
    getgchar();
}


reccr()
{
    getgchar();
}

endprim()
{
}


setfastcom()
{
    if(!netinited)
        netinit();
    fastmode = 1;
    xsetfastcom();
}


setslowcom()
{
    if(!netinited)
        netinit();
    fastmode = 0;
    xsetslowcom();
}

/*
**	ginit - invokes xginit which takes special action on the terminal
**
*/
ginit()
{
    if(!netinited)
	netinit();
    xginit();
    flushg();
}

/*
**	greset - invokes xgreset which takes special action on the terminal
**
*/
greset()
{
    if(!netinited)
	netinit();
    xgreset();
    flushg();
}

gflush()
{
    if(!netinited)
	netinit();
    flushg();
}


gexit()
{
    if(!netinited)
	netinit();
    xgexit();
    flushg();
    ttyrestore();
}


gfinish()
{
    if(!netinited)
	netinit();
    flushg();
}


/*
**	C-unix system specific i/o functions for remote graphics library
**
**			  Paul Haeberli - Sept 1983
*/
#ifdef PROTOCOLDEBUG
putgchar( onechar )
char onechar;
{
if (isopen == 0)
    {
    isopen = 1;
    fp = fopen("protsave", "w");
    }
if (current == READER || current == NONE)
    {
    if (current != NONE)
	fprintf(fp, "end");
    fprintf(fp, "\nSent to iris:\n");
    current = WRITER;
    column = 0;
    }
print6char(onechar);
    outbuff[bp++] = onechar;
    if(bp >= OUTBUFFSIZE) {
	flushg();
	offbuffend++;
    }
}
#endif

/*
**	outcheck - see if we are within LONGESTBYTEGROUP of the the
**		   end of the buffer.  If so, flush output.
*/
outcheck()
{
    if( bp > checkp )
	flushg();
    offbuffend = 0;
}

/*
**	arrayflush - only flush when needed after array transfer  
**
*/
arrayflush()
{
    if(offbuffend) {
	flushg();
        offbuffend = 0;
    }
}

/*
**	flushg - flush the graphics stream
**
*/
flushg()
{
    if ( bp != outbuff ) {
        fflush(stdout);
	if(haveether) 
	    netwrite(1,outbuff,bp-outbuff);
        else
	    write(1,outbuff,bp-outbuff);
        bp = outbuff;
    }
}

netwrite(fd, buf, cc)
int fd;
char *buf;
register cc;
{
    struct xnsio io;

    io.addr = buf;
    io.count = cc;
    io.dtype = 0;
    ioctl(fd, NXWRITE, &io);
    return(io.count);
}

cleanexit()
{
    ttyrestore();
    exit(0);
}

netinit()
{
    register short i;

    netinited = 1;		   	/* careful - the order is important! */	
    for(i=0; i<LONGESTBYTEGROUP; i++) {	/* sure to get out of graphics mode */
	putgchar(0);
	fullcheck();
    }
    if( ioctl(1, NXIOSLOW, 0) == -1 )		/* see if we're on the net */
    	setslowcom(); 
    else {
	haveether = 1;
    	setfastcom(); 
    }
    ttysave();
    signal(SIGQUIT,cleanexit);
    signal(SIGINT,cleanexit);
    signal(SIGBUS,cleanexit);
    signal(SIGTERM,cleanexit);
}

/*
**	echoff - Turn local echoing off
**
*/
echoff()
{
    if(!netinited)
	netinit();
    ttydat.sg_flags = tty_save & ~ECHO;
    stty(0, &ttydat);
}


/*
**	echoon - Turn local echoing on
**
*/
echoon()
{
    ttydat.sg_flags = tty_save | ECHO;
    stty(0, &ttydat);
}

/*
**	ttysave - save the state of the tty
**
*/
ttysave()
{
    short t_local = LLITOUT;

    gtty(0, &ttydat);
    tty_save = ttydat.sg_flags;
    if(!haveether)
        ioctl(1,TIOCLBIS,&t_local);	/* set literal output mode */
}

/*
**	ttyrestore - restore the state of the tty
**
*/
ttyrestore()
{
    short t_local = LLITOUT;

    ttydat.sg_flags = tty_save;
    stty(0, &ttydat);
    if(!haveether)
        ioctl(1,TIOCLBIC,&t_local);	/* clr literal output mode */
}

/*
** 	D2IEEE - convert from DEC to IEEE floating point formats.  Returns zero
**	         on underflow (instead of denormalized IEEE).  
**
** 		Charles (Herb) Kuta - Aug 1983
*/
long D2IEEE(fn)
float fn;
{
    register unsigned long n;
    						/* if characteristic <= 2 */
    if ( ((n = *(long *)&fn) & (0xff << 7)) <= (0x2 << 7) )   
	return 0;
    else {
        n -= 0x2 << 7;
        return ((n>>16) | (n<<16));
    }
}


/*
** 	IEEE2DEC - convert from IEEE to DEC floating point formats.  Returns 
**		   zero for denormalized IEEE and DECmax on overflow 
**		   (including IEEE infinity).
**
** 		Charles (Herb) Kuta - Aug 1983
*/
float IEEE2DEC(n)
register long n;
{
    register long characteristic;
    long nn;

    n = ((n>>16) & 0xffff) | (n<<16);
    if ((characteristic = n & (0xff << 7)) == 0)
    	return 0.0;
    else if (characteristic >= (0xfe << 7))
    	nn = (n < 0) ? 0xffffffff : 0xffff7fff;
    else
    	nn = n + (0x2 << 7);
    return (*(float *)&nn);
}
