summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/machine/pit8253.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/machine/pit8253.c')
-rw-r--r--src/emu/machine/pit8253.c1216
1 files changed, 1216 insertions, 0 deletions
diff --git a/src/emu/machine/pit8253.c b/src/emu/machine/pit8253.c
new file mode 100644
index 00000000000..4499d66d3c6
--- /dev/null
+++ b/src/emu/machine/pit8253.c
@@ -0,0 +1,1216 @@
+/*****************************************************************************
+ *
+ * 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 "memconv.h"
+#include "machine/pit8253.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 */
+
+ attotime last_updated; /* time when last updated */
+
+ emu_timer *outputtimer; /* MAME timer for output change callback */
+ emu_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 */
+ 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 */
+ UINT32 cycles_to_freq; /* cycles until frequency callback called */
+ UINT32 freq_count; /* counter period for periodic modes, 0 if counter non-periodic */
+};
+
+struct pit8253
+{
+ const 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)
+{
+ 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)
+ {
+ timer_reset(timer->freqtimer,attotime_never);
+ }
+ else
+ {
+ timer_reset(timer->freqtimer,double_to_attotime(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;
+ }
+
+ if (timer->output_callback != NULL)
+ {
+ timer->cycles_to_output = cycles_to_output;
+ if (cycles_to_output == CYCLES_NEVER || timer->clockin == 0)
+ {
+ timer_reset(timer->outputtimer,attotime_never);
+ }
+ else
+ {
+ timer_reset(timer->outputtimer,
+ double_to_attotime(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. */
+ attotime now = timer_get_time();
+ attotime elapsed_time = attotime_sub(now,timer->last_updated);
+ INT64 elapsed_cycles = attotime_to_double(elapsed_time) * timer->clockin;
+
+ timer->last_updated = attotime_add(timer->last_updated,double_to_attotime(elapsed_cycles/timer->clockin));
+
+ 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 = timer_get_time();
+
+ update(timer);
+ }
+}
+
+
+static TIMER_CALLBACK( freqcallback )
+{
+ struct pit8253_timer *timer = get_timer(get_pit(param & 0x0F),(param >> 4) & 0x0F);
+ INT64 cycles = timer->cycles_to_freq;
+ double t;
+
+ LOG2(("pit8253: freqcallback(): pit %d, timer %d, %d cycles\n",param & 0xf,(param >> 4) & 0xf,(UINT32)cycles));
+
+ simulate(timer,cycles);
+
+ t = cycles / timer->clockin;
+
+ timer->last_updated = attotime_add(timer->last_updated, double_to_attotime(t));
+}
+
+
+static TIMER_CALLBACK( outputcallback )
+{
+ struct pit8253_timer *timer = get_timer(get_pit(param & 0x0F),(param >> 4) & 0x0F);
+ INT64 cycles = timer->cycles_to_output;
+ double t;
+
+ LOG2(("pit8253: outputcallback(): pit %d, timer %d, %d cycles\n",param & 0xf,(param >> 4) & 0xf,(UINT32)cycles));
+
+ simulate(timer,cycles);
+
+ t = cycles / timer->clockin;
+
+ timer->last_updated = attotime_add(timer->last_updated, double_to_attotime(t));
+}
+
+
+int pit8253_init(int count, const 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));
+
+ 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 = timer_alloc(outputcallback);
+ timer_adjust(timer->outputtimer, attotime_never, i | (timerno<<4), attotime_zero);
+ }
+ if (timer->freq_callback == NULL)
+ timer->freqtimer = NULL;
+ else
+ {
+ timer->freqtimer = timer_alloc(freqcallback);
+ timer_adjust(timer->freqtimer, attotime_never, i | (timerno<<4), attotime_zero);
+ }
+
+ /* set up state save values */
+ state_save_register_item("pit8253", n, timer->clockin);
+ state_save_register_item("pit8253", n, timer->control);
+ state_save_register_item("pit8253", n, timer->status);
+ state_save_register_item("pit8253", n, timer->lowcount);
+ state_save_register_item("pit8253", n, timer->latch);
+ state_save_register_item("pit8253", n, timer->count);
+ state_save_register_item("pit8253", n, timer->value);
+ state_save_register_item("pit8253", n, timer->wmsb);
+ state_save_register_item("pit8253", n, timer->rmsb);
+ state_save_register_item("pit8253", n, timer->output);
+ state_save_register_item("pit8253", n, timer->gate);
+ state_save_register_item("pit8253", n, timer->latched_count);
+ state_save_register_item("pit8253", n, timer->latched_status);
+ state_save_register_item("pit8253", n, timer->null_count);
+ state_save_register_item("pit8253", n, timer->phase);
+ state_save_register_item("pit8253", n, timer->cycles_to_output);
+ state_save_register_item("pit8253", n, timer->cycles_to_freq);
+ state_save_register_item("pit8253", n, timer->freq_count);
+ state_save_register_item("pit8253", n, timer->last_updated.seconds);
+ state_save_register_item("pit8253", n, timer->last_updated.attoseconds);
+ ++n;
+ }
+ pit8253_reset(i);
+ }
+
+ LOG1(("pit8253_init(): initialized successfully\n"));
+
+ return 0;
+}
+
+
+/* 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 UINT8 pit8253_read(int which,offs_t offset)
+{
+ struct pit8253 *pit = get_pit(which);
+ struct pit8253_timer *timer = get_timer(pit,offset);
+ UINT8 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 */
+ 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 */
+ 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 */
+ 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);
+
+ 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;
+
+ 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));
+
+ 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); }
+
+READ16_HANDLER ( pit8253_0_lsb_r ) { return pit8253_read(0, offset); }
+READ16_HANDLER ( pit8253_1_lsb_r ) { return pit8253_read(1, offset); }
+WRITE16_HANDLER ( pit8253_0_lsb_w ) { if (ACCESSING_LSB) pit8253_write(0, offset, data); }
+WRITE16_HANDLER ( pit8253_1_lsb_w ) { if (ACCESSING_LSB) pit8253_write(1, offset, data); }
+
+READ16_HANDLER ( pit8253_16le_0_r ) { return read16le_with_read8_handler(pit8253_0_r, offset, mem_mask); }
+READ16_HANDLER ( pit8253_16le_1_r ) { return read16le_with_read8_handler(pit8253_1_r, offset, mem_mask); }
+WRITE16_HANDLER ( pit8253_16le_0_w ) { write16le_with_write8_handler(pit8253_0_w, offset, data, mem_mask); }
+WRITE16_HANDLER ( pit8253_16le_1_w ) { write16le_with_write8_handler(pit8253_1_w, offset, data, mem_mask); }
+
+READ32_HANDLER ( pit8253_32le_0_r ) { return read32le_with_read8_handler(pit8253_0_r, offset, mem_mask); }
+READ32_HANDLER ( pit8253_32le_1_r ) { return read32le_with_read8_handler(pit8253_1_r, offset, mem_mask); }
+WRITE32_HANDLER ( pit8253_32le_0_w ) { write32le_with_write8_handler(pit8253_0_w, offset, data, mem_mask); }
+WRITE32_HANDLER ( pit8253_32le_1_w ) { write32le_with_write8_handler(pit8253_1_w, offset, data, mem_mask); }
+
+READ64_HANDLER ( pit8253_64be_0_r ) { return read64be_with_read8_handler(pit8253_0_r, offset, mem_mask); }
+READ64_HANDLER ( pit8253_64be_1_r ) { return read64be_with_read8_handler(pit8253_1_r, offset, mem_mask); }
+WRITE64_HANDLER ( pit8253_64be_0_w ) { write64be_with_write8_handler(pit8253_0_w, offset, data, mem_mask); }
+WRITE64_HANDLER ( pit8253_64be_1_w ) { write64be_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); }
+