/*
 *	Portable timer interface definitions (frmb made i386 specific..)
 *	Copyright (C) 1996-1999 Jim Moores
 *	Modifications (C) 2001-2005 Fred Barnes  <frmb@kent.ac.uk>
 *
 *	This program is free software; you can redistribute it and/or modify
 *	it under the terms of the GNU General Public License as published by
 *	the Free Software Foundation; either version 2 of the License, or
 *	(at your option) any later version.
 *
 *	This program is distributed in the hope that it will be useful,
 *	but WITHOUT ANY WARRANTY; without even the implied warranty of
 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *	GNU General Public License for more details.
 *
 *	You should have received a copy of the GNU General Public License
 *	along with this program; if not, write to the Free Software
 *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#ifndef TIMER_HEADER
#define TIMER_HEADER

#if defined(HAVE_CONFIG_H)
#include <config.h>
#endif

typedef int Time;

#include <rts.h>
#include <i386/sched.h>

#define GetTimeField(X) ((Time)((X)[Time_f]))
#define SetTimeField(X,V) ((word *)(X))[Time_f] = (V)
#define IncTimeField(X) (((word *)(X))[Time_f])++

extern sync_type sf;

#ifdef OOS_BUILD
static int ualarm (int delay, int interval)
{
	oos_panic("ualarm not implemented");
}
#endif	/* OOS_BUILD */

/* define as extern __inline__ */
#ifdef ENABLE_CPU_TIMERS
	#ifdef TARGET_OS_CYGWIN
		#define CPUFACTOR "_glob_cpufactor"
		#define CPUKHZ "_glob_cpukhz"
	#else
		#define CPUFACTOR "glob_cpufactor"
		#define CPUKHZ "glob_cpukhz"
	#endif
#endif


#ifdef __TIMER_C
Time Time_GetTime (void)
#else
extern __inline__ Time Time_GetTime (void)
#endif
{
	#ifdef ENABLE_CPU_TIMERS

		Time time_in_us;

		__asm__ __volatile__ ("			\n" \
			"	pushl	%%edx		\n" \
			"	pushl	%%ebx		\n" \
			"	pushl	%%eax		\n" \
			"				\n" \
			"	.byte	0x0f, 0x31	\n" \
			"	movl	%%edx, %%ebx	\n" \
			"	mull	" CPUFACTOR "	\n" \
			"	movl	%%ebx, %%eax	\n" \
			"	movl	%%edx, %%ebx	\n" \
			"	mull	" CPUFACTOR "	\n" \
			"	addl	%%ebx, %%eax	\n" \
			"	movl	%%eax, (%%ecx)	\n" \
			"				\n" \
			"	popl	%%eax		\n" \
			"	popl	%%ebx		\n" \
			"	popl	%%edx		\n" \
			: : "c" (&time_in_us) : "cc", "memory");

		return time_in_us;
	#else
		return rtime();
	#endif
}


#ifdef ENABLE_CPU_TIMERS

#if !defined(__SCHED_C)

#if defined(CAN_DO_SCHEDSTRUCT)
extern sched_vars_type svars;
#else
extern int timeout;
#endif	/* !CAN_DO_SCHEDSTRUCT */
#endif


#if defined(CAN_DO_SCHEDSTRUCT)
	#define dummyvar svars.dummy0
#else
	extern Time timeout;

	#define dummyvar timeout
#endif

/*
 *	this checks to see whether the time stored in sf.ftime is <= the current time
 */
