/*
 * USC-ACSC W2 Monitor: $Header:vga.c 12.0$
 * Copyright University of Southern California, 1988
 */

/* W2 Monitor VGA Terminal Driver.
 */

#include <dos.h>
#include "pcparam.h"
#include "rb.h"
#include "vga.h"
#include "vars.h"

/*
 * This is the shared data structure with unix 
 * we use a pointer (vga) to a buffer so that we can 
 * insure that it is aligned properly for unix to access.
 */
char vga_params_buf[(sizeof (struct vga_params)) * NCPU + 16] = { 0 };
struct vga_params *vga = (struct vga_params *) vga_params_buf;
int vga_cur_mode[NCPU];

/* defines to make bios requests semi meaningful */
static struct bios_int vga_bios;
#define	vga_mode	vga_bios.gen_reg.h
#define vga_mode_l	vga_bios.gen_reg.x
#define command		ah
#define type		al
#define	sub_command	bl
#define	scan_lines	al
#define VGA_SET_ALTERNATE	0x12
#define VGA_SET_SCAN		0x30
#define VGA_BIOS_MODE		0x00

/*
 * parameters to set the modes to
 */
struct vga_mode_param {
	u_char	type;
	u_char	scan;
	u_long	screen_addr;
};

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

char hkey[NCPU] = { 0 };	/* if we've enabled hot keys yet */

/*
 * all we do to initialize is make sure that it is aligned
 * properly, and set write status.
 */

vga_init()
{
	vga = (struct vga_params *) align(vga, 4, int);
	vga->write_status = exchw(VGA_WRITE_OK);
	DEBUGF(*dbugptr & VGADEBUG, printf("vga=%x\n",vga););
}

static int
set_vga_mode(parms)
	register struct vga_mode_param *parms;

{
	DEBUGF(*dbugptr & VGADEBUG, printf("vga: setting scan=%d\n",parms->scan););
	if (parms->scan != 3) {
		vga_mode.command = VGA_SET_ALTERNATE;
		vga_mode.sub_command = VGA_SET_SCAN;
		vga_mode.scan_lines = parms->scan;
		int10(&vga_bios);
	}
	DEBUGF(*dbugptr & VGADEBUG, printf("vga: setting mode=%d\n",parms->type););
	vga_mode.command = VGA_BIOS_MODE;
	vga_mode.type = parms->type;
	int10(&vga_bios);
	return 0;
}


void
set_screen(vga,mode)
struct vga_params *vga;
u_short	mode;

{
	int	rc;

	if (mode > VGA_MAX_MODES) {
		return;
	}

	if(vga->write_status == exchw(VGA_WRITE_OK)) {
		if ( rc = set_vga_mode(&vga_mode_param[mode]) ){
			DEBUGF(*dbugptr & VGADEBUG, printf("set_screen_mode: Mode %d - error %d\n", mode,rc););
		} else
			DEBUGF(*dbugptr & VGADEBUG, printf("set_screen_mode: Mode %d - result %d\n", mode,rc););
	}
	vga->u.vga_screen_addr = exchl(vga_mode_param[mode].screen_addr);
	DEBUGF(*dbugptr & VGADEBUG, printf("screen_addr=%lx\n", (long) vga_mode_param[mode].screen_addr););
}

/* function to execute parse and execute vga command */
int
vga_command()

{
	int	rc;
	short	opcode;
	struct vga_params *v = &vga[cur_cbcb->cpu];

	/*
	 * Change byte order of unix opcode.
	 */
	opcode = exchw(v->unix_opcode);
	DEBUGF(*dbugptr & VGADEBUG, printf("vga command, op_code = %d \n",opcode););

	switch ( opcode ) {

	case	VGA_HKEY_REQ:
		hkey[cur_cbcb->cpu] = 1;	/* allowing hot keys */
		break;

	case	VGA_SET_MODE:
		set_screen(v,exchw(v->mode));
		vga_cur_mode[cur_cbcb->cpu] = exchw(v->mode);
		break;

	default:
		printf("vga: unsupported op code = %d\n",opcode); VGADEBUG, printf("vga: unsupported op code = %d\n",opcode);
		break;
	}

	v->unix_opcode = 0;	/* reset */

	return 1;
}

