/*****************************************************************************
 *
 *  Programmable Interval Timer 8253/8254
 *
 *  Three Independent Timers
 *  (gate, clock, out pins)
 *
 *  8254 has an additional readback feature
 *
 *  Revision History
 *      8-Jul-2004 - AJ:    Fixed some bugs. Styx now runs correctly.
 *                          Implemented 8254 features.
 *      1-Mar-2004 - NPW:   Did an almost total rewrite and cleaned out much
 *                          of the ugliness in the previous design.  Bug #430
 *                          seems to be fixed
 *      1-Jul-2000 - PeT:   Split off from PC driver and componentized
 *
 *****************************************************************************/

#include <math.h>
#include "driver.h"
#include "includes/pit8253.h"
#include "state.h"



/***************************************************************************

	Structures & macros

***************************************************************************/

#define	MAX_TIMER		3
#define	VERBOSE			0

#if	(VERBOSE ==	2)
#define	LOG1(msg)		logerror msg
#define	LOG2(msg)		logerror msg
#elif (VERBOSE == 1)
#define	LOG1(msg)		logerror msg
#define	LOG2(msg)		(void)(0)
#else
#define	LOG1(msg)		(void)(0)
#define	LOG2(msg)		(void)(0)
#endif


#define	TIMER_TIME_NEVER ((UINT64) -1)

#define	CYCLES_NEVER ((UINT32) -1)

struct pit8253_timer
{
	double clockin;					/* input clock frequency in	Hz */

	void (*output_callback)(int);	/* callback	function for when output changes */
	void (*freq_callback)(double);	/* callback	function for when output frequency changes */

	mame_time last_updated;			/* time	when last updated */
	double saved_time;				/* last_updated	relative to	time when state	saved/restored */

	mame_timer *outputtimer;		/* MAME	timer for output change	callback */
	mame_timer *freqtimer;			/* MAME	timer for output frequency change callback */

	UINT16 value;					/* current counter value ("CE" in Intel docs) */
	UINT16 latch;					/* latched counter value ("OL" in Intel docs) */
	UINT16 count;					/* new counter value ("CR" in Intel docs) */
	UINT8 control;					/* 6-bit control byte */
	UINT8 status;					/* status byte - 8254 only */
	UINT8 lowcount;					/* LSB of new counter value	for	16-bit writes */
	int	rmsb;						/* 1 = Next	read is	MSB	of 16-bit value	*/
	int	wmsb;						/* 1 = Next	write is MSB of	16-bit value */
	int	output;						/* 0 = low,	1 =	high */

	int	gate;						/* gate	input (0 = low,	1 =	high) */
	int	latched_count;				/* number of bytes of count	latched	*/
	int	latched_status;				/* 1 = status latched (8254	only) */
	int	null_count;					/* 1 = mode	control	or count written, 0	= count	loaded */
	int	phase;						/* see phase definition	tables in simulate2(), below */

	UINT32 cycles_to_output;		/* cycles until	output callback	called */
	UINT32 cycles_to_freq;			/* cycles until	frequency callback called */
	UINT32 freq_count;				/* counter period for periodic modes, 0	if counter non-periodic	*/
};

struct pit8253
{
	struct pit8253_config *config;
	struct pit8253_timer timers[MAX_TIMER];
};

#define	CTRL_ACCESS(control)		(((control)	>> 4) &	0x03)
#define	CTRL_MODE(control)			(((control)	>> 1) &	(((control)	& 0x04)	? 0x03 : 0x07))
#define	CTRL_BCD(control)			(((control)	>> 0) &	0x01)


static int pit_count;
static struct pit8253 *pits;



/***************************************************************************

	Functions

***************************************************************************/

static struct pit8253 *get_pit(int which)
{
	assert(which >=	0);
	assert(which < pit_count);
	return &pits[which];
}


static struct pit8253_timer	*get_timer(struct pit8253 *pit,int which)
{
	which &= 3;
	if (which < MAX_TIMER)
		return &pit->timers[which];
	return NULL;
}


static UINT32 decimal_from_bcd(UINT16 val)
{
	/* In BCD mode,	a nybble loaded	with value A-F counts down the same	as in
	   binary mode,	but	wraps around to	9 instead of F after 0,	so loading the
	   count register with 0xFFFF gives	a period of
			  0xF  - for the units to count	down to	0
	   +   10*0xF  - for the tens to count down	to 0
	   +  100*0xF  - for the hundreds to count down	to 0
	   + 1000*0xF  - for the thousands to count	down to	0
	   = 16665 cycles
	*/
	return
		((val>>12) & 0xF) *	 1000 +
		((val>>	8) & 0xF) *	  100 +
		((val>>	4) & 0xF) *	   10 +
		( val	   & 0xF);
}


static UINT32 adjusted_count(int bcd,UINT16	val)
{
	if (bcd	== 0)
		return val == 0	? 0x10000 :	val;
	return val == 0	? 10000	: decimal_from_bcd(val);
}


/* This	function subtracts 1 from timer->value "cycles" times, taking into
   account binary or BCD operation,	and	wrapping around	from 0 to 0xFFFF or
   0x9999 as necessary.	*/
