diff options
Diffstat (limited to 'src/devices/machine/mc68681.cpp')
-rw-r--r-- | src/devices/machine/mc68681.cpp | 200 |
1 files changed, 106 insertions, 94 deletions
diff --git a/src/devices/machine/mc68681.cpp b/src/devices/machine/mc68681.cpp index 0f6f8830ea1..3a3fcfcecf7 100644 --- a/src/devices/machine/mc68681.cpp +++ b/src/devices/machine/mc68681.cpp @@ -62,7 +62,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 */ @@ -112,8 +112,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 +123,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), @@ -200,15 +200,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 +211,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 +247,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 +306,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 +372,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 +400,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 +420,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) @@ -468,6 +474,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); @@ -604,7 +614,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 } @@ -630,12 +640,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: @@ -783,7 +799,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 */ @@ -865,7 +881,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); @@ -882,7 +898,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); @@ -899,7 +915,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); @@ -916,7 +932,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); @@ -933,21 +949,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) @@ -1118,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(); @@ -1127,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(); @@ -1144,8 +1160,9 @@ 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 + 1, 0); } @@ -1168,10 +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() @@ -1243,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->set_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->set_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() @@ -1283,9 +1297,15 @@ 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(); } } @@ -1295,24 +1315,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; } @@ -1546,13 +1555,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); @@ -1608,11 +1624,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 @@ -1620,9 +1635,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 @@ -1634,25 +1648,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(); } |