/*
================================ COPYRIGHT ================================
The contents of this file are subject to the VTech Informations Ltd. License
of VT-OS Ver. 1.1 operating system (the "License"); you may not use this
file except in compliance with the License.

Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied.  See the License
for the specific language governing rights and limitations under the License.
  
The Original Code is VT-OS Ver. 1.1 Operating System, released
on October 1st, 1999
	
The Initial Developer of the Original Code is VTech Informations Ltd.  All
codes are Copyright (C) VTech Informations Ltd. 1999.  All Rights Reserved.
===========================================================================
*/

//*	----------------------------------------------------------------------------
//*	History:
//*	May.19,2000	Kam		- Modify timer quantize from 10 or 50 ms to 5, 10 or 50 ms

/*
===========================================================================
File        :   tmrapi.c
Author(s)   :   Thomas Cheng
Company     :   VTech Informations Ltd.
Project     :   Helio
Date:	    :   October 1st, 1999
Purpose:	:   timer module
Revision    :   1.1
Note        :   None
===========================================================================
*/




#include "stdafx.h"
#include "platform.h"
#ifdef PR31700
#include "intc.h"
#include "clock.h"
#include "pr31700s.h"
#include "pr31700c.h"
#endif
#include "eventmgr.h"
#include "msg.h"
#include "TmrApi.h"
#include "alarmgr.h"
#include "str.h"
#include "dev_pwr.h"
#include "sylang.h"

//#define	DEBUG							//_dbg define this to enable printf debug
//#define	PIN_DEBUG		(1 << 13)		// define this to enable pin debug

#ifdef	PIN_DEBUG	////////////////////////////////////////////////////////////

#define	DebugPinInit()	\
	CPU->REG_MFIO_SEL |= PIN_DEBUG;	\
	CPU->REG_MFIO_DATA_OUT &= ~(PIN_DEBUG);	\
CPU->REG_MFIO_DIRECTN |= PIN_DEBUG
#define	DebugSetPin()		CPU->REG_MFIO_DATA_OUT |= PIN_DEBUG
#define	DebugClearPin()		CPU->REG_MFIO_DATA_OUT &= ~PIN_DEBUG
#define	DebugTogglePin()	CPU->REG_MFIO_DATA_OUT ^= PIN_DEBUG

#else	////////////////////////////////////////////////////////////////////////

#define	DebugPinInit()
#define	DebugSetPin()
#define	DebugClearPin()
#define	DebugTogglePin()

#endif	PIN_DEBUG	////////////////////////////////////////////////////////////

#ifdef	DEBUG
#ifdef	PC_SIM
#define	VTRACE			TRACE
#else
#define	VTRACE			printf
#endif	PC_SIM
#else
#define	VTRACE
#endif	DEBUG

#define TMRBASE50MS		57600
#define TMRBASE10MS		(TMRBASE50MS / 5)
#define TMRBASE5MS		(TMRBASE50MS / 10)

#define	QUANT_MIN		5						// min quantization timer base is 5ms
//#define	QUANT_MIN		10						// min quantization timer base is 5ms

extern MsgQueueType msgQueue;

LLONG rtc_ref;  /* Reference time */
SHORT norm_mday[12]={31,28,31,30,31,30,31,31,30,31,30,31};	/* Days in months in normal year */
SHORT leap_mday[12]={31,29,31,30,31,30,31,31,30,31,30,31};	/* Days in months in leap year */
SHORT four_yday[4]={365,365,365,366};	/* Days in four years in cycle */

TMR_TABLE tmr_table[TMR_MAX_ENTRY];	/* Timer table */
UWORD tmr_cnt = 0, tmr_max_cnt = 1;	/* Timer counter */

UWORD tmr_timebase;

/*	Function	: Hcf
*	Purpose		: Compute HCF of two numbers
*  Scope		: OS
*	Input		: Two numbers
*	Output      : HCF of two numbers
*	Comment		:
*/
UWORD Hcf(UWORD n1, UWORD n2)
{
	while (1)
	{
		n1 = n1 % n2;
		if (n1 == 0)
			return n2;
		n2 = n2 % n1;
		if (n2 == 0)
			return n1;
	}
}

/*	Function	: Lcm
*	Purpose		: Compute LCM of two numbers
*  Scope		: OS
*	Input		: Two numbers
*	Output      : LCM of two numbers
*	Comment		:
*/
UWORD Lcm(UWORD n1, UWORD n2)
{
	return (n1/Hcf(n1, n2))*n2;
}

/*	Function	: WordBreak
*	Purpose		: Break the number of type word into long long
*  Scope		: OS
*	Input		: A number of type word
*	Output      : Broken number of type long long
*	Comment		:
*/
LLONG WordBreak(UWORD n1)
{
	LLONG n3;
	n3.l = n1 & 0x0000FFFF;
	n3.h = ((n1 >> (WORD_SIZE/2)) & 0x0000FFFF);
	return n3;
}

/*	Function	: LLAdd
*	Purpose		: Add two numbers of type long long
*  Scope		: OS
*	Input		: Two numbers of type long long
*	Output      : Added number of type long long
*	Comment		:
*/
LLONG LLAdd(LLONG n1, LLONG n2)
{
	LLONG n3;
	n3.l = n1.l + n2.l;
	n3.h = n1.h + n2.h;
	if (n1.l > n3.l)
		n3.h++;
	return n3;
}

/*	Function	: LLMin
*	Purpose		: Subtract one number of type long long from another
*  Scope		: OS
*	Input		: Two numbers of type long long
*	Output      : Subtracted number of type long long
*	Comment		:
*/
LLONG LLMin(LLONG n1, LLONG n2)
{
	LLONG n3;
	n3.h = n1.h - n2.h;
	if (n1.l < n2.l)
		n3.h--;
	n3.l = n1.l - n2.l;
	return n3;
}