static void	decrease_counter_value(struct pit8253_timer	*timer,UINT64 cycles)
{
	UINT16 value;
	int units, tens, hundreds, thousands;
	
	if (CTRL_BCD(timer->control) ==	0)
	{
		timer->value -=	(cycles	& 0xFFFF);
		return;
	}

	value = timer->value;
	units	  =	 value		  &	0xF;
	tens	  =	(value >>  4) &	0xF;
	hundreds  =	(value >>  8) &	0xF;
	thousands =	(value >> 12) &	0xF;

	if (cycles <= units)
	{
		units -= cycles;
	}
	else
	{
		cycles -= units;
		units =	(10	- cycles%10)%10;

		cycles =(cycles+9)/10; /* the +9	is so we get a carry if	cycles%10 wasn't 0 */
		if (cycles <= tens)
		{
			tens -=	cycles;
		}
		else
		{
			cycles -= tens;
			tens = (10 - cycles%10) % 10;

			cycles = (cycles+9) / 10;
			if (cycles <= hundreds)
			{
				hundreds -=	cycles;
			}
			else
			{
				cycles -= hundreds;
				hundreds = (10 - cycles%10)%10;
				cycles=(cycles+9)/10;
				thousands =	(10	+ thousands	- cycles%10)%10;
			}
		}
	}

	timer->value = (thousands << 12) | (hundreds <<	8) | (tens << 4) | units;
}


static double get_frequency(struct pit8253_timer *timer)
{
	LOG2(("pit8253: get_frequency() : %lf\n",(double)(timer->freq_count == 0 ? 0 : timer->clockin / timer->freq_count)));
	return timer->freq_count ==	0 ?	0 :	timer->clockin / timer->freq_count;
}


/* Call	the	frequency callback in "cycles" cycles */
static void	freq_callback_in(struct	pit8253_timer *timer,UINT32	cycles)
{
	LOG2(("pit8253: freq_callback_in(): %d cycles\n",cycles));

	if (timer->freq_callback ==	NULL)
	{
		return;
	}

	if (timer->clockin == 0	|| cycles == CYCLES_NEVER)
	{
		mame_timer_reset(timer->freqtimer,time_never);
	}
	else
	{
		mame_timer_reset(timer->freqtimer,double_to_mame_time(cycles / timer->clockin));
	}
	timer->cycles_to_freq =	cycles;
}


static void	set_freq_count(struct pit8253_timer	*timer)
{
	int	mode = CTRL_MODE(timer->control);
	UINT32 freq_count;

	if ((mode == 2 || mode == 3) &&	timer->gate	!= 0 &&	timer->phase !=	0)
	{
		freq_count = adjusted_count(CTRL_BCD(timer->control),timer->count);
	}
	else
	{
		freq_count = 0;
	}

	if (freq_count != timer->freq_count)
	{
		timer->freq_count =	freq_count;
		if (timer->freq_callback !=	NULL)
		{
			timer->freq_callback(get_frequency(timer));
			freq_callback_in(timer,CYCLES_NEVER);
		}
	}

	LOG2(("pit8253: set_freq_count() : %d\n",freq_count));
}


/* Call	the	output callback	in "cycles" cycles */
static void	trigger_countdown(struct pit8253_timer *timer)
{
	LOG2(("pit8253: trigger_countdown()\n"));

	timer->phase = 1;
	timer->value = timer->count;
	if (CTRL_MODE(timer->control) == 3 && timer->output	== 0)
		timer->value &=	0xfffe;

	set_freq_count(timer);
}


static void	set_output(struct pit8253_timer	*timer,int output)
{
	if (output != timer->output)
	{
		timer->output =	output;
		if (timer->output_callback != NULL)
		{
			timer->output_callback(output);
		}
	}
}


/* This	emulates timer "timer" for "elapsed_cycles" cycles and assumes no
   callbacks occur during that time. */
