#include "insignia.h"
#include "host_def.h"

#if !defined(NTVDM) || ( defined(NTVDM) && !defined(X86GFX) )

/*			INSIGNIA (SUB)MODULE SPECIFICATION
			-----------------------------


	THIS PROGRAM SOURCE FILE  IS  SUPPLIED IN CONFIDENCE TO THE
	CUSTOMER, THE CONTENTS  OR  DETAILS  OF  ITS OPERATION MUST
	NOT BE DISCLOSED TO ANY  OTHER PARTIES  WITHOUT THE EXPRESS
	AUTHORISATION FROM THE DIRECTORS OF INSIGNIA SOLUTIONS LTD.


DOCUMENT 			: name and number

RELATED DOCS		: include all relevant references

DESIGNER			: J Maiden 

REVISION HISTORY	:
First version		: J Maiden, SoftPC 2.0
Second version		: J Shanly, SoftPC 3.0

SUBMODULE NAME		: write mode 1 and 2

SOURCE FILE NAME	: ega_writem1.c

PURPOSE			: functions to write to EGA memory in write modes 1 & 2
		
		
SccsID = @(#)ega_wrtm12.c	1.15 8/10/92 Copyright Insignia Solutions Ltd.

[1.INTERMODULE INTERFACE SPECIFICATION]

[1.0 INCLUDE FILE NEEDED TO ACCESS THIS INTERFACE FROM OTHER SUBMODULES]

	INCLUDE FILE : ega_cpu.pi

[1.1    INTERMODULE EXPORTS]

	PROCEDURES() :	
			ega_mode1_chn_b_write();
			ega_mode1_chn_w_write();
			ega_mode1_chn_b_fill();
			ega_mode1_chn_w_fill();
			ega_mode1_chn_b_move();
			ega_mode1_chn_w_move();
			ega_mode2_chn_b_write();
			ega_mode2_chn_w_write();
			ega_mode2_chn_b_fill();
			ega_mode2_chn_w_fill();
			ega_mode2_chn_b_move();
			ega_mode2_chn_w_move();

	DATA 	     :	give type and name

-------------------------------------------------------------------------
[1.2 DATATYPES FOR [1.1] (if not basic C types)]

	STRUCTURES/TYPEDEFS/ENUMS: 
		
-------------------------------------------------------------------------
[1.3 INTERMODULE IMPORTS]
     (not o/s objects or standard libs)


-------------------------------------------------------------------------

[1.4 DESCRIPTION OF INTERMODULE INTERFACE]

[1.4.1 IMPORTED OBJECTS]

DATA OBJECTS	  :	struct EGA_CPU

FILES ACCESSED    :	NONE

DEVICES ACCESSED  :	NONE

SIGNALS CAUGHT	  :	NONE

SIGNALS ISSUED	  :	NONE


[1.4.2 EXPORTED OBJECTS]

/*=======================================================================
[3.INTERMODULE INTERFACE DECLARATIONS]
=========================================================================

[3.1 INTERMODULE IMPORTS]						*/

/* [3.1.1 #INCLUDES]                                                    */


#ifdef EGG

#include	"xt.h"
#include	"debug.h"
#include	"sas.h"
#include	TypesH
#include	"cpu.h"
#include	"gmi.h"
#include	"egacpu.h"
#include	"egaports.h"
#include	"cpu_vid.h"
#include	"gfx_upd.h"

/* [3.1.2 DECLARATIONS]                                                 */


/* [3.2 INTERMODULE EXPORTS]						*/ 


/*
5.MODULE INTERNALS   :   (not visible externally, global internally)]     

[5.1 LOCAL DECLARATIONS]						*/

/* [5.1.1 #DEFINES]							*/

#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.
 */
#ifdef PROD
#include "SOFTPC_EGA.seg"
#else
#include "SOFTPC_EGA_WRITE.seg"
#endif
#endif

/* [5.1.2 TYPEDEF, STRUCTURE, ENUM DECLARATIONS]			*/


/* [5.1.3 PROCEDURE() DECLARATIONS]					*/


/* -----------------------------------------------------------------------
[5.2 LOCAL DEFINITIONS]

   [5.2.1 INTERNAL DATA DEFINITIONS 					*/

/* [5.2.2 INTERNAL PROCEDURE DEFINITIONS]				*/


/*
7.INTERMODULE INTERFACE IMPLEMENTATION :

/*
[7.1 INTERMODULE DATA DEFINITIONS]				*/