/*	Function	: LLMul1
*	Purpose		: Multiple one number of type long long to another of type word
*  Scope		: OS
*	Input		: A number of type long long and another of type word
*	Output      : Multiplied number of type long long
*	Comment		:
*/
LLONG LLMul1(UWORD n1, UWORD n2)
{
	LLONG d1, d2, d3;
	d1 = WordBreak(n1);
	d2 = WordBreak(n2);
	d3.l = d1.l * d2.l;
	d3.h = d1.h * d2.h;
	n1 = d1.h * d2.l;
	n2 = d1.l * d2.h;
	d1 = WordBreak(n1);
	d2 = WordBreak(n2);
	d1.l <<= (WORD_SIZE/2);
	d2.l <<= (WORD_SIZE/2);
	d3 = LLAdd(d3, d1);
	d3 = LLAdd(d3, d2);
	return d3;
}

/*	Function	: LLMul2
*	Purpose		: Multiple two numbers of type long long
*  Scope		: OS
*	Input		: Two numbers of type long long
*	Output      : Multiplied number of type long long
*	Comment		:
*/
LLONG LLMul2(LLONG n1, LLONG n2)
{
	LLONG d1, d2, d3;
	d3 = LLMul1(n1.l, n2.l);
	d1 = LLMul1(n1.h, n2.l);
	d1.h = d1.l; d1.l = 0;
	d2 = LLMul1(n1.l, n2.h);
	d2.h = d2.l; d2.l = 0;
	d3 = LLAdd(d3, d1);
	d3 = LLAdd(d3, d2);
	return d3;
}

/*	Function	: LLDiv
*	Purpose		: Divide one number of type long long to another of type word
*  Scope		: OS
*	Input		: A number of type long long and another of type word
*	Output      : Divided number of type long long and remainder of type word
*	Comment		:
*/
LLONG LLDiv(LLONG n1, UWORD n2, UWORD *n3_r)
{
	LLONG d1, d3;
	*n3_r = n1.h*(UWORD)(WORD_MASK%n2) + n1.h%n2 + n1.l%n2;
	d3 = LLMul1(n1.h, WORD_MASK/n2);
	d1.h = 0; d1.l = n1.h/n2;
	d3 = LLAdd(d3, d1);
	d1.h = 0; d1.l = n1.l/n2;
	d3 = LLAdd(d3, d1);
	d1.h = 0; d1.l = *n3_r/n2;
	d3 = LLAdd(d3, d1);
	*n3_r %= n2;
	return d3;
}

/*	Function	: RtcToTime
*	Purpose		: Convert binary counter into time of RTM format
*  Scope		: OS
*	Input		: A binary counter
*	Output      : Time of RTM format
*	Comment		:
*/
void RtcToTime(LLONG cnt, RTM *time)
{
	UWORD cnt_r, year4=0;
	cnt = LLDiv(cnt, (UWORD)32768, (UWORD *)&cnt_r);
	time->msec = (SHORT) (cnt_r*1000/32768);
	cnt = LLDiv(cnt, (UWORD)60, (UWORD *)&cnt_r);
	time->sec = (SHORT) cnt_r;
	cnt = LLDiv(cnt, (UWORD)60, (UWORD *)&cnt_r);
	time->min = (SHORT) cnt_r;
	cnt = LLDiv(cnt, (UWORD)24, (UWORD *)&cnt_r);
	time->hour = (SHORT) cnt_r;
	time->wday = (cnt.l+2)%7;
	time->year = 4*LLDiv(cnt, (UWORD)1461, (UWORD *)&cnt_r).l;
	time->yday = (SHORT) cnt_r;
	while(time->yday >= four_yday[year4])
		time->yday -= four_yday[year4++];
	time->year += year4+1901;
	time->mday = time->yday;
	time->mon = 0;
	if (year4 == 3)
	{
		while(time->mday >= leap_mday[time->mon])
			time->mday -= leap_mday[time->mon++];
		time->mday++;
	}
	else
	{
		while(time->mday >= norm_mday[time->mon])
			time->mday -= norm_mday[time->mon++];
		time->mday++;
	}
}

/*	Function	: TimeToRtc
*	Purpose		: Convert time of RTM format into binary counter
*  Scope		: OS
*	Input		: Time of RTM format
*	Output      : A binary counter
*	Comment		:
*/
LLONG TimeToRtc(RTM time)
{
	LLONG cnt;
	WORD year4;
	UWORD aday;
	time.year -= 1901;
	year4 = time.year%4;
	aday = time.mday-1;
	if (year4 == 3)
	{
		while(--time.mon >= 0)
			aday += leap_mday[time.mon];
	}
	else
	{
		while(--time.mon >= 0)
			aday += norm_mday[time.mon];
	}
	while(--year4 >= 0)
		aday += four_yday[year4];
	time.year = time.year/4;
	aday += 1461*(UWORD)time.year;
	cnt.l = time.hour;
	cnt.l *= 60;
	cnt.l += time.min;
	cnt.l *= 60;
	cnt.l += time.sec;
	cnt.l *= 32768;
	cnt.l += (32768*time.msec/1000);
	cnt.h = 0;
	cnt = LLAdd(cnt, LLMul1(aday, (UWORD)0xA8C00000));
	return cnt;
}

/*	Function	: TmrInit
*	Purpose		: Initialize the timer interrupt of period 1 msec
*  Scope		: OS
*	Input		: None
*	Output      : None
*	Comment		:
*/
void TmrInit()
{
	UWORD i;
	tmr_cnt = 0;
	tmr_max_cnt = 1;	/* Timer counter */
	for (i=0; i<TMR_MAX_ENTRY; i++)
	{
		tmr_table[i].label = 0;
		tmr_table[i].period = 0;
		tmr_table[i].offset = 0;
		tmr_table[i].fptr = 0;
	}
#ifdef PR31700
	ENA_TIMERCLK;
	tmr_timebase = TMRBASE50MS;
	SET_PERVAL(tmr_timebase);
#endif
	DebugPinInit ();
}