static void	simulate2(struct pit8253_timer *timer,UINT64 elapsed_cycles)
{
	UINT32 adjusted_value;
	int	bcd	= CTRL_BCD(timer->control);
	int	mode = CTRL_MODE(timer->control);
	int	cycles_to_output = 0;

	if (timer->cycles_to_freq != CYCLES_NEVER)
	{
		timer->cycles_to_freq -= elapsed_cycles;
	}

	LOG2(("pit8253: simulate2(): simulating %d cycles in mode %d, bcd = %d, phase = %d, gate = %d, value = 0x%04x\n",
		  (int)elapsed_cycles,mode,bcd,timer->phase,timer->gate,timer->value));

	switch (mode) {
	case 0:
		/* Mode	0: (Interrupt on Terminal Count)

		          +------------------
		          |
		----------+
		  <- n+1 ->

		  ^
		  +- counter load

		phase|output|length	 |value|next|comment
		-----+------+--------+-----+----+----------------------------------
			0|low   |infinity|     |1   |waiting for count
			1|low   |1       |     |2   |internal delay	when counter loaded
			2|low   |n       |n..1 |3   |counting down
			3|high  |infinity|0..1 |3   |counting down

		Gate level sensitive only. Low disables	counting, high enables it. */

		if (timer->gate	== 0 ||	timer->phase ==	0)
		{
			cycles_to_output = CYCLES_NEVER;
		}
		else
		{
			if (elapsed_cycles > 0 && timer->phase == 1)
			{
				--elapsed_cycles;
				timer->phase = 2;
			}

			if (timer->phase ==	2)
			{
				adjusted_value = adjusted_count(bcd,timer->value);
				if (elapsed_cycles < adjusted_value)
				{
					/* Counter didn't wrap */
					decrease_counter_value(timer,elapsed_cycles);
				}
				else
				{
					/* Counter wrapped,	output goes	high */
					elapsed_cycles -= adjusted_value;
					timer->phase = 3;
					timer->value = 0;
				}
			}

			if (timer->phase ==	3)
			{
				decrease_counter_value(timer,elapsed_cycles);
				cycles_to_output = CYCLES_NEVER;
			}
			else
			{
				cycles_to_output = adjusted_count(bcd,timer->value)	+ (timer->phase	== 1 ? 1 : 0);
			}
		}

		set_output(timer,timer->phase == 3 ? 1 : 0);
		break;


	case 1:
		/* Mode	1: (Hardware Retriggerable One-Shot	a.k.a. Programmable	One-Shot)

		--+	      +------------------
		  |       |
		  +-------+
		  <-  n	 ->

		  ^
		  +- trigger

		phase|output|length	 |value|next|comment
		-----+------+--------+-----+----+----------------------------------
			0|high	|infinity|0..1 |1	|counting down
			1|low	|n		 |n..1 |0	|counting down

		Gate rising-edge sensitive only.
		Rising edge	initiates counting and resets output after next	clock. */

		adjusted_value = adjusted_count(bcd,timer->value);
		if (elapsed_cycles < adjusted_value)
		{
			/* Counter didn't wrap */
			decrease_counter_value(timer,elapsed_cycles);
			cycles_to_output = (timer->phase ==	0 ?	CYCLES_NEVER : adjusted_count(bcd,timer->value));
		}
		else
		{
			/* Counter wrapped,	output goes	high */
			elapsed_cycles -= adjusted_value;
			timer->phase = 0;
			timer->value = 0;
			decrease_counter_value(timer,elapsed_cycles);
			cycles_to_output = CYCLES_NEVER;
		}
		set_output(timer,timer->phase == 0 ? 1 : 0);
		break;


	case 2:
		/* Mode	2: (Rate Generator)

		--------------+	+---------+	+----
		              | |         | |
		              +-+         +-+
		    <-    n	   -X-    n	   ->
					  <1>
			^
			+- counter load	or trigger

		phase|output|length	 |value|next|comment
		-----+------+--------+-----+----+----------------------------------
			0|high	|infinity|	   |1	|waiting for count
			1|v!=1	|n		 |n..1 |1	|counting down

		Counter	rewrite	has	no effect until	repeated

		Gate rising-edge and level sensitive.
		Gate low disables counting and sets	output immediately high.
		Rising-edge	reloads	count and initiates	counting
		Gate high enables counting.	*/

		if (timer->gate	== 0 ||	timer->phase ==	0)
		{
			/* Gate	low	or mode	control	write forces output	high */
			set_output(timer,1);
			cycles_to_output = CYCLES_NEVER;
		}
		else
		{
			adjusted_value = adjusted_count(bcd,timer->value);
			if (elapsed_cycles < adjusted_value)
			{
				/* Counter didn't wrap */
				decrease_counter_value(timer,elapsed_cycles);
			}
			else
			{
				/* Counter wrapped around one or more times	*/
				elapsed_cycles -= adjusted_value;
				trigger_countdown(timer);
				decrease_counter_value(timer,elapsed_cycles	% adjusted_count(bcd,timer->count));
			}
			cycles_to_output = (timer->value ==	1 ?	1 :	(adjusted_count(bcd,timer->value) -	1));

			set_output(timer,timer->value != 1 ? 1 : 0);
		}
		break;


	case 3:
		/* Mode	3: (Square Wave	Generator)

		----------------+			+-----------+			+----
		                |           |           |           |
		                +-----------+           +-----------+
			<- (n+1)/2 -X-	 n/2   ->
			^
			+- counter load	or trigger

		phase|output|length	 |value|next|comment
		-----+------+--------+-----+----+----------------------------------
			0|high	|infinity|	   |1	|waiting for count
			1|		|infinity|n..0 |1	|counting down double speed

		Counter	rewrite	has	no effect until	repeated (output falling or	rising)

		Gate rising-edge and level sensitive.
		Gate low disables counting and sets	output immediately high.
		Rising-edge	reloads	count and initiates	counting
		Gate high enables counting.	*/

		if (timer->gate	== 0 ||	timer->phase ==	0)
		{
			/* Gate	low	or mode	control	write forces output	high */
			set_output(timer,1);
			cycles_to_output = CYCLES_NEVER;
		}
		else
		{
			adjusted_value = adjusted_count(bcd,timer->value);
			if ((elapsed_cycles<<1)	< adjusted_value)
			{
				/* Counter didn't wrap around */
				decrease_counter_value(timer,elapsed_cycles<<1);
			}
			else
			{
				/* Counter wrapped around one or more times	*/
				elapsed_cycles -= ((adjusted_value+1)>>1);

				set_output(timer,1 - timer->output);
				trigger_countdown(timer);

				elapsed_cycles %= adjusted_count(bcd,timer->count);
				adjusted_value = adjusted_count(bcd,timer->value);
				if ((elapsed_cycles<<1)	>= adjusted_value)
				{
					/* Counter wrapped around an even number of	times */
					elapsed_cycles -= ((adjusted_value+1)>>1);

					set_output(timer,1 - timer->output);
					trigger_countdown(timer);
				}
				decrease_counter_value(timer,elapsed_cycles<<1);
			}
			cycles_to_output = (adjusted_count(bcd,timer->value) + 1) >> 1;
		}
		break;


	case 4:
	case 5:
		/* Mode	4: (Software Trigger Strobe)
		   Mode	5: (Hardware Trigger Strobe)

		--------------+	+--------------------
		              | |
		              +-+
			<-	n+1	 ->
			^		  <1>
			+- counter load	(mode 4) or	trigger	(mode 5)

		phase|output|length	 |value|next|comment
		-----+------+--------+-----+----+----------------------------------
			0|high	|infinity|0..1 |0	|waiting for count
			1|high	|1		 |	   |2	|internal delay	when counter loaded
			2|high	|n		 |n..1 |3	|counting down
			3|low	|1		 |0	   |0	|strobe

		Mode 4 only: counter rewrite loads new counter
		Mode 5 only: count not reloaded	immediately.
		Mode control write doesn't stop count but sets output high

		Mode 4 only: Gate level	sensitive only.	Low	disables counting, high	enables	it.
		Mode 5 only: Gate rising-edge sensitive	only. Rising edge initiates	counting */

		if (timer->gate	== 0 &&	mode ==	4)
		{
			cycles_to_output = CYCLES_NEVER;
		}
		else
		{
			if (elapsed_cycles > 0 && timer->phase == 1)
			{
				--elapsed_cycles;
				timer->phase = 2;
			}

			if (elapsed_cycles > 0 && timer->phase == 3)
			{
				--elapsed_cycles;
				timer->phase = 0;
				decrease_counter_value(timer,1);
			}

			if (timer->value ==	0 && timer->phase == 2)
				adjusted_value = 0;
			else
				adjusted_value = adjusted_count(bcd,timer->value);

			if (elapsed_cycles < adjusted_value)
			{
				/* Counter didn't wrap */
				decrease_counter_value(timer,elapsed_cycles);
			}
			else
			{
				elapsed_cycles -= adjusted_value;
				timer->value = 0;
				if (elapsed_cycles == 0)
				{
					/* We hit the strobe cycle */
					timer->phase = 3;
				}
				else
				{
					decrease_counter_value(timer,elapsed_cycles);
					timer->phase = 0;
				}
			}
			switch(timer->phase) {
			case 0:
				cycles_to_output = CYCLES_NEVER;
				break;
			case 1:
				cycles_to_output = adjusted_count(bcd,timer->value)	+ 1;
				break;
			case 2:
				cycles_to_output = adjusted_count(bcd,timer->value);
				break;
			case 3:
				cycles_to_output = 1;
				break;
			}
		}
		set_output(timer,timer->phase != 3 ? 1 : 0);
		break;
	}

	assert(cycles_to_output	!= 0);

	if (timer->output_callback != NULL)
	{
		timer->cycles_to_output	= cycles_to_output;
		if (cycles_to_output ==	CYCLES_NEVER ||	timer->clockin == 0)
		{
			mame_timer_reset(timer->outputtimer,time_never);
		}
		else
		{
			mame_timer_reset(timer->outputtimer,
				double_to_mame_time(cycles_to_output / timer->clockin));
		}
	}

	if (timer->cycles_to_freq == 0)
		timer->cycles_to_freq =	CYCLES_NEVER;
}


