From ea1b40a9f731a448376a6b3dfe7c086def877538 Mon Sep 17 00:00:00 2001 From: hap Date: Sat, 23 Mar 2024 18:12:06 +0100 Subject: hd6301x: fix missed timer 2 event(s) when tconr < elapsed counts, m6801: improve serial rx timing (novag tv interface from next commit relies on more accurate timing) --- src/devices/cpu/m6800/m6801.cpp | 210 ++++++++++++++++++++++++++++------------ src/devices/cpu/m6800/m6801.h | 13 ++- 2 files changed, 158 insertions(+), 65 deletions(-) diff --git a/src/devices/cpu/m6800/m6801.cpp b/src/devices/cpu/m6800/m6801.cpp index 725a3d42731..ae14307b246 100644 --- a/src/devices/cpu/m6800/m6801.cpp +++ b/src/devices/cpu/m6800/m6801.cpp @@ -852,12 +852,50 @@ void hd6301x_cpu_device::check_timer_event() set_timer_event(); } +void m6801_cpu_device::schedule_tx_event() +{ + if (m_trcsr & M6801_TRCSR_TE) + machine().scheduler().synchronize(timer_expired_delegate(FUNC(m6801_cpu_device::tx_tick), this)); +} + +void m6801_cpu_device::schedule_rx_event() +{ + // don't schedule more than 1 rx event + if (m_rx_event_remain) + m_rx_event_remain++; + else if (clock_internal_rx()) + { + machine().scheduler().synchronize(timer_expired_delegate(FUNC(m6801_cpu_device::rx_tick), this)); + m_rx_event_remain = 1; + } +} + +void m6801_cpu_device::check_serial_event(int amount) +{ + // serial tx/rx event (when CC is 1 or 2) + m_tx_event -= amount; + if (m_tx_event <= 0) + { + m_tx_event += m_tx_period; + schedule_tx_event(); + } + + m_rx_event -= amount; + while (m_rx_event <= 0) // rx can have more than 1 clock per timeslice + { + m_rx_event += m_rx_period; + schedule_rx_event(); + } +} + void m6801_cpu_device::increment_counter(int amount) { m6800_cpu_device::increment_counter(amount); CTD += amount; if (CTD >= m_timer_next) check_timer_event(); + if (m_tx_period) + check_serial_event(amount); } void hd6301x_cpu_device::increment_counter(int amount) @@ -892,13 +930,14 @@ void hd6301x_cpu_device::increment_counter(int amount) CTD += amount; if (CTD >= m_timer_next || (m_tcsr3 & 0xc0) == 0xc0) check_timer_event(); + if (m_tx_period) + check_serial_event(amount); } void m6801_cpu_device::eat_cycles() { - int cycles_to_eat = std::min(int(m_timer_next - CTD), m_icount); - if (cycles_to_eat > 0) - increment_counter(cycles_to_eat); + while (m_icount > 0) + increment_counter(1); } /* cleanup high-word of counters */ @@ -935,23 +974,24 @@ void m6801_cpu_device::set_rmcr(u8 data) { case 0: LOGSER("SCI: Using external serial clock: false\n"); - m_sci_timer->adjust(attotime::never); + reset_sci_timer(); m_use_ext_serclock = false; break; case 3: // external clock LOGSER("SCI: Using external serial clock: true\n"); m_use_ext_serclock = true; - m_sci_timer->adjust(attotime::never); + reset_sci_timer(); break; case 1: case 2: { int divisor = M6801_RMCR_SS[m_rmcr & M6801_RMCR_SS_MASK]; - attotime period = cycles_to_attotime(divisor); - LOGSER("SCI: Setting serial rate, Divisor: %d Hz: %d\n", divisor, period.as_hz()); - m_sci_timer->adjust(period, 0, period); + LOGSER("SCI: Setting serial rate, Divisor: %d Hz: %d\n", divisor, cycles_to_attotime(divisor).as_hz()); + reset_sci_timer(); + m_rx_event = m_rx_period = divisor / 8; + m_tx_event = m_tx_period = divisor; m_use_ext_serclock = false; } break; @@ -971,7 +1011,7 @@ void hd6301x_cpu_device::set_rmcr(u8 data) case 7: // external clock LOGSER("SCI: Using external serial clock: true\n"); m_use_ext_serclock = true; - m_sci_timer->adjust(attotime::never); + reset_sci_timer(); break; case 1: @@ -982,14 +1022,15 @@ void hd6301x_cpu_device::set_rmcr(u8 data) if (BIT(m_rmcr, 5)) { LOGSER("SCI: Using Timer 2 clock\n"); - m_sci_timer->adjust(attotime::never); + reset_sci_timer(); } else { int divisor = M6801_RMCR_SS[m_rmcr & M6801_RMCR_SS_MASK]; - attotime period = cycles_to_attotime(divisor); - LOGSER("SCI: Setting serial rate, Divisor: %d Hz: %d\n", divisor, period.as_hz()); - m_sci_timer->adjust(period, 0, period); + LOGSER("SCI: Setting serial rate, Divisor: %d Hz: %d\n", divisor, cycles_to_attotime(divisor).as_hz()); + reset_sci_timer(); + m_rx_event = m_rx_period = divisor / 8; + m_tx_event = m_tx_period = divisor; } m_use_ext_serclock = false; break; @@ -1129,8 +1170,12 @@ void m6801_cpu_device::serial_receive() // start bit found m_rxbits++; + // it synchronizes to the middle of the next bit + m_rx_clocks = 11; + LOGRX("SCI Received START bit\n"); } + break; case M6801_SERIAL_STOP: @@ -1199,10 +1244,57 @@ void m6801_cpu_device::serial_receive() } } -TIMER_CALLBACK_MEMBER(m6801_cpu_device::sci_tick) +bool m6801_cpu_device::clock_internal_rx() +{ + // internally, rx runs at 8 times the data rate while it waits for the start bit + if (!(m_trcsr & M6801_TRCSR_WU) && m_rxbits == M6801_SERIAL_START) + m_rx_clocks = 1; + else if (m_rx_clocks == 0) + m_rx_clocks = 8; + + return (--m_rx_clocks == 0 && m_trcsr & M6801_TRCSR_RE); +} + +TIMER_CALLBACK_MEMBER(m6801_cpu_device::rx_tick) { - serial_transmit(); serial_receive(); + + // handle leftover rx clocks now + if (m_rx_event_remain) + { + while (--m_rx_event_remain) + if (clock_internal_rx()) + serial_receive(); + } +} + +TIMER_CALLBACK_MEMBER(m6801_cpu_device::tx_tick) +{ + serial_transmit(); +} + +void m6801_cpu_device::reset_sci_timer() +{ + m_rx_clocks = 0; + m_sci_clocks = 0; + m_tx_period = 0; + m_rx_period = 0; +} + +void m6801_cpu_device::clock_serial() +{ + if (m_use_ext_serclock) + { + m_sci_clocks++; + + if (!(m_sci_clocks & (m_sclk_divider - 1) >> 3) && clock_internal_rx()) + serial_receive(); + if (m_sci_clocks >= m_sclk_divider) + { + m_sci_clocks = 0; + serial_transmit(); + } + } } @@ -1290,8 +1382,6 @@ void m6801_cpu_device::device_start() { m6800_cpu_device::device_start(); - m_sci_timer = timer_alloc(FUNC(m6801_cpu_device::sci_tick), this); - std::fill(std::begin(m_port_ddr), std::end(m_port_ddr), 0); std::fill(std::begin(m_port_data), std::end(m_port_data), 0); m_p3csr = 0; @@ -1318,7 +1408,13 @@ void m6801_cpu_device::device_start() m_trcsr_read_orfe = 0; m_trcsr_read_rdrf = 0; m_tx = 0; - m_ext_serclock = 0; + m_rx_event = 0; + m_tx_event = 0; + m_rx_period = 0; + m_tx_period = 0; + m_rx_event_remain = 0; + m_rx_clocks = 0; + m_sci_clocks = 0; m_use_ext_serclock = false; m_latch09 = 0; @@ -1355,7 +1451,13 @@ void m6801_cpu_device::device_start() save_item(NAME(m_trcsr_read_orfe)); save_item(NAME(m_trcsr_read_rdrf)); save_item(NAME(m_tx)); - save_item(NAME(m_ext_serclock)); + save_item(NAME(m_rx_event)); + save_item(NAME(m_tx_event)); + save_item(NAME(m_rx_period)); + save_item(NAME(m_tx_period)); + save_item(NAME(m_rx_event_remain)); + save_item(NAME(m_rx_clocks)); + save_item(NAME(m_sci_clocks)); save_item(NAME(m_use_ext_serclock)); save_item(NAME(m_latch09)); @@ -1450,8 +1552,8 @@ void m6801_cpu_device::device_reset() m_trcsr_read_tdre = 0; m_trcsr_read_orfe = 0; m_trcsr_read_rdrf = 0; - m_ext_serclock = 0; m_use_ext_serclock = false; + reset_sci_timer(); set_rmcr(0); } @@ -2329,46 +2431,45 @@ void hd6301x_cpu_device::ocr2l_w(u8 data) void hd6301x_cpu_device::increment_t2cnt(int amount) { - if (amount > u8(m_tconr - m_t2cnt)) + while (amount--) { - if (m_t2cnt > m_tconr) + if (++m_t2cnt == ((m_tconr + 1) & 0xff)) { - amount -= 256 - m_t2cnt; m_t2cnt = 0; - } - m_t2cnt = (m_t2cnt + amount) % (m_tconr + 1); - if (BIT(m_tcsr3, 3)) - { - if (m_tout3 != BIT(m_tcsr3, 2)) + if (BIT(m_tcsr3, 3)) { - m_tout3 = BIT(m_tcsr3, 2); + if (m_tout3 != BIT(m_tcsr3, 2)) + { + m_tout3 = BIT(m_tcsr3, 2); + m_port2_written = true; + write_port2(); + } + } + else if (BIT(m_tcsr3, 2)) + { + m_tout3 = !m_tout3; m_port2_written = true; write_port2(); } - } - else if (BIT(m_tcsr3, 2)) - { - m_tout3 = !m_tout3; - m_port2_written = true; - write_port2(); - } - if (BIT(m_rmcr, 5) && !m_use_ext_serclock) - { - m_ext_serclock++; - if (m_ext_serclock >= 32) + if (BIT(m_rmcr, 5) && !m_use_ext_serclock) { - m_ext_serclock = 0; - machine().scheduler().synchronize(timer_expired_delegate(FUNC(hd6301x_cpu_device::sci_tick), this)); + m_sci_clocks++; + + if (!(m_sci_clocks & 3)) + schedule_rx_event(); + if (m_sci_clocks >= 32) + { + m_sci_clocks = 0; + schedule_tx_event(); + } } - } - m_tcsr3 |= 0x80; - m_timer_next = 0; // HACK + m_tcsr3 |= 0x80; + m_timer_next = 0; // HACK + } } - else - m_t2cnt += amount; } u8 hd6301x_cpu_device::t2cnt_r() @@ -2547,21 +2648,6 @@ u8 m6801_cpu_device::ff_r() } -void m6801_cpu_device::clock_serial() -{ - if (m_use_ext_serclock) - { - m_ext_serclock++; - - if (m_ext_serclock >= m_sclk_divider) - { - m_ext_serclock = 0; - serial_transmit(); - serial_receive(); - } - } -} - std::unique_ptr m6801_cpu_device::create_disassembler() { return std::make_unique(6801); diff --git a/src/devices/cpu/m6800/m6801.h b/src/devices/cpu/m6800/m6801.h index 11968b4e3a8..77a17ce62e9 100644 --- a/src/devices/cpu/m6800/m6801.h +++ b/src/devices/cpu/m6800/m6801.h @@ -163,14 +163,15 @@ protected: bool m_port2_written; u8 m_trcsr, m_rmcr, m_rdr, m_tdr, m_rsr, m_tshr; - int m_rxbits, m_txbits, m_txstate, m_trcsr_read_tdre, m_trcsr_read_orfe, m_trcsr_read_rdrf, m_tx, m_ext_serclock; + int m_rxbits, m_txbits, m_txstate, m_trcsr_read_tdre, m_trcsr_read_orfe, m_trcsr_read_rdrf, m_tx; + s32 m_rx_event, m_tx_event, m_rx_period, m_tx_period; + u8 m_rx_event_remain, m_rx_clocks, m_sci_clocks; bool m_use_ext_serclock; u8 m_latch09; int m_is3_state; PAIR m_timer_over; - emu_timer *m_sci_timer; u32 m_timer_next; // point of next timer event static const u8 cycles_6803[256]; @@ -197,7 +198,13 @@ protected: int m6801_rx(); void serial_transmit(); void serial_receive(); - TIMER_CALLBACK_MEMBER(sci_tick); + TIMER_CALLBACK_MEMBER(tx_tick); + TIMER_CALLBACK_MEMBER(rx_tick); + bool clock_internal_rx(); + void schedule_tx_event(); + void schedule_rx_event(); + void check_serial_event(int amount); + void reset_sci_timer(); void set_os3(int state); }; -- cgit v1.2.3