/*
 * 5799-WZQ (C) COPYRIGHT IBM CORPORATION 1986,1987,1988
 * LICENSED MATERIALS - PROPERTY OF IBM
 * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083
 */
/* $Header:aedtty.c 12.0$ */
/* $ACIS:aedtty.c 12.0$ */
/* $Source: /ibm/acis/usr/sys/cacons/RCS/aedtty.c,v $ */

#if !defined(lint) && !defined(NO_RCS_HDRS)
static char *rcsid = "$Header:aedtty.c 12.0$";
#endif

#include "aed.h"
#if NAED > 0
#include "../machine/machparam.h"
#include "../machinecons/screen_conf.h"
#include "../machinecons/aedtty.h"
#include "../machinecons/aed_tty_mcode.h"
#include "../machine/io.h"

extern SCREEN_INFO cons_info[];
#ifndef MINIROOT
extern int lpstatus;
#endif /* !MINIROOT */

int aedmode = NORMAL_VIDEO;
int aedcurpos = 0;
int aedwantpos = 0;

#define AEDREV	'0'
#define AEDUL	'1'
#define AEDSO	'2'
#define AEDNOSCROLL '3'
#define	AEDSTATUS '4'
#define	AEDBMOVE   '9'
#define AEDEOS	'J'
#define AEDEOL	'I'
#define AEDPOSCUR 'Y'
#define AEDDEL	'O'
#define AEDINS	'N'
#define AEDSCREENREV 'S'

#define AED_LAST_LINE 52
#define AED_LAST_CHAR 79
#ifdef ATR
#define spl_aed()	_spl2()
#else
#define spl_aed()	_spl6()
#endif ATR
#define AEDINVPOS	0xffff
#define AED_NOSCROLL	0x100
#define AED_WHITE_ON_BLACK 0x200
#define ESC		27
#define	CR		13
#define LF		10

aed_probe(rwaddr)
register char *rwaddr;
{
	short poll;
	int result;
	AED_ENTER(rwaddr=(char *));


	/* read control store to reset AED (Must be done for sensing) */
	aedrd(&poll, 1, 0X0000);
	AED_DELAY;

	/* Now do the normal screen probe routine */
	result = screen_probe(rwaddr);
	AED_EXIT();
	return (result);
}

int	aed_in_init;
aed_screen_init ()
{
	int s;
#ifndef AED_SET
	short poll;		   /* semaphore word */
#endif AED_SET
	AED_ENTER();

	if (aed_in_init++) {
		screen_sw[CONS_AED].flags = 0;
		return(1);	/* used only by local aed stuff */
	}
#ifndef AED_SET
	/* read control store to reset AED */
	aedrd(&poll,1,0X0000);

	/* copy microcode file into control store */
	aedwr(aed_tty_mcode,AEDTTY_MC_LEN/2,0x0000); 

	/* send a null command to the AED to start and wait for ready */
	aedrd(&poll,1,0X0002);
	aed_tty_command(0, 0);
	delay(1);	/* force delay to allow aed microcode to init completely  */
#endif AED_SET


	/* Initialize AED sram pointers */
	vinit();
	aed_put_status(0,"                                                                               ");

	aedmode = NORMAL_VIDEO;

#ifndef BLACK_ON_WHITE
	aedmode ^= AED_WHITE_ON_BLACK;
	s = spl_aed();
	vterm(ESC);
	vterm(AEDSCREENREV);
	splx(s);
#endif
	
	aedcurpos = 0;
	aedwantpos = 0;
	aed_in_init = 0;
	AED_EXIT();
	return(0);	/* used only by local aed stuff */
}


static aed_tty_command(buff, nwords)
register char *buff;		/* Buffer to give to AED */
register short nwords;		/* number of words to transfer */

{
	short poll;		   /* semaphore word */

	/* copy the AED buffer to shared ram */
	aedwr(buff,nwords,0X4002);

	/* Wake up microcode */
	poll = 1;
	aedwr(&poll,1,0X4000);

	/* wait for completion */
	do {
		AED_DELAY;
		aedrd(&poll,1,0X4000);
	}
	while (poll != 0);

}
char aedbuff[5];		/* buffered escape sequences */
#define AED_NORMAL	0
#define AED_ESC		4	/* read ESC */
#define AED_ESC1	1	/* read last 1 character */
#define AED_ESC2	2	/* read last 2 character */
#define AED_ESC3	3	/* read last 3 character */
int aedstate = AED_NORMAL;

