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

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

#include "vga.h"
#include "pcbios.h"

#if NVGA > 0

/*
 *	Routines to Handle the VGA adapter/screen (memory mapped).
 *	We implement a simple termcap for now in software.
 *
 *  Entry points from screen_sw table:
 *
 *     screen_sw	      vga.c
 *     ---------	      -----
 *     *probe		      vga_probe
 *     *init		      vga_screen_init
 *     *s_putc		      vga_screen_putc	      (KERNEL ONLY)
 *     *pos_cur 	      vga_pos_cursor
 *     *blank		      vga_screen_blank	      (KERNEL ONLY)
 *     *move		      vga_screen_move
 *     *printscreen	      vga_screen_print
 *     *color_table	      vga_color_table	      (KERNEL ONLY)
 *
 *     *fgbg		      vga_sa_color_table      (STANDALONE ONLY)
 *     *s_putc		      vga_sa_screen_putc      (STANDALONE ONLY)
 *     *blank		      vga_sa_screen_blank     (STANDALONE ONLY)
 *
 *  Note: Even though various display modes support multiple pages,
 *	  the current implementation only uses one page (page 0).
 *
 *  Note: To minimize external source code changes, ranging checking
 *	  on incoming parameters 'foregrnd' and 'backgrnd' MUST be 
 *	  stripped of insignificant bits. The emulators DO NOT DO the
 *	  range checking. The following routines must conform to this.
 *
 *	     vga_screen_putc   vga_screen_blank   vga_color_table
 *
 */


/*
 * Include files needed for this device driver
 */

#include   "types.h"
#ifdef KERNEL
#include   "../machinecons/screen_conf.h"
#include   "../machinecons/consdefs.h"
#include   "../machinecons/ibm_emul.h"
#include   "../machine/io.h"
#include   "../ca_atr/kls.h"
#else /* assume STANDALONE */
#include   "screen_conf.h"
#include   "consdefs.h"
#include   "mono_tcap.h"
#include   "sa.h"
#endif KERNEL

/* included by users so it's in the RT directory */
#include   "../machinecons/vgaio.h"

#include   "../ca_atr/pc_bios.h"
#include   "../ca_atr/vgadefs.h"
#include   "../ca_atr/display_debug.h"
#include   "../ca_atr/pcif.h"
#include   "../pc_code/vga.h"


/*--------------------------------------------------------------*
 * External Variables and Constants				*
 *--------------------------------------------------------------*/

#ifdef KERNEL
#define LP_PUT(si, c) lp_put(si, c)
#define	PUT_STATUS(si, col, str) put_status(si, col, str)
#else  /* assume STANDALONE */
#define LP_PUT(si, c) lp_put(c)
#define	PUT_STATUS(si, col, str) put_status(col, str)
extern struct postconfig post_config;
#define config post_config
#endif KERNEL

extern int lpstatus;		   /* line printer status	    */

int	vga_screen_type	   = SCREEN_TYPE_NONE;	  /* type of display */

struct	vga_params	*vga;	/* pointer to pc side param structure */
struct	vga_params	vga_fake;/* fake data area used for old pc_code */

static short	vga_fgbg;		/* fore/background flag */
static short	cur_row = 0;		/* current cursor row */
static short	cur_col = 0;		/* current cursor column */
static int	vga_screen_lock = 0;	/* lock for screen updates */

/* 
 * The following buffer is used to write to while in text mode and
 * executing in the background instead of writing to the screen.
 */
static char local_text_buffer[VGA_BUF_LEN];
struct  vga_color vga_table[VGA_MAX_COLORS];
struct  vga_color vga_init_table[] = VGA_DEFAULT_COLORS;
struct	vga_color vga_mono_red = { 0x3f, 0x3f, 0 };
static int	vga_win;

/* this variable is used to map to the physical screen while in the
 * foreground. It will contain the full PC real mode address.
 * While in the background, mapping the 512 window to this value will not
 * cause an error because v_screen would have been set up to point to a
 * unix buffer.
 */
static int	vga_screen_window = 0xb8000;

/* this pointer is used for writing.
 * it might point to the physical screen (with the 512 window mapped) while
 * in the foreground, or to the local text buffer while in the background.
 * When pointing to the pc physical screen, it expects the 512 window to
 * be mapped to vga_screen_window.
 */
static char	*v_screen = 0;	/* points to screen buffer */

/*
 * set color after screen_init.
 */
static char vga_must_set_color = 0;

/*
 * set mode array. this is used if old pc_code types are used.
 * old pc_code types can't deal with vga set mode request, and
 * must be issued separate bios_request to work.
 */

