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

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

/* $Header:monotty.c 12.0$ */
/* $ACIS:monotty.c 12.0$ */
/* $Source: /ibm/acis/usr/sys/cacons/RCS/monotty.c,v $ */
#include "mono.h"

#if NMONO > 0

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

#include "../h/param.h"
#include "../machinecons/screen_conf.h"
#include "../machinecons/consdefs.h"
#include "../machinecons/monotty.h"

#include "../machine/io.h"


/*
 * Output Driver for a PC MONOCROME screen adapter on a RT PC.
 */

#ifdef ATR
char *screen_buffer;
#else
char *screen_buffer = SCREEN_BUFFER;
#endif ATR
#ifndef MINIROOT
extern int lpstatus;
#endif /* !MINIROOT */
LOCAL char *lines[MONO_SCREEN_LENGTH + 1] = {
	0
};				       /* pointer to start of lines */
LOCAL char *screen_ptr = 0;
LOCAL unsigned mono_color;

#ifdef ATR
#ifdef PCIF_PASS2
#define move_block(from, to, length)			\
	{						\
	set_128_window(screen_addr(CONS_MONO));		\
	bcopy(from+pcif_128_fw, to+pcif_128_fw, length);\
	}
#endif PCIF_PASS2
#else
#define move_block bcopy
#endif ATR

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

	screen_ptr = p;
	/* note the <=. This so that lines[screen_length] is the end of the buffer */
	for (i = 0; i <= MONO_SCREEN_LENGTH; ++i) {
		lines[i] = p;
		p += (MONO_SCREEN_WIDTH << 1);
	
	}
#ifdef BLACK_ON_WHITE
	mono_color = FOREGROUND_COLOR; /* set background color to be black */
#else
	mono_color = BACKGROUND_COLOR; /* set background color to be black */
#endif
	mono_screen_clear();
}

LOCAL
mono_screen_clear ()
{
	mono_screen_blank (NORMAL_VIDEO, 0, 0, MONO_SCREEN_LENGTH-1, 
		MONO_SCREEN_WIDTH-1, FOREGROUND_COLOR, BACKGROUND_COLOR);
	mono_pos_cursor (0, 0);
}

LOCAL
mono_screen_attr(sa)
register int sa;
{
	register int ma;	/* Mono attribute */

	if (sa & NORMAL_VIDEO)
		ma = MONO_NORMAL_VIDEO;
	else if (sa & REVERSE_VIDEO)
		ma = MONO_REVERSE_VIDEO;
	else if (sa & UNDERLINE_VIDEO)
		ma = MONO_UNDERLINE_VIDEO;
	else
		ma = MONO_NORMAL_VIDEO;

	if (sa & HI_INTENSITY)
		ma |= MONO_HI_INTENSITY;
	if (sa & BLINK)
		ma |= MONO_BLINK;

	return (ma);
		
}

mono_screen_putc(c, sa, fgcolor, bgcolor)
register int c;		/* Character to put on screen
register int sa;	/* Screen Attribute */
unsigned fgcolor,bgcolor;
{
	/* Monochrome attribute field */
	static int mono_attr = MONO_NORMAL_VIDEO;
	/* Last Screen Attribute */
	static int la = NORMAL_VIDEO;
#ifdef ATR
	int window = get_128_window();
	(void) set_128_window(screen_addr(CONS_MONO));
#endif ATR

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


	/*
	 * output the given character with the screen attribute
	 */
	PUT_PC1B(*screen_ptr,(char) c);
	if (sa != la)
		mono_attr = mono_screen_attr (sa);
	PUT_PC1B(*(screen_ptr + 1), mono_attr);
	la = sa;
#ifdef ATR
	set_128_window(window);
#endif ATR
}

