// license:BSD-3-Clause // copyright-holders:smf /********************************************************************* i8251.c Intel 8251 Universal Synchronous/Asynchronous Receiver Transmitter code NEC uPD71051 is a clone The V53/V53A use a customized version with only the Asynchronous mode and a split command / mode register *********************************************************************/ #include "emu.h" #include "i8251.h" //#define VERBOSE 1 #include "logmacro.h" //************************************************************************** // DEVICE DEFINITIONS //************************************************************************** DEFINE_DEVICE_TYPE(I8251, i8251_device, "i8251", "Intel 8251 USART") DEFINE_DEVICE_TYPE(V53_SCU, v53_scu_device, "v63_scu", "NEC V53 SCU") //------------------------------------------------- // i8251_device - constructor //------------------------------------------------- i8251_device::i8251_device( const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) : device_t(mconfig, type, tag, owner, clock), device_serial_interface(mconfig, *this), m_txd_handler(*this), m_dtr_handler(*this), m_rts_handler(*this), m_rxrdy_handler(*this), m_txrdy_handler(*this), m_txempty_handler(*this), m_syndet_handler(*this), m_cts(1), m_dsr(1), m_rxd(0), m_rxc(0), m_txc(0) { } i8251_device::i8251_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : i8251_device(mconfig, I8251, tag, owner, clock) { } v53_scu_device::v53_scu_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : i8251_device(mconfig, V53_SCU, tag, owner, clock) { } //------------------------------------------------- // device_start - device-specific startup //------------------------------------------------- void i8251_device::device_start() { // resolve callbacks m_txd_handler.resolve_safe(); m_rts_handler.resolve_safe(); m_dtr_handler.resolve_safe(); m_rxrdy_handler.resolve_safe(); m_txrdy_handler.resolve_safe(); m_txempty_handler.resolve_safe(); save_item(NAME(m_flags)); save_item(NAME(m_sync_byte_offset)); save_item(NAME(m_sync_byte_count)); save_item(NAME(m_sync_bytes)); save_item(NAME(m_status)); save_item(NAME(m_command)); save_item(NAME(m_mode_byte)); save_item(NAME(m_cts)); save_item(NAME(m_dsr)); save_item(NAME(m_rxd)); save_item(NAME(m_rxc)); save_item(NAME(m_txc)); save_item(NAME(m_rxc_count)); save_item(NAME(m_txc_count)); save_item(NAME(m_br_factor)); save_item(NAME(m_rx_data)); save_item(NAME(m_tx_data)); } /*------------------------------------------------- update_rx_ready -------------------------------------------------*/ void i8251_device::update_rx_ready() { int state = m_status & I8251_STATUS_RX_READY; // masked? if (!BIT(m_command, 2)) state = 0; m_rxrdy_handler(state != 0); } /*------------------------------------------------- receive_clock -------------------------------------------------*/ void i8251_device::receive_clock() { /* receive enable? */ if (BIT(m_command, 2)) { const bool sync = is_receive_register_synchronized(); if (sync) { --m_rxc_count; if (m_rxc_count) return; } //logerror("I8251\n"); /* get bit received from other side and update receive register */ receive_register_update_bit(m_rxd); if (is_receive_register_synchronized()) m_rxc_count = sync ? m_br_factor : (3 * m_br_factor / 2); if (is_receive_register_full()) { receive_register_extract(); receive_character(get_received_char()); } } } /*------------------------------------------------- is_tx_enabled -------------------------------------------------*/ bool i8251_device::is_tx_enabled() const { return BIT(m_command , 0) && !m_cts; } /*------------------------------------------------- check_for_tx_start -------------------------------------------------*/ void i8251_device::check_for_tx_start() { if (is_tx_enabled() && (m_status & (I8251_STATUS_TX_EMPTY | I8251_STATUS_TX_READY)) == I8251_STATUS_TX_EMPTY) start_tx(); } /*------------------------------------------------- start_tx -------------------------------------------------*/ void i8251_device::start_tx() { LOG("start_tx %02x\n", m_tx_data); transmit_register_setup(m_tx_data); m_status &= ~I8251_STATUS_TX_EMPTY; m_status |= I8251_STATUS_TX_READY; } /*------------------------------------------------- transmit_clock -------------------------------------------------*/ void i8251_device::transmit_clock() { m_txc_count++; if (m_txc_count != m_br_factor) return; m_txc_count = 0; if (is_transmit_register_empty()) { if ((m_status & I8251_STATUS_TX_READY) == 0 && (is_tx_enabled() || (m_flags & I8251_DELAYED_TX_EN) != 0)) start_tx(); else m_status |= I8251_STATUS_TX_EMPTY; update_tx_ready(); update_tx_empty(); } /* if diserial has bits to send, make them so */ if (!is_transmit_register_empty()) { uint8_t data = transmit_register_get_data_bit(); m_txd_handler(data); } #if 0 /* hunt mode? */ /* after each bit has been shifted in, it is compared against the current sync byte */ if (BIT(m_command, 7)) { /* data matches sync byte? */ if (m_data == m_sync_bytes[m_sync_byte_offset]) { /* sync byte matches */ /* update for next sync byte? */ m_sync_byte_offset++; /* do all sync bytes match? */ if (m_sync_byte_offset == m_sync_byte_count) { /* ent hunt mode */ m_command &= ~(1<<7); } } else { /* if there is no match, reset */ m_sync_byte_offset = 0; } } #endif } /*------------------------------------------------- update_tx_ready -------------------------------------------------*/ void i8251_device::update_tx_ready() { /* clear tx ready state */ int tx_ready; /* tx ready output is set if: DB Buffer Empty & CTS is set & Transmit enable is 1 */ tx_ready = is_tx_enabled() && (m_status & I8251_STATUS_TX_READY) != 0; m_txrdy_handler(tx_ready); } /*------------------------------------------------- update_tx_empty -------------------------------------------------*/ void i8251_device::update_tx_empty() { if (m_status & I8251_STATUS_TX_EMPTY) { /* tx is in marking state (high) when tx empty! */ m_txd_handler(1); } m_txempty_handler((m_status & I8251_STATUS_TX_EMPTY) != 0); } //------------------------------------------------- // device_reset - device-specific reset //------------------------------------------------- void i8251_device::device_reset() { LOG("I8251: Reset\n"); /* what is the default setup when the 8251 has been reset??? */ /* i8251 datasheet explains the state of tx pin at reset */ /* tx is set to 1 */ m_txd_handler(1); /* assumption */ m_rts_handler(1); m_dtr_handler(1); transmit_register_reset(); receive_register_reset(); m_flags = 0; /* expecting mode byte */ m_flags |= I8251_EXPECTING_MODE; /* not expecting a sync byte */ m_flags &= ~I8251_EXPECTING_SYNC_BYTE; /* no character to read by cpu */ /* transmitter is ready and is empty */ m_status = I8251_STATUS_TX_EMPTY | I8251_STATUS_TX_READY; m_mode_byte = 0; m_command = 0; m_rx_data = 0; m_tx_data = 0; m_rxc_count = m_txc_count = 0; m_br_factor = 1; /* update tx empty pin output */ update_tx_empty(); /* update rx ready pin output */ update_rx_ready(); /* update tx ready pin output */ update_tx_ready(); } /*------------------------------------------------- control_w -------------------------------------------------*/ void i8251_device::command_w(uint8_t data) { /* command */ LOG("I8251: Command byte\n"); m_command = data; LOG("Command byte: %02x\n", data); if (BIT(data, 7)) LOG("hunt mode\n"); if (BIT(data, 5)) LOG("/rts set to 0\n"); else LOG("/rts set to 1\n"); if (BIT(data, 2)) LOG("receive enable\n"); else LOG("receive disable\n"); if (BIT(data, 1)) LOG("/dtr set to 0\n"); else LOG("/dtr set to 1\n"); if (BIT(data, 0)) LOG("transmit enable\n"); else LOG("transmit disable\n"); /* bit 7: 0 = normal operation 1 = hunt mode bit 6: 0 = normal operation 1 = internal reset bit 5: 0 = /RTS set to 1 1 = /RTS set to 0 bit 4: 0 = normal operation 1 = reset error flag bit 3: 0 = normal operation 1 = send break character bit 2: 0 = receive disable 1 = receive enable bit 1: 0 = /DTR set to 1 1 = /DTR set to 0 bit 0: 0 = transmit disable 1 = transmit enable */ m_rts_handler(!BIT(data, 5)); m_dtr_handler(!BIT(data, 1)); if (BIT(data, 4)) m_status &= ~(I8251_STATUS_PARITY_ERROR | I8251_STATUS_OVERRUN_ERROR | I8251_STATUS_FRAMING_ERROR); if (BIT(data, 6)) { // datasheet says "returns to mode format", not // completely resets the chip. behavior of DEC Rainbow // backs this up. m_flags |= I8251_EXPECTING_MODE; } check_for_tx_start(); update_rx_ready(); update_tx_ready(); update_tx_empty(); } void i8251_device::mode_w(uint8_t data) { LOG("I8251: Mode byte = %X\n", data); m_mode_byte = data; /* Synchronous or Asynchronous? */ if ((data & 0x03) != 0) { /* Asynchronous bit 7,6: stop bit length 0 = inhibit 1 = 1 bit 2 = 1.5 bits 3 = 2 bits bit 5: parity type 0 = parity odd 1 = parity even bit 4: parity test enable 0 = disable 1 = enable bit 3,2: character length 0 = 5 bits 1 = 6 bits 2 = 7 bits 3 = 8 bits bit 1,0: baud rate factor 0 = defines command byte for synchronous or asynchronous 1 = x1 2 = x16 3 = x64 */ LOG("I8251: Asynchronous operation\n"); const int data_bits_count = ((data >> 2) & 0x03) + 5; LOG("Character length: %d\n", data_bits_count); parity_t parity; if (BIT(data, 4)) { if (BIT(data, 5)) { LOG("enable parity checking (even parity)\n"); parity = PARITY_EVEN; } else { LOG("enable parity checking (odd parity)\n"); parity = PARITY_ODD; } } else { LOG("parity check disabled\n"); parity = PARITY_NONE; } stop_bits_t stop_bits; switch ((data >> 6) & 0x03) { case 0: default: stop_bits = STOP_BITS_0; LOG("stop bit: inhibit\n"); break; case 1: stop_bits = STOP_BITS_1; LOG("stop bit: 1 bit\n"); break; case 2: stop_bits = STOP_BITS_1_5; LOG("stop bit: 1.5 bits\n"); break; case 3: stop_bits = STOP_BITS_2; LOG("stop bit: 2 bits\n"); break; } set_data_frame(1, data_bits_count, parity, stop_bits); receive_register_reset(); switch (data & 0x03) { case 1: m_br_factor = 1; break; case 2: m_br_factor = 16; break; case 3: m_br_factor = 64; break; } m_txc_count = 0; #if 0 /* data bits */ m_receive_char_length = (((data >> 2) & 0x03) + 5); if (BIT(data, 4)) { /* parity */ m_receive_char_length++; } /* stop bits */ m_receive_char_length++; m_receive_flags &= ~I8251_TRANSFER_RECEIVE_SYNCHRONISED; m_receive_flags |= I8251_TRANSFER_RECEIVE_WAITING_FOR_START_BIT; #endif /* not expecting mode byte now */ m_flags &= ~I8251_EXPECTING_MODE; // m_status = I8251_STATUS_TX_EMPTY | I8251_STATUS_TX_READY; } else { /* bit 7: Number of sync characters 0 = 1 character 1 = 2 character bit 6: Synchronous mode 0 = Internal synchronisation 1 = External synchronisation bit 5: parity type 0 = parity odd 1 = parity even bit 4: parity test enable 0 = disable 1 = enable bit 3,2: character length 0 = 5 bits 1 = 6 bits 2 = 7 bits 3 = 8 bits bit 1,0 = 0 */ LOG("I8251: Synchronous operation\n"); /* setup for sync byte(s) */ m_flags |= I8251_EXPECTING_SYNC_BYTE; m_sync_byte_offset = 0; if (BIT(data, 7)) { m_sync_byte_count = 1; } else { m_sync_byte_count = 2; } } } WRITE8_MEMBER(i8251_device::control_w) { if (m_flags & I8251_EXPECTING_MODE) { if (m_flags & I8251_EXPECTING_SYNC_BYTE) { LOG("I8251: Sync byte\n"); LOG("Sync byte: %02x\n", data); /* store sync byte written */ m_sync_bytes[m_sync_byte_offset] = data; m_sync_byte_offset++; if (m_sync_byte_offset == m_sync_byte_count) { /* finished transfering sync bytes, now expecting command */ m_flags &= ~(I8251_EXPECTING_MODE | I8251_EXPECTING_SYNC_BYTE); m_sync_byte_offset = 0; // m_status = I8251_STATUS_TX_EMPTY | I8251_STATUS_TX_READY; } } else { mode_w(data); } } else { command_w(data); } } /*------------------------------------------------- status_r -------------------------------------------------*/ READ8_MEMBER(i8251_device::status_r) { uint8_t status = (m_dsr << 7) | m_status; LOG("status: %02x\n", status); return status; } /*------------------------------------------------- data_w -------------------------------------------------*/ WRITE8_MEMBER(i8251_device::data_w) { m_tx_data = data; LOG("data_w %02x\n" , data); /* writing clears */ m_status &=~I8251_STATUS_TX_READY; update_tx_ready(); // Store state of tx enable when writing to DB buffer if (is_tx_enabled()) { m_flags |= I8251_DELAYED_TX_EN; } else { m_flags &= ~I8251_DELAYED_TX_EN; } check_for_tx_start(); /* if transmitter is active, then tx empty will be signalled */ update_tx_ready(); update_tx_empty(); } /*------------------------------------------------- receive_character - called when last bit of data has been received -------------------------------------------------*/ void i8251_device::receive_character(uint8_t ch) { LOG("receive_character %02x\n", ch); m_rx_data = ch; /* char has not been read and another has arrived! */ if (m_status & I8251_STATUS_RX_READY) m_status |= I8251_STATUS_OVERRUN_ERROR; m_status |= I8251_STATUS_RX_READY; update_rx_ready(); } /*------------------------------------------------- data_r - read data -------------------------------------------------*/ READ8_MEMBER(i8251_device::data_r) { LOG("read data: %02x, STATUS=%02x\n",m_rx_data,m_status); /* reading clears */ m_status &= ~I8251_STATUS_RX_READY; update_rx_ready(); return m_rx_data; } WRITE_LINE_MEMBER(i8251_device::write_rxd) { m_rxd = state; // device_serial_interface::rx_w(state); } WRITE_LINE_MEMBER(i8251_device::write_cts) { m_cts = state; check_for_tx_start(); update_tx_ready(); update_tx_empty(); } WRITE_LINE_MEMBER(i8251_device::write_dsr) { m_dsr = !state; } WRITE_LINE_MEMBER(i8251_device::write_rxc) { if (m_rxc != state) { m_rxc = state; if (m_rxc) receive_clock(); } } WRITE_LINE_MEMBER(i8251_device::write_txc) { if (m_txc != state) { m_txc = state; if (!m_txc) transmit_clock(); } } READ_LINE_MEMBER(i8251_device::txrdy_r) { return is_tx_enabled() && (m_status & I8251_STATUS_TX_READY) != 0; } WRITE8_MEMBER(v53_scu_device::command_w) { i8251_device::command_w(data); } WRITE8_MEMBER(v53_scu_device::mode_w) { i8251_device::mode_w(data); }