summaryrefslogtreecommitdiffstatshomepage
path: root/trunk/src/emu/machine/pit8253.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/emu/machine/pit8253.c')
-rw-r--r--trunk/src/emu/machine/pit8253.c1193
1 files changed, 1193 insertions, 0 deletions
diff --git a/trunk/src/emu/machine/pit8253.c b/trunk/src/emu/machine/pit8253.c
new file mode 100644
index 00000000000..e37db95ddd4
--- /dev/null
+++ b/trunk/src/emu/machine/pit8253.c
@@ -0,0 +1,1193 @@
+/*****************************************************************************
+ *
+ * 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"
+
+
+
+/***************************************************************************
+
+ Structures & macros
+
+***************************************************************************/
+
+#define MAX_TIMER 3
+#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 CYCLES_NEVER ((UINT32) -1)
+
+/* device types */
+enum {
+ TYPE_PIT8253 = 0,
+ TYPE_PIT8254,
+
+ NUM_TYPES
+};
+
+
+/* device tags */
+static const char * const device_tags[NUM_TYPES] = { "pit8253", "pit8254" };
+
+
+struct pit8253_timer
+{
+ int index; /* index number of the timer */
+ double clockin; /* input clock frequency in Hz */
+ int clock; /* clock signal when clockin is 0 */
+
+ devcb_resolved_read_line in_gate_func; /* callback for gate input */
+ devcb_resolved_write_line out_out_func; /* callback function for when output changes */
+
+ attotime last_updated; /* time when last updated */
+
+ emu_timer *updatetimer; /* MAME timer to process updates */
+
+ 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 */
+ INT32 rmsb; /* 1 = Next read is MSB of 16-bit value */
+ INT32 wmsb; /* 1 = Next write is MSB of 16-bit value */
+ INT32 output; /* 0 = low, 1 = high */
+
+ INT32 gate; /* gate input (0 = low, 1 = high) */
+ INT32 latched_count; /* number of bytes of count latched */
+ INT32 latched_status; /* 1 = status latched (8254 only) */
+ INT32 null_count; /* 1 = mode control or count written, 0 = count loaded */
+ INT32 phase; /* see phase definition tables in simulate2(), below */
+
+ UINT32 cycles_to_output; /* cycles until output callback called */
+};
+
+typedef struct _pit8253_t pit8253_t;
+struct _pit8253_t
+{
+ const struct pit8253_config *config;
+ int device_type;
+ 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)
+
+
+/***************************************************************************
+
+ Functions
+
+***************************************************************************/
+
+/* makes sure that the passed in device is of the right type */
+INLINE pit8253_t *get_safe_token(device_t *device)
+{
+ assert(device != NULL);
+ assert((device->type() == PIT8253) || (device->type() == PIT8254));
+
+ return (pit8253_t *) downcast<legacy_device_base *>(device)->token();
+}
+
+
+static struct pit8253_timer *get_timer(struct _pit8253_t *pit,int which)
+{
+ which &= 3;
+ if (which < MAX_TIMER)
+ return &pit->timers[which];
+ return NULL;
+}
+
+
+static int pit8253_gate(struct pit8253_timer *timer)
+{
+ if (!timer->in_gate_func.isnull())
+ return timer->in_gate_func();
+ else
+ return timer->gate;
+}
+
+
+INLINE 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;
+}
+
+
+/* Counter loading: transfer of a count from the CR to the CE */
+static void load_counter_value(device_t *device, struct pit8253_timer *timer)
+{
+ timer->value = timer->count;
+ timer->null_count = 1;
+ if ( CTRL_MODE(timer->control) == 3 && timer->output == 0)
+ timer->value &= 0xfffe;
+}
+
+
+static void set_output(device_t *device, struct pit8253_timer *timer,int output)
+{
+ if (output != timer->output)
+ {
+ timer->output = output;
+ timer->out_out_func(timer->output);
+ }
+}
+
+
+/* This emulates timer "timer" for "elapsed_cycles" cycles and assumes no
+ callbacks occur during that time. */
+static void simulate2(device_t *device, struct pit8253_timer *timer, INT64 elapsed_cycles)
+{
+ UINT32 adjusted_value;
+ int bcd = CTRL_BCD(timer->control);
+ int mode = CTRL_MODE(timer->control);
+ int cycles_to_output = 0;
+
+ LOG2(("pit8253: 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,pit8253_gate(timer),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( device, timer );
+ }
+
+ if ( pit8253_gate(timer) == 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( device, 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( device, timer );
+ set_output( device, 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( device, 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 (pit8253_gate(timer) == 0 || timer->phase == 0)
+ {
+ /* Gate low or mode control write forces output high */
+ set_output(device, 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( device, timer );
+ }
+
+ adjusted_value = adjusted_count( bcd, timer->value );
+
+ do
+ {
+ if ( timer->phase == 2 )
+ {
+ if ( elapsed_cycles + 1 >= adjusted_value )
+ {
+ /* Coounter hits 1, output goes low */
+ timer->phase = 3;
+ set_output( device, timer, 0 );
+ }
+ }
+
+ if ( elapsed_cycles > 0 && timer->phase == 3 )
+ {
+ /* Reload counter, output goes high */
+ --elapsed_cycles;
+ timer->phase = 2;
+ load_counter_value( device, timer );
+ adjusted_value = adjusted_count( bcd, timer->value );
+ set_output( device, 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;
+
+
+ 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 (pit8253_gate(timer) == 0 || timer->phase == 0)
+ {
+ /* Gate low or mode control write forces output high */
+ set_output(device, 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( device, 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( device, timer );
+ adjusted_value = adjusted_count( bcd, timer->value );
+ set_output( device, 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( device, timer );
+ adjusted_value = adjusted_count( bcd, timer->value );
+ set_output( device, 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<<1);
+ 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 (pit8253_gate(timer) == 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( device, 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( device, timer, 0 );
+ }
+
+ if (elapsed_cycles > 0 && timer->phase == 3)
+ {
+ --elapsed_cycles;
+ timer->phase = 0;
+ decrease_counter_value(timer,1);
+ set_output( device, 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;
+ }
+
+ timer->cycles_to_output = cycles_to_output;
+ 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 - device->machine().time(), timer->index );
+ }
+
+ LOG2(("pit8253: 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,pit8253_gate(timer),timer->output,timer->value,cycles_to_output));
+}
+
+
+/* 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(device_t *device, struct pit8253_timer *timer, INT64 elapsed_cycles)
+{
+ if ( elapsed_cycles > 0 )
+ simulate2(device, timer, elapsed_cycles);
+ else
+ if ( timer->clockin )
+ timer->updatetimer->adjust(attotime::from_hz( timer->clockin ), timer->index );
+}
+
+
+/* This brings timer "timer" up to date */
+static void update(device_t *device, 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. */
+ attotime now = device->machine().time();
+ attotime elapsed_time = now - timer->last_updated;
+ INT64 elapsed_cycles = elapsed_time.as_double() * timer->clockin;
+
+ LOG1(("pit8253: update(): timer %d, %" I64FMT "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(device, timer, elapsed_cycles);
+}
+
+
+static TIMER_CALLBACK( update_timer_cb )
+{
+ device_t *device = (device_t *)ptr;
+ pit8253_t *pit8253 = get_safe_token(device);
+ struct pit8253_timer *timer = get_timer(pit8253,param);
+
+ LOG2(("pit8253: output_changed(): timer %d\n",param));
+
+ update( device, timer );
+}
+
+
+/* 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. */
+READ8_DEVICE_HANDLER( pit8253_r )
+{
+ pit8253_t *pit8253 = get_safe_token(device);
+ struct pit8253_timer *timer = get_timer(pit8253,offset);
+ UINT8 data;
+ UINT16 value;
+
+ LOG2(("pit8253_r(): offset %d\n", offset));
+
+ if (timer == NULL)
+ {
+ /* Reading mode control register is illegal according to docs */
+ /* Experimentally determined: reading it returns 0 */
+ data = 0;
+ }
+ else
+ {
+ update(device, 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 */
+ 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_r(): offset=%d data=0x%02x\n", offset, data));
+ return data;
+}
+
+
+/* Loads a new value from the bus to the count register (CR) */
+static void load_count(device_t *device, 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;
+ if (mode == 2 || mode == 3)
+ {
+ if (timer->phase == 0)
+ {
+ timer->phase = 1;
+ }
+ }
+ else
+ {
+ if (mode == 0 || mode == 4)
+ {
+ timer->phase = 1;
+ }
+ }
+}
+
+
+static void readback(device_t *device, struct pit8253_timer *timer,int command)
+{
+ UINT16 value;
+ update(device, 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 */
+ 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;
+ }
+ }
+ }
+}
+
+
+WRITE8_DEVICE_HANDLER( pit8253_w )
+{
+ pit8253_t *pit8253 = get_safe_token(device);
+ struct pit8253_timer *timer = get_timer(pit8253,offset);
+ int read_command;
+
+ LOG2(("pit8253_w(): offset=%d data=0x%02x\n", offset, data));
+
+ if (timer == NULL) {
+ /* Write to mode control register */
+ timer = get_timer(pit8253, (data >> 6) & 3);
+ if (timer == NULL)
+ {
+ /* Readback command. Illegal on 8253 */
+ /* Todo: find out what (if anything) the 8253 hardware actually does here. */
+ if (pit8253->device_type == TYPE_PIT8254)
+ {
+ LOG1(("pit8253_w(): readback %02x\n", 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(device, get_timer(pit8253,0), read_command);
+ if ((data & 4) != 0)
+ readback(device, get_timer(pit8253,1), read_command);
+ if ((data & 8) != 0)
+ readback(device, get_timer(pit8253,2), read_command);
+ }
+ return;
+ }
+
+ update(device, timer);
+
+ if (CTRL_ACCESS(data) == 0)
+ {
+ LOG1(("pit8253_write(): timer=%d readback\n", (data >> 6) & 3));
+
+ /* Latch current timer value */
+ /* Experimentally verified: this command does not affect the mode control register */
+ readback(device, timer, 1);
+ }
+ else {
+ LOG1(("pit8253_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(device, timer, CTRL_MODE(timer->control) ? 1 : 0);
+ }
+ }
+ else
+ {
+ int middle_of_a_cycle = 0;
+
+ update(device, timer);
+
+ if ( device->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(device, timer, data);
+ simulate2(device, timer, 0 );
+ if ( CTRL_MODE(timer->control) == 0 )
+ {
+ set_output( device, 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(device, timer, data << 8);
+ simulate2(device, timer, 0 );
+ break;
+
+ case 3:
+ /* read/write bits 0-7 first, then 8-15 */
+ if (timer->wmsb != 0)
+ {
+ /* 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(device, timer,timer->lowcount | (data << 8));
+ simulate2(device, 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( device, timer, 0 );
+ }
+ }
+ timer->wmsb = 1 - timer->wmsb;
+ break;
+ }
+ }
+}
+
+static void pit8253_gate_w(device_t *device, int gate, int state)
+{
+ pit8253_t *pit8253 = get_safe_token(device);
+ struct pit8253_timer *timer = get_timer(pit8253, gate);
+
+ LOG2(("pit8253_gate_w(): gate=%d state=%d\n", gate, state));
+
+ if (timer == NULL)
+ return;
+
+ if (!timer->in_gate_func.isnull())
+ {
+ logerror("pit8253_gate_w: write has no effect because a read handler is already defined!\n");
+ }
+ else
+ {
+ if (state != timer->gate)
+ {
+ int mode = CTRL_MODE(timer->control);
+
+ update(device, timer);
+ timer->gate = state;
+ if (state != 0 && ( mode == 1 || mode == 2 || mode == 5 ))
+ {
+ timer->phase = 1;
+ }
+ update(device, timer);
+ }
+ }
+}
+
+WRITE_LINE_DEVICE_HANDLER( pit8253_gate0_w ) { pit8253_gate_w(device, 0, state); }
+WRITE_LINE_DEVICE_HANDLER( pit8253_gate1_w ) { pit8253_gate_w(device, 1, state); }
+WRITE_LINE_DEVICE_HANDLER( pit8253_gate2_w ) { pit8253_gate_w(device, 2, state); }
+
+
+/* ----------------------------------------------------------------------- */
+
+int pit8253_get_output(device_t *device, int timerno)
+{
+ pit8253_t *pit8253 = get_safe_token(device);
+ struct pit8253_timer *timer = get_timer(pit8253,timerno);
+ int result;
+
+ update(device, timer);
+ result = timer->output;
+ LOG2(("pit8253_get_output(): PIT timer=%d result=%d\n", timerno, result));
+ return result;
+}
+
+
+
+void pit8253_set_clockin(device_t *device, int timerno, double new_clockin)
+{
+ pit8253_t *pit8253 = get_safe_token(device);
+ struct pit8253_timer *timer = get_timer(pit8253,timerno);
+
+ LOG2(("pit8253_set_clockin(): PIT timer=%d, clockin = %lf\n", timerno,new_clockin));
+
+ update(device, timer);
+ timer->clockin = new_clockin;
+ update(device, timer);
+}
+
+
+static void pit8253_set_clock_signal(device_t *device, int timerno, int state)
+{
+ pit8253_t *pit8253 = get_safe_token(device);
+ struct pit8253_timer *timer = get_timer(pit8253,timerno);
+
+ LOG2(("pit8253_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(device, timer, 1);
+ }
+ timer->clock = state;
+}
+
+WRITE_LINE_DEVICE_HANDLER( pit8253_clk0_w ) { pit8253_set_clock_signal(device, 0, state); }
+WRITE_LINE_DEVICE_HANDLER( pit8253_clk1_w ) { pit8253_set_clock_signal(device, 1, state); }
+WRITE_LINE_DEVICE_HANDLER( pit8253_clk2_w ) { pit8253_set_clock_signal(device, 2, state); }
+
+
+static void common_start( device_t *device, int device_type ) {
+ pit8253_t *pit8253 = get_safe_token(device);
+ int timerno;
+
+ pit8253->config = (const struct pit8253_config *)device->static_config();
+ pit8253->device_type = device_type;
+
+ /* register for state saving */
+ for (timerno = 0; timerno < MAX_TIMER; timerno++)
+ {
+ struct pit8253_timer *timer = get_timer(pit8253, timerno);
+
+ /* initialize timer */
+ timer->clockin = pit8253->config->timer[timerno].clockin;
+ timer->updatetimer = device->machine().scheduler().timer_alloc(FUNC(update_timer_cb), (void *)device);
+ timer->updatetimer->adjust(attotime::never, timerno);
+
+ /* resolve callbacks */
+ timer->in_gate_func.resolve(pit8253->config->timer[timerno].in_gate_func, *device);
+ timer->out_out_func.resolve(pit8253->config->timer[timerno].out_out_func, *device);
+
+ /* set up state save values */
+ device->save_item(NAME(timer->clockin), timerno);
+ device->save_item(NAME(timer->control), timerno);
+ device->save_item(NAME(timer->status), timerno);
+ device->save_item(NAME(timer->lowcount), timerno);
+ device->save_item(NAME(timer->latch), timerno);
+ device->save_item(NAME(timer->count), timerno);
+ device->save_item(NAME(timer->value), timerno);
+ device->save_item(NAME(timer->wmsb), timerno);
+ device->save_item(NAME(timer->rmsb), timerno);
+ device->save_item(NAME(timer->output), timerno);
+ device->save_item(NAME(timer->gate), timerno);
+ device->save_item(NAME(timer->latched_count), timerno);
+ device->save_item(NAME(timer->latched_status), timerno);
+ device->save_item(NAME(timer->null_count), timerno);
+ device->save_item(NAME(timer->phase), timerno);
+ device->save_item(NAME(timer->cycles_to_output), timerno);
+ device->save_item(NAME(timer->last_updated), timerno);
+ device->save_item(NAME(timer->clock), timerno);
+ }
+}
+
+
+static DEVICE_START( pit8253 ) {
+ common_start( device, TYPE_PIT8253 );
+}
+
+
+static DEVICE_START( pit8254 ) {
+ common_start( device, TYPE_PIT8254 );
+}
+
+
+static DEVICE_RESET( pit8253 ) {
+ pit8253_t *pit = get_safe_token(device);
+ int i;
+
+ for (i = 0; i < MAX_TIMER; i++)
+ {
+ struct pit8253_timer *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->index = i;
+ timer->control = timer->status = 0x30;
+ timer->rmsb = timer->wmsb = 0;
+ timer->count = timer->value = timer->latch = 0;
+ timer->lowcount = 0;
+
+ if (!timer->in_gate_func.isnull())
+ timer->gate = timer->in_gate_func();
+ else
+ timer->gate = 1;
+
+ timer->output = 2; /* output is undetermined */
+ timer->latched_count = 0;
+ timer->latched_status = 0;
+ timer->null_count = 1;
+ timer->cycles_to_output = CYCLES_NEVER;
+
+ timer->last_updated = device->machine().time();
+
+ update(device, timer);
+ }
+}
+
+
+DEVICE_GET_INFO( pit8253 ) {
+ switch ( state ) {
+ /* --- the following bits of info are returned as 64-bit signed integers --- */
+ case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(pit8253_t); break;
+ case DEVINFO_INT_INLINE_CONFIG_BYTES: info->i = 0; break;
+
+ /* --- the following bits of info are returned as pointers to data or functions --- */
+ case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(pit8253); break;
+ case DEVINFO_FCT_STOP: /* nothing */ break;
+ case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME(pit8253); break;
+
+ /* --- the following bits of info are returned as NULL-terminated strings --- */
+ case DEVINFO_STR_NAME: strcpy(info->s, "Intel PIT8253"); break;
+ case DEVINFO_STR_FAMILY: strcpy(info->s, "PIT8253"); break;
+ case DEVINFO_STR_VERSION: strcpy(info->s, "1.00"); break;
+ case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break;
+ case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright the MAME and MESS Teams"); break;
+ }
+}
+
+
+DEVICE_GET_INFO( pit8254 ) {
+ switch ( state ) {
+ /* --- the following bits of info are returned as 64-bit signed integers --- */
+ case DEVINFO_STR_NAME: strcpy(info->s, "Intel PIT8254"); break;
+
+ /* --- the following bits of info are returned as pointers to data or functions --- */
+ case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(pit8254); break;
+
+ default: DEVICE_GET_INFO_CALL(pit8253); break;
+ }
+}
+
+
+DEFINE_LEGACY_DEVICE(PIT8253, pit8253);
+DEFINE_LEGACY_DEVICE(PIT8254, pit8254);