#ifdef A_VID
IMPORT VOID	_ch2_mode1_chn_byte_write_glue();
IMPORT VOID	_ch2_mode1_chn_word_write_glue();
IMPORT VOID	_ch2_mode1_chn_byte_fill_glue();
IMPORT VOID	_ch2_mode1_chn_word_fill_glue();
IMPORT VOID	_ch2_mode1_chn_byte_move_glue();
IMPORT VOID	_ch2_mode1_chn_word_move_glue();

WRT_POINTERS mode1_handlers =
{
	_ch2_mode1_chn_byte_write_glue,
	_ch2_mode1_chn_word_write_glue,
	_ch2_mode1_chn_byte_fill_glue,
	_ch2_mode1_chn_word_fill_glue,
	_ch2_mode1_chn_byte_move_glue,
	_ch2_mode1_chn_byte_move_glue,
	_ch2_mode1_chn_word_move_glue,
	_ch2_mode1_chn_word_move_glue
};

IMPORT VOID	_ch2_mode2_chn_byte_write_glue();
IMPORT VOID	_ch2_mode2_chn_word_write_glue();
IMPORT VOID	_ch2_mode2_chn_byte_fill_glue();
IMPORT VOID	_ch2_mode2_chn_word_fill_glue();
IMPORT VOID	_ch2_mode2_chn_byte_move_glue();
IMPORT VOID	_ch2_mode2_chn_word_move_glue();

WRT_POINTERS mode2_handlers =
{
	_ch2_mode2_chn_byte_write_glue,
	_ch2_mode2_chn_word_write_glue,
	_ch2_mode2_chn_byte_fill_glue,
	_ch2_mode2_chn_word_fill_glue,
	_ch2_mode2_chn_byte_move_glue,
	_ch2_mode2_chn_byte_move_glue,
	_ch2_mode2_chn_word_move_glue,
	_ch2_mode2_chn_word_move_glue
};
#else
VOID	ega_mode1_chn_b_write();
VOID	ega_mode1_chn_w_write();
VOID	ega_mode1_chn_b_fill();
VOID	ega_mode1_chn_w_fill();
VOID	ega_mode1_chn_b_move();
VOID	ega_mode1_chn_w_move();

VOID	ega_mode2_chn_b_write();
VOID	ega_mode2_chn_w_write();
VOID	ega_mode2_chn_b_fill();
VOID	ega_mode2_chn_w_fill();
VOID	ega_mode2_chn_b_move();
VOID	ega_mode2_chn_w_move();

WRT_POINTERS mode1_handlers =
{
      ega_mode1_chn_b_write,
      ega_mode1_chn_w_write,
      ega_mode1_chn_b_fill,
      ega_mode1_chn_w_fill,
      ega_mode1_chn_b_move,
      ega_mode1_chn_b_move,
      ega_mode1_chn_w_move,
      ega_mode1_chn_w_move,
};
 
WRT_POINTERS mode2_handlers =
{
      ega_mode2_chn_b_write,
      ega_mode2_chn_w_write,
      ega_mode2_chn_b_fill,
      ega_mode2_chn_w_fill,
      ega_mode2_chn_b_move,
      ega_mode2_chn_b_move,
      ega_mode2_chn_w_move,
      ega_mode2_chn_w_move,
};
#endif /* A_VID */


GLOBAL VOID
copy_alternate_bytes IFN3(byte *, start, byte *, end, byte *, source)
{
	while (start <= end)
	{
		*start = *source;
		start += 4;       /* advance by longs, writing bytes */
		source += 4;
	}
}

GLOBAL VOID
fill_alternate_bytes IFN3(byte *, start, byte *, end, byte, value )
{
	while( start <= end )
	{
		*start = value;
		start += 4;	/* advance by longs, writing bytes */
	}
}

#ifdef  BIGEND
#define first_half(wd)      ((wd & 0xff00) >> 8)
#define sec_half(wd)        (wd & 0xff)
#else
#define first_half(wd)      (wd & 0xff)
#define sec_half(wd)        ((wd & 0xff00) >> 8)
#endif

GLOBAL VOID
fill_both_bytes IFN3(USHORT, data, USHORT *, dest, ULONG, len )
{
	USHORT swapped;

#ifdef BIGEND
	swapped = ((data & 0xff00) >> 8) | ((data & 0xff) << 8);
#endif

	if( (ULONG) dest & 1 )
	{
		*((UTINY *) dest) = first_half(data);

		dest = (USHORT *) ((ULONG) dest + 1);
		len--;

		while( len-- )
		{
			*dest = data;
			dest += 2;
		}

		*((UTINY *) dest) = sec_half(data);
	}
	else 
	{
		while( len-- )
		{
#ifdef BIGEND
			*dest = swapped;
#else
			*dest = data;
#endif
			dest += 2;
		}
	}
}


