/*************************************************************************
*
*	Fuz.c - a Double-Precision Math Demo by David Milligan.
*
*	Copyright (C) 1987 by MicroBotics, Inc. and David Milligan.
*	All rights reserved. Permission is granted to reproduce and
*	redistribute on a strictly non-profit basis provided all
*	copyright messages and source comments remain intact.
*
**************************************************************************
*			Fuzzy Numbers on the Amiga
*
*	and you can blame it on David Milligan who wrote it for
*	MicroBotics to demonstrate the use of the MC68881 coprocessor
*	on their MultiFunction Module for the StarBoard2 (they're great!).
*	This program & it's mutants do that very thing, albeit it rather
*	'fuzzily', which reflects upon my math prowess and unfortunately
*	my programming prowess. I'm trying dammit!
*
*	The Fuzzy algorithm in this program is actually a lot of fun to
*	play with. It generates some incredibly convoluted shapes, some
*	that are quite pretty. To really appreciate it tho', try running
*	a version that doesn't use the '881 - it gets boring very fast.
*	Also, the Fuzzy algorithm I used in this program is actually a
*	port from an old Atari 800 BASIC program called 'wheels', a fun
*	but very,very slow graphics demo.
*	   Fuz works off of three input variable - rot or rotation factor,
*	vx and vy which are the X & Y vectors. The values that give the
*	best results are floating point numbers of course. Use a value of
*	.1 or less for rot, vy generally likes bigger #'s like 599.9893,
*	and vx can be anything including 0 up to the 9 character limit on
*	the input buffer. - when it's started it'll plot the default values
*	I put in, and takes 20 seconds (roughly) to draw. I chose these
*	values because they're a good example of the type of numerical
*	input you should try. It's a toy. Play with it.
*
*	  I compiled this using Manx Aztec C 3.40b. This version of Manx
*	has some pretty good support libraries for quick & dirty access to
*	the IEEE double precision math function using the 68881. The basic
*	process is this makefile for Fuz below.
*
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cut here ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

#######################################################################
#			Makefile for Fuz.c
#	Fuz, Ffuz, Ftrans, and RedFuz are copyright (C) 1987 
#	by MicroBotics, Inc. and David Milligan. All rights reserved.
#	Permission is granted to reproduce and redistribute on a
#	strictly non-profit basis. 
#######################################################################
CFLAGS	=	+fi +L -S
LFLAGS	=	-lmi32 -lc32
OBJ	=	Fuz.o

fuz:		$(OBJ)
		ln -o Fuz $(OBJ) $(LFLAGS)

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cut here ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
*	The main things are to compile using the +fi option, and then link
*	using either mi.lib or mi32.lib math libraries. The mi libs support
*	the MC68881 as a peripheral device whereas the m8 libs are for in-line
*	code for a 68020/68881 machine.
*
*	Lattice 3.10 users can compile & link Fuz by using the script
*	files Fuz.cmd & Fuz.lnk. Have both scripts in the same directory
*	as Fuz.c and from CLI type:   Execute Fuz.cmd
*	The file mathieeedoubtrans_lib.lib used in Fuz.lnk is the Lattice
*	compatible double-precision transcendental math functions for the
*	Lattice linker, Blink. The other double-precision math functions
*	are in amiga.lib.
*

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cut here ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
;
;	Fuz.cmd
;
LC -cw -dLATTICE -v Fuz
blink with Fuz.lnk

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cut here ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

;
;	Fuz.lnk
;
FROM LIB:c.o+Fuz.o
TO Fuz
LIB *
LIB:mathieeedoubtrans_lib.lib,*
LIB:lcmieee.lib,*
LIB:lcm.lib,*
LIB:lc.lib,*
LIB:amiga.lib

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cut here ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
*
*	If you have trouble compiling my 68881 demos, I can be reached
*	via Email on the following systems. I'll gladly give you a hand.
*	I'd also be interested in seeing other programs that use the
*	MFM & 68881.
*
*		BIX	-	dmilligan
*		CI$	-	70707,2521
*		PLINK	-	Dmil
*		WELL	-	dmil
*
*			David Milligan, for MicroBotics 
*
****************************************************************************/

/*
 *   the following lines should be included in any mathchip-using
 *   program made with a compiler that "understands" the mathchip.
 *   (IE: when you use Manx v3.40b's "mi" libraries.)
 */

#ifdef LATTICE
#define sin IEEEDPSin	/* Lattice normally doesn't handle these functions */
#define cos IEEEDPCos	/* so we have to send the calls specifically to the */
#define fabs IEEEDPAbs	/* mathieeedoub???.library files */
#endif

#include <math.h>	/* this declares all the math functions as double */

long MathIeeeDoubBasBase;
long MathIeeeDoubTransBase;

double atof();		/* declare any other math functions */
double fabs();

/*
 *   then follows the rest of the program
 */

#include <exec/types.h>
#include <intuition/intuition.h>
#include <devices/timer.h>
#include <stdio.h>

#define INTUITION	0x00000001
#define GRAPHICS	0x00000002
#define SCREEN		0x00000004
#define WINDOW		0x00000008
#define MIDB		0x00000010
#define MIDT		0x00000020
#define NS_MODE		HIRES|LACE
#define NS_TYPE		CUSTOMSCREEN
#define NS_WIDTH	640
#define NS_HEIGHT	400
#define NS_PLANES	4
#define	NW_IDCMP	MOUSEBUTTONS|GADGETDOWN|GADGETUP|CLOSEWINDOW
#define	NW_FLAGS	BORDERLESS|BACKDROP|ACTIVATE|WINDOWCLOSE
#define LINEOTYPE	12

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

#define PI ((DOUBLE)3.14159265358979323846)
#define DEG	(PI/80)

#define CX	(NS_WIDTH/2)
#define CY	(NS_HEIGHT/2)
#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];

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,NS_HEIGHT-LINEOTYPE-13, &Toyfont, &timebuf, NULL };
struct Border timer_brdr =
	{ 5,NS_HEIGHT-LINEOTYPE-15, 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, NS_WIDTH, NS_HEIGHT,
	NS_PLANES, 0, 0,
	NS_MODE, NS_TYPE,
	&Toyfont,NULL, NULL
	};

struct NewWindow nw =
	{
	0, LINEOTYPE, NS_WIDTH, NS_HEIGHT-LINEOTYPE, 7, 0,
	NW_IDCMP,
	NW_FLAGS,
	&toy_gad[4], NULL,
	"               MicroBotics' Fuzzy Number Plotter, by Dmil",
	NULL, NULL, 0, 0, 0, 0, NS_TYPE
	};

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;

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

	mask |= MIDT;

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

	mask |= MIDB;

	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);
	if (mask & MIDT)
		CloseLibrary(MathIeeeDoubTransBase);
	if (mask & MIDB)
		CloseLibrary(MathIeeeDoubBasBase);
	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);		
}
