diff options
Diffstat (limited to 'src/devices/cpu/h8/h8_sci.cpp')
-rw-r--r-- | src/devices/cpu/h8/h8_sci.cpp | 1037 |
1 files changed, 547 insertions, 490 deletions
diff --git a/src/devices/cpu/h8/h8_sci.cpp b/src/devices/cpu/h8/h8_sci.cpp index c511ea664f9..3ec8951a22d 100644 --- a/src/devices/cpu/h8/h8_sci.cpp +++ b/src/devices/cpu/h8/h8_sci.cpp @@ -1,721 +1,778 @@ // license:BSD-3-Clause // copyright-holders:Olivier Galibert + #include "emu.h" #include "h8_sci.h" -// Verbosity level -// 0 = no messages -// 1 = transmitted/recieved bytes, reception errors and clock setup -// 2 = everything but status register reads -// 3 = everything -const int V = 1; +#include "h8.h" + +#define LOG_REGS (1 << 1U) // Register writes +#define LOG_RREGS (1 << 2U) // Register reads +#define LOG_RATE (1 << 3U) // Bitrate setting, beware that gk2000 changes it all the time ending up in a massive slowdown, don't leave it active +#define LOG_DATA (1 << 4U) // Bytes transmitted +#define LOG_CLOCK (1 << 5U) // Clock and transmission start/stop +#define LOG_STATE (1 << 6U) // State machine states +#define LOG_TICK (1 << 7U) // Clock ticks +//#define VERBOSE (LOG_DATA) +//#define LOG_OUTPUT_FUNC osd_printf_info +#include "logmacro.h" DEFINE_DEVICE_TYPE(H8_SCI, h8_sci_device, "h8_sci", "H8 Serial Communications Interface") + +// Clocking: +// Async mode: +// The circuit wants 16 events per bit. +// * Internal clocking: the cpu clock is divided by one of (1, 4, 16, 64) from the cks field of smr +// then by (brr+1) then by 2. +// * External clocking: the external clock is supposed to be 16*bitrate. +// Sync mode: +// The circuit wants 2 events per bit, a positive and a negative edge. +// * Internal clocking: the cpu clock is divided by one of (1, 4, 16, 64) from the cks field of smr +// then by (brr+1) then by 2. Events are then interpreted has been alternatively positive and +// negative (e.g. another divide-by-two, sync-wise). +// * External clocking: the external clock is supposed to be at bitrate, both edges are used. +// +// Synchronization: +// Async mode: +// Both modes use a 4-bits counter incremented on every event (16/bit). +// +// * Transmit sets the counter to 0 at transmit start. Output data line changes value +// on counter == 0. If the clock output is required, clk=1 outside of transmit, +// clk=0 on counter==0, clk=1 on counter==8. +// +// * Receive sets the counter to 0 when the data line initially goes down (start bit) +// Output line is read on counter==8. It is unknown whether the counter is reset +// on every data line level change. +// +// Sync mode: +// * Transmit changes the data line on negative edges, the clock line, following positive and +// negative edge definition, is output as long as transmit is active and is otherwise 1. +// +// * Receive reads the data line on positive edges. +// +// Framing: +// Async mode: 1 bit of start at 0, 7 or 8 bits of data, nothing or 1 bit of parity or 1 bit of multiprocessing, 1 or 2 bits of stop at 1. +// Sync mode: 8 bits of data. +// +// Multiprocessing bit is an extra bit which value can be set on transmit in bit zero of ssr. +// On receive when zero the byte is dropped. + + const char *const h8_sci_device::state_names[] = { "idle", "start", "bit", "parity", "stop", "last-tick" }; -h8_sci_device::h8_sci_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : +h8_sci_device::h8_sci_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) : device_t(mconfig, H8_SCI, tag, owner, clock), - cpu(*this, DEVICE_SELF_OWNER), - tx_cb(*this), - clk_cb(*this), intc(nullptr), intc_tag(nullptr), external_to_internal_ratio(0), internal_to_external_ratio(0), sync_timer(nullptr), eri_int(0), rxi_int(0), txi_int(0), tei_int(0), - tx_state(0), rx_state(0), tx_bit(0), rx_bit(0), clock_state(0), clock_mode(0), tx_parity(0), rx_parity(0), ext_clock_counter(0), clock_value(false), ext_clock_value(false), rx_value(false), - rdr(0), tdr(0), smr(0), scr(0), ssr(0), brr(0), rsr(0), tsr(0), clock_base(0), divider(0) + m_cpu(*this, finder_base::DUMMY_TAG), + m_intc(*this, finder_base::DUMMY_TAG), + m_external_to_internal_ratio(0), m_internal_to_external_ratio(0), m_sync_timer(nullptr), m_id(0), m_eri_int(0), m_rxi_int(0), m_txi_int(0), m_tei_int(0), + m_tx_state(0), m_rx_state(0), m_tx_bit(0), m_rx_bit(0), m_clock_state(0), m_tx_parity(0), m_rx_parity(0), m_tx_clock_counter(0), m_rx_clock_counter(0), + m_clock_mode(INTERNAL_ASYNC), m_ext_clock_value(false), m_rx_value(true), + m_rdr(0), m_tdr(0), m_smr(0), m_scr(0), m_ssr(0), m_ssr_read(0), m_brr(0), m_rsr(0), m_tsr(0), m_clock_event(0), m_divider(0) { - external_clock_period = attotime::never; + m_external_clock_period = attotime::never; } -void h8_sci_device::set_info(const char *_intc_tag, int eri, int rxi, int txi, int tei) +void h8_sci_device::do_set_external_clock_period(const attotime &period) { - intc_tag = _intc_tag; - eri_int = eri; - rxi_int = rxi; - txi_int = txi; - tei_int = tei; + m_external_clock_period = period; } -void h8_sci_device::set_external_clock_period(const attotime &period) +void h8_sci_device::smr_w(u8 data) { - external_clock_period = period; -} + m_smr = data; + + LOGMASKED(LOG_REGS, "smr_w %02x %s %c%c%c%s /%d (%06x)\n", data, + data & SMR_CA ? "sync" : "async", + data & SMR_CHR ? '7' : '8', + data & SMR_PE ? data & SMR_OE ? 'o' : 'e' : 'n', + data & SMR_STOP ? '2' : '1', + data & SMR_MP ? " mp" : "", + 1 << 2*(data & SMR_CKS), + m_cpu->pc()); -WRITE8_MEMBER(h8_sci_device::smr_w) -{ - smr = data; - if(V>=2) logerror("smr_w %02x %s %c%c%c%s /%d (%06x)\n", data, - data & SMR_CA ? "sync" : "async", - data & SMR_CHR ? '7' : '8', - data & SMR_PE ? data & SMR_OE ? 'o' : 'e' : 'n', - data & SMR_STOP ? '2' : '1', - data & SMR_MP ? " mp" : "", - 1 << 2*(data & SMR_CKS), - cpu->pc()); clock_update(); + + sync_rx_start(); } -READ8_MEMBER(h8_sci_device::smr_r) +u8 h8_sci_device::smr_r() { - if(V>=2) logerror("smr_r %02x (%06x)\n", smr, cpu->pc()); - return smr; + if(!machine().side_effects_disabled()) + LOGMASKED(LOG_RREGS, "smr_r %02x (%06x)\n", m_smr, m_cpu->pc()); + return m_smr; } -WRITE8_MEMBER(h8_sci_device::brr_w) +void h8_sci_device::brr_w(u8 data) { - brr = data; - if(V>=2) logerror("brr_w %02x (%06x)\n", data, cpu->pc()); + m_brr = data; + LOGMASKED(LOG_REGS, "brr_w %02x (%06x)\n", m_brr, m_cpu->pc()); clock_update(); } -READ8_MEMBER(h8_sci_device::brr_r) +u8 h8_sci_device::brr_r() { - if(V>=2) logerror("brr_r %02x (%06x)\n", brr, cpu->pc()); - return brr; + if(!machine().side_effects_disabled()) + LOGMASKED(LOG_RREGS, "brr_r %02x (%06x)\n", m_brr, m_cpu->pc()); + return m_brr; } -bool h8_sci_device::is_sync_start() const +void h8_sci_device::sync_rx_start() { - return (smr & SMR_CA) && ((scr & (SCR_TE|SCR_RE)) == (SCR_TE|SCR_RE)); + if(m_rx_state == ST_IDLE && (m_smr & SMR_CA) && (m_scr & SCR_RE) && !(m_scr & SCR_TE) && !has_recv_error()) + rx_start(); } bool h8_sci_device::has_recv_error() const { - return ssr & (SSR_ORER|SSR_PER|SSR_FER); + return m_ssr & (SSR_ORER|SSR_PER|SSR_FER); } -WRITE8_MEMBER(h8_sci_device::scr_w) +void h8_sci_device::scr_w(u8 data) { - if(V>=2) logerror("scr_w %02x%s%s%s%s%s%s clk=%d (%06x)\n", data, - data & SCR_TIE ? " txi" : "", - data & SCR_RIE ? " rxi" : "", - data & SCR_TE ? " tx" : "", - data & SCR_RE ? " rx" : "", - data & SCR_MPIE ? " mpi" : "", - data & SCR_TEIE ? " tei" : "", - data & SCR_CKE, - cpu->pc()); + LOGMASKED(LOG_REGS, "scr_w %02x%s%s%s%s%s%s clk=%d (%06x)\n", data, + data & SCR_TIE ? " txi" : "", + data & SCR_RIE ? " rxi" : "", + data & SCR_TE ? " tx" : "", + data & SCR_RE ? " rx" : "", + data & SCR_MPIE ? " mpi" : "", + data & SCR_TEIE ? " tei" : "", + data & SCR_CKE, + m_cpu->pc()); - uint8_t delta = scr ^ data; - scr = data; + u8 delta = m_scr ^ data; + m_scr = data; clock_update(); - if((delta & SCR_RE) && !(scr & SCR_RE)) { - rx_state = ST_IDLE; + if((delta & SCR_RE) && !(m_scr & SCR_RE)) { + m_rx_state = ST_IDLE; clock_stop(CLK_RX); } - if((delta & SCR_RE) && (scr & SCR_RE) && rx_state == ST_IDLE && !has_recv_error() && !is_sync_start()) - rx_start(); - if((delta & SCR_TIE) && (scr & SCR_TIE) && (ssr & SSR_TDRE)) - intc->internal_interrupt(txi_int); - if((delta & SCR_TEIE) && (scr & SCR_TEIE) && (ssr & SSR_TEND)) - intc->internal_interrupt(tei_int); - if((delta & SCR_RIE) && (scr & SCR_RIE) && (ssr & SSR_RDRF)) - intc->internal_interrupt(rxi_int); - if((delta & SCR_RIE) && (scr & SCR_RIE) && has_recv_error()) - intc->internal_interrupt(eri_int); + if((delta & (SCR_RE | SCR_TE))) + sync_rx_start(); + if((delta & SCR_TIE) && (m_scr & SCR_TIE) && (m_ssr & SSR_TDRE)) + m_intc->internal_interrupt(m_txi_int); + if((delta & SCR_TEIE) && (m_scr & SCR_TEIE) && (m_ssr & SSR_TEND)) + m_intc->internal_interrupt(m_tei_int); + if((delta & SCR_RIE) && (m_scr & SCR_RIE) && (m_ssr & SSR_RDRF)) + m_intc->internal_interrupt(m_rxi_int); + if((delta & SCR_RIE) && (m_scr & SCR_RIE) && has_recv_error()) + m_intc->internal_interrupt(m_eri_int); } -READ8_MEMBER(h8_sci_device::scr_r) +u8 h8_sci_device::scr_r() { - if(V>=2) logerror("scr_r %02x (%06x)\n", scr, cpu->pc()); - return scr; + if(!machine().side_effects_disabled()) + LOGMASKED(LOG_RREGS, "scr_r %02x (%06x)\n", m_scr, m_cpu->pc()); + return m_scr; } -WRITE8_MEMBER(h8_sci_device::tdr_w) +void h8_sci_device::tdr_w(u8 data) { - if(V>=2) logerror("tdr_w %02x (%06x)\n", data, cpu->pc()); - tdr = data; - if(cpu->access_is_dma()) { - ssr &= ~SSR_TDRE; - if(tx_state == ST_IDLE) + LOGMASKED(LOG_REGS, "tdr_w %02x (%06x)\n", data, m_cpu->pc()); + m_tdr = data; + if((m_cpu->access_is_dma()) && (m_scr & SCR_TE)) { + m_ssr &= ~(SSR_TDRE | SSR_TEND); + if(m_tx_state == ST_IDLE) tx_start(); } } -READ8_MEMBER(h8_sci_device::tdr_r) +u8 h8_sci_device::tdr_r() { - if(V>=2) logerror("tdr_r %02x (%06x)\n", tdr, cpu->pc()); - return tdr; + if(!machine().side_effects_disabled()) + LOGMASKED(LOG_RREGS, "tdr_r %02x (%06x)\n", m_tdr, m_cpu->pc()); + return m_tdr; } -WRITE8_MEMBER(h8_sci_device::ssr_w) +void h8_sci_device::ssr_w(u8 data) { - if(!(scr & SCR_TE)) { - data |= SSR_TDRE; - ssr |= SSR_TDRE; - } - if((ssr & SSR_TDRE) && !(data & SSR_TDRE)) - ssr &= ~SSR_TEND; - ssr = ((ssr & ~SSR_MPBT) | (data & SSR_MPBT)) & (data | (SSR_TEND|SSR_MPB|SSR_MPBT)); - if(V>=2) logerror("ssr_w %02x -> %02x (%06x)\n", data, ssr, cpu->pc()); + if((m_scr & SCR_TE) && (m_ssr & m_ssr_read & SSR_TDRE) && !(data & SSR_TDRE)) + m_ssr &= ~(SSR_TDRE | SSR_TEND); + m_ssr = (m_ssr & (~m_ssr_read | data | SSR_TDRE | SSR_TEND | SSR_MPB) & ~SSR_MPBT) | (data & SSR_MPBT); + LOGMASKED(LOG_REGS, "ssr_w %02x -> %02x (%06x)\n", data, m_ssr, m_cpu->pc()); - if(tx_state == ST_IDLE && !(ssr & SSR_TDRE)) + if(m_tx_state == ST_IDLE && !(m_ssr & SSR_TDRE)) tx_start(); - if((scr & SCR_RE) && rx_state == ST_IDLE && !has_recv_error() && !is_sync_start()) - rx_start(); + sync_rx_start(); } -READ8_MEMBER(h8_sci_device::ssr_r) +u8 h8_sci_device::ssr_r() { - if(V>=3) logerror("ssr_r %02x (%06x)\n", ssr, cpu->pc()); - return ssr; + if(!machine().side_effects_disabled()) { + LOGMASKED(LOG_RREGS, "ssr_r %02x (%06x)\n", m_ssr, m_cpu->pc()); + m_ssr_read = m_ssr; + } + return m_ssr; } -READ8_MEMBER(h8_sci_device::rdr_r) +u8 h8_sci_device::rdr_r() { - if(V>=2) logerror("rdr_r %02x (%06x)\n", rdr, cpu->pc()); - if(cpu->access_is_dma()) - ssr &= ~SSR_RDRF; - return rdr; + if(!machine().side_effects_disabled()) + LOGMASKED(LOG_RREGS, "rdr_r %02x (%06x)\n", m_rdr, m_cpu->pc()); + + if(!machine().side_effects_disabled() && m_cpu->access_is_dma()) + m_ssr &= ~SSR_RDRF; + return m_rdr; } -WRITE8_MEMBER(h8_sci_device::scmr_w) +void h8_sci_device::scmr_w(u8 data) { - if(V>=2) logerror("scmr_w %02x (%06x)\n", data, cpu->pc()); + LOGMASKED(LOG_REGS, "scmr_w %02x (%06x)\n", data, m_cpu->pc()); } -READ8_MEMBER(h8_sci_device::scmr_r) +u8 h8_sci_device::scmr_r() { - if(V>=2) logerror("scmr_r (%06x)\n", cpu->pc()); + if(!machine().side_effects_disabled()) + LOGMASKED(LOG_RREGS, "scmr_r (%06x)\n", m_cpu->pc()); return 0x00; } void h8_sci_device::clock_update() { - // Sync: Divider must be the time of a half-period (both edges are used, datarate*2) - // Async: Divider must be the time of one period (only raising edge used, datarate*16) + m_divider = 2 << (2*(m_smr & SMR_CKS)); + m_divider *= m_brr+1; - divider = 2 << (2*(smr & SMR_CKS)); - divider *= brr+1; - - if(smr & SMR_CA) { - if(scr & SCR_CKE1) - clock_mode = CLKM_EXTERNAL_SYNC; + if(m_smr & SMR_CA) { + if(m_scr & SCR_CKE1) + m_clock_mode = EXTERNAL_SYNC; else - clock_mode = CLKM_INTERNAL_SYNC_OUT; + m_clock_mode = INTERNAL_SYNC_OUT; } else { - if(scr & SCR_CKE1) - clock_mode = CLKM_EXTERNAL_ASYNC; - else if(scr & SCR_CKE0) - clock_mode = CLKM_INTERNAL_ASYNC_OUT; + if(m_scr & SCR_CKE1) + m_clock_mode = EXTERNAL_ASYNC; + else if(m_scr & SCR_CKE0) + m_clock_mode = INTERNAL_ASYNC_OUT; else - clock_mode = CLKM_INTERNAL_ASYNC; + m_clock_mode = INTERNAL_ASYNC; } - if(clock_mode == CLKM_EXTERNAL_ASYNC && !external_clock_period.is_never()) - clock_mode = CLKM_EXTERNAL_RATE_ASYNC; - if(clock_mode == CLKM_EXTERNAL_SYNC && !external_clock_period.is_never()) - clock_mode = CLKM_EXTERNAL_RATE_SYNC; + if(m_clock_mode == EXTERNAL_ASYNC && !m_external_clock_period.is_never()) + m_clock_mode = EXTERNAL_RATE_ASYNC; + if(m_clock_mode == EXTERNAL_SYNC && !m_external_clock_period.is_never()) + m_clock_mode = EXTERNAL_RATE_SYNC; - if(V>=1) { - char buf[4096]; - switch(clock_mode) { - case CLKM_INTERNAL_ASYNC: - sprintf(buf, "clock internal at %d Hz, async, bitrate %d bps\n", int(cpu->clock() / divider), int(cpu->clock() / (divider*16))); + if(VERBOSE & LOG_RATE) { + std::string new_message; + switch(m_clock_mode) { + case INTERNAL_ASYNC: + new_message = util::string_format("clock internal at %d Hz, async, bitrate %d bps\n", int(m_cpu->system_clock() / m_divider), int(m_cpu->system_clock() / (m_divider*16))); break; - case CLKM_INTERNAL_ASYNC_OUT: - sprintf(buf, "clock internal at %d Hz, async, bitrate %d bps, output\n", int(cpu->clock() / divider), int(cpu->clock() / (divider*16))); + case INTERNAL_ASYNC_OUT: + new_message = util::string_format("clock internal at %d Hz, async, bitrate %d bps, output\n", int(m_cpu->system_clock() / m_divider), int(m_cpu->system_clock() / (m_divider*16))); break; - - case CLKM_EXTERNAL_ASYNC: - sprintf(buf, "clock external, async\n"); + case EXTERNAL_ASYNC: + new_message = "clock external, async\n"; break; - case CLKM_EXTERNAL_RATE_ASYNC: - sprintf(buf, "clock external at %d Hz, async, bitrate %d bps\n", int(cpu->clock()*internal_to_external_ratio), int(cpu->clock()*internal_to_external_ratio/16)); + case EXTERNAL_RATE_ASYNC: + new_message = util::string_format("clock external at %d Hz, async, bitrate %d bps\n", int(m_cpu->system_clock()*m_internal_to_external_ratio), int(m_cpu->system_clock()*m_internal_to_external_ratio/16)); break; - - case CLKM_INTERNAL_SYNC_OUT: - sprintf(buf, "clock internal at %d Hz, sync, output\n", int(cpu->clock() / (divider*2))); + case INTERNAL_SYNC_OUT: + new_message = util::string_format("clock internal at %d Hz, sync, output\n", int(m_cpu->system_clock() / (m_divider*2))); break; - - case CLKM_EXTERNAL_SYNC: - sprintf(buf, "clock external, sync\n"); + case EXTERNAL_SYNC: + new_message = "clock external, sync\n"; break; - case CLKM_EXTERNAL_RATE_SYNC: - sprintf(buf, "clock external at %d Hz, sync\n", int(cpu->clock()*internal_to_external_ratio)); + case EXTERNAL_RATE_SYNC: + new_message = util::string_format("clock external at %d Hz, sync\n", int(m_cpu->system_clock()*m_internal_to_external_ratio)); break; } - if(buf != last_clock_message) { - last_clock_message = buf; - logerror("%s", buf); + if(new_message != m_last_clock_message) { + (LOG_OUTPUT_FUNC)(new_message); + m_last_clock_message = std::move(new_message); } } } void h8_sci_device::device_start() { - tx_cb.resolve_safe(); - clk_cb.resolve_safe(); + m_sync_timer = timer_alloc(FUNC(h8_sci_device::sync_tick), this); - sync_timer = timer_alloc(0); - - if(external_clock_period.is_never()) { - internal_to_external_ratio = 0; - external_to_internal_ratio = 0; + if(m_external_clock_period.is_never()) { + m_internal_to_external_ratio = 0; + m_external_to_internal_ratio = 0; } else { - external_to_internal_ratio = (external_clock_period*cpu->clock()).as_double(); - internal_to_external_ratio = 1/external_to_internal_ratio; + m_external_to_internal_ratio = (m_external_clock_period*m_cpu->system_clock()).as_double(); + m_internal_to_external_ratio = 1/m_external_to_internal_ratio; } - intc = siblingdevice<h8_intc_device>(intc_tag); - save_item(NAME(rdr)); - save_item(NAME(tdr)); - save_item(NAME(smr)); - save_item(NAME(scr)); - save_item(NAME(ssr)); - save_item(NAME(brr)); - save_item(NAME(rsr)); - save_item(NAME(tsr)); - save_item(NAME(rx_bit)); - save_item(NAME(tx_bit)); - save_item(NAME(rx_state)); - save_item(NAME(tx_state)); - save_item(NAME(tx_parity)); - save_item(NAME(clock_state)); - save_item(NAME(clock_value)); - save_item(NAME(clock_base)); - save_item(NAME(divider)); - save_item(NAME(ext_clock_value)); - save_item(NAME(ext_clock_counter)); - save_item(NAME(cur_sync_time)); + save_item(NAME(m_tx_state)); + save_item(NAME(m_rx_state)); + save_item(NAME(m_tx_bit)); + save_item(NAME(m_rx_bit)); + save_item(NAME(m_clock_state)); + save_item(NAME(m_tx_parity)); + save_item(NAME(m_rx_parity)); + save_item(NAME(m_tx_clock_counter)); + save_item(NAME(m_rx_clock_counter)); + save_item(NAME(m_clock_mode)); + save_item(NAME(m_ext_clock_value)); + save_item(NAME(m_rx_value)); + + save_item(NAME(m_rdr)); + save_item(NAME(m_tdr)); + save_item(NAME(m_smr)); + save_item(NAME(m_scr)); + save_item(NAME(m_ssr)); + save_item(NAME(m_ssr_read)); + save_item(NAME(m_brr)); + save_item(NAME(m_rsr)); + save_item(NAME(m_tsr)); + save_item(NAME(m_clock_event)); + save_item(NAME(m_clock_step)); + save_item(NAME(m_divider)); } void h8_sci_device::device_reset() { - rdr = 0x00; - tdr = 0xff; - smr = 0x00; - scr = 0x00; - ssr = 0x84; - brr = 0xff; - rsr = 0x00; - tsr = 0xff; - rx_bit = 0; - tx_bit = 0; - tx_state = ST_IDLE; - rx_state = ST_IDLE; - clock_state = 0; - clock_mode = CLKM_INTERNAL_ASYNC; - clock_base = 0; + m_rdr = 0x00; + m_tdr = 0xff; + m_smr = 0x00; + m_scr = 0x00; + m_ssr = 0x84; + m_brr = 0xff; + m_rsr = 0x00; + m_tsr = 0xff; + m_rx_bit = 0; + m_tx_bit = 0; + m_tx_state = ST_IDLE; + m_rx_state = ST_IDLE; + m_clock_state = 0; + m_clock_mode = INTERNAL_ASYNC; + m_clock_event = 0; clock_update(); - clock_value = true; - ext_clock_value = true; - ext_clock_counter = 0; - rx_value = true; - clk_cb(clock_value); - tx_cb(1); - cur_sync_time = attotime::never; + m_ext_clock_value = true; + m_tx_clock_counter = 0; + m_rx_clock_counter = 0; + m_cpu->do_sci_clk(m_id, 1); + m_cpu->do_sci_tx(m_id, 1); } -void h8_sci_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +TIMER_CALLBACK_MEMBER(h8_sci_device::sync_tick) { // Used only to force system-wide syncs } -WRITE_LINE_MEMBER(h8_sci_device::rx_w) -{ - rx_value = state; - if(V>=2) logerror("rx=%d\n", state); - if(!rx_value && !(clock_state & CLK_RX) && rx_state != ST_IDLE) - clock_start(CLK_RX); -} - -WRITE_LINE_MEMBER(h8_sci_device::clk_w) +void h8_sci_device::do_rx_w(int state) { - if(ext_clock_value != state) { - ext_clock_value = state; - if(clock_state) { - switch(clock_mode) { - case CLKM_EXTERNAL_ASYNC: - if(ext_clock_value) { - ext_clock_counter = (ext_clock_counter+1) & 15; + if(m_cpu->standby()) { + m_rx_value = state; + return; + } - if((clock_state & CLK_TX) && ext_clock_counter == 0) - tx_dropped_edge(); - if((clock_state & CLK_RX) && ext_clock_counter == 8) - rx_raised_edge(); - } - break; + if(state != m_rx_value && (m_clock_state & CLK_RX)) + if(m_rx_clock_counter == 1 || m_rx_clock_counter == 15) + m_rx_clock_counter = 0; - case CLKM_EXTERNAL_SYNC: - if((!ext_clock_value) && (clock_state & CLK_TX)) - tx_dropped_edge(); + if(m_rx_state == ST_IDLE && !(m_smr & SMR_CA) && (m_scr & SCR_RE) && m_rx_value && !state) + rx_start(); - else if(ext_clock_value && (clock_state & CLK_RX)) - rx_raised_edge(); - break; - } - } - } + m_rx_value = state; } -uint64_t h8_sci_device::internal_update(uint64_t current_time) +void h8_sci_device::do_clk_w(int state) { - uint64_t event = 0; - switch(clock_mode) { - case CLKM_INTERNAL_SYNC_OUT: - if(clock_state || !clock_value) { - uint64_t fp = divider*2; - if(current_time >= clock_base) { - uint64_t delta = current_time - clock_base; - if(delta >= fp) { - delta -= fp; - clock_base += fp; - } - assert(delta < fp); - - bool new_clock = delta >= divider; - if(new_clock != clock_value) { - cpu->synchronize(); - if((!new_clock) && (clock_state & CLK_TX)) - tx_dropped_edge(); - - else if(new_clock && (clock_state & CLK_RX)) - rx_raised_edge(); - - clock_value = new_clock; - if(clock_state || clock_value) - clk_cb(clock_value); - } - } - event = clock_base + (clock_value ? fp : divider); - } - break; + if(m_ext_clock_value == state) + return; - case CLKM_INTERNAL_ASYNC: - case CLKM_INTERNAL_ASYNC_OUT: - if(clock_state || !clock_value) { - uint64_t fp = divider*16; - if(current_time >= clock_base) { - uint64_t delta = current_time - clock_base; - if(delta >= fp) { - delta -= fp; - clock_base += fp; - } - assert(delta < fp); - bool new_clock = delta >= divider*8; - if(new_clock != clock_value) { - cpu->synchronize(); - if((!new_clock) && (clock_state & CLK_TX)) - tx_dropped_edge(); - - else if(new_clock && (clock_state & CLK_RX)) - rx_raised_edge(); - - clock_value = new_clock; - if(clock_mode == CLKM_INTERNAL_ASYNC_OUT && (clock_state || !clock_value)) - clk_cb(clock_value); - } - } + m_ext_clock_value = state; + if(!m_clock_state || m_cpu->standby()) + return; - event = clock_base + (clock_value ? fp : divider*8); - } - break; + if(m_clock_mode == EXTERNAL_ASYNC) { + if(m_clock_state & CLK_TX) + tx_async_tick(); + if(m_clock_state & CLK_RX) + rx_async_tick(); + } else if(m_clock_mode == EXTERNAL_SYNC) { + if(m_clock_state & CLK_TX) + tx_sync_tick(); + if(m_clock_state & CLK_RX) + rx_sync_tick(); + } +} - case CLKM_EXTERNAL_RATE_SYNC: - if(clock_state || !clock_value) { - uint64_t ctime = uint64_t(current_time*internal_to_external_ratio*2); - if(ctime >= clock_base) { - uint64_t delta = ctime - clock_base; - clock_base += delta & ~1; - delta &= 1; - bool new_clock = delta >= 1; - if(new_clock != clock_value) { - cpu->synchronize(); - if((!new_clock) && (clock_state & CLK_TX)) - tx_dropped_edge(); - - else if(new_clock && (clock_state & CLK_RX)) - rx_raised_edge(); - - clock_value = new_clock; - } - } +u64 h8_sci_device::internal_update(u64 current_time) +{ + if(!m_clock_event || current_time < m_clock_event) + return m_clock_event; - event = uint64_t((clock_base + (clock_value ? 2 : 1))*external_to_internal_ratio)+1; - } - break; + if(m_clock_mode == INTERNAL_ASYNC || m_clock_mode == INTERNAL_ASYNC_OUT || m_clock_mode == EXTERNAL_RATE_ASYNC) { + if(m_clock_state & CLK_TX) + tx_async_tick(); + if(m_clock_state & CLK_RX) + rx_async_tick(); + } else if(m_clock_mode == INTERNAL_SYNC_OUT || m_clock_mode == EXTERNAL_RATE_SYNC) { + if(m_clock_state & CLK_TX) + tx_sync_tick(); + if(m_clock_state & CLK_RX) + rx_sync_tick(); + } - case CLKM_EXTERNAL_RATE_ASYNC: - if(clock_state || !clock_value) { - uint64_t ctime = uint64_t(current_time*internal_to_external_ratio); - if(ctime >= clock_base) { - uint64_t delta = ctime - clock_base; - clock_base += delta & ~15; - delta &= 15; - bool new_clock = delta >= 8; - if(new_clock != clock_value) { - cpu->synchronize(); - if((!new_clock) && (clock_state & CLK_TX)) - tx_dropped_edge(); - - else if(new_clock && (clock_state & CLK_RX)) - rx_raised_edge(); - - clock_value = new_clock; - } - } + if(m_clock_state) { + if(m_clock_step) + m_clock_event += m_clock_step; + else if(m_clock_mode == EXTERNAL_RATE_ASYNC || m_clock_mode == EXTERNAL_RATE_SYNC) + m_clock_event = u64(u64(m_clock_event * m_internal_to_external_ratio + 1) * m_external_to_internal_ratio + 1); + else + m_clock_event = 0; - event = uint64_t((clock_base + (clock_value ? 16 : 8))*external_to_internal_ratio)+1; + if(m_clock_event) { + if(s64 ticks = m_clock_event - m_cpu->now_as_cycles(); ticks >= 0LL) + m_sync_timer->adjust(attotime::from_ticks(ticks, m_cpu->system_clock())); + m_cpu->internal_update(); } - break; - case CLKM_EXTERNAL_ASYNC: - case CLKM_EXTERNAL_SYNC: - break;; - } - if(event) { - attotime ctime = machine().time(); - attotime sync_time = attotime::from_ticks(event-10, cpu->clock()); - if(cur_sync_time != sync_time && sync_time > ctime) { - sync_timer->adjust(sync_time - ctime); - cur_sync_time = sync_time; - } + } else if(!m_clock_state) { + m_clock_event = 0; + if(m_clock_mode == INTERNAL_ASYNC_OUT || m_clock_mode == INTERNAL_SYNC_OUT) + m_cpu->do_sci_clk(m_id, 1); } - return event; + return m_clock_event; +} + +void h8_sci_device::notify_standby(int state) +{ + if(!state && m_clock_event) + m_clock_event += m_cpu->total_cycles() - m_cpu->standby_time(); } void h8_sci_device::clock_start(int mode) { // Happens when back-to-back - if(clock_state & mode) + if(m_clock_state & mode) return; - if(!clock_state) { - cpu->synchronize(); - clock_state = mode; - switch(clock_mode) { - case CLKM_INTERNAL_ASYNC: - case CLKM_INTERNAL_ASYNC_OUT: - case CLKM_INTERNAL_SYNC_OUT: - if(V>=2) logerror("Starting internal clock\n"); - clock_base = cpu->total_cycles(); - cpu->internal_update(); - break; + if(mode == CLK_TX) + m_tx_clock_counter = 15; + else + m_rx_clock_counter = 15; - case CLKM_EXTERNAL_RATE_ASYNC: - if(V>=2) logerror("Simulating external clock async\n"); - clock_base = uint64_t(cpu->total_cycles()*internal_to_external_ratio); - cpu->internal_update(); - break; + m_clock_state |= mode; + if(m_clock_state != mode) + return; - case CLKM_EXTERNAL_RATE_SYNC: - if(V>=2) logerror("Simulating external clock sync\n"); - clock_base = uint64_t(cpu->total_cycles()*2*internal_to_external_ratio); - cpu->internal_update(); - break; + m_clock_step = 0; + + switch(m_clock_mode) { + case INTERNAL_ASYNC: + case INTERNAL_ASYNC_OUT: + case INTERNAL_SYNC_OUT: { + LOGMASKED(LOG_CLOCK, "Starting internal clock\n"); + m_clock_step = m_divider; + u64 now = mode == CLK_TX ? m_cpu->total_cycles() : m_cpu->now_as_cycles(); + m_clock_event = (now / m_clock_step + 1) * m_clock_step; + m_sync_timer->adjust(attotime::from_ticks(m_clock_event - now, m_cpu->system_clock())); + m_cpu->internal_update(); + break; + } - case CLKM_EXTERNAL_ASYNC: - if(V>=2) logerror("Waiting for external clock async\n"); - ext_clock_counter = 15; - break; + case EXTERNAL_RATE_ASYNC: + case EXTERNAL_RATE_SYNC: { + LOGMASKED(LOG_CLOCK, "Simulating external clock\n", m_clock_mode == EXTERNAL_RATE_ASYNC ? "async" : "sync"); + u64 now = mode == CLK_TX ? m_cpu->total_cycles() : m_cpu->now_as_cycles(); + m_clock_event = u64(u64(now * m_internal_to_external_ratio + 1) * m_external_to_internal_ratio + 1); + m_sync_timer->adjust(attotime::from_ticks(m_clock_event - now, m_cpu->system_clock())); + m_cpu->internal_update(); + break; + } - case CLKM_EXTERNAL_SYNC: - if(V>=2) logerror("Waiting for external clock sync\n"); - break; - } - } else - clock_state |= mode; + case EXTERNAL_ASYNC: + case EXTERNAL_SYNC: + LOGMASKED(LOG_CLOCK, "Waiting for external clock\n"); + break; + } } void h8_sci_device::clock_stop(int mode) { - clock_state &= ~mode; - cpu->internal_update(); + m_clock_state &= ~mode; + if(!m_clock_state) { + m_clock_event = 0; + m_clock_step = 0; + LOGMASKED(LOG_CLOCK, "Stopping clocks\n"); + } + m_cpu->internal_update(); } void h8_sci_device::tx_start() { - ssr |= SSR_TDRE; - tsr = tdr; - tx_parity = smr & SMR_OE ? 0 : 1; - if(V>=1) logerror("start transmit %02x '%c'\n", tsr, tsr >= 32 && tsr < 127 ? tsr : '.'); - if(scr & SCR_TIE) - intc->internal_interrupt(txi_int); - if(smr & SMR_CA) { - tx_state = ST_BIT; - tx_bit = 8; + m_ssr |= SSR_TDRE; + m_tsr = m_tdr; + m_tx_parity = m_smr & SMR_OE ? 0 : 1; + LOGMASKED(LOG_DATA, "start transmit %02x '%c'\n", m_tsr, m_tsr >= 32 && m_tsr < 127 ? m_tsr : '.'); + if(m_scr & SCR_TIE) + m_intc->internal_interrupt(m_txi_int); + if(m_smr & SMR_CA) { + m_tx_state = ST_BIT; + m_tx_bit = 8; } else { - tx_state = ST_START; - tx_bit = 1; + m_tx_state = ST_START; + m_tx_bit = 1; } clock_start(CLK_TX); - if(rx_state == ST_IDLE && !has_recv_error() && is_sync_start()) + if(m_rx_state == ST_IDLE && (m_smr & SMR_CA) && (m_scr & SCR_TE) && (m_scr & SCR_RE) && !has_recv_error()) rx_start(); } -void h8_sci_device::tx_dropped_edge() +void h8_sci_device::tx_async_tick() +{ + m_tx_clock_counter = (m_tx_clock_counter + 1) & 15; + LOGMASKED(LOG_TICK, "tx_async_tick %x\n", m_tx_clock_counter); + if(m_tx_clock_counter == 0) { + tx_async_step(); + + if(m_clock_mode == INTERNAL_ASYNC_OUT) + m_cpu->do_sci_clk(m_id, 0); + + } else if(m_tx_clock_counter == 8 && m_clock_mode == INTERNAL_ASYNC_OUT) + m_cpu->do_sci_clk(m_id, 1); +} + +void h8_sci_device::tx_async_step() { - if(V>=2) logerror("tx_dropped_edge state=%s bit=%d\n", state_names[tx_state], tx_bit); - switch(tx_state) { + LOGMASKED(LOG_STATE, "tx_async_step state=%s bit=%d\n", state_names[m_tx_state], m_tx_bit); + switch(m_tx_state) { case ST_START: - tx_cb(false); - assert(tx_bit == 1); - tx_state = ST_BIT; - tx_bit = smr & SMR_CHR ? 7 : 8; + m_cpu->do_sci_tx(m_id, false); + assert(m_tx_bit == 1); + m_tx_state = ST_BIT; + m_tx_bit = m_smr & SMR_CHR ? 7 : 8; break; case ST_BIT: - tx_parity ^= (tsr & 1); - tx_cb(tsr & 1); - tsr >>= 1; - tx_bit--; - if(!tx_bit) { - if(smr & SMR_CA) { - if(!(ssr & SSR_TDRE)) + m_tx_parity ^= (m_tsr & 1); + m_cpu->do_sci_tx(m_id, m_tsr & 1); + m_tsr >>= 1; + m_tx_bit--; + if(!m_tx_bit) { + if(m_smr & SMR_CA) { + if(!(m_ssr & SSR_TDRE)) tx_start(); else { - tx_state = ST_LAST_TICK; - tx_bit = 0; + m_tx_state = ST_LAST_TICK; + m_tx_bit = 0; } - } else if(smr & SMR_PE) { - tx_state = ST_PARITY; - tx_bit = 1; + } else if(m_smr & SMR_PE) { + m_tx_state = ST_PARITY; + m_tx_bit = 1; } else { - tx_state = ST_STOP; - tx_bit = smr & SMR_STOP ? 2 : 1; + m_tx_state = ST_STOP; + m_tx_bit = m_smr & SMR_STOP ? 2 : 1; } } break; case ST_PARITY: - tx_cb(tx_parity); - assert(tx_bit == 1); - tx_state = ST_STOP; - tx_bit = smr & SMR_STOP ? 2 : 1; + m_cpu->do_sci_tx(m_id, m_tx_parity); + assert(m_tx_bit == 1); + m_tx_state = ST_STOP; + m_tx_bit = m_smr & SMR_STOP ? 2 : 1; break; case ST_STOP: - tx_cb(true); - tx_bit--; - if(!tx_bit) { - if(!(ssr & SSR_TDRE)) - tx_start(); + m_cpu->do_sci_tx(m_id, true); + m_tx_bit--; + if(!m_tx_bit) { + if(!(m_ssr & SSR_TDRE)) + tx_start(); else { - tx_state = ST_LAST_TICK; - tx_bit = 0; + m_tx_state = ST_LAST_TICK; + m_tx_bit = 0; } } break; case ST_LAST_TICK: - tx_state = ST_IDLE; - tx_bit = 0; + m_tx_state = ST_IDLE; + m_tx_bit = 0; clock_stop(CLK_TX); - tx_cb(1); - ssr |= SSR_TEND; - if(scr & SCR_TEIE) - intc->internal_interrupt(tei_int); + m_cpu->do_sci_tx(m_id, 1); + if(m_scr & SCR_TEIE) + m_intc->internal_interrupt(m_tei_int); + + // if there's more to send, start the transmitter + if((m_scr & SCR_TE) && !(m_ssr & SSR_TDRE)) + tx_start(); + else + m_ssr |= SSR_TEND; break; default: abort(); } - if(V>=2) logerror(" -> state=%s bit=%d\n", state_names[tx_state], tx_bit); + LOGMASKED(LOG_STATE, " -> state=%s bit=%d\n", state_names[m_tx_state], m_tx_bit); +} + +void h8_sci_device::tx_sync_tick() +{ + m_tx_clock_counter = (m_tx_clock_counter + 1) & 1; + LOGMASKED(LOG_TICK, "tx_sync_tick %x\n", m_tx_clock_counter); + if(m_tx_clock_counter == 0) { + tx_sync_step(); + + if(m_clock_mode == INTERNAL_SYNC_OUT && m_tx_state != ST_IDLE) + m_cpu->do_sci_clk(m_id, 0); + + } else if(m_tx_clock_counter == 1 && m_clock_mode == INTERNAL_SYNC_OUT) + m_cpu->do_sci_clk(m_id, 1); +} + +void h8_sci_device::tx_sync_step() +{ + LOGMASKED(LOG_STATE, "tx_sync_step bit=%d\n", m_tx_bit); + if(!m_tx_bit) { + m_tx_state = ST_IDLE; + clock_stop(CLK_TX); + m_cpu->do_sci_tx(m_id, 1); + if(m_scr & SCR_TEIE) + m_intc->internal_interrupt(m_tei_int); + + // if there's more to send, start the transmitter + if((m_scr & SCR_TE) && !(m_ssr & SSR_TDRE)) + tx_start(); + else + m_ssr |= SSR_TEND; + } else { + m_cpu->do_sci_tx(m_id, m_tsr & 1); + m_tsr >>= 1; + m_tx_bit--; + } } void h8_sci_device::rx_start() { - ssr |= SSR_TDRE; - rx_parity = smr & SMR_OE ? 0 : 1; - rsr = 0x00; - if(V>=2) logerror("start receive\n"); - if(smr & SMR_CA) { - rx_state = ST_BIT; - rx_bit = 8; - clock_start(CLK_RX); + m_rx_parity = m_smr & SMR_OE ? 0 : 1; + m_rsr = 0x00; + LOGMASKED(LOG_STATE, "start receive\n"); + if(m_smr & SMR_CA) { + m_rx_state = ST_BIT; + m_rx_bit = 8; } else { - rx_state = ST_START; - rx_bit = 1; - if(!rx_value) - clock_start(CLK_RX); + m_rx_state = ST_START; + m_rx_bit = 1; } + clock_start(CLK_RX); } void h8_sci_device::rx_done() { - if(!(ssr & SSR_FER)) { - if((smr & SMR_PE) && rx_parity) { - ssr |= SSR_PER; - if(V>=1) logerror("Receive parity error\n"); - } else if(ssr & SSR_RDRF) { - ssr |= SSR_ORER; - if(V>=1) logerror("Receive overrun\n"); + if(!(m_ssr & SSR_FER)) { + if((m_smr & SMR_PE) && m_rx_parity) { + m_ssr |= SSR_PER; + LOGMASKED(LOG_DATA, "Receive parity error\n"); + } else if(m_ssr & SSR_RDRF) { + m_ssr |= SSR_ORER; + LOGMASKED(LOG_DATA, "Receive overrun\n"); } else { - ssr |= SSR_RDRF; - if(V>=1) logerror("Received %02x '%c'\n", rsr, rsr >= 32 && rsr < 127 ? rsr : '.'); - rdr = rsr; + m_ssr |= SSR_RDRF; + LOGMASKED(LOG_DATA, "Received %02x '%c'\n", m_rsr, m_rsr >= 32 && m_rsr < 127 ? m_rsr : '.'); + m_rdr = m_rsr; } } - if(scr & SCR_RIE) { + if(m_scr & SCR_RIE) { if(has_recv_error()) - intc->internal_interrupt(eri_int); + m_intc->internal_interrupt(m_eri_int); else - intc->internal_interrupt(rxi_int); - } - if((scr & SCR_RE) && !has_recv_error() && !is_sync_start()) - rx_start(); - else { - clock_stop(CLK_RX); - rx_state = ST_IDLE; + m_intc->internal_interrupt(m_rxi_int); } + + m_rx_state = ST_IDLE; + clock_stop(CLK_RX); + + sync_rx_start(); } -void h8_sci_device::rx_raised_edge() +void h8_sci_device::rx_async_tick() { - if(V>=2) logerror("rx_raised_edge state=%s bit=%d\n", state_names[rx_state], rx_bit); - switch(rx_state) { + m_rx_clock_counter = (m_rx_clock_counter + 1) & 15; + LOGMASKED(LOG_TICK, "rx_async_tick %x\n", m_rx_clock_counter); + if(m_rx_clock_counter == 8) + rx_async_step(); +} + +void h8_sci_device::rx_async_step() +{ + LOGMASKED(LOG_STATE, "rx_async_step state=%s bit=%d\n", state_names[m_rx_state], m_rx_bit); + switch(m_rx_state) { case ST_START: - if(rx_value) { + if(m_rx_value) { + m_rx_state = ST_IDLE; clock_stop(CLK_RX); break; } - rx_state = ST_BIT; - rx_bit = smr & SMR_CHR ? 7 : 8; + m_rx_state = ST_BIT; + m_rx_bit = m_smr & SMR_CHR ? 7 : 8; break; case ST_BIT: - rx_parity ^= rx_value; - rsr >>= 1; - if(rx_value) { - rx_parity = !rx_parity; - rsr |= (smr & (SMR_CA|SMR_CHR)) == SMR_CHR ? 0x40 : 0x80; + m_rsr >>= 1; + if(m_rx_value) { + m_rx_parity = !m_rx_parity; + m_rsr |= (m_smr & (SMR_CA|SMR_CHR)) == SMR_CHR ? 0x40 : 0x80; } - rx_bit--; - if(!rx_bit) { - if(smr & SMR_CA) - rx_done(); - else if(smr & SMR_PE) { - rx_state = ST_PARITY; - rx_bit = 1; + m_rx_bit--; + if(!m_rx_bit) { + if(m_smr & SMR_PE) { + m_rx_state = ST_PARITY; + m_rx_bit = 1; } else { - rx_state = ST_STOP; - rx_bit = 1; // Always 1 on rx + m_rx_state = ST_STOP; + m_rx_bit = 1; // Always 1 on rx } } break; case ST_PARITY: - rx_parity ^= rx_value; - assert(rx_bit == 1); - rx_state = ST_STOP; - rx_bit = 1; + m_rx_parity ^= m_rx_value; + assert(m_rx_bit == 1); + m_rx_state = ST_STOP; + m_rx_bit = 1; break; case ST_STOP: - assert(rx_bit == 1); - if(!rx_value) - ssr |= SSR_FER; - else if((smr & SMR_PE) && rx_parity) - ssr |= SSR_PER; + assert(m_rx_bit == 1); + if(!m_rx_value) + m_ssr |= SSR_FER; + else if((m_smr & SMR_PE) && m_rx_parity) + m_ssr |= SSR_PER; rx_done(); break; default: abort(); } - if(V>=2) logerror(" -> state=%s, bit=%d\n", state_names[rx_state], rx_bit); + LOGMASKED(LOG_STATE, " -> state=%s, bit=%d\n", state_names[m_rx_state], m_rx_bit); +} + +void h8_sci_device::rx_sync_tick() +{ + m_rx_clock_counter = (m_rx_clock_counter + 1) & 1; + LOGMASKED(LOG_TICK, "rx_sync_tick %x\n", m_rx_clock_counter); + + if(m_rx_clock_counter == 0 && m_clock_mode == INTERNAL_SYNC_OUT) + m_cpu->do_sci_clk(m_id, 0); + + else if(m_rx_clock_counter == 1) { + if(m_clock_mode == INTERNAL_SYNC_OUT) + m_cpu->do_sci_clk(m_id, 1); + + rx_sync_step(); + } +} + +void h8_sci_device::rx_sync_step() +{ + LOGMASKED(LOG_STATE, "rx_sync_step bit=%d\n", m_rx_value); + m_rsr >>= 1; + if(m_rx_value) + m_rsr |= 0x80; + m_rx_bit--; + + if(!m_rx_bit) + rx_done(); } |