// license:BSD-3-Clause
// copyright-holders:Wilbert Pol, Nathan Woods
/*****************************************************************************
 *
 *  Programmable Interval Timer 8253/8254
 *
 *  Three Independent Timers
 *  (gate, clock, out pins)
 *
 *  8254 has an additional readback feature
 *
 *  Revision History
 *      1-Apr-2008 - WFP:   Changed the implementation into a device.
 *      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 "emu.h"
#include "machine/pit8253.h"

/* device types */
enum
{
	TYPE_PIT8253 = 0,
	TYPE_PIT8254
};


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

    Structures & macros

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

#define VERBOSE         0

#define LOG1(msg)       do { if (VERBOSE >= 1) logerror msg; } while (0)
#define LOG2(msg)       do { if (VERBOSE >= 2) logerror msg; } while (0)


DEFINE_DEVICE_TYPE(PIT8253, pit8253_device, "pit8253", "Intel 8253 PIT")
DEFINE_DEVICE_TYPE(PIT8254, pit8254_device, "pit8254", "Intel 8254 PIT")
DEFINE_DEVICE_TYPE(FE2010_PIT, fe2010_pit_device, "fe2010_pit", "Faraday FE2010 PIT")

pit8253_device::pit8253_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
	pit8253_device(mconfig, PIT8253, tag, owner, clock, I8253)
{
}

pit8253_device::pit8253_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, int chip_type) :
	device_t(mconfig, type, tag, owner, clock),
	m_clk0(0),
	m_clk1(0),
	m_clk2(0),
	m_out0_handler(*this),
	m_out1_handler(*this),
	m_out2_handler(*this),
	m_type(chip_type)
{
}


pit8254_device::pit8254_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
	pit8253_device(mconfig, PIT8254, tag, owner, clock, I8254)
{
}

fe2010_pit_device::fe2010_pit_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
	pit8253_device(mconfig, FE2010_PIT, tag, owner, clock, FE2010)
{
}

pit8253_device::pit8253_timer *pit8253_device::get_timer(int which)
{
	which &= 3;
	if (which < PIT8253_MAX_TIMER)
		return &m_timers[which];

	return nullptr;
}


//-------------------------------------------------
//  device_start - device-specific startup
//-------------------------------------------------

void pit8253_device::device_start()
{
	m_timers[0].clockin = m_clk0;
	m_timers[1].clockin = m_clk1;
	m_timers[2].clockin = m_clk2;

	m_out0_handler.resolve_safe();
	m_out1_handler.resolve_safe();
	m_out2_handler.resolve_safe();

	for (int timerno = 0; timerno < PIT8253_MAX_TIMER; timerno++)
	{
		pit8253_timer *timer = get_timer(timerno);

		/* initialize timer */
		timer->updatetimer = timer_alloc(timerno);
		timer->updatetimer->adjust(attotime::never, timerno);

		/* set up state save values */
		save_item(NAME(timer->clockin), timerno);
		save_item(NAME(timer->control), timerno);
		save_item(NAME(timer->status), timerno);
		save_item(NAME(timer->lowcount), timerno);
		save_item(NAME(timer->latch), timerno);
		save_item(NAME(timer->count), timerno);
		save_item(NAME(timer->value), timerno);
		save_item(NAME(timer->wmsb), timerno);
		save_item(NAME(timer->rmsb), timerno);
		save_item(NAME(timer->output), timerno);
		save_item(NAME(timer->gate), timerno);
		save_item(NAME(timer->latched_count), timerno);
		save_item(NAME(timer->latched_status), timerno);
		save_item(NAME(timer->null_count), timerno);
		save_item(NAME(timer->phase), timerno);
		save_item(NAME(timer->last_updated), timerno);
		save_item(NAME(timer->clock), timerno);

		/* zerofill */
		timer->gate = 1;
		timer->phase = 0;
		timer->clock = 0;

		timer->index = timerno;
		timer->control = timer->status = 0x30;
		timer->rmsb = timer->wmsb = 0;
		timer->count = timer->value = timer->latch = 0;
		timer->lowcount = 0;

		timer->output = 0;
		timer->latched_count = 0;
		timer->latched_status = 0;
		timer->null_count = 1;

		timer->last_updated = machine().time();
	}
}


