#include	"miniblt.h"

//----------------------------------------------------------------------------------------
// private functions
//----------------------------------------------------------------------------------------
static void	bitblt_line( BLT_PARAM *p );
static void	blt_copy( BLT_PARAM *p, ULONG *src, LONG src_offset, ULONG *dst, LONG dst_offset,
										  LONG dy, LONG mid_cnt, ULONG sm, ULONG em );

//----------------------------------------------------------------------------------------
// Prepare blt function
//----------------------------------------------------------------------------------------
LONG	blt_init( BLT_PARAM *p, BLT_MEM *bfn, LONG mode )
{
	(void)	bfn;

	p->wr_mode = mode;
	return( 1 );
}

//----------------------------------------------------------------------------------------
// Reset blt function, free all previously allocated memory
//----------------------------------------------------------------------------------------
void	blt_reset( BLT_PARAM *p, BLT_MEM *bfn )
{
	(void)	p;
	(void)	bfn;
}

//----------------------------------------------------------------------------------------
// Copy one line and go to the next destination line
//----------------------------------------------------------------------------------------
void	bltline_incdst( BLT_PARAM *p, BLT_LINE *fn )
{
	fn( p );

	p->dst_start_addr = (void *) ((UCHAR *) p->dst_start_addr + p->dst.width );
}

//----------------------------------------------------------------------------------------
// Simplified bit block transfer
// Function result:		Function pointer (T_PREPARELINE set) or zero
// p:									blt context
// src_x1:						x1 source
// src_y1:						y1 source
// dummy_x:						dummy
// dummy_y:						dummy
// dst_x1:						x1 destination
// dst_y1:						y1 destination
// dst_x2:						x2 destination
// dst_y2:						y2 destination
//----------------------------------------------------------------------------------------
BLT_LINE	*bitblt( BLT_PARAM *p, LONG src_x1, LONG src_y1, LONG dummy_x, LONG dummy_y, LONG dst_x1, LONG dst_y1, LONG dst_x2, LONG dst_y2 )
{
	UCHAR	*src;
	UCHAR	*dst;
	LONG	src_offset;
	LONG	dst_offset;
	LONG	mid_cnt;
	LONG	dy;
	ULONG	start_mask;
	ULONG	end_mask;
	LONG	shifts;

	src_x1 -= p->src.xmin;																		// take coordinate offset of the source into account
	src_y1 -= p->src.ymin;

	dst_x1 -= p->dst.xmin;																		// take coordinate offset of the destination into account
	dst_y1 -= p->dst.ymin;
 	dst_x2 -= p->dst.xmin;
	dst_y2 -= p->dst.ymin;

	dy = dst_y2 - dst_y1;

	src_x1 *= get_PX_BITS( p->src.px_format );
	dst_x1 *= get_PX_BITS( p->dst.px_format );
	dst_x2 *= get_PX_BITS( p->dst.px_format );
	dst_x2 += get_PX_BITS( p->dst.px_format ) - 1;

	start_mask = 0xffffffffUL >> ( dst_x1 & 31 );							// start mask
	end_mask = 0xffffffffUL << ( ~dst_x2 & 31 );							// end mask

#if GE_LITTLE_ENDIAN																						// swap the masks on little endian machines
	start_mask = REVERSEu32( start_mask );
	end_mask = REVERSEu32( end_mask );
#endif

	src = (UCHAR *) p->src.addr;
	dst = (UCHAR *) p->dst.addr;
	src_offset = p->src.width;
	dst_offset = p->dst.width;
	
	shifts = ( dst_x1 & 31 ) - ( src_x1 & 31 );								// # of shifts to the right [blt_copy() does not support bitwise shifting]

	dst_x1 >>= 5;
	dst_x2 >>= 5;

	dst += dst_y1 * p->dst.width;
	dst += dst_x1 << 2;
	
	mid_cnt = dst_x2 - dst_x1;																// # of destination words - 1

	src_x1 >>= 5;
 	src += src_y1 * p->src.width;
	src += src_x1 << 2;

	src_offset -= mid_cnt << 2;
	dst_offset -= mid_cnt << 2;

	if ( mid_cnt == 0 )
	{
		start_mask &= end_mask;																	// combine masks
		end_mask = 0;
		src_offset -= 4;																				// adjust offsets
		dst_offset -= 4;
	}

	mid_cnt -= 2;

	if (( shifts & 7 ) == 0 )																	// use mis-alignment instead of shifting?
	{
		src -= shifts >> 3;
		shifts = 0;
	}
	else																											// this case is not supported
		return( 0 );

	if ( p->wr_mode & T_PREPARELINE )													// prepare output for single lines?
	{
		p->src_start_addr = src;																// save internal params
		p->dst_start_addr = dst;
		p->src_offset = src_offset;
		p->dst_offset = dst_offset;
		p->mid_cnt = mid_cnt;
		p->start_mask = start_mask;
		p->end_mask = end_mask;
	
		return((BLT_LINE *) bitblt_line );
	}
	else
	{
		blt_copy( p, (ULONG *) src, src_offset, (ULONG *) dst, dst_offset,dy, mid_cnt, start_mask, end_mask );
		return( 0 );
	}
}

//----------------------------------------------------------------------------------------
// Copy one line
//----------------------------------------------------------------------------------------
static void	bitblt_line( BLT_PARAM *p )
{
	blt_copy( p, (ULONG *) p->src_start_addr, p->src_offset, (ULONG *) p->dst_start_addr, p->dst_offset,
						0, p->mid_cnt, p->start_mask, p->end_mask );
}

//----------------------------------------------------------------------------------------
// Copy Bitmap (post increment)
// Function result:		Function pointer (T_PREPARELINE set) or zero
// p:									blt context
// src:								pointer to the first source word
// src_offset:				distance to the next source line in bytes
// src:								pointer to the first destination word
// src_offset:				distance to the next destination line in bytes
// dy:								# of lines - 1
// mid_cnt:						# of main loops - 1
// start_mask:				mask for the start word (bits set will be copied)
// start_mask:				mask for the end word (bits set will be copied)
//----------------------------------------------------------------------------------------
static void	blt_copy( BLT_PARAM *p, ULONG *src, LONG src_offset, ULONG *dst, LONG dst_offset,
										  LONG dy, LONG mid_cnt, ULONG sm, ULONG em )
{
	LONG i;
	ULONG	src_word;

	while ( dy >= 0 )
	{
		src_word = *src++;

		// intel bug
		//*dst++ = ( (~src_word) & sm ) ^ ( (*dst) | sm );
		*dst++ = ( src_word & sm ) | ( *dst & ( ~sm ));

		for ( i = mid_cnt; i >= 0; i-- )
			*dst++ = *src++;

		if ( mid_cnt > -2 )
		{
			src_word = *src;

			// intel bug
			//*dst = ( ~src_word & em ) ^ ( *dst | em );
			*dst = ( src_word & em ) | ( *dst & ( ~em ));
		}

		src = (ULONG *) ((UCHAR *) src + src_offset );
		dst = (ULONG *) ((UCHAR *) dst + dst_offset );

		dy--;
	}
}