/* This	emulates timer "timer" for "elapsed_cycles" cycles, broken down into
   sections	punctuated by callbacks.

   The loop	technically	should never execute even once.	It's here to eliminate
   the following potential bug:

   1) The mame timer isn't perfectly accurate.
   2) The output callback is executed too late,	after an update	which
	  brings the timer's local time past the callback time.
   3) A	short pulse	is skipped.
   4) That short pulse would have triggered	an interrupt. The interrupt	is
	  skipped.

   This	is a loop instead of an	"if" statement in case the mame timer is
   inaccurate by more than one cycle, and the output changed multiple
   times during	the	discrepancy. In	practice updates should	still be O(1).
*/
static void	simulate(struct	pit8253_timer *timer,UINT64	elapsed_cycles)
{
	while ((timer->cycles_to_output	!= CYCLES_NEVER	&&
			timer->cycles_to_output	<= elapsed_cycles) ||
		   (timer->cycles_to_freq != CYCLES_NEVER &&
			timer->cycles_to_freq <= elapsed_cycles))
	{
		UINT32 cycles_to_callback;

		if (timer->cycles_to_output	< timer->cycles_to_freq	&&
			timer->cycles_to_output	!= CYCLES_NEVER)
		{
			cycles_to_callback = timer->cycles_to_output;
		}
		else
		{
			cycles_to_callback = timer->cycles_to_freq;
		}

		simulate2(timer,cycles_to_callback);
		elapsed_cycles -= cycles_to_callback;
	}
	simulate2(timer,elapsed_cycles);
}


