/*
Copyright (C) 2015-2019 The University of Notre Dame
This software is distributed under the GNU General Public License.
See the file LICENSE for details.
*/

#include "interrupt.h"
#include "clock.h"
#include "ioports.h"
#include "process.h"

// Minimum PIT frequency is 18.2Hz.
#define CLICKS_PER_SECOND 20
#define CLICKS_MILLITIME 1/CLICKS_PER_SECOND

#define TIMER0		    0x40
#define TIMER_MODE	  0x43
#define SQUARE_WAVE   0x36
#define TIMER_FREQ	  1193182
#define TIMER_COUNT	  (((unsigned)TIMER_FREQ)/CLICKS_PER_SECOND)

static uint32_t clicks = 0;
static uint32_t seconds = 0;

static struct list queue = { 0, 0 };

static unsigned long read_pit_counter(void) {
	unsigned long count = 0;
	// Disable interrupts
	// cli();
	// al = channel in bits 6 and 7, remaining bits clear
	outb(0b0000000,TIMER_MODE);
	
	count = inb(TIMER0);		// Low byte
	count |= inb(TIMER0)<<8;		// High byte
	
	return count;
}

static void clock_interrupt(int i, int code)
{
	clicks++;
	process_wakeup_all(&queue);
	if(clicks >= CLICKS_PER_SECOND) {
		clicks = 0;
		seconds++;
		process_preempt();
	}
}

clock_t clock_read()
{
	clock_t result;
	result.seconds = seconds;
	result.millis = 1000 * clicks / CLICKS_PER_SECOND;
	return result;
}

clock_t clock_diff(clock_t start, clock_t stop)
{
	clock_t result;
	if(stop.millis < start.millis) {
		stop.millis += 1000;
		stop.seconds -= 1;
	}
	result.seconds = stop.seconds - start.seconds;
	result.millis = stop.millis - start.millis;
	return result;
}

void clock_wait(uint32_t millis)
{
	clock_t start, elapsed;
	uint32_t total;

	start = clock_read();
	do {
		process_wait(&queue);
		elapsed = clock_diff(start, clock_read());
		total = elapsed.millis + elapsed.seconds * 1000;
	} while(total < millis);
}

#ifdef KERNEL_CLOCK_EXT
  #include "kernel_clock_ext.c"
#endif

void clock_init()
{
	outb(SQUARE_WAVE, TIMER_MODE);
	outb((TIMER_COUNT & 0xff), TIMER0);
	outb((TIMER_COUNT >> 8) & 0xff, TIMER0);

	interrupt_register(32, clock_interrupt);
	interrupt_enable(32);


#ifdef KERNEL_CLOCK_EXT
  CLOCKINITMESSAGE
#else
	printf("clock: ticking %d %% %d\n",read_pit_counter(),TIMER_COUNT);
#endif
}