/***********************************************************************
*
*	Fuzmath.c - a variation on Fuz.c to compare performance of the
*		    various math libraries against the 68881.
*
*	This particular version of Fuz is intended to be compiled using
*	Manx Aztec C version 3.40a or later. A version to compile with
*	Lattice C could be made provided one wanted to make the 'hacks'
*	necessary to the Lattice linker libraries. The performance of
*	Lattice-compiled versions should behave the same way, or very
*	nearly so.
*	  This program will use one of three math libraries when compiled
*	depending upon a 'flag' defined in the makefile or the program.
*
*	Here is the makefile I used:
*
##########################################################
#	Makefile for FuzMath.c
mathi:
		cc +fi +L -Dmathi -S fuzmath.c
		ln -o Fuzmath.i fuzmath.o -lmi32 -lc32
mathf:
		cc +ff +L -Dmathf -S fuzmath.c
		ln -o Fuzmath.f fuzmath.o -lm32 -lc32
matha:
		cc +fi +L -Dmatha -S fuzmath.c
		ln -o Fuzmath.a fuzmath.o -lmas32 -lc32

all:		mathi mathf matha
##########################################################
*
*	'mathi' will produce a version that uses the 68881 & double precision
*	math, 'mathf' will produce a version using FFP single precision math,
*	and 'matha' will produce a version using double precision math but
*	without using the 68881.
*	In order to be able to run either version of the double precision
*	fuzmath program and have it use the proper mathieeedoubbas.library
*	file, I had to hack up a few things, since the 68881 version and
*	non-68881 version double precision math library have the same name,
*	i.e., they both open 'mathieeedoubbas.library'. To make things easier
*	for me, I used the public domain program 'NewZap' to modify the
*	standard (non-68881) mathieeedoubbas.library, so that it is now
*	called 'mathieeedoubsft.library' (for software-based). I also went
*	and changed the Manx linker lib 'ma32.lib' internally so that it
*	would now try & access 'mathieeedoubsft.library', and renamed it
*	mas32.lib. This way I can have both versions of the double precision
*	math libraries on the same disk and access them seperately.
*	     Now, I'm sure that the above is horribly confusing to almost
*	everybody, especially Redmond Simonsen, but I don't really expect
*	anyone to bother with this program, as it's merely for demonstration
*	purposes for MicroBotics. But here it is, to do with as you will.
*	If you feel froggy, leap!
*
*
*			David Milligan, for MicroBotics 
*
****************************************************************************/

#include <exec/types.h>
#include <intuition/intuition.h>
#include <time.h>
#include <math.h>
#include <stdio.h>

#define INTUITION	0x00000001
#define GRAPHICS	0x00000002
#define SCREEN		0x00000004
#define WINDOW		0x00000008
#define MIDB		0x00000010
#define MIDT		0x00000020
#define MIDF		0x00000040
#define MIDA		0x00000080
#define	NW_IDCMP	MOUSEBUTTONS|GADGETDOWN|GADGETUP|CLOSEWINDOW
#define	NW_FLAGS	BORDERLESS|BACKDROP|ACTIVATE|WINDOWCLOSE

#ifndef TRUE
#define TRUE 1
#define FALSE 0
#endif

#define PI ((FLOAT)3.14159265358979323846)
#define DEG	(PI/80)
#define CX	320
#define CY	200
#define XTWEAK	170
#define YTWEAK	140

/*
 * Gadget definitions
 */
#define ROT	1
#define VX	2
#define VY	3
#define WHOA	4
#define GO	5
#define NUMGADS 5
#define BOOLAFLAGS	RELVERIFY|GADGIMMEDIATE
#define STRAFLAGS	RELVERIFY|GADGIMMEDIATE|STRINGCENTER
#define GADL_EDGE	5
#define GADT_EDGE	30

struct  GfxBase       *GfxBase;
struct  IntuitionBase *IntuitionBase;
struct  RastPort      *rp;
struct  ViewPort      *vp;
struct	Window	*w;
struct	Screen	*screen;
struct	IntuiMessage	*message;

UBYTE rotbuf[10] =	".02";	/* This set of values draws a nice display */
UBYTE vxbuf[10] =	"199";
UBYTE vybuf[10] =	"192";
char timebuf[25];

#ifdef	mathi
extern long MathIeeeDoubBasBase;
long MathIeeeDoubTransBase = 0;
#endif
#ifdef	mathf
extern long MathBase;
extern long MathTransBase;
#endif
#ifdef	matha
extern long MathIeeeDoubBasBase;
#endif

double sin();		/* We want double-precision values, */
double cos();		/* so we declare these as double    */
double fabs();
double atof();

double rot,vx,vy,inc;

struct timeval t1;
struct timeval t2;

