/*
    TMClock.c, Copyright  (c) by Lukas Ruf, lr@lpr.ch,
    Swiss Federal Institute of Technology,
    Computer Engineering and Networks Laboratory.
 
    TOPSY -- A Teachable Operating System.
             Implementation of a tiny and simple
             micro kernel for teaching purposes.
 
    For further information, please visit http://www.tik.ee.ethz.ch/~topsy
 
    This software is provided under the terms of the GNU General Public Licence.
 
    A full copy of the GNU GPL is provided in the file COPYING found in the 
    development root of Topsy.
*/
/*
	
	File:                  $Source: /home/ruf/cvs_Topsy/cvs/Topsy/Threads/ia32/TMClock.c,v $
 	Author(s):             Lukas Ruf, lr@lpr.ch
 	Affiliation:           ETH Zuerich, TIK
 	Version:               $Revision: 3040 $
 	Creation Date:         
 	Last Date of Change:   $Date: 2007-06-17 21:55:44 +0200 (Son, 17 Jun 2007) $      by: $Author: sryffel $
	
	
	$Log: TMClock.c,v $
	Revision 1.5  2000/11/23 11:17:48  cjeker
	fixes for ia32 Now ia32 and mips should work again
	
	Revision 1.4  2000/11/15 10:13:34  ruf
	ia32Debug and Repository Reorganisation
	
	Revision 1.3  1999/12/13 21:48:34  ruf
	GNU General Public Licence Update
	
	Revision 1.2  1999/06/06 20:55:14  jeker
	putting everything together for Topsy 2.0
	
	Revision 1.1  1999/05/13 17:05:47  jeker
	Initial revision
 
*/
#include "TMHal.h"
#include "TMHalAsm.h"
#include "IOHal.h"
#include "IODevice.h"
#include "TMScheduler.h"
#include "Support.h"
#include "TMClock.h" 

#include "SupportAsm.h"
#include "Video.h"
#include "video.h"
#include "Tools.h" 
#include "Lock.h"

typedef struct  tTimeS
{
  unsigned char s;    /* sec  00..59 */
  unsigned char m;    /* min  00..59 */
  unsigned char h;    /* std  00..23 */
  unsigned char d;    /* day  01..31 */
  unsigned char dow;  /* day of week  0..7 */
  unsigned char mon;  /* month  01..12 */
  unsigned char y;    /* year   00..255 == 1980..1980+255 */
}
tTime;

static volatile unsigned long   _TimerCalled;
static volatile unsigned char   Rotate[5] = "|/-\\";
static volatile unsigned long   Next;
static volatile tTime   Time;

char str[20];
static volatile unsigned long long clockCount, clockCountOld;
volatile unsigned long long _cpuFrequency; // In Hertz, updated realtime
volatile unsigned int _cpuFrequencyMHz; // In Mega Hertz, updated realtime
volatile unsigned int _countdown; /* Clobal var that counts down at a frequency of 1s.
						* If you want to reset this var first call lock(_countdownLock),
						* reset it to the desired value, and then call unlock(_countdownLock).
						* If you want to read _countdown, you don't need to Lock. */
LockDesc _countdownLockDesc = {FALSE};
Lock _countdownLock = &_countdownLockDesc;


int LeapYear(int pyea)
{
  return ((((pyea % 4 == 0) && (pyea % 100 != 0)) || (pyea % 400 == 0)) ? 1 : 0);
}

/* Number of Day in Year */
int NumOfDay(int pday, int pmon, int pyea)
{
  int d = 0, e = 0;
  d = (pmon + 10) / 13;
  e = pday + (611*(pmon+2))/20 - 2*d - 91;
  return (e + LeapYear(pyea)*d);
}

/* Day of Week */
unsigned char DoW(int pyea, int pNumOfDay)
{
  int j = 0, c = 0;
  j = (pyea - 1) % 100;
  c = (pyea - 1) / 100;
  return (28+j+pNumOfDay+(j/4)+(c/4)+5*c) % 7;
}

int NewMonth(unsigned char pday, unsigned char pmon, unsigned char pyea)
{
  int ldmonth[12] = {31,27,31,30,31,30,31,31,30,31,30,31};
  return (pday > (ldmonth[pmon-1]+LeapYear(pyea)*(int)(pmon==2)));
}

void DispTime()
{
  unsigned char ts[3], tm[3], th[3];
  itoa(Time.s,(char*)ts);
  itoa(Time.m,(char*)tm);
  itoa(Time.h,(char*)th);
  if (Time.h < 10)
  {
    th[1] = th[0];
    th[0] = '0';
    th[2] = 0;
  }
  if (Time.m < 10)
  {
    tm[1] = tm[0];
    tm[0] = '0';
    tm[2] = 0;
  }
  if (Time.s < 10)
  {
    ts[1] = ts[0];
    ts[0] = '0';
    ts[2] = 0;
  }
  __printstr((24*80+00)*2,0x1E,th);
  __printstr((24*80+02)*2,0x1E,(unsigned char*)":");
  __printstr((24*80+03)*2,0x1E,tm);
  __printstr((24*80+05)*2,0x1E,(unsigned char*)":");
  __printstr((24*80+06)*2,0x1E,ts);
  return;
}