/* This	brings timer "timer" up to date */
static void	update(struct pit8253_timer	*timer)
{
	/* With	the	82C54's maximum clockin of 10MHz, 64 bits is nearly 60,000
	   years of	time. Should be	enough for now.	*/
	mame_time now =	mame_timer_get_time();
	mame_time elapsed_time = sub_mame_times(now,timer->last_updated);
	UINT64 elapsed_cycles =	mame_time_to_double(elapsed_time) *	timer->clockin;

#if defined(_MSC_VER) && (_MSC_VER <= 1200)
	/* casting unsigned __int64 to double is not supported on VC6 or before */
	timer->last_updated	= add_mame_times(timer->last_updated,double_to_mame_time(((INT64) elapsed_cycles)/timer->clockin));
#else
	timer->last_updated	= add_mame_times(timer->last_updated,double_to_mame_time(elapsed_cycles/timer->clockin));
#endif

	simulate(timer,elapsed_cycles);
}


void pit8253_reset(int which)
{
	struct pit8253 *pit	= get_pit(which);
	struct pit8253_timer *timer;
	int	i;

	LOG1(("pit8253_reset(): resetting pit %d\n", which));

	for	(i = 0;	i <	MAX_TIMER; i++)
	{
		timer =	get_timer(pit,i);
		/* According to	Intel's 8254 docs, the state of a timer is undefined
		   until the first mode	control	word is	written. Here we define	this
		   undefined behaviour */
		timer->control = timer->status = 0x30;
		timer->rmsb	= timer->wmsb =	0;
		timer->count = timer->value	= timer->latch = 0;
		timer->lowcount	= 0;
		timer->gate	= 1;
		timer->output =	0;
		timer->latched_count = 0;
		timer->latched_status =	0;
		timer->null_count =	1;
		timer->cycles_to_output	= timer->cycles_to_freq	= CYCLES_NEVER;

		timer->last_updated	= mame_timer_get_time();

		update(timer);
	}
}


static void freqcallback(int param)
{
	struct pit8253_timer *timer = get_timer(get_pit(param &	0x0F),(param >>	4) & 0x0F);
	UINT64 cycles =	timer->cycles_to_freq;
	double t;

	LOG2(("pit8253: freqcallback(): pit %d, timer %d, %d cycles\n",param & 0xf,(param >> 4) & 0xf,cycles));

	simulate(timer,cycles);

#if defined(_MSC_VER) && (_MSC_VER <= 1200)
	/* casting unsigned __int64 to double is not supported on VC6 or before */
	t = ((INT64) cycles) / timer->clockin;
#else
	t = cycles / timer->clockin;
#endif

	timer->last_updated	= add_mame_times(timer->last_updated, double_to_mame_time(t));
}


static void outputcallback(int param)
{
	struct pit8253_timer *timer = get_timer(get_pit(param &	0x0F),(param >>	4) & 0x0F);
	UINT64 cycles =	timer->cycles_to_output;
	double t;

	LOG2(("pit8253: outputcallback(): pit %d, timer %d, %d cycles\n",param & 0xf,(param >> 4) & 0xf,cycles));

	simulate(timer,cycles);

#if defined(_MSC_VER) && (_MSC_VER <= 1200)
	/* casting unsigned __int64 to double is not supported on VC6 or before */
	t = ((INT64) cycles) / timer->clockin;
#else
	t = cycles / timer->clockin;
#endif

	timer->last_updated	= add_mame_times(timer->last_updated, double_to_mame_time(t));
}


static void	postload(void)
{
	int	i, timerno;
	struct pit8253 *pit;
	struct pit8253_timer *timer;
	mame_time now;

	LOG2(("pit8253: postload()\n"));

	now = mame_timer_get_time();

	for	(i = 0;	i <	pit_count; i++)
	{
		pit	= get_pit(i);
		for	(timerno = 0; timerno <	MAX_TIMER; timerno++)
		{
			timer =	get_timer(pit,timerno);

			/* Adjust for the fact that	the	value returned by
			   mame_timer_get_time() is	discontinuous over a save and restore. */

			timer->saved_time =	mame_time_to_double(sub_mame_times(now,timer->last_updated));
		}
	}
}


