#include "insignia.h"
#include "host_def.h"
/*[
======================================================================

				 SoftPC Revision 3.0

 Title:
		v7_ports.c

 Description:
		Code for the extended registers of the Video 7 VGA.
		Based on the V7 VGA Technical Reference Manual.

 Author:
		Phil Taylor

 Date:
		26 September 1990

 SccsID	"@(#)v7_ports.c	1.19 01/13/95 Copyright Insignia Solutions Ltd."

======================================================================
]*/


#ifdef V7VGA

#include "xt.h"
#include "gvi.h"
#include "gmi.h"
#include "ios.h"
#include "gfx_upd.h"
#include "debug.h"
#include "egacpu.h"
#include "egaports.h"
#include "egagraph.h"
#include "egaread.h"
#include "vgaports.h"


#ifdef SEGMENTATION
/*
 * The following #include specifies the code segment into which this
 * module will by placed by the MPW C compiler on the Mac II running
 * MultiFinder.
 */ 
#include "SOFTPC_VGA.seg"
#endif


IMPORT	byte	crtc_0_7_protect;
IMPORT	byte	crtc_0_8_protect;
IMPORT	byte	crtc_9_b_protect;
IMPORT	byte	crtc_c_protect;

FORWARD void    draw_v7ptr();

GLOBAL ULONG fg_latches;
GLOBAL UTINY fg_bg_control;

GLOBAL	void	remove_v7ptr IPT0();

SAVED	word	curr_v7ptr_x;
SAVED	word	curr_v7ptr_y;

/*(
----------------------------------------------------------------------

Function:	
		vga_seq_extn_control( io_addr port, half_word value )

Purpose:
		To emulate writing to the Extensions Control Register.

Input:
		port	- the V7VGA I/O port (should always be 0x3c5)
		value	- the value to be written to the register

Output:
		The Extensions Control Register is set to the correct value.

----------------------------------------------------------------------
)*/

GLOBAL VOID
vga_seq_extn_control(port, value)
io_addr         port;
half_word       value;

{
	NON_PROD(if(io_verbose & EGA_PORTS_VERBOSE)fprintf(trace_file,"seq(6,%#x)\n",value);)
	note_entrance2("vga_seq_extn_control(%x,%x)", port, value);

	if (value == 0xea)
		sequencer.extensions_control.as_bfld.extension_enable = 1;
	else if (value == 0xae)
		sequencer.extensions_control.as_bfld.extension_enable = 0;
}

GLOBAL VOID
v7_get_banks( rd_bank, wrt_bank )

UTINY *rd_bank;
UTINY *wrt_bank;

{
	if( get_seq_chain4_mode() && get_chain4_mode() ) {
		set_v7_bank_for_seq_chain4( rd_bank, wrt_bank );
	}
	else {

	/* 
	   1.4.92 MG
	   Note that we and off the top bit of the bank selects. This means
	   that accesses to the top 512k (which we don't have) get mapped into
	   the bottom 512k, rather than being thrown away. This prevents SEGVs
	   and saves complications in the write routines, but causes other
	   problems.

	   Further explanation is in draw_v7ptr() at the end of this file.
	*/

		*rd_bank=(extensions_controller.ram_bank_select.as_bfld.cpu_read_bank_select&1);
		*wrt_bank=(extensions_controller.ram_bank_select.as_bfld.cpu_write_bank_select&1);
	}
}

/*(
----------------------------------------------------------------------

Function:	
		vga_extn_outb( io_addr port, half_word value )

Purpose:
		To emulate writing to the V7VGA Extension Registers

Input:
		port	- the V7VGA I/O port (should always be 0x3c5)
		value	- the value to be written to the register

Output:
		The Extension Registers are set to the correct value, and 
		any other required actions are emulated.

----------------------------------------------------------------------
)*/

GLOBAL VOID
vga_extn_outb(port, value)
io_addr         port;
half_word       value;