//-------------------------------------------------
//  device_reset - device-specific reset
//-------------------------------------------------

void pit8253_device::device_reset()
{
	for (int i = 0; i < PIT8253_MAX_TIMER; i++)
	{
		pit8253_timer *timer = get_timer(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->output = 2; /* output is undetermined */
		timer->latched_count = 0;
		timer->latched_status = 0;
		timer->null_count = 1;

		timer->last_updated = machine().time();

		update(timer);
	}
}


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

    Functions

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

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


inline uint32_t pit8253_device::adjusted_count(int bcd, uint16_t val)
{
	if (!bcd)
		return (val == 0) ? 0x10000 : val;
	else if (val == 0)
		return 10000;

	/* 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);
}


/* 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. */
void pit8253_device::decrease_counter_value(pit8253_timer *timer, int64_t cycles)
{
	uint16_t value;
	uint8_t 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;
}


/* Counter loading: transfer of a count from the CR to the CE */
void pit8253_device::load_counter_value(pit8253_timer *timer)
{
	timer->value = timer->count;
	timer->null_count = 0;

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


void pit8253_device::set_output(pit8253_timer *timer, int output)
{
	if (output != timer->output)
	{
		timer->output = output;
		LOG2(("set_output(): timer %d, %s\n", timer->index, output ? "low to high" : "high to low"));

		switch (timer->index)
		{
		case 0:
			m_out0_handler(output);
			break;

		case 1:
			m_out1_handler(output);
			break;

		case 2:
			m_out2_handler(output);
			break;
		}
	}
}


/* This emulates timer "timer" for "elapsed_cycles" cycles and assumes no
   callbacks occur during that time. */
void pit8253_device::simulate2(pit8253_timer *timer, int64_t elapsed_cycles)
{
	uint32_t adjusted_value;
	int bcd = CTRL_BCD(timer->control);
	int mode = CTRL_MODE(timer->control);
	static const uint32_t CYCLES_NEVER = (0xffffffff);
	uint32_t cycles_to_output = 0;

	LOG2(("simulate2(): simulating %d cycles for %d in mode %d, bcd = %d, phase = %d, gate = %d, output %d, value = 0x%04x\n",
			(int)elapsed_cycles, timer->index, mode, bcd, timer->phase, timer->gate, timer->output, 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->phase == 0)
		{
			cycles_to_output = CYCLES_NEVER;
		}
		else
		{
			if (elapsed_cycles >= 0 && timer->phase == 1)
			{
				/* Counter load cycle */
				if (elapsed_cycles > 0)
				{
					--elapsed_cycles;
					timer->phase = 2;
				}
				load_counter_value(timer);
			}

			if (timer->gate == 0)
			{
				cycles_to_output = CYCLES_NEVER;
			}
			else
			{
				if (timer->phase == 2)
				{
					adjusted_value = adjusted_count(bcd, timer->value);
					if (elapsed_cycles >= adjusted_value)
					{
						/* Counter wrapped, output goes high */
						elapsed_cycles -= adjusted_value;
						timer->phase = 3;
						timer->value = 0;
						set_output(timer, 1);
					}
				}

				decrease_counter_value(timer, elapsed_cycles);

				switch (timer->phase)
				{
				case 1:  cycles_to_output = 1; break;
				case 2:  cycles_to_output = adjusted_count(bcd, timer->value); break;
				case 3:  cycles_to_output = adjusted_count(bcd, timer->value); break;
				}
			}
		}
		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|     |1   |counting down
		    1|high  |1       |     |2   |internal delay to load counter
		    2|low   |n       |n..1 |3   |counting down
		    3|high  |infinity|0..1 |3   |counting down

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

		if (elapsed_cycles >= 0 && timer->phase == 1)
		{
			/* Counter load cycle, output goes low */
			if (elapsed_cycles > 0)
			{
				--elapsed_cycles;
				timer->phase = 2;
			}
			load_counter_value(timer);
			set_output(timer, 0);
		}

		if (timer->phase == 2)
		{
			adjusted_value = adjusted_count(bcd, timer->value);
			if (elapsed_cycles >= adjusted_value)
			{
				/* Counter wrapped, output goes high */
				timer->phase = 3;
				set_output(timer, 1);
			}
		}

		decrease_counter_value(timer, elapsed_cycles);

		switch (timer->phase)
		{
		case 1:   cycles_to_output = 1; break;
		case 2:   cycles_to_output = adjusted_count(bcd, timer->value); break;
		default:  cycles_to_output = CYCLES_NEVER; break;
		}
		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|high  |1       |     |2   |internal delay to load counter
		    2|high  |n       |n..2 |3   |counting down
		    3|low   |1       |1    |2   |reload counter

		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
		{
			if (elapsed_cycles >= 0 && timer->phase == 1)
			{
				if (elapsed_cycles > 0)
				{
					--elapsed_cycles;
					timer->phase = 2;
				}
				load_counter_value(timer);
			}

			adjusted_value = adjusted_count(bcd, timer->value);

			do
			{
				if (timer->phase == 2)
				{
					if (elapsed_cycles + 1 >= adjusted_value)
					{
						/* Counter hits 1, output goes low */
						timer->phase = 3;
						set_output(timer, 0);
					}
				}

				if (elapsed_cycles > 0 && timer->phase == 3)
				{
					/* Reload counter, output goes high */
					elapsed_cycles -= adjusted_value;
					timer->phase = 2;
					load_counter_value(timer);
					adjusted_value = adjusted_count(bcd, timer->value);
					set_output(timer, 1);
				}
			}
			while (elapsed_cycles >= adjusted_value);

			/* Calculate counter value */
			decrease_counter_value(timer, elapsed_cycles);

			switch (timer->phase)
			{
			case 1:   cycles_to_output = 1; break;
			default:  cycles_to_output = (timer->value == 1) ? 1 : (adjusted_count(bcd, timer->value) - 1); break;
			}
		}
		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|high  |1       |     |2   |internal delay to load counter
		    2|high  |n/2(+1) |n..0 |3   |counting down double speed, reload counter
		    3|low   |n/2     |n..0 |2   |counting down double speed, reload counter

		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;
			if(m_type == FE2010)
				load_counter_value(timer);
		}
		else
		{
			if (elapsed_cycles >= 0 && timer->phase == 1)
			{
				if (elapsed_cycles > 0)
				{
					--elapsed_cycles;
					timer->phase = 2;
				}
				load_counter_value(timer);
			}

			if (elapsed_cycles > 0)
			{
				adjusted_value = adjusted_count(bcd, timer->value);

				do
				{
					if (timer->phase == 2 && elapsed_cycles >= ((adjusted_value + 1) >> 1))
					{
						/* High phase expired, output goes low */
						elapsed_cycles -= ((adjusted_value + 1) >> 1);
						timer->phase = 3;
						load_counter_value(timer);
						adjusted_value = adjusted_count(bcd, timer->value);
						set_output(timer, 0);
					}

					if (timer->phase == 3 && elapsed_cycles >= (adjusted_value >> 1))
					{
						/* Low phase expired, output goes high */
						elapsed_cycles -= (adjusted_value >> 1);
						timer->phase = 2;
						load_counter_value(timer);
						adjusted_value = adjusted_count(bcd, timer->value);
						set_output(timer, 1);
					}
				}
				while ((timer->phase == 2 && elapsed_cycles >= ((adjusted_value + 1) >> 1)) ||
						(timer->phase == 3 && elapsed_cycles >= (adjusted_value >> 1)));

				decrease_counter_value(timer, elapsed_cycles * 2);

				switch (timer->phase)
				{
				case 1:  cycles_to_output = 1; break;
				case 2:  cycles_to_output = (adjusted_count(bcd, timer->value) + 1) >> 1; break;
				case 3:  cycles_to_output = adjusted_count(bcd, timer->value) >> 1; break;
				}
			}
		}
		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/counting down
		    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)
			{
				if (elapsed_cycles > 0)
				{
					--elapsed_cycles;
					timer->phase = 2;
				}
				load_counter_value(timer);
			}

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

			if (timer->phase == 2 && elapsed_cycles >= adjusted_value)
			{
				/* Counter has hit zero, set output to low */
				elapsed_cycles -= adjusted_value;
				timer->phase = 3;
				timer->value = 0;
				set_output(timer, 0);
			}

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

			decrease_counter_value(timer, elapsed_cycles);

			switch (timer->phase)
			{
			case 1:  cycles_to_output = 1; break;
			case 2:  cycles_to_output = adjusted_count(bcd, timer->value); break;
			case 3:  cycles_to_output = 1; break;
			}
		}
		break;
	}

	if (cycles_to_output == CYCLES_NEVER || timer->clockin == 0)
	{
		timer->updatetimer->adjust(attotime::never, timer->index);
	}
	else
	{
		attotime next_fire_time = timer->last_updated + cycles_to_output * attotime::from_hz(timer->clockin);

		timer->updatetimer->adjust(next_fire_time - machine().time(), timer->index);
	}

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


/* This emulates timer "timer" for "elapsed_cycles" cycles, broken down into
   sections punctuated by callbacks. */
void pit8253_device::simulate(pit8253_timer *timer, int64_t elapsed_cycles)
{
	if (elapsed_cycles > 0)
		simulate2(timer, elapsed_cycles);
	else if (timer->clockin)
		timer->updatetimer->adjust(attotime::from_hz(timer->clockin), timer->index);
}


/* This brings timer "timer" up to date */
void pit8253_device::update(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. */
	attotime now = machine().time();
	attotime elapsed_time = now - timer->last_updated;
	int64_t elapsed_cycles = elapsed_time.as_double() * timer->clockin;

	LOG2(("update(): timer %d, %d elapsed_cycles\n", timer->index, elapsed_cycles));

	if (timer->clockin)
		timer->last_updated += elapsed_cycles * attotime::from_hz(timer->clockin);
	else
		timer->last_updated = now;

	simulate(timer, elapsed_cycles);
}


/* 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. */
uint16_t pit8253_device::masked_value(pit8253_timer *timer)
{
	LOG2(("masked_value\n"));
	if ((CTRL_MODE(timer->control) == 3) && (m_type != FE2010))
		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. */
READ8_MEMBER( pit8253_device::read )
{
	pit8253_timer *timer = get_timer(offset);
	uint8_t data;
	uint16_t value;

	LOG2(("read(): offset %d\n", offset));

	if (timer == nullptr)
	{
		/* 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 ? 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 */
					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 */

					// reading back the current count while in the middle of a
					// 16-bit write returns a xor'ed version of the value written
					// (apricot diagnostic timer test tests this)
					if (timer->wmsb)
						data = ~timer->lowcount;
					else
						data = value >> (timer->rmsb ? 8 : 0);

					timer->rmsb = 1 - timer->rmsb;
					break;
				}
			}
		}
	}

	LOG2(("read(): offset=%d data=0x%02x\n", offset, data));
	return data;
}