/*
 * output a character to the AED
 * we look at the characters passing thru to see if 
 * they are a complete escape sequence - we only send it
 * when it is complete to protect the status line
 */
aed_screen_putc(c,sa,fgcolor,bgcolor) 
register int c;
register int sa;
unsigned fgcolor,bgcolor;
{
	register int count;
	register int i;
	register int s;
	AED_ENTER();

	if (sa != LITERAL_VIDEO) {
		/* only two entries are valid */
		fgcolor &= 0x1;
		bgcolor &= 0x1;
		/* if they're equal, no character printed */
		if (fgcolor == bgcolor) {
			sa &= ~UNDERLINE_VIDEO;
			c = ' ';
		}
		/* if bg is not zero, reverse the sense of REV */
		if (bgcolor != 0) { 
			sa ^= REVERSE_VIDEO;
		}

		vwait();

		s = spl_aed();

		aed_set_mode(sa);

		splx(s);

		if (aedwantpos != aedcurpos) {
			aed_pos_cursor(aedwantpos & 0xff, aedwantpos >> 8);
		}

		/* 
		 * Keep track of where we really are!
		 * note: this assumes that the emulator only
		 * send printing characters
		 */
		if (c < ' ') {
			aedcurpos = AEDINVPOS;
		} else if ((aedcurpos & 0xff) >= AED_LAST_CHAR) {
			aedcurpos &= 0xff00;
			aedcurpos += 0x100; /* hand move the cursor to the top */
		} else {
			aedcurpos += 1;
		}
	}


	vwait();

	switch(aedstate)
		{
	case AED_NORMAL:
		if (c == ESC) {
			aedstate = AED_ESC;
			aedbuff[0] = c;
			return;
		}
		vterm(c);
		return;
	case AED_ESC:
		aedbuff[1] = c;
		switch(c)
			{
		case AEDPOSCUR:
			aedstate = AED_ESC1;
			return;		/* need 2 more characters */
		case 'G':
			aedbuff[0] = 07;
			count = 1 ;
			break;
		case '@':		/* fake ESC @ for end of ESC 4 seq. */
			aedbuff[0] = 0200;
			count = 1;
			break;
		case AEDUL:
			aedmode ^= UNDERLINE_VIDEO;
			count = 2;
			break;
		case AEDSO:
			aedmode ^= HI_INTENSITY;
			count = 2;
			break;
		case AEDREV:
			aedmode ^= REVERSE_VIDEO;
			count = 2;
			break;
		case AEDNOSCROLL:
			aedmode ^= AED_NOSCROLL;
			count = 2;
			break;
		case AEDSCREENREV:
			aedmode ^= AED_WHITE_ON_BLACK;
			count = 2;
			break;
		case AEDSTATUS:
			aedstate = AED_ESC1;
			return;
		case AEDBMOVE:
			aedstate = AED_ESC1; /* need 3 more characters */
			return;
		default:
			count = 2;
			break;
			}
		break;
	case AED_ESC1:
		aedbuff[2] = c;
		if (aedbuff[1] == AEDSTATUS) {
			count = 3;
			break;
		}
		aedstate = AED_ESC2;
		return;
	case AED_ESC2:
		aedbuff[3] = c;
		if (aedbuff[1] == AEDPOSCUR) {
			count = 4;
			break;
		}
		aedstate = AED_ESC3;
		return;
	case AED_ESC3:
		aedbuff[4] = c;
		count = 5;
		break;
	default:
		aedstate = AED_NORMAL;
		return;
		}
		
/* inhibit interrupts so that escape sequence can be output in one piece */
	s = spl_aed();
	for (i=0; i<count; ++i)
		vterm(aedbuff[i]);
	splx(s);
	aedstate = AED_NORMAL;

	AED_EXIT();
}

aed_put_status(pos,str)
	register int pos;
	register char *str;
{
	register int s;
	register int c;
	AED_ENTER();
/* inhibit interrupts so that escape sequence can be output in one piece */
	s = spl_aed();
	aed_flip();
	vterm(ESC);	/* send out ESC */
	vterm(AEDSTATUS);		/*  4  */
	vterm(pos+' ');	/* column pos plus offset */
	do {
		c = *str++;
		vterm(c);
	} while (c);
	aed_flip();
	splx(s);
	AED_EXIT();
}

