/*
 * cpr.c --  experiment with user copper lists
 * Copyright (c) 1988, I and I Computing, and Commodore-Amiga, Inc.
 *
 *
 * Executables based on this information may be used in software
 * for Commodore Amiga computers.  All other rights reserved.
 *
 * This information is provided "as is"; no warranties are made.
 * All use is at your own risk, and no liability or responsibility is assumed.
 */

#include "sysall.h"
#include "getargs.h"

/* debug */
#define D( x ) 	;

struct Screen *screen = NULL;
struct ViewPort *vp = NULL;
struct UCopList	*ucl = NULL;

int		which_demo = 0;

struct Arg myargs[] = {
	{'w', ARG_TINT, &which_demo, "0-colors, 1-wait/move, 2-bitplanes"},
};

main(argc, argv)
char	**argv;
{
	void *AllocMem();
	long int i;
	struct UCopList	*oldwbucl;
	struct Window *awindow;
	struct Window *AWindow();

	OpenI( 33 );
	OpenGfx( 33 );

	/* hope we'll get the workbench	*/
	if ( !( awindow = AWindow() ) )
	{
		printf( "no active window!\n" );
		goto OUT;
	}

    if ( getargs( argv, myargs, NUMARGS( myargs ), NULL ) < 0 ) goto OUT;

	screen = awindow->WScreen;
	vp = &screen->ViewPort;
	oldwbucl = vp->UCopIns;	/* save previous	*/

	D( printf("largest hpos: %d, or %d lores pixels\n", 0xe2, 0xe2<<1) );
	D( printf("Custom base at %lx\n", &custom) );
	D( printf("Color0 at %lx\n", &custom.color[0]) );

	/* i will free this myself	*/
	if ( !(ucl = AllocMem( (LONG) sizeof (struct UCopList), 
		(LONG) MEMF_CHIP | (LONG) MEMF_CLEAR) )) goto OUT;

	CINIT( ucl, 400L );

	switch ( which_demo )
	{
	case 0:
		colorsCopList( ucl );
		break;
	case 1:
		waitmoveCopList( ucl );
		break;
	case 2:
		bplCopList( ucl, screen );
		break;
	default:
		printf("arg out of range\n");
		break;
	}

	CEND( ucl );

	vp->UCopIns = ucl;
	D( dumpUCL( ucl ) );

	MakeScreen( screen );
	RethinkDisplay();

	printf("cool?\n");
	getchar();

OUT:
	/* put him back	*/
	if (screen)
	{
		vp->UCopIns = oldwbucl;
		MakeScreen( screen );
		RethinkDisplay();
	}

	if ( ucl )
	{
		FreeCopList( ucl->FirstCopList );
		FreeMem( ucl, (LONG) sizeof (struct UCopList) );
	}

	CloseI();
	CloseGfx();

	printf("bye.\n");
}

/* recall format:
 *	CWAIT( ucl, vbeam, hbeam )
 *	CMOVE( ucl, register, value )
 */

#define HWAIT2	(0)

/* macros for specifying bitplane pointer registers	*/
#define BPLPTH( p )		 (*((UWORD *) &custom.bplpt[0] + (2 * ((p) - 1))))
#define BPLPTL( p )		 (*((UWORD *) &custom.bplpt[0] + (2 * ((p) - 1)) + 1))

/*
 * demonstrate technique of changing bitplane pointers on the fly.
 * only works with 2 bitplane screen
 */
bplCopList( ucl, screen )
struct UCopList *ucl;
struct Screen *screen;
{
	int		splitline;
	struct BitMap	*bmap = &screen->BitMap;
	long	planeaddr;

	splitline = 140;

	planeaddr = (long) bmap->Planes[ 0 ];
	CWAIT( ucl, (long) splitline, (long) HWAIT2 );
	CMOVE( ucl, BPLPTH( 1 ), (long) (planeaddr >> 16) & 0xffff );
	CMOVE( ucl, BPLPTL( 1 ), (long) planeaddr & 0xffff );

	planeaddr = (long) bmap->Planes[ 1 ];
	CMOVE( ucl, BPLPTH( 2 ), (long) (planeaddr >> 16) & 0xffff );
	CMOVE( ucl, BPLPTL( 2 ), (long) planeaddr & 0xffff );
}