static void	presave(void)
{
	int	i, timerno;
	struct pit8253 *pit;
	struct pit8253_timer *timer;
	mame_time now;

	LOG2(("pit8253: presave()\n"));

	now = mame_timer_get_time();

	for	(i = 0;	i <	pit_count; i++)
	{
		pit	= get_pit(i);
		for	(timerno = 0; timerno <	MAX_TIMER; timerno++)
		{
			timer =	get_timer(pit,timerno);

			/* Adjust for the fact that	the	value returned by
			   mame_timer_get_time() is	discontinuous over a save and restore. */

			timer->last_updated	= add_mame_times(double_to_mame_time(timer->saved_time),now);
		}
	}
}


int	pit8253_init(int count,	struct pit8253_config *config)
{
	int	i, timerno,	n=0;
	struct pit8253 *pit;
	struct pit8253_timer *timer;

	LOG2(("pit8253_init(): initializing %d pit(s)\n", count));

	pit_count =	count;
	pits = auto_malloc(count * sizeof(struct pit8253));
	if (!pits)
		goto error;

	memset(pits, 0,	count *	sizeof(struct pit8253));

	for (i = 0;	i < count; i++)
	{
		pit	= get_pit(i);
		pit->config	= &config[i];

		for	(timerno = 0; timerno <	MAX_TIMER; timerno++)
		{
			timer =	get_timer(pit,timerno);

			timer->clockin = pit->config->timer[timerno].clockin;
			timer->output_callback = pit->config->timer[timerno].output_callback;
			timer->freq_callback = pit->config->timer[timerno].clock_callback;

			if (timer->output_callback == NULL)
				timer->outputtimer = NULL;
			else
			{
				timer->outputtimer = mame_timer_alloc(outputcallback);
				if (!timer->outputtimer)
					goto error;
				mame_timer_adjust(timer->outputtimer, time_never, i	| (timerno<<4),	time_zero);

			}
			if (timer->freq_callback ==	NULL)
				timer->freqtimer = NULL;
			else
			{
				timer->freqtimer = mame_timer_alloc(freqcallback);
				if (!timer->freqtimer)
					goto error;
				mame_timer_adjust(timer->freqtimer,	time_never,	i |	(timerno<<4), time_zero);
			}

			/* set up state	save values	*/
			state_save_register_double("pit8253", n, "clockin",          &timer->clockin,          1);
			state_save_register_UINT8 ("pit8253", n, "control",          &timer->control,          1);
			state_save_register_UINT8 ("pit8253", n, "status",           &timer->status,           1);
			state_save_register_UINT8 ("pit8253", n, "lowcount",         &timer->lowcount,         1);
			state_save_register_UINT16("pit8253", n, "latch",            &timer->latch,            1);
			state_save_register_UINT16("pit8253", n, "count",            &timer->count,            1);
			state_save_register_UINT16("pit8253", n, "value",            &timer->value,            1);
			state_save_register_int	  ("pit8253", n, "wmsb",             &timer->wmsb);
			state_save_register_int	  ("pit8253", n, "rmsb",             &timer->rmsb);
			state_save_register_int	  ("pit8253", n, "output",           &timer->output);
			state_save_register_int	  ("pit8253", n, "gate",             &timer->gate);
			state_save_register_int	  ("pit8253", n, "latched_count",    &timer->latched_count);
			state_save_register_int	  ("pit8253", n, "latched_status",   &timer->latched_status);
			state_save_register_int	  ("pit8253", n, "null_count",       &timer->null_count);
			state_save_register_UINT32("pit8253", n, "cycles_to_output", &timer->cycles_to_output, 1);
			state_save_register_UINT32("pit8253", n, "cycles_to_freq",   &timer->cycles_to_freq,   1);
			state_save_register_UINT32("pit8253", n, "freq_count",       &timer->freq_count,       1);
			state_save_register_double("pit8253", n, "saved_time",       &timer->saved_time,       1);
			++n;
		}
		pit8253_reset(i);
	}

	state_save_register_func_postload(postload);
	state_save_register_func_presave(presave);

	LOG1(("pit8253_init(): initialized successfully\n"));

	return 0;

error:
	pit_count =	0;
	pits = NULL;

	LOG1(("pit8253_init(): failed to initialize\n"));

	return 1;
}


/* We recycle bit 0	of timer->value	to hold	the	phase in mode 3	when count is
   odd.	Since read commands	in mode	3 always return	even numbers, we need to
   mask	this bit off. */
static UINT16 masked_value(struct pit8253_timer	*timer)
{
	LOG2(("pit8253: masked_value\n"));

	if (CTRL_MODE(timer->control) == 3)
		return timer->value	& 0xfffe;
	return timer->value;
}

/* Reads only affect the following bits	of the counter state:
	 latched_status
	 latched_count
	 rmsb
  so they don't affect any timer operations except other reads. */