static struct vga_mode_param	vga_mode_param[] = {
	/* type  color  col  row   hres  vres  screeen_addr */
	{ 0x00,    0 }, /* UNIX 001 (0) */
	{ 0x00,    1 }, /* UNIX 002 (1) */
	{ 0x00,    2 }, /* UNIX 003 (2) */
	{ 0x01,    0 }, /* UNIX 011 (3) */
	{ 0x01,    1 }, /* UNIX 012 (4) */
	{ 0x01,    2 }, /* UNIX 013 (5) */
	{ 0x02,    0 }, /* UNIX 021 (6) */
	{ 0x02,    1 }, /* UNIX 022 (7) */
	{ 0x02,    2 }, /* UNIX 023 (8) */
	{ 0x03,    0 }, /* UNIX 033 (9) */
	{ 0x03,    1 }, /* UNIX 032 (10) */
	{ 0x03,    2 }, /* UNIX 039 (11) */
	{ 0x04,    0 }, /* UNIX 041 (12) */
	{ 0x05,    0 }, /* UNIX 051 (13) */
	{ 0x06,    0 }, /* UNIX 061 (14) */
	{ 0x07,    1 }, /* UNIX 072 (15) */
	{ 0x07,    2 }, /* UNIX 073 (16) */
	{ 0x0d,    0 }, /* UNIX 0d1 (17) */
	{ 0x0e,    0 }, /* UNIX 0e1 (18) */
	{ 0x0f,    0 }, /* UNIX 0f2 (19) */
	{ 0x10,    1 }, /* UNIX 102 (20) */
	{ 0x11,    3 }, /* UNIX 114 (21) */
	{ 0x12,    3 }, /* UNIX 124 (22) */
	{ 0x13,    0 }, /* UNIX 132 (23) */
};

/*
 * save PC physical screen to unix buffer
 */

static void
copy_screen_in(from,to)
char	*from;			/* full PC address */
char	*to;			/* unix address */
{
	register char *s,*d;
	register int  k,j,i;
	int  w_512;

	w_512 = get_512_window();

	s = (char *) set_512_window(from);
	s += pcif_512_fw;

	d = to;

	for ( i=0; i < VGA_ROWS; i++ )
		for ( j=0; j < VGA_COLUMNS; j++ )
			for ( k=0; k < VGA_CELL_SIZE; k++ )
				*d++ = *s++;
	set_512_window(w_512);
}

/*
 * restore PC physical screen from unix buffer 
 */

static void
copy_screen_out(from,to)
char	*from;			/* unix address */
char	*to;			/* full PC address */
{
	register char *d,*s;
	register int  k,j,i;
	int  w_512;

	w_512 = get_512_window();

	d = (char *) set_512_window(to);
	d += pcif_512_fw;

	s = from;
	for ( i=0; i < VGA_ROWS; i++ )
		for ( j=0; j < VGA_COLUMNS; j++ )
			for ( k=0; k < VGA_CELL_SIZE; k++ )
				*d++ = *s++;
	set_512_window(w_512);
}


/* primitive debug routine to map directly into the physical text screen */

static char long_buffer[10];
static char *
p_long(value)
	unsigned value;
{
	char	c;
	int	count = 8;

	long_buffer[count] = 0;
	while (count--) {
		c = value & 0xf;
		value >>= 4;
		if (c < 10) {
			c += '0';
		} else {
			c += 'a'-10;
		}
		long_buffer[count] = c;
	}
	return(long_buffer);
}

int
v_debug(line,s)
int	line;
char	*s;

{	int	old_window;
	char	*p = 0;
	u_long	i;

	old_window = get_512_window();
	i = set_512_window(0xb8000) + pcif_512_fw +
		(line * VGA_COLUMNS * VGA_CELL_SIZE);
	p += i;
	while( *s ) {
		*p++ = *s++;
		*p++ = SCREEN_RED;
	}
	set_512_window(old_window);

}


#ifndef KERNEL	/* STANDALONE */
#define VPUTC(off,c,attr)	vputc(off,c,attr)
/*
 * function to put a character to the screen buffer. It requires an offset
 * from the start of the buffer.
 */
static void
vputc(off,c,attr)
register int	off;
u_char	c;
u_char	attr;