/*	Function	: LcmCnt
*	Purpose		: Find the LCM of periods of all interrupts
*  Scope		: OS
*	Input		: None
*	Output      : LCM of periods of all interrupts
*	Comment		:
*/
UWORD LcmCnt()
{
	UWORD i, tmr_max_cnt = 1;
	
	for (i=0; i<TMR_MAX_ENTRY; i++)
		if (tmr_table[i].label != 0)
			tmr_max_cnt = Lcm(tmr_max_cnt, tmr_table[i].period);
		return tmr_max_cnt;
}

/*	Function	: TmrISR
*	Purpose		: Timer interrupt service routine
*  Scope		: OS
*	Input		: None
*	Output      : None
*	Comment		:
*/
void TmrISR()
{
	UWORD i;
	
	DebugSetPin ();
	
	if (tmr_timebase == TMRBASE10MS)
		tmr_cnt += 10 / QUANT_MIN;
#if		(QUANT_MIN == 10)
	else
		tmr_cnt += 50 / QUANT_MIN;
#else
	else if (tmr_timebase == TMRBASE50MS)
		tmr_cnt += 50 / QUANT_MIN;
	else
		tmr_cnt += 5 / QUANT_MIN;
#endif	(QUANT_MIN == 10)
	
	if (tmr_cnt % tmr_max_cnt == 0)
		tmr_cnt = 0;
	
	//	VTRACE ("\n@%x", tmr_cnt);
	for (i=0; i<TMR_MAX_ENTRY; i++)
	{
		if (tmr_table[i].label != 0)
			if ((tmr_cnt + tmr_table[i].offset) % tmr_table[i].period == 0)
			{
				//				VTRACE ("\nrun label=%08X", tmr_table[i].label);
				if (tmr_table[i].msg == TRUE)
					MsgAppendMsgInt((void *)tmr_table[i].fptr, 0, 0, 0, NULL);
				else
					tmr_table[i].fptr();
			}
	}
	DebugClearPin ();
}

/*      Function        : ClearAppTmr
*      Purpose         : Clear application created timer
*      Scope           : OS
*      Input           : None
*      Output          : None
*	Comment		:
*/
void ClearAppTmr()
{
	UWORD i;
	for (i=0; i<TMR_MAX_ENTRY; i++)
	{
        if ((UWORD)(tmr_table[i].fptr) & 0x10000000)
			tmr_table[i].label = 0;
	}
}


/*	Function	: TmrIntEnable
*	Purpose		: Timer interrupt enable
*  Scope		: OS
*	Input		: Period of the interrupt in msec and called function pointer
*	Output      : Interrupt label assigned by TmrIntEnable
*	Comment		:
*/
UWORD TmrIntEnable(UWORD period, void *fptr)
{
	UWORD i;
	
#ifdef PR31700
	DIS_GBL_INT;
	ENA_PERTIMER;
	ENA_PERINT;
#endif
	period = TmrQuantizePeriod(period);
	for (i=0; i<TMR_MAX_ENTRY; i++)
	{
		if (tmr_table[i].label == 0)
		{
			do
			{
				tmr_table[i].label = CPU->REG_RTC_LOW;
			} while (tmr_table[i].label == 0);
			tmr_table[i].period = period;
			tmr_table[i].offset = period - tmr_cnt % period;
			if (period >= 1000/QUANT_MIN)
				tmr_table[i].offset = (tmr_table[i].offset/(50/QUANT_MIN)+1)*(50/QUANT_MIN);
#if		(QUANT_MIN == 5)
			else if (period >= 10/QUANT_MIN)
				tmr_table[i].offset = (tmr_table[i].offset/(10/QUANT_MIN)+1)*(10/QUANT_MIN);
#endif	(QUANT_MIN == 5)
			tmr_table[i].msg = TRUE;
#ifdef PC_SIM
			tmr_table[i].fptr = (void (__cdecl *)(void )) fptr;
#endif
#ifdef PR31700
			tmr_table[i].fptr = (void *) fptr;
#endif
			tmr_max_cnt = Lcm(tmr_max_cnt, period);
#ifdef PR31700
			ENA_GBL_INT;
#endif
			VTRACE ("\nTmr %d enabled: label %08X Period %d %d", i, tmr_table[i].label, tmr_table[i].period, tmr_timebase);
			return tmr_table[i].label;
		}
	}
#ifdef PR31700
	ENA_GBL_INT;
#endif
	VTRACE ("\nTimer exceeds max");
	return 0;
}

UWORD TmrIntEnableInt(UWORD period, void *fptr)
{
	UWORD i;
#ifdef PR31700
	ENA_PERTIMER;
	ENA_PERINT;
#endif
	period = TmrQuantizePeriod(period);
	for (i=0; i<TMR_MAX_ENTRY; i++)
	{
		if (tmr_table[i].label == 0)
		{
			do
			{
				tmr_table[i].label = CPU->REG_RTC_LOW;
			} while (tmr_table[i].label == 0);
			tmr_table[i].period = period;
			tmr_table[i].offset = period - tmr_cnt % period;
			if (tmr_table[i].period >= 1000/QUANT_MIN)
				tmr_table[i].offset = (tmr_table[i].offset/(50/QUANT_MIN)+1)*(50/QUANT_MIN);
#if		(QUANT_MIN == 5)
			else if (tmr_table[i].period >= 10/QUANT_MIN)
				tmr_table[i].offset = (tmr_table[i].offset/(10/QUANT_MIN)+1)*(10/QUANT_MIN);
#endif	(QUANT_MIN == 5)
			tmr_table[i].msg = TRUE;
#ifdef PC_SIM
			tmr_table[i].fptr = (void (__cdecl *)(void )) fptr;
#endif
#ifdef PR31700
			tmr_table[i].fptr = (void *) fptr;
#endif
			tmr_max_cnt = Lcm(tmr_max_cnt, period);
			VTRACE ("\nTmr %d enabled Int: label %08X Period %d %d", i, tmr_table[i].label, tmr_table[i].period, tmr_timebase);
			return tmr_table[i].label;
		}
	}
	VTRACE ("\nTimer exceeds max");
	return 0;
}