extern struct MsgPort *CreatePort();
extern struct IORequest *CreateExtIO();

unsigned int mask = 0;
BOOL idling;
int from_wb,col,cnt,nx,ny,n;

/************************************************
 * 		Main Program 			*
 ************************************************/
main(argc)
int argc;
{
	ULONG  class;
	USHORT code;
	register struct Gadget *gad;
	
	col = 1;
	inc = nx = ny = cnt = 0;

	if(argc)		/* Did we start from CLI or Workbench? */
	  from_wb = 0;		/* Started from CLI	*/
	else
	  from_wb = -1;		/* Started from WB	*/

	std_open();		/* open all the librarys, screens, etc. */

	rot = atof(rotbuf); 	/* use default values for initial plot */
	vx = atof(vxbuf);
	vy = atof(vybuf);
	SetBPen(rp, 0);
	SetAPen(rp,col);
	Move(rp,CX,CY+YTWEAK);
	idling = FALSE;

	GetSysTime(&t1);

/************************************************
 * 		Main Fuzzy Area			*
 ************************************************/

FOREVER 
  {
	if((message = (struct IntuiMessage *)GetMsg(w->UserPort)) != NULL)
	{
	class = message->Class;
	code  = message->Code;
	gad = message->IAddress;
	ReplyMsg(message);
	   switch (class)
	    {
             case CLOSEWINDOW:
		stage_left();
		break;
	     case GADGETDOWN:
	     case GADGETUP:
		do_gads(class,gad,w);
		break;
	   default:
		break;
	    }

	} 	/* if(message) */
	if(ny < YTWEAK)
	    {
	    inc += rot;
	    if(vx == 0)
		{
		nx = (int)(sin(inc*DEG)*XTWEAK);
		}
		else
		{
		nx = (int)(sin(inc*DEG)*fabs(sin((inc*DEG)*vx))*XTWEAK);
		}
	    ny = (int)(cos(inc*DEG)*fabs(cos((inc*DEG)*vy))*YTWEAK);
	    cnt ++;
	    if(cnt >141)
		 {
		  cnt = 0;
		  col++;
		  if(col == 15)
		    col = 1;
		  SetAPen(rp,col);
		 }
	     Draw(rp,nx+CX,ny+CY);
	     if(ny >= YTWEAK && !idling)
		{
		GetSysTime(&t2);
		 showetime();
		}
	    }
  }
 stage_left();	/* main exit */
}	/* end of main */


/*
 *	color values
 *		pen #	  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15
 */
USHORT red[] =		{ 0, 13,11,12,13,13,14,15, 8, 4, 0, 0, 0, 0, 0, 4 };
USHORT green[] =	{ 0,  2, 3, 4, 5, 7, 9,13,11, 9, 6, 4, 2, 0, 5,10 };
USHORT blue[] =		{ 0,  2, 2, 2, 2, 1, 1, 0, 0, 0, 2, 7,10,14,15,15 };

struct TextAttr Topfont =
	{
	"topaz.font", TOPAZ_SIXTY,
	FSF_EXTENDED | FSF_ITALIC | FSF_BOLD,
	FPF_ROMFONT | FPF_PROPORTIONAL
	};
struct TextAttr Toyfont =
	{
	"topaz.font", TOPAZ_EIGHTY,
	FSF_EXTENDED,
	FPF_ROMFONT | FPF_PROPORTIONAL
	};
/*
 *	border line vectors
 */
USHORT gadvecs2[] =	{ 0,0,58,0,58,10,0,10,0,0 };
USHORT gadvecs3[] =	{ 0,0,84,0,84,12,0,12,0,0 };
USHORT timervec[] =	{0,0,200,0,200,11,0,11,0,0};

struct IntuiText timer_text = { 7,0,JAM2,20,380, &Toyfont, &timebuf, NULL };
struct Border timer_brdr = { 5,377, 1,0,JAM2, 5, timervec, NULL };

struct Border gad_brdr[NUMGADS] = 
{
	{ -2,-3, 15,0,JAM1, 5, gadvecs3, NULL },
	{ -2,-3, 13,0,JAM1, 5, gadvecs3, NULL },
	{ -2,-3, 11,0,JAM1, 5, gadvecs3, NULL },
	{ -2,-1,  5,0,JAM1, 5, gadvecs2, NULL },
	{ -2,-1, 10,0,JAM1, 5, gadvecs2, NULL }
};

struct IntuiText gad_txt[NUMGADS] =
{
	{ 7,0,JAM2,	28,-12, &Toyfont, "rot", NULL },
	{ 7,0,JAM2,	34,-12, &Toyfont, "vx", NULL },
	{ 7,0,JAM2,	34,-12, &Toyfont, "vy", NULL },
	{ 7,4,JAM2,	1,   0, &Topfont, "Whoa!", NULL },
	{ 7,9,JAM2,	1,   0, &Topfont, "Start", NULL }
};

