mirror of
https://github.com/holub/mame
synced 2025-04-24 17:30:55 +03:00
-6840ptm: Fixed counter duration and fixed output behavior in single-shot and dual-8-bit modes. [Ryan Holtz] (#10111)
This commit is contained in:
parent
ad63028466
commit
3eb3713e87
@ -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<double>(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<double>(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<double>(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;
|
||||
|
@ -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];
|
||||
};
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user