UWORD TmrIntEnableIntPen(UWORD period, void *fptr)
{
	UWORD i;
#ifdef PR31700
	ENA_PERTIMER;
	ENA_PERINT;
#endif
	period = TmrQuantizePeriod(period);
	for (i=0; i<TMR_MAX_ENTRY; i++)
	{
		if (tmr_table[i].label == 0)
		{
			do
			{
				tmr_table[i].label = CPU->REG_RTC_LOW;
			} while (tmr_table[i].label == 0);
			tmr_table[i].period = period;
			tmr_table[i].offset = period - tmr_cnt % period;
			if (tmr_table[i].period >= 1000/QUANT_MIN)
				tmr_table[i].offset = (tmr_table[i].offset/(50/QUANT_MIN)+1)*(50/QUANT_MIN);
#if		(QUANT_MIN == 5)
			else if (tmr_table[i].period >= 10/QUANT_MIN)
				tmr_table[i].offset = (tmr_table[i].offset/(10/QUANT_MIN)+1)*(10/QUANT_MIN);
#endif	(QUANT_MIN == 5)
			tmr_table[i].msg = FALSE;
#ifdef PC_SIM
			tmr_table[i].fptr = (void (__cdecl *)(void )) fptr;
#endif
#ifdef PR31700
			tmr_table[i].fptr = (void *) fptr;
#endif
			tmr_max_cnt = Lcm(tmr_max_cnt, period);
			VTRACE ("\nTmr %d enabled IntPen: label %08X Period %d %d", i, tmr_table[i].label, tmr_table[i].period, tmr_timebase);
			return tmr_table[i].label;
		}
	}
	VTRACE ("\nTimer exceeds max");
	return 0;
}

/*	Function	: TmrIntDisable
*	Purpose		: Timer interrupt disable
*  Scope		: OS
*	Input		: Interrupt label assigned by TmrIntEnable
*	Output      : None
*	Comment		:
*/
void TmrIntDisable(UWORD label)
{
	UWORD i;
	
#ifdef PR31700
	DIS_GBL_INT;
#endif
	for (i=0; i<TMR_MAX_ENTRY; i++)
	{
		if (tmr_table[i].label == label)
		{
			tmr_table[i].label = 0;
			break;
		}
	}
	
	TmrComputeTimeBase();
	
#ifdef DEBUG
	if (i == TMR_MAX_ENTRY)
		VTRACE ("\nTimer wrongly disables");
#endif
	
	tmr_max_cnt = LcmCnt();
	if (tmr_max_cnt == 1)
	{
#ifdef PR31700
		DIS_PERTIMER;
		DIS_PERINT;
#endif
	}
#ifdef PR31700
	ENA_GBL_INT;
#endif
	VTRACE ("\nTimer disabled: label=%08X %d", label, tmr_timebase);
}

void TmrIntDisableInt(UWORD label)
{
	UWORD i;
	
	for (i=0; i<TMR_MAX_ENTRY; i++)
	{
		if (tmr_table[i].label == label)
		{
			tmr_table[i].label = 0;
			VTRACE ("\nTimer correctly disables");
			break;
		}
	}
	
	TmrComputeTimeBase();
	
#ifdef DEBUG
	if (i == TMR_MAX_ENTRY)
		VTRACE ("\nTimer wrongly disables");
#endif
	
	tmr_max_cnt = LcmCnt();
	
	if (tmr_max_cnt == 1)
	{
#ifdef PR31700
		DIS_PERTIMER;
		DIS_PERINT;
#endif
	}
	VTRACE ("\nTimer disabled: label=%08X %d", label, tmr_timebase);
}

void TmrIntDisableAll()
{
	UWORD i;
#ifdef PR31700
    DIS_GBL_INT;
#endif
	//	tmr_table[label-1].label = 0;
	for (i=0; i<TMR_MAX_ENTRY; i++)
	{
		tmr_table[i].label = 0;
	}
	
	tmr_max_cnt = 1;
	
#ifdef PR31700
	DIS_PERTIMER;
	DIS_PERINT;
#endif
	
#ifdef PR31700
    ENA_GBL_INT;
#endif
}

void TmrWaitTime(UWORD interval)
{
	RTM time1, time2;
	RtcGetTime(&time1);
	do
	{
		RtcGetTime(&time2);
	} while (RtcDiffTime(&time1, &time2).l < interval);
}

/*	Function	: RtcISR
*	Purpose		: Real time clock interrupt service routine
*  Scope		: OS
*	Input		: None
*	Output      : None
*	Comment		:
*/
void RtcISR()
{
	MsgType msg;
	msg.msgID = RTC_ROLLOVER;
#ifdef PC_SIM
	msg.ptr_fct = (void (__cdecl *)(void *))RtcDecode;
#endif
#ifdef PR31700
	msg.ptr_fct = (void *)RtcDecode;
#endif
	//MsgAppend(msg, &msgQueue);
	MsgAppendMsgInt(msg.ptr_fct, msg.msgID, 0, 0, NULL);
	rtc_ref.h += 0x100;
}

/*	Function	: RtcDecode
*	Purpose		: Decode the RTC message
*  Scope		: OS
*	Input		: RTC message
*	Output      : None
*	Comment		:
*/
void RtcDecode(MsgType *msg)
{
	EvtAppendEvt(RTC_EVENT, msg->msgID, msg->para1, msg->para2, 0);
}

void RtcGetTimeEvt()
{
	RTM *time;
	time = qmalloc(sizeof(RTM));
	RtcGetTime(time);
	EvtAppendEvt(RTC_EVENT, RTC_GETTIME, 0, 0, time);
}