void GetTime()
{
  unsigned char tadd = 0;
  Time.s  = __readRTC(0x00);
  Time.m  = __readRTC(0x02);
  Time.h  = __readRTC(0x04);
  Time.d  = __readRTC(0x07);
  Time.dow  = __readRTC(0x06);
  Time.mon  = __readRTC(0x08);
  Time.y  = __readRTC(0x09);
  if (Time.h & 0x80)
    tadd = 12;
  Time.s    = (unsigned char)BCDtoBIN((int)Time.s);
  Time.m    = (unsigned char)BCDtoBIN((int)Time.m);
  Time.h    = (unsigned char)BCDtoBIN((int)(Time.h & 0x7F));
  Time.d    = (unsigned char)BCDtoBIN((int)Time.d);
  Time.mon = (unsigned char)BCDtoBIN((int)Time.mon);
  Time.y    = (unsigned char)BCDtoBIN((int)Time.y);
  Time.h   += tadd;
  DispTime();
  return;
}

void SetEasyWatch()
{
  /* binary + 24h Format */
  unsigned char lb = __readRTC(0x0B);
  lb |=  0x02;  /* set 24h-Format */
  lb |=  0x80;  /* set RTC update disable */
  __writeRTC(0x0B,lb);
  GetTime();
  lb &=  0x7F;  /* enable update interval */
  __writeRTC(0x0B,lb);
  return;
}


void SetTime()
{
  __writeRTC(0x00,Time.s);
  __writeRTC(0x02,Time.m);
  __writeRTC(0x04,Time.h);
  return;
}

void SetDate()
{
  __writeRTC(0x06,Time.dow);
  __writeRTC(0x07,Time.d);
  __writeRTC(0x08,Time.mon);
  __writeRTC(0x09,Time.y);
  return;
}


void UpdateTime()
{
  DispTime();   /* RTC is updated by 32378Hz (AT Update Clock) */
  Time.s++;
  if (Time.s > 59)
  {
    Time.s = 0;
    Time.m++;
    if (Time.m > 59)
    {
      Time.m = 0;
      Time.h++;
      if (Time.h > 23)
      {
        Time.h = 0;
        Time.d++;
        Time.dow %= 7;
        Time.dow ++;
        if (NewMonth(Time.d,Time.mon,Time.y))
        {
          Time.d = 1;
          Time.mon++;
          if (Time.mon > 12)
          {
            Time.mon = 1;
            Time.y++;
          }
        }
      }
    }
  }
  return;
}

void TimerInit()
{
  _TimerCalled = 0;
  Next = 0;
  SetEasyWatch();
  printf("Binary Watch Handling Enabled\n");
#ifdef APIC
  ApicTimerInit();
#endif
  __endOf_IRQ(T_IR_00);   /* Reset Timer Channel 0 Interrupt */
  __enable_IRQ(T_IR_00);  /* and enable Interrupt Line 0 */
#ifdef APIC
  ioapic_enable_IRQ(T_IR_00);
#endif
  printf("Interrupt Request Line 0x00 (Timer Channel 0) started \n");
  return;
}

unsigned long TimerSeconds()
{
  return (unsigned long) TO_SECONDS(_TimerCalled);
}

unsigned long TimerCalled()
{
  return _TimerCalled;
}

void _TimerISR()
{       /* Timer Master ISR */
#ifdef APIC
  ApicTimerISR();
#endif
  if (UPDATE_COND(_TimerCalled))
  { 
    /* LR 001114 */
    //-LR 	clockCount = __getClockCount();
    _cpuFrequency = clockCount-clockCountOld;
    clockCountOld = clockCount;
    // Pay attention with future CPUs that have a frequency above 4GHz !
    _cpuFrequencyMHz = roundToMHz((unsigned int)_cpuFrequency);
    snprintf(str, sizeof(str), "  %iMHz  ", _cpuFrequencyMHz);
    __printstr((24*80+65)*2,0x1E,(unsigned char*)str);
    UpdateTime();
    lockTry(_countdownLock);
    _countdown--;
    unlock(_countdownLock);
  }
  _TimerCalled++;
  if (SPIN_COND(_TimerCalled))
    __printchar((24*80+79)*2  ,0x1D,Rotate[(Next++) % 4]);
  return;
}

unsigned int roundToMHz(unsigned int cpuFreq)
{
  int roundNum = 0;
  int cpuFreqMHz = 0;
  int LSNum = 0;

  // round to MHz
  cpuFreqMHz =  (cpuFreq+500000)/1000000;

  // Frequency must end with 0,3 or 6:
  LSNum = cpuFreqMHz - (cpuFreqMHz/10)*10;
  switch(LSNum)
  {
  case 1 :
    return cpuFreqMHz-1;
  case 2 :
    return cpuFreqMHz+1;
  case 4 :
    return cpuFreqMHz-1;
  case 5 :
    return cpuFreqMHz+1;
  case 7 :
    return cpuFreqMHz-1;
  case 8 :
    return cpuFreqMHz-2;
  case 9 :
    return cpuFreqMHz+1;
  default :
    return cpuFreqMHz;
  }
}


/* since we don't have an RTC installed we don't do anything here
 * otherwise we would calculate the seconds
 * since January 1st 1998, 00:00, UTC, and return or set them
 */
unsigned long tmRTClockGetSeconds()
{
  return 0;
}

void tmRTClockSetSeconds(unsigned long seconds, unsigned long microSeconds)
{
  return;
}

