/*
 * BLITTER.c
 *
 * PROGRAMMER : Gregg A. Tavares 
 *    VERSION : 00.002
 *    CREATED : 08/22/89 
 *   MODIFIED : 10/25/89
 *
 * DESCRIPTION
 *   various BLITTER routines for manipulating graphic images.
 *
 * NOTES
 *   Set ClipLeft, ClipTop, ClipWidth and ClipHeight to the
 *   maximum area you want effected in your destination BitMap.
 *
 *   This code was written with Manx C68k 3.6a with the +l option
 *   (32 bit ints) I don't know what if anything you'll have to
 *   change to use it with out that option or with another compiler.
 *
 *   You must also add -L100 to compile with Manx.
 *
 * COPYWRONG
 *   I hereby place this code IN THE PUBLIC DOMAIN.  Use it, Sell it,
 *   burn it, frame it, worship it, whatever makes your pants smoke :-)
 *
 * DISCLAIMER
 *   If this code causes you or any one you know any problems don't
 *   come crying to me! ;-)
 *
 * HISTORY
 *    9/6/89 Wednesday
 *      Made CopyBitMap work non-distructively when copying on the same
 *      BitMap.
 *
*/

short	ClipLeft	= 0;
short	ClipTop		= 0;	
short	ClipHeight	= 320;
short	ClipWidth	= 200;

#include <exec/types.h>
#include <hardware/blit.h>
#include <hardware/dmabits.h>
#include <hardware/custom.h>
#include <graphics/gfx.h>

/* remove the following macros if they are standard in your 'C' */

#ifndef OKAY
#define OKAY		1
#endif

#ifndef NULL
#define NULL		0L
#endif

#ifndef FALSE
#define FALSE		0L
#endif

#ifndef TRUE
#define TRUE		(!FALSE)
#endif

#ifndef abs
#define	abs(value)	(((value) < 0) ? -(value) : (value))
#endif

#ifndef min
#define	min(a,b)	(((a) < (b)) ? (a) : (b))
#endif

#ifndef max
#define	max(a,b)	(((a) > (b)) ? (a) : (b))
#endif

#ifndef sign
#define sign(value)	(((value) > 0) - ((value) < 0))
#endif

#define WAITFORBLITTER	while(custom.dmaconr & DMAF_BLTDONE);

/* 
#define DEBUG 1
*/

#if DEBUG
typedef struct {
	UWORD bltddat;
	UWORD bltcon0;
	UWORD bltcon1;
	UWORD bltafwm;
	UWORD bltalwm;
	APTR bltcpt;
	APTR bltbpt;
	APTR bltapt;
	APTR bltdpt;
	UWORD bltsize;
	UWORD bltcmod;
	UWORD bltbmod;
	UWORD bltamod;
	UWORD bltdmod;
	UWORD bltcdat;
	UWORD bltbdat;
	UWORD bltadat;
} BLITTER;
#else
typedef struct Custom BLITTER;
#endif

#if DEBUG
void dorealblit (hardware)
	BLITTER *hardware;
{
	OwnBlitter ();

	WAITFORBLITTER

	custom.bltddat = hardware->bltddat;
	custom.bltcon0 = hardware->bltcon0;
	custom.bltcon1 = hardware->bltcon1;
	custom.bltafwm = hardware->bltafwm;
	custom.bltalwm = hardware->bltalwm;
	custom.bltcpt  = hardware->bltcpt;
	custom.bltbpt  = hardware->bltbpt;
	custom.bltapt  = hardware->bltapt;
	custom.bltdpt  = hardware->bltdpt;
	custom.bltcmod = hardware->bltcmod;
	custom.bltbmod = hardware->bltbmod;
	custom.bltamod = hardware->bltamod;
	custom.bltdmod = hardware->bltdmod;
	custom.bltcdat = hardware->bltcdat;
	custom.bltbdat = hardware->bltbdat;
	custom.bltadat = hardware->bltadat;
	custom.bltsize = hardware->bltsize;

	DisownBlitter ();
}
#endif