/*	Function	: RtcClr
*	Purpose		: Clear real time clock
*  Scope		: OS
*	Input		: None
*	Output      : None
*	Comment		:
*/
void RtcClr()
{
#ifdef PR31700
	CPU->REG_TIMER_CTRL |= RTCCLR;
	CPU->REG_TIMER_CTRL &= ~RTCCLR;
#endif
}

/*	Function	: RtcGetTime
*	Purpose		: Get the current time of RTM format
*  Scope		: OS
*	Input		: None
*	Output      : Current time of RTM format
*	Comment		:
*/
void RtcGetTime(RTM *time)
{
	LLONG cnt;
#ifdef PC_SIM
	cnt.h = 0;
	cnt.l = 0;
#endif
#ifdef PR31700
	cnt.h = RTCHIGH(CPU->REG_RTC_HIGH);
	cnt.l = CPU->REG_RTC_LOW;
	RtcToTime(LLAdd(cnt, rtc_ref), time);
#endif
#ifdef PC_SIM
	CTime timex = CTime::GetCurrentTime();
	
	time->year = timex.GetYear();
	time->mday = timex.GetDay();
	time->mon = timex.GetMonth() - 1;
	time->hour = timex.GetHour();
	time->min = timex.GetMinute();
	time->sec = timex.GetSecond();
	time->msec = 0;
#endif
}

/*	Function	: RtcSetTime
*	Purpose		: Set time value for real time clock
*  Scope		: OS
*	Input		: Current time of RTM format
*	Output      : None
*	Comment		:
*/
void RtcSetTime(RTM *time)
{
	AlmSetStatus(FALSE);
	rtc_ref = TimeToRtc(*time);
	RtcClr();
}

/*	Function	: RtcRollOver
*	Purpose		: Force the real time clock to roll over
*  Scope		: OS
*	Input		: None
*	Output      : None
*	Comment		:
*/
void RtcRollOver()
{
	LLONG cnt;
#ifdef PR31700
	cnt.h = RTCHIGH(CPU->REG_RTC_HIGH);
	cnt.l = CPU->REG_RTC_LOW;
#endif
	RtcClr();
	rtc_ref = LLAdd(cnt, rtc_ref);
}

/*	Function	: RtcDiffTime
*	Purpose		: Calculate the difference of the two times of RTM format
*  Scope		: OS
*	Input		: Two times of RTM format
*	Output      : Elapsed time in seconds, from time1 to time2
*	Comment		:
*/
LLONG RtcDiffTime(RTM *time_1, RTM *time_2)
{
	UWORD n1;
	LLONG cnt1 = TimeToRtc(*time_1), cnt2 = TimeToRtc(*time_2), cnt3;
	cnt3.h = 0;
	cnt3.l = 1000;
	return LLDiv(LLMul2(LLMin(cnt2, cnt1), cnt3), 32768, &n1);
}

/*	Function	: RtcCompareTime
*	Purpose		: Compare two times
*  Scope		: OS
*	Input		: Two times of RTM format
*	Output      : Return TRUE if time1 > time2; otherwise FALSE
*	Comment		:
*/
BOOLEAN RtcCompareTime(RTM *time_1, RTM *time_2)
{
	LLONG cnt1 = TimeToRtc(*time_1), cnt2 = TimeToRtc(*time_2);
	if (cnt1.h > cnt2.h)
		return TRUE;
	else if (cnt1.h == cnt2.h && cnt1.l > cnt2.l)
		return TRUE;
   	else
		return FALSE;
}

SHORT RtcGetDaysInMonth(SHORT year, SHORT mon)
{
	if (year%4 == 0)
		return leap_mday[mon];
	else
		return norm_mday[mon];
}

BOOLEAN RtcCheckYMDValid(SHORT year, SHORT mon, SHORT mday)
{
	if (year < 1901 || year > 2099)
		return FALSE;
	if (mon < 0 || mon > 11)
		return FALSE;
	if (mday < 1 || mday > RtcGetDaysInMonth(year, mon))
		return FALSE;
	return TRUE;
}

void RtcYMDToWday1(SHORT year, SHORT mon, SHORT mday, SHORT *wday, SHORT *nth_week)
{
	RTM time = {0, 0, 0, 0, 0, 0, 0, 0, 0};
	LLONG cnt;
	time.year = year;
	time.mon = mon;
	time.mday = mday;
	cnt = TimeToRtc(time);
	RtcToTime(cnt, &time);
	*wday = time.wday;
	*nth_week =	(mday-1)/7+1;
	if (*wday < (mday-1)%7)
		*nth_week = *nth_week + 1;
}

BOOLEAN RtcWday1ToYMD(SHORT year, SHORT mon, SHORT wday, SHORT nth_week, SHORT *mday)
{
	RTM time = {0, 0, 0, 0, 0, 0, 0, 0, 0};
	LLONG cnt;
	time.year = year;
	time.mon = mon;
	time.mday = 1;
	cnt = TimeToRtc(time);
	RtcToTime(cnt, &time);
	*mday = (wday - time.wday + 7)%7 + 1;
	
	if (nth_week == -1)
		while (*mday + 7 > RtcGetDaysInMonth(year, mon))
			*mday = *mday + 7;
		else
		{
			if (wday < (*mday-1)%7)
				nth_week = nth_week - 1;
			*mday = *mday + (nth_week - 1)*7;
		}
		if (*mday <= 0 || *mday > RtcGetDaysInMonth(year, mon))
		{
			*mday = -1;
			return FALSE;
		}
		return TRUE;
}

void RtcYMDToWday2(SHORT year, SHORT mon, SHORT mday, SHORT *wday, SHORT *nth_wday)
{
	RTM time = {0, 0, 0, 0, 0, 0, 0, 0, 0};
	LLONG cnt;
	time.year = year;
	time.mon = mon;
	time.mday = mday;
	cnt = TimeToRtc(time);
	RtcToTime(cnt, &time);
	*wday = time.wday;
	*nth_wday =	(mday-1)/7+1;
}

