/*
 * 5799-WZQ (C) COPYRIGHT IBM CORPORATION 1986,1987,1988
 * LICENSED MATERIALS - PROPERTY OF IBM
 * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083
 */
#include "ega.h"
#if NEGA > 0

/* $Header:egatty.c 12.0$ */
/* $ACIS:egatty.c 12.0$ */
/* $Source: /ibm/acis/usr/sys/cacons/RCS/egatty.c,v $ */

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

/************************************************************************/
/*									*/
/*	Routines to Handle the EGA adapter/screen (memory mapped).	*/
/* We implement a simple termcap for now in software.			*/
/*									*/
/************************************************************************/

#include "../h/param.h"
#include "screen_conf.h"
#include "consdefs.h"
#include "egatty.h"
#include "ega_font.h"
#include "ega_init.h"


#ifdef KERNEL
#include "../machine/io.h"
#endif KERNEL

/*
 * Output Driver for an EGA adapter on a PC AT.
 */
/* pointer to start of lines */
LOCAL volatile char *egalines[EGA_SCREEN_LENGTH + 1];
LOCAL volatile char *ega_screen_ptr = 0;
static short mode = EGA_COLOR;	/* index into array of initial EGA register values */
#ifndef MINIROOT
extern lpstatus;
#endif /* !MINIROOT */
int ega_screen_type;		/* switches read from the ega */

#define ega_move_block bcopy

/*
 * this table roughly corresponse to the apa8c's default color table.
 */
int ega_table[8];

/*
 * initialize ega register's to the proper mode
 */
ega_set_reg(ega_mode)
	struct ega_mode *ega_mode;
{
	struct ega_reg *ega_reg = ega_mode->array_addr;
	unsigned  len = ega_mode->array_length;
	int i,c,s;

	s=_spl0();	/* no interrupts , it may harm the aed */
	for (i=0; i < len; ega_reg++,i++) {
		if (ega_reg->addr_addr != READ) {
			*(ega_reg->addr_addr) = ega_reg->reg_addr;
			*(ega_reg->io_addr) = ega_reg->value;
		} else {
			c = *(ega_reg->io_addr);
		}
	}
	splx(s);
}

/*
 * load an ega font into page 2
 */
ega_load_font(fp,len,size)
	char *fp;
	unsigned len;
	int size;
{
	volatile char	*cp;
	int	i,j;

	/* set c mode to copy the font */
	ega_set_reg(&ega_mode_init[LOAD_FONT]);


	for (i=0,cp=FONT_ADDR; i < len;i+=size,fp+=size,cp+=FONT_SPACING) {
		for (j=0 ; j < size; j++)
			cp[j] = fp[j];
	}
}

/*
 * initialize one of the alpha modes
 */
ega_init_regs(mode)
short mode;
{
	/* copy the font */
	if (mode != EGA_COLOR) {
		/* monochrome & ega fonts */
		ega_load_font(ega_font8x14,sizeof(ega_font8x14),FONT_8x14_SIZE);
	} else {
		/* color display fonts */
		ega_load_font(ega_font8x8,sizeof(ega_font8x8),FONT_8x8_SIZE);
	}
	ega_set_reg(&ega_mode_init[mode]);
}