#if 0
	if (!mask && !tsbit && !tebit && !fsbit && !febit) {
		/*
		 * Use Special Quick Blit.
		 */
	} else if (!mask) {
	} else {
#endif
/**************************************************************************
 *
 * CopyMaskBitMap
 *
 * SYNOPSIS
 *    void CopyMaskBitMap (
 *                struct BitMap *frombm,
 *                int            fromleft,  
 *                int            fromtop,
 *                struct BitMap *tobm,
 *                int            toleft,
 *                int            totop,
 *                int            width,
 *                int            height,
 *                UBYTE		 planemask,
 *		  UBYTE		*mask
 *                );
 *
 * FUNCTION
 *    Use the blitter to copy from one bitmap to another using a mask
 *    for transparency.  As far as I can tell if you are blitting from
 *    a one word source into a two word destination (example: pixels
 *    10<->13 to 14<->17) you must use two blits (yech!) in the generic
 *    case.  If are always blitting entire shape clipped to word boundries
 *    then use ShapeBlit().
 *
 * INPUTS
 *    frombm    = pointer to bitmap to copy from
 *    fromleft  = x position to start copying from
 *    fromtop   = y position to start copying from
 *    tobm      = pointer to bitmap to copy to
 *    toleft    = x position to start copying to
 *    totop     = y position to start copying to
 *    width     = width in pixels of area to copy
 *    height    = height in pixels of area to copy
 *    planemask = bitmask of planes to effect, bit0 = plane 0, bit1 = plane1...
 *    mask      = optional pointer to a mask bitplane of same size as a
 *                bitplane in frombm->Planes[0].
 *
 * BUGS
 *   NOTE: If you copy from LEFT to RIGHT or TOP to BOTTOM from a bitmap
 *         to the SAME bitmap and the areas overlap the area will be trashed
 *         (or smeared)
 *
 * HISTORY
 *
 *
 * SEE ALSO
 *   ShapeBlit (), CopyBitMap ()
*/

void CopyMaskBitMap (
	frombm,
	fromleft,
	fromtop,
	tobm,
	toleft,
	totop,
	inwidth,
	inheight,
	planemask,
	mask
)
	struct BitMap	*frombm;
	int		 fromleft;
	int		 fromtop;
	struct BitMap	*tobm;
	int		 toleft;
	int		 totop;
	int		 inwidth;
	int		 inheight;
	UBYTE		 planemask;
	UBYTE		*mask;
{
	register ULONG		 fromoffset;
	register ULONG		 tooffset;
		 BLITTER	 h;
	register BLITTER	*hardware = 
#if DEBUG
						&h;
#else
						(struct Custom *)0x0DFF000;
#endif
	UWORD		fsword;
	UWORD		tsword;
	UWORD		newwidth;
	UWORD		fend;
	UWORD		tend;
	UWORD		fsbit;
	UWORD		tsbit;
	UWORD		febit;
	UWORD		tebit;
	UWORD		frommod;
	UWORD		tomod;
	UWORD		shift;
	UWORD		bltshift;
	UWORD		fbwidth;
	UWORD		tbwidth;
	UWORD		bltwidth;
	ULONG		toptr;
	UWORD		size;
	UWORD		firstmask;
	UWORD		lastmask;
	UWORD		othermask;
	UWORD		ndx;
	UWORD		depth;
	UWORD		more;
	UBYTE		tempmask;

/* --------------------------------------------------------------------- */
	int width  = inwidth; 
	int height = inheight;

	if (toleft < ClipLeft) {
		width	  = inwidth - (ClipLeft - toleft);
		toleft	  = ClipLeft;
		fromleft += inwidth - width;
	} else if (toleft > ClipLeft + ClipWidth - inwidth) {
		width	  = (ClipLeft + ClipWidth) - toleft;
	}

	if (totop < ClipTop) {
		height	 = inheight - (ClipTop - totop);
		totop	 = ClipTop;
		fromtop += inheight - height;
	} else  if (totop > ClipTop + ClipHeight - inheight) {
		height	 = (ClipTop + ClipHeight) - totop;
	}

	if (width <= 0 || height <= 0) {
		return;
	}
/* --------------------------------------------------------------------- */

#ifndef DEBUG
	OwnBlitter ();
#endif
	more      = FALSE;
	othermask = 0xFFFF;

	tempmask = planemask;

	depth = min (frombm->Depth, tobm->Depth);

	fsword = fromleft >> 4;
	tsword = toleft   >> 4;

	newwidth = width - 1;
	fend   = fromleft + newwidth;
	tend   = toleft   + newwidth;

	fromoffset = fromtop * frombm->BytesPerRow + (fsword << 1);
	tooffset   = totop   * tobm->BytesPerRow   + (tsword << 1);

	fsbit	= fromleft & 0x0F;
	tsbit	= toleft   & 0x0F;

	febit	= fend & 0x0F;
	tebit	= tend & 0x0F;

	fbwidth = (fend >> 4) - fsword + 1;
	tbwidth = (tend >> 4) - tsword + 1;

	bltwidth  = fbwidth;
	shift = (tsbit - fsbit);
	if (shift > 15) {
		bltwidth += 1;
		shift    &= 0x0F;
		tooffset -= 2;
		othermask = 0x0000;
	}

	firstmask = 0xFFFF >> fsbit;

	if (tbwidth > fbwidth) {
		more = TRUE;
		lastmask = 0xFFFF << shift;
	} else {
		lastmask  = 0xFFFF << (15 - febit);
	}

	frommod = frombm->BytesPerRow - (bltwidth << 1);
	tomod   = tobm->BytesPerRow   - (bltwidth << 1);

	bltshift = shift << 12;
	size = (height << 6) | bltwidth;

	mask += fromoffset;

#ifndef DEBUG
	WAITFORBLITTER
#endif

	hardware->bltcon0 = bltshift | 0x0FCA;
	hardware->bltcon1 = bltshift;
	hardware->bltafwm = firstmask;
	hardware->bltalwm = lastmask & othermask;
	hardware->bltamod = frommod;
	hardware->bltbmod = frommod;
	hardware->bltcmod = tomod;
	hardware->bltdmod = tomod;

	for (ndx = 0; ndx < depth; ndx++) {
		if (planemask & 0x01) {
			toptr = (ULONG)tobm->Planes[ndx] + (ULONG)tooffset;
#ifndef DEBUG
			WAITFORBLITTER
#endif

			hardware->bltapt  = (APTR)mask;
			hardware->bltbpt  = (APTR)((ULONG)frombm->Planes[ndx] + fromoffset);
			hardware->bltcpt  = (APTR)toptr;
			hardware->bltdpt  = (APTR)toptr;
			hardware->bltsize = size;

#if DEBUG
	dorealblit (hardware);
#endif

		}
		planemask >>= 1;
	}

	if (more) {
		bltwidth <<= 1;
		newwidth = bltwidth - 2;
		fromoffset += newwidth;
		mask       += newwidth;
		tooffset   += newwidth;

		frommod = frombm->BytesPerRow - (2 << 1);
		tomod   = tobm->BytesPerRow   - (2 << 1);

		size	= (height << 6) | 2;

		firstmask = 0xFFFF << (15 - febit);
		lastmask  = 0xFFFF >> (((15 - shift) + 1) & 0x0F);
#ifndef DEBUG
		WAITFORBLITTER
#endif

		hardware->bltafwm = lastmask & firstmask;
		hardware->bltalwm = 0x0000;
		hardware->bltbmod = frommod;
		hardware->bltamod = frommod;
		hardware->bltcmod = tomod;
		hardware->bltdmod = tomod;

		for (ndx = 0; ndx < depth; ndx++) {
			if (tempmask & 0x01) {
				toptr = (ULONG)tobm->Planes[ndx] + (ULONG)tooffset;
#ifndef DEBUG
				WAITFORBLITTER
#endif

				hardware->bltapt  = (APTR)mask;
				hardware->bltbpt  = (APTR)((ULONG)frombm->Planes[ndx] + fromoffset);
				hardware->bltcpt  = (APTR)toptr;
				hardware->bltdpt  = (APTR)toptr;
				hardware->bltsize = size;

#if DEBUG
	dorealblit (hardware);
#endif

			}
			tempmask >>= 1;
		}

	}


#ifndef DEBUG
	DisownBlitter ();
#endif

}

/**************************************************************************
 *
 * CopyBitMap
 *
 * SYNOPSIS
 *    void CopyBitMap (
 *                struct BitMap *frombm,
 *                int            fromleft,  
 *                int            fromtop,
 *                struct BitMap *tobm,
 *                int            toleft,
 *                int            totop,
 *                int            width,
 *                int            height,
 *                UBYTE		 planemask,
 *                );
 *
 * FUNCTION
 *    Use the blitter to copy from one bitmap to another.  If the blit
 *    is word aligned (ie. First pixel is the first bit of the first
 *    word and last pixel is last bit of the last word) and the same is
 *    true for both the source and the destination then this routine will
 *    use a special blit (D=A) which is twice as fast as the normal
 *    'clipped' blit (D=AB+~AC).
 *
 * INPUTS
 *    frombm    = pointer to bitmap to copy from
 *    fromleft  = x position to start copying from
 *    fromtop   = y position to start copying from
 *    tobm      = pointer to bitmap to copy to
 *    toleft    = x position to start copying to
 *    totop     = y position to start copying to
 *    width     = width in pixels of area to copy
 *    height    = height in pixels of area to copy
 *    planemask = bitmask of planes to effect, bit0 = plane 0, bit1 = plane1...
 *
 * HISTORY
 *
 *
 * SEE ALSO
 *   ShapeBlit (), CopyMaskBitMap ()
*/
void CopyBitMap (
	frombm,
	fromleft,
	fromtop,
	tobm,
	toleft,
	totop,
	inwidth,
	inheight,
	planemask,
)
	struct BitMap	*frombm;
	int		 fromleft;
	int		 fromtop;
	struct BitMap	*tobm;
	int		 toleft;
	int		 totop;
	int		 inwidth;
	int		 inheight;
	UBYTE		 planemask;
{
	register ULONG		 fromoffset;
	register ULONG		 tooffset;
		 BLITTER	 h;
	register BLITTER	*hardware = 
#if DEBUG
						&h;
#else
						(struct Custom *)0x0DFF000;
#endif
	register ULONG		newoffset;
	UWORD		fsword;
	UWORD		tsword;
	UWORD		newwidth;
	UWORD		fend;
	UWORD		tend;
	UWORD		fsbit;
	UWORD		tsbit;
	UWORD		febit;
	UWORD		tebit;
	UWORD		frommod;
	UWORD		tomod;
	UWORD		shift;
	UWORD		bltshift;
	UWORD		fbwidth;
	UWORD		tbwidth;
	UWORD		bltwidth;
	ULONG		toptr;
	UWORD		size;
	UWORD		firstmask;
	UWORD		lastmask;
	UWORD		othermask;
	UWORD		ndx;
	UWORD		depth;
	UWORD		control;

/* --------------------------------------------------------------------- */
	int width  = inwidth; 
	int height = inheight;

	if (toleft < ClipLeft) {
		width	  = inwidth - (ClipLeft - toleft);
		toleft	  = ClipLeft;
		fromleft += inwidth - width;
	} else if (toleft > ClipLeft + ClipWidth - inwidth) {
		width	  = (ClipLeft + ClipWidth) - toleft;
	}

	if (totop < ClipTop) {
		height	 = inheight - (ClipTop - totop);
		totop	 = ClipTop;
		fromtop += inheight - height;
	} else  if (totop > ClipTop + ClipHeight - inheight) {
		height	 = (ClipTop + ClipHeight) - totop;
	}

	if (width <= 0 || height <= 0) {
		return;
	}
/* --------------------------------------------------------------------- */
	depth = min (frombm->Depth, tobm->Depth);

	fsword = fromleft >> 4;
	tsword = toleft   >> 4;

	newwidth = width - 1;
	fend   = fromleft + newwidth;
	tend   = toleft   + newwidth;

	fromoffset = fromtop * frombm->BytesPerRow + (fsword << 1);
	tooffset   = totop   * tobm->BytesPerRow   + (tsword << 1);

	fsbit	= fromleft & 0x0F;
	tsbit	= toleft   & 0x0F;

	febit	= fend & 0x0F;
	tebit	= tend & 0x0F;

	fbwidth = (fend >> 4) - fsword + 1;
	tbwidth = (tend >> 4) - tsword + 1;
	bltwidth  = fbwidth;

	if (!fsbit && !tsbit && febit == 15 && tebit == 15) {
		frommod = frombm->BytesPerRow - (bltwidth << 1);
		tomod   = tobm->BytesPerRow   - (bltwidth << 1);

		size = (height << 6) | bltwidth;

		if (fromoffset < tooffset) {
			control     = 0x0002;
			newoffset   = (bltwidth - 1) << 1;
			fromoffset += frombm->BytesPerRow * (height - 1) + newoffset;
			tooffset   += tobm->BytesPerRow * (height - 1) + newoffset;
		} else {
			control = 0x0000;
		}

#ifndef DEBUG
		OwnBlitter ();
		WAITFORBLITTER
#endif

		hardware->bltcon0 = 0x09F0;
		hardware->bltcon1 = control;
		hardware->bltafwm = 0xFFFF;
		hardware->bltalwm = 0xFFFF;
		hardware->bltamod = frommod;
		hardware->bltdmod = tomod;

		for (ndx = 0; ndx < depth; ndx++) {
			if (planemask & 0x01) {
				toptr = (ULONG)tobm->Planes[ndx] + (ULONG)tooffset;
#ifndef DEBUG
				WAITFORBLITTER
#endif

				hardware->bltapt  = (APTR)((ULONG)frombm->Planes[ndx] + fromoffset);
				hardware->bltdpt  = (APTR)toptr;
				hardware->bltsize = size;

#if DEBUG
	dorealblit (hardware);
#endif

			}
			planemask >>= 1;
		}
#ifndef DEBUG
		DisownBlitter ();
#endif

	} else {

		BltBitMap (frombm, fromleft, fromtop,
			   tobm,   toleft,   totop,
			   width, height, 0xC0, 0xFF, NULL);

#if 0
		shift = (tsbit - fsbit);

		if (shift > 15) {
			bltwidth += 1;
			shift    &= 0x0F;
			tooffset -= 2;
			othermask = 0x0000;
		}

		firstmask = 0xFFFF >> fsbit;

		if (tbwidth > fbwidth) {
			more = TRUE;
			lastmask = 0xFFFF << shift;
		} else {
			lastmask  = 0xFFFF << (15 - febit);
		}

		frommod = frombm->BytesPerRow - (bltwidth << 1);
		tomod   = tobm->BytesPerRow   - (bltwidth << 1);

		bltshift = shift << 12;
		size = (height << 6) | bltwidth;

		mask += fromoffset;

#ifndef DEBUG
		WAITFORBLITTER
#endif

		hardware->bltcon0 = bltshift | 0x0FCA;
		hardware->bltcon1 = bltshift;
		hardware->bltafwm = firstmask;
		hardware->bltalwm = lastmask & othermask;
		hardware->bltamod = frommod;
		hardware->bltbmod = frommod;
		hardware->bltcmod = tomod;
		hardware->bltdmod = tomod;

		for (ndx = 0; ndx < depth; ndx++) {
			if (planemask & 0x01) {
				toptr = (ULONG)tobm->Planes[ndx] + (ULONG)tooffset;
#ifndef DEBUG
				WAITFORBLITTER
#endif

				hardware->bltapt  = (APTR)mask;
				hardware->bltbpt  = (APTR)((ULONG)frombm->Planes[ndx] + fromoffset);
				hardware->bltcpt  = (APTR)toptr;
				hardware->bltdpt  = (APTR)toptr;
				hardware->bltsize = size;

#if DEBUG
	dorealblit (hardware);
#endif

			}
			planemask >>= 1;
		}
#endif
	}
}