#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_EGA_CHN.seg"
#endif

VOID
ega_mode1_chn_b_write IFN2(ULONG, value, ULONG, offset )
{
	ULONG lsb;

	UNUSED(value);
	
	note_entrance0("ega_mode1_chn_b_write");

	lsb = offset & 1;
	offset = (offset >> 1) << 2;

	if( lsb )	/* odd address, in plane 1 or 3 */
	{
		if( VGLOBS->plane_enable & 2 )
			EGA_plane01[offset + 1] = get_latch1; 

		if( VGLOBS->plane_enable & 8 )
			EGA_plane23[offset + 1] = get_latch3; 
	}
	else		/* even address, in plane 0 or 2 */
	{
		if( VGLOBS->plane_enable & 1 )
			EGA_plane01[offset] = get_latch0; 

		if( VGLOBS->plane_enable & 4 )
			EGA_plane23[offset] = get_latch2; 
	}

	update_alg.mark_byte( offset );
}

VOID
ega_mode1_chn_w_write IFN2(ULONG, value, ULONG, offset )
{
	ULONG lsb;

	UNUSED(value);

	note_entrance0("ega_mode1_chn_w_write");

	lsb = offset & 1;
	offset = (offset >> 1) << 2;

	if( lsb )	/* odd address, low byte in planes 1 and 3 */
	{
		if( VGLOBS->plane_enable & 2 )
			EGA_plane01[offset + 1] = get_latch1; 

		if( VGLOBS->plane_enable & 1 )
			EGA_plane01[offset + 4] = get_latch0; 

		if( VGLOBS->plane_enable & 8 )
			EGA_plane23[offset + 1] = get_latch3; 

		if( VGLOBS->plane_enable & 4 )
			EGA_plane23[offset + 4] = get_latch2; 
	}
	else		/* even address, low byte in planes 0 and 2 */
	{
		if( VGLOBS->plane_enable & 1 )
			EGA_plane01[offset] = get_latch0; 

		if( VGLOBS->plane_enable & 2 )
			EGA_plane01[offset + 1] = get_latch1; 

		if( VGLOBS->plane_enable & 4 )
			EGA_plane23[offset] = get_latch2; 

		if( VGLOBS->plane_enable & 8 )
			EGA_plane23[offset + 1] = get_latch3; 
	}

	update_alg.mark_word( offset );
}

/* used by both byte and word mode1 fill */

LOCAL VOID
ega_mode1_chn_fill IFN2(ULONG, offset, ULONG, count )
{
	ULONG low_offset;			/* distance into regen buffer of start of write */
	ULONG high_offset;		/* distance into regen buffer of end of write */
	ULONG length;			/* length of fill in bytes */
	ULONG lsb;

	note_entrance0("ega_mode1_chn_fill");

	/*
	 *	Complicated by possibility that only one of a chained pair of
	 *	planes is write enabled, needing alternate bytes to be written.
	 */

	high_offset = offset + count - 1;
	lsb = high_offset & 1;
	high_offset = (high_offset >> 1) << 2;
	high_offset |= lsb;

	low_offset = offset;
	length = count;

	switch( VGLOBS->plane_enable & 3 )
	{
		case 1:	/* just plane 0, ie even addresses to be written */
			if( offset & 1 )
				low_offset++;

			low_offset = (low_offset >> 1) << 2;
			fill_alternate_bytes( &EGA_plane01[low_offset],
							&EGA_plane01[high_offset], get_latch0 );
			break;

		case 2:	/* just plane 1, ie odd addresses to be written */
			if(( offset & 1 ) == 0 )
				low_offset++;

			low_offset = (low_offset >> 1) << 2;
			fill_alternate_bytes( &EGA_plane01[low_offset],
							&EGA_plane01[high_offset], get_latch1 );
			break;

		case 3:	/* sensible case is to have both chained planes write enabled */
			lsb = low_offset & 1;
			low_offset = (low_offset >> 1) << 2;

			if( lsb )
			{
				EGA_plane01[low_offset + 1] = get_latch1;
				low_offset += 4;
				length--;
			}

			if( length & 1 )
			{
				length -= 1;
				EGA_plane01[low_offset + (length << 1)] = get_latch0;
			}

			fill_both_bytes( get_latch1 | get_latch0 << 8,
							(USHORT *)&EGA_plane01[low_offset], length >> 1 );
			break;
	}	/* end of switch on plane01 enabled */

	low_offset = offset;
	length = count;

	switch( VGLOBS->plane_enable & 0xc )	/* isolate 2 bits for planes2 and 3 */
	{
		case 4:	/* just plane 2, ie even addresses to be written */
			if( low_offset & 1 )
				low_offset++;

			low_offset = (low_offset >> 1) << 2;
			fill_alternate_bytes( &EGA_plane23[low_offset],
							&EGA_plane23[high_offset], get_latch2 );
			break;

		case 8:	/* just plane 3, ie odd addresses to be written */
			if(( low_offset & 1 ) == 0 )
				low_offset++;

			low_offset = (low_offset >> 1) << 2;
			fill_alternate_bytes( &EGA_plane23[low_offset],
							&EGA_plane23[high_offset], get_latch3 );
			break;

		case 12:	/* sensible case is to have both chained planes write enabled */
			lsb = low_offset & 1;
			low_offset = (low_offset >> 1) << 2;

			if( lsb )
			{
				EGA_plane23[low_offset + 1] = get_latch1;
				low_offset += 4;
				length--;
			}

			if( length & 1 )
			{
				length -= 1;
				EGA_plane23[low_offset + (length << 1)] = get_latch0;
			}

			fill_both_bytes( get_latch1 | get_latch0 << 8,
							(USHORT *)&EGA_plane23[low_offset], length >> 1 );
			break;
	}	/* end of switch on plane23 enabled */
}