#define RED		(0x0f00)
#define GREEN	(0x00f0)
#define BLUE	(0x000f)

#define HWAIT1	(0x3e)

/*
 * visually demonstrate the amounts of time taken by wait and move
 * copper instructions.  Note that MakeVPort() will eliminate wait
 * instructions that it deems to be irrelevant.
 */
waitmoveCopList( ucl )
struct UCopList *ucl;
{
	CWAIT( ucl, (long) 100, (long) HWAIT1 );
	CMOVE( ucl, custom.color[ 0 ], (long) RED );
	CWAIT( ucl, (long) 100, (long) HWAIT1 + 1 );
	CMOVE( ucl, custom.color[ 0 ], (long) GREEN );
	CWAIT( ucl, (long) 100, (long) HWAIT1 + 2 );
	CMOVE( ucl, custom.color[ 0 ], (long) BLUE );

	CWAIT( ucl, (long) 101, (long) HWAIT1 );
	CMOVE( ucl, custom.color[ 0 ], (long) RED );
	CMOVE( ucl, custom.color[ 0 ], (long) GREEN );
	CMOVE( ucl, custom.color[ 0 ], (long) BLUE );
}

/*
 * change the background color every display row
 */
colorsCopList( ucl )
struct UCopList *ucl;
{
	int		row;
	int		numlines;

	numlines = screen->Height;


	clLineColor( ucl, 0, 1, 0xf );
	for (row = 0; row < numlines; ++row)
	{
		clLineColor( ucl, row + 1, 0, funcolor( row ) );
	}
}

#define FUNCOL( x, y, z) (((x)<<8)+((y)<<4)+(z))


funcolor( num )
{
	int	inc;
	int	dec;
	int	block;
	int	funcol;

#if JUST_BLUE
	return (num & 0xf);
#endif

	inc = num & 0xf;
	dec = 0xf - inc;

	block =  (num >> 4) % 6;

	switch ( block )
	{
	case 0:
		funcol = FUNCOL( inc, 0xf, dec);
		break;
	case 1:
		funcol = FUNCOL( 0xf, dec, inc);
		break;
	case 2:
		funcol = FUNCOL( dec, inc, 0xf);
		break;
	case 3:
		funcol = FUNCOL( inc, dec, 0xf);
		break;
	case 4:
		funcol = FUNCOL( 0xf, inc, dec);
		break;
	case 5:
		funcol = FUNCOL( dec, 0xf, inc);
		break;
	}
	return( funcol );
}

#define HLINESTART (0L)

clLineColor( ucl, line, pen, color)
struct UCopList *ucl;
{
	CWAIT( ucl, (long) line, HLINESTART)
	CMOVE( ucl, custom.color[ pen ], (long) color);
}


dumpUCL( ucl )
struct UCopList *ucl;
{
	printf("UCL at %lx\n", ucl);
	if (ucl)
	{
		printf("FirstCL: %lx\n", ucl->FirstCopList);
		printf("CurrentCL: %lx\n", ucl->CopList);

		dumpCL( ucl->FirstCopList );
	}
}

dumpCL( cl )
struct CopList	*cl;
{
	int i;
	printf("CopList at %lx\n", cl);

	for( i = 0 ; cl && ++i < 10; cl = cl->Next)
	{
		printf("CopIns block at %lx, count %d\n", cl->CopIns, cl->MaxCount);
		dumpCIns( cl->CopIns, cl->MaxCount );
	}
}

dumpCIns( ci, num )
struct CopIns *ci;
{
	while (num--)
	{
		switch (ci->OpCode)
		{
		case COPPER_MOVE:
			printf("CMOVE\t0x%x\t0x%x\n", ci->DESTADDR, ci->DESTDATA);
			break;
		case COPPER_WAIT:
			printf("CWAIT\t%d\t%d\n", ci->VWAITPOS, ci->HWAITPOS);
			break;
		case CPRNXTBUF:
			printf("NXTBUF\t\t%lx\n", ci->NXTLIST);
			break;
		default:
			printf("unknown copper instruction: %x, (%x, %x)\n",
				ci->OpCode, ci->DESTADDR, ci->DESTDATA);

			return;
		}
		++ci;
	}
}
