/* Copyright 1984 by the Massachusetts Institute of Technology */

/* Routines to implement timers.
 * Two machine dependent routines are needed to make the timers work.
 * 1 - mdt_init()	Initializes the interrupt vector and anything else
 * 			that might need to be done to make the timers work.
 * 2 - mdt_int()	A routine to receive the timer interrupt and call
 *			time_int().
 */

#include	<types.h>
#include	<sys.h>
#include	<sysext.h>
#include	<params.h>
#include	<timer.h>

#define	NULL	0
#define N1 9835
#define D1 10000
#define N5 99667
#define D5 100000
#define N15 99889
#define D15 100000

timer	tmblks[NUMTIMR];
timer	*nxttme;
int	num_timers;
int	max_timers;
ext int num_tsks, av1_tsks, av5_tsks, av15_tsks;

/* Routine to initialize the timer package.
 */
time_init()
{
	int	i;

/*	printf("TIMEINIT:\n"); */
	nxttme = NULL;
	num_timers = max_timers = 0;
	systod = 0;
	for (i = 0; i < NUMTIMR; i++) {
		tmblks[i].t_flags = 0;
		tmblks[i].t_link = NULL;
	}
	mdt_init();
}

/* This routine gets called from interrupt level at each tick.
 */
time_int()
{
	static	int	tick_count = TICKS;
	static	intf	tcnt = 0;
	reg timer	*tblk;

	systick++;
	if (tick_count == 0) {
		while (1) {
			tblk = nxttme;
			if (tblk == NULL) break;
			if (tblk->t_time != systod) break;
			nxttme = nxttme->t_link;
			num_timers--;
			addtsk(syshnd, tblk->t_priority, tblk->t_routine,
								tblk->t_data);
			if (tblk->t_flags & REPEAT) {
				tblk->t_time += tblk->t_length;
				time_insert(tblk);
			}
			else {
				tblk->t_flags = 0;
				tblk->t_link = NULL;
			}
		}
		systod++;
		tick_count = TICKS;

		/* Compute load averages */
		tcnt *= 1000;
		av1_tsks = ((N1 * av1_tsks) + ((D1-N1) * tcnt)) / D1;
		av5_tsks = ((N5 * av5_tsks) + ((D5-N5) * tcnt)) / D5;
		av15_tsks = ((N15 * av15_tsks) + ((D15-N15) * tcnt)) / D15;
		tcnt = 0;
	}
	else	tick_count--;

	/* Count tasks/second */
	tcnt += num_tsks;
}

/* This routine sets a timer to go off in time seconds.
 */
stime(routine, data, delay, priority)
void	(*routine)();			/* routine to call */
word	data;				/* data to pass the routine */
unsw	delay;				/* number of seconds to wait */
unsb	priority;			/* priority to run the task at */
{
	reg int	i;
	reg timer *tmblk;
	reg int	imask;

	imask = disable();
	for (i = 0; i < NUMTIMR; i++) {
		if (!(tmblks[i].t_flags & INUSE)) {
			tmblk = &tmblks[i];
			tmblk->t_time = systod + delay;
			tmblk->t_flags = INUSE;
			tmblk->t_routine = routine;
			tmblk->t_data = data;
			tmblk->t_priority = priority;
			time_insert(tmblk);
			enable(imask);
			return;
		}
	}
	bughalt("STIME: No more timer blocks.");
}

/* This routine sets up a repeating timer.
 */
itime(routine, data, delay, priority)
int	(*routine)();			/* routine to call */
word	data;				/* data to pass the routine */
unsw	delay;				/* number of seconds to wait */
unsb	priority;			/* priority to run the task at */
{
	reg int	i;
	reg timer *tmblk;
	reg int	imask;

	imask = disable();
	for (i = 0; i < NUMTIMR; i++) {
		if (!(tmblks[i].t_flags & INUSE)) {
			tmblk = &tmblks[i];
			tmblk->t_time = systod + delay;
			tmblk->t_length = delay;
			tmblk->t_flags = INUSE | REPEAT;
			tmblk->t_routine = routine;
			tmblk->t_data = data;
			tmblk->t_priority = priority;
			time_insert(tmblk);
			enable(imask);
			return;
		}
	}
	bughalt("ITIME: No more timer blocks.");
}

/* This routine inserts a timer block at the right point in the timer list.
 * The timer list is kept ordered by when the event is to happen with the
 * first event pointed to by nxttme.
 */
time_insert(tblk)
timer	*tblk;				/* timer to insert */
{
	reg int		time;
	reg timer	**current;
	reg timer	*look_ahead;
	
	num_timers++;
	if (num_timers > max_timers) max_timers = num_timers;
	time = tblk->t_time;
	current = &nxttme;
	look_ahead = nxttme;
	while (look_ahead != NULL && look_ahead->t_time < time) {
		current = &look_ahead->t_link;
		look_ahead = look_ahead->t_link;
	}
	*current = tblk;
	tblk->t_link = look_ahead;
}