VOID
ega_mode1_chn_b_fill IFN3(ULONG, value, ULONG, offset, ULONG, count )
{
  UNUSED(value);
  
  note_entrance0("ega_mode1_chn_b_fill");

  ega_mode1_chn_fill( offset, count );
  update_alg.mark_fill( offset, offset + count - 1 );
}

VOID
ega_mode1_chn_w_fill IFN3(ULONG, value, ULONG, offset, ULONG, count )
{
	UNUSED(value);
	
	note_entrance0("ega_mode1_chn_w_fill");

	ega_mode1_chn_fill( offset, count );
	update_alg.mark_fill( offset, offset + count - 1 );
}

LOCAL VOID
ega_mode1_chn_move_vid_src IFN5(ULONG, ead, ULONG, eas, ULONG, count,
	UTINY	*, EGA_plane, ULONG, plane )
{
	ULONG end, lsbd, lsbs, dst, src;

	lsbs = eas & 1;
	eas = (eas >> 1) << 2;	
	eas |= lsbs;

	end = ead + count - 1;
	lsbd = end & 1;
	end = (end >> 1) << 2;	
	end |= lsbd;

	lsbd = ead & 1;
	ead = (ead >> 1) << 2;	
	ead |= lsbd;

	if( lsbd != ( plane & 1 ))
	{
		dst = lsbd ? ead + 3 : ead + 1;
		src = lsbs ? eas + 3 : eas + 1;
	}
	else
	{
		dst = ead;
		src = eas;
	}

	copy_alternate_bytes( &EGA_plane[dst], &EGA_plane[end], &EGA_plane[src] );
}

GLOBAL VOID
ega_mode1_chn_b_move IFN4(ULONG, ead, ULONG, eas, ULONG, count, ULONG, src_flag )
{
	note_entrance0("ega_mode1_chn_b_move");

	if( src_flag )
	{
		if( getDF() )
		{
			eas -= count - 1;
			ead -= count - 1;
		}

		if( VGLOBS->plane_enable & 1 )
			ega_mode1_chn_move_vid_src( ead, eas, count, EGA_plane01, 0 );

		if( VGLOBS->plane_enable & 2 )
			ega_mode1_chn_move_vid_src( ead, eas, count, EGA_plane01, 1 );

		if( VGLOBS->plane_enable & 4 )
			ega_mode1_chn_move_vid_src( ead, eas, count, EGA_plane23, 2 );

		if( VGLOBS->plane_enable & 8 )
			ega_mode1_chn_move_vid_src( ead, eas, count, EGA_plane23, 3 );
	}
	else	/* source is not in ega memory, it becomes a fill */
	{
		if( getDF() )
			ead -= count - 1;

		ega_mode1_chn_fill( ead, count );
	}

	update_alg.mark_string( ead, ead + count - 1 );
}

VOID
ega_mode1_chn_w_move IFN4(ULONG, ead, ULONG, eas, ULONG, count, ULONG, src_flag)
{
	note_entrance0("ega_mode1_chn_w_move");

	count <<= 1;

	if( src_flag )
	{
		if( getDF() )
		{
			eas -= count - 2;
			ead -= count - 2;
		}

		if( VGLOBS->plane_enable & 1 )
			ega_mode1_chn_move_vid_src( ead, eas, count, EGA_plane01, 0 );

		if( VGLOBS->plane_enable & 2 )
			ega_mode1_chn_move_vid_src( ead, eas, count, EGA_plane01, 1 );

		if( VGLOBS->plane_enable & 4 )
			ega_mode1_chn_move_vid_src( ead, eas, count, EGA_plane23, 2 );

		if( VGLOBS->plane_enable & 8 )
			ega_mode1_chn_move_vid_src( ead, eas, count, EGA_plane23, 3 );
	}
	else	/* source is not in ega memory, it becomes a fill */
	{
		if( getDF() )
			ead -= count - 2;

		ega_mode1_chn_fill( ead, count );
	}

	update_alg.mark_string( ead, ead + count - 1 );
}