#define MONO_NO_MODE (0xff00|MONO_BLINK|MONO_HI_INTENSITY)
LOCAL mono_reverse_screen()
{
	register short *ptr;
	register short *end = (short *)lines[MONO_SCREEN_LENGTH];

#ifdef ATR
	set_128_window(screen_addr(CONS_MONO));
#endif ATR
	for (ptr = (short *) lines[0]; ptr <= end; ptr++) {
		register short tmp;

		tmp = GET_PC2B(*ptr);
		if ((tmp & ~MONO_NO_MODE) == MONO_REVERSE_VIDEO) {
			tmp = (tmp & MONO_NO_MODE) | MONO_NORMAL_VIDEO;
		} else {
			tmp = (tmp & MONO_NO_MODE) | MONO_REVERSE_VIDEO;
		}
		PUT_PC2B(*ptr, tmp);
	}
}
		
	
/*
 * Emulate a 2 entry color table for the aed.
 * Note: This does allow the two color table entries to differ
 */
mono_color_table(table_entry,red,green,blue,flags)
	unsigned table_entry,red,green,blue;
	int	flags;
{
	int	s;
	int	current,wanted,one_count;

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

	switch(flags) {
	/* most flags mean flip the color on monochrome displays */
	case	COLOR_FG_INC:
	case	COLOR_BG_INC:
	case	COLOR_FG_DEC:
	case	COLOR_BG_DEC:
	case	REVERSE_COLOR:
		mono_color ^= 1;
		mono_reverse_screen();
		return(0);

	case	COLOR_SET:
		/* now see if we need to change anything */
		current = mono_color ^ 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) {
			mono_color ^= 1;
			mono_reverse_screen();
		}
		return(0);
	/* can't handle any of the other flags */
	default:
		return(-1);
	}
}

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

	/* only two entries are valid */
	fgcolor &= 0x1;
	bgcolor &= 0x1;
	/* if they're equal, no character printed */
	if (fgcolor == bgcolor) {
		screen_attr &= ~ UNDERLINE_VIDEO;
	}
	/* if they're reversed, reverse the sense of REV */
	if (bgcolor != mono_color) { 
		screen_attr ^= REVERSE_VIDEO;
	}

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

#ifdef ATR
	set_128_window(screen_addr(CONS_MONO));
#endif ATR
	fill = (' ' << 8) + mono_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 mono_screen_blank\n\r"));
}


#undef fill


/*
 * move line1 ... line2 to dest
 */
mono_screen_move(line1, line2, dest)
	register int line1, line2, dest;
{
	register char *start = lines[line1], *end = lines[line2 + 1];
EDEBUG (0x02, aed_pr("MONO_SCREEN_MOVE\n\r"));
	if (line1 > dest)
		move_block(start, lines[dest], end - start);
	else if (line1 != dest)
		rmove_block(start, lines[dest], end - start);
EDEBUG (0x02, aed_pr("leave mono_screen_move\n\r"));
}


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


#endif

#ifndef rmove_block
LOCAL
rmove_block(from, to, length)
	register int *from, *to, length;
{
#ifdef ATR
	set_128_window(screen_addr(CONS_MONO));
#endif ATR
	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

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

	if ((unsigned) x >= MONO_SCREEN_WIDTH || (unsigned) y >= MONO_SCREEN_LENGTH)
		return;
	set_ptr (x, y);
	pos = (screen_ptr - SCREEN_BUFFER) >> 1;
EDEBUG (0x01, aed_pr("mono_pos_cursor (%d, %d) (%d):(%x)x\n", x, y, pos, screen_ptr));

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



mono_probe(rwaddr)
char *rwaddr;
{
#ifdef ATR
/*  Set up the 128K byte window for screen access.  */
	/* Get the screen buffer offset into the 128K window */
	mono_screen_buffer = (char *)set_128_window(screen_addr(CONS_MONO));
				/* Place it into the screen switch table */
	screen_sw[CONS_MONO].rwaddr = mono_screen_buffer; 
	screen_buffer = mono_screen_buffer;
#endif ATR

	return(screen_probe(screen_buffer));
}

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

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

	for (l = 0; l < SCREEN_LENGTH; ++l) {
		p = lines[l];
		for (i = 0; i < SCREEN_WIDTH; ++i, p += 2) {
			mono_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 NMONO