{
	static int level = 0;
	int	old_window = get_512_window();	
	u_long	taddr;


	/* We have not yet enabled the hot key interrupt from OS/2
	 * so we have to poll the write status to determine if we
	 * can write to the screen or not. We do not do any saving
	 * of the physical screen until the hot key mode has been 
	 * set up. Instead we cache everything we write and restore
	 * the screen when we find we can write again. This handles
	 * just about everycase except 1) writes from pc_code (these
	 * shouldn't happen anyway!, and 2) when we hotkey away, and
	 * back before we do any writes and we don't do a read. (e.i.
	 * between writing the vmunix sizes on booting.
	 * 
	 * Rationale: we do not know the exact moment the user
	 * switched screens on the PC (interrupts and keyboard io
	 * disabled) so we might be saving some other screen on the
	 * PC! Also, we do not want to write into a PC application's
	 * screen.
	 *
	 * The check below allows printouts via the pc code if vputc
	 * is called recursively (e.i. dereference through an invalid
	 * pointer.
	 *
	 * Note on pcif windows: This code is explicitly expecting the
	 * window to be set to vga_screen_window (it is replaced by
	 * a macro in kernel mode).
	 */

	if (level++) {
		pc_req(CB_PUTC, c, PUTCENT);	/* recursion! */
		return;
	}


	set_512_window(vga_win);	
	if ( (vga->write_status == VGA_WRITE_DISABLED)
					&& (vga_fgbg == VGA_FOREGROUND) ){
		/* still in foreground so switch ptr */
		vga_fgbg = VGA_BACKGROUND;
		v_screen = local_text_buffer;
	}
	else if ( (vga->write_status == VGA_WRITE_OK)
					&& (vga_fgbg == VGA_BACKGROUND) ){
		/* still in background so switch to foreground.
		 * restore v_screen pointer to point to physical
		 * screen.
		 */
		vga_screen_window = /*vga->u.vga_screen_addr;*/
				0xb8000;
		taddr = set_512_window(vga_screen_window)+ pcif_512_fw;
		v_screen = (char *) taddr;
		vga_fgbg = VGA_FOREGROUND;
		screen_redraw();

	}
	set_512_window(old_window);	
	/*
	 * mirror the screen writes so we can restore the screen when we
	 * are in the forground again
	 */
        *(local_text_buffer+off) = c;
	*(local_text_buffer+(off+1)) = attr;
	*(v_screen + off++ ) = c;			
	*(v_screen + off) = attr;		
	--level;
}
#else KERNEL
#define VPUTC(off,c,attr)	(*(v_screen+(off)) = (c) ,\
				 *(v_screen+(off+1)) = (attr))
#endif KERNEL

/*
 * function to fill the screen buffer with a given character and attribute
 * This works only in text mode.
 */

static void
v_fill(c,attr,sr,sc,er,ec)
u_char c;	/* character to put */
u_char attr;	/* attribute to put */
u_short	sr;	/* start row */
u_short sc;	/* start column */
u_short er;	/* end row */
u_short ec;	/* end column */

{	register int	start,end;

#ifdef VGA_PARANOIA
	if ( sr >= VGA_ROWS ) sr = VGA_ROWS-1;
	if ( er >= VGA_ROWS ) er = VGA_ROWS-1;
	if ( ec >= VGA_COLUMNS ) ec = VGA_COLUMNS-1;
	if ( sc >= VGA_COLUMNS ) sc = VGA_COLUMNS-1;
#endif VGA_PARANOIA
	start = CUR_OFFSET(sr,sc);
	end   = CUR_OFFSET(er,ec);
	if ( end > VGA_BUF_LEN )
		end = VGA_BUF_LEN;
#ifdef KERNEL
	if (start > end)
		return;
	if (start&2) {
		VPUTC(start,c,attr);
		start+= 2;
	}
	v_set((int *) (v_screen + start),
		(int *) (v_screen + end + 2), (c<<8) | attr);
	if ((end&2) == 0)
		VPUTC(end,c,attr);
	
#else
	while ( start <= end ) {
		VPUTC(start,c,attr);
		start+= 2;
	}
#endif
}

#ifdef KERNEL
/*
 * store fill as 4-byte quantities for efficiency.
 */
static
v_set(start,end,value)
long *start, *end;
unsigned value;
{
	value |= value << 16;
	while (start < end)
		*start++ = value;
}
#endif KERNEL

/* macro for text mode operation only. */
#define vga_blank(a,sr,sc,er,ec)	v_fill(BLANK,a,sr,sc,er,ec)

/* move text.  */
static void
vga_move_up(sr,er,dr)
register u_short sr;	/* start row */
u_short	er;		/* end row */
register u_short dr;	/* target row */

{
	register char	*s,*d;
	register i;
#ifdef VGA_PARANOIA
	if ( dr >= sr ) return;
	if ( er < sr ) return;
#endif VGA_PARANOIA
	while ( sr <= er ) {
		s = v_screen + CUR_OFFSET(sr,0);
		d = v_screen + CUR_OFFSET(dr,0);
		bcopy(s, d, VGA_COLUMNS * 2);
#ifndef KERNEL
		if (v_screen != local_text_buffer) {
			s = local_text_buffer + CUR_OFFSET(sr,0);
			d = local_text_buffer + CUR_OFFSET(dr,0);
			for ( i=0; i < VGA_COLUMNS ; i++ ) {
				*d++ = *s++;
				*d++ = *s++;
			}
		}
#endif !KERNEL
		sr++;
		dr++;
	}
}

