From 3eb3713e876019b4824546efab462c69bc175e10 Mon Sep 17 00:00:00 2001 From: MooglyGuy Date: Thu, 21 Jul 2022 01:04:36 +0200 Subject: [PATCH] -6840ptm: Fixed counter duration and fixed output behavior in single-shot and dual-8-bit modes. [Ryan Holtz] (#10111) --- src/devices/machine/6840ptm.cpp | 543 ++++++++++++++------------------ src/devices/machine/6840ptm.h | 26 +- 2 files changed, 253 insertions(+), 316 deletions(-) diff --git a/src/devices/machine/6840ptm.cpp b/src/devices/machine/6840ptm.cpp index f63a3c4bca7..f99652db339 100644 --- a/src/devices/machine/6840ptm.cpp +++ b/src/devices/machine/6840ptm.cpp @@ -94,9 +94,9 @@ void ptm6840_device::device_start() m_out_cb.resolve_all_safe(); m_irq_cb.resolve_safe(); - m_timer[0] = timer_alloc(FUNC(ptm6840_device::timeout), this); - m_timer[1] = timer_alloc(FUNC(ptm6840_device::timeout), this); - m_timer[2] = timer_alloc(FUNC(ptm6840_device::timeout), this); + m_timer[0] = timer_alloc(FUNC(ptm6840_device::state_changed), this); + m_timer[1] = timer_alloc(FUNC(ptm6840_device::state_changed), this); + m_timer[2] = timer_alloc(FUNC(ptm6840_device::state_changed), this); for (auto & elem : m_timer) { @@ -117,13 +117,12 @@ void ptm6840_device::device_start() save_item(NAME(m_gate)); save_item(NAME(m_clk)); save_item(NAME(m_mode)); - save_item(NAME(m_fired)); + save_item(NAME(m_single_fired)); save_item(NAME(m_enabled)); save_item(NAME(m_external_clock)); save_item(NAME(m_counter)); save_item(NAME(m_disable_time)); save_item(NAME(m_latch)); - save_item(NAME(m_hightime)); } @@ -141,20 +140,17 @@ void ptm6840_device::device_reset() m_status_read_since_int = 0; m_irq = 0; m_t3_scaler = 0; - m_hightime[0] = false; - m_hightime[1] = false; - m_hightime[2] = false; for (int i = 0; i < 3; i++) { - m_counter[i] = 0xffff; - m_latch[i] = 0xffff; + m_counter[i] = 0xffff; + m_latch[i] = 0xffff; m_disable_time[i] = attotime::never; - m_output[i] = false; - m_clk[i] = false; - m_fired[i] = 0; - m_enabled[i] = 0; - m_mode[i] = 0; + m_output[i] = false; + m_clk[i] = false; + m_single_fired[i] = false; + m_enabled[i] = false; + m_mode[i] = 0; } } @@ -166,106 +162,57 @@ void ptm6840_device::device_resolve_objects() //------------------------------------------------- -// subtract_from_counter - Subtract from Counter +// deduct_from_counter - count back by one step //------------------------------------------------- -void ptm6840_device::subtract_from_counter(int counter, int count) +void ptm6840_device::deduct_from_counter(int idx) { - // Determine the clock frequency for this timer - double clk = m_control_reg[counter] & INTERNAL_CLK_EN ? static_cast(clock()) : m_external_clock[counter]; - - // Dual-byte mode - if (m_control_reg[counter] & COUNT_MODE_8BIT) + if (m_control_reg[idx] & COUNT_MODE_8BIT) { - int lsb = m_counter[counter] & 0xff; - int msb = m_counter[counter] >> 8; + // Dual-byte mode + uint16_t msb = m_counter[idx] >> 8; + uint16_t lsb = m_counter[idx] & 0xff; - // Count the clocks - lsb -= count; + lsb--; - // Loop while we're less than zero - while (lsb < 0) + bool timed_out = false; + if (lsb == 0xffff) { // Borrow from the MSB - lsb += (m_latch[counter] & 0xff) + 1; + lsb = (m_latch[idx] & 0xff) + 1; msb--; - // If MSB goes less than zero, we've expired - if ((msb == 0 && !m_hightime[counter]) || (msb < 0 && m_hightime[counter])) + if (msb < 0) { - timeout(counter); - msb = (m_latch[counter] >> 8) + 1; + // If MSB is less than zero, we've timed out, no need to manually reload + timed_out = true; + state_changed(idx); + } + else if (msb == 0) + { + // If MSB is at zero, our output state potentially needs to change, also no need to manually reload + msb = (m_latch[idx] >> 8) + 1; + state_changed(idx); } } - // Store the result - m_counter[counter] = (msb << 8) | lsb; - } - - // Word mode - else - { - int word = m_counter[counter]; - - // Count the clocks - word -= count; - - // if we're less than zero - if (word < 0) + // Store the result if we haven't timed out (which already reloads the counter from the latches) + if (!timed_out) { - // We've expired - timeout(counter); - } - else - { - // Store the result - m_counter[counter] = word; - } - } - - if (m_enabled[counter]) - { - int clks = m_counter[counter]; - if (m_control_reg[counter] & COUNT_MODE_8BIT) - { - /* In dual 8 bit mode, let the counter fire when MSB == 0 */ - m_hightime[counter] = !(clks & 0xff00); - clks &= 0xff00; - } - - attotime duration = attotime::from_hz(clk) * clks; - if (counter == 2) - { - duration *= m_t3_divisor; - } - - duration += attotime::from_ticks(2, clock()); - - m_timer[counter]->adjust(duration, counter); - } -} - - - -//------------------------------------------------- -// tick -//------------------------------------------------- - -void ptm6840_device::tick(int counter, int count) -{ - if (counter == 2) - { - m_t3_scaler += count; - - if ( m_t3_scaler > m_t3_divisor - 1) - { - subtract_from_counter(counter, 1); - m_t3_scaler = 0; + m_counter[idx] = (msb << 8) + lsb; } } else { - subtract_from_counter(counter, count); + // Word mode + m_counter[idx]--; + + const bool one_shot_mode = m_mode[idx] == 4 || m_mode[idx] == 6; + // If we've ticked once in one-shot-mode, or we've expired, our state needs to change + if ((one_shot_mode && !m_output[idx]) || m_counter[idx] == 0xffff) + { + state_changed(idx); + } } } @@ -309,77 +256,70 @@ void ptm6840_device::update_interrupts() // compute_counter - Compute Counter //------------------------------------------------- -uint16_t ptm6840_device::compute_counter(int counter) const +int ptm6840_device::compute_counter(int idx) const { uint32_t clk; - // If there's no timer, return the count - if (!m_enabled[counter]) + // If the timer is disabled, return the raw counter value + if (!m_enabled[idx]) { - LOGMASKED(LOG_COUNTERS, "Timer #%d read counter: %04x\n", counter + 1, m_counter[counter]); - return m_counter[counter]; + LOGMASKED(LOG_COUNTERS, "Timer #%d read counter: %04x\n", idx + 1, m_counter[idx]); + return m_counter[idx]; } else if (m_control_reg[0] & RESET_TIMERS) { - // If we're held in reset, return the latch value, as it's what is meaningful - return m_latch[counter]; + // If we're held in reset, return either the latch value for 16-bit mode, or the computed count for dual-8-bit + if (m_control_reg[idx] & COUNT_MODE_8BIT) + { + const uint16_t latch_lsb = m_latch[idx] & 0xff; + const uint16_t latch_msb = m_latch[idx] >> 8; + return latch_msb * (latch_lsb + 1); + } + return m_latch[idx]; } // determine the clock frequency for this timer - if (m_control_reg[counter] & INTERNAL_CLK_EN) + if (m_control_reg[idx] & INTERNAL_CLK_EN) { clk = clock(); } else { - clk = m_external_clock[counter]; + clk = m_external_clock[idx]; } - if (counter == 2) + if (idx == 2) { clk /= m_t3_divisor; } - LOGMASKED(LOG_COUNTERS, "Timer #%d %s clock freq %d\n", counter + 1, (m_control_reg[counter] & INTERNAL_CLK_EN) ? "internal" : "external", clk); + LOGMASKED(LOG_COUNTERS, "Timer #%d %s clock freq %d\n", idx + 1, (m_control_reg[idx] & INTERNAL_CLK_EN) ? "internal" : "external", clk); // See how many are left - attotime remaining_time = m_timer[counter]->remaining(); + attotime remaining_time = m_timer[idx]->remaining(); if (remaining_time.is_never()) { - if (m_disable_time[counter].is_never()) + if (m_disable_time[idx].is_never()) { - return m_counter[counter]; + return m_counter[idx]; } - remaining_time = m_disable_time[counter]; - } - attotime e_time = attotime::from_ticks(2, clock()); - int remaining = 0; - if (remaining_time >= e_time) - { - remaining_time -= e_time; - remaining = remaining_time.as_ticks(clk); + remaining_time = m_disable_time[idx]; } + int remaining = remaining_time.as_ticks(clk); - // Adjust the count for dual byte mode - if (m_control_reg[counter] & COUNT_MODE_8BIT) - { - int divisor = (m_counter[counter] & 0xff) + 1; - int msb = remaining / divisor; - int lsb = remaining % divisor; - remaining = (msb << 8) | lsb; - } - - LOGMASKED(LOG_COUNTERS, "Timer #%d read counter: %04x\n", counter + 1, remaining); + LOGMASKED(LOG_COUNTERS, "Timer #%d read counter: %04x\n", idx + 1, remaining); return remaining; } //------------------------------------------------- -// reload_count - Reload Counter +// reload_counter //------------------------------------------------- -void ptm6840_device::reload_count(int idx) +void ptm6840_device::reload_counter(int idx) { + const bool one_shot_mode = m_mode[idx] == 4 || m_mode[idx] == 6; + // Copy the latched value in m_counter[idx] = m_latch[idx]; @@ -397,22 +337,30 @@ void ptm6840_device::reload_count(int idx) } // Determine the number of clock periods before we expire - int count = m_counter[idx]; + int count = m_counter[idx] + 1; if (m_control_reg[idx] & COUNT_MODE_8BIT) { - if (m_hightime[idx]) - count = 0xff; + const uint16_t latch_lsb = m_latch[idx] & 0xff; + const uint16_t latch_msb = m_latch[idx] >> 8; + if (!m_output[idx]) + { + count = (latch_lsb + 1) * latch_msb; + } else - count = ((count >> 8) + 1) * ((count & 0xff) + 1); + { + count = latch_lsb + 1; + } } - - m_fired[idx] = 0; - - const bool one_shot_mode = m_mode[idx] == 4 || m_mode[idx] == 6; - if (one_shot_mode) + else if (one_shot_mode) { - m_output[idx] = false; - m_out_cb[idx](m_output[idx]); + if (!m_output[idx]) + { + count = 1; + } + else + { + count = m_counter[idx]; + } } // Set the timer @@ -420,11 +368,12 @@ void ptm6840_device::reload_count(int idx) if (clk == 0.0) { - m_enabled[idx] = 0; + m_enabled[idx] = false; m_timer[idx]->adjust(attotime::never); } else { + m_enabled[idx] = true; attotime duration = attotime::from_hz(clk) * count; if (idx == 2) @@ -432,12 +381,8 @@ void ptm6840_device::reload_count(int idx) duration *= m_t3_divisor; } - duration += attotime::from_ticks(2, clock()); - LOGMASKED(LOG_COUNTERS, "Timer #%d init_timer: duration = %f\n", idx + 1, duration.as_double()); - m_enabled[idx] = 1; - const bool one_shot_mode = m_mode[idx] == 4 || m_mode[idx] == 6; const bool gated = (!one_shot_mode && m_gate[idx]) || (m_control_reg[0] & RESET_TIMERS); if (gated) @@ -558,9 +503,8 @@ void ptm6840_device::write(offs_t offset, uint8_t data) for (int i = 0; i < 3; i++) { m_timer[i]->adjust(attotime::never); - m_enabled[i] = 0; - m_hightime[i] = false; - reload_count(i); + m_enabled[i] = false; + reload_counter(i); m_output[i] = false; m_out_cb[i](m_output[i]); } @@ -570,8 +514,8 @@ void ptm6840_device::write(offs_t offset, uint8_t data) { for (int i = 0; i < 3; i++) { - m_hightime[i] = false; - reload_count(i); + m_single_fired[i] = false; + reload_counter(i); if (!m_disable_time[i].is_never() && m_timer[i]->remaining().is_never() && ((m_control_reg[i] & INTERNAL_CLK_EN) || m_external_clock[i] != 0.0)) { m_timer[i]->adjust(m_disable_time[i], i); @@ -587,56 +531,7 @@ void ptm6840_device::write(offs_t offset, uint8_t data) // Changing the clock source? (e.g. Zwackery) if (diffs & INTERNAL_CLK_EN) { - m_hightime[idx] = false; - if (!(m_control_reg[0] & RESET_TIMERS)) - { - double divisor = idx == 2 ? m_t3_divisor : 1.0; - double clk = (m_control_reg[idx] & INTERNAL_CLK_EN ? static_cast(clock()) : m_external_clock[idx]) / divisor; - - if (clk == 0.0) - { - // Temporarily restore the old control value to retrieve the current counter value - m_control_reg[idx] ^= diffs; - m_counter[idx] = compute_counter(idx); - m_control_reg[idx] = data; - - m_enabled[idx] = 0; - m_timer[idx]->adjust(attotime::never); - } - else - { - attotime duration = attotime::from_hz(clk); - u16 updated_count = m_counter[idx]; - if (m_control_reg[idx] & INTERNAL_CLK_EN && m_external_clock[idx] == 0) - { - duration *= updated_count; - } - else - { - // Temporarily restore the old control value to retrieve the current counter value - m_control_reg[idx] ^= diffs; - updated_count = compute_counter(idx); - duration *= updated_count; - m_control_reg[idx] = data; - } - - duration += attotime::from_ticks(2, clock()); - - m_enabled[idx] = 1; - - const bool one_shot_mode = m_mode[idx] == 4 || m_mode[idx] == 6; - const bool gated = !one_shot_mode && m_gate[idx]; - if (gated) - { - m_timer[idx]->adjust(attotime::never); - m_disable_time[idx] = duration; - } - else - { - m_timer[idx]->adjust(duration, idx); - } - } - } + update_expiration_for_clock_source(idx, !(m_control_reg[idx] & INTERNAL_CLK_EN), m_external_clock[idx]); } break; } @@ -664,8 +559,7 @@ void ptm6840_device::write(offs_t offset, uint8_t data) // Reload the count if in an appropriate mode if (!(m_control_reg[idx] & 0x10) || (m_control_reg[0] & RESET_TIMERS)) { - m_hightime[idx] = false; - reload_count(idx); + reload_counter(idx); } LOGMASKED(LOG_COUNTERS, "%s: Counter #%d latch = %04X\n", machine().describe_context(), idx + 1, m_latch[idx]); @@ -676,70 +570,69 @@ void ptm6840_device::write(offs_t offset, uint8_t data) //------------------------------------------------- -// timeout - Called if timer is mature +// state_changed - called if timer output state +// changes (masked or not) //------------------------------------------------- -TIMER_CALLBACK_MEMBER(ptm6840_device::timeout) +TIMER_CALLBACK_MEMBER(ptm6840_device::state_changed) { - LOGMASKED(LOG_TIMEOUTS, "**ptm6840 t%d timeout**\n", param + 1); - - // Set the interrupt flag - m_status_reg |= (1 << param); - m_status_read_since_int &= ~(1 << param); - update_interrupts(); - - if (m_control_reg[param] & COUNT_OUT_EN) - { - switch (m_mode[param]) - { - case 0: - case 2: - - if (m_control_reg[param] & COUNT_MODE_8BIT) - { - m_hightime[param] = !m_hightime[param]; - m_output[param] = m_hightime[param]; - m_out_cb[param](m_output[param]); - } - else - { - m_output[param] = !m_output[param]; - m_out_cb[param](m_output[param]); - } - LOGMASKED(LOG_TIMEOUTS, "%6.6f: **ptm6840 t%d output %d **\n", machine().time().as_double(), param + 1, m_output[param]); - break; - - case 4: - case 6: - if (!m_fired[param]) - { - m_output[param] = true; - LOGMASKED(LOG_TIMEOUTS, "**ptm6840 t%d output %d **\n", param + 1, m_output[param]); - - m_out_cb[param](m_output[param]); - - // No changes in output until reinit - m_fired[param] = 1; - - m_status_reg |= (1 << param); - m_status_read_since_int &= ~(1 << param); - update_interrupts(); - } - break; - } - } - else - { - m_out_cb[param](0); - } - - m_enabled[param] = 0; + LOGMASKED(LOG_TIMEOUTS, "**ptm6840 t%d state_changed**\n", param + 1); + // Set the interrupt flag if at the end of a full cycle const bool one_shot_mode = m_mode[param] == 4 || m_mode[param] == 6; - if (!one_shot_mode) + const bool dual_8bit = m_control_reg[param] & COUNT_MODE_8BIT; + const bool end_of_cycle = (!dual_8bit && !one_shot_mode) || m_output[param]; + if (end_of_cycle) { - reload_count(param); + m_status_reg |= (1 << param); + m_status_read_since_int &= ~(1 << param); + update_interrupts(); } + + const bool enable_output = m_control_reg[param] & COUNT_OUT_EN; + switch (m_mode[param]) + { + case 0: + case 2: + m_output[param] = !m_output[param]; + if (enable_output) + { + m_out_cb[param](m_output[param]); + } + else + { + m_out_cb[param](0); + } + LOGMASKED(LOG_TIMEOUTS, "%6.6f: **ptm6840 t%d output %d **\n", machine().time().as_double(), param + 1, m_output[param]); + break; + + case 4: + case 6: + m_output[param] = !m_output[param]; + LOGMASKED(LOG_TIMEOUTS, "**ptm6840 t%d output %d **\n", param + 1, m_output[param]); + + if (!m_single_fired[param]) + { + if (enable_output) + { + m_out_cb[param](m_output[param]); + } + + if (!m_output[param]) + { + // Don't allow output to change until reinitialization + m_single_fired[param] = true; + } + } + else + { + m_out_cb[param](0); + } + break; + } + + m_enabled[param] = false; + reload_counter(param); } @@ -754,8 +647,9 @@ void ptm6840_device::set_gate(int idx, int state) { if (!(m_control_reg[0] & RESET_TIMERS)) { - m_hightime[idx] = false; - reload_count(idx); + m_single_fired[idx] = false; + m_output[idx] = false; + reload_counter(idx); } if (!m_disable_time[idx].is_never() && ((m_control_reg[idx] & INTERNAL_CLK_EN) || m_external_clock[idx] != 0.0)) { @@ -799,11 +693,93 @@ void ptm6840_device::set_clock(int idx, int state) // Don't allow ticking if timers are held in reset, internally-clocked, or gated if (use_external_clk && timer_running && !gated) { - tick(idx, 1); + if (idx == 2) + { + m_t3_scaler++; + if (m_t3_scaler >= m_t3_divisor) + { + deduct_from_counter(idx); + m_t3_scaler = 0; + } + } + else + { + deduct_from_counter(idx); + } } } +//------------------------------------------------- +// update_expiration_for_clock_source +//------------------------------------------------- + +void ptm6840_device::update_expiration_for_clock_source(int idx, bool changed_to_external, double new_external_clock) +{ + if (!(m_control_reg[0] & RESET_TIMERS)) + { + double divisor = idx == 2 ? m_t3_divisor : 1.0; + double clk = (m_control_reg[idx] & INTERNAL_CLK_EN ? static_cast(clock()) : new_external_clock) / divisor; + + // First, figure out how much time was remaining on the counter + if (changed_to_external) + { + m_control_reg[idx] |= INTERNAL_CLK_EN; + } + + int updated_counter = compute_counter(idx); + + if (changed_to_external) + { + m_control_reg[idx] &= ~INTERNAL_CLK_EN; + } + + if (clk == 0.0) + { + // If we're externally clocked with no fixed incoming clock + + // Adjust for dual-byte mode if we're not in the last countdown + if ((m_control_reg[idx] & COUNT_MODE_8BIT) && !m_output[idx]) + { + const uint16_t latch_lsb = m_latch[idx] & 0xff; + const uint16_t latch_msb = m_latch[idx] >> 8; + const uint8_t count_lsb = updated_counter % latch_msb; + const uint8_t count_msb = (updated_counter - count_lsb) / (latch_lsb + 1); + m_counter[idx] = (count_msb << 8) | count_lsb; + } + else + { + m_counter[idx] = updated_counter; + } + + m_enabled[idx] = false; + m_timer[idx]->adjust(attotime::never); + return; + } + else + { + // If we're externally clocked with a valid incoming clock OR we're internally-clocked + attotime duration = attotime::from_hz(clk) * updated_counter; + + m_enabled[idx] = true; + + const bool one_shot_mode = m_mode[idx] == 4 || m_mode[idx] == 6; + const bool gated = !one_shot_mode && m_gate[idx]; + if (gated) + { + m_timer[idx]->adjust(attotime::never); + m_disable_time[idx] = duration; + } + else + { + m_timer[idx]->adjust(duration, idx); + } + } + } +} + + + //------------------------------------------------- // set_ext_clock - set external clock frequency //------------------------------------------------- @@ -813,46 +789,9 @@ void ptm6840_device::set_ext_clock(int idx, double clk) if (m_external_clock[idx] == clk) return; - m_counter[idx] = compute_counter(idx); - if (!(m_control_reg[idx] & INTERNAL_CLK_EN) && clk == 0.0) + if (!(m_control_reg[idx] & INTERNAL_CLK_EN)) { - m_enabled[idx] = 0; - m_timer[idx]->adjust(attotime::never); - } - else - { - double new_clk = (m_control_reg[idx] & INTERNAL_CLK_EN) ? (double)clock() : clk; - - // Determine the number of clock periods before we expire - int count = m_counter[idx]; - - if (m_control_reg[idx] & COUNT_MODE_8BIT) - { - count = ((count >> 8) + 1) * ((count & 0xff) + 1); - } - - attotime duration = attotime::from_hz(new_clk) * count; - - if (idx == 2) - { - duration *= m_t3_divisor; - } - - duration += attotime::from_ticks(2, clock()); - - m_enabled[idx] = 1; - - const bool one_shot_mode = m_mode[idx] == 4 || m_mode[idx] == 6; - const bool gated = (!one_shot_mode && m_gate[idx]) || (m_control_reg[0] & RESET_TIMERS); - if (gated) - { - m_disable_time[idx] = duration; - m_timer[idx]->adjust(attotime::never); - } - else - { - m_timer[idx]->adjust(duration, idx); - } + update_expiration_for_clock_source(idx, false, clk); } m_external_clock[idx] = clk; diff --git a/src/devices/machine/6840ptm.h b/src/devices/machine/6840ptm.h index 9cab9cbd1ba..614901625a4 100644 --- a/src/devices/machine/6840ptm.h +++ b/src/devices/machine/6840ptm.h @@ -33,11 +33,11 @@ public: auto o3_callback() { return m_out_cb[2].bind(); } auto irq_callback() { return m_irq_cb.bind(); } - int status(int clock) const { return m_enabled[clock]; } // get whether timer is enabled - int irq_state() const { return m_irq; } // get IRQ state - uint16_t count(int counter) const { return compute_counter(counter); } // get counter value - void set_ext_clock(int counter, double clock); // set clock frequency - int ext_clock(int counter) const { return m_external_clock[counter]; } // get clock frequency + int status(int idx) const { return m_enabled[idx]; } // get whether timer is enabled + int irq_state() const { return m_irq; } // get IRQ state + int count(int idx) const { return compute_counter(idx); } // get counter value + void set_ext_clock(int counter, double clock); // set clock frequency + int ext_clock(int idx) const { return m_external_clock[idx]; } // get clock frequency void write(offs_t offset, uint8_t data); uint8_t read(offs_t offset); @@ -61,12 +61,13 @@ protected: virtual void device_resolve_objects() override; private: - void subtract_from_counter(int counter, int count); - void tick(int counter, int count); - TIMER_CALLBACK_MEMBER(timeout); + void deduct_from_counter(int idx); + void tick(int counter); + TIMER_CALLBACK_MEMBER(state_changed); - uint16_t compute_counter(int counter) const; - void reload_count(int idx); + int compute_counter(int idx) const; + void reload_counter(int idx); + void update_expiration_for_clock_source(int idx, bool changed_to_external = false, double new_external_clock = 0.0); enum { @@ -112,7 +113,7 @@ private: bool m_clk[3]; // Clock states bool m_enabled[3]; uint8_t m_mode[3]; - bool m_fired[3]; + bool m_single_fired[3]; uint8_t m_t3_divisor; uint8_t m_t3_scaler; uint8_t m_irq; @@ -129,9 +130,6 @@ private: attotime m_disable_time[3]; static const char *const opmode[]; - - // set in dual 8 bit mode to indicate Output high time cycle - bool m_hightime[3]; };