BOOLEAN RtcWday2ToYMD(SHORT year, SHORT mon, SHORT wday, SHORT nth_wday, SHORT *mday)
{
	RTM time = {0, 0, 0, 0, 0, 0, 0, 0, 0};
	LLONG cnt;
	time.year = year;
	time.mon = mon;
	time.mday = 1;
	cnt = TimeToRtc(time);
	RtcToTime(cnt, &time);
	*mday = (wday - time.wday + 7)%7 + 1;
	if (nth_wday == -1)
		while (*mday + 7 > RtcGetDaysInMonth(year, mon))
			*mday = *mday + 7;
		else
			*mday = *mday + (nth_wday - 1)*7;
		if (*mday > RtcGetDaysInMonth(year, mon))
		{
			*mday = -1;
			return FALSE;
		}
		return TRUE;
}

void RtcYMDToWday3(SHORT year, SHORT mon, SHORT mday, SHORT *wday, SHORT *nth_wday)
{
	RTM time = {0, 0, 0, 0, 0, 0, 0, 0, 0};
	LLONG cnt;
	time.year = year;
	time.mon = mon;
	time.mday = mday;
	cnt = TimeToRtc(time);
	RtcToTime(cnt, &time);
	*wday = time.wday;
	*nth_wday =	(time.yday)/7+1;
}

void RtcWday3ToYMD(SHORT year, SHORT nth_wday, SHORT wday, SHORT *mon, SHORT *mday)
{
	RTM time = {0, 0, 0, 0, 0, 0, 0, 0, 0};
	LLONG cnt;
	*mon = 0;
	time.year = year;
	time.mon = 0;
	time.mday = 1;
	cnt = TimeToRtc(time);
	RtcToTime(cnt, &time);
	*mday = (wday - time.wday + 7)%7;
	*mday = *mday + (nth_wday - 1)*7;
	if (year % 4 == 0)
	{
		while(*mday >= leap_mday[*mon])
			*mday -= leap_mday[(*mon)++];
		(*mday)++;
	}
	else
	{
		while(*mday >= norm_mday[*mon])
			*mday -= norm_mday[(*mon)++];
		(*mday)++;
	}
	if (*mon == 12)
		*mon = 0;
}

void RtcYMDToWday4(SHORT year, SHORT mon, SHORT mday, SHORT *wday, SHORT *nth_week)
{
	RTM time = {0, 0, 0, 0, 0, 0, 0, 0, 0};
	LLONG cnt;
	time.year = year;
	time.mon = mon;
	time.mday = mday;
	cnt = TimeToRtc(time);
	RtcToTime(cnt, &time);
	*wday = time.wday;
	*nth_week =	(time.yday)/7+1;
	if (*wday < (time.yday)%7)
		*nth_week = *nth_week + 1;
}

BOOLEAN RtcWday4ToYMD(SHORT year, SHORT nth_week, SHORT wday, SHORT *mon, SHORT *mday)
{
	RTM time = {0, 0, 0, 0, 0, 0, 0, 0, 0};
	LLONG cnt;
	*mon = 0;
	time.year = year;
	time.mon = 0;
	time.mday = 1;
	cnt = TimeToRtc(time);
	RtcToTime(cnt, &time);
	*mday = (wday - time.wday + 7)%7;
	if (wday <= (*mday-1)%7)
		nth_week = nth_week - 1;
	*mday = *mday + (nth_week - 1)*7;
	if (*mday < 0)
	{
		*mon = 11;
		*mday = *mday + 32;
		return FALSE;
	}
	else if (year % 4 == 0)
	{
		while(*mday >= leap_mday[*mon])
			*mday -= leap_mday[(*mon)++];
		(*mday)++;
	}
	else
	{
		while(*mday >= norm_mday[*mon])
			*mday -= norm_mday[(*mon)++];
		(*mday)++;
	}
	if (*mon == 12)
	{
		*mon = 0;
		return FALSE;
	}
	return TRUE;
}

BOOLEAN RtcWday5ToYMD(SHORT year, SHORT nth_week, SHORT wday, SHORT *mon, SHORT *mday)
{
	RTM time = {0, 0, 0, 0, 0, 0, 0, 0, 0};
	LLONG cnt;
	*mon = 0;
	time.year = year;
	time.mon = 0;
	time.mday = 1;
	cnt = TimeToRtc(time);
	RtcToTime(cnt, &time);
	
	// If Jan 1st is Sunday
	if (time.wday == 0)
	{
		// If the specified date is not Sunday, week number needs to be decremented.
		if (wday != 0)
			nth_week--;
	}
	// If Jan 1st is not Sunday
	else
	{
		// If the specified date is Sunday, week number needs to be incremented.
		if (wday == 0)
			nth_week++;
	}
	
	*mday = (wday - time.wday + 7)%7;
	if (wday <= (*mday-1)%7)
		nth_week = nth_week - 1;
	*mday = *mday + (nth_week - 1)*7;
	if (*mday < 0)
	{
		*mon = 11;
		*mday = *mday + 32;
		return FALSE;
	}
	else if (year % 4 == 0)
	{
		while(*mday >= leap_mday[*mon])
			*mday -= leap_mday[(*mon)++];
		(*mday)++;
	}
	else
	{
		while(*mday >= norm_mday[*mon])
			*mday -= norm_mday[(*mon)++];
		(*mday)++;
	}
	if (*mon == 12)
	{
		*mon = 0;
		return FALSE;
	}
	return TRUE;
}

void RtcDaysShift(SHORT *year, SHORT *mon, SHORT *mday, SHORT day)
{
	RTM time = {0, 0, 0, 0, *mday, *mon, *year, 0, 0};
	LLONG cnt;
	if (day > 0)
		cnt = LLAdd(TimeToRtc(time), LLMul1(day, (UWORD)0xA8C00000));
	else if (day < 0)
		cnt = LLMin(TimeToRtc(time), LLMul1(-day, (UWORD)0xA8C00000));
	RtcToTime(cnt, &time);
	*year = time.year;
	*mon = time.mon;
	*mday = time.mday;
}

