#ifndef DOSEMU_TIMERS_H
#define DOSEMU_TIMERS_H

#include <unistd.h>
#include "types.h"

/* A 'magical' constant used to force the result we want, in this case
 * getting 100Hz out of SIGALRM_call()
 */
#define TIMER_DIVISOR   6

extern void timer_tick(void);
extern Bit8u pit_inp(ioport_t, void *);
extern void pit_outp(ioport_t, Bit8u, void *);

/* The divisor used to time faster-than-1sec activities (floppy,printer,
 * IPX) - see signal.c
 */
#define PARTIALS	5

#define BIOS_TICK_ADDR		0x46c
#define TICK_OVERFLOW_ADDR	0x470

/* underlying clock rate in HZ */
#define PIT_TICK_RATE		1193182
#define PIT_TICK_RATE_k		1193.182
#define PIT_TICK_RATE_M		1.193182
#define PIT_TICK_RATE_f		0.193182

/* number of 18.2Hz ticks = 86400*PIT_TICK_RATE/65536 = 1573042.6756 */
#define TICKS_IN_A_DAY		1573042

#define PIT_TIMERS      4            /* 4 timers (w/opt. at 0x44)   */

/* --------------------------------------------------------------------- */

#define JIFFIE_TIME		(1000000/sysconf(_SC_CLK_TCK))

/* this specifies how many microseconds int 0x2f, ax=0x1680, will usleep().
 * we don't really have a "give up time slice" primitive, but something
 * like this works okay...thanks to Andrew Tridgell.
 * same for int 0x15, ax=0x1000 (TopView/DESQview).
 */
#define INT2F_IDLE_USECS	(JIFFIE_TIME*8)
#define INT15_IDLE_USECS	(JIFFIE_TIME*8)
#define INT28_IDLE_USECS	(JIFFIE_TIME/2)
void reset_idle(int val);
void alarm_idle(void);
void trigger_idle(void);
void untrigger_idle(void);
void idle(int threshold1, int threshold, int threshold2, const char *who);
void idle_enable(int threshold, int threshold2, const char *who);
void idle_enable2(int threshold1, int threshold, int threshold2,
    const char *who);
void dosemu_sleep(void);
void cpu_idle(void);
int timer_get_vpend(int timer);
void pit_late_init(void);

int CAN_SLEEP(void);

/* --------------------------------------------------------------------- */
/*	New unified timing macros with/without Pentium rdtsc - AV 8/97	 */
/* --------------------------------------------------------------------- */
/*
 * replace 64/32 divide instructions with 64*32 multiply
 * how it works: we multiply by f = (1/x)<<32 = (1<<32)/x
 *	input = tH:tL, k
 *		tL*k ----->      edx:eax(discard)
 *		tH*k ----->  edx:eax
 *		sum	     -------
 *	output =	     edx:eax
 *
 * all operations unsigned, it really shines on K6 where mul=3 cycles!
 *
 * GCC also generates 2 mul instructions, no need to use asm here (BO).
 */
static __inline__ hitimer_t _mul64x32_(hitimer_t v, unsigned long f)
{
	return (unsigned long)(v >> 32) * (unsigned long long)f +
	  (((unsigned long)(v & 0xffffffff) * (unsigned long long)f) >> 32);
}

#define LLF_US		(unsigned long long)0x100000000LL
#define LLF_TICKS	(unsigned long long)(LLF_US*PIT_TICK_RATE_M)

/* warning - 'k' must be a constant here! (but can also be a float) */
#define T64DIV(t,k)	_mul64x32_((t),(LLF_US/k))
#define TICKtoMS(t)	({unsigned long _m=T64DIV(t,PIT_TICK_RATE_k); _m;})

#define CPUtoTICK(t)	_mul64x32_((t),config.cpu_tick_spd)
/* t*1.193 = t+t*0.193 */
#define UStoTICK(t)	({ hitimer_t _l=(t); \
			_l+_mul64x32_(_l,(LLF_US*PIT_TICK_RATE_f)); })

#define TICKtoUS(t)	({unsigned long _m=T64DIV(t,PIT_TICK_RATE_M); _m;})

/* --------------------------------------------------------------------- */

extern hitimer_u ZeroTimeBase;
extern hitimer_t t_vretrace;

static inline hitimer_t GETTSC(void)
{
	hitimer_t d;
#if defined(__x86_64__)
	unsigned int lo, hi;
	asm volatile("rdtsc" : "=a" (lo), "=d" (hi));
	d = lo | ((unsigned long)hi << 32);
#elif defined(__i386__)
	__asm__ __volatile__ ("rdtsc" : "=A" (d));
#else
	d = 0;
#endif
	return d;
}

#define TSCtoUS(t) _mul64x32_(t, config.cpu_spd)

/* 1 us granularity */
extern hitimer_t GETusTIME(int sc);
extern hitimer_t GETusSYSTIME(void);

/* 838 ns granularity */
extern hitimer_t GETtickTIME(int sc);

int stop_cputime (int);
int restart_cputime (int);
extern int cpu_time_stop;	/* for dosdebug */
void uncache_time(void);

void freeze_dosemu_manual(void);
void freeze_dosemu(void);
void unfreeze_dosemu(void);
extern int dosemu_frozen;
extern int dosemu_user_froze;
extern hitimer_t pic_sys_time;

/* --------------------------------------------------------------------- */

extern Bit8u pit_control_inp(ioport_t, void *);
extern void pit_control_outp(ioport_t port, Bit8u val, void *);
extern void get_time_init(void);
extern void cputime_late_init(void);
extern void do_sound(Bit16u period);
void int_yield(void);

#endif /* DOSEMU_TIMERS_H */
