#include "testit.h"

void			*oldcms,
			*newcms,
			*oldcmq,
			*newcmq,
			*naught;
char			*buf1,
			*buf2,
			*buf3,
			*buf4;
ULONG			identity;
BOOL			negative;

void			*TimerBase;
struct MsgPort		*tport;
struct timerequest	*treq;
struct timeval		tim1,
			tim2,
			tim3,
			timold,
			timdif,
			totold,
			totnew;

extern struct ExecBase	*SysBase;

#define S0_D0	0
#define S0_D1	1
#define	S0_D2	2
#define	S0_D3	3
#define	S1_D0	4
#define	S1_D1	5
#define	S1_D2	6
#define	S1_D3	7
#define	S2_D0	8
#define	S2_D1	9
#define	S2_D2	10
#define	S2_D3	11
#define	S3_D0	12
#define	S3_D1	13
#define	S3_D2	14
#define	S3_D3	15

void printoffset(unsigned short alignment);
#pragma regcall(printoffset(d0))

void testit(unsigned long loops,unsigned long bytes,long offset);
#pragma regcall(testit(d0,d1,a0))

long main(void)
{
#asm
	movea.l	(4).w,a0
	move.l	_LVOCopyMem#+2(a0),a1
	move.l	a1,_newcms
	move.l	_LVOCopyMemQuick#+2(a0),_newcmq
	move.l	-4(a1),_identity
	move.l	-8(a1),_oldcms
	move.l	-12(a1),_oldcmq
	lea	_donaught#,a0
	move.l	a0,_naught
#endasm
	if	(SysBase->LibNode.lib_Version < 37)
	{
		Write(Output(),"You need 2.0 to run this program!\n",34);
		goto warn;
	}
	if	(identity != '*Art')
	{
		Printf("You must run CopyMemQuicker first!\n");
warn:		return 5;
	}
	tport = CreatePort("cmemtest",0);
	treq = (struct timerequest *)CreateExtIO(tport,(LONG)sizeof(struct timerequest));
	OpenDevice("timer.device",UNIT_VBLANK,(struct IORequest *)treq,0);
	TimerBase = treq->tr_node.io_Device;
	Printf(	"This test will compare the old CopyMem/CopyMemQuick routines with\n"
		"the new ones you have installed.  A great variety of tests will be\n"
		"run, and this might take some time, especially if your system has a\n"
		"slow processor.\n\n"
		"Initiating test (please be patient...)\n\n");
	if	((!(buf1 = (char *)malloc(65540)))
	||	(!(buf2 = (char *)malloc(65540))))
	{
		Printf("Not enough memory to run test!\n");
		return 10;
	}

	{
		UWORD	attnflags = SysBase->AttnFlags;
		ULONG	scalefactor;

		if	(attnflags & AFF_68040)
			scalefactor = 0;
		else if	(attnflags & AFF_68030)
			scalefactor = 1;
		else if	(attnflags & AFF_68020)
			scalefactor = 2;
		else if	(attnflags & AFF_68010)
			scalefactor = 3;
		else	scalefactor = 4;

		testit(565 >> scalefactor,65536,S0_D0);
		testit(147 >> scalefactor,65536,S0_D1);
		testit(413 >> scalefactor,65536,S0_D2);
		testit(147 >> scalefactor,65536,S0_D3);
		testit(147 >> scalefactor,65536,S1_D0);
		testit(382 >> scalefactor,65536,S1_D1);
		testit(147 >> scalefactor,65536,S1_D2);
		testit(501 >> scalefactor,65536,S1_D3);
		testit(501 >> scalefactor,65536,S2_D0);
		testit(147 >> scalefactor,65536,S2_D1);
		testit(382 >> scalefactor,65536,S2_D2);
		testit(147 >> scalefactor,65536,S2_D3);
		testit(147 >> scalefactor,65536,S3_D0);
		testit(413 >> scalefactor,65536,S3_D1);
		testit(147 >> scalefactor,65536,S3_D2);
		testit(564 >> scalefactor,65536,S3_D3);
		testit(33900 >> scalefactor,1024,S0_D0);
		testit(9400 >> scalefactor,1024,S0_D1);
		testit(24000 >> scalefactor,1024,S2_D2);
		testit(196000 >> scalefactor,128,S0_D0);
		testit(155000 >> scalefactor,128,S2_D2);
		testit(588000 >> scalefactor,19,S0_D0);
		testit(622000 >> scalefactor,18,S0_D0);
		testit(663000 >> scalefactor,17,S0_D0);
		testit(956000 >> scalefactor,16,S0_D0);
		testit(1060000 >> scalefactor,8,S0_D0);
		testit(1430000 >> scalefactor,4,S0_D0);
		testit(2190000 >> scalefactor,1,S0_D0);
	}
	free(buf2);
	free(buf1);
	Printf("Total timing:\n-------------\n");
	Printf("%s routines	:%4ld.%02ld secs","Old",totold.tv_secs,totold.tv_micro / 10000);
	Printf("\n");
	Printf("%s routines	:%4ld.%02ld secs","New",totnew.tv_secs,totnew.tv_micro / 10000);
	Printf("\n");
	{
		unsigned long	pctint,
				hund1,
				hund2;
		struct timeval	*time3 = &tim3,
				*totalold = &totold;

		time3->tv_secs = totalold->tv_secs;
		time3->tv_micro = totalold->tv_micro;
		SubTime(time3,&totnew);
		if	((LONG)time3->tv_secs < 0)
		{
			time3->tv_secs = -((LONG)time3->tv_secs + 1);
			time3->tv_micro = 1000000 - time3->tv_micro;
			negative = TRUE;
		}
		else	negative = FALSE;
		hund2 = (time3->tv_secs * 100) + (time3->tv_micro / 10000);
		hund1 = (totalold->tv_secs * 100) + (totalold->tv_micro / 10000);
		pctint = hund2 * 100000 / hund1;
		if	((pctint % 10) > 5)
			pctint += 10;
		pctint /= 10;
		Printf("Total s%s\t:%4ld.%02ld %%   ",(negative) ? "lowdown" : "peedup",pctint / 100,pctint % 100);
		Printf("\n");
	}
	CloseDevice((struct IORequest *)treq);
	DeleteExtIO((struct IORequest *)treq);
	DeletePort(tport);
	return 0;
}