/*	Function	: AlmISR
*	Purpose		: Alarm interrupt service routine
*  Scope		: OS
*	Input		: None
*	Output      : None
*	Comment		:
*/
void AlmISR()
{
	MsgType msg;
	msg.msgID = ALARM_HIT;
    WakeCpuUp = TRUE;
#ifdef PC_SIM
	msg.ptr_fct = (void (__cdecl *)(void *))AlmDecode;
#endif
#ifdef PR31700
	msg.ptr_fct = (void *)AlmDecode;
#endif
	//MsgAppend(msg, &msgQueue);
	MsgAppendMsgInt(msg.ptr_fct, msg.msgID, 0, 0, NULL);
}

/*	Function	: AlmDecode
*	Purpose		: Decode the alarm message
*  Scope		: OS
*	Input		: Alarm message
*	Output      : None
*	Comment		:
*/
void AlmDecode(MsgType *msg)
{
	/* EvtAppendEvt(ALARM_EVENT, msg->msgID, msg->para1, msg->para2, 0); */
	/* jump to alarm manager */
	AlarmOnHit();
}

/*	Function	: AlmSetStatus
*	Purpose		: Enable or disable the alarm
*  Scope		: OS
*	Input		: Alarm status
*	Output      : None
*	Comment		:
*/
void AlmSetStatus(BOOLEAN status)
{
#ifdef PR31700
	if (status == TRUE)
		ENA_ALARMINT;
	else
		DIS_ALARMINT;
#endif
}

/*	Function	: AlmGetTime
*	Purpose		: Obtain alarm time
*  Scope		: OS
*	Input		: None
*	Output      : Alarm time of RTM format
*	Comment		:
*/
void AlmGetTime(RTM *time)
{
	LLONG cnt;
#ifdef PR31700
	cnt.h = ALARMHIGH(CPU->REG_ALARM_HIGH);
	cnt.l = CPU->REG_ALARM_LOW;
#endif
	RtcToTime(LLAdd(cnt, rtc_ref), time);
}

/*	Function	: AlmSetTime
*	Purpose		: Set alarm time value
*  Scope		: OS
*	Input		: Alarm time of RTM format
*	Output      : None
*	Comment		:
*/
void AlmSetTime(RTM *time)
{
	LLONG cnt;
	RtcRollOver();
	cnt = LLMin(TimeToRtc(*time), rtc_ref);
	if (cnt.h <= 0xFF)
	{
#ifdef PR31700
		CPU->REG_ALARM_HIGH = cnt.h;
		CPU->REG_ALARM_LOW = cnt.l;
#endif
	}
}

/* RtcFormatDate
output a formatted date string
input  	in_date    date to format
date_pref	output format
output 	out_buf
return 	byte written into out_buf
*/
SHORT RtcFormatDate(RTM *in_date, BYTE *date_pref, BYTE *out_buf)
{
/* date_pref:
NULL	system default
M	month, 1 or 2 digit
MM	month, 2 digit
D	day, 1 or 2 digit
DD	day, 2 digit
Y	year, 2 digit
YY	year, 4 digit
h	hour, 24 time format, 1 or 2 digit
hh	hour, 24 time format, 2 digit
H       hour, 12 time format, 1 or 2 digit
HH	hour, 12 time format, 2 digit
m	minute, 1 or 2 digit
mm	minute, 2 digit
s	second, 1 or 2 digit
ss	second, 2 digit
e       hundred-second, 1 or 2 digit
ee      hundred-second, 2 digit
A       AM / PM
a       am / pm
~       force to print next character, not check keyword
W	weekday, 3 character, first character in capital
WW	weekday, full name, first character in capital
w	weekday, 3 character
ww	weekday, full name
O       month name, 3 character, first character in capital
OO      month name, full name, first character in capital
o       month name, 3 character
oo      month name, full name
	*/
	
	SHORT inbuf_len;
	USHORT value;
	BYTE *in_ptr, *out_ptr;
	BYTE d_flag, kw_flag;
	SHORT wday, nth_week;
	BYTE defaultsetting[] = "W O DD hh:mm:ss YY";
	
    //BYTE wday_name[7][10] = {"sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday" };
    //BYTE mon_name[12][10] = {"january", "february", "march", "april", "may", "june", "july", "august", "september", "october", "november", "december"};
    
    BYTE wday_name[7][10] = {SYSUN1, SYMON1, SYTUE1, SYWED1, SYTHU1, SYFRI1, SYSAT1};
	
	BYTE mon_name[12][10] = {SYJAN, SYFEB, SYMAR, SYAPR, SYMAY, SYJUN, SYJUL, SYAUG, SYSEP, SYOCT, SYNOV, SYDEC};
	
#ifdef DEBUG
	if(!out_buf)
	{
		VTRACE ("\nFormatDate: parameter out_buf cannot = NULL");
		return FALSE;
	}
#endif
	
	if(!date_pref)
		date_pref = defaultsetting;
	
	inbuf_len = strlen(date_pref);
	in_ptr = date_pref;
	out_ptr = out_buf;
	
	while(inbuf_len)
	{
		d_flag = 0;
		kw_flag = 0;
		switch(*in_ptr)
		{
		case 'M':
		case 'D':
		case 'Y':
		case 'h':
		case 'm':
		case 's':
		case 'H':
		case 'e':
			kw_flag = 1; /* key word found */
			if(inbuf_len > 1)
				if(*(in_ptr+1) == *in_ptr)
					d_flag = 1;  /* double */
				
				if (*in_ptr == 'M') value = in_date->mon + 1;
				else if (*in_ptr == 'm') value = in_date->min;
				else if (*in_ptr == 'e') value = (in_date->msec + 5)/ 10;
				else if (*in_ptr == 's') value = in_date->sec;
				else if (*in_ptr == 'h') value = in_date->hour;
				else if (*in_ptr == 'D') value = in_date->mday;
				else if (*in_ptr == 'Y') value = in_date->year;
				else if (*in_ptr == 'H')
				{
					if(in_date->hour > 12)
						value = in_date->hour - 12;
					else
						value = in_date->hour;
				}
				
				if((*in_ptr == 'Y') && (!d_flag))  /* 2 digit in yr */
					value -= ((USHORT) (value / 100)) * 100;
				else if((value < 10) && d_flag) /* 2 digit for 1 digit value, add '0' */
					*out_ptr++ = '0';
				
				in_ptr += d_flag;  /* move to next keyword */
				inbuf_len -= d_flag;
				d_flag = sprintf((char*)out_ptr, "%d", value);
				out_ptr += d_flag;
				break;
		case 'a':	/* am/pm */
			if(in_date->hour < 12)
				*out_ptr++ = 'a';
			else
				*out_ptr++ = 'p';
			*out_ptr++ = 'm';
			break;
		case 'A':	/* AM/PM */
			if(in_date->hour < 12)
				*out_ptr++ = 'A';
			else
				*out_ptr++ = 'P';
			*out_ptr++ = 'M';
			break;
		case '~':	/* print next character */
			if(inbuf_len < 2)
				break;
			in_ptr++;
			inbuf_len--;
			*out_ptr++ = *in_ptr;
			break;
		case 'w':
		case 'W':  /* week day name */
			if(inbuf_len > 1)
				if(*(in_ptr+1) == *in_ptr)
					d_flag = 1;  /* double */
				RtcYMDToWday1(in_date->year, in_date->mon, in_date->mday, &wday, &nth_week);
				if(wday > 6) break;  /* should within 0 - 6 */
				if(!d_flag)
					wday_name[wday][3] = 0x00;  /* 3 characters only */
				strcpy(out_ptr, wday_name[wday]);
				if(*in_ptr == 'W')  /* upper case */
					*out_ptr -= 'a' - 'A';
				out_ptr += strlen(wday_name[wday]);
				in_ptr += d_flag;
				inbuf_len -= d_flag;
				break;
				
		case 'o':
		case 'O':  /* week day name */
			if(in_date->mon > 11) break;
			if(inbuf_len > 1)
				if(*(in_ptr+1) == *in_ptr)
					d_flag = 1;  /* double */
				if(!d_flag)
					mon_name[in_date->mon][3] = 0x00;  /* 3 characters only */
				strcpy(out_ptr, mon_name[in_date->mon]);
				if(*in_ptr == 'O')  /* upper case */
					*out_ptr -= 'a' - 'A';
				out_ptr += strlen(mon_name[in_date->mon]);
				in_ptr += d_flag;
				inbuf_len -= d_flag;
				break;
				
		default:
			kw_flag = 0;
			*out_ptr++ = *in_ptr;
		}  /* switch */
		inbuf_len--;
		in_ptr++;
	} /* while */
	if(!kw_flag)  /* sprinf will auto add NULL at end */
		*out_ptr = 0;
	return (SHORT)((UWORD)out_ptr - (UWORD)out_buf);
}

