
/*

Program:	Idle Led
Version:	2.0a	26/10/93
Author:		Lindsay Meek (meek@fizzy.csu.murdoch.edu.au)

Description:	Whenever the CPU is busy, the power led goes on else it is off.

		This is the second version that operates by attaching itself
		to the vertical blanking interrupt. A low priority (-127)
		task is active which increments a counter and turns off the
		light. When this task does not run, the vblank irq detects
		the non-incrementing counter and turns the light on.

		This version is more system friendly than 1.0

		The CPU must be busy for more than 1 vbi for the
		light to activate
*/

#include <exec/nodes.h>
#include <exec/tasks.h>
#include <exec/interrupts.h>
#include <hardware/cia.h>
#include <hardware/custom.h>
#include <hardware/intbits.h>
#include <libraries/dos.h>

struct Task *FindTask(char *);

extern void IdleIrq(void);

/* various counters used to detect the idle state in the vblank */
ULONG idle_vbi_cnt_limit[3];

/* Run this as a process. It aborts when it detects a CTRL_C signal */
main(int argc,char **argv)
{
	struct Task *met;
	struct Interrupt mei;
	BYTE oldpri;

	mei.is_Node.ln_Type = NT_INTERRUPT;
	mei.is_Node.ln_Pri  = -127;
	mei.is_Node.ln_Name = "Idle-Led-Off";
	mei.is_Data = (APTR)idle_vbi_cnt_limit;
	mei.is_Code = IdleIrq;

	/* reset counter,last state */

	AddIntServer(INTB_VERTB, &mei);		/* Install IRQ server */

	met=FindTask(NULL);		/* Fetch this tcb */
	oldpri=met->tc_Node.ln_Pri;	/* Save old priority */
	met->tc_Node.ln_Pri=40;		/* Raise priority for calibration */

	idle_vbi_cnt_limit[0]=0;
	idle_vbi_cnt_limit[1]=0;
	idle_vbi_cnt_limit[2]=0;

	/* wait for next vbi */
	while(idle_vbi_cnt_limit[0]==0) ;

	/* count # of increments in one frame (for calibration) */
	idle_vbi_cnt_limit[0]=0;
	idle_vbi_cnt_limit[2]=0;
	while(idle_vbi_cnt_limit[0] == 0) 
		idle_vbi_cnt_limit[2]++;

	/* > 50 % load is busy */
	idle_vbi_cnt_limit[2] /= 2;

	met->tc_Node.ln_Pri=-127;	/* Make priority VERY low for idle */

	/* Busy loop for eating idle cpu cycles */
	while((met->tc_SigRecvd & SIGBREAKF_CTRL_C)==0)	/* Abort on CTRL_C */
	{
		/* Increment idle counter */
		idle_vbi_cnt_limit[1]++;
	}

	RemIntServer(INTB_VERTB,&mei);			/* Remove IRQ server */

	met->tc_Node.ln_Pri=oldpri;			/* Change priority back */

	/* Turn on power light */
	ciaa.ciapra &= -1-CIAF_LED;

	return 0;
}

#asm

;
;Assembly section for vertical blanking interrupt server
;
;You may have to put this into a seperate .asm file and compile to get it working
;with your C compiler
;

	include	"hardware/cia.i"

_ciaa	=	$bfe001		;i defined it here so i could use the small model!

	public	_IdleIrq

_IdleIrq:

	addq.l	#1,(a1)+

	move.l	(a1),d0		;idle_cnt
	clr.l	(a1)+		;=0
	cmp.l	(a1),d0		;>=idle_limit
	bge.s	_IdleIrq1	;yes - then its idle
	bclr.b	#CIAB_LED,_ciaa+ciapra	;turn on power light (cpu is busy)
	moveq	#0,d0		;permit other servers to execute
	rts			

_IdleIrq1:

	bset.b	#CIAB_LED,_ciaa+ciapra	;turn off power light (cpu is idle)
	moveq	#0,d0		;permit other servers to execute
	rts

#endasm
