// timer.cc 
//	Routines to emulate a hardware timer device.
//
//      A hardware timer generates a CPU interrupt every X milliseconds.
//      This means it can be used for implementing time-slicing.
//
//      We emulate a hardware timer by scheduling an interrupt to occur
//      every time stats->totalTicks has increased by TimerTicks.
//
//      In order to introduce some randomness into time-slicing, if "doRandom"
//      is set, then the interrupt is comes after a random number of ticks.
//
//	Remember -- nothing in here is part of Nachos.  It is just
//	an emulation for the hardware that Nachos is running on top of.
//
//  DO NOT CHANGE -- part of the machine emulation
//
// Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"
#include "timer.h"
#include "main.h"
#include "sysdep.h"

//----------------------------------------------------------------------
// Timer::Timer
//      Initialize a hardware timer device.  Save the place to call
//	on each interrupt, and then arrange for the timer to start
//	generating interrupts.
//
//      "doRandom" -- if true, arrange for the interrupts to occur
//		at random, instead of fixed, intervals.
//      "toCall" is the interrupt handler to call when the timer expires.
//----------------------------------------------------------------------

Timer::Timer(bool doRandom, CallBackObj *toCall)
{
    randomize = doRandom;
    callPeriodically = toCall;
    disable = FALSE;
    SetInterrupt();
}

//----------------------------------------------------------------------
// Timer::CallBack
//      Routine called when interrupt is generated by the hardware 
//	timer device.  Schedule the next interrupt, and invoke the
//	interrupt handler.
//----------------------------------------------------------------------
void 
Timer::CallBack() 
{
    // invoke the Nachos interrupt handler for this device
    callPeriodically->CallBack();
    
    SetInterrupt();	// do last, to let software interrupt handler
    			// decide if it wants to disable future interrupts
}

//----------------------------------------------------------------------
// Timer::SetInterrupt
//      Cause a timer interrupt to occur in the future, unless
//	future interrupts have been disabled.  The delay is either
//	fixed or random.
//----------------------------------------------------------------------

void
Timer::SetInterrupt() 
{
    if (!disable) {
       int delay = TimerTicks;
    
       if (randomize) {
	     delay = 1 + (RandomNumber() % (TimerTicks * 2));
        }
       // schedule the next timer device interrupt
       kernel->interrupt->Schedule(this, delay, TimerInt);
    }
}