VOID
ega_mode2_chn_b_write IFN2(ULONG, value, ULONG, offset )
{
	ULONG	value1;
	ULONG lsb;

	note_entrance0("ega_mode2_chn_b_write");

	lsb = offset & 1;
	offset = (offset >> 1) << 2;

	if( EGA_CPU.fun_or_protection )
	{
		if( lsb )	/* odd address, applies to planes 1 and 3 */
		{
			if( VGLOBS->plane_enable & 2 )
			{
				value1 = value & 2 ? 0xff : 0;
				EGA_plane01[offset + 1] = do_logicals( value1, get_latch1 );
			}

			if( VGLOBS->plane_enable & 8 )
			{
				value1 = value & 8 ? 0xff : 0;
				EGA_plane23[offset + 1] = do_logicals( value1, get_latch3 );
			}
		}
		else		/* even address, applies to planes 0 and 2 */
		{
			if( VGLOBS->plane_enable & 1 )
			{
				value1 = value & 1 ? 0xff : 0;
				EGA_plane01[offset] = do_logicals( value1, get_latch0 );
			}

			if( VGLOBS->plane_enable & 4 )
			{
				value1 = value & 4 ? 0xff : 0;
				EGA_plane23[offset] = do_logicals( value1, get_latch2 );
			}
		}
	}
	else	/* no difficult function or protection stuff */
	{
		if( lsb )	/* odd address, applies to planes 1 and 3 */
		{
			if( VGLOBS->plane_enable & 2 )
				EGA_plane01[offset + 1] = value & 2 ? 0xff : 0;

			if( VGLOBS->plane_enable & 8 )
				EGA_plane23[offset + 1] = value & 8 ? 0xff : 0;
		}
		else		/* even address, applies to planes 0 and 2 */
		{
			if( VGLOBS->plane_enable & 1 )
				EGA_plane01[offset] = value & 1 ? 0xff : 0;

			if( VGLOBS->plane_enable & 8 )
				EGA_plane23[offset] = value & 4 ? 0xff : 0;
		}
	}

	update_alg.mark_byte( offset );
}

VOID
ega_mode2_chn_w_write IFN2(ULONG, value, ULONG, offset )
{
	ULONG value2;
	ULONG lsb;
	ULONG low, high;

	low = value & 0xff;
	high = value >> 8;

	note_entrance0("ega_mode2_chn_w_write");

	lsb = offset & 1;
	offset = (offset >> 1) << 2;

	if( EGA_CPU.fun_or_protection )
	{
		if( lsb )	/* odd address, low byte in planes 1 and 3 */
		{
			if( VGLOBS->plane_enable & 2 )
			{
				value2 = low & 2 ? 0xff : 0;
				EGA_plane01[offset + 1] = do_logicals( value2, get_latch1 );
			}

			if( VGLOBS->plane_enable & 1 )
			{
				value2 = high & 1 ? 0xff : 0;
				EGA_plane01[offset + 4] = do_logicals( value2, get_latch0 );
			}

			if( VGLOBS->plane_enable & 8 )
			{
				value2 = low & 8 ? 0xff : 0;
				EGA_plane23[offset + 1] = do_logicals( value2, get_latch3 );
			}

			if( VGLOBS->plane_enable & 4 )
			{
				value2 = high & 4 ? 0xff : 0;
				EGA_plane23[offset + 4] = do_logicals( value2, get_latch2 );
			}
		}
		else		/* even address, low byte in planes 0 and 2 */
		{
			if( VGLOBS->plane_enable & 1 )
			{
				value2 = low & 1 ? 0xff : 0;
				EGA_plane01[offset] = do_logicals( value2, get_latch0 );
			}

			if( VGLOBS->plane_enable & 2 )
			{
				value2 = high & 2 ? 0xff : 0;
				EGA_plane01[offset + 1] = do_logicals( value2, get_latch1 );
			}

			if( VGLOBS->plane_enable & 4 )
			{
				value2 = low & 4 ? 0xff : 0;
				EGA_plane23[offset] = do_logicals( value2, get_latch2 );
			}

			if( VGLOBS->plane_enable & 8 )
			{
				value2 = high & 8 ? 0xff : 0;
				EGA_plane23[offset + 1] = do_logicals( value2, get_latch3 );
			}
		}
	}
	else	/* easy no function or bit prot case */
	{
		if( lsb )	/* odd address, low byte in planes 1 and 3 */
		{
			if( VGLOBS->plane_enable & 2 )
				EGA_plane01[offset + 1] = low & 2 ? 0xff : 0;

			if( VGLOBS->plane_enable & 1 )
				EGA_plane01[offset + 4] = high & 1 ? 0xff : 0;

			if( VGLOBS->plane_enable & 8 )
				EGA_plane23[offset + 1] = low & 8 ? 0xff : 0;

			if( VGLOBS->plane_enable & 4 )
				EGA_plane23[offset + 4] = high & 4 ? 0xff : 0;
		}
		else		/* even address, low byte in planes 0 and 2 */
		{
			if( VGLOBS->plane_enable & 1 )
				EGA_plane01[offset] = low & 1 ? 0xff : 0;

			if( VGLOBS->plane_enable & 2 )
				EGA_plane01[offset + 1] = high & 2 ? 0xff : 0;

			if( VGLOBS->plane_enable & 4 )
				EGA_plane23[offset] = low & 4 ? 0xff : 0;

			if( VGLOBS->plane_enable & 8 )
				EGA_plane23[offset + 1] = high & 8 ? 0xff : 0;
		}
	}

	update_alg.mark_word( offset );
}

