;/*	PerfMeter by Digital Design, Inc.

lc -v -y -ba perfmeter.c
blink lib:cback.o+perfmeter.o to perfmeter lib lib:lc.lib+lib:amiga.lib
quit

	Author:		Juha 'Comm' Tuominen / Digital Design
	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	Released:	15-Jul-91	v1.10
	Revised:	07-Sep-91	v1.11
	Revised:	08-Sep-91	v1.12
	Revised:	18-Sep-91	v1.2	(needs KS2.0 now)
	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	Compiler:	SAS/C v5.10->
	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/

#include <exec/types.h>
#include <exec/memory.h>
#include <exec/execbase.h>
#include <exec/tasks.h>
#include <libraries/dos.h>
#include <devices/timer.h>
#include <intuition/intuitionbase.h>
#include <libraries/diskfont.h>
#include <graphics/rastport.h>
#include <graphics/gfx.h>
#include <graphics/gfxmacros.h>
#include <graphics/text.h>
#include <proto/all.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#define STACK_SIZE 1000L	/* Arska's stack size */

#define NAME 		"Perfmeter"
#define VERSION		1
#define REVISION	2
#define DATE		"16.9.91"
#define VERS		"Perfmeter 1.2"
#define VSTRING		"PerfMeter 1.2 (18.9.91)\n\0"
#define VERSTAG		"\0$VER: Perfmeter 1.2 (18.9.91)\0"

#define FONTSIZE 8
#define FONTNAME "hires-5a.font"

#define WINDOWWIDTH		172
#define WINDOWHEIGTH	130

long _stack=16384;
char *_procname=VSTRING;
long _priority=5;
long _BackGroundIO=FALSE;

void MemCleanup() {}

UBYTE	versiontag[]=VERSTAG;	/* for version command */

BOOL	task_added=FALSE;
BOOL	zipped=FALSE;
BOOL	oldzipped=FALSE;
BOOL	timeropen=FALSE;
BOOL	timeropen2=FALSE;
BOOL	keepgoing=TRUE;
BOOL	time_requested=FALSE;
BOOL	time_requested2=FALSE;
ULONG	maxchip, maxfast, freechip=0, freefast=0,maxpub,freepub;
ULONG	cputime=NULL,cputemp=NULL,prevcpu=68;
ULONG	count=0;
ULONG	chipscale=0, fastscale=0, publicscale=0;
ULONG	jakaja=0,cputemp2=0;
ULONG	tag1;
char	processor[10]="68000";
char	*taskname="Arska Väijy";
APTR	stack=NULL;
WORD	cordinates[]={0,0,80,29};	/* zipped window cordinates and size */
int		cpuload=0,mode=0,oldmode=1;
int		buffer[7][151];
int		load=0;
int		switchcount=0;
int		upsecs=0,upmins=0,uphours=0,updays=0;

extern	struct ExecBase	*SysBase;
struct	IntuitionBase *IntuitionBase=NULL;
struct	Library *DiskfontBase=NULL;
struct	Window *window=NULL;
struct	IntuiText abouttext[];
struct	IntuiText proceed;
struct	GfxBase *GfxBase=NULL;
struct	MsgPort *timerport=NULL;
struct	timerequest *timermsg=NULL;
struct	MsgPort *timerport2=NULL;
struct	timerequest *timermsg2=NULL;
struct	Task *task=NULL;
struct	TextFont *textfont;

struct	NewWindow mywindow =
{	420,
	14,
	WINDOWWIDTH,
	WINDOWHEIGTH,
	0,1,
	CLOSEWINDOW | MENUPICK | NEWSIZE,
	SMART_REFRESH | WINDOWDRAG | WINDOWCLOSE | WINDOWDEPTH,
	NULL,
	NULL,
	"(Idle)",
	NULL,
	NULL,
	70,
	38,
	NULL,
	NULL,
	WBENCHSCREEN

};

struct TagItem tagitems[2]={
	{WA_Zoom,&cordinates},
	{0,}
};