{
	half_word	old_value;

	note_entrance2("vga_extn_outb(%x,%x)", port, value);
	NON_PROD(if(io_verbose & EGA_PORTS_VERBOSE)fprintf(trace_file,"seq ext (%#x,%#x)\n",
		sequencer.address.as.abyte,value);)

	switch (sequencer.address.as.abyte) {

		case 0x83:
			note_entrance0("attribute controller index");
			attribute_controller.address.as.abyte = value;
			break;
		case 0x94:
			note_entrance0("pointer pattern");
			extensions_controller.pointer_pattern = value;
			host_start_update ();
			(*clear_v7ptr)(curr_v7ptr_x, curr_v7ptr_y);
			draw_v7ptr();
			host_end_update ();
			break;
		case 0x9c:
			note_entrance0("pointer horiz position hi");
			extensions_controller.ptr_horiz_posn_hi.as.abyte = value;
			break;
		case 0x9d:
			note_entrance0("pointer horiz position lo");
			extensions_controller.ptr_horiz_posn_lo = value;
			break;
		case 0x9e:
			note_entrance0("pointer vert position hi");
			extensions_controller.ptr_vert_posn_hi.as.abyte = value;
			break;
		case 0x9f:
			note_entrance0("pointer vert position lo");
			extensions_controller.ptr_vert_posn_lo = value;
			host_start_update ();
			(*clear_v7ptr)(curr_v7ptr_x, curr_v7ptr_y);
			draw_v7ptr();
			host_end_update ();
			break;
		case 0xa0:
			note_entrance0("graphics controller mem latch 0");
			/* Direct access to memory data latch 0 */
			put_latch0(value);
			break;
		case 0xa1:
			note_entrance0("graphics controller mem latch 1");
			/* Direct access to memory data latch 1 */
			put_latch1(value);
			break;
		case 0xa2:
			note_entrance0("graphics controller mem latch 2");
			/* Direct access to memory data latch 2 */
			put_latch2(value);
			break;
		case 0xa3:
			note_entrance0("graphics controller mem latch 3");
			/* Direct access to memory data latch 3 */
			put_latch3(value);
			break;
		case 0xa4:
			note_entrance0("clock select");
			extensions_controller.clock_select.as.abyte = value;
			/* Typically set to 0x10 for extended hi-res modes */
			break;
		case 0xa5:
			note_entrance0("cursor attributes");
			old_value=extensions_controller.cursor_attrs.as.abyte;
			extensions_controller.cursor_attrs.as.abyte = value;

			/*
			   8.6.92 MG
			   We need to check that the pointer was disabled 
			   before we redraw it when it is enabled. Otherwise
			   we can get old pointers left on the screen in 
			   Windows.
			*/

			/* Not doing cursor mode stuff (whatever that means) */

			if (value&0x80!=old_value&0x80) {
				host_start_update ();
				if (value & 0x80) {
					/* Enable hardware graphics pointer */ 
					draw_v7ptr();
				}
				else {
					/* Disable hardware graphics pointer */ 
					(*clear_v7ptr)(curr_v7ptr_x, curr_v7ptr_y);
				}
				host_end_update ();
			}
			break;

		/* 
		   31.3.92 MG Register c1 is an undocumented DAC control
		   register, bit 0 switches between 6 and 8 bit data in 
		   the DAC.
		*/

		case 0xc1:
			extensions_controller.dac_control.as.abyte=value;
			if (extensions_controller.dac_control.as_bfld.dac_8_bits) {
				DAC_data_bits=8;
				DAC_data_mask=0xff;
			}
			else {
				DAC_data_bits=6;
				DAC_data_mask=0x3f;
			}
			break;
		case 0xea:
			note_entrance0("switch strobe");
			/* loads up switch readback with some CPU data lines */
			extensions_controller.switch_readback = 0xff;
			/* This is hard coded for the jumper/switch settings, and might not be right */
			break;
		case 0xeb:
			note_entrance0("emulation_control");
			extensions_controller.emulation_control.as.abyte = value;
			if (extensions_controller.emulation_control.as_bfld.write_prot_2)
				crtc_0_8_protect = TRUE;
			else
				crtc_0_8_protect = FALSE;
			if (extensions_controller.emulation_control.as_bfld.write_prot_1)
				crtc_9_b_protect = TRUE;
			else
				crtc_9_b_protect = FALSE;
			if (extensions_controller.emulation_control.as_bfld.write_prot_0)
				crtc_c_protect = TRUE;
			else
				crtc_c_protect = FALSE;
			break;
		case 0xec:
			note_entrance0("foreground latch 0");
			extensions_controller.foreground_latch_0 = value;
			SET_FG_LATCH( 0, value );
			break;
		case 0xed:
			note_entrance0("foreground latch 1");
			extensions_controller.foreground_latch_1 = value;
			SET_FG_LATCH( 1, value );
			break;
		case 0xee:
			note_entrance0("foreground latch 2");
			extensions_controller.foreground_latch_2 = value;
			SET_FG_LATCH( 2, value );
			break;
		case 0xef:
			note_entrance0("foreground latch 3");
			extensions_controller.foreground_latch_3 = value;
			SET_FG_LATCH( 3, value );
			break;
		case 0xf0:
			note_entrance0("fast foreground latch load");
			switch (extensions_controller.fast_latch_load_state.as_bfld.fg_latch_load_state)
			{
				case 0:
					extensions_controller.foreground_latch_0 = value;
					extensions_controller.fast_latch_load_state.as_bfld.fg_latch_load_state = 1;
					SET_FG_LATCH( 0, value );
					break;
				case 1:
					extensions_controller.foreground_latch_1 = value;
					extensions_controller.fast_latch_load_state.as_bfld.fg_latch_load_state = 2;
					SET_FG_LATCH( 1, value );
					break;
				case 2:
					extensions_controller.foreground_latch_2 = value;
					extensions_controller.fast_latch_load_state.as_bfld.fg_latch_load_state = 3; 
					SET_FG_LATCH( 2, value );
					break;
				case 3:
					extensions_controller.foreground_latch_3 = value;
					extensions_controller.fast_latch_load_state.as_bfld.fg_latch_load_state = 0;
					SET_FG_LATCH( 3, value );
					break;
			}
			break;
		case 0xf1:
			note_entrance0("fast latch load state");
			extensions_controller.fast_latch_load_state.as.abyte = value;
			break;
		case 0xf2:
			note_entrance0("fast background latch load");
			switch (extensions_controller.fast_latch_load_state.as_bfld.bg_latch_load_state)
			{
				case 0:
					put_latch0(value);
					extensions_controller.fast_latch_load_state.as_bfld.bg_latch_load_state = 1;
					break;
				case 1:
					put_latch1(value);
					extensions_controller.fast_latch_load_state.as_bfld.bg_latch_load_state = 2;
					break;
				case 2:
					put_latch2(value);
					extensions_controller.fast_latch_load_state.as_bfld.bg_latch_load_state = 3; 
					break;
				case 3:
					put_latch3(value);
					extensions_controller.fast_latch_load_state.as_bfld.bg_latch_load_state = 0;
					break;
			}
			break;
		case 0xf3:
			note_entrance0("masked write control");
			extensions_controller.masked_write_control.as.abyte = value;
			break;
		case 0xf4:
			note_entrance0("masked write mask");
			extensions_controller.masked_write_mask = value;
			break;
		case 0xf5:
			note_entrance0("foreground/background pattern");
			extensions_controller.fg_bg_pattern = value;
			break;
		case 0xf6:
			note_entrance0("1Mb RAM bank select");
			extensions_controller.ram_bank_select.as.abyte = value;
			update_banking();
			break;
		case 0xf7:
			note_entrance0("switch readback");
			extensions_controller.switch_readback = value;
			break;
		case 0xf8:
			note_entrance0("clock control");
			extensions_controller.clock_control.as.abyte = value;
			/* Hope we don't have to do anything here */
			break;
		case 0xf9:
			note_entrance0("page select");
			extensions_controller.page_select.as.abyte = value;
			update_banking();
			break;
		case 0xfa:
			note_entrance0("foreground color");
			extensions_controller.foreground_color.as.abyte = value;
			break;
		case 0xfb:
			note_entrance0("background color");
			extensions_controller.background_color.as.abyte = value;
			break;
		case 0xfc:
			note_entrance0("compatibility control");
			{
				BOOL now_seqchain4;
				BOOL now_seqchain;

				now_seqchain4 = get_seq_chain4_mode();
				now_seqchain = get_seq_chain_mode();
				extensions_controller.compatibility_control.as.abyte = value;
				set_seq_chain4_mode(extensions_controller.compatibility_control.as_bfld.sequential_chain4);
				set_seq_chain_mode(extensions_controller.compatibility_control.as_bfld.sequential_chain);
				if (get_chain4_mode() && (now_seqchain4 != get_seq_chain4_mode()))
				{
					/* do we need to change the read/write routines here?? */
					ega_read_routines_update();
					ega_write_routines_update( CHAINED );
				}
			}
			break;
		case 0xfd:
			note_entrance0("timing select");
			extensions_controller.timing_select.as.abyte = value;
			/* Used to select timing states for V-RAM hi-res modes */
			/* Hope we don't have to do anything here */
			break;
		case 0xfe:
			note_entrance0("foreground/background control");
			extensions_controller.fg_bg_control.as.abyte = value;
			fg_bg_control = value;
			ega_read_routines_update();
			ega_write_routines_update( WRITE_MODE );

			/***
			set_fg_bg_mode();
			***/
			break;
		case 0xff:
			note_entrance0("16-bit interface control");
			extensions_controller.interface_control.as.abyte = value;

			/***
			sort_out_memory_stuff();
			sort_out_interface_stuff();
			***/
			break;
		default:
			NON_PROD(if(io_verbose & EGA_PORTS_VERBOSE)
			        fprintf(trace_file,"Bad extensions index %x\n",
			        sequencer.address.as.abyte);)
			break;
	}
}

