summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/machine/r65c52.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/machine/r65c52.cpp')
-rw-r--r--src/devices/machine/r65c52.cpp988
1 files changed, 988 insertions, 0 deletions
diff --git a/src/devices/machine/r65c52.cpp b/src/devices/machine/r65c52.cpp
new file mode 100644
index 00000000000..91e1f573f54
--- /dev/null
+++ b/src/devices/machine/r65c52.cpp
@@ -0,0 +1,988 @@
+// license:BSD-3-Clause
+// copyright-holders:jwallace
+/**********************************************************************
+
+ Rockwell 65C52 Dual Asynchronous Communication Interface Adapter
+
+ A slightly tweaked combination of two 6551 ACIAs on a single chip
+
+**********************************************************************/
+
+#include "emu.h"
+#include "r65c52.h"
+
+#define VERBOSE 0
+#include "logmacro.h"
+
+DEFINE_DEVICE_TYPE(R65C52, r65c52_device, "r65c52", "Rockwell 65C52 DACIA")
+
+r65c52_device::r65c52_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
+ device_t(mconfig, R65C52, tag, owner, clock),
+ m_internal_clock1(*this, "clock1"),
+ m_internal_clock2(*this, "clock2"),
+ m_irq_handler(*this),
+ m_txd_handler(*this),
+ m_rxc_handler(*this),
+ m_rts_handler(*this),
+ m_dtr_handler(*this),
+ m_aux_ctrl{0, 0}, m_control{0, 0},
+ m_compare{0, 0},
+ m_format{0, 0}, m_status{0, 0},
+ m_tdr{0, 0}, m_tdre{true, true},
+ m_rdr{0, 0}, m_rdrf{false, false},
+ m_ier{0, 0}, m_isr{0, 0},
+ m_irq{0, 0},
+ m_overrun{false, false},
+ m_parity_err{false, false},
+ m_parity_err_mode{0, 0},
+ m_txd{0, 0},
+ m_rxc(0),
+ m_rts{0, 0},
+ m_dtr{0, 0},
+ m_xtal(clock),
+ m_divide{0, 0},
+ m_cts{0, 0},
+ m_dsr{0, 0},
+ m_dcd{0, 0},
+ m_rxd{0, 0}, m_wordlength{0, 0}, m_stoplength{0, 0}, m_brk{0, 0}, m_echo_mode{0, 0}, m_parity{0, 0},
+ m_rx_state{STATE_START, STATE_START},
+ m_rx_clock{0, 0}, m_rx_bits{0, 0}, m_rx_shift{0, 0}, m_rx_parity{0, 0},
+ m_rx_counter{0, 0},
+ m_tx_state{STATE_START, STATE_START},
+ m_tx_output{OUTPUT_MARK, OUTPUT_MARK},
+ m_tx_clock{0, 0}, m_tx_bits{0, 0}, m_tx_shift{0, 0}, m_tx_parity{0, 0},
+ m_tx_counter{0, 0}
+{
+}
+
+const int r65c52_device::internal_divider[16] =
+{
+ 4608,
+ 2096,
+ 1713,
+ 1536,
+ 768,
+ 384,
+ 192,
+ 128,
+ 96,
+ 64,
+ 48,
+ 32,
+ 24,
+ 12,
+ 6,
+ 1
+};
+
+void r65c52_device::device_add_mconfig(machine_config &config)
+{
+ CLOCK(config, m_internal_clock1, 0);
+ CLOCK(config, m_internal_clock2, 0);
+ m_internal_clock1->signal_handler().set(FUNC(r65c52_device::internal_clock1));
+ m_internal_clock2->signal_handler().set(FUNC(r65c52_device::internal_clock2));
+}
+
+void r65c52_device::device_start()
+{
+ // state saving
+ save_item(NAME(m_aux_ctrl));
+ save_item(NAME(m_compare));
+ save_item(NAME(m_control));
+ save_item(NAME(m_format));
+ save_item(NAME(m_status));
+ save_item(NAME(m_tdr));
+ save_item(NAME(m_tdre));
+ save_item(NAME(m_rdr));
+ save_item(NAME(m_rdrf));
+
+ save_item(NAME(m_ier));
+ save_item(NAME(m_isr));
+ save_item(NAME(m_irq));
+
+ save_item(NAME(m_overrun));
+ save_item(NAME(m_parity_err));
+ save_item(NAME(m_parity_err_mode));
+
+ save_item(NAME(m_txd));
+ save_item(NAME(m_rxc));
+ save_item(NAME(m_rts));
+ save_item(NAME(m_dtr));
+
+ save_item(NAME(m_xtal));
+ save_item(NAME(m_divide));
+ save_item(NAME(m_cts));
+ save_item(NAME(m_dsr));
+ save_item(NAME(m_dcd));
+ save_item(NAME(m_rxd));
+
+ save_item(NAME(m_wordlength));
+ save_item(NAME(m_stoplength));
+ save_item(NAME(m_brk));
+ save_item(NAME(m_echo_mode));
+ save_item(NAME(m_parity));
+
+ save_item(NAME(m_rx_state));
+ save_item(NAME(m_rx_clock));
+ save_item(NAME(m_rx_bits));
+ save_item(NAME(m_rx_shift));
+ save_item(NAME(m_rx_parity));
+ save_item(NAME(m_rx_counter));
+
+ save_item(NAME(m_tx_state));
+ save_item(NAME(m_tx_output));
+ save_item(NAME(m_tx_clock));
+ save_item(NAME(m_tx_bits));
+ save_item(NAME(m_tx_shift));
+ save_item(NAME(m_tx_parity));
+
+ save_item(NAME(m_tx_counter));
+
+ m_internal_clock1->set_unscaled_clock(m_xtal);
+ m_internal_clock2->set_unscaled_clock(m_xtal);
+
+ set_xtal(m_xtal);
+}
+
+void r65c52_device::device_reset()
+{
+ for (int i = 0; i < 2; i++)
+ {
+ m_tdre[i] = true;
+ m_rdrf[i] = true;
+
+ m_status[i] = SR_FRAMING_ERROR;
+
+ m_aux_ctrl[i] = 0;
+ m_compare[i] = 0;
+ m_rdr[i] = 0x00;
+ m_isr[i] = 0x80;
+ m_ier[i] = 0x80;
+
+ m_rts[i] = 1;
+ m_dtr[i] = 1;
+
+ output_rts(i, 1);
+
+ output_dtr(i, 1);
+
+ m_rx_state[i] = STATE_START;
+ m_rx_counter[i] = 0;
+ }
+}
+
+u8 r65c52_device::stoplengthcounter(int idx)
+{
+ if (m_stoplength[idx] == 2)
+ {
+ if (m_wordlength[idx] == 5 && m_parity[idx] == PARITY_NONE)
+ {
+ return m_divide[idx] + (m_divide[idx] / 2);
+ }
+
+ if (m_wordlength[idx] < 8 || m_parity[idx] == PARITY_NONE)
+ {
+ return m_divide[idx] * 2;
+ }
+ }
+
+ return m_divide[idx];
+}
+
+void r65c52_device::output_irq(int idx, int irq)
+{
+ LOG("R65C52: %x IRQ %x \n", idx + 1, irq);
+
+ if (m_irq[idx] != irq)
+ {
+ m_irq[idx] = irq;
+
+ m_irq_handler[idx](!m_irq[idx]);
+ }
+}
+
+void r65c52_device::output_txd(int idx, int txd)
+{
+ switch (m_tx_output[idx])
+ {
+ case OUTPUT_MARK:
+ txd = 1;
+ break;
+
+ case OUTPUT_BREAK:
+ txd = 0;
+ break;
+ }
+
+ if (m_txd[idx] != txd)
+ {
+ m_txd[idx] = txd;
+ m_txd_handler[idx](m_txd[idx]);
+ }
+}
+
+void r65c52_device::output_rts(int idx, int rts)
+{
+ LOG("R65C52: %x RTS %x \n", idx + 1, rts);
+ if (m_rts[idx] != rts)
+ {
+ m_rts[idx] = rts;
+ m_rts_handler[idx](m_rts[idx]);
+ if (rts)
+ {
+ m_status[idx] |= SR_RTS;
+ }
+ else
+ {
+ m_status[idx] &= ~SR_RTS;
+ }
+ }
+}
+
+void r65c52_device::output_dtr(int idx, int dtr)
+{
+ LOG("R65C52: %x DTR %x \n", idx + 1, dtr);
+ if (m_dtr[idx] != dtr)
+ {
+ m_dtr[idx] = dtr;
+ m_dtr_handler[idx](m_dtr[idx]);
+ if (dtr)
+ {
+ m_status[idx] |= SR_DTR;
+ }
+ else
+ {
+ m_status[idx] &= ~SR_DTR;
+ }
+ }
+}
+
+void r65c52_device::update_irq(int idx)
+{
+ LOG("R65C52: %x IER %x ISR %x\n", idx + 1, m_ier[idx], m_isr[idx]);
+ output_irq(idx, (m_ier[idx] & m_isr[idx] & 0x7f) ? 1 : 0);
+}
+
+void r65c52_device::update_divider(int idx)
+{
+ // bits 0-3
+ double scale = internal_divider[(m_control[idx] >> 0) & 0xf];
+
+ if (m_xtal != 0)
+ {
+ m_divide[idx] = 16;
+ if (!m_dtr[idx] || m_rx_state[idx] != STATE_START)
+ {
+ scale = (double)1 / scale;
+ }
+ else
+ {
+ scale = 0;
+ }
+
+ LOG("R65C52: %x CLOCK %d SCALE %f \n", idx + 1, m_xtal * scale, scale);
+ }
+ else
+ {
+ m_divide[idx] = scale * 16;
+ scale = 0;
+ }
+
+ if (idx == 0)
+ {
+ m_internal_clock1->set_clock_scale(scale);
+ }
+ else
+ {
+ m_internal_clock2->set_clock_scale(scale);
+ }
+}
+
+u8 r65c52_device::read_rdr(int idx)
+{
+ if (!machine().side_effects_disabled())
+ {
+ m_status[idx] &= ~(SR_BRK | SR_FRAMING_ERROR);
+ m_isr[idx] &= ~(IRQ_PAR | IRQ_FOB | IRQ_RDRF);
+ m_rdrf[idx] = false;
+ m_parity_err[idx] = false;
+ m_overrun[idx] = false;
+ LOG("R65C52: %x RDR %x \n", idx + 1, m_rdr[idx]);
+ update_irq(idx);
+ }
+ return m_rdr[idx];
+}
+
+u8 r65c52_device::read_status(int idx)
+{
+ if (!machine().side_effects_disabled())
+ {
+ LOG("R65C52: %x STATUS %x \n", idx + 1, m_status[idx]);
+ m_dtr[idx] = false;
+ m_rts[idx] = false;
+ }
+ return m_status[idx];
+}
+
+void r65c52_device::write_ier(int idx, u8 data)
+{
+ if (data & 0x80)
+ {
+ m_ier[idx] |= (data & 0x7f);
+ }
+ else
+ {
+ m_ier[idx] &= ~(data & 0x7f);
+ }
+
+ LOG("R65C52: %x IER %x \n", idx + 1, m_ier[idx]);
+ update_irq(idx);
+}
+
+void r65c52_device::write_tdr(int idx, u8 data)
+{
+ m_tdr[idx] = data;
+ m_tdre[idx] = false;
+ m_isr[idx] &= ~IRQ_TDRE;
+
+ LOG("R65C52: %x TDR %x \n", idx + 1, m_tdr[idx]);
+ update_irq(idx);
+}
+
+void r65c52_device::write_control(int idx, u8 data)
+{
+ m_control[idx] = data;
+
+ // bits 0-3
+ update_divider(idx);
+
+ // bit 4
+ m_echo_mode[idx] = BIT(m_control[idx], 4);
+
+ // bit 5
+ m_stoplength[idx] = 1 + BIT(m_control[idx], 5);
+
+ LOG("R65C52: %x CTRL%X ECHO %x STOP%x\n", idx + 1, m_control[idx], m_echo_mode[idx], m_stoplength[idx]);
+}
+
+void r65c52_device::write_format(int idx, u8 data)
+{
+ m_format[idx] = data;
+
+ // bit 0
+ output_rts(idx, BIT(m_format[idx], 0));
+
+ // bit 1
+ output_dtr(idx, BIT(m_format[idx], 1));
+
+ // bit 2
+ if (!BIT(m_format[idx], 2))
+ {
+ m_parity[idx] = PARITY_NONE;
+ }
+ else
+ {
+ // bits 3-4
+ m_parity[idx] = (m_format[idx] >> 3) & 3;
+ }
+
+ // bits 5-6
+ m_wordlength[idx] = 5 + ((m_format[idx] >> 5) & 3);
+
+ LOG("R65C52: %x FMT %x RTS %x DTR %x PARITY %x WORDLENGTH %x \n", idx + 1, m_format[idx], (m_format[idx] >> 0) & 1, (m_format[idx] >> 1) & 1, m_parity[idx], m_wordlength[idx]);
+
+ update_divider(idx);
+}
+
+void r65c52_device::write_aux_ctrl(int idx, u8 data)
+{
+ m_aux_ctrl[idx] = data;
+
+ // bit 0
+ m_parity_err_mode[idx] = BIT(m_format[idx], 0);
+
+ // bit 1
+ m_brk[idx] = BIT(m_format[idx], 1);
+
+ LOG("R65C52: %x AUX CTRL %x \n", idx + 1, m_aux_ctrl[idx]);
+}
+
+void r65c52_device::write_compare(int idx, u8 data)
+{
+ LOG("R65C52: %x COMPARE %x \n", idx + 1, data);
+ m_compare[idx] = data;
+}
+
+u8 r65c52_device::read_isr(int idx)
+{
+ u8 isr = m_isr[idx];
+
+ if (m_status[idx] & SR_BRK || m_status[idx] & SR_FRAMING_ERROR || m_overrun[idx])
+ {
+ isr |= IRQ_FOB;
+ }
+
+ if ((isr & 0x7f) != 0)
+ {
+ isr |= 0x80;
+ }
+ if (!m_echo_mode[idx])
+ {
+ if (m_cts[idx])
+ {
+ isr |= 0x80;
+ }
+ }
+ else
+ {
+ isr &= ~0x80;
+ }
+
+ isr &= ~(IRQ_CTS | IRQ_DCD | IRQ_DSR | IRQ_FOB);
+
+ if (!machine().side_effects_disabled())
+ {
+ m_isr[idx] = isr;
+ LOG("R65C52: %x ISR %x \n", idx + 1, m_isr[idx]);
+ update_irq(idx);
+ }
+
+ return isr;
+}
+
+void r65c52_device::map(address_map &map)
+{
+ map(0x00, 0x00).rw(FUNC(r65c52_device::isr_0_r), FUNC(r65c52_device::ier_0_w));
+ map(0x01, 0x01).rw(FUNC(r65c52_device::status_0_r), FUNC(r65c52_device::format_ctrl_0_w));
+ map(0x02, 0x02).nopr().w(FUNC(r65c52_device::aux_compare_0_w));
+ map(0x03, 0x03).rw(FUNC(r65c52_device::rdr_0_r), FUNC(r65c52_device::tdr_0_w));
+ map(0x04, 0x04).rw(FUNC(r65c52_device::isr_1_r), FUNC(r65c52_device::ier_1_w));
+ map(0x05, 0x05).rw(FUNC(r65c52_device::status_1_r), FUNC(r65c52_device::format_ctrl_1_w));
+ map(0x06, 0x06).nopr().w(FUNC(r65c52_device::aux_compare_1_w));
+ map(0x07, 0x07).rw(FUNC(r65c52_device::rdr_1_r), FUNC(r65c52_device::tdr_1_w));
+}
+
+void r65c52_device::format_ctrl_0_w(u8 data)
+{
+ if (data & 0x80)
+ {
+ write_format(0, data);
+ }
+ else
+ {
+ write_control(0, data);
+ }
+}
+
+void r65c52_device::format_ctrl_1_w(u8 data)
+{
+ if (data & 0x80)
+ {
+ write_format(1, data);
+ }
+ else
+ {
+ write_control(1, data);
+ }
+}
+
+void r65c52_device::aux_compare_0_w(u8 data)
+{
+ if (data & 0x40)
+ {
+ write_aux_ctrl(0, data);
+ }
+ else
+ {
+ write_compare(0, data);
+ }
+}
+
+void r65c52_device::aux_compare_1_w(u8 data)
+{
+ if (data & 0x40)
+ {
+ write_aux_ctrl(1, data);
+ }
+ else
+ {
+ write_compare(1, data);
+ }
+}
+
+void r65c52_device::set_xtal(u32 xtal)
+{
+ m_xtal = xtal;
+
+ if (started())
+ {
+ m_internal_clock1->set_unscaled_clock(m_xtal);
+ m_internal_clock2->set_unscaled_clock(m_xtal);
+ update_divider(0);
+ update_divider(1);
+ }
+}
+
+void r65c52_device::internal_clock1(int state)
+{
+ transmitter_clock(0, state);
+ receiver_clock(0, state);
+}
+
+void r65c52_device::internal_clock2(int state)
+{
+ transmitter_clock(1, state);
+ receiver_clock(1, state);
+}
+
+void r65c52_device::write_rxc(int state)
+{
+ for (int i = 0; i < 2; i++)
+ {
+ receiver_clock(i, state);
+ }
+}
+
+void r65c52_device::write_txc(int state)
+{
+ for (int i = 0; i < 2; i++)
+ {
+ transmitter_clock(i, state);
+ }
+}
+
+void r65c52_device::write_rxd1(int state)
+{
+ m_rxd[0] = state;
+}
+
+void r65c52_device::write_rxd2(int state)
+{
+ m_rxd[1] = state;
+}
+
+void r65c52_device::write_cts1(int state)
+{
+ _write_cts(0, state);
+}
+
+void r65c52_device::write_cts2(int state)
+{
+ _write_cts(1, state);
+}
+
+void r65c52_device::_write_cts(int idx, int state)
+{
+ if (m_cts[idx] != state)
+ {
+ m_cts[idx] = state;
+ m_isr[idx] |= IRQ_CTS;
+
+ if (m_cts[idx])
+ {
+ m_status[idx] |= SR_CTS;
+
+ if (m_tx_output[idx] == OUTPUT_TXD)
+ {
+ m_tx_output[idx] = OUTPUT_MARK;
+ output_txd(idx, 1);
+ }
+ }
+ else
+ {
+ m_status[idx] &= ~SR_CTS;
+ }
+ update_irq(idx);
+ }
+ LOG("R65C52: %x CTS STATUS %x \n", idx + 1, m_status[idx]);
+}
+
+void r65c52_device::write_dsr1(int state)
+{
+ _write_dsr(0, state);
+}
+
+void r65c52_device::write_dsr2(int state)
+{
+ _write_dsr(1, state);
+}
+
+void r65c52_device::_write_dsr(int idx, int state)
+{
+ if (m_dsr[idx] != state)
+ {
+ m_dsr[idx] = state;
+ m_isr[idx] |= IRQ_DSR;
+
+ if (m_dsr[idx])
+ {
+ m_status[idx] |= SR_DSR;
+ }
+ else
+ {
+ m_status[idx] &= ~SR_DSR;
+ }
+ update_irq(idx);
+ }
+ LOG("R65C52: %x DSR STATUS %x \n", idx + 1, m_status[idx]);
+}
+
+void r65c52_device::write_dcd1(int state)
+{
+ _write_dcd(0, state);
+}
+
+void r65c52_device::write_dcd2(int state)
+{
+ _write_dcd(1, state);
+}
+
+void r65c52_device::_write_dcd(int idx, int state)
+{
+ if (m_dcd[idx] != state)
+ {
+ m_dcd[idx] = state;
+ m_isr[idx] |= IRQ_DCD;
+
+ if (m_dcd[idx])
+ {
+ m_status[idx] |= SR_DCD;
+ }
+ else
+ {
+ m_status[idx] &= ~SR_DCD;
+ }
+ update_irq(idx);
+ }
+ LOG("R65C52: %x DCD STATUS %x \n", idx + 1, m_status[idx]);
+}
+
+void r65c52_device::receiver_clock(int idx, int state)
+{
+ if (m_rx_clock[idx] != state)
+ {
+ m_rx_clock[idx] = state;
+
+ if (m_rx_clock[idx])
+ {
+ m_rx_counter[idx]++;
+
+ switch (m_rx_state[idx])
+ {
+ case STATE_START:
+ if (m_rx_counter[idx] == 1)
+ {
+ if (!m_rxd[idx] && !m_dtr[idx])
+ {
+ LOG("R65C52: RX%x START BIT \n", idx + 1);
+ }
+ else
+ {
+ m_rx_counter[idx] = 0;
+ }
+ }
+
+ if (m_rx_counter[idx] >= m_divide[idx] / 2)
+ {
+ if (!m_rxd[idx])
+ {
+ m_rx_state[idx] = STATE_DATA;
+ m_rx_counter[idx] = 0;
+ m_rx_shift[idx] = 0;
+ m_rx_parity[idx] = 0;
+ m_rx_bits[idx] = 0;
+ }
+ else
+ {
+ m_rx_counter[idx] = 0;
+
+ LOG("R65C52: RX%x false START BIT\n", idx + 1);
+ }
+ }
+ break;
+
+ case STATE_DATA:
+ if (m_rx_counter[idx] == m_divide[idx])
+ {
+ m_rx_counter[idx] = 0;
+
+ if (m_rx_bits[idx] < m_wordlength[idx])
+ {
+ LOG("R65C52: RX%x DATA BIT %d %d\n", idx + 1, m_rx_bits[idx], m_rxd[idx]);
+ }
+ else
+ {
+ LOG("R65C52: RX%x PARITY BIT %x\n", idx + 1, m_rxd[idx]);
+ }
+
+ if (m_rxd[idx])
+ {
+ m_rx_shift[idx] |= 1 << m_rx_bits[idx];
+ }
+
+ m_rx_bits[idx]++;
+
+ m_rx_parity[idx] ^= m_rxd[idx];
+
+ if ((m_rx_bits[idx] == m_wordlength[idx] && m_parity[idx] == PARITY_NONE) ||
+ (m_rx_bits[idx] == (m_wordlength[idx] + 1) && m_parity[idx] != PARITY_NONE))
+ {
+ m_rx_state[idx] = STATE_STOP;
+ }
+ }
+ break;
+
+ case STATE_STOP:
+ if (m_rx_counter[idx] >= stoplengthcounter(idx))
+ {
+ m_rx_counter[idx] = 0;
+
+ LOG("R65C52: RX%x STOP BIT\n", idx + 1);
+ if (!(m_rdrf[idx]))
+ {
+ if (!m_rxd[idx])
+ {
+ m_status[idx] |= SR_FRAMING_ERROR;
+ }
+
+ if ((m_parity[idx] == PARITY_ODD && !m_rx_parity[idx]) ||
+ (m_parity[idx] == PARITY_EVEN && m_rx_parity[idx]))
+ {
+ m_parity_err[idx] = true;
+ }
+ else
+ {
+ m_parity_err[idx] = false;
+ }
+
+ if (m_parity_err_mode[idx])
+ {
+ if (m_parity_err[idx])
+ {
+ m_isr[idx] |= IRQ_PAR;
+ }
+ else
+ {
+ m_isr[idx] &= ~IRQ_PAR;
+ }
+ update_irq(idx);
+ }
+ m_rdr[idx] = m_rx_shift[idx];
+
+ if (m_wordlength[idx] == 7 && m_parity[idx] != PARITY_NONE)
+ {
+ m_rdr[idx] &= 0x7f;
+ }
+
+ // In compare mode, we only flip RDRF if the data matches
+ if (m_compare[idx] == 0 || (m_compare[idx] == m_rdr[idx]))
+ {
+ m_rdrf[idx] = true;
+ m_isr[idx] |= IRQ_RDRF;
+ m_compare[idx] = 0;
+ update_irq(idx);
+ }
+ m_overrun[idx] = false;
+ }
+ else
+ {
+ m_overrun[idx] = true;
+ }
+
+ LOG("R65C52: RX%x DATA %x\n", idx + 1, m_rdr[idx]);
+
+ m_rx_state[idx] = STATE_START;
+
+ if (m_dtr[idx])
+ {
+ update_divider(idx);
+ }
+ }
+ break;
+ }
+ }
+ }
+}
+
+void r65c52_device::transmitter_clock(int idx, int state)
+{
+ if (m_tx_clock[idx] != state)
+ {
+ m_tx_clock[idx] = state;
+
+ if (!m_tx_clock[idx] && !m_dtr[idx])
+ {
+ if (m_echo_mode[idx])
+ {
+ if (!(m_overrun[idx]))
+ {
+ output_txd(idx, m_rxd[idx]);
+ }
+ else
+ {
+ output_txd(idx, 1);
+ }
+ }
+
+ if (!m_cts[idx] && m_tx_output[idx] == OUTPUT_MARK && !(m_tdre[idx]))
+ {
+ m_tx_state[idx] = STATE_START;
+ m_tx_counter[idx] = 0;
+ }
+
+ m_tx_counter[idx]++;
+
+ switch (m_tx_state[idx])
+ {
+ case STATE_START:
+ m_tx_counter[idx] = 0;
+
+ m_tx_state[idx] = STATE_DATA;
+ m_tx_shift[idx] = m_tdr[idx];
+ m_tx_bits[idx] = 0;
+ m_tx_parity[idx] = 0;
+
+ if (m_cts[idx])
+ {
+ m_tx_output[idx] = OUTPUT_MARK;
+ LOG("R65C52: TX%x CTS MARK START\n", idx + 1);
+ }
+ else if (!(m_tdre[idx]))
+ {
+ LOG("R65C52: TX%x DATA %x\n", idx + 1, m_tdr[idx]);
+
+ m_tx_output[idx] = OUTPUT_TXD;
+
+ LOG("R65C52: TX%x START BIT\n", idx + 1);
+
+ m_tdre[idx] = true;
+ m_isr[idx] |= IRQ_TDRE;
+ update_irq(idx);
+ }
+ else if (m_brk[idx])
+ {
+ m_tx_output[idx] = OUTPUT_BREAK;
+
+ LOG("R65C52: TX%x BREAK START\n", idx + 1);
+ }
+ else
+ {
+ m_tx_output[idx] = OUTPUT_MARK;
+ LOG("R65C52: TX%x MARK START\n", idx + 1);
+ }
+
+ if (m_tx_output[idx] != OUTPUT_BREAK)
+ {
+ m_tdre[idx] = true;
+ m_isr[idx] |= IRQ_TDRE;
+ update_irq(idx);
+ }
+
+ // fatalerror("setup");
+ output_txd(idx, 0);
+ break;
+
+ case STATE_DATA:
+ if (m_tx_counter[idx] == m_divide[idx])
+ {
+ m_tx_counter[idx] = 0;
+
+ if (m_tx_bits[idx] < m_wordlength[idx])
+ {
+ output_txd(idx, (m_tx_shift[idx] >> m_tx_bits[idx]) & 1);
+
+ m_tx_bits[idx]++;
+ m_tx_parity[idx] ^= m_txd[idx];
+
+ if (m_tx_output[idx] == OUTPUT_TXD)
+ {
+ LOG("R65C52: TX%x DATA BIT TXD %d %d\n", idx + 1, m_tx_bits[idx], m_txd[idx]);
+ }
+ }
+ else if (m_tx_bits[idx] == m_wordlength[idx] && m_parity[idx] != PARITY_NONE)
+ {
+ m_tx_bits[idx]++;
+
+ switch (m_parity[idx])
+ {
+ case PARITY_ODD:
+ m_tx_parity[idx] = !m_tx_parity[idx];
+ break;
+
+ case PARITY_MARK:
+ m_tx_parity[idx] = 1;
+ break;
+
+ case PARITY_SPACE:
+ m_tx_parity[idx] = 0;
+ break;
+ }
+
+ output_txd(idx, m_tx_parity[idx]);
+
+ if (m_tx_output[idx] == OUTPUT_TXD)
+ {
+ LOG("R65C52: TX%x PARITY BIT %d\n", idx + 1, m_txd);
+ }
+
+ if (!m_parity_err_mode[idx])
+ {
+ if (m_tx_parity[idx])
+ {
+ m_isr[idx] |= IRQ_PAR;
+ }
+ else
+ {
+ m_isr[idx] &= ~IRQ_PAR;
+ }
+ update_irq(idx);
+ }
+ }
+ else
+ {
+ m_tx_state[idx] = STATE_STOP;
+
+ output_txd(idx, 1);
+ // m_tdre[idx] = true;
+ // m_isr[idx] |= IRQ_TDRE;
+ // update_irq(idx);
+
+ if (m_tx_output[idx] == OUTPUT_TXD)
+ {
+ LOG("R65C52: TX%x STOP BIT\n", idx + 1);
+ }
+ }
+ }
+ break;
+
+ case STATE_STOP:
+ if (m_tx_counter[idx] >= stoplengthcounter(idx))
+ {
+ if (m_tx_output[idx] == OUTPUT_BREAK)
+ {
+ if (!m_brk[idx])
+ {
+ LOG("R65C52: TX%x BREAK END\n", idx + 1);
+
+ m_tx_counter[idx] = 0;
+ m_tx_state[idx] = STATE_STOP;
+ m_tx_output[idx] = OUTPUT_TXD;
+
+ output_txd(idx, 1);
+ }
+ else
+ {
+ m_tx_counter[idx]--;
+ }
+ }
+ else
+ {
+ m_tx_state[idx] = STATE_START;
+ m_tx_counter[idx] = 0;
+ }
+ }
+ break;
+ }
+ }
+ }
+}