struct TextAttr TOPAZ80 = {(STRPTR)"topaz.font",TOPAZ_EIGHTY,0,0};

struct IntuiText abouttext[9] =
{
	{2,1,JAM1,8, 4,(struct TextAttr *)&TOPAZ80,(UBYTE *)VERS,(struct IntuiText *)&abouttext[1]},
	{2,1,JAM1,8,14,(struct TextAttr *)&TOPAZ80,(UBYTE *)"by Juha Tuominen",(struct IntuiText *)&abouttext[2]},
	{2,1,JAM1,8,22,(struct TextAttr *)&TOPAZ80,(UBYTE *)"Copyright © 1991",(struct IntuiText *)&abouttext[3]},
	{2,1,JAM1,8,30,(struct TextAttr *)&TOPAZ80,(UBYTE *)"Digital Design, Inc.",(struct IntuiText *)&abouttext[4]},
	{2,1,JAM1,8,38,(struct TextAttr *)&TOPAZ80,(UBYTE *)" ",(struct IntuiText *)&abouttext[5]},
	{2,1,JAM1,8,46,(struct TextAttr *)&TOPAZ80,(UBYTE *)"Average 68040 load: 0   %",(struct IntuiText *)&abouttext[6]},
	{2,1,JAM1,8,54,(struct TextAttr *)&TOPAZ80,(UBYTE *)" ",(struct IntuiText *)&abouttext[7]},
	{2,1,JAM1,8,62,(struct TextAttr *)&TOPAZ80,(UBYTE *)"Arska Väijy has found",(struct IntuiText *)&abouttext[8]},
	{2,1,JAM1,8,70,(struct TextAttr *)&TOPAZ80,(UBYTE *)"0 perfect numbers.",(struct IntuiText *)NULL}
};

struct IntuiText proceed=
{
	2,1,JAM1,5,3,(struct TextAttr *)&TOPAZ80,(UBYTE *)"Continue",(struct IntuiText *)NULL
};

/* This menu isn't visually beatufull but do just fine for this program */

struct IntuiText IText1={2,1,JAM2,0,0,&TOPAZ80,"Quit",NULL};
struct MenuItem MenuItem9={NULL,0,64,58,8,ITEMTEXT+ITEMENABLED+HIGHCOMP,0,(APTR)&IText1,NULL,NULL,NULL,MENUNULL};
struct IntuiText IText2={2,1,JAM2,0,0,&TOPAZ80,"Switch",NULL};
struct MenuItem MenuItem8={&MenuItem9,0,56,58,8,ITEMTEXT+ITEMENABLED+HIGHCOMP,0,(APTR)&IText2,NULL,NULL,NULL,MENUNULL};
struct IntuiText IText3={2,1,JAM2,0,0,&TOPAZ80,"Public",NULL};
struct MenuItem MenuItem7={&MenuItem8,0,48,58,8,ITEMTEXT+ITEMENABLED+HIGHCOMP,0,(APTR)&IText3,NULL,NULL,NULL,MENUNULL};
struct IntuiText IText4={2,1,JAM2,0,0,&TOPAZ80,"Fast",NULL};
struct MenuItem MenuItem6={&MenuItem7,0,40,58,8,ITEMTEXT+ITEMENABLED+HIGHCOMP,0,(APTR)&IText4,NULL,NULL,NULL,MENUNULL};
struct IntuiText IText5={2,1,JAM2,0,0,&TOPAZ80,"Chip",NULL};
struct MenuItem MenuItem5={&MenuItem6,0,32,58,8,ITEMTEXT+ITEMENABLED+HIGHCOMP,0,(APTR)&IText5,NULL,NULL,NULL,MENUNULL};
struct IntuiText IText6={2,1,JAM2,0,0,&TOPAZ80,"Load",NULL};
struct MenuItem MenuItem4={&MenuItem5,0,24,58,8,ITEMTEXT+ITEMENABLED+HIGHCOMP,0,(APTR)&IText6,NULL,NULL,NULL,MENUNULL};
struct IntuiText IText7={2,1,JAM2,0,0,&TOPAZ80,"CPU",NULL};
struct MenuItem MenuItem3={&MenuItem4,0,16,58,8,ITEMTEXT+ITEMENABLED+HIGHCOMP,0,(APTR)&IText7,NULL,NULL,NULL,MENUNULL};
struct IntuiText IText8={2,1,JAM2,0,0,&TOPAZ80,"Idle",NULL};
struct MenuItem MenuItem2={&MenuItem3,0,8,58,8,ITEMTEXT+ITEMENABLED+HIGHCOMP,0,(APTR)&IText8,NULL,NULL,NULL,MENUNULL};
struct IntuiText IText9={2,1,JAM2,0,0,&TOPAZ80,"About",NULL};
struct MenuItem MenuItem1={&MenuItem2,0,0,58,8,ITEMTEXT+ITEMENABLED+HIGHCOMP,0,(APTR)&IText9,NULL,NULL,NULL,MENUNULL};