struct StringInfo gad_info[NUMGADS-2] =
{ 
	{ rotbuf,  NULL, 0, 10, 0, 0,0,0,2,1, 0, NULL, NULL },
	{ vxbuf,   NULL, 0, 10, 0, 0,0,0,2,1, 0, NULL, NULL },
	{ vybuf,   NULL, 0, 10, 0, 0,0,0,2,1, 0, NULL, NULL }
};

struct Gadget toy_gad[NUMGADS] = 
{
	{		/*	rotation string gadget	*/
	NULL,
	GADL_EDGE,GADT_EDGE,
	80,10,
	GADGHCOMP,STRAFLAGS,STRGADGET,
	(APTR)&gad_brdr[0],NULL, &gad_txt[0],
	0, (APTR)&gad_info[0],
	ROT, NULL
	},
	{		/*	x-vector input gadget	*/
	&toy_gad[0],
	GADL_EDGE,GADT_EDGE+25,
	80,10,
	GADGHCOMP,STRAFLAGS,STRGADGET,
	(APTR)&gad_brdr[1],NULL, &gad_txt[1],
	0, (APTR)&gad_info[1],
	VX, NULL
	},
	{		/*	y-vector input gadget	*/
	&toy_gad[1],
	GADL_EDGE,GADT_EDGE+50,
	80,10,
	GADGHCOMP,STRAFLAGS,STRGADGET,
	(APTR)&gad_brdr[2],NULL, &gad_txt[2],
	0, (APTR)&gad_info[2],
	VY, NULL
	},
	{		/*	stop button	*/
	&toy_gad[2],
	GADL_EDGE+11,GADT_EDGE+65,
	58,9,
	GADGHCOMP, BOOLAFLAGS, BOOLGADGET,
	(APTR)&gad_brdr[3],NULL, &gad_txt[3],
	0, NULL,
	WHOA, NULL
	},
	{		/*	start button 	*/
	&toy_gad[3],
	GADL_EDGE+11,GADT_EDGE+80,
	58,9, 
	GADGHCOMP, BOOLAFLAGS, BOOLGADGET, 
	(APTR)&gad_brdr[4],NULL, &gad_txt[4],
	0, NULL, 
	GO, NULL
	}
};

struct NewScreen ns =
	{
	0, 0, 640, 400,
	4, 0, 0,
	HIRES|LACE, CUSTOMSCREEN,
	&Toyfont,NULL, NULL
	};

struct NewWindow nw =
	{
	0, 0, 640, 400, 7, 0,
	NW_IDCMP,
	NW_FLAGS,
	&toy_gad[4], NULL,
#ifdef mathi
	"       IEEE Double-Precision Math using the 68881 coprocessor",
#endif
#ifdef matha
	"       IEEE Double-Precision Math - 68881 not used",
#endif
#ifdef mathf
	"       FFP Single-Precision Math - 68881 not used",
#endif
	NULL, NULL, 0, 0, 0, 0, CUSTOMSCREEN
	};

std_open()
{
	register int i;

	if(!(IntuitionBase = (struct IntuitionBase *)
			OpenLibrary("intuition.library", 0)))
		errexit(" Can't open Intuition");

	mask |= INTUITION;

	if(!(GfxBase = (struct GfxBase *)OpenLibrary("graphics.library", 0)))
		errexit(" Can't open Gfx lib");

	mask |= GRAPHICS;

#ifdef	mathi

	if(!(MathIeeeDoubBasBase = OpenLibrary("mathieeedoubbas.library",0)))
		errexit(" no basic ieee library\n");

	mask |= MIDB;

	if(!(MathIeeeDoubTransBase = OpenLibrary("mathieeedoubtrans.library",0)))
		errexit(" no transcendental ieee library\n");

	mask |= MIDT;
#endif
#ifdef	mathf

	if(!(MathBase = OpenLibrary("mathffp.library",0)))
		errexit(" no FFP library\n");

	if(!(MathTransBase = OpenLibrary("mathtrans.library",0)))
		errexit(" no transcendental FFP library\n");

	mask |= MIDF;
#endif
#ifdef	matha

	if(!(MathIeeeDoubBasBase = OpenLibrary("mathieeedoubsft.library",0)))
		errexit(" no basic mathieeedoubsft library\n");

	mask |= MIDA;

#endif

	if(!(screen = (struct Screen *)OpenScreen(&ns))) 
		errexit(" Can't open the screen");

	mask |= SCREEN;

	nw.Screen = screen;

	ShowTitle(screen,FALSE);	/* turn off the screen title bar */
	vp = &screen->ViewPort;
		
/*
 * install some loud garish color
 */
	for(i = 0; i < 16; i++)
		SetRGB4(vp,i,red[i],green[i],blue[i]);

	if(!(w = (struct Window *)OpenWindow(&nw)))
		errexit(" Can't open the window");

	mask |= WINDOW;

	rp = w->RPort;
}