VOID
ega_mode2_chn_b_fill IFN3(ULONG, value, ULONG, offset, ULONG, count )
{
	ULONG low_offset;		/* distance into regen buffer of write start and end */
	ULONG high_offset;	/* distance into regen buffer of write start and end */
	ULONG new_value;

	note_entrance0("ega_mode2_chn_b_fill");

	/*
	 *	Complicated by possibility that only one of a chained pair of
	 *	planes is write enabled, needing alternate bytes to be written.
	 */ 

	/* starting on odd address makes it difficult, go to next one */

	if(( (ULONG) offset & 1 ) && count )
	{
		ega_mode2_chn_b_write(value, offset++ );
		count--;
	}

	/* ending on even address makes it difficult, retreat to previous one */

	if(( (ULONG)( offset + count - 1 ) & 1 ) == 0 && count )
	{
		ega_mode2_chn_b_write(value, offset + count - 1 );
		count--;
	}

	low_offset = (offset >> 1) << 2;				/* start of write */
	high_offset = ((offset + count - 1) >> 1) << 2;		/* end of write */

	switch( VGLOBS->plane_enable & 3 )
	{
		case 1:	/* just plane 0, ie even addresses to be written */
			value = value & 1 ? 0xff : 0;

			if( EGA_CPU.fun_or_protection )
				value = do_logicals( value, get_latch0 );

			fill_alternate_bytes( &EGA_plane01[low_offset],
									&EGA_plane01[high_offset], value );
			break;

		case 2:	/* just plane 1, ie odd addresses to be written */
			value = value & 2 ? 0xff : 0;

			if( EGA_CPU.fun_or_protection )
				value = do_logicals( value, get_latch1 );

			fill_alternate_bytes( &EGA_plane01[low_offset + 1],
									&EGA_plane01[high_offset], value );
			break;

		case 3:	/* sensible case is to have both chained planes write enabled */
			new_value = ( value & 1 ? 0xff : 0) | (value & 2 ? 0xff00: 0);

			if( EGA_CPU.fun_or_protection )
				new_value = do_logicals( new_value, get_latch01);

			fill_both_bytes( new_value, (USHORT *)&EGA_plane01[low_offset], count >> 1 );
			break;

	}	/* end of switch on plane01 enabled */

	switch( VGLOBS->plane_enable & 0xc )	/* isolate 2 bits for planes2 and 3 */
	{
		case 4:	/* just plane 2, ie even addresses to be written */
			value = value & 4 ? 0xff : 0;

			if( EGA_CPU.fun_or_protection )
				value = do_logicals( value, get_latch2 );

			fill_alternate_bytes( &EGA_plane23[low_offset],
								&EGA_plane23[high_offset], value );
			break;

		case 8:	/* just plane 3, ie odd addresses to be written */
			value = value & 8 ? 0xff : 0;

			if( EGA_CPU.fun_or_protection )
				value = do_logicals( value, get_latch3 );

			fill_alternate_bytes( &EGA_plane23[low_offset + 1],
								&EGA_plane23[high_offset], value );
			break;

		case 12:	/* sensible case is to have both chained planes write enabled */
			new_value = ( value & 4 ? 0xff : 0) | (value & 8 ? 0xff00: 0);

			if( EGA_CPU.fun_or_protection )
				new_value = do_logicals( new_value, get_latch23);

			fill_both_bytes( new_value, (USHORT *)&EGA_plane23[low_offset], count >> 1 );
			break;
	}	/* end of switch on plane23 enabled */

	update_alg.mark_fill( offset, offset + count - 1 );
}