/*(
----------------------------------------------------------------------

Function:	
		vga_extn_inb( io_addr port, half_word *value )

Purpose:
		To emulate reading from the V7VGA Extension Registers

Input:
		port	- the V7VGA I/O port (should always be 0x3c5)

Output:
		value	- the value read from the register
		Any other required actions are emulated.

----------------------------------------------------------------------
)*/

GLOBAL VOID
vga_extn_inb(port, value)
io_addr         port;
half_word       *value;
{
	note_entrance1("vga_extn_inb(%x)", port);

	switch (sequencer.address.as.abyte) {

		case 0x83:
			*value = attribute_controller.address.as.abyte;
			break;
		case 0x8e:
		case 0x8f:
			/* chip revision 3 */
			*value = 0x70;
			break;
		case 0x94:
			*value = extensions_controller.pointer_pattern;
			break;
		case 0x9c:
			*value = extensions_controller.ptr_horiz_posn_hi.as.abyte;
			break;
		case 0x9d:
			*value = extensions_controller.ptr_horiz_posn_lo;
			break;
		case 0x9e:
			*value = extensions_controller.ptr_vert_posn_hi.as.abyte;
			break;
		case 0x9f:
			*value = extensions_controller.ptr_vert_posn_lo;
			break;
		case 0xa0:
			/* Direct access to memory data latch 0 */
			*value = get_latch0;
			break;
		case 0xa1:
			/* Direct access to memory data latch 1 */
			*value = get_latch1;
			break;
		case 0xa2:
			/* Direct access to memory data latch 2 */
			*value = get_latch2;
			break;
		case 0xa3:
			/* Direct access to memory data latch 3 */
			*value = get_latch3;
			break;
		case 0xa4:
			*value = extensions_controller.clock_select.as.abyte;
			break;
		case 0xa5:
			*value = extensions_controller.cursor_attrs.as.abyte & 0x89;
			break;

		/* 
		   31.3.92 MG Register C1 controls 6/8 bit data in the DAC.
		*/

		case 0xc1:
			*value=extensions_controller.dac_control.as.abyte;	
			break;
		case 0xeb:
			*value = extensions_controller.emulation_control.as.abyte;
			break;
		case 0xec:
			*value = extensions_controller.foreground_latch_0;
			break;
		case 0xed:
			*value = extensions_controller.foreground_latch_1;
			break;
		case 0xee:
			*value = extensions_controller.foreground_latch_2;
			break;
		case 0xef:
			*value = extensions_controller.foreground_latch_3;
			break;
		case 0xf0:
			switch (extensions_controller.fast_latch_load_state.as_bfld.fg_latch_load_state)
			{
				case 0:
					*value = extensions_controller.foreground_latch_0;
					break;
				case 1:
					*value = extensions_controller.foreground_latch_1;
					break;
				case 2:
					*value = extensions_controller.foreground_latch_2;
					break;
				case 3:
					*value = extensions_controller.foreground_latch_3;
					break;
			}
			extensions_controller.fast_latch_load_state.as_bfld.fg_latch_load_state = 0;
			break;
		case 0xf1:
			*value = extensions_controller.fast_latch_load_state.as.abyte;
			break;
		case 0xf2:
			switch (extensions_controller.fast_latch_load_state.as_bfld.bg_latch_load_state)
			{
				case 0:
					*value = get_latch0;
					break;
				case 1:
					*value = get_latch1;
					break;
				case 2:
					*value = get_latch2;
					break;
				case 3:
					*value = get_latch3;
					break;
			}
			extensions_controller.fast_latch_load_state.as_bfld.bg_latch_load_state = 0;
			break;
		case 0xf3:
			*value = extensions_controller.masked_write_control.as.abyte & 3;
			break;
		case 0xf4:
			*value = extensions_controller.masked_write_mask;
			break;
		case 0xf5:
			*value = extensions_controller.fg_bg_pattern;
			break;
		case 0xf6:
			*value = extensions_controller.ram_bank_select.as.abyte;
			break;
		case 0xf7:
			*value = extensions_controller.switch_readback;
			break;
		case 0xf8:
			*value = extensions_controller.clock_control.as.abyte;
			break;
		case 0xf9:
			*value = extensions_controller.page_select.as_bfld.extended_page_select;
			break;
		case 0xfa:
			*value = extensions_controller.foreground_color.as.abyte;
			break;
		case 0xfb:
			*value = extensions_controller.background_color.as.abyte;
			break;
		case 0xfc:
			*value = extensions_controller.compatibility_control.as.abyte;
			break;
		case 0xfd:
			*value = extensions_controller.timing_select.as.abyte;
			break;
		case 0xfe:
			*value = extensions_controller.fg_bg_control.as.abyte & 0xe;
			break;
		case 0xff:
			*value = extensions_controller.interface_control.as.abyte;
			break;
		default:
			NON_PROD(if(io_verbose & EGA_PORTS_VERBOSE)
			        fprintf(trace_file,"Bad extensions index %x\n",
			        sequencer.address.as.abyte);)

		/* 31.3.92 MG This used to return 0xFF, but a real card
		   returns zero. */

			*value = 0;
			break;

	}
	note_entrance1("returning %x",*value);
}