#ifdef __TIMER_C
int Time_PastTimeout (void)
#else
extern __inline__ int Time_PastTimeout (void)
#endif
{
	__asm__ __volatile__ ("				\n" \
		"	pushl	%%edx			\n" \
		"					\n" \
		"	.byte	0x0f, 0x31		\n" \
		"	cmpl	4(%%ecx), %%edx		\n" \
		"	ja	1f			\n" \
		"	jb	3f			\n" \
		"	cmpl	0(%%ecx), %%eax		\n" \
		"	ja	1f			\n" \
		"3:					\n" \
		"	xorl	%%eax, %%eax		\n" \
		"	jmp	2f			\n" \
		"1:					\n" \
		"	movl	$1, %%eax		\n" \
		"2:					\n" \
		"	popl	%%edx			\n"
		: "=a" (dummyvar) : "c" (&(sf.ftime)) : "cc", "memory");

#if 0
{
	unsigned int hi, lo;

	__asm__ __volatile__ ("				\n" \
		"	.byte	0x0f, 0x31		\n" \
		: "=d" (hi), "=a" (lo) : : "cc", "memory");

	fprintf (stderr, "Time_PastTimeout() = %d.  ftime = [%8.8x %8.8x] now = [%8.8x %8.8x]\n", dummyvar, *(unsigned int *)(((void *)&(sf.ftime) + 4)), *(unsigned int *)(&(sf.ftime)), hi, lo);
}
#endif
	return dummyvar;
}

/*
 *	this sets the value in sf.ftime (to something in the future only)
 */
#ifdef __TIMER_C
void Time_SetPastTimeout (Time delay)
#else
extern __inline__ void Time_SetPastTimeout (Time delay)
#endif
{
	__asm__ __volatile__ ("				\n" \
		"	pushl	%%edx			\n" \
		"	pushl	%%eax			\n" \
		"					\n" \
		"	xorl	%%edx, %%edx		\n" \
		"	movl	%%ebx, %%eax		\n" \
		"	mull " CPUKHZ "			\n" \
		"	shr	$10, %%eax		\n" \
		"	movl	%%edx, %%ebx		\n" \
		"	shl	$22, %%ebx		\n" \
		"	andl	$0x003fffff, %%eax	\n" \
		"	orl	%%ebx, %%eax		\n" \
		"	movl	%%eax, 0(%%ecx)		\n" \
		"	shr	$10, %%edx		\n" \
		"	movl	%%edx, 4(%%ecx)		\n" \
		"	.byte	0x0f, 0x31		\n" \
		"	addl	%%eax, 0(%%ecx)		\n" \
		"	adcl	%%edx, 4(%%ecx)		\n" \
		"					\n" \
		"	popl	%%eax			\n" \
		"	popl	%%edx			\n" \
		: "=b" (dummyvar) : "b" (delay), "c" (&(sf.ftime)) : "cc", "memory");
#if 0
{
	unsigned int hi, lo;

	__asm__ __volatile__ ("				\n" \
		"	.byte	0x0f, 0x31		\n" \
		: "=d" (hi), "=a" (lo) : : "cc", "memory");

	fprintf (stderr, "Time_SetPastTimeout(%d).  ftime = [%8.8x %8.8x] now = [%8.8x %8.8x]\n", delay, *(unsigned int *)(((void *)&(sf.ftime) + 4)), *(unsigned int *)(&(sf.ftime)), hi, lo);
}
#endif
	return;
}
#endif


#ifdef __TIMER_C
Time Time_PLUS(Time t1, Time t2)
#else
extern __inline__ Time Time_PLUS(Time t1, Time t2)
#endif
{
	return (t1+t2);
}


#ifdef __TIMER_C
Time Time_MINUS(Time t1, Time t2)
#else
extern __inline__ Time Time_MINUS(Time t1, Time t2)
#endif
{
	return (t1-t2);
}


#ifdef __TIMER_C
bool Time_AFTER(Time t1, Time t2)
#else
extern __inline__ bool Time_AFTER(Time t1, Time t2)
#endif
{
	Time temp_time;

	temp_time = t2 - t1;
	if (temp_time & 0x80000000) {
		return 1;
	}
	return 0;
}


#ifdef __TIMER_C
bool Time_PollAlarm(void)
#else
extern __inline__ bool Time_PollAlarm(void)
#endif
{
	return (sf.u.s.tim_sync);
}


#ifdef __TIMER_C
void Time_SetAlarm(Time t)
#else
extern __inline__ void Time_SetAlarm(Time t)
#endif
{
	ualarm(t, 0);
}


#ifdef __TIMER_C
void Time_ResetAlarm(void)
#else
extern __inline__ void Time_ResetAlarm(void)
#endif
{
	ualarm(0,0);
}

#endif /* timer.h */