ega_screen_init()
{
	register volatile char *p = SCREEN_BUFFER;
	register int i;

/* Get the switch setting on EGA adapter from the config information
 * written to ROMP at startup.  ega_info is declared in machdep.c */

	ega_screen_type = ega_read_sw();

/* Check the switches for valid setting; if not valid, then switches
 * were set incorrectly.
 * From the switch setting we determine the initial mode and adjust
 * the screen_sw and screen_addr tables for the proper display.
 */ 

	switch(ega_screen_type) {
			/*
			 * Color Graphics Display attatched to the ega
			 */
		case 0x9:		
		case 0x8:		
		case 0xf:
		case 0xe:
			/*
			 * set the CGD mode up
			 */
			mode = EGA_COLOR;
			screen_sw[CONS_EGA].vbits = 200;
			screen_sw[CONS_EGA].hbits = 640;
			screen_sw[CONS_EGA].color_entries = 8;
			ega_init_regs(mode);
			break;

			/*
			 * Monochrome is attached to the ega
			 */
		case 0x5:
		case 0x4:
		case 0xb:
		case 0xa:
			/* set up PCD (mono) */
			mode = EGA_MONO;
			screen_sw[CONS_EGA].vbits = 350;
			screen_sw[CONS_EGA].hbits = 640;
			screen_sw[CONS_EGA].color_entries = 2;
			ega_init_regs(mode);
			break;

			/*
			 * Enhanced color display attatched to the ega
			 */
		case 0x7: 	
		case 0x6:
		case 0xd:
		case 0xc:
			/*
			 * at least find the adapter if it exists
			 * even if the switches are wrong
			 */
		default:
			 /* set the ECD up */
			mode = EGA_ENHANCED;
			screen_sw[CONS_EGA].vbits = 350;
			screen_sw[CONS_EGA].hbits = 640;
			screen_sw[CONS_EGA].color_entries = 8;
			ega_init_regs(mode);
			break;

	}
	ega_screen_ptr = p;
	for (i=0; i < 8; i++) {
		ega_table[i] = i;
	}

/* note the <=. This so that lines[screen_length] is the end of the buffer */
	for (i = 0; i <= EGA_SCREEN_LENGTH; ++i) {
		egalines[i] = p;
		p += (EGA_SCREEN_WIDTH << 1);
	
	}
#ifndef BLACK_ON_WHITE
	if (mode == EGA_MONO) {
		ega_table[0] = BACKGROUND_COLOR;
	}
#else
	if (mode == EGA_MONO) {
		ega_table[0] = FOREGROUND_COLOR;
	}
	ega_color_table(0,0xffffffff,0xffffffff,0xffffffff,COLOR_SET);
	ega_color_table(EGA_FOREGROUND,0x0,0x0,0x0,COLOR_SET);
#endif
	
	ega_screen_clear();
	
}

LOCAL
ega_screen_clear()
{
	ega_screen_blank(NORMAL_VIDEO,0,0,EGA_SCREEN_LENGTH-1,
			EGA_SCREEN_WIDTH-1,EGA_FOREGROUND,BACKGROUND_COLOR);
	ega_pos_cursor (0, 0);
}

/* keep track of the last attribute */
static int ega_attr = EGA_NORMAL_VIDEO;
static int la = NORMAL_VIDEO;

LOCAL
ega_screen_attr(sa,fg_color,bg_color)
register int sa;
register unsigned fg_color,bg_color;
{
	register int ea;	/* EGA attribute */

	if (mode == EGA_MONO) {  	/* emulate monochrome display */
		switch (sa & (REVERSE_VIDEO | UNDERLINE_VIDEO)) {

			case REVERSE_VIDEO:
				ea = EGA_REVERSE_VIDEO;
				break;
			case UNDERLINE_VIDEO:
				ea = EGA_UNDERLINE_VIDEO;
				break;
			case (REVERSE_VIDEO | UNDERLINE_VIDEO):
				ea = EGA_REVERSE_VIDEO;
				break;
		
			case NORMAL_VIDEO:
			default:
				ea = EGA_NORMAL_VIDEO;
		}

		if (sa & HI_INTENSITY)
			ea |= EGA_HI_INTENSITY;
		if (sa & BLINK)
			ea |= EGA_BLINK;             
	}  else	{				/* color attributes */

		/* only 8 colors */
		fg_color &= 0x7;
		bg_color &= 0x7;

		/* reverse video is the colors swapped */
		if (sa & REVERSE_VIDEO) {
			ea= bg_color | (fg_color << 4);
		} else {
			ea= fg_color | (bg_color << 4);
		}

		/* set blink and hi_intensity */
		if (sa & BLINK) {
			ea |= EGA_BLINK;
		}
		if (sa & HI_INTENSITY) {
			ea |= EGA_HI_INTENSITY;
		}
	}

	return(ea);
}

ega_screen_putc(c, sa, fg, bg)
register int c;		/* Character to put on screen
register int sa;	/* Screen Attribute */
unsigned fg,bg;
{
	/*
	 * output the given character with the screen attribute
	 */
	if (mode == EGA_MONO) {
		bg &= 0x1;
		fg &= 0x1;
		/* if they're equal, no character printed */
		if (fg == bg) {
			sa &= ~UNDERLINE_VIDEO;
			c = ' ';
		}
		/* if they're reversed, reverse the sence of REV */
		if (bg != ega_table[0]) {
			sa ^= REVERSE_VIDEO;
		}
	}

	PUT_PC1B(*ega_screen_ptr,(char) c);
	sa |= ((fg & 0x7) << 8) | ((bg & 0x7) << 12);
	if (sa != la)  {
		ega_attr = ega_screen_attr (sa,fg,bg);
	}
	PUT_PC1B(*(ega_screen_ptr + 1), ega_attr);
	la = sa;
}