/*(
----------------------------------------------------------------------

Function:	
		draw_v7ptr()

Purpose:
		To emulate the V7 hardware pointer on the screen.

Input:
		None.

Output:
		The pointer is displayed on the screen.

----------------------------------------------------------------------
)*/

GLOBAL VOID
draw_v7ptr()
{
	sys_addr pattern;

	if (extensions_controller.cursor_attrs.as_bfld.pointer_enable)
	{
		curr_v7ptr_x = extensions_controller.ptr_horiz_posn_lo + 
			(extensions_controller.ptr_horiz_posn_hi.as_bfld.ptr_horiz_position << 8);


		if (get_seq_chain4_mode() && get_chain4_mode())
		{
			/*
			 * In the extended 256 col modes we seem to need to halve
			 * the x coordinate to get the pointer in the right place.
			 */
			curr_v7ptr_x >>= 1;
		}

		curr_v7ptr_y = extensions_controller.ptr_vert_posn_lo + 
			(extensions_controller.ptr_vert_posn_hi.as_bfld.ptr_vert_position << 8);

		/*
		 * I have never seen the pointer bank select bits used, so 
		 * this is a guess as to their meaning.
		 */

	/* 
	   1.4.92 MG
	   Sadly, this guess isn't correct - the pointer_bank_select bits are
	   used to select which 256k bank the pointer data is read from. Now
	   we have a problem, as if a program writes the data to the third
	   bank then it actually ends up in the first because the bank
	   selection routines for memory access and off the top bit.

	   As a result, we also need to lose the top bit here. The correct
	   way to do this would be to either support nonexistent VGA memory
	   correctly, or to add another 512k to the memory we already use.
	   
	   This fix is mainly to make Windows 3.1 work with its video-7
	   driver. It stuffs bytes into the last few k of the 1Mb space on
	   the video-7 to see if the memory exists. As we just map this
	   access to 512k lower, it thinks we have 1Mb of RAM rather than
	   512k, so puts the pointer at the top of the 1Mb.

	   Programs which call the VGA BIOS to determine the memory size
	   will not have this problem.
	*/

		pattern = (((extensions_controller.interface_control.as_bfld.pointer_bank_select&1) << 16)
			+ (0xc000 + (extensions_controller.pointer_pattern << 6))) << 2;

		(*paint_v7ptr)(pattern, curr_v7ptr_x, curr_v7ptr_y);
	}
}