static void
vga_move_down(sr,er,dr)
short sr;	/* start row */
register short er;		/* end row */
register short dr;	/* target row */

{
	register char	*s,*d;
	register i;
#ifdef VGA_PARANOIA
	if ( dr < sr ) return;
	if ( er < sr ) return;
#endif VGA_PARANOIA
	dr = dr - sr + er;
	while ( sr <= dr ) {
		s = v_screen + CUR_OFFSET(er,0);
		d = v_screen + CUR_OFFSET(dr,0);
		for ( i=0; i < VGA_COLUMNS ; i++ ) {
				*d++ = *s++;
				*d++ = *s++;
		}
#ifndef KERNEL
		if (v_screen != local_text_buffer) {
			s = local_text_buffer + CUR_OFFSET(er,0);
			d = local_text_buffer + CUR_OFFSET(dr,0);
			for ( i=0; i < VGA_COLUMNS ; i++ ) {
				*d++ = *s++;
				*d++ = *s++;
			}
		}
#endif !KERNEL
		er--;
		dr--;
	}
}

/*
 * function to clear the screen in text mode
 */
static void
vga_screen_clear()

{	int	old_window;


	old_window = get_512_window();
	set_512_window(vga_screen_window);

	v_fill(BLANK,
		vga_attribute(0,VGA_FG_DEFAULT,VGA_BG_DEFAULT),
					    0,0,VGA_ROWS-1,VGA_COLUMNS-1);
	vga_pos_cursor(0,0);

	set_512_window(old_window);
}



/*
 * This is called from buf_emul to switch to graphics mode. In graphics mode
 * we are not responsible for the screen. NOTE: this may be called even
 * if we are in the background. The pc_code will remember and set the mode
 * before it tries to restore the screen.
 */
int
vga_set_mode(mode)

{
	int	i,old_window;
	struct	bios_int bios_data;

#if NPCBIOS > 0
	/*
	 * use the old biosreq stuff to set the  mode
	 */
	if (config.os_type == OLD_TYPE) {
		/*
		 * set the scan lines
		 */
		if (vga_mode_param[mode].scan <= VGA_MAX_SCAN) {
			bios_data.gen_reg.h.al = vga_mode_param[mode].scan;
			bios_data.gen_reg.h.ah = VGA_ALT_SELECT;
			bios_data.gen_reg.h.bl = VGA_ALT_SET_SCAN;
			bios_data.interrupt = VIDEO;
			bios_data.valid_reg = AX_VALID | BX_VALID;
			CALLBIOS( bios_data );
		}
		/*
		 * set mode
		 */
		bios_data.gen_reg.h.ah = VGA_BIOS_MODE;
		bios_data.gen_reg.h.al = vga_mode_param[mode].bios_mode;
		bios_data.interrupt = VIDEO;
		bios_data.valid_reg = AX_VALID;
		CALLBIOS( bios_data );
		return;
	}
#endif

	old_window = get_512_window();
	set_512_window(vga_win);

	/* first set the mode pc side. */
	vga->unix_opcode = VGA_SET_MODE;
	vga->mode = mode;
	pc_req(CB_BIOSREQ, (char *) 0, BIOSENT);
	i = (u_int) ~0;	/* max wait possible */
	while( vga->unix_opcode && --i) ;	/* wait for ack */
	if ( i == 0 ) {
		/* timed out */
		v_debug(10,"PANIC: cannot switch to graphics mode");
		v_debug(11,"console driver not responding");
	}
	set_512_window(old_window);

}

vga_screen_restore()
{
	int old_window,i;
	u_long	taddr;

#ifdef KERNEL
	if ( VGA_SCREEN_LOCKED ) {
		return(0);	/* update in progress */
	}
#endif KERNEL

	old_window = get_512_window();
	vga_win = get_pc_cb(BIOSENT);	/* pick up vga_win in case
					   we were suspended */
	if (config.os_type != OLD_TYPE)
		vga =(struct vga_params *)(set_512_window(vga_win)+pcif_512_fw);
	/* get new address of physical screen. */
	vga_screen_window = /*vga->u.vga_screen_addr; */
		0xb8000;
	/*
	 * restore v_screen pointer to point to physical
	 * screen.
	 */
	taddr = set_512_window(vga_screen_window)+pcif_512_fw;
	v_screen = (char *) taddr;
	RESTORE_TEXT_SCREEN();
	vga_fgbg = VGA_FOREGROUND;
	vga_screen_type=vga_get_type();
	/* restore color table */
	for(i=0; i < VGA_MAX_COLOR; i++) {
		vga_set_color(i,&vga_table[i]);
	}
	if (vga_must_set_color) {
		if (vga_screen_type == SCREEN_TYPE_MONO) {
			vga_set_color(SCREEN_RED,&vga_mono_red);
		}
		vga_table[SCREEN_RED] = vga_init_table[SCREEN_RED];
		vga_must_set_color = 0;
	}
	/* restore the cursor */
	vga_pos_cursor(cur_col,cur_row);
	set_512_window(old_window);
	return(1);
}