UWORD TmrQuantizePeriod(UWORD period)
{
	UWORD adj_period;
	
#if		(QUANT_MIN == 10)
	if (period < 1000) {									// Q: < 1sec ?
		adj_period = (period/10)*(10/QUANT_MIN);
		if (adj_period == 0)
			adj_period = 1;
		tmr_timebase = TMRBASE10MS;
	} else {
		adj_period = (period/50)*(50/QUANT_MIN);
	}
#else
	if (period < 10) {										// Q: < 10ms ?
		adj_period = (period/5)*(5/QUANT_MIN);
		if (adj_period == 0)
			adj_period = 1;
		tmr_timebase = TMRBASE5MS;
		SET_PERVAL(tmr_timebase);
	} else if (period < 1000) {								// Q: < 1sec ?
		adj_period = (period/10)*(10/QUANT_MIN);
		if (tmr_timebase > TMRBASE10MS) {
			tmr_timebase = TMRBASE10MS;
			SET_PERVAL(tmr_timebase);
		}
	} else {												// otherwise, >= 1sec
		adj_period = (period/50)*(50/QUANT_MIN);
	}
#endif	(QUANT_MIN == 10)
	
	return adj_period;
}

void TmrComputeTimeBase()
{
	UWORD i;
	
	tmr_timebase = TMRBASE50MS;
	for (i=0; i<TMR_MAX_ENTRY; i++)
	{
		if (tmr_table[i].label)
		{
#if		(QUANT_MIN == 10)
			if (tmr_table[i].period < 1000/QUANT_MIN)		// Q: < 1sec ?
				tmr_timebase = TMRBASE10MS;
#else
			if (tmr_table[i].period < 10/QUANT_MIN) {		// Q: < 10ms ?
				tmr_timebase = TMRBASE5MS;
				break;
			}
			else if (tmr_table[i].period < 1000/QUANT_MIN)	// Q: < 1sec ?
				tmr_timebase = TMRBASE10MS;
#endif	(QUANT_MIN == 10)
		}
	}
	
#if		(QUANT_MIN == 10)
	if (tmr_timebase == TMRBASE50MS)		// Q: 50ms-based timer ?
		tmr_cnt = (tmr_cnt/(50/QUANT_MIN))*(50/QUANT_MIN);
#else
	if (tmr_timebase == TMRBASE10MS)			// Q: 10ms-based timer ?
		tmr_cnt = (tmr_cnt/(10/QUANT_MIN))*(10/QUANT_MIN);
	else if (tmr_timebase == TMRBASE50MS)		// Q: 50ms-based timer ?
		tmr_cnt = (tmr_cnt/(50/QUANT_MIN))*(50/QUANT_MIN);
	else										// otherwise, 5ms-based timer
		tmr_cnt = (tmr_cnt/(5/QUANT_MIN))*(5/QUANT_MIN);
#endif	(QUANT_MIN == 10)
	SET_PERVAL(tmr_timebase);
}