struct Menu Menu1={NULL,0,0,62,0,	MENUENABLED,"Project",&MenuItem1};



void cleanexit(int error);
void openthings(void);
void addtimerequest(long secs, long micros);
void changefont(void);
void addtimerequest2(long secs, long micros);
void uptime(void);
ULONG maxmemsize(long memtype);
void updatewindow(void);
void oddstuff(void);
void idletask(void);
void __saveds switchroutine(void);

void CXBRK(void) 
{ 
	cleanexit(0);
}

void cleanexit(int error)
{
	if(task_added) RemTask(task);
	if(task) FreeMem(task,sizeof(struct Task));
	if(stack) FreeMem(stack,STACK_SIZE);
	if(timeropen2)
	{	if(time_requested2)
		{
			AbortIO((struct IORequest *)timermsg2);
			WaitIO((struct IORequest *)timermsg2);
		}
		CloseDevice((struct IORequest *)timermsg2);
	}
	if(timermsg2) DeleteExtIO((struct IORequest *)timermsg2);
	if(timerport2) DeletePort(timerport2);

	if(timeropen)
	{	if(time_requested)
		{
			AbortIO((struct IORequest *)timermsg);
			WaitIO((struct IORequest *)timermsg);
		}
		CloseDevice((struct IORequest *)timermsg);
	}
	if(timermsg) DeleteExtIO((struct IORequest *)timermsg);
	if(timerport) DeletePort(timerport);
	if(window->MenuStrip) ClearMenuStrip(window);
	if(window) CloseWindow(window);
	if(DiskfontBase) CloseLibrary(DiskfontBase);
	if(GfxBase) CloseLibrary(GfxBase);
	if(IntuitionBase) CloseLibrary((struct Library *)IntuitionBase);
	XCEXIT(error);
}


void openthings(void)
{
	int i,j;

	if (SysBase->LibNode.lib_Version<37)
		cleanexit(99);
	if (!(GfxBase=(struct GfxBase *)OpenLibrary("graphics.library",37L)))
		cleanexit(30);
	if (!(IntuitionBase=(struct IntuitionBase*)OpenLibrary("intuition.library",37L)))
		cleanexit(30);
	if (!(DiskfontBase=(struct Library*)OpenLibrary("diskfont.library", 37L)))
		cleanexit(30);
	if(!(timerport=CreatePort(0,0)))
		cleanexit(22);
	if(!(timermsg=(struct timerequest *) CreateExtIO(timerport, sizeof(struct timerequest))))
		cleanexit(22);
	if(OpenDevice("timer.device", UNIT_VBLANK, ((struct IORequest *) timermsg), 0))
		cleanexit(22);

	timeropen=TRUE;

	if(!(timerport2=CreatePort(0,0)))
		cleanexit(22);
	if(!(timermsg2=(struct timerequest *) CreateExtIO(timerport2, sizeof(struct timerequest))))
		cleanexit(22);
	if(OpenDevice("timer.device", UNIT_VBLANK, ((struct IORequest *) timermsg2), 0))
		cleanexit(22);

	timeropen2=TRUE;

	if(!(window=OpenWindowTagList(&mywindow,(struct TagItem *)&tagitems)))
		cleanexit(27);
	SetMenuStrip(window,&Menu1);

	if(!(stack=AllocMem(STACK_SIZE, MEMF_CLEAR)))
		cleanexit(10);
	if(!(task=(struct Task*)AllocMem(sizeof(struct Task),MEMF_CLEAR|MEMF_PUBLIC)))
		cleanexit(10);

	maxchip=maxmemsize(MEMF_CHIP);
	maxfast=maxmemsize(MEMF_FAST);
	maxpub=maxchip+maxfast;

	if(SysBase->AttnFlags & AFF_68010)	strcpy(processor,"68010");
	if(SysBase->AttnFlags & AFF_68020)	strcpy(processor,"68020");
	if(SysBase->AttnFlags & AFF_68030)	strcpy(processor,"68030");
	if(SysBase->AttnFlags & AFF_68040)	strcpy(processor,"68040");

	/*	Because we run this part only once we just have to assume that user 
		does not add memory cartiges or change the main processor while
		this program is running :)
	*/

	chipscale=maxchip/100;
	fastscale=maxfast/100;
	publicscale=maxpub/100;

	/* init buffer */
	for(i=0;i<6;i++)
	{
		for(j=0;j<150;j++)
			buffer[i][j]=0;
	}
}