#ifdef KERNEL
vga_screen_save()
{
	int	old_window;

	/*
	 * NOTE: the color table and cursor position are
	 * already saved
	 */

	if ( VGA_SCREEN_LOCKED ) {
		return(0);	/* update in progress */
	}
	old_window = get_512_window();
	/* get new address of physical screen. */
	set_512_window(vga_screen_window);
	SAVE_TEXT_SCREEN();
	/* last chance to get the current information */
	vga_screen_type=vga_get_type();
	/* future writes will be to local buffer */
	v_screen = local_text_buffer;
	vga_fgbg = VGA_BACKGROUND;
	set_512_window(old_window);
	return(1);
}
#endif KERNEL

/* probe */
int
vga_probe( rwaddr )
char	*rwaddr;

{
	/* we are always here */
	return(1);
}

/* initialize the screen */
int
vga_screen_init()

{	
	int	old_window,i;

	old_window = get_512_window();
	vga_win = get_pc_cb(BIOSENT);
	if (config.os_type != OLD_TYPE) {
		vga =(struct vga_params *)(set_512_window(vga_win)+pcif_512_fw);	} else {
		vga = &vga_fake;
		vga->write_status = VGA_WRITE_OK;
	}

 	/* Don't assume anything about FG/BG conditions */
	vga_set_mode(MODE_039);
	if (vga->write_status == VGA_WRITE_DISABLED) {
		vga_fgbg = VGA_BACKGROUND;
		v_screen = local_text_buffer;
		vga_must_set_color = 1;
#ifdef KERNEL
		screen_sw[CONS_VGA].flags |= CONSDEV_SAVED;
#endif KERNEL
	} else {
		u_long	taddr;

		vga_fgbg = VGA_FOREGROUND;
		/* get the physical screen address */
		vga_screen_window = /*vga->u.vga_screen_addr;*/
			0xb8000;

		/* setup to write directly to phyical screen */
		taddr = set_512_window(vga_screen_window) + pcif_512_fw;
		v_screen = (char *) taddr;
		set_512_window(vga_win);
		vga_must_set_color = 0;
#ifdef KERNEL
		screen_sw[CONS_VGA].flags &= ~CONSDEV_SAVED;
#endif KERNEL
	}

	/* get the current display type */
	vga_screen_type = vga_get_type();
	/* initialize the color table */
	for(i=0; i < VGA_MAX_COLOR; i++) {
		vga_set_color(i,&vga_init_table[i]);
	}
	if (vga_screen_type == SCREEN_TYPE_MONO) {
		vga_set_color(SCREEN_RED,&vga_mono_red);
	}
	vga_table[SCREEN_RED] = vga_init_table[SCREEN_RED];

	/* initialize the screen */
	vga_screen_clear();

	vga_screen_lock = 0;

	vga->pc_opcode = 0;
	vga->unix_opcode = 0;
	set_512_window(old_window);
	return(0);
}

#ifndef	KERNEL
/* put a character out to the screen.
 * standalone (=> text mode) only
 */

int
vga_sa_screen_putc(character, screen_attr)
register int character;		/* character to put */
register int screen_attr;	/* attribute to use */

{
	return(vga_screen_putc(character, screen_attr, VGA_FG_DEFAULT,
		VGA_BG_DEFAULT));
}

#endif

#ifndef	KERNEL
static
#endif
int
vga_screen_putc(character, screen_attr, fg, bg)
register int character;		/* character to put */
register int screen_attr;	/* attribute to use */
register unsigned fg, bg;

{
	int	old_window;
	int	s;

	old_window = get_512_window();
	set_512_window(vga_screen_window);

	VGA_LOCK_SCREEN(s);

	VPUTC(CUR_OFFSET(cur_row,cur_col),(u_char)(character&0xff),
		vga_attribute(screen_attr,fg,bg));

	VGA_UNLOCK_SCREEN(s);

	set_512_window(old_window);
	return 0;
}