GLOBAL	VOID	remove_v7ptr IFN0()

{
	(*clear_v7ptr)(curr_v7ptr_x, curr_v7ptr_y);
}

GLOBAL	BOOL	v7ptr_between_lines IFN2(int, start_line, int, end_line)

{
	if (curr_v7ptr_y+32<start_line||curr_v7ptr_y>end_line)
		return FALSE;
	return TRUE;
}

#ifdef CPU_40_STYLE
/*
 * 4.0 video support moves v7 fg latch value from variable 'fg_latches'
 * int CPU variable accessed by interface fn to get/set all 4 bytes of
 * latches. Take byte index and value and update v7 latch via interface
 */
GLOBAL void set_v7_fg_latch_byte IFN2(IU8, index, IU8, value)
{
	IU32 v7latch;

	/* get current value */
	v7latch = getVideov7_fg_latches();

	/* change byte 'index' to 'value */
	switch(index)
	{
	case 0:
		v7latch = (v7latch & 0xffffff00) | value;
		break;

	case 1:
		v7latch = (v7latch & 0xffff00ff) | (value << 8);
		break;

	case 2:
		v7latch = (v7latch & 0xff00ffff) | (value << 16);
		break;

	case 3:
		v7latch = (v7latch & 0x00ffffff) | (value << 24);
		break;

	default:
		always_trace1("set_v7_fg_latch_byte: index > 3 (%d)", index);
	}

	/* update v7 latches */
	setVideov7_fg_latches(v7latch);
}
#endif	/* CPU_40_STYLE */

#endif /* V7VGA */