static data8_t pit8253_read(int	which,offs_t offset)
{
	struct pit8253 *pit	= get_pit(which);
	struct pit8253_timer *timer	= get_timer(pit,offset);
	data8_t	data;
	UINT16 value;

	LOG2(("pit8253_read(): pit %d, offset %d\n",which,offset));

	if (timer == NULL)
	{
		/* Reading mode	control	register is	illegal	according to docs */
		/* Experimentally determined: reading it returns 0 */
		data = 0;
	}
	else
	{
		update(timer);

		if (timer->latched_status)
		{
			/* Read	status register	(8254 only)	*/
			data = timer->status;
			timer->latched_status =	0;
		}
		else
		{
			if (timer->latched_count !=	0)
			{
				/* Read	back latched count */
				data = (timer->latch >>	(timer->rmsb !=	0 ?	8 :	0))	& 0xff;
				timer->rmsb	= 1	- timer->rmsb;
				--timer->latched_count;
			}
			else {
				value =	masked_value(timer);

				/* Read	back current count */
				switch(CTRL_ACCESS(timer->control))	{
				case 0:
				default:
					/* This	should never happen	*/
					assert(false);
					data = 0; /* Appease compiler */
					break;

				case 1:
					/* read	counter	bits 0-7 only */
					data = (value >> 0)	& 0xff;
					break;

				case 2:
					/* read	counter	bits 8-15 only */
					data = (value >> 8)	& 0xff;
					break;

				case 3:
					/* read	bits 0-7 first,	then 8-15 */
					data = (value >> (timer->rmsb != 0 ? 8 : 0)) & 0xff;
					timer->rmsb	= 1	- timer->rmsb;
					break;
				}
			}
		}
	}

	LOG2(("pit8253_read(): PIT #%d offset=%d data=0x%02x\n", which, (int) offset, (unsigned) data));
	return data;
}


/* Loads a new value from the bus to the count register	(CR) */
static void	load_count(struct pit8253_timer	*timer,	UINT16 newcount)
{
	int	mode = CTRL_MODE(timer->control);

	LOG1(("pit8253: load_count(): %04x\n",newcount));

	if (newcount ==	1)
	{
		/* Count of	1 is illegal in	modes 2	and	3. What	happens	here was
		   determined experimentally. */
		if (mode ==	2)
			newcount = 2;
		if (mode ==	3)
			newcount = 0;
	}
	timer->count = newcount;
	timer->null_count =	1;
	if (mode ==	2 || mode == 3)
	{
		if (timer->phase ==	0)
		{
			trigger_countdown(timer);
		}
		else
		{
			int	bcd	= CTRL_BCD(timer->control);
			if (mode ==	2)
			{
				freq_callback_in(timer,adjusted_count(bcd,timer->value));
			}
			else
			{
				freq_callback_in(timer,(adjusted_count(bcd,timer->value) + 1) >> 1);
			}
		}
	}
	else
	{
		if (mode ==	0 || mode == 4)
		{
			trigger_countdown(timer);
		}
	}
}


static void	readback(struct	pit8253_timer *timer,int command)
{
	UINT16 value;
	update(timer);

	if ((command & 1) == 0)
	{
		/* readback	status command */
		if (timer->latched_status == 0)
		{
			timer->status =	timer->control | (timer->output	!= 0 ? 0x80	: 0) | (timer->null_count != 0 ? 0x40 :	0);
		}

		timer->latched_status =	1;
	}
	/* Experimentally determined: the read latch command seems to have no
	   effect if we're halfway through a 16-bit read */
	if ((command & 2) == 0 && timer->rmsb == 0)
	{
		/* readback	count command */

		if (timer->latched_count ==	0)
		{
			value =	masked_value(timer);
			switch(CTRL_ACCESS(timer->control))	{
			case 0:
				/* This	should never happen	*/
				assert(false);
				break;

			case 1:
				/* latch bits 0-7 only */
				timer->latch = ((value << 8) & 0xff00) | (value	& 0xff);
				timer->latched_count = 1;
				break;

			case 2:
				/* read	bits 8-15 only */
				timer->latch = (value &	0xff00)	| ((value >> 8)	& 0xff);
				timer->latched_count = 1;
				break;

			case 3:
				/* latch all 16	bits */
				timer->latch = value;
				timer->latched_count = 2;
				break;
			}
		}
	}
}


