#include	"miniblt.h"

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

//----------------------------------------------------------------------------------------
// Prepare blt function
//----------------------------------------------------------------------------------------
Int32	blt_init(BLT_PARAM* p, BLT_MEM* bfn, Int32 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, Int32 src_x1, Int32 src_y1, Int32 dummy_x, Int32 dummy_y, Int32 dst_x1, Int32 dst_y1, Int32 dst_x2, Int32 dst_y2)
{
	UChar* src;
	UChar* dst;
	Int32	 src_offset;
	Int32	 dst_offset;
	Int32	 mid_cnt;
	Int32	 dy;
	UInt32 start_mask;
	UInt32 end_mask;
	Int32	 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, (UInt32*) src, src_offset, (UInt32*) 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, (UInt32*) p->src_start_addr, p->src_offset, (UInt32*) 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, UInt32* src, Int32 src_offset, UInt32* dst, Int32 dst_offset,
										 Int32 dy, Int32 mid_cnt, UInt32 sm, UInt32 em)
{
	Int32	 i;
	UInt32 src_word;

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

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

		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 = (UInt32*) ((UChar*) src + src_offset);
		dst = (UInt32*) ((UChar*) dst + dst_offset);

		dy--;
	}
}