/*
 * blank the given line (line), starting at the given position (position)
 * giving proper number of blanks (width)
 */
ega_screen_blank(screen_attr, sy, sx, ey, ex, fg, bg)
	register int screen_attr, sy, sx, ey, ex;
	register unsigned fg, bg;
{
	register short *ptr = (short *)(egalines[sy] + (sx << 1)),
	*end = (short *)(egalines[ey] + (ex << 1));
	register int width = end - ptr;

EDEBUG (0x02, aed_pr("EGA_SCREEN_BLANK\n\r"));
	if (mode == EGA_MONO) {
		bg &= 0x1;
		fg &= 0x1;
		/* if they're equal, no character printed */
		if (fg == bg) {
			screen_attr &= ~UNDERLINE_VIDEO;
		}
		/* if they're reversed, reverse the sence of REV */
		if (bg != ega_table[0]) {
			screen_attr ^= REVERSE_VIDEO;
		}
	}
#define fill sy

	fill = (' ' << 8) + ega_screen_attr(screen_attr, fg, bg);
	fill = (fill << 16) + fill;
	if ((int)ptr & 02) {
		PUT_PC2B(*ptr++,fill);        /* align to word bdy */
		--width;
	}
	while ((width -= 8) >= 0) {
		register int *wptr = (int *)ptr;

		PUT_PC4B(wptr[0], fill);
		PUT_PC4B(wptr[1], fill);
		PUT_PC4B(wptr[2], fill);
		PUT_PC4B(wptr[3], fill);
		ptr = (short *)(wptr + 4);
	}
	while (ptr <= end)
		PUT_PC2B(*ptr++, fill);
EDEBUG (0x02, aed_pr("leave ega_screen_blank\n\r"));
}


#undef fill


/*
 * move line1 ... line2 to dest
 */
ega_screen_move(line1, line2, dest)
	register int line1, line2, dest;
{
	register volatile char *start = egalines[line1], *end = egalines[line2 + 1];
EDEBUG (0x02, aed_pr("EGA_SCREEN_MOVE\n\r"));
	if (line1 > dest) {
		ega_move_block(start, egalines[dest], end - start);
	}
	else if (line1 != dest)
		ega_rmove_block(start, egalines[dest], end - start);
EDEBUG (0x02, aed_pr("leave ega_screen_move\n\r"));
}


#ifndef ega_move_block
LOCAL
ega_move_block(from, to, length)
	register int *from, *to, length;
{
	length >>= 2;		       /* get as int's */
	while (--length >= 0)
		PUT_PC4B(*to++,GET_PC4B(*from++));
}


#endif

#ifndef ega_rmove_block
LOCAL
ega_rmove_block(from, to, length)
	register int *from, *to, length;
{
	from = (int *)(((int)from) + length);
	to = (int *)(((int)to) + length);
	length >>= 2;		       /* get as int's */
	while (--length >= 0)
		PUT_PC4B(*--to,GET_PC4B(*--from));
}


#endif


ega_pos_cursor(x, y)
register int x, y;
{
	register int pos;

	set_ptr (x, y);
	pos = (ega_screen_ptr - SCREEN_BUFFER) >> 1;
EDEBUG (0x01, aed_pr("ega_pos_cursor (%d, %d) (%d):(%x)x\n", x, y, pos, 
ega_screen_ptr));

	PUT_SCR_REG_C(14, pos >> 8);
	PUT_SCR_REG_C(15, pos);
}

ega_probe(rwaddr)
char *rwaddr;
{
	ega_init_regs(EGA_ENHANCED);
	return(screen_probe(rwaddr));
}

/*
 * read the switches
 */
ega_read_sw()
{
	int	i,sw=0;

	*MISCELLANEOUS = 1;
	for (i=0; i < EGA_NSWITCH; i++) {
		*MISCELLANEOUS = i  << 2;
		sw <<= 1;
		if (*MISCELLANEOUS & EGA_SWITCH_BIT) {
			sw |= 1;
		}
	}
	return(sw);
}