void changefont(void)
{
	struct	TextAttr	textattr;
	struct	TextFont	*textfont;

	textattr.ta_Name=FONTNAME;;
	textattr.ta_YSize=FONTSIZE;
	textattr.ta_Style=FS_NORMAL;
	textattr.ta_Flags=FPF_ROMFONT | FPF_DISKFONT;

	if(textfont=(struct TextFont *)OpenDiskFont(&textattr))
	{	SetFont(window->RPort,textfont);
		if(!window->RPort->TxHeight==8)
		{	cleanexit(10);
		}
	}
	else
	{	cleanexit(10);
	}
}

void addtimerequest(long secs, long micros)
{
	time_requested=TRUE;
	timermsg->tr_node.io_Command=TR_ADDREQUEST;
	timermsg->tr_time.tv_secs=secs;
	timermsg->tr_time.tv_micro=micros;
	SendIO((struct IORequest *)timermsg);
}

void addtimerequest2(long secs, long micros)
{
	time_requested2=TRUE;
	timermsg2->tr_node.io_Command=TR_ADDREQUEST;
	timermsg2->tr_time.tv_secs=secs;
	timermsg2->tr_time.tv_micro=micros;
	SendIO((struct IORequest *)timermsg2);
}

void uptime(void)
{	char temp[30];

	if(++upsecs>60)
	{	upsecs=0;
		if(++upmins>60)
		{	upmins=0;
			if(++uphours>24)
			{	uphours=0;
				updays++;
			}
		}
	}
	if(!zipped)
	{	SetDrMd(window->RPort,JAM2);
		SetBPen(window->RPort,0);
		SetAPen(window->RPort,1);
		Move(window->RPort,25,123);
		sprintf(temp,"Uptime: %02d days  %02d:%02d:%02d",updays,uphours,upmins,upsecs);
		Text(window->RPort,temp,25);
	}
}

ULONG maxmemsize(long memtype)
{
	ULONG blocksize=0;
	struct MemHeader *MemHeader;

	Forbid();
		for(MemHeader=(struct MemHeader *)SysBase->MemList.lh_Head;MemHeader->mh_Node.ln_Succ;MemHeader=(struct MemHeader *)MemHeader->mh_Node.ln_Succ)
		{
			if(MemHeader->mh_Attributes&memtype)
				blocksize+=((ULONG)MemHeader->mh_Upper-(ULONG)MemHeader->mh_Lower);
		}
	Permit();

	return(blocksize);

	/* MaxMemSize() by Louis A. Mamakos. */
}