VOID
ega_mode2_chn_w_fill IFN3(ULONG, value, ULONG, offset, ULONG, count )
{
	ULONG	low_offset;		/* distance into regen buffer of write start and end */
	ULONG	high_offset;	/* distance into regen buffer of write start and end */
	ULONG	value1;

	note_entrance0("ega_mode2_chn_w_fill");

	/*
	 *	Complicated by possibility that only one of a chained pair of
	 *	planes is write enabled, needing alternate bytes to be written.
	 */ 

	/* starting on odd address makes it difficult, go to next one */

	if(( (ULONG) offset & 1 ) && count )
	{
		ega_mode2_chn_b_write( value, offset++);
		count--;

		if( count )
		{
			ega_mode2_chn_b_write( value >> 8, offset + count - 1 );
			count--;
		}

		value = ( value << 8 ) | ( value >> 8 );
	}

	low_offset = (offset >> 1) << 2;				/* start of write */
	high_offset = ((offset + count - 1) >> 1) << 2;		/* end of write */

	switch( VGLOBS->plane_enable & 3 )
	{
		case 1:	/* just plane 0, ie even addresses to be written */
			value1 = value & 1 ? 0xff : 0;

			if( EGA_CPU.fun_or_protection )
				value1 = do_logicals( value1, get_latch0 );

			fill_alternate_bytes( &EGA_plane01[low_offset],
									&EGA_plane01[high_offset], value1 );
			break;

		case 2:	/* just plane 1, ie odd addresses to be written */
			value1 = ( value >> 8 ) & 2 ? 0xff : 0;

			if( EGA_CPU.fun_or_protection )
				value1 = do_logicals( value1, get_latch1 );

			fill_alternate_bytes( &EGA_plane01[low_offset + 1],
									&EGA_plane01[high_offset], value1 );
			break;

		case 3:	/* sensible case is to have both chained planes write enabled */
			/* get a word pattern for filling */
			value1 = ( value & 1 ? 0xff : 0 ) | (( value >> 8 ) & 2 ? 0xff00: 0 );

			if( EGA_CPU.fun_or_protection )
				value1 = do_logicals( value1, get_latch01 );

			fill_both_bytes( value1, (USHORT *)&EGA_plane01[low_offset], count >> 1 );
			break;
	}	/* end of switch on plane01 enabled */

	switch( VGLOBS->plane_enable & 0xc )	/* isolate 2 bits for planes 2 and 3 */
	{
		case 4:	/* just plane 2, ie even addresses to be written */
			value1 = value & 4 ? 0xff : 0;

			if( EGA_CPU.fun_or_protection )
				value1 = do_logicals( value1, get_latch2 );

			fill_alternate_bytes( &EGA_plane23[low_offset],
									&EGA_plane23[high_offset], value1 );
			break;

		case 8:	/* just plane 3, ie odd addresses to be written */
			value1 = ( value >> 8 ) & 8 ? 0xff : 0;

			if( EGA_CPU.fun_or_protection )
				value1 = do_logicals( value1, get_latch3 );

			fill_alternate_bytes( &EGA_plane23[low_offset + 1],
									&EGA_plane23[high_offset], value1 );
			break;

		case 12:	/* sensible case is to have both chained planes write enabled */
			/* get a word pattern for filling */
			value1 = ( value & 4 ? 0xff : 0 ) | (( value >> 8 ) & 8 ? 0xff00: 0 );

			if( EGA_CPU.fun_or_protection )
				value1 = do_logicals( value1, get_latch23);

			fill_both_bytes( value1, (USHORT *)&EGA_plane23[low_offset], count >> 1 );
			break;
	}	/* end of switch on plane23 enabled */

	/* the 3rd parameter is needed by GORE. */
	update_alg.mark_wfill( offset, offset + count - 1, 0 );
}

