summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/machine/pit8253.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/machine/pit8253.cpp')
-rw-r--r--src/devices/machine/pit8253.cpp167
1 files changed, 110 insertions, 57 deletions
diff --git a/src/devices/machine/pit8253.cpp b/src/devices/machine/pit8253.cpp
index 28173bad237..c75c8b6a9cc 100644
--- a/src/devices/machine/pit8253.cpp
+++ b/src/devices/machine/pit8253.cpp
@@ -21,7 +21,7 @@
*****************************************************************************/
#include "emu.h"
-#include "machine/pit8253.h"
+#include "pit8253.h"
/***************************************************************************
@@ -30,10 +30,16 @@
***************************************************************************/
-#define VERBOSE 0
+#define LOG_1 (1U << 1)
+#define LOG_2 (1U << 2)
-#define LOG1(msg) do { if (VERBOSE >= 1) logerror msg; } while (0)
-#define LOG2(msg) do { if (VERBOSE >= 2) logerror msg; } while (0)
+//#define VERBOSE (LOG_1 | LOG_2)
+//#define LOG_OUTPUT_STREAM std::cout
+
+#include "logmacro.h"
+
+#define LOG1(...) LOGMASKED(LOG_1, __VA_ARGS__)
+#define LOG2(...) LOGMASKED(LOG_2, __VA_ARGS__)
DEFINE_DEVICE_TYPE(PIT_COUNTER, pit_counter_device, "pit_counter", "PIT Counter")
DEFINE_DEVICE_TYPE(PIT8253, pit8253_device, "pit8253", "Intel 8253 PIT")
@@ -53,7 +59,7 @@ pit8253_device::pit8253_device(const machine_config &mconfig, const char *tag, d
pit8253_device::pit8253_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, pit_type chip_type) :
device_t(mconfig, type, tag, owner, clock),
m_clk{0, 0, 0},
- m_out_handler{{*this}, {*this}, {*this}},
+ m_out_handler(*this),
m_counter(*this, "counter%u", 0U),
m_type(chip_type)
{
@@ -92,9 +98,9 @@ void pit8253_device::device_resolve_objects()
{
for (int timer = 0; timer < 3; timer++)
{
- m_out_handler[timer].resolve_safe();
m_counter[timer]->m_index = timer;
m_counter[timer]->m_clockin = m_clk[timer];
+ m_counter[timer]->m_clock_period = (m_clk[timer] != 0) ? attotime::from_hz(m_clk[timer]) : attotime::never;
}
}
@@ -105,12 +111,13 @@ void pit8253_device::device_resolve_objects()
void pit_counter_device::device_start()
{
- /* initialize timer */
- m_updatetimer = timer_alloc();
- m_updatetimer->adjust(attotime::never);
+ /* initialize timers */
+ m_update_timer = timer_alloc(FUNC(pit_counter_device::update_tick), this);
+ adjust_timer(attotime::never);
/* set up state save values */
save_item(NAME(m_clockin));
+ save_item(NAME(m_clock_period));
save_item(NAME(m_control));
save_item(NAME(m_status));
save_item(NAME(m_lowcount));
@@ -191,6 +198,14 @@ void pit_counter_device::device_reset()
#define CTRL_MODE(control) (((control) >> 1) & (((control) & 0x04) ? 0x03 : 0x07))
#define CTRL_BCD(control) (((control) >> 0) & 0x01)
+inline void pit_counter_device::adjust_timer(attotime target)
+{
+// if (target != m_next_update)
+ {
+ m_next_update = target;
+ m_update_timer->adjust(target - machine().time());
+ }
+}
inline uint32_t pit_counter_device::adjusted_count() const
{
@@ -277,9 +292,6 @@ void pit_counter_device::load_counter_value()
{
m_value = m_count;
m_null_count = 0;
-
- if (CTRL_MODE(m_control) == 3 && m_output == 0)
- m_value &= 0xfffe;
}
@@ -288,7 +300,7 @@ void pit_counter_device::set_output(int output)
if (output != m_output)
{
m_output = output;
- LOG2(("set_output(): %s\n", output ? "low to high" : "high to low"));
+ LOG2("set_output() timer %d: %s\n", m_index, output ? "low to high" : "high to low");
downcast<pit8253_device *>(owner())->m_out_handler[m_index](output);
}
@@ -305,8 +317,8 @@ void pit_counter_device::simulate(int64_t elapsed_cycles)
static const uint32_t CYCLES_NEVER = (0xffffffff);
uint32_t cycles_to_output = 0;
- LOG2(("simulate(): simulating %d cycles in mode %d, bcd = %d, phase = %d, gate = %d, output %d, value = 0x%04x\n",
- (int)elapsed_cycles, mode, bcd, m_phase, m_gate, m_output, m_value));
+ LOG2("simulate(): simulating %d cycles in mode %d, bcd = %d, phase = %d, gate = %d, output %d, value = 0x%04x\n",
+ (int)elapsed_cycles, mode, bcd, m_phase, m_gate, m_output, m_value);
switch (mode)
{
@@ -491,7 +503,7 @@ void pit_counter_device::simulate(int64_t elapsed_cycles)
}
}
- if (elapsed_cycles > 0 && m_phase == 3)
+ if (elapsed_cycles >= adjusted_value && m_phase == 3)
{
/* Reload counter, output goes high */
elapsed_cycles -= adjusted_value;
@@ -678,18 +690,12 @@ void pit_counter_device::simulate(int64_t elapsed_cycles)
}
if (cycles_to_output == CYCLES_NEVER || m_clockin == 0)
- {
- m_updatetimer->adjust(attotime::never);
- }
+ adjust_timer(attotime::never);
else
- {
- attotime next_fire_time = m_last_updated + cycles_to_output * attotime::from_hz(m_clockin);
+ adjust_timer(m_last_updated + cycles_to_output * m_clock_period);
- m_updatetimer->adjust(next_fire_time - machine().time());
- }
-
- LOG2(("simulate(): simulating %d cycles in mode %d, bcd = %d, phase = %d, gate = %d, output %d, value = 0x%04x, cycles_to_output = %04x\n",
- (int)elapsed_cycles, mode, bcd, m_phase, m_gate, m_output, m_value, cycles_to_output));
+ LOG2("simulate(): simulating %d cycles in mode %d, bcd = %d, phase = %d, gate = %d, output %d, value = 0x%04x, cycles_to_output = %04x\n",
+ (int)elapsed_cycles, mode, bcd, m_phase, m_gate, m_output, m_value, cycles_to_output);
}
/* This brings timer "timer" up to date */
@@ -698,13 +704,56 @@ void pit_counter_device::update()
/* 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 - m_last_updated;
- int64_t elapsed_cycles = elapsed_time.as_double() * m_clockin;
+ int64_t elapsed_cycles = 0;
+ if (m_clockin != 0)
+ {
+ if (now > m_last_updated)
+ {
+ attotime elapsed_time = now - m_last_updated;
+
+ // in the case of sub-Hz frequencies, just loop; there's not going to be many
+ if (m_clock_period.m_seconds != 0)
+ {
+ while (elapsed_time >= m_clock_period)
+ {
+ elapsed_cycles++;
+ elapsed_time -= m_clock_period;
+ }
+ }
+
+ // otherwise, compute it a straightforward way
+ else
+ {
+ elapsed_cycles = elapsed_time.m_attoseconds / m_clock_period.m_attoseconds;
+
+ // not expecting to see many cases of this, but just in case, let's do it right
+ if (elapsed_time.m_seconds != 0)
+ {
+ // first account for the elapsed_cycles counted above (guaranteed to be <= elapsed_time.m_attoseconds)
+ elapsed_time.m_attoseconds -= elapsed_cycles * m_clock_period.m_attoseconds;
+
+ // now compute the integral cycles per second based on the clock period
+ int64_t cycles_per_second = ATTOSECONDS_PER_SECOND / m_clock_period.m_attoseconds;
+
+ // add that many times the number of elapsed seconds
+ elapsed_cycles += cycles_per_second * elapsed_time.m_seconds;
+
+ // now compute how many attoseconds we missed for each full second (will be 0 for integral values)
+ int64_t remainder_per_second = ATTOSECONDS_PER_SECOND - cycles_per_second * m_clock_period.m_attoseconds;
+
+ // add those to the elapsed attoseconds
+ elapsed_time.m_attoseconds += elapsed_time.m_seconds * remainder_per_second;
+
+ // finally, see if that adds up to any additional cycles
+ elapsed_cycles += elapsed_time.m_attoseconds / m_clock_period.m_attoseconds;
+ }
+ }
- LOG2(("update(): %d elapsed_cycles\n", elapsed_cycles));
+ LOG2("update(): %d elapsed_cycles\n", elapsed_cycles);
- if (m_clockin)
- m_last_updated += elapsed_cycles * attotime::from_hz(m_clockin);
+ m_last_updated += elapsed_cycles * m_clock_period;
+ }
+ }
else
m_last_updated = now;
@@ -712,14 +761,13 @@ void pit_counter_device::update()
sections punctuated by callbacks. */
if (elapsed_cycles > 0)
simulate(elapsed_cycles);
- else if (m_clockin)
- m_updatetimer->adjust(attotime::from_hz(m_clockin));
+ else if (m_clockin != 0)
+ adjust_timer(m_last_updated + m_clock_period);
}
-/* We recycle bit 0 of m_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. */
+/* Since read commands in mode 3 always return even numbers,
+ we need to mask bit 0 off. */
uint16_t pit_counter_device::masked_value() const
{
if ((CTRL_MODE(m_control) == 3) && (downcast<pit8253_device *>(owner())->m_type != pit_type::FE2010))
@@ -799,7 +847,7 @@ uint8_t pit_counter_device::read()
}
}
- LOG2(("read(): data=0x%02x\n", data));
+ LOG2("read(): data=0x%02x\n", data);
return data;
}
@@ -807,7 +855,7 @@ uint8_t pit8253_device::read(offs_t offset)
{
offset &= 3;
- LOG2(("read(): offset %d\n", offset));
+ LOG2("read(): offset %d\n", offset);
if (offset == 3)
{
@@ -824,7 +872,7 @@ uint8_t pit8253_device::read(offs_t offset)
void pit_counter_device::load_count(uint16_t newcount)
{
int mode = CTRL_MODE(m_control);
- LOG1(("load_count(): %04x\n", newcount));
+ LOG1("load_count(): %04x\n", newcount);
if (newcount == 1)
{
@@ -910,7 +958,7 @@ void pit8253_device::readback_command(uint8_t data)
void pit8254_device::readback_command(uint8_t data)
{
- LOG1(("write(): readback %02x\n", data & 0x3f));
+ 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;
@@ -919,18 +967,18 @@ void pit8254_device::readback_command(uint8_t data)
m_counter[timer]->readback(read_command);
}
-void pit_counter_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+TIMER_CALLBACK_MEMBER(pit_counter_device::update_tick)
{
update();
}
-void pit_counter_device::control_w(uint8_t data)
+TIMER_CALLBACK_MEMBER(pit_counter_device::control_w_deferred)
{
update();
-
+ uint8_t data = (uint8_t)param;
if (CTRL_ACCESS(data) == 0)
{
- LOG1(("write(): readback\n"));
+ LOG1("write(): readback\n");
/* Latch current timer value */
/* Experimentally verified: this command does not affect the mode control register */
@@ -938,7 +986,7 @@ void pit_counter_device::control_w(uint8_t data)
}
else
{
- LOG1(("write(): bytes=%d mode=%d bcd=%d\n", (data >> 4) & 3, (data >> 1) & 7, data & 1));
+ LOG1("write(): bytes=%d mode=%d bcd=%d\n", (data >> 4) & 3, (data >> 1) & 7, data & 1);
m_control = (data & 0x3f);
m_null_count = 1;
@@ -949,10 +997,10 @@ void pit_counter_device::control_w(uint8_t data)
}
}
-void pit_counter_device::count_w(uint8_t data)
+TIMER_CALLBACK_MEMBER(pit_counter_device::count_w_deferred)
{
update();
-
+ uint8_t data = (uint8_t)param;
bool middle_of_a_cycle = (machine().time() > m_last_updated && m_clockin != 0);
switch (CTRL_ACCESS(m_control))
@@ -966,7 +1014,7 @@ void pit_counter_device::count_w(uint8_t data)
/* check if we should compensate for not being on a cycle boundary */
if (middle_of_a_cycle)
- m_last_updated += attotime::from_hz(m_clockin);
+ m_last_updated += m_clock_period;
load_count(data);
simulate(0);
@@ -980,7 +1028,7 @@ void pit_counter_device::count_w(uint8_t data)
/* check if we should compensate for not being on a cycle boundary */
if (middle_of_a_cycle)
- m_last_updated += attotime::from_hz(m_clockin);
+ m_last_updated += m_clock_period;
load_count(data << 8);
simulate(0);
@@ -995,7 +1043,7 @@ void pit_counter_device::count_w(uint8_t data)
{
/* check if we should compensate for not being on a cycle boundary */
if (middle_of_a_cycle)
- m_last_updated += attotime::from_hz(m_clockin);
+ m_last_updated += m_clock_period;
load_count(m_lowcount | (data << 8));
simulate(0);
@@ -1021,7 +1069,7 @@ void pit8253_device::write(offs_t offset, uint8_t data)
{
offset &= 3;
- LOG2(("write(): offset=%d data=0x%02x\n", offset, data));
+ LOG2("write(): offset=%d data=0x%02x\n", offset, data);
if (offset == 3)
{
@@ -1036,9 +1084,11 @@ void pit8253_device::write(offs_t offset, uint8_t data)
m_counter[offset]->count_w(data);
}
-void pit_counter_device::gate_w(int state)
+TIMER_CALLBACK_MEMBER(pit_counter_device::gate_w_deferred)
{
- LOG2(("gate_w(): state=%d\n", state));
+ update();
+ int state = param;
+ LOG2("gate_w(): state=%d\n", state);
if (state != m_gate)
{
@@ -1059,17 +1109,20 @@ void pit_counter_device::gate_w(int state)
void pit_counter_device::set_clockin(double new_clockin)
{
- LOG2(("set_clockin(): clockin = %f\n", new_clockin));
+ LOG2("set_clockin(): clockin = %f\n", new_clockin);
- update();
+ if (started())
+ update();
m_clockin = new_clockin;
- update();
+ m_clock_period = (new_clockin != 0) ? attotime::from_hz(new_clockin) : attotime::never;
+ if (started())
+ update();
}
void pit_counter_device::set_clock_signal(int state)
{
- LOG2(("set_clock_signal(): state = %d\n", state));
+ LOG2("set_clock_signal(): state = %d\n", state);
/* Trigger on low to high transition */
if (!m_clock_signal && state)