#define SCREEN_NORMAL	0
#define SCREEN_SAVE	1
#define SCREEN_RESTORE	2
#define SCREEN_DONE	3

int screen_state = SCREEN_NORMAL;
extern struct XCLOCK xclock;
int screen_time = -1;
int screen_counter;
static struct cbcb *next_cbcb;		/* the screen to restore to */

/*
 * switch screen/keyboard/mouse to the next cpu
 * 1. switch the screen_cbcb
 * 2. IPL processor if ready for IPL
 * 3. disable writes to old screen, enable on new one
 * 3. send hot key to Unix so that it can save the screen
 */
next_screen()
{
	struct cbcb *cbcb = screen_cbcb;

	if (++cbcb >= end_cbcb)
		cbcb = cbcbptr;
	if (screen_cbcb != cbcb)
	{
		/*
		 * screen changed; disable write to screen that's 
		 * losing it, and send appropriate hot key sequence
		 */
		next_cbcb = cbcb;	/* what to restore to */
		screen_change(SCREEN_SAVE);
	}
}

#define SCREEN_COUNTER	60	/* 60 seconds to do it */

/*
 * routine to check to see if Unix has completed the requested
 * screen save. If not we will resend the command again.
 */
screen_change(state)
{
	struct kbdata *kb;

	if (state != SCREEN_NORMAL)
	{
		screen_state = state;
		screen_counter = SCREEN_COUNTER;	/* for save and restore both */
	}
	else if (vga[screen_cbcb->cpu].pc_opcode == 0 ||
			--screen_counter <= 0 ||
			screen_cbcb->state != STATE_RUNNING)
	{
		++screen_state;	/* go to next state */
		screen_counter = SCREEN_COUNTER;	/* for save and restore both */
	}
	screen_time = xclock.sec;	/* current time */
	switch(screen_state)
	{
	case SCREEN_SAVE:
		vga[screen_cbcb->cpu].write_status = exchw(VGA_WRITE_DISABLED);
		if (hkey[screen_cbcb->cpu])
		{
			vga[screen_cbcb->cpu].pc_opcode = exchw(VGA_SAVE);
			kbsend((KBDSTAT_HOT_KEY<<8) + VGA_SAVE, screen_cbcb);
			break;
		}
		/* fall thru in non hot-key case to do the restore now */
		screen_state = SCREEN_RESTORE;
		screen_counter = SCREEN_COUNTER;	/* for save and restore both */
	case SCREEN_RESTORE:
	{
		int cpu = next_cbcb->cpu;
		/*
		 * tell new owner of screen to restore it
		 * we reset the 8514 in case the new owner doesn't use it
		 */
		if (screen_counter == SCREEN_COUNTER)
			outpw(0x4ae8,0);	/* reset 8514 first time thru */
		screen_cbcb = next_cbcb;
		vga[cpu].write_status = exchw(VGA_WRITE_OK);
		if ((IOIN(screen_cbcb->port+R_STAT) & R_IPLR) == 0 &&
			screen_cbcb->state == STATE_IPL)
		{
			SETBIT(screen_cbcb->port+R_CTRL, R_IPLC); /* IPL it */
			screen_cbcb->state = STATE_RUNNING;
			state_change(screen_cbcb);
		} else
		{
/*			int mode = vga_cur_mode[cpu];
			if (mode)
				set_screen(&vga[cpu],mode);	/* causes problems */
			vga[cpu].pc_opcode = exchw(VGA_RESTORE);
			kbsend((KBDSTAT_HOT_KEY<<8) + VGA_RESTORE, screen_cbcb);
		}
		if (hkey[cpu])
			break;
		/* fall thru for non hot-key case */
	}
	case SCREEN_DONE:
	default:
		screen_state = SCREEN_NORMAL;
		screen_time = -1;		/* we're done */
		break;
	}
}