/* position the cursor
 * We implement the cursor in software. We can use this routine only in
 * text mode.
 * To make the cursor, we set the background color of the cell to the
 * cursor background color, and turn on the blinking bit. We do not touch the
 * foreground color, which makes it easy to retore the cell when the cursor is
 * moved.
 */

int
vga_pos_cursor(column,row)
int	column,row;
{
	int	s;

	if ( VGA_IN_FOREGROUND ) {
		unsigned int	pos = row * VGA_COLUMNS + column;
		VGA_LOCK_SCREEN(s);
		out(VGA_CRT_ADDR_COLOR,VGA_CURSOR_HIGH);
		out(VGA_CRT_REG_COLOR,(pos>>8) & 0xff);
		out(VGA_CRT_ADDR_COLOR,VGA_CURSOR_LOW);
		out(VGA_CRT_REG_COLOR,(pos) & 0xff);
		VGA_UNLOCK_SCREEN(s);
	}

	/* remember new cursor position */
	cur_row = row;
	cur_col = column;

	return 0;
}

#ifndef	KERNEL
/* blank the screen.
 * standalone (=> text mode) only
 */

int
vga_sa_screen_blank(screen_attr,start_row,start_col,end_row,end_col)
int	screen_attr,start_row,start_col,end_row,end_col;
{
	vga_screen_blank(screen_attr,start_row,start_col,end_row,end_col,
		VGA_FG_DEFAULT, VGA_BG_DEFAULT);
}
#endif

#ifndef	KERNEL
static
#endif
int
vga_screen_blank(screen_attr,start_row,start_col,end_row,end_col,fg,bg)
int	screen_attr,start_row,start_col,end_row,end_col;
unsigned fg,bg;

{
	int	old_window;
	int	s;

	old_window = get_512_window();
	set_512_window(vga_screen_window);

	VGA_LOCK_SCREEN(s);

		
	vga_blank(vga_attribute(screen_attr,fg,bg),start_row,start_col,
							end_row,end_col);

	VGA_UNLOCK_SCREEN(s);

	set_512_window(old_window);
	return 0;
}

/*
 * move a block of screen data from one rectangular window to another.
 * line1 and line2 are row coordinates of the start and end lines of the
 * source block. dest is the row coordinate of the target block.
 */

int
vga_screen_move(line1,line2,dest)
int	line1,line2,dest;

{
	int	old_window;
	int	s;


	old_window = get_512_window();
	set_512_window(vga_screen_window);

	VGA_LOCK_SCREEN(s);

	if (line1 > dest) {
		vga_move_up(line1,line2,dest);
	} else {
		vga_move_down(line1,line2,dest);
	}
	VGA_UNLOCK_SCREEN(s);

	set_512_window(old_window);
	return 0;
}

/*
 * vga_screen_print - dump display buffer to printer
 *
 *
 *    screen printing functions
 *    flag == 0    print the current screen contents
 *    flag != 0    invert the log output flag
 *
 * Note: This model for this routine was found in the monotty.c
 *   and egatty.c device drivers.
 *
 * Note: flag is ignored, always go to printer.
 *	 lpstatus is an external declared in lptty.c
 */
vga_screen_print( si, flag )
register SCREEN_INFO *si;
int flag;
{

	register int   row, column;
	register int display_buffer= 0;
	int	old_window;
	int	s;


	/* Make sure PCIF window is set correctly */
	old_window = get_512_window();
	set_512_window(vga_screen_window);


	/* print out the VGA screen buffer on the printer */
	for ( row = 0; row < VGA_ROWS; ++row ) {
		for ( column = 0; column < VGA_COLUMNS;
						++column, display_buffer += 2 ) {
		  VGA_LOCK_SCREEN(s);
		  if ( LP_PUT(si, *(v_screen+display_buffer)) ) {
		      VGA_UNLOCK_SCREEN(s);
		      delay(250);
		      goto done; /* quit if error */
		  }
		  VGA_UNLOCK_SCREEN(s);
		}
		LP_PUT(si, '\r');
		LP_PUT(si, '\n');
	}
	LP_PUT(si,'\f');
done:
	PUT_STATUS(si, 33, "        ");
	lpstatus = -1;


	set_512_window(old_window);
	return(0);
}

#ifndef	KERNEL /* STANDALONE */
/*
 * Don't both supporting weird color stuff just for
 * standalone...
 */
int
vga_sa_color_table(screen_attr,command)
int	screen_attr, command;
{
	return(vga_color_table(VGA_FG_DEFAULT,VGA_BG_DEFAULT, 0, 0, command));
}

#endif KERNEL

