#include <stdio.h>
#include <exec/execbase.h>
#include <clib/timer_protos.h>
#include <pragmas/timer_lib.h>
#define STANDALONE
#include <functions.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;
BOOL			has010,
			has020,
			has030;

extern struct ExecBase	*SysBase;

#define	O_LONG	0
#define O_ODD	1
#define O_EVEN	2

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;
	}
	has030 = FALSE;
	has020 = FALSE;
	has010 = FALSE;
	if	(SysBase->AttnFlags & AFF_68030)
		has030 = TRUE;
	if	(SysBase->AttnFlags & AFF_68020)
		has020 = TRUE;
	else if	(SysBase->AttnFlags & AFF_68010)
		has010 = TRUE;
	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.  All test values are chosen to take a mean time of\n"
		"10 seconds to execute with a 68010 (a middle range processor).\n\n"
		"Initiating test (please be patient...)\n\n");
	if	((!(buf1 = (char *)malloc(66000)))
	||	(!(buf2 = (char *)malloc(66000))))
	{
		Printf("Not enough memory to run test!\n");
		return 10;
	}

	if	(has030)
	{
		Printf("680%s0 found, prolonging test\n\n",(SysBase->AttnFlags & AFF_68040) ? "4" : "3");
		testit(     79*5,65536,O_ODD);
		testit(    207*5,65536,O_EVEN);
		testit(    207*5,65536,O_LONG);
		testit(   4990*5, 1024,O_ODD);
		testit(  12500*5, 1024,O_EVEN);
		testit(  12500*5, 1024,O_ODD);
		testit(  76200*5,  128,O_LONG);
		testit( 243000*5,   17,O_LONG);
		testit( 322000*5,   16,O_LONG);
		testit( 243000*5,   15,O_LONG);
		testit( 256000*5,   14,O_LONG);
		testit( 464000*5,    8,O_LONG);
		testit( 619000*5,    4,O_LONG);
		testit(1063000*5,    1,O_LONG);
	}
	else if	(has020)
	{
		Printf("680%s0 found, prolonging test\n\n","2");
		testit(     79*3,65536,O_ODD);
		testit(    207*3,65536,O_EVEN);
		testit(    207*3,65536,O_LONG);
		testit(   4990*3, 1024,O_ODD);
		testit(  12500*3, 1024,O_EVEN);
		testit(  12500*3, 1024,O_LONG);
		testit(  76200*3,  128,O_LONG);
		testit( 243000*3,   17,O_LONG);
		testit( 322000*3,   16,O_LONG);
		testit( 243000*3,   15,O_LONG);
		testit( 256000*3,   14,O_LONG);
		testit( 464000*3,    8,O_LONG);
		testit( 619000*3,    4,O_LONG);
		testit(1063000*3,    1,O_LONG);
	}
	else if	(has010)
	{
		testit(     79*1,65536,O_ODD);
		testit(    207*1,65536,O_LONG);
		testit(   4990*1, 1024,O_ODD);
		testit(  12500*1, 1024,O_LONG);
		testit(  76200*1,  128,O_LONG);
		testit( 243000*1,   17,O_LONG);
		testit( 322000*1,   16,O_LONG);
		testit( 243000*1,   15,O_LONG);
		testit( 256000*1,   14,O_LONG);
		testit( 464000*1,    8,O_LONG);
		testit( 619000*1,    4,O_LONG);
		testit(1063000*1,    1,O_LONG);
	}
	else
	{
		Printf("68000 found, shortening test\n\n");
		testit(     79/2,65536,O_ODD);
		testit(    207*1,65536,O_LONG);
		testit(   4990/2, 1024,O_ODD);
		testit(  12500*1, 1024,O_LONG);
		testit(  76200*1,  128,O_LONG);
		testit( 243000*2/3, 17,O_LONG);
		testit( 322000*2/3, 16,O_LONG);
		testit( 243000*2/3, 15,O_LONG);
		testit( 256000*2/3, 14,O_LONG);
		testit( 464000/2,    8,O_LONG);
		testit( 619000/2,    4,O_LONG);
		testit(1063000/2,    1,O_LONG);
	}

	free(buf2);
	free(buf1);
	Printf("Total timing:\n-------------\n");
	Printf("%s routines	:%4ld.%02ld secs","Old",totold.tv_secs,totold.tv_micro / 10000);
	if	(has010)
		Printf(" (%s predicted)","203.35");
	Printf("\n");
	Printf("%s routines	:%4ld.%02ld secs","New",totnew.tv_secs,totnew.tv_micro / 10000);
	if	(has010)
		Printf(" (%s predicted)","156.58");
	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);
		if	(has010)
			Printf(" (%s predicted)"," 23.00");
		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 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;
	buf4 = buf2;
	switch(offset)
	{
		case O_LONG:	Printf("long");
				break;
		case O_EVEN:	Printf("even");
				buf3 += 2;
				buf4 += 2;
				break;
		case O_ODD:	Printf("odd");
				buf3 += 3;
				break;
	}
	Printf(" offset)\n");

	GetSysTime(time1);
	Forbid();
	{
#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
	}
	Permit();
	GetSysTime(time3);
	SubTime(time3,time1);
	GetSysTime(time1);
	Forbid();
	{
#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
	}
	Permit();
	GetSysTime(time2);
	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);

	GetSysTime(time1);
	Forbid();
	{
#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
	}
	Permit();
	GetSysTime(time2);
	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)))
	{
	GetSysTime(time1);
	Forbid();
	{
#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
	}
	Permit();
	GetSysTime(time2);
	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);

	GetSysTime(time1);
	Forbid();
	{
#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
	}
	Permit();
	GetSysTime(time2);
	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","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");
}