void updatewindow(void)
{
	int i,j;
	ULONG	curchip, curfast, curpub;
	struct RastPort *rp=window->RPort;

	if(mode!=oldmode || oldzipped!=zipped)	/* User has decided to look some other item or window zip gadget is pressed*/
	{	if(!zipped)	/* No use to refresh the window if it's zipped */
		{	RefreshWindowFrame(window);
			SetAPen(rp,0);
			RectFill(rp,10,15,160,110);

			/* 3D border */
			SetAPen(rp,1);
			Move(rp,10,111);
			Draw(rp,10,16);
			Draw(rp,161,16);
			SetAPen(rp,2);
			Draw(rp,161,111);
			Draw(rp,11,111);

			/* Scaling lines */
			SetAPen(rp,3);
			Move(rp,11,38);
			Draw(rp,160,38);
			Move(rp,11,64);
			Draw(rp,160,64);
			Move(rp,11,90);
			Draw(rp,160,90);

			/* Draw existing GFX from buffer */
			Move(rp,10,110-buffer[mode][0]);
			SetAPen(rp,1);
			for(j=1;j<=150;j++)
				Draw(rp,j+10,110-buffer[mode][j]);
		}
		oldmode=mode;
		oldzipped=zipped;
	}

	/* let's scroll a bit, OK? */
	for(i=0;i<7;i++)
	{
		for(j=0;j<150;j++)
			buffer[i][j]=buffer[i][j+1];
	}
	curchip=AvailMem(MEMF_CHIP);
	curfast=AvailMem(MEMF_FAST);
	curpub=AvailMem(MEMF_PUBLIC);

	buffer[0][150]=(UBYTE)(100*cputemp/60);
	buffer[1][150]=load;
	buffer[2][150]=(UBYTE)(curchip/chipscale);
	if(fastscale)
		buffer[3][150]=(UBYTE)(curfast/fastscale);
	buffer[4][150]=(UBYTE)(curpub/publicscale);
	buffer[5][150]=switchcount;
	buffer[6][150]=(UBYTE)(83-(100*cputemp/60));
	if(buffer[6][150]<0) buffer[6][150]=0;


	for(i=0;i<7;i++)
	{	if(buffer[i][150]>93)
		{	buffer[i][150]=93;
		}
	}

	if(!zipped)
	{	ScrollRaster(rp,1,0,11,17,160,110);
		SetDrMd(rp,JAM1);
		SetAPen(rp,3);
		WritePixel(rp,160,38);
		WritePixel(rp,160,64);
		WritePixel(rp,160,90);
		SetAPen(rp,1);
		Move(rp,159,110-buffer[mode][149]);
		Draw(rp,160,110-buffer[mode][150]);
	}
}




void idletask(void)	/*	This is Arska Väijy */
{
	ULONG suurinluku=1,taydellinenluku,luku;

	Disable();
		task->tc_Switch=switchroutine;
		task->tc_Flags|=TF_SWITCH;
	Enable();

	while(1)
	{	taydellinenluku=0;
		for(luku=suurinluku;luku>1;luku--)
		{
			if(!(suurinluku%luku))
				taydellinenluku+=luku;
		}
		if(taydellinenluku==suurinluku)
			count++;
		suurinluku++;
	}

	/* As you know, there may not be busy looped parts in multitasking
	   programs :) */
}

void __saveds switchroutine(void)
{
	/*	Thanks to Juhani Vehviläinen for sweatting with me because of
		this idle counting routine */

	cputime+=SysBase->Quantum-SysBase->Elapsed;
	switchcount++;
}