#asm
	xdef	_donaught
	xdef	__cli_parse
	xdef	__wb_parse
_donaught:
__cli_parse:
__wb_parse:
	rts
#endasm

void printoffset(unsigned short alignment)
{
	Printf((alignment & 2) ? "even" : "long");
	if	(alignment & 1)
		Printf("+1");
}

void testit(unsigned long loops,unsigned long bytes,long offset)
{
	struct timeval	*time1 = &tim1,
			*time2 = &tim2,
			*time3 = &tim3;
	unsigned long	pctint;

	Printf("Copying %ld bytes %ld times (",bytes,loops);
	buf3 = buf1 + ((offset & 0xC) >> 2);
	buf4 = buf2 + (offset & 0x3);
	printoffset((offset & 0xC) >> 2);
	Printf(" -> ");
	printoffset(offset & 0x3);
	Printf(" offset)\n");

	Forbid();
	GetSysTime(time1);
	{
#asm
	movem.l	a2-a3/a6/d4-d5,-(sp)
	movea.l	_naught,a2
	movea.l	_buf3,a3
	movea.l	_buf4,a6
	move.l	%%loops,d4
	move.w	d4,d5
	swap	d4
	subq.w	#1,d5
	bcs.s	l1end
loop1	movea.l	a3,a0
	movea.l	a6,a1
	move.l	%%bytes,d0
	jsr	(a2)
	dbf	d5,loop1
l1end	dbf	d4,loop1
	movem.l	(sp)+,a2-a3/a6/d4-d5
#endasm
	}
	GetSysTime(time3);
	Permit();

	SubTime(time3,time1);

	Forbid();
	GetSysTime(time1);
	{
#asm
	movem.l	a2-a3/a6/d4-d5,-(sp)
	movea.l	_oldcms,a2
	movea.l	_buf3,a3
	movea.l	_buf4,a6
	move.l	%%loops,d4
	move.w	d4,d5
	swap	d4
	subq.w	#1,d5
	bcs.s	l2end
loop2	movea.l	a3,a0
	movea.l	a6,a1
	move.l	%%bytes,d0
	jsr	(a2)
	dbf	d5,loop2
l2end	dbf	d4,loop2
	movem.l	(sp)+,a2-a3/a6/d4-d5
#endasm
	}
	GetSysTime(time2);
	Permit();

	SubTime(time2,time1);
	SubTime(time2,time3);
	AddTime(&totold,time2);
	timold.tv_secs = 
	timdif.tv_secs = time2->tv_secs;
	timold.tv_micro =
	timdif.tv_micro = time2->tv_micro;
	Printf("Old CopyMem%s: %2ld.%02ld secs\n","\t",time2->tv_secs,time2->tv_micro / 10000);

	Forbid();
	GetSysTime(time1);
	{
#asm
	movem.l	a2-a3/a6/d4-d5,-(sp)
	movea.l	_newcms,a2
	movea.l	_buf3,a3
	movea.l	_buf4,a6
	move.l	%%loops,d4
	move.w	d4,d5
	swap	d4
	subq.w	#1,d5
	bcs.s	l3end
loop3	movea.l	a3,a0
	movea.l	a6,a1
	move.l	%%bytes,d0
	jsr	(a2)
	dbf	d5,loop3
l3end	dbf	d4,loop3
	movem.l	(sp)+,a2-a3/a6/d4-d5
#endasm
	}
	GetSysTime(time2);
	Permit();

	SubTime(time2,time1);
	SubTime(time2,time3);
	AddTime(&totnew,time2);
	SubTime(&timdif,time2);
	if	((LONG)timdif.tv_secs < 0)
	{
		timdif.tv_secs = -((LONG)timdif.tv_secs + 1);
		timdif.tv_micro = 1000000 - timdif.tv_micro;
		negative = TRUE;
	}
	else	negative = FALSE;
	Printf("New CopyMem%s: %2ld.%02ld secs","\t",time2->tv_secs,time2->tv_micro / 10000);
	pctint	= ((timdif.tv_secs * 100) + (timdif.tv_micro / 10000)) * 10000
		/ ((timold.tv_secs * 100) + (timold.tv_micro / 10000));
	if	((pctint % 10) > 5)
		pctint += 10;
	pctint /= 10;
	Printf(" (%s%2ld.%ld%%)\n",(negative) ? "+" : "-",pctint / 10,pctint % 10);
	if	((!offset) && (!(bytes % 4)))
	{
	Forbid();
	GetSysTime(time1);
	{
#asm
	movem.l	a2-a3/a6/d4-d5,-(sp)
	movea.l	_oldcmq,a2
	movea.l	_buf3,a3
	movea.l	_buf4,a6
	move.l	%%loops,d4
	move.w	d4,d5
	swap	d4
	subq.w	#1,d5
	bcs.s	l4end
loop4	movea.l	a3,a0
	movea.l	a6,a1
	move.l	%%bytes,d0
	jsr	(a2)
	dbf	d5,loop4
l4end	dbf	d4,loop4
	movem.l	(sp)+,a2-a3/a6/d4-d5
#endasm
	}
	GetSysTime(time2);
	Permit();

	SubTime(time2,time1);
	SubTime(time2,time3);
	AddTime(&totold,time2);
	timold.tv_secs = 
	timdif.tv_secs = time2->tv_secs;
	timold.tv_micro =
	timdif.tv_micro = time2->tv_micro;
	Printf("Old CopyMem%s: %2ld.%02ld secs\n","Quick",time2->tv_secs,time2->tv_micro / 10000);

	Forbid();
	GetSysTime(time1);
	{
#asm
	movem.l	a2-a3/a6/d4-d5,-(sp)
	movea.l	_newcmq,a2
	movea.l	_buf3,a3
	movea.l	_buf4,a6
	move.l	%%loops,d4
	move.w	d4,d5
	swap	d4
	subq.w	#1,d5
	bcs.s	l5end
loop5	movea.l	a3,a0
	movea.l	a6,a1
	move.l	%%bytes,d0
	jsr	(a2)
	dbf	d5,loop5
l5end	dbf	d4,loop5
	movem.l	(sp)+,a2-a3/a6/d4-d5
#endasm
	}
	GetSysTime(time2);
	Permit();

	SubTime(time2,time1);
	SubTime(time2,time3);
	AddTime(&totnew,time2);
	SubTime(&timdif,time2);
	if	(negative = ((LONG)timdif.tv_secs < 0))
	{
		timdif.tv_secs = -((LONG)timdif.tv_secs + 1);
		timdif.tv_micro = 1000000 - timdif.tv_micro;
	}
	Printf("New CopyMem%s: %2ld.%02ld secs","Quick",time2->tv_secs,time2->tv_micro / 10000);
	pctint	= ((timdif.tv_secs * 100) + (timdif.tv_micro / 10000)) * 10000
		/ ((timold.tv_secs * 100) + (timold.tv_micro / 10000));
	if	((pctint % 10) > 5)
		pctint += 10;
	pctint /= 10;
	Printf(" (%s%2ld.%ld%%)\n",(negative) ? "+" : "-",pctint / 10,pctint % 10);
	}
	Printf("\n");
}