/*
 * vga_color_table - The primary use of this routine is change
 *    the display environment where color is concerned. The ATR
 *    system expanded upon this to involve all aspects of the
 *    display environment.
 *
 *    Each aspect that can be changed has a command associated
 *    with it. The commands are:
 *
 *	 COLOR_FG_INC	     -	increment foreground color
 *	 COLOR_FG_DEC	     -	decrement background color
 *	 COLOR_BG_INC	     -	increment foreground color
 *	 COLOR_BG_DEC	     -	decrement background color
 *	 COLOR_SET	     -	color value in color code table
 *	 REVERSE_COLOR	     -	reverse fgrnd and bgrnd colors
 *	 CHANGE_DISPLAY_MODE -	change display mode
 *	 ENABLE_BLINK	     -	enable blinking setting
 *	 ENABLE_BG_INTENSITY -	enable bgrnd intensity setting
 *	 ENABLE_FG_INTENSITY -	enable fgrnd intensity setting
 *
 *    Based on the command passed 'command', the first four
 *    parameters have different semantics.
 *
 *	 FOREGRND     param1	inc/dec foregrnd color entry
 *	 BACKGRND     param1	inc/dec backgrnd color entry
 *
 *	 COLOR_REG    param1	set clr reg to RBG value
 *	 RED	      param2
 *	 GREEN	      param3
 *	 BLUE	      param4
 *
 *	 FG_ENTRY     param1	reverse foregrnd and backgrnd
 *	 BG_ENTRY     param2
 */
vga_color_table( param1, param2, param3, param4, command )
int param1, param2, param3, param4, command;

#define  FOREGRND     param1	    /* used to inc/dec foregrnd color entry  */
#define  BACKGRND     param1	    /* used to inc/dec backgrnd color entry  */

#define  COLOR_REG    param1	    /* used to set color reg to RBG value    */
#define  RED	      param2
#define  GREEN	      param3
#define  BLUE	      param4

#define  FG_ENTRY     param1	    /* used to reverse foregrnd and backgrnd */
#define  BG_ENTRY     param2

{
	DDEBUG(VGA_COLOR, { printf("enter: vga_color_table routine\n"); });

	register struct tty *tp;
	struct	vga_color	*cp;
	struct	vga_color	temp_entry;

	if ( param1 > VGA_MAX_COLORS)
		return;


	/* check for commands that are for A/N and APA color */
	switch ( command )
	  {
		/* increment foreground or background color */
		case COLOR_FG_INC:
		case COLOR_BG_INC:
			cp = &vga_table[param1];
			cp->blue++;
			if (cp->blue > VGA_COLOR_MASK ) {
				cp->blue &= VGA_COLOR_MASK;
				cp->green++;
				if (cp->green >= VGA_COLOR_MASK) {
					cp->green &= VGA_COLOR_MASK;
					cp->red++;
					cp->red &= VGA_COLOR_MASK;
				}
			}
	  		vga_set_color(param1,cp);
			break;
		/* increment foreground or background color */
		case COLOR_FG_DEC:
		case COLOR_BG_DEC:
			cp = &vga_table[param1];
			cp->blue--;
			if (cp->blue < 0x0 ) {
				cp->blue &= VGA_COLOR_MASK;
				cp->green--;
				if (cp->green < 0x0) {
					cp->green &= VGA_COLOR_MASK;
					cp->red--;
					cp->red &= VGA_COLOR_MASK;
				}
			}
	  		vga_set_color(param1,cp);
			break;

	     	case COLOR_SET:		/* set color reg in 256 DAC table     */
			cp = &vga_table[param1];
			cp->blue = (BLUE >> CLRTBL_SHIFT) & VGA_COLOR_MASK;
			cp->red = (RED >> CLRTBL_SHIFT) & VGA_COLOR_MASK;
			cp->green = (GREEN >> CLRTBL_SHIFT) & VGA_COLOR_MASK;
			vga_set_color(param1,cp);
			break;

	     	case REVERSE_COLOR:	/* reverse table values for fg and bg */
	
		    	/* get significant bits from 'FG_ENTRY' and 'BG_ENTRY'    */
			FG_ENTRY &= VGA_FG_MASK;
		    	BG_ENTRY &= VGA_BG_MASK;

		    	/* exchange palette settings of foreground and background */
		    	temp_entry = vga_table[ FG_ENTRY ];
		    	vga_table[ FG_ENTRY ] = vga_table[ BG_ENTRY ];
		    	vga_table[ BG_ENTRY ] = temp_entry;

			vga_set_color(FG_ENTRY,&vga_table[FG_ENTRY]);
			vga_set_color(BG_ENTRY,&vga_table[BG_ENTRY]);
			break;

		default: 
			return(-1);
	  };

	DDEBUG(VGA_COLOR, { printf("normal exit: vga_color_table routine\n");});
	return(0 );
}

