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

#ifndef lint
static char *rcsid = "$Header:egatty.c 12.0$";
#endif

#include "ega.h"

#if NEGA > 0

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

#include "param.h"
#include "screen_conf.h"
#include "consdefs.h"
#include "egatty.h"
#include "mono_tcap.h"

#include "sa.h"

/*
 * Output Driver for an EGA adapter.
 */

char *screen_buffer;
LOCAL char *clines[EGA_SCREEN_LENGTH + 1] = {
	0
};				       /* pointer to start of lines */
LOCAL char *scrn_ptr = 0;
static short mode;	/* index into array of initial EGA register values */

extern lpstatus;

#ifndef ATR
#define mve_block bcopy
#endif  ATR

#ifdef IBMRTPC
#include "ega_font.h"
#include "ega_init.h"
/*
 * 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;

	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);
		}
	}
}

/*
 * load an ega font into page 2
 */
ega_load_font(fp,len,size)
	char *fp;
	unsigned len;
	int size;
{
	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;
{
#ifdef ATR
	if (cpu == CPU_ATR)
		exit(0);
#endif
	/* 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]);
}
#else
#define ega_init_regs(x)
#endif



LOCAL
ega_screen_clear (si)
register SCREEN_INFO *si;
{
	ega_screen_blank(NORMAL_VIDEO,0,0,EGA_SCREEN_LENGTH-1,EGA_SCREEN_WIDTH-1);
	ega_pos_cursor (0, 0);
}

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

LOCAL
ega_screen_attr(sa)
register int sa;
{
	register int ea;	/* EGA attribute */


	if (mode == EGA_MONO)  { 	/* emulate monochrome display */
		if (sa & NORMAL_VIDEO)
			ea = MONO_NORMAL_VIDEO;
		else if (sa & REVERSE_VIDEO)
			ea = MONO_REVERSE_VIDEO;
		else if (sa & UNDERLINE_VIDEO)
			ea = MONO_UNDERLINE_VIDEO;

		if (sa & HI_INTENSITY)
			ea |= MONO_HI_INTENSITY;
		if (sa & BLINK)
			ea |= MONO_BLINK;             
	}
	else	{				/* color attributes */
		if (sa & NORMAL_VIDEO)
			ea = ega_attr;
		else if (sa & REVERSE_VIDEO)
					/* Swap bits 0-2 with bits 4-6 */
	   	  	ea = ((ega_attr & 0x0007) << 4) | ((ega_attr & 0x0070) >> 4);
		else if (sa & FOREGROUND_COLOR) {  /* change foreground color */
			if ((ega_attr & 0x000f) == 0x000f)
				ea = (ega_attr & 0x00f0);
			else
				ea = ega_attr + 0x0001;  /* increment to next color */
			}
		else if (sa & BACKGROUND_COLOR) {  /* change background color */
			if ((ega_attr & 0x0070) == 0x0070)
				ea = (ega_attr & 0x008f);
			else
				ea = ega_attr + 0x0010; /* increment to next color */
			}

		if (sa & BLINK)
			ea |= MONO_BLINK;             
	}
	return(ea);
}

ega_screen_putc(c, sa)
register int c;		/* Character to put on screen
register int sa;	/* Screen Attribute */
{
	/*
	 * output the given character with the screen attribute
	 */
	PUT_PC1B(*scrn_ptr,(char) c);
	if (sa != la) 
		ega_attr = ega_screen_attr (sa);
	PUT_PC1B(*(scrn_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)
	register int screen_attr, sy, sx, ey, ex;
{
	register short *ptr = (short *)(clines[sy] + (sx << 1)),
	*end = (short *)(clines[ey] + (ex << 1));
	register int width = end - ptr;

EDEBUG (0x02, aed_pr("EGA_SCREEN_BLANK\n\r"));
#define fill sy

        set_128_window(screen_addr(CONS_EGA));
	fill = (' ' << 8) + ega_screen_attr (screen_attr);
	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 char *start = clines[line1], *end = clines[line2 + 1];
EDEBUG (0x02, aed_pr("EGA_SCREEN_MOVE\n\r"));
	if (line1 > dest)
		mve_block(start, clines[dest], end - start);
	else if (line1 != dest)
		rmve_block(start, clines[dest], end - start);
EDEBUG (0x02, aed_pr("leave ega_screen_move\n\r"));
}


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


#endif

#ifndef rmve_block
LOCAL
rmve_block(from, to, length)
	register int *from, *to, length;
{
        set_128_window(screen_addr(CONS_EGA));
	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 = (scrn_ptr - SCREEN_BUFFER) >> 1;
EDEBUG (0x01, aed_pr("ega_pos_cursor (%d, %d) (%d):(%x)x\n", x, y, pos, scrn_ptr));

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


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

/* 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;
			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;
			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;
			ega_init_regs(mode);
			break;

	}

	scrn_ptr = p;
/* note the <=. This so that lines[screen_length] is the end of the buffer */
	for (i = 0; i <= EGA_SCREEN_LENGTH; ++i) {
		clines[i] = p;
		p += (EGA_SCREEN_WIDTH << 1);
	
	}
	ega_screen_clear();
}

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);
}

/* This routine will write a new attribute byte through the entire
 * screen buffer.  It is called from ega_fgbg when the foreground
 * or background color is to be changed.
 */

ega_paint_color(newattr)
int newattr;			/* new attribute */
{
	char *ptr = SCREEN_BUFFER;
	char *end = (char *) (ptr + 80 * 25 * 2);	/* end of buffer */
	char *attr_ptr = ++ptr;		/* first attribute byte */
	
	set_128_window(screen_addr(CONS_EGA));
	while (attr_ptr < end)	{
		PUT_PC1B(*attr_ptr, newattr);
		attr_ptr +=2;
	}
	ega_attr = newattr;
}

/*
 * these are called from screendev via screen_sw table
 */
egaopen()
{
	return(0);
}

egaclose()
{
	return(0);
}


ega_fgbg(screen_attr, color)
{
	int ega_scr_attr = color;
	ega_scr_attr = ega_screen_attr(ega_scr_attr);
	ega_paint_color(ega_scr_attr);
	return(screen_attr);
}

/*
 * 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 char *p = SCREEN_BUFFER;

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

	for (l = 0; l < SCREEN_LENGTH; ++l) {
		p = clines[l];
		for (i = 0; i < SCREEN_WIDTH; ++i, p += 2) {
			ega_pos_cursor (i, l);
			if (lp_put(GET_PC1B(*p))) {
				delay(250);
				goto done; /* quit if error */
			}
		}
		lp_put('\r');
		lp_put('\n');
	}
	lp_put('\f');		       /* skip to top of form */
done:
	put_status(33, "        ");
	lpstatus = -1;
}
#endif NEGA