static void	pit8253_write(int which, offs_t	offset,	int	data)
{
	struct pit8253 *pit	= get_pit(which);
	struct pit8253_timer *timer	= get_timer(pit,offset);
	int	read_command;

	LOG2(("pit8253_write(): PIT #%d offset=%d data=0x%02x\n", which, (int) offset, (unsigned) data));

	if (timer == NULL) {
		/* Write to	mode control register */
		timer =	get_timer(pit, (data >>	6) & 3);
		if (timer == NULL)
		{
			/* Readback	command. Illegal on	8253 */
			/* Todo: find out what (if anything) the 8253 hardware actually	does here. */
			if (pit->config->type == TYPE8254)
			{
				LOG1(("pit8253_write(): PIT #%d readback %02x\n", which, data & 0x3f));

				/* Bit 0 of	data must be 0.	Todo: find out what	the	hardware does if it	isn't. */
				read_command = (data >>	4) & 3;
				if ((data &	2) != 0)
					readback(get_timer(pit,0),read_command);
				if ((data &	4) != 0)
					readback(get_timer(pit,1),read_command);
				if ((data &	8) != 0)
					readback(get_timer(pit,2),read_command);
			}
			return;
		}

		update(timer);

		if (CTRL_ACCESS(data) == 0)
		{
			LOG1(("pit8253_write(): PIT #%d timer=%d readback\n", which, (data >> 6) & 3));

			/* Latch current timer value */
			/* Experimentally verified:	this command does not affect the mode control register */
			readback(timer,1);
		}
		else {
			LOG1(("pit8253_write(): PIT #%d timer=%d bytes=%d mode=%d bcd=%d\n", which, (data >> 6) & 3, (data >> 4) & 3, (data >> 1) & 7,data & 1));

			timer->control = (data & 0x3f);
			timer->null_count =	1;
			timer->wmsb	= timer->rmsb =	0;
			/* Phase 0 is always the phase after a mode	control	write */
			timer->phase = 0;
			set_output(timer,1);
			set_freq_count(timer);
		}
	}
	else
	{
		update(timer);

		switch(CTRL_ACCESS(timer->control))	{
		case 0:
			/* This	should never happen	*/
			assert(false);
			break;

		case 1:
			/* read/write counter bits 0-7 only	*/
			load_count(timer,data);
			break;

		case 2:
			/* read/write counter bits 8-15	only */
			load_count(timer,data << 8);
			break;

		case 3:
			/* read/write bits 0-7 first, then 8-15	*/
			if (timer->wmsb	!= 0)
			{
				load_count(timer,timer->lowcount | (data <<	8));
			}
			else
			{
				timer->lowcount	= data;
				if (CTRL_MODE(timer->control) == 0)
				{
					/* The Intel docs say that writing the MSB in mode 0, phase
					   2 won't stop the count, but this was experimentally
					   determined to be	false. */
					timer->phase = 0;
				}
			}
			timer->wmsb	= 1	- timer->wmsb;
			break;
		}
	}
	update(timer);
}


static void	pit8253_gate_write(int which,int offset,int	data)
{
	struct pit8253_timer *timer	= get_timer(get_pit(which),offset);
	int	mode;
	int	gate = (data!=0	? 1	: 0);

	LOG2(("pit8253_gate_write(): PIT #%d offset=%d gate=%d\n", which, (int) offset, (unsigned) data));

	if (timer == NULL)
		return;

	mode = CTRL_MODE(timer->control);

	if (gate !=	timer->gate)
	{
		update(timer);
		timer->gate	= gate;
		set_freq_count(timer);
		if (gate !=	0 &&
			(mode == 1 || mode == 5	||
			 (timer->phase == 1	&& (mode ==	2 || mode == 3))))
		{
			trigger_countdown(timer);
		}
		update(timer);
	}
}



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

int	pit8253_get_frequency(int which, int timerno)
{
	struct pit8253_timer *timer	= get_timer(get_pit(which),timerno);

	assert(timer !=	NULL);
	update(timer);
	return get_frequency(timer);
}



int	pit8253_get_output(int which, int timerno)
{
	struct pit8253_timer *timer	= get_timer(get_pit(which),timerno);
	int	result;

	assert(timer !=	NULL);
	update(timer);
	result = timer->output;
	LOG2(("pit8253_get_output(): PIT #%d timer=%d result=%d\n", which, timerno, result));
	return result;
}



void pit8253_set_clockin(int which,	int	timerno, double	new_clockin)
{
	struct pit8253_timer *timer	= get_timer(get_pit(which),timerno);

	LOG2(("pit8253_set_clockin(): PIT #%d timer=%d, clockin = %lf\n", which, (int) timerno,new_clockin));

	assert(timer !=	NULL);
	update(timer);
	timer->clockin = new_clockin;
	update(timer);

	if (timer->freq_callback !=	NULL)
	{
		timer->freq_callback(get_frequency(timer));
		if (timer->cycles_to_freq != CYCLES_NEVER)
		{
			freq_callback_in(timer,timer->cycles_to_freq);
		}
	}
}



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

READ8_HANDLER (	pit8253_0_r	) {	return pit8253_read(0, offset);	}
READ8_HANDLER (	pit8253_1_r	) {	return pit8253_read(1, offset);	}
WRITE8_HANDLER ( pit8253_0_w ) { pit8253_write(0, offset, data); }
WRITE8_HANDLER ( pit8253_1_w ) { pit8253_write(1, offset, data); }

READ32_HANDLER ( pit8253_32_0_r	) {	return read32_with_read8_handler(pit8253_0_r, offset, mem_mask); }
READ32_HANDLER ( pit8253_32_1_r	) {	return read32_with_read8_handler(pit8253_1_r, offset, mem_mask); }
WRITE32_HANDLER	( pit8253_32_0_w ) { write32_with_write8_handler(pit8253_0_w, offset, data,	mem_mask); }
WRITE32_HANDLER	( pit8253_32_1_w ) { write32_with_write8_handler(pit8253_1_w, offset, data,	mem_mask); }

WRITE8_HANDLER ( pit8253_0_gate_w )	{ pit8253_gate_write(0,	offset,	data); }
WRITE8_HANDLER ( pit8253_1_gate_w )	{ pit8253_gate_write(1,	offset,	data); }