/*
 * determine the type of display connected to the vga
 */
static struct vga_color vga_color_test = { 0x12, 0x12, 0x12 };
static struct vga_color vga_gray_test  = { 0x04, 0x12, 0x04 };
int
vga_get_type()
{
	int	save_type = vga_screen_type;
	int	screen_type = -1; /* initialize to a bogus value */
	struct  vga_color save_color = vga_table[0];
	register input_status_1;

	if (!VGA_IN_FOREGROUND) {
		/*
		 * display isn't in the forground, we can't monkey
		 * with the registers, return the last know type.
		 */
		return(vga_screen_type);
	}

 	input_status_1 = vga_get_status();

	/*
	 * set the DAC for color when doing this test
	 */
	vga_screen_type = SCREEN_TYPE_COLOR;


	/* test for the color display */
	while (!vga_vert_retrace(input_status_1));
	vga_set_color(0,&vga_color_test);
	while (vga_retrace(input_status_1));
	if (vga_switch_sense()) {
		screen_type = SCREEN_TYPE_COLOR;
	} else {
		/*test for the gray scale display */
		while (!vga_vert_retrace(input_status_1));
		vga_set_color(0,&vga_gray_test);
		while (vga_retrace(input_status_1));
		if (vga_switch_sense()) {
			screen_type = SCREEN_TYPE_MONO;
		} else {
			screen_type = SCREEN_TYPE_NONE;
		}
	}
	/*
	 * now restore the DAC and current type variable
	 */
	vga_screen_type = save_type;
	vga_set_color(0,&save_color);
	return(screen_type);

}

/*
 * vga_attribute - given the screen attribute flags and/or
 *   commands 'screen_attr', return the actual system dependent
 *   attribute setting. 
 *
 * Note: The difference between 'screen_attr' and 'system_attr'
 *   is the screen attribute (screen_attr) refers to the
 *   screen_conf.c 's attribute FLAGS and/or COMMANDS, and
 *   the system attribute (system_attr) is the actual system
 *   attribute settings.
 */
LOCAL
vga_attribute( screen_attr, foregrnd, backgrnd )
register unsigned screen_attr, foregrnd, backgrnd;
{
	DDEBUG(VGA_ATTR, { printf("enter: vga_color_an routine\n"); });

	register int sys_attr;	      /* system attribute	      */

	sys_attr =  (foregrnd & VGA_FG_MASK) |
		 ((backgrnd & VGA_BG_MASK) << VGA_COLOR_SHIFT );

	if ( screen_attr & HI_INTENSITY )
	     sys_attr |=  VGA_INTENSE;
	else sys_attr &= ~VGA_INTENSE;

	if ( screen_attr & BLINK )  /* this also sets backgrnd, hi intensity */
	      sys_attr |=  VGA_BLINK;
	else  sys_attr &= ~VGA_BLINK;

	DDEBUG(VGA_ATTR, { printf("normal exit: vga_color_an routine\n"); });
	return( sys_attr );
}

/*
 * set the vga color table entry. This is the REAL vga color table,
 * not the psuedo color table (which is the ega's REAL color table).
 * entry must be 0 to 255, red/green/blue (fields int the vga_color
 * struct) should be 0 to 63, but neither the code nor hardware chokes
 * on out of range values. The caller is responsible to doing the 
 * value checking.
 */
LOCAL
vga_set_color(entry,colorptr)
	struct	vga_color	*colorptr;
{

	int green = colorptr->green;


	if (VGA_IN_FOREGROUND) {
		/*
		 * for the monochrome display translate the color to
		 * gray scale and set that to the green port.
		 */
		if (vga_screen_type == SCREEN_TYPE_MONO) {
			green = VGA_GRAY(colorptr->red,colorptr->green,
								colorptr->blue);
		} 
	/*
	 * magic hardware incantation to load color table. (this is actually
	 * more straight forward than most). NOTE: the red and blue are ignored
	 * on the mono, but we set them anyway. This allows color displays to
	 * be "almost" right even it the current type is mono (this can happen
	 * if someone has a mono ibm8514 and a color vga, or someone has changed
	 * displays with re-initting the screen).
	 */
		out(VGA_DAC_ADDR,entry);
		out(VGA_DAC_DATA, colorptr->red);
		out(VGA_DAC_DATA, green);
		out(VGA_DAC_DATA, colorptr->blue);
	}
	/*
	 * remember the real color for 1) increment/decrement functions,
	 * 2) reverse functions, and 3) reloaded the color table for
	 * a different screen type. 
	 */
	vga_table[entry] = *colorptr;
}
#endif NVGA