/*
 * position the aed cursor on the screen
 */
aed_pos_cursor(column,line)
	register int column;
	register int line;
{
	register int s;
	AED_ENTER();

	aedwantpos = ((line & 0xff) << 8) | column;
	if (aedwantpos != aedcurpos) {
		s = spl_aed();
		vterm(ESC);
		vterm(AEDPOSCUR);
		vterm(' '+line);
		vterm(' '+column);
		splx(s);
		aedcurpos = aedwantpos;
	}
	AED_EXIT();
}

/*
 * blank out the aed screen
 */
aed_screen_blank(screen_at,sy,sx,ey,ex,fgcolor,bgcolor)
	int screen_at;
	register int sy,sx,ey,ex;
	unsigned fgcolor,bgcolor;
{
	register int i,s;
	AED_ENTER();

	/* AED has only two "color" table entries */
	fgcolor &= 0x1;
	bgcolor &= 0x1;
	/* if fg & bg are the same, you can't see the underlines */
	if (fgcolor == bgcolor) {
		screen_at &= ~UNDERLINE_VIDEO;
	}
	/* if bg is not "0" reverse the sense of reverse */
	if (bgcolor != 0) {
		screen_at ^= REVERSE_VIDEO;
	}
	s = spl_aed();
	aed_set_mode(screen_at);
	splx(s);
	/* the emulator MUST call aed_pos_cursor  after clear */
	aed_pos_cursor(sx,sy);

	/* if it's the whole screen, use erase to end of screen function */
	if ((ex == AED_LAST_CHAR) && (ey >= (AED_LAST_LINE)) &&
	 (sy != AED_LAST_LINE)) {
		s = spl_aed();
		vterm(ESC);
		vterm(AEDEOS);
		splx(s);
		return;
	}

	/* while we can clear to the end of line, do it */
	while ( sy++ < ey) {
		s=spl_aed();
		vterm(ESC);
		vterm(AEDEOL);
		splx(s);
		/* aed_pos_cursor(0,sy); */
		vterm(LF);	/* a faster putcursor */
		aedcurpos += 0x100;
	}

	/* clear the last set of characters */
	if (ex == AED_LAST_CHAR) {
		s=spl_aed();
		vterm(ESC);
		vterm(AEDEOL);
		splx(s);
	} else {
		/* not escape sequences , no need to SPL */
		for (i=0; i <= ex; i++) {
			vterm(' ');
		}
	}
	AED_EXIT();
	
}

/* 
 * move screen
 */
aed_screen_move(start,end,dest)
	register int	start,end,dest;
{
	register int s;
	AED_ENTER();

	s=spl_aed();
	if ((start < dest) && (dest < end)) {
		vterm(ESC);
		vterm(AEDBMOVE);
		if ((end + (dest - start)) > AED_LAST_LINE)
			end = AED_LAST_LINE - (dest - start);
		vterm(end+' ');
		vterm(start+' ');
		vterm(end + (dest - start) + ' ');
		return;
	}
	vterm(ESC);
	vterm(AEDBMOVE);
	vterm(start+' ');
	vterm(end+' ');
	vterm(dest+' ');
	splx(s);
	AED_EXIT();
}

/*
 * Emulate a 2 entry color table for the aed.
 * Note: This does allow the two color table entries to differ
 */
aed_color_table(table_entry,red,green,blue,flags)
	unsigned table_entry,red,green,blue;
	int	flags;
{
	int	s;
	int	current,wanted,one_count;
	int	result = -1;
	AED_ENTER();

	/* there's only two table entries */
	if (table_entry > 1) {
		goto done;
	}

	switch (flags) {

	/* for monochromes, most flags mean switch fg & bg colors */
	case	COLOR_FG_INC:
	case	COLOR_BG_INC:
	case	COLOR_FG_DEC:
	case	COLOR_BG_DEC:
	case	REVERSE_COLOR:
		aedmode ^= AED_WHITE_ON_BLACK;
		s = spl_aed();
		vterm(ESC);
		vterm(AEDSCREENREV);
		splx(s);
		result = 0;
		goto done;
	/* for monochromes, COLOR_SET is a conditional switch colors */
	case	COLOR_SET:
		/* now see if we need to change anything */
		current = ((aedmode & AED_WHITE_ON_BLACK) == 0) ^ table_entry;

		/* map the RGB color to a white or black */
		one_count = 0;
		if (red & SCREEN_HIGH_BIT) one_count++;
		if (blue & SCREEN_HIGH_BIT) one_count++;
		if (green & SCREEN_HIGH_BIT) one_count++;
		wanted = (one_count >= 2);

		/* if a change needs to be made, make it */
		if (current != wanted) {
			aedmode ^= AED_WHITE_ON_BLACK;
			s = spl_aed();
			vterm(ESC);
			vterm(AEDSCREENREV);
			splx(s);
		}
		result=0;
		goto done;
	/* we really don't know how to deal with anything else */
	default:
		goto done;
	}
done:
	AED_EXIT();
	return(result);
}
		