/*
 *	Process the gadget messages
 */

do_gads(mclass,igad,win)
register ULONG mclass;
register struct Gadget *igad;
register struct Window *win;
{
	register int gadgid;
	gadgid = igad->GadgetID;

	if(mclass == GADGETUP)
	   switch (gadgid)
		{
		case WHOA:	/* Stop drawing & display elapsed time */
		   ny = YTWEAK;
		   GetSysTime(&t2);
		   showetime();
		   break;
		case GO:	/* Start drawing the current input values */
		   rot = atof(rotbuf);
		   vx = atof(vxbuf);
		   vy = atof(vybuf);
		   SetRast(rp,0);
		   RefreshWindowFrame(w);
		   col = 1;
		   SetAPen(rp,col);
		   Move(rp,CX,CY+YTWEAK);
		   inc =0;
		   cnt = 0;
		   ny = 0;
		   GetSysTime(&t1);
		   idling = FALSE;
		   break;

		default:
		  break;
		}
}

errexit(str)
register UBYTE *str;
{
	if(!from_wb)		/* started from CLI, so it's safe to */
	  printf("%s\n",str);	/* printf the error message	*/

	stage_left();
}

stage_left()
{
/*
 *	use the masked flag bits to close what we opened
 */

	if (mask & WINDOW)
		CloseWindow(w);
	if (mask & SCREEN)
		CloseScreen(screen);
#ifdef	mathi
	if (mask & MIDT)
		CloseLibrary(MathIeeeDoubTransBase);
	if (mask & MIDB)
		CloseLibrary(MathIeeeDoubBasBase);
#endif
#ifdef	mathf
	if (mask & MIDF)
		CloseLibrary(MathBase);
		CloseLibrary(MathTransBase);
#endif
#ifdef	matha
	if (mask & MIDA)
		CloseLibrary(MathIeeeDoubBasBase);
#endif
	if (mask & GRAPHICS)
		CloseLibrary(GfxBase);
	if (mask & INTUITION)
		CloseLibrary(IntuitionBase);
	exit();
}

/*
 *	A rude & crude elapsed time counter
 */

showetime()
{
	if(!idling)
	   sprintf(timebuf,"%lu.%lu seconds"
		,t2.tv_secs-t1.tv_secs,t2.tv_micro-t1.tv_micro);
	   DrawBorder(rp,&timer_brdr,0L,0L);
	   PrintIText(rp,&timer_text,0L,0L);
	   idling = TRUE;
}

struct timerequest *PrepareTimer(precision)
SHORT precision;
{

	int error;
	SHORT whichunit;

	struct MsgPort *timerport;
	struct timerequest *timermsg;
	
        timerport = CreatePort(0,0);
        if (timerport == NULL) 
		return(NULL);	/* Error during CreatePort */

	timermsg = (struct timerequest *)CreateExtIO(
					timerport,sizeof(struct timerequest));
	if (timermsg == NULL)
		{
		DeletePort(timerport);
		return(NULL);	/* Error during CreateExtIO */
		}
	
	if(precision)	/* if true, use precision timer  ( under 1 second ) */
		whichunit = UNIT_MICROHZ;
	else
		whichunit = UNIT_VBLANK;

	error = OpenDevice(TIMERNAME, whichunit, timermsg, 0);
	if (error != 0)
		{
		DeleteExtIO(timermsg,sizeof(struct timerequest));
		DeletePort(timerport);
		return(NULL);	/* Error during OpenDevice */
		}
	return(timermsg);
}

int
GetSysTime(tv)
struct timeval *tv;
{
	struct timerequest *tr;
	tr = PrepareTimer(TRUE);	/* MUST use Precise timer for this */
	if(tr == 0) return(-1);		/* non zero return says error */
 
	tr->tr_node.io_Command = TR_GETSYSTIME;
	DoIO( tr );

	tv->tv_secs = tr->tr_time.tv_secs;
	tv->tv_micro = tr->tr_time.tv_micro;

	DeleteTimer(tr);
	return(0);

}

int
DeleteTimer(tr)
struct timerequest *tr;
{
	struct MsgPort *tp;

	tp = tr->tr_node.io_Message.mn_ReplyPort;
	if(tr != 0)
	{
		CloseDevice(tr);
		DeleteExtIO(tr,sizeof(struct timerequest));
	}
	if(tp != 0)
		DeletePort(tp);
	return(0);		
}