/* Loads a new value from the bus to the count register (CR) */
void pit8253_device::load_count(pit8253_timer *timer, uint16_t newcount)
{
	int mode = CTRL_MODE(timer->control);
	LOG1(("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;

	if (mode == 2 || mode == 3)
	{
		if (timer->phase == 0)
			timer->phase = 1;
	}
	else
	{
		if (mode == 0 || mode == 4)
			timer->phase = 1;
	}
}


void pit8253_device::readback(pit8253_timer *timer, int command)
{
	uint16_t value;
	update(timer);

	if ((command & 1) == 0)
	{
		/* readback status command */
		if (!timer->latched_status)
		{
			timer->status = (timer->control & 0x3f) | ((timer->output != 0) ? 0x80 : 0) | (timer->null_count ? 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)
	{
		/* readback count command */
		if (timer->latched_count == 0)
		{
			value = masked_value(timer);
			switch (CTRL_ACCESS(timer->control))
			{
			case 0:
				/* This should never happen */
				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;
			}
		}
	}
}


void pit8253_device::readback_command(uint8_t data)
{
	/* Readback command. Illegal on 8253 */
	/* Todo: find out what (if anything) the 8253 hardware actually does here. */
}

void pit8254_device::readback_command(uint8_t data)
{
	LOG1(("write(): readback %02x\n", data & 0x3f));

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

void pit8253_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
	update(get_timer(id));
}

WRITE8_MEMBER( pit8253_device::write )
{
	pit8253_timer *timer = get_timer(offset);

	LOG2(("write(): offset=%d data=0x%02x\n", offset, data));

	if (timer == nullptr)
	{
		/* Write to mode control register */
		timer = get_timer((data >> 6) & 3);
		if (timer == nullptr)
		{
			readback_command(data);
			return;
		}

		update(timer);

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

			/* Latch current timer value */
			/* Experimentally verified: this command does not affect the mode control register */
			readback(timer, 1);
		}
		else
		{
			LOG1(("write(): timer=%d bytes=%d mode=%d bcd=%d\n", (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, CTRL_MODE(timer->control) ? 1 : 0);
		}
	}
	else
	{
		int middle_of_a_cycle = 0;

		update(timer);

		if (machine().time() > timer->last_updated && timer->clockin != 0)
			middle_of_a_cycle = 1;

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

		case 1:
			/* read/write counter bits 0-7 only */

			/* check if we should compensate for not being on a cycle boundary */
			if (middle_of_a_cycle)
				timer->last_updated += attotime::from_hz(timer->clockin);

			load_count(timer, data);
			simulate2(timer, 0);

			if (CTRL_MODE(timer->control) == 0)
			{
				set_output(timer, 0);
			}
			break;

		case 2:
			/* read/write counter bits 8-15 only */

			/* check if we should compensate for not being on a cycle boundary */
			if (middle_of_a_cycle)
				timer->last_updated += attotime::from_hz(timer->clockin);

			load_count(timer, data << 8);
			simulate2(timer, 0);

			if (CTRL_MODE(timer->control) == 0)
			{
				set_output(timer, 0);
			}
			break;

		case 3:
			/* read/write bits 0-7 first, then 8-15 */
			if (timer->wmsb)
			{
				/* check if we should compensate for not being on a cycle boundary */
				if (middle_of_a_cycle)
					timer->last_updated += attotime::from_hz(timer->clockin);

				load_count(timer, timer->lowcount | (data << 8));
				simulate2(timer, 0);
			}
			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;
					set_output(timer, 0);
				}
			}
			timer->wmsb = 1 - timer->wmsb;
			break;
		}
	}
}

void pit8253_device::gate_w(int gate, int state)
{
	pit8253_timer *timer = get_timer(gate);

	if (timer == nullptr)
		return;

	LOG2(("gate_w(): gate=%d state=%d\n", gate, state));

	if (state != timer->gate)
	{
		int mode = CTRL_MODE(timer->control);

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

WRITE_LINE_MEMBER( pit8253_device::write_gate0 )
{
	gate_w(0, state);
}

WRITE_LINE_MEMBER( pit8253_device::write_gate1 )
{
	gate_w(1, state);
}

WRITE_LINE_MEMBER( pit8253_device::write_gate2 )
{
	gate_w(2, state);
}


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

void pit8253_device::set_clockin(int timerno, double new_clockin)
{
	pit8253_timer *timer = get_timer(timerno);
	assert(timer != nullptr);

	LOG2(("set_clockin(): PIT timer=%d, clockin = %f\n", timerno, new_clockin));

	update(timer);
	timer->clockin = new_clockin;
	update(timer);
}


void pit8253_device::set_clock_signal(int timerno, int state)
{
	pit8253_timer *timer = get_timer(timerno);
	assert(timer != nullptr);

	LOG2(("set_clock_signal(): PIT timer=%d, state = %d\n", timerno, state));

	/* Trigger on low to high transition */
	if (!timer->clock && state)
	{
		/* Advance a cycle */
		simulate2(timer, 1);
	}
	timer->clock = state;
}

WRITE_LINE_MEMBER( pit8253_device::write_clk0 )
{
	set_clock_signal(0, state);
}

WRITE_LINE_MEMBER( pit8253_device::write_clk1 )
{
	set_clock_signal(1, state);
}

WRITE_LINE_MEMBER( pit8253_device::write_clk2 )
{
	set_clock_signal(2, state);
}
