/*

clock.c - Progression of time

*/

/*...sincludes:0:*/
#include <stdlib.h>
#include "types.h"
#include "interrupts.h"
#include "list.h"
#include "fiber.h"
#include "clock.h"

/*...vtypes\46\h:0:*/
/*...vinterrupts\46\h:0:*/
/*...vlist\46\h:0:*/
/*...vfiber\46\h:0:*/
/*...vclock\46\h:0:*/
/*...e*/

static LIST clock_list;
static unsigned clock_ticks = 0;

/*...sclock_init:0:*/
void clock_init(void)
	{
	list_init(&clock_list);
	}
/*...e*/
/*...sclock_schedule:0:*/
void clock_schedule(CLOCK *clock, unsigned ticks, NF nf, void *nfp)
	{
	CLOCK *clock_look = list_first(&clock_list);
	clock->fiber.nf  = nf;
	clock->fiber.nfp = nfp;
	if ( clock_look == NULL )
		{
		clock->ticks = ticks;
		list_add_last(&clock_list, clock);
		}
	else if ( ticks <= clock_look->ticks )
		{
		clock->ticks = ticks;
		list_add_first(&clock_list, clock);
		clock_look->ticks -= ticks;
		}
	else
		for ( ;; )
			{
			CLOCK *clock_prev = clock_look;
			ticks -= clock_look->ticks;
			clock_look = list_next(clock_look);
			if ( clock_look == NULL )
				{
				clock->ticks = ticks;
				list_add_last(&clock_list, clock);
				return;
				}
			else if ( ticks <= clock_look->ticks )
				{
				clock->ticks = ticks;
				list_add_after(&clock_list, clock_prev, clock);
				clock_look->ticks -= ticks;
				return;
				}
			}
	}
/*...e*/
/*...sclock_tick_to_process:0:*/
static BOOLEAN clock_tick_to_process(void)
	{
	BOOLEAN process;
	interrupts_disable();
	if ( clock_ticks != 0 )
		{
		--clock_ticks;
		process = TRUE;
		}
	else
		process = FALSE;
	interrupts_enable();
	return process;
	}
/*...e*/
/*...sclock_tick_fiber:0:*/
static void clock_tick_fiber(void *nfp)
	{
	nfp=nfp; /* keep compiler happy */
	while ( clock_tick_to_process() )
		{
		CLOCK *clock;
		if ( (clock = list_first(&clock_list)) != NULL &&
		     --(clock->ticks) == 0 )
			do
				{
				list_remove_first(&clock_list);
				fiber_enqueue((FIBER *) clock);
				}
			while ( (clock = list_first(&clock_list)) != NULL &&
			        clock->ticks == 0 );
		}	
	}

static FIBER clock_fiber = { NULL, clock_tick_fiber, NULL };
/*...e*/
/*...sclock_tick:0:*/
void clock_tick(void)
	{
	interrupts_disable();
	clock_tick_isr();
	interrupts_enable();
	}
/*...e*/
/*...sclock_tick_isr:0:*/
void clock_tick_isr(void)
	{
	if ( ++clock_ticks == 1 )
		fiber_enqueue_isr(&clock_fiber);
	}
/*...e*/
