diff options
Diffstat (limited to 'src/devices/machine/mc68681.cpp')
-rw-r--r-- | src/devices/machine/mc68681.cpp | 387 |
1 files changed, 242 insertions, 145 deletions
diff --git a/src/devices/machine/mc68681.cpp b/src/devices/machine/mc68681.cpp index 2787720cc64..96d37fec996 100644 --- a/src/devices/machine/mc68681.cpp +++ b/src/devices/machine/mc68681.cpp @@ -33,16 +33,14 @@ The extra baud rates are implemented by an 'X' bit for each channel. The X bit chooses between two baud rate tables, in addition to the ACR[7] bit. - The X bit is changed by additional commands that are written to CRA and - CRB. + The X bit is changed by additional commands that are written to CRA and CRB. The MISR is a read only register that takes the place of the 'BRG Test' register on the MC68681. The low power standby mode is entered and left by a command written to CRA or CRB registers. Writing the commands to either register affects the whole - DUART, not just one channel. Resetting the DUART also leaves low power - mode. + DUART, not just one channel. Resetting the DUART also leaves low power mode. */ #include "emu.h" @@ -62,7 +60,7 @@ static const char *const duart68681_reg_read_names[0x10] = static const char *const duart68681_reg_write_names[0x10] = { - "MRA", "CSRA", "CRA", "THRA", "ACR", "IMR", "CRUR", "CTLR", "MRB", "CSRB", "CRB", "THRB", "IVR", "OPCR", "Set OP Bits", "Reset OP Bits" + "MRA", "CSRA", "CRA", "THRA", "ACR", "IMR", "CTUR", "CTLR", "MRB", "CSRB", "CRB", "THRB", "IVR", "OPCR", "Set OP Bits", "Reset OP Bits" }; static const int baud_rate_ACR_0[] = { 50, 110, 134, 200, 300, 600, 1200, 1050, 2400, 4800, 7200, 9600, 38400, 0, 0, 0 }; /* xr68c681 X=0 */ @@ -70,8 +68,8 @@ static const int baud_rate_ACR_0_X_1[] = { 75, 110, 134, 150, 3600, 14400, 28800 static const int baud_rate_ACR_1[] = { 75, 110, 134, 150, 300, 600, 1200, 2000, 2400, 4800, 1800, 9600, 19200, 0, 0, 0 }; /* xr68c681 X=0 */ static const int baud_rate_ACR_1_X_1[] = { 50, 110, 134, 200, 3600, 14400, 28800, 57600, 115200, 4800, 7200, 9600, 38400, 0, 0, 0 }; -static const int baud_rate_ACR_0_340[] = { 50, 110, 134, 200, 300, 600, 1200, 1050, 2400, 4800, 7200, 9600, 38400, 76800, 0, 0 }; /* xr68c681 ACR:7=0 */ -static const int baud_rate_ACR_1_340[] = { 75, 110, 134, 150, 300, 600, 1200, 2000, 2400, 4800, 1800, 9600, 19200, 38400, 0, 0 }; /* xr68c681 ACR:7=1 */ +static const int baud_rate_ACR_0_340[] = { 50, 110, 134, 200, 300, 600, 1200, 1050, 2400, 4800, 7200, 9600, 38400, 76800, 0, 0 }; /* xr68c681 ACR:7=0 */ +static const int baud_rate_ACR_1_340[] = { 75, 110, 134, 150, 300, 600, 1200, 2000, 2400, 4800, 1800, 9600, 19200, 38400, 0, 0 }; /* xr68c681 ACR:7=1 */ #define INT_INPUT_PORT_CHANGE 0x80 #define INT_DELTA_BREAK_B 0x40 @@ -103,7 +101,7 @@ static const int baud_rate_ACR_1_340[] = { 75, 110, 134, 150, 300, 600, 1 DEFINE_DEVICE_TYPE(SCN2681, scn2681_device, "scn2681", "SCN2681 DUART") DEFINE_DEVICE_TYPE(MC68681, mc68681_device, "mc68681", "MC68681 DUART") DEFINE_DEVICE_TYPE(SC28C94, sc28c94_device, "sc28c94", "SC28C94 QUART") -DEFINE_DEVICE_TYPE(MC68340_DUART, mc68340_duart_device, "mc68340duart", "MC68340 DUART Device") +DEFINE_DEVICE_TYPE(MC68340_DUART, mc68340_duart_device, "mc68340duart", "MC68340 DUART") DEFINE_DEVICE_TYPE(XR68C681, xr68c681_device, "xr68c681", "XR68C681 DUART") DEFINE_DEVICE_TYPE(DUART_CHANNEL, duart_channel, "duart_channel", "DUART channel") @@ -112,8 +110,8 @@ DEFINE_DEVICE_TYPE(DUART_CHANNEL, duart_channel, "duart_channel", "DUART channel // LIVE DEVICE //************************************************************************** -duart_base_device::duart_base_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) - : device_t(mconfig, type, tag, owner, clock), +duart_base_device::duart_base_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) : + device_t(mconfig, type, tag, owner, clock), m_chanA(*this, CHANA_TAG), m_chanB(*this, CHANB_TAG), m_chanC(*this, CHANC_TAG), @@ -123,7 +121,7 @@ duart_base_device::duart_base_device(const machine_config &mconfig, device_type write_b_tx(*this), write_c_tx(*this), write_d_tx(*this), - read_inport(*this), + read_inport(*this, 0), write_outport(*this), ip3clk(0), ip4clk(0), @@ -140,8 +138,8 @@ scn2681_device::scn2681_device(const machine_config &mconfig, const char *tag, d } mc68681_device::mc68681_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) - : duart_base_device(mconfig, type, tag, owner, clock), - m_read_vector(false) + : duart_base_device(mconfig, type, tag, owner, clock) + , m_read_vector(false) { } @@ -173,11 +171,11 @@ mc68340_duart_device::mc68340_duart_device(const machine_config &mconfig, const } xr68c681_device::xr68c681_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : mc68681_device(mconfig, XR68C681, tag, owner, clock), - m_XTXA(false), - m_XRXA(false), - m_XTXB(false), - m_XRXB(false) + : mc68681_device(mconfig, XR68C681, tag, owner, clock) + , m_XTXA(false) + , m_XRXA(false) + , m_XTXB(false) + , m_XRXB(false) { } @@ -200,15 +198,7 @@ void duart_base_device::set_clocks(int clk3, int clk4, int clk5, int clk6) void duart_base_device::device_start() { - write_irq.resolve_safe(); - write_a_tx.resolve_safe(); - write_b_tx.resolve_safe(); - write_c_tx.resolve_safe(); - write_d_tx.resolve_safe(); - read_inport.resolve(); - write_outport.resolve_safe(); - - duart_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(duart_base_device::duart_timer_callback),this), nullptr); + duart_timer = timer_alloc(FUNC(duart_base_device::duart_timer_callback), this); save_item(NAME(ACR)); save_item(NAME(IMR)); @@ -219,6 +209,7 @@ void duart_base_device::device_start() save_item(NAME(IPCR)); save_item(NAME(IP_last_state)); save_item(NAME(half_period)); + save_item(NAME(m_irq_state)); } void mc68681_device::device_start() @@ -254,6 +245,7 @@ void duart_base_device::device_reset() // "reset clears internal registers (SRA, SRB, IMR, ISR, OPR, OPCR) puts OP0-7 in the high state, stops the counter/timer, and puts channels a/b in the inactive state" IPCR = 0; + m_irq_state = false; write_irq(CLEAR_LINE); write_outport(OPR ^ 0xff); } @@ -312,13 +304,21 @@ void duart_base_device::update_interrupts() */ if ((ISR & IMR) != 0) { - LOG("68681: Interrupt line active (IMR & ISR = %02X)\n", (ISR & IMR)); - write_irq(ASSERT_LINE); + if (!m_irq_state) + { + m_irq_state = true; + LOG("Interrupt line active (IMR & ISR = %02X)\n", (ISR & IMR)); + write_irq(ASSERT_LINE); + } } else { - LOG("68681: Interrupt line not active (IMR & ISR = %02X)\n", ISR & IMR); - write_irq(CLEAR_LINE); + if (m_irq_state) + { + m_irq_state = false; + LOG("Interrupt line not active (IMR & ISR = %02X)\n", ISR & IMR); + write_irq(CLEAR_LINE); + } } if (OPCR & 0xf0) { @@ -370,9 +370,9 @@ uint8_t mc68681_device::get_irq_vector() return IVR; } -double duart_base_device::get_ct_rate() +uint32_t duart_base_device::get_ct_rate() { - double rate = 0.0f; + uint32_t rate = 0; if (ACR & 0x40) { @@ -398,11 +398,15 @@ double duart_base_device::get_ct_rate() switch ((ACR >> 4) & 3) { case 0: // IP2 - case 1: // TxCA - case 2: // TxCB //logerror( "68681 (%s): Unhandled timer/counter mode %d\n", device->tag(), (duart68681->ACR >> 4) & 3); rate = clock(); break; + case 1: // TxCA + rate = m_chanA->get_tx_rate(); + break; + case 2: // TxCB + rate = m_chanB->get_tx_rate(); + break; case 3: // X1/CLK / 16 rate = clock() / 16; break; @@ -414,14 +418,14 @@ double duart_base_device::get_ct_rate() uint16_t duart_base_device::get_ct_count() { - double clock = get_ct_rate(); - return (duart_timer->remaining() * clock).as_double(); + uint32_t clock = get_ct_rate(); + return duart_timer->remaining().as_ticks(clock); } void duart_base_device::start_ct(int count) { - double clock = get_ct_rate(); - duart_timer->adjust(attotime::from_hz(clock) * count, 0); + uint32_t clock = get_ct_rate(); + duart_timer->adjust(attotime::from_ticks(count, clock), 0); } TIMER_CALLBACK_MEMBER(duart_base_device::duart_timer_callback) @@ -439,28 +443,25 @@ TIMER_CALLBACK_MEMBER(duart_base_device::duart_timer_callback) } // timer driving any serial channels? - if (BIT(ACR, 7) == 1) - { - uint8_t csr = m_chanA->get_chan_CSR(); + uint8_t csr = m_chanA->get_chan_CSR(); - if ((csr & 0xf0) == 0xd0) // tx is timer driven - { - m_chanA->tx_clock_w(half_period); - } - if ((csr & 0x0f) == 0x0d) // rx is timer driven - { - m_chanA->rx_clock_w(half_period); - } + if ((csr & 0x0f) == 0x0d) // tx is timer driven + { + m_chanA->tx_16x_clock_w(half_period); + } + if ((csr & 0xf0) == 0xd0) // rx is timer driven + { + m_chanA->rx_16x_clock_w(half_period); + } - csr = m_chanB->get_chan_CSR(); - if ((csr & 0xf0) == 0xd0) // tx is timer driven - { - m_chanB->tx_clock_w(half_period); - } - if ((csr & 0x0f) == 0x0d) // rx is timer driven - { - m_chanB->rx_clock_w(half_period); - } + csr = m_chanB->get_chan_CSR(); + if ((csr & 0x0f) == 0x0d) // tx is timer driven + { + m_chanB->tx_16x_clock_w(half_period); + } + if ((csr & 0xf0) == 0xd0) // rx is timer driven + { + m_chanB->rx_16x_clock_w(half_period); } if (!half_period) @@ -471,6 +472,10 @@ TIMER_CALLBACK_MEMBER(duart_base_device::duart_timer_callback) } else { + // assert OP3 counter ready + if ((OPCR & 0xc) == 0x4) + OPR |= 0x8; + // Counter mode set_ISR_bits(INT_COUNTER_READY); start_ct(0xffff); @@ -524,7 +529,7 @@ uint8_t mc68340_duart_device::read(offs_t offset) uint8_t sc28c94_device::read(offs_t offset) { uint8_t r = 0; - offset &= 0x1f; + offset &= 0x3f; if (offset < 0x10) { @@ -607,7 +612,7 @@ uint8_t duart_base_device::read(offs_t offset) break; case 0x0d: /* IP */ - if (!read_inport.isnull()) + if (!read_inport.isunset()) { r = read_inport(); // TODO: go away } @@ -633,12 +638,18 @@ uint8_t duart_base_device::read(offs_t offset) } case 0x0f: /* Stop counter command */ - clear_ISR_bits(INT_COUNTER_READY); // Stop the counter only if (!(ACR & 0x40)) - duart_timer->adjust(attotime::never); + { + duart_timer->enable(false); + // clear OP3 counter ready + if ((OPCR & 0xc) == 0x4) + OPR &= ~0x8; + } + + clear_ISR_bits(INT_COUNTER_READY); break; default: @@ -683,7 +694,7 @@ void mc68340_duart_device::write(offs_t offset, uint8_t data) void sc28c94_device::write(offs_t offset, uint8_t data) { - offset &= 0x1f; + offset &= 0x3f; if (offset < 0x10) { @@ -699,10 +710,10 @@ void sc28c94_device::write(offs_t offset, uint8_t data) m_chanC->write_chan_reg(offset&3, data); break; - case 0x18: /* MRC */ - case 0x19: /* CSRC */ - case 0x1a: /* CRC */ - case 0x1b: /* THRC */ + case 0x18: /* MRD */ + case 0x19: /* CSRD */ + case 0x1a: /* CRD */ + case 0x1b: /* THRD */ m_chanD->write_chan_reg(offset&3, data); break; } @@ -711,6 +722,7 @@ void sc28c94_device::write(offs_t offset, uint8_t data) void xr68c681_device::write(offs_t offset, uint8_t data) { if (offset == 0x02) /* CRA */ + { switch (data >> 4) { case 0x08: /* set RX extend bit */ @@ -744,8 +756,9 @@ void xr68c681_device::write(offs_t offset, uint8_t data) data &= 0x0f; break; } - + } else if (offset == 0x0a) /* CRB */ + { switch (data >> 4) { case 0x08: /* set RX extend bit */ @@ -779,6 +792,7 @@ void xr68c681_device::write(offs_t offset, uint8_t data) data &= 0x0f; break; } + } mc68681_device::write(offset, data); /* pass on 68681 command */ } @@ -786,7 +800,7 @@ void xr68c681_device::write(offs_t offset, uint8_t data) void duart_base_device::write(offs_t offset, uint8_t data) { offset &= 0x0f; - LOG("Writing 68681 (%s) reg %x (%s) with %04x\n", tag(), offset, duart68681_reg_write_names[offset], data); + LOG("Writing 68681 (%s) reg %x (%s) with %02x\n", tag(), offset, duart68681_reg_write_names[offset], data); switch (offset) { case 0x00: /* MRA */ @@ -868,7 +882,7 @@ void duart_base_device::write(offs_t offset, uint8_t data) } } -WRITE_LINE_MEMBER(duart_base_device::ip0_w) +void duart_base_device::ip0_w(int state) { uint8_t newIP = (IP_last_state & ~0x01) | ((state == ASSERT_LINE) ? 1 : 0); @@ -885,7 +899,7 @@ WRITE_LINE_MEMBER(duart_base_device::ip0_w) IP_last_state = newIP; } -WRITE_LINE_MEMBER(duart_base_device::ip1_w) +void duart_base_device::ip1_w(int state) { uint8_t newIP = (IP_last_state & ~0x02) | ((state == ASSERT_LINE) ? 2 : 0); @@ -902,7 +916,7 @@ WRITE_LINE_MEMBER(duart_base_device::ip1_w) IP_last_state = newIP; } -WRITE_LINE_MEMBER(duart_base_device::ip2_w) +void duart_base_device::ip2_w(int state) { uint8_t newIP = (IP_last_state & ~0x04) | ((state == ASSERT_LINE) ? 4 : 0); @@ -919,7 +933,7 @@ WRITE_LINE_MEMBER(duart_base_device::ip2_w) IP_last_state = newIP; } -WRITE_LINE_MEMBER(duart_base_device::ip3_w) +void duart_base_device::ip3_w(int state) { uint8_t newIP = (IP_last_state & ~0x08) | ((state == ASSERT_LINE) ? 8 : 0); @@ -936,21 +950,21 @@ WRITE_LINE_MEMBER(duart_base_device::ip3_w) IP_last_state = newIP; } -WRITE_LINE_MEMBER(duart_base_device::ip4_w) +void duart_base_device::ip4_w(int state) { uint8_t newIP = (IP_last_state & ~0x10) | ((state == ASSERT_LINE) ? 0x10 : 0); // TODO: special mode for ip4 (Ch. A Rx clock) IP_last_state = newIP; } -WRITE_LINE_MEMBER(duart_base_device::ip5_w) +void duart_base_device::ip5_w(int state) { uint8_t newIP = (IP_last_state & ~0x20) | ((state == ASSERT_LINE) ? 0x20 : 0); // TODO: special mode for ip5 (Ch. B Tx clock) IP_last_state = newIP; } -WRITE_LINE_MEMBER(duart_base_device::ip6_w) +void duart_base_device::ip6_w(int state) { uint8_t newIP = (IP_last_state & ~0x40) | ((state == ASSERT_LINE) ? 0x40 : 0); // TODO: special mode for ip6 (Ch. B Rx clock) @@ -1084,7 +1098,6 @@ int xr68c681_device::calc_baud(int ch, bool rx, uint8_t data) { int baud_rate; - baud_rate = baud_rate_ACR_0[data & 0x0f]; if (ch == 0) @@ -1121,7 +1134,7 @@ int xr68c681_device::calc_baud(int ch, bool rx, uint8_t data) void duart_base_device::clear_ISR_bits(int mask) { - if ((ISR & mask) != 0) + if (ISR & mask) { ISR &= ~mask; update_interrupts(); @@ -1130,7 +1143,7 @@ void duart_base_device::clear_ISR_bits(int mask) void duart_base_device::set_ISR_bits(int mask) { - if ((~ISR & mask) != 0) + if (!(ISR & mask)) { ISR |= mask; update_interrupts(); @@ -1147,9 +1160,11 @@ duart_channel::duart_channel(const machine_config &mconfig, const char *tag, dev , SR(0) , rx_enabled(0) , rx_fifo_num(0) - , tx_enabled(0) + , m_tx_data_in_buffer(false) + , m_tx_break(false) + , m_bits_transmitted(255) { - std::fill_n(&rx_fifo[0], MC68681_RX_FIFO_SIZE, 0); + std::fill_n(&rx_fifo[0], MC68681_RX_FIFO_SIZE + 1, 0); } void duart_channel::device_start() @@ -1170,9 +1185,10 @@ void duart_channel::device_start() save_item(NAME(rx_fifo_read_ptr)); save_item(NAME(rx_fifo_write_ptr)); save_item(NAME(rx_fifo_num)); - save_item(NAME(tx_enabled)); - save_item(NAME(tx_data)); - save_item(NAME(tx_ready)); + save_item(NAME(m_tx_data)); + save_item(NAME(m_tx_data_in_buffer)); + save_item(NAME(m_tx_break)); + save_item(NAME(m_bits_transmitted)); } void duart_channel::device_reset() @@ -1186,6 +1202,7 @@ void duart_channel::device_reset() tx_baud_rate = rx_baud_rate = 0; CSR = 0; + m_tx_break = false; } // serial device virtual overrides @@ -1208,18 +1225,28 @@ void duart_channel::rcv_complete() void duart_channel::rx_fifo_push(uint8_t data, uint8_t errors) { - if (rx_fifo_num >= MC68681_RX_FIFO_SIZE) + if (rx_fifo_num == (MC68681_RX_FIFO_SIZE + 1)) { logerror("68681: FIFO overflow\n"); SR |= STATUS_OVERRUN_ERROR; - return; + // In case of overrun the FIFO tail entry is overwritten + // Back rx_fifo_write_ptr up by one position + if (rx_fifo_write_ptr) + rx_fifo_write_ptr--; + else + rx_fifo_write_ptr = MC68681_RX_FIFO_SIZE; } rx_fifo[rx_fifo_write_ptr++] = data | (errors << 8); - if (rx_fifo_write_ptr == MC68681_RX_FIFO_SIZE) + if (rx_fifo_write_ptr > MC68681_RX_FIFO_SIZE) rx_fifo_write_ptr = 0; - if (rx_fifo_num++ == 0) + if (rx_fifo_num <= MC68681_RX_FIFO_SIZE) + { + rx_fifo_num++; + } + + if (rx_fifo_num == 1) { SR |= STATUS_RECEIVER_READY; if (!(MR1 & MODE_BLOCK_ERROR)) @@ -1233,20 +1260,17 @@ void duart_channel::rx_fifo_push(uint8_t data, uint8_t errors) void duart_channel::tra_complete() { - //printf("%s ch %d Tx complete\n", tag(), m_ch); - tx_ready = 1; - SR |= STATUS_TRANSMITTER_READY; - - if (m_ch == 0) - m_uart->clear_ISR_bits(INT_TXRDYA); + if (!(SR & STATUS_TRANSMITTER_READY)) + { + transmit_register_setup(m_tx_data); + m_bits_transmitted = 0; + m_tx_data_in_buffer = false; + } else - m_uart->clear_ISR_bits(INT_TXRDYB); - - // if local loopback is on, write the transmitted data as if a byte had been received - if ((MR2 & 0xc0) == 0x80) - rx_fifo_push(tx_data, 0); - - update_interrupts(); + { + SR |= STATUS_TRANSMITTER_EMPTY; + update_interrupts(); + } } void duart_channel::tra_callback() @@ -1273,9 +1297,17 @@ void duart_channel::tra_callback() m_uart->write_d_tx(bit); } } - else // must call this to advance the transmitter + else + { + // loop back transmitted bit + rx_w(transmit_register_get_data_bit()); + } + + // TxRDY is not set until the end of start bit time + if (++m_bits_transmitted > 1 && !m_tx_data_in_buffer) { - transmit_register_get_data_bit(); + SR |= STATUS_TRANSMITTER_READY; + update_interrupts(); } } @@ -1285,24 +1317,13 @@ void duart_channel::update_interrupts() switch (MR2 & 0xc0) // what mode are we in? { case 0x00: // normal mode - if (tx_enabled) - SR |= STATUS_TRANSMITTER_EMPTY; - else - SR &= ~STATUS_TRANSMITTER_EMPTY; break; case 0x40: // automatic echo mode - SR &= ~STATUS_TRANSMITTER_EMPTY; SR &= ~STATUS_TRANSMITTER_READY; break; case 0x80: // local loopback mode - if (tx_enabled) - SR |= STATUS_TRANSMITTER_EMPTY; - else - SR &= ~STATUS_TRANSMITTER_EMPTY; break; case 0xc0: // remote loopback mode - // write me, what the txrdy/txemt regs do for remote loopback mode is undocumented afaik, for now just clear both - SR &= ~STATUS_TRANSMITTER_EMPTY; SR &= ~STATUS_TRANSMITTER_READY; break; } @@ -1363,13 +1384,17 @@ uint8_t duart_channel::read_rx_fifo() } rv = rx_fifo[rx_fifo_read_ptr++]; - if (rx_fifo_read_ptr == MC68681_RX_FIFO_SIZE) + if (rx_fifo_read_ptr > MC68681_RX_FIFO_SIZE) { rx_fifo_read_ptr = 0; } rx_fifo_num--; - SR &= ~STATUS_FIFO_FULL; + if (rx_fifo_num == (MC68681_RX_FIFO_SIZE - 1)) + { + SR &= ~STATUS_FIFO_FULL; + } + if (!(MR1 & MODE_BLOCK_ERROR)) SR &= ~(STATUS_RECEIVED_BREAK | STATUS_FRAMING_ERROR | STATUS_PARITY_ERROR); if (rx_fifo_num == 0) @@ -1428,7 +1453,7 @@ void duart_channel::write_chan_reg(int reg, uint8_t data) CSR = data; tx_baud_rate = m_uart->calc_baud(m_ch, false, data & 0xf); rx_baud_rate = m_uart->calc_baud(m_ch, true, (data>>4) & 0xf); - //printf("%s ch %d CSR %02x Tx baud %d Rx baud %d\n", tag(), m_ch, data, tx_baud_rate, rx_baud_rate); + //printf("%s ch %d CSR %02x Tx baud %d Rx baud %d\n", tag(), m_ch, data, tx_baud_rate, rx_baud_rate); set_rcv_rate(rx_baud_rate); set_tra_rate(tx_baud_rate); break; @@ -1532,13 +1557,20 @@ void duart_channel::write_CR(uint8_t data) receive_register_reset(); break; case 3: /* Reset channel transmitter */ - tx_enabled = 0; - SR &= ~STATUS_TRANSMITTER_READY; + SR &= ~(STATUS_TRANSMITTER_READY | STATUS_TRANSMITTER_EMPTY); if (m_ch == 0) + { + m_uart->write_a_tx(1); m_uart->clear_ISR_bits(INT_TXRDYA); + } else + { + m_uart->write_b_tx(1); m_uart->clear_ISR_bits(INT_TXRDYB); + } transmit_register_reset(); + m_bits_transmitted = 255; + m_tx_data_in_buffer = false; break; case 4: /* Reset Error Status */ SR &= ~(STATUS_RECEIVED_BREAK | STATUS_FRAMING_ERROR | STATUS_PARITY_ERROR | STATUS_OVERRUN_ERROR); @@ -1549,7 +1581,36 @@ void duart_channel::write_CR(uint8_t data) else m_uart->clear_ISR_bits(INT_DELTA_BREAK_B); break; - /* TODO: case 6 and case 7 are start break and stop break respectively, which start or stop holding the TxDA or TxDB line low (space) after whatever data is in the buffer finishes transmitting (following the stop bit?), or after two bit-times if no data is being transmitted */ + case 6: /* Start Tx break */ + if (!m_tx_break) + { + m_tx_break = true; + if ((MR2 & 0xc0) == 0x80) + { + // Local loopback mode: set delta break in ISR, simulate break rx + if (m_ch == 0) + m_uart->set_ISR_bits(INT_DELTA_BREAK_A); + else + m_uart->set_ISR_bits(INT_DELTA_BREAK_B); + rx_fifo_push(0 , STATUS_RECEIVED_BREAK); + } + // TODO: Actually send break signal + } + break; + case 7: /* Stop tx break */ + if (m_tx_break) + { + m_tx_break = false; + if ((MR2 & 0xc0) == 0x80) + { + // Local loopback mode: set delta break in ISR + if (m_ch == 0) + m_uart->set_ISR_bits(INT_DELTA_BREAK_A); + else + m_uart->set_ISR_bits(INT_DELTA_BREAK_B); + } + } + break; default: LOG("68681: Unhandled command (%x) in CR%d\n", (data >> 4) & 0x07, m_ch); break; @@ -1565,11 +1626,10 @@ void duart_channel::write_CR(uint8_t data) SR &= ~STATUS_RECEIVER_READY; } - if (BIT(data, 2)) + if (!(SR & STATUS_TRANSMITTER_READY) && BIT(data, 2)) { - tx_enabled = 1; - tx_ready = 1; - SR |= STATUS_TRANSMITTER_READY; + SR |= STATUS_TRANSMITTER_READY | STATUS_TRANSMITTER_EMPTY; + m_tx_data_in_buffer = false; if (m_ch == 0) m_uart->set_ISR_bits(INT_TXRDYA); else @@ -1577,9 +1637,8 @@ void duart_channel::write_CR(uint8_t data) } if (BIT(data, 3)) { - tx_enabled = 0; - tx_ready = 0; - SR &= ~STATUS_TRANSMITTER_READY; + SR &= ~(STATUS_TRANSMITTER_READY | STATUS_TRANSMITTER_EMPTY); + m_tx_data_in_buffer = false; if (m_ch == 0) m_uart->clear_ISR_bits(INT_TXRDYA); else @@ -1591,25 +1650,23 @@ void duart_channel::write_CR(uint8_t data) void duart_channel::write_TX(uint8_t data) { - tx_data = data; - -/* if (!tx_ready) - { - printf("Write %02x to TX when TX not ready!\n", data); - }*/ - - //printf("%s ch %d Tx %c [%02x]\n", tag(), m_ch, isprint(data) ? data : ' ', data); - - tx_ready = 0; - SR &= ~STATUS_TRANSMITTER_READY; + if (!(SR & STATUS_TRANSMITTER_READY)) + { + logerror("write_tx transmitter not ready (data 0x%02x discarded)\n", data); + return; + } - if (m_ch == 0) - m_uart->clear_ISR_bits(INT_TXRDYA); + SR &= ~(STATUS_TRANSMITTER_READY | STATUS_TRANSMITTER_EMPTY); + if (!is_transmit_register_empty()) + { + m_tx_data = data; + m_tx_data_in_buffer = true; + } else - m_uart->clear_ISR_bits(INT_TXRDYB); - - // send tx_data - transmit_register_setup(tx_data); + { + transmit_register_setup(data); + m_bits_transmitted = 0; + } update_interrupts(); } @@ -1623,3 +1680,43 @@ uint8_t duart_channel::get_chan_CSR() { return CSR; } + +void duart_channel::tx_16x_clock_w(bool state) +{ + if (state) + { + m_tx_prescaler--; + if (m_tx_prescaler == 8) + { + tx_clock_w(true); + } + else if (m_tx_prescaler == 0) + { + m_tx_prescaler = 16; + tx_clock_w(false); + } + } +} + +void duart_channel::rx_16x_clock_w(bool state) +{ + if (!is_receive_register_synchronized()) + { + // Skip over the start bit once synchonization is achieved + m_rx_prescaler = 32; + rx_clock_w(true); + } + else if (state) + { + m_rx_prescaler--; + if (m_rx_prescaler == 8) + { + rx_clock_w(false); + } + else if (m_rx_prescaler == 0) + { + m_rx_prescaler = 16; + rx_clock_w(true); + } + } +} |