diff options
Diffstat (limited to 'src/devices/cpu/f2mc16/f2mc16_uart.cpp')
-rw-r--r-- | src/devices/cpu/f2mc16/f2mc16_uart.cpp | 456 |
1 files changed, 456 insertions, 0 deletions
diff --git a/src/devices/cpu/f2mc16/f2mc16_uart.cpp b/src/devices/cpu/f2mc16/f2mc16_uart.cpp new file mode 100644 index 00000000000..80dbb75fe93 --- /dev/null +++ b/src/devices/cpu/f2mc16/f2mc16_uart.cpp @@ -0,0 +1,456 @@ +// license:BSD-3-Clause +// copyright-holders:smf +/*************************************************************************** + + Fujitsu Micro F2MC-16 series UART + +***************************************************************************/ + +#include "emu.h" +#include "f2mc16_uart.h" + +#define LOG_READ (1U << 1) +#define LOG_WRITE (1U << 2) + +//#define VERBOSE (LOG_GENERAL | LOG_READ | LOG_WRITE) +//#define LOG_OUTPUT_FUNC osd_printf_info +#include "logmacro.h" + +#define LOGREAD(...) LOGMASKED(LOG_READ, __VA_ARGS__) +#define LOGWRITE(...) LOGMASKED(LOG_WRITE, __VA_ARGS__) + +namespace { + +struct SMR { enum : uint8_t +{ + SOE = 1 << 0, + SCKE = 1 << 1, + CS = 7 << 3, + CS_RESERVED = 5 << 3, + CS_INTERNAL_TIMER = 6 << 3, + CS_EXTERNAL_CLOCK = 7 << 3, + MD = 3 << 6, + MD_ASYNC_NORMAL = 0 << 6, + MD_ASYNC_MULTIPROCESSOR = 1 << 6, + MD_SYNC = 2 << 6, + MD_PROHIBITED = 3 << 6 +}; }; + +struct SCR { enum : uint8_t +{ + PEN = 1 << 7, + P = 1 << 6, + SBL = 1 << 5, + CL = 1 << 4, + AD = 1 << 3, + REC = 1 << 2, + RXE = 1 << 1, + TXE = 1 << 0 +}; }; + +struct SSR { enum : uint8_t +{ + PE = 1 << 7, + ORE = 1 << 6, + FRE = 1 << 5, + RDRF = 1 << 4, + TDRE = 1 << 3, + RIE = 1 << 1, + TIE = 1 << 0 +}; }; + +struct CDCR { enum : uint8_t +{ + DIV = 15 << 0 +}; }; + +} // anonymous namespace + +DEFINE_DEVICE_TYPE(F2MC16_UART, f2mc16_uart_device, "f2mc16_uart", "F2MC16 UART") + +f2mc16_uart_device::f2mc16_uart_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock, required_device<f2mc16_intc_device> &intc, uint8_t rx_vector, uint8_t tx_vector) : + f2mc16_uart_device(mconfig, tag, owner, clock) +{ + m_intc.set_tag(intc); + m_rx_vector = rx_vector; + m_tx_vector = tx_vector; +} + +f2mc16_uart_device::f2mc16_uart_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : + device_t(mconfig, F2MC16_UART, tag, owner, clock), + m_intc(*this, finder_base::DUMMY_TAG), + m_sck_cb(*this), + m_sck_hz_cb(*this), + m_sot_cb(*this), + m_internal_timer_changed(attotime::zero), + m_internal_timer_hz(0), + m_peripheral_clock_hz(0), + m_sck_in_hz(0), + m_sck_in(1), + m_sck_out(1), + m_sck_out_hz(0), + m_sin(1), + m_sin_prev(0), + m_sot(1), + m_hz(0), + m_clock_count(0), + m_tx_bit(-1), + m_tx_bits(0), + m_tx_shift(0), + m_rx_bit(-1), + m_rx_shift(0), + m_smr(0), + m_sidr(0), + m_sodr(0) +{ +} + +void f2mc16_uart_device::device_start() +{ + m_rx_timer = timer_alloc(FUNC(f2mc16_uart_device::rx_timer_callback), this); + m_tx_timer = timer_alloc(FUNC(f2mc16_uart_device::tx_timer_callback), this); + + save_item(NAME(m_internal_timer_changed)); + save_item(NAME(m_rx_start_time)); + save_item(NAME(m_tx_start_time)); + save_item(NAME(m_rx_ticks)); + save_item(NAME(m_tx_ticks)); + save_item(NAME(m_sck_in)); + save_item(NAME(m_sck_in_hz)); + save_item(NAME(m_sck_out)); + save_item(NAME(m_sck_out_hz)); + save_item(NAME(m_sin)); + save_item(NAME(m_sin_prev)); + save_item(NAME(m_sot)); + save_item(NAME(m_internal_timer_hz)); + save_item(NAME(m_hz)); + save_item(NAME(m_clock_count)); + save_item(NAME(m_tx_bit)); + save_item(NAME(m_tx_bits)); + save_item(NAME(m_tx_shift)); + save_item(NAME(m_rx_bit)); + save_item(NAME(m_rx_shift)); + save_item(NAME(m_smr)); + save_item(NAME(m_scr)); + save_item(NAME(m_sidr)); + save_item(NAME(m_sodr)); + save_item(NAME(m_ssr)); + save_item(NAME(m_cdcr)); + + m_sck_cb(m_sck_out); + m_sck_hz_cb(m_sck_out_hz); + m_sot_cb(m_sot); + + update_serial(); +} + +void f2mc16_uart_device::device_reset() +{ + m_smr = 0; + m_scr = SCR::REC; + m_ssr = SSR::TDRE; + m_cdcr = CDCR::DIV; + + update_serial(); +} + +void f2mc16_uart_device::device_clock_changed() +{ + m_peripheral_clock_hz = clock(); + update_serial(); +} + +void f2mc16_uart_device::internal_timer_hz(uint32_t hz) +{ + if (machine().scheduler().currently_executing()) + machine().scheduler().synchronize(timer_expired_delegate(FUNC(f2mc16_uart_device::update_internal_timer), this), hz); + else + update_internal_timer(hz); +} + +void f2mc16_uart_device::internal_timer(int state) +{ + // TODO: +} + +void f2mc16_uart_device::sck_hz(uint32_t hz) +{ + // TODO: track when hz changes. + m_sck_in_hz = hz; + + update_serial(); +} + +void f2mc16_uart_device::sck(int state) +{ + if (m_sck_in != state) + { + m_sck_in = state; + + update_serial(); + } +} + +void f2mc16_uart_device::sin(int state) +{ + if (m_sin != state) + { + m_sin = state; + + update_serial(); + } +} + +uint8_t f2mc16_uart_device::smr_r() +{ + return m_smr; +} + +void f2mc16_uart_device::smr_w(uint8_t data) +{ + m_smr = data & (SMR::MD | SMR::CS | SMR::SCKE | SMR::SOE); + + update_serial(); +} + +uint8_t f2mc16_uart_device::scr_r() +{ + return m_scr | SMR::CS; +} + +void f2mc16_uart_device::scr_w(uint8_t data) +{ + if (!(data & SCR::REC)) + { + m_ssr &= ~(SSR::PE | SSR::ORE | SSR::FRE); + data |= SCR::REC; + } + + m_scr = data & (SCR::PEN | SCR::P | SCR::SBL | SCR::CL | SCR::AD | SCR::REC | SCR::RXE | SCR::TXE); + + update_serial(); +} + +uint8_t f2mc16_uart_device::sidr_r() +{ + if (!machine().side_effects_disabled()) + { + LOGREAD("%s read data %02x\n",machine().describe_context(), tag(), m_sidr); + + if (m_ssr & SSR::RDRF) + { + m_ssr &= ~SSR::RDRF; + update_serial(); + } + } + + return m_sidr; +} + +void f2mc16_uart_device::sodr_w(uint8_t data) +{ + LOGWRITE("%s write data %02x\n", machine().describe_context(), data); + + m_sodr = data; + m_ssr &= ~SSR::TDRE; + + update_serial(); +} + +uint8_t f2mc16_uart_device::ssr_r() +{ + return m_ssr; +} + +void f2mc16_uart_device::ssr_w(uint8_t data) +{ + m_ssr = (m_ssr & ~(SSR::TIE | SSR::RIE)) | (data & (SSR::TIE | SSR::RIE)); + + update_serial(); +} + +void f2mc16_uart_device::cdcr_w(uint8_t data) +{ + m_cdcr = data & CDCR::DIV; + + update_serial(); +} + +TIMER_CALLBACK_MEMBER(f2mc16_uart_device::update_internal_timer) +{ + m_internal_timer_hz = param; + m_internal_timer_changed = machine().time(); + + update_serial(); +} + +void f2mc16_uart_device::update_serial() +{ + const int clock_counts[] = { 16, 16, 1, 0 }; + const int baud_rate_generator[][5] = + { + { 13, 4, 26, 52, 104 }, + { 0, 0, 2, 4, 8 } + }; + + uint8_t clock_count = clock_counts[(m_smr & SMR::MD) >> 6]; + uint32_t hz; + + if ((m_smr & SMR::CS) == SMR::CS_RESERVED) + hz = 0; + else if ((m_smr & SMR::CS) == SMR::CS_INTERNAL_TIMER) + hz = m_internal_timer_hz; + else if ((m_smr & SMR::CS) == SMR::CS_EXTERNAL_CLOCK) + hz = (m_smr & SMR::SCKE) ? 0 : m_sck_in_hz; + else if (baud_rate_generator[(m_smr & SMR::MD) == SMR::MD_SYNC][(m_smr & SMR::CS) >> 3]) + hz = m_peripheral_clock_hz / (0x10 - m_cdcr) / baud_rate_generator[(m_smr & SMR::MD) == SMR::MD_SYNC][(m_smr & SMR::CS) >> 3]; + else + hz = 0; + + //if (m_hz != hz || m_clock_count != clock_count) + //printf("%s baud %d\n", tag(), clock_count ? (hz / clock_count) : 0); + + m_clock_count = clock_count; + m_hz = hz; + + uint32_t sck_out_clock = (m_smr & SMR::SCKE) ? m_hz : 0; + if (m_sck_out_hz != sck_out_clock) + { + m_sck_out_hz = sck_out_clock; + m_sck_hz_cb(m_sck_out_hz); + } + + if (!m_sin && m_sin_prev && m_rx_bit < 0 && + ((m_smr & SMR::MD) == SMR::MD_ASYNC_NORMAL || (m_smr & SMR::MD) == SMR::MD_ASYNC_MULTIPROCESSOR)) + m_rx_timer->adjust(attotime::from_hz((m_hz / m_clock_count) * 2)); + + m_sin_prev = m_sin; + + if (m_tx_bit < 0 && !(m_ssr & SSR::TDRE)) + m_tx_timer->adjust(attotime::zero); + + m_intc->set_irq(m_tx_vector, (m_ssr & SSR::TIE) && (m_ssr & SSR::TDRE)); + m_intc->set_irq(m_rx_vector, (m_ssr & SSR::RIE) && (m_ssr & SSR::RDRF)); + m_intc->set_completion_request(m_rx_vector, (SSR::PE | SSR::ORE | SSR::FRE) != 0); +} + +TIMER_CALLBACK_MEMBER(f2mc16_uart_device::rx_timer_callback) +{ + int data_bits = ((m_smr & SMR::MD) != SMR::MD_ASYNC_NORMAL || (m_scr & SCR::CL)) ? 8 : 7; + int parity_bits = ((m_smr & SMR::MD) == SMR::MD_ASYNC_MULTIPROCESSOR || + ((m_smr & SMR::MD) == SMR::MD_ASYNC_NORMAL && (m_scr & SCR::P))) ? 1 : 0; + int start_stop_bits = (((m_smr & SMR::MD) == SMR::MD_ASYNC_NORMAL || + (m_smr & SMR::MD) == SMR::MD_ASYNC_MULTIPROCESSOR)) ? 2 : 0; + int length = data_bits + parity_bits + start_stop_bits; + + if (m_rx_bit < 0) + { + if (start_stop_bits && m_sin) + return; + + m_rx_bit = 0; + m_rx_shift = 0; + } + + if (m_sin) + m_rx_shift |= 1 << m_rx_bit; + m_rx_bit++; + + if (m_rx_bit >= length) + { + if ((m_smr & SMR::MD) == SMR::MD_ASYNC_NORMAL || + (m_smr & SMR::MD) == SMR::MD_ASYNC_MULTIPROCESSOR) + { + m_rx_shift >>= 1; + + if (!m_sin) + m_ssr |= SSR::FRE; + + if ((m_smr & SMR::MD) == SMR::MD_ASYNC_NORMAL && (m_scr & SCR::PEN)) + { + int parity = (m_scr & SCR::P) ? 1 : 0; + for (int i = 0; i < data_bits; i++) + parity ^= BIT(m_rx_shift, i); + + if (parity) + m_ssr |= SSR::PE; + } + + if (!(m_rx_shift & (1 << data_bits))) + m_ssr |= SSR::FRE; + } + + m_rx_bit = -1; + + if (m_ssr & SSR::RDRF) + m_ssr |= SSR::ORE; + else + { + m_ssr |= SSR::RDRF; + m_sidr = BIT(m_rx_shift, 0, data_bits); + } + + update_serial(); + return; + } + + m_rx_timer->adjust(attotime::from_hz(m_hz / m_clock_count)); +} + +TIMER_CALLBACK_MEMBER(f2mc16_uart_device::tx_timer_callback) +{ + if (m_tx_bit == m_tx_bits) + m_tx_bit = -1; + + if (m_tx_bit < 0) + { + if (!(m_ssr & SSR::TDRE)) + { + m_tx_bit = 0; + m_tx_bits = ((m_smr & SMR::MD) != SMR::MD_ASYNC_NORMAL || (m_scr & SCR::CL)) ? 8 : 7; + m_tx_shift = BIT(m_sodr, 0, m_tx_bits); + + if ((m_smr & SMR::MD) == SMR::MD_ASYNC_MULTIPROCESSOR) + { + if (m_scr & SCR::AD) + m_tx_shift |= 1 << m_tx_bits; + m_tx_bits++; + } + else if ((m_scr & SCR::PEN) && (m_smr & SMR::MD) == SMR::MD_ASYNC_NORMAL) + { + int parity = (m_scr & SCR::P) ? 1 : 0; + for (int i = 0; i < m_tx_bits; i++) + parity ^= BIT(m_tx_shift, i); + + if (parity) + m_tx_shift |= 1 << m_tx_bits; + m_tx_bits++; + } + + if ((m_smr & SMR::MD) == SMR::MD_ASYNC_NORMAL || + (m_smr & SMR::MD) == SMR::MD_ASYNC_MULTIPROCESSOR) + { + int stop_bits = (m_scr & SCR::SBL) ? 2 : 1; + for (int i = 0; i < stop_bits; i++) + { + m_tx_shift |= 1 << m_tx_bits; + m_tx_bits++; + } + + m_tx_shift <<= 1; + m_tx_bits++; + } + else if (m_rx_bit < 0 && (m_scr & SCR::RXE) && (m_smr & SMR::MD) == SMR::MD_SYNC) + m_rx_timer->adjust(attotime::from_hz((m_hz / m_clock_count) * 2)); + + m_ssr |= SSR::TDRE; + update_serial(); + } + else + return; + } + + m_sot_cb(BIT(m_tx_shift, m_tx_bit)); + m_tx_bit++; + m_tx_timer->adjust(attotime::from_hz(m_hz / m_clock_count)); +} |