LOCAL VOID
ega_mode2_chn_move_guts IFN8(UTINY *, eas, UTINY *, ead, LONG, count,
	UTINY *, EGA_plane, ULONG, scratch, ULONG, plane, ULONG, w,
	ULONG, src_flag )
{
	ULONG src, dst;
	UTINY *source;
	USHORT value;
	ULONG lsb;

	src = (ULONG) eas;

	dst = (ULONG) ead;

	/*
	 *	even planes cannot start with odd addresses
	 *	odd planes cannot start with even addresses
	 */

	if(( dst & 1 ) != ( plane & 1 ))	
	{
#ifdef BACK_M
		src--;
		scratch--;
#else
		src++;
		scratch++;
#endif
		dst++;
		count--;
	}

	lsb = dst & 1;
	dst = (dst >> 1) << 2;
	dst |= lsb;

	if( src_flag )
	{
		lsb = src & 1;
		src = (src >> 1) << 2;
		src |= lsb;

		if( plane & 1 )
		{

		/*
		 *	This causes latches to be read from 2 bytes above the word
		 *	that was read if it was on an odd address, ie it only applies
		 *	to planes 1 and 3 in chained mode word operations.
		 */

			source = w ? &EGA_plane[src] + 2 : &EGA_plane[src];
		}
		else
		{
			source = &EGA_plane[src];
		}

		src = scratch;
	}

	if( EGA_CPU.fun_or_protection )
	{
		while( count > 0 )
		{
			count -= 2;

			value = *(UTINY *) src & (1 << plane) ? 0xff : 0;
#ifdef BACK_M
			src -= 2;
#else
			src += 2;
#endif

			if( src_flag )
			{
				put_latch( plane, *source );
				source += 4;
			}

			EGA_plane[dst] = do_logicals( value, get_latch(plane) );
			dst += 4;
		}
	}
	else
	{
		while( count > 0 )
		{
			count -= 2;

			EGA_plane[dst] = *(UTINY *) src & (1 << plane) ? 0xff : 0;
#ifdef BACK_M
			src -= 2;
#else
			src += 2;
#endif
			dst += 4;
		}
	}
}

/*
 * Used by ega_mode2_chn_b_move with w == 0 and by
 * ega_mode2_gen_w_move with w == 1
 */

VOID
ega_mode2_chn_move IFN5(UTINY, w, UTINY *, ead, UTINY *, eas, ULONG, count,
	ULONG, src_flag )
{
	UTINY *scr;

	IMPORT VOID (*string_read_ptr)();

	count <<= w;

	if( src_flag )
	{
		/*
		 *	Source is in EGA, latches will change with each byte moved. We
		 *	restore CPU's view of source in regen, and use it to update planes
		 *	with the aid of the SAS scratch area.
		 */

#ifdef BACK_M
		scr = VGLOBS->scratch + 0x10000 - 1;
#else
		scr = VGLOBS->scratch;
#endif

		if( getDF() )
		{
			eas = eas - count + 1 + w;
			ead = ead - count + 1 + w;
		}

		(*string_read_ptr)( scr, eas, count );
	}
	else
	{
		if( getDF() )
		{
#ifdef BACK_M
			eas = eas + count - 1 - w;
#else
			eas = eas - count + 1 + w;
#endif
			ead = ead - count + 1 + w;
		}
	}

	if( VGLOBS->plane_enable & 1 )		/* plane 0, even addresses, enabled */
		ega_mode2_chn_move_guts( eas, ead, count, EGA_plane01, (ULONG) scr, 0, w, src_flag );

	if( VGLOBS->plane_enable & 2 )		/* plane 1, odd addresses, enabled */
		ega_mode2_chn_move_guts( eas, ead, count, EGA_plane01, (ULONG) scr, 1, w, src_flag );

	if( VGLOBS->plane_enable & 4 )		/* plane 2, even addresses, enabled */
		ega_mode2_chn_move_guts( eas, ead, count, EGA_plane23, (ULONG) scr, 2, w, src_flag );

	if( VGLOBS->plane_enable & 8 )		/* plane 3, odd addresses, enabled */
		ega_mode2_chn_move_guts( eas, ead, count, EGA_plane23, (ULONG) scr, 3, w, src_flag );

	update_alg.mark_string( (int) ead, (int) ead + count - 1 );
}

VOID
ega_mode2_chn_b_move IFN4(UTINY *, ead, UTINY *, eas, ULONG, count,
	ULONG, src_flag)
{
  note_entrance0("ega_mode2_chn_b_move");

  /* general function, 0 means byte write */

  ega_mode2_chn_move(0, ead, eas, count, src_flag);
}

VOID
ega_mode2_chn_w_move IFN4(UTINY *, ead, UTINY *, eas, ULONG, count,
	ULONG, src_flag)
{
  note_entrance0("ega_mode2_chn_w_move");

  /* general function, 1 means word write */

  ega_mode2_chn_move(1, ead, eas, count, src_flag);
}

#endif

#endif	/* !NTVDM | (NTVDM & !X86GFX) */