/* set the proper mode dependent on sa */
aed_set_mode(sa)
	register int sa;
{
	register int newmode;
	AED_ENTER();

	newmode = (sa ^ aedmode);
	if (newmode & REVERSE_VIDEO) {
		vterm(ESC);
		vterm(AEDREV);
	}
	if (newmode & UNDERLINE_VIDEO) {
		vterm(ESC);
		vterm(AEDUL);
	}
	if (newmode & HI_INTENSITY) {
		vterm(ESC);
		vterm(AEDSO);
	}
	if (!(aedmode & AED_NOSCROLL)) {
		vterm(ESC);
		vterm(AEDNOSCROLL);
	}
	aedmode = (sa & (REVERSE_VIDEO | UNDERLINE_VIDEO | HI_INTENSITY)) | 
		AED_NOSCROLL | (aedmode & AED_WHITE_ON_BLACK);
	AED_EXIT();
}

/*
 * if we are in standout mode or in underline mode go to normal mode
 * for the status line.
 * also called after the status line is output to return to so or ul
 * mode.
 */
aed_flip()
{
	AED_ENTER();
	if (aedmode & HI_INTENSITY) {
		vterm(ESC);	/* send out ESC */
		vterm(AEDSO);
	}
	if (aedmode & UNDERLINE_VIDEO) {
		vterm(ESC);	/* send out ESC */
		vterm(AEDUL);
	}
	if (aedmode & REVERSE_VIDEO) {
		vterm(ESC);	/* send out ESC */
		vterm(AEDREV);
	}
	AED_EXIT();
}

#ifndef MINIROOT

static char dumpline[4] = {'\033', '8', 0, 0};
/* static */ char aedbuffer[128];

aed_screen_print(si, flag)
register SCREEN_INFO *si;
register int flag;
{
	register int l, i;
	register char *p;
	char *aedline();

	/* print out the aed screen buffer on the printer */

	for (l = 0; l < SCREEN_LENGTH; ++l) 
		{
		p = aedline(l);
		for (i = 0; i < SCREEN_WIDTH; i++, p++) 
			{
			if (!*p) break;	/* quit at terminating null */
			if (lp_put(si, p[0])) 
				{
				delay(250);
				goto done; /* quit if error */
				}
			}
		lp_put(si, '\r');
		lp_put(si, '\n');
		}
	lp_put(si, '\f');		       /* skip to top of form */
done:
	put_status(si, 33, "        ");
	lpstatus = -1;
}


char *aedline(line)
	register int line;
{
	register SCREEN_INFO *si = &cons_info[CONS_AED];
	char *s, c;
	AED_ENTER();

	dumpline[2] = ' ' + line;	/* blank plus line# offset */
	s = dumpline;
	while (c = *s++)
		vterm(c);		/* send dumpline request */
	vwait();			/* be sure dumpline is complete */
	aedrd(aedbuffer,SCREEN_WIDTH/2,0X4100);
	 	/* for characters. Attribute array at 0x4180 */
	aedbuffer[SCREEN_WIDTH] = '\0';	/* paranoia */
	return(aedbuffer);
	AED_EXIT();
}
#endif /* !MINIROOT */


#ifdef ATR
aed_rd(from, to, count)
short *from, *to;
int count;
{
	while (count-- > 0)
		*to++ = GET_PC2B(*from++);
}

aed_wr(from, to, count)
short *from, *to;
int count;
{
	while (count-- > 0)
		PUT_PC2B(*to++, *from++);
}
#endif ATR

#endif NAED