/*
 * Change the color pallet entry
 */
ega_color_table(table_entry,red,green,blue,flags)
	unsigned table_entry,red,green,blue,flags;
{
	int new;

	if (mode == EGA_MONO) {
		return(ega_mono_table(table_entry,red,green,blue,flags));
	}

	/*
	 * Use only 8 tables, 9-f are hi intensity of 0-7
	 */
	if (table_entry > 8) {
		return(-1);
	}

	switch (flags) {
	case REVERSE_COLOR:
		{
		unsigned	tmp;
#define FG	table_entry
#define BG	red

		tmp = ega_table[FG];
		ega_table[FG] = ega_table[BG];
		SETCOLOR(FG,ega_table[FG]);
		ega_table[BG] = tmp;
		SETCOLOR(BG,ega_table[BG]);
		return(0);
		}
	case COLOR_FG_INC:
	case COLOR_BG_INC:
		ega_table[table_entry] = (ega_table[table_entry]+1) & 0x7;
		SETCOLOR(table_entry,ega_table[table_entry]);
		return(0);
	case COLOR_FG_DEC:
	case COLOR_BG_DEC:
		ega_table[table_entry] = (ega_table[table_entry]-1) & 0x7;
		SETCOLOR(table_entry,ega_table[table_entry]);
		return(0);
	case COLOR_SET:
		new = 0;
		/* determin which colors are present */
		if (red & SCREEN_HIGH_BIT) {
			new |= EGA_C_RED;
		}
		if (green & SCREEN_HIGH_BIT) {
			new |= EGA_C_GREEN;
		}
		if (blue & SCREEN_HIGH_BIT) {
			new |= EGA_C_BLUE;
		}
		ega_table[table_entry] = new;
		SETCOLOR(table_entry,new);
		return(0);
	/* ignore the other commands */
	default:
		return(-1);
	}
}

#define EGA_NO_MODE (0xff00|EGA_BLINK|EGA_HI_INTENSITY)
/* this routine is used in the monochrome color table simulation */
LOCAL
ega_reverse_screen()
{
	register short *ptr;
	register short *end = (short *)egalines[EGA_SCREEN_LENGTH];

	for (ptr = (short *) egalines[0]; ptr <= end; ptr++) {
		register short tmp;

		tmp = GET_PC2B(*ptr);
		if ((tmp & ~EGA_NO_MODE) == EGA_REVERSE_VIDEO) {
			tmp = (tmp & EGA_NO_MODE) | EGA_NORMAL_VIDEO;
		} else {
			tmp = (tmp & EGA_NO_MODE) | EGA_REVERSE_VIDEO;
		}
		PUT_PC2B(*ptr, tmp);
	}
}
		
	
/*
 * Emulate a 2 entry color table for the ega monochrome mode.
 * Note: This does allow the two color table entries to differ
 */
LOCAL
ega_mono_table(table_entry,red,green,blue,flags)
	unsigned table_entry,red,green,blue;
	int	flags;
{
	int	current,wanted,one_count;

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

	switch(flags) {
	/* most flags mean reverse the colors for monochromes */
	case	COLOR_FG_INC:
	case	COLOR_BG_INC:
	case	COLOR_FG_DEC:
	case	COLOR_BG_DEC:
	case	REVERSE_COLOR:
		ega_table[0] ^= 1;
		ega_reverse_screen();
		return(0);

	case	COLOR_SET:
		/* now see if we need to change anything */
		current = ega_table[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) {
			ega_table[0] ^= 1;
			ega_reverse_screen();
		}
		return(0);
	default:
		return(-1);
	}
}

#ifndef MINIROOT
/*
 * screen printing functions:
 * flag == 0	print the current screen contents
 * flag != 0	invert the log output flag
 */
ega_screen_print(si, flag)
register SCREEN_INFO *si;
register int flag;
{
	register int l, i;
	register volatile char *p = SCREEN_BUFFER;

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

	for (l = 0; l < SCREEN_LENGTH; ++l) {
		p = egalines[l];
		for (i = 0; i < SCREEN_WIDTH; ++i, p += 2) {
			ega_pos_cursor (i, l);
			if (lp_put(si,GET_PC1B(*p))) {
				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;
}
#endif /* !MINIROOT */
#endif NEGA