void _main(char *arguments)
{
	ULONG	timersignal, 
			timersignal2,
			idcmpsignal, 
			signals, 
			class;
	UWORD	code, 
			qualifier;
	struct	IntuiMessage *msg=NULL;
	char	temp1[80],temp2[80];
	struct	Node *mynode;
	ULONG	sum1=0;
	int		pos1=0,pos=0,last[61],length=0,i,j=0,argumentcount=0;
	

	/* handle arguments */
	for(i=0;i<strlen(arguments);i++)
	{	if(arguments[i]==32 || arguments[i]==13 || arguments[i]==10)
		{	if(!strncmp(temp1,"-x",2))
				mywindow.LeftEdge=atoi(temp1+2);
			if(!strncmp(temp1,"-y",2))
				mywindow.TopEdge=atoi(temp1+2);
			argumentcount++;
			j=0;
		}
		else
		{	temp1[j++]=arguments[i];
			temp1[j]=0;
		}
	}

	openthings();
	changefont();

    for(i=0;i<60;i++)
		last[i]=0;

	timersignal = 1L << timerport->mp_SigBit;
	timersignal2= 1L << timerport2->mp_SigBit;
	idcmpsignal = 1L << window->UserPort->mp_SigBit;

	addtimerequest(1,0);
	addtimerequest2(0,10);

	task->tc_Node.ln_Type=NT_TASK;
	task->tc_Node.ln_Name=taskname;
	task->tc_Node.ln_Pri=-128;
	task->tc_SPLower=(APTR)stack;
	task->tc_SPUpper=(APTR)(STACK_SIZE+(ULONG)stack);
	task->tc_SPReg=task->tc_SPUpper;

	AddTask(task,idletask,0L);
	task_added=TRUE;

	while(keepgoing)
	{	signals = Wait(timersignal | timersignal2 | idcmpsignal);

		if(signals & idcmpsignal)
		{
			while(msg=(struct IntuiMessage *)GetMsg(window->UserPort))
			{
				class=msg->Class;
				code=msg->Code;
				qualifier=msg->Qualifier;
				ReplyMsg((struct Message *) msg);

				switch(class)
				{
					case CLOSEWINDOW:
						keepgoing=FALSE;
						break;
					case NEWSIZE:
						if(window->Width==WINDOWWIDTH && window->Height==WINDOWHEIGTH)
							zipped=FALSE;
						else
						{	zipped=TRUE;
							SetAPen(window->RPort,0);
							RectFill(window->RPort,10,15,75,26);
							Move(window->RPort,10,21);
							SetAPen(window->RPort,1);
							Text(window->RPort,"..zZzZz..",9);
						}
						break;
					case MENUPICK:
						switch(ITEMNUM(code))
						{
							case 0:
								cputime=100-cpuload;
								if(cputime<1 || cputime>100)
									cputime=20;
								sprintf(temp1,"Average %s load: %d %% ",processor,cputime);
								cputime=0;
								abouttext[5].IText=(UBYTE *)temp1;
								sprintf(temp2,"%d perfect numbers.",count);
								abouttext[8].IText=(UBYTE *)temp2;
								AutoRequest(NULL,&abouttext[0],NULL,&proceed,NULL,NULL,272,128);
								break;
							case 1:
								mode=0;
								window->Title="(Idle)";
								break;
							case 2:
								mode=6;
								window->Title="(CPU)";	
								break;
							case 3:
								mode=1;
								window->Title="(Load)";
								break;
							case 4:
								mode=2;
								window->Title="(Chip)";
								break;
							case 5:
								mode=3;
								window->Title="(Fast)";
								break;
							case 6:
								mode=4;
								window->Title="(Public)";
								break;
							case 7:
								mode=5;
								window->Title="(Switch)";
								break;
							case 8:
								cleanexit(0);
						}
						break;
				}
			}
		}

		if(signals & timersignal)
		{
			GetMsg(timerport);
			addtimerequest(1,0);
			uptime();
			if(100*cputime/60<100)
				cputemp=cputime;
			cputemp2+=100*cputime/60;
			cputime=0;
			cpuload=cputemp2/++jakaja;
			length = 0;
			Disable();
				for(mynode=(SysBase->TaskReady).lh_Head;mynode=mynode->ln_Succ;length++)
				    ;
			Enable();
			sum1+=(length-last[pos1]);
			last[pos]=length;
			if(++pos1==60)	pos1=0;
			if(++pos==60)	pos=0;
			load=(sum1*100+50)/480;
			if(load>100)

				load=100;
			updatewindow();
			switchcount=0;
		}
		if(signals & timersignal2)
		{
			GetMsg(timerport2);
			addtimerequest2(0,8);
		}
	}
	cleanexit(0);
}
