From 1076f566a43f55b3f6a026e03f06685cf433483a Mon Sep 17 00:00:00 2001 From: Robbbert Date: Fri, 6 Sep 2019 13:15:45 +1000 Subject: i8251: Preliminary addition of synchronous support. - Can transmit - Can receive with one sync byte - bisync still being worked on --- src/devices/machine/i8251.cpp | 445 +++++++++++++++++++++++++----------------- src/devices/machine/i8251.h | 42 +++- src/emu/diserial.cpp | 7 +- 3 files changed, 300 insertions(+), 194 deletions(-) diff --git a/src/devices/machine/i8251.cpp b/src/devices/machine/i8251.cpp index 1c3189d8f8f..bae0d9b638f 100644 --- a/src/devices/machine/i8251.cpp +++ b/src/devices/machine/i8251.cpp @@ -80,10 +80,9 @@ void i8251_device::device_start() m_rxrdy_handler.resolve_safe(); m_txrdy_handler.resolve_safe(); m_txempty_handler.resolve_safe(); + m_syndet_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)); @@ -154,12 +153,95 @@ void i8251_device::receive_clock() } } +bool i8251_device::calc_parity(u8 ch) +{ + bool data = 0; + for (u8 b = 0; b < 8; b++) + data ^= BIT(ch, b); + return data; +} + +void i8251_device::sync1_rxc() +{ + // is rx enabled? + if (!BIT(m_command, 2)) + return; + + // if ext sync, and syndet low, quit + // Todo: what should happen here? + if (m_syndet_pin && !m_ext_syn_set) + return; + + u8 need_parity = BIT(m_mode_byte, 4); + + // see about parity + if (need_parity && (m_rxd_bits == m_data_bits_count)) + { + if (calc_parity(m_sync1) != m_rxd) + m_status |= I8251_STATUS_PARITY_ERROR; + // and then continue on as if everything was ok + } + else + { + // add bit to byte + m_sync1 = (m_sync1 >> 1) | (m_rxd << (m_data_bits_count-1)); + } + + // if we are in hunt mode, the byte loaded has to + // be the sync byte. If not, go around again. + // if we leave hunt mode now, the sync byte must not go to the receive buffer + bool was_in_hunt_mode = false; + if (m_hunt_on) + { + if (m_sync1 == m_sync8) + { + m_rxd_bits = m_data_bits_count; + m_hunt_on = false; + was_in_hunt_mode = true; + } + else + return; + } + + // is byte complete? if not, quit + m_rxd_bits++; + if (m_rxd_bits < (m_data_bits_count + need_parity)) + return; + + // now we have a synchronised byte, and parity has been dealt with + + // copy byte to rx buffer + if (!was_in_hunt_mode) + receive_character(m_sync1); + + // Is it a sync byte? syndet gets indicated whenever + // a sync byte passes by, regardless of hunt_mode status. + if (m_sync1 == m_sync8) + update_syndet(true); + + m_rxd_bits = 0; + m_sync1 = 0; +} + +void i8251_device::sync2_rxc() +{ + // is rx enabled? + if (!BIT(m_command, 2)) + return; + + // if ext sync, and syndet low, quit + if (m_syndet_pin && !m_ext_syn_set) + return; + + // remainder yet to be written +} + /*------------------------------------------------- is_tx_enabled -------------------------------------------------*/ bool i8251_device::is_tx_enabled() const { - return BIT(m_command , 0) && !m_cts; + return BIT(m_command, 0) && !m_cts; } /*------------------------------------------------- @@ -196,7 +278,7 @@ void i8251_device::transmit_clock() if (is_transmit_register_empty()) { - if ((m_status & I8251_STATUS_TX_READY) == 0 && (is_tx_enabled() || (m_flags & I8251_DELAYED_TX_EN) != 0)) + if ((m_status & I8251_STATUS_TX_READY) == 0 && (is_tx_enabled() || m_delayed_tx_en)) start_tx(); else m_status |= I8251_STATUS_TX_EMPTY; @@ -211,33 +293,6 @@ void i8251_device::transmit_clock() 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 } @@ -281,6 +336,32 @@ void i8251_device::update_tx_empty() +/*--------------------------------------------------- + update_syndet - indicates that a sync + character string has been received + 1 = valid sync; 0 = hunt mode on, or status read +-----------------------------------------------------*/ + +void i8251_device::update_syndet(bool voltage) +{ + LOG("I8251: Syndet %d\n",voltage); + // Sanity check + if (voltage && m_hunt_on) + printf("I8251: Syndet - invalid parameters\n"); + + // Adjust status register + if (voltage) + m_status |= 0x40; + else + m_status &= ~0x40; + + // If syndet is set as an output pin, indicate new status + if (!m_syndet_pin) + m_syndet_handler(voltage); +} + + + //------------------------------------------------- // device_reset - device-specific reset //------------------------------------------------- @@ -301,11 +382,8 @@ void i8251_device::device_reset() 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; + m_flags = I8251_NEXT_MODE; /* no character to read by cpu */ /* transmitter is ready and is empty */ @@ -401,9 +479,20 @@ void i8251_device::command_w(uint8_t data) // datasheet says "returns to mode format", not // completely resets the chip. behavior of DEC Rainbow // backs this up. - m_flags |= I8251_EXPECTING_MODE; + m_flags = I8251_NEXT_MODE; } + // Hunt mode + m_hunt_on = false; + update_syndet(false); + if (m_sync_byte_count) + m_hunt_on = BIT(data, 7); + if (m_hunt_on) + m_ext_syn_set = false; + + if (BIT(data, 3)) + m_txd_handler(0); + check_for_tx_start(); update_rx_ready(); update_tx_ready(); @@ -412,13 +501,10 @@ void i8251_device::command_w(uint8_t data) void i8251_device::mode_w(uint8_t data) { - LOG("I8251: Mode byte = %X\n", data); + LOG("I8251: Mode byte = %02X\n", data); m_mode_byte = data; - /* Synchronous or Asynchronous? */ - if ((data & 0x03) != 0) - { /* Asynchronous bit 7,6: stop bit length @@ -442,159 +528,144 @@ void i8251_device::mode_w(uint8_t data) 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; - } + + Synchronous + + 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 + */ - stop_bits_t stop_bits; - switch ((data >> 6) & 0x03) - { - case 0: - default: - stop_bits = STOP_BITS_0; - LOG("stop bit: inhibit\n"); - break; + m_data_bits_count = ((data >> 2) & 0x03) + 5; + LOG("Character length: %d\n", m_data_bits_count); - case 1: - stop_bits = STOP_BITS_1; - LOG("stop bit: 1 bit\n"); + parity_t parity = PARITY_NONE; + switch (data & 0x30) + { + case 0x10: + LOG("Enable ODD parity checking.\n"); + parity = PARITY_ODD; break; - case 2: - stop_bits = STOP_BITS_1_5; - LOG("stop bit: 1.5 bits\n"); + case 0x30: + LOG("Enable EVEN parity checking.\n"); + parity = PARITY_EVEN; break; - case 3: - stop_bits = STOP_BITS_2; - LOG("stop bit: 2 bits\n"); - break; - } + default: + LOG("Disable parity check.\n"); + } + stop_bits_t stop_bits = STOP_BITS_0; + m_br_factor = 1; + m_flags = I8251_NEXT_COMMAND; + m_sync_byte_count = 0; + m_syndet_pin = false; - set_data_frame(1, data_bits_count, parity, stop_bits); - receive_register_reset(); + /* Synchronous or Asynchronous? */ + if (data & 0x03) + { + LOG("I8251: Asynchronous operation\n"); - switch (data & 0x03) + switch (data & 0xc0) { - case 1: m_br_factor = 1; break; - case 2: m_br_factor = 16; break; - case 3: m_br_factor = 64; break; + case 0x40: + stop_bits = STOP_BITS_1; + LOG("stop bit: 1 bit\n"); + break; + + case 0x80: + stop_bits = STOP_BITS_1_5; + LOG("stop bit: 1.5 bits\n"); + break; + + case 0xc0: + stop_bits = STOP_BITS_2; + LOG("stop bit: 2 bits\n"); + break; + + default: + LOG("stop bit: inhibit\n"); + break; } - m_txc_count = 0; - -#if 0 - /* data bits */ - m_receive_char_length = (((data >> 2) & 0x03) + 5); + set_data_frame(1, m_data_bits_count, parity, stop_bits); - if (BIT(data, 4)) + switch (data & 0x03) { - /* parity */ - m_receive_char_length++; - } + case 2: + m_br_factor = 16; + break; - /* 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; + case 3: + m_br_factor = 64; + break; + } } 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; - } - + m_flags = BIT(data, 7) ? I8251_NEXT_SYNC2 : I8251_NEXT_SYNC1; + m_sync_byte_count = BIT(data, 7) ? 1 : 2; + set_data_frame(0, m_data_bits_count, parity, stop_bits); + m_syndet_pin = BIT(data, 6); + m_sync8 = 0; + m_sync16 = 0; + m_rxd_bits = 0; + m_sync1 = 0; } + + receive_register_reset(); + m_txc_count = 0; + + // m_status = I8251_STATUS_TX_EMPTY | I8251_STATUS_TX_READY; +} + +void i8251_device::sync1_w(uint8_t data) +{ + m_sync16 = (data << 8); + m_flags = I8251_NEXT_SYNC2; +} + +void i8251_device::sync2_w(uint8_t data) +{ + if (m_sync_byte_count == 2) + m_sync16 |= data; + else + m_sync8 = data; + + m_flags = I8251_NEXT_COMMAND; } void i8251_device::control_w(uint8_t data) { - 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); - } - } + if (m_flags == I8251_NEXT_SYNC1) + sync1_w(data); + else + if (m_flags == I8251_NEXT_SYNC2) + sync2_w(data); + else + if (m_flags == I8251_NEXT_MODE) + mode_w(data); else - { command_w(data); - } } @@ -608,6 +679,9 @@ uint8_t i8251_device::status_r() uint8_t status = (m_dsr << 7) | m_status; LOG("status: %02x\n", status); + + // Syndet always goes off after status read + update_syndet(false); return status; } @@ -621,20 +695,15 @@ void i8251_device::data_w(uint8_t data) { m_tx_data = data; - LOG("data_w %02x\n" , 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(); + // Store state of tx enable when writing to DB buffer + m_delayed_tx_en = is_tx_enabled(); + check_for_tx_start(); /* if transmitter is active, then tx empty will be signalled */ @@ -685,7 +754,7 @@ uint8_t i8251_device::data_r() uint8_t i8251_device::read(offs_t offset) { - if (BIT(offset, 0)) + if (offset) return status_r(); else return data_r(); @@ -693,7 +762,7 @@ uint8_t i8251_device::read(offs_t offset) void i8251_device::write(offs_t offset, uint8_t data) { - if (BIT(offset, 0)) + if (offset) control_w(data); else data_w(data); @@ -722,13 +791,18 @@ WRITE_LINE_MEMBER(i8251_device::write_dsr) WRITE_LINE_MEMBER(i8251_device::write_rxc) { - if (m_rxc != state) + if (!m_rxc && state) { - m_rxc = state; - - if (m_rxc) + if (m_sync_byte_count == 1) + sync1_rxc(); + else + if (m_sync_byte_count == 2) + sync2_rxc(); + else receive_clock(); } + + m_rxc = state; } WRITE_LINE_MEMBER(i8251_device::write_txc) @@ -742,6 +816,17 @@ WRITE_LINE_MEMBER(i8251_device::write_txc) } } +// forcibly kill hunt mode +WRITE_LINE_MEMBER(i8251_device::write_syn) +{ + if (m_syndet_pin && state) // must be set as input + { + m_ext_syn_set = true; + m_hunt_on = false; + update_syndet(true); + } +} + READ_LINE_MEMBER(i8251_device::txrdy_r) { return is_tx_enabled() && (m_status & I8251_STATUS_TX_READY) != 0; diff --git a/src/devices/machine/i8251.h b/src/devices/machine/i8251.h index 317d17db827..15d1543c03d 100644 --- a/src/devices/machine/i8251.h +++ b/src/devices/machine/i8251.h @@ -44,6 +44,7 @@ public: DECLARE_WRITE_LINE_MEMBER( write_dsr ); DECLARE_WRITE_LINE_MEMBER( write_txc ); DECLARE_WRITE_LINE_MEMBER( write_rxc ); + DECLARE_WRITE_LINE_MEMBER( write_syn ); DECLARE_READ_LINE_MEMBER(txrdy_r); @@ -86,9 +87,10 @@ protected: enum { - I8251_EXPECTING_MODE = 0x01, - I8251_EXPECTING_SYNC_BYTE = 0x02, - I8251_DELAYED_TX_EN = 0x04 + I8251_NEXT_COMMAND = 0, + I8251_NEXT_MODE, + I8251_NEXT_SYNC1, + I8251_NEXT_SYNC2, }; private: @@ -100,25 +102,33 @@ private: devcb_write_line m_txempty_handler; devcb_write_line m_syndet_handler; + void sync1_rxc(); + void sync2_rxc(); + void update_syndet(bool voltage); + bool calc_parity(u8 ch); + /* flags controlling how i8251_control_w operates */ uint8_t m_flags; /* offset into sync_bytes used during sync byte transfer */ - uint8_t m_sync_byte_offset; + //uint8_t m_sync_byte_offset; /* number of sync bytes written so far */ uint8_t m_sync_byte_count; /* the sync bytes written */ - uint8_t m_sync_bytes[2]; + //uint8_t m_sync_bytes[2]; + u8 m_sync1; + u16 m_sync2; /* status of i8251 */ uint8_t m_status; uint8_t m_command; /* mode byte - bit definitions depend on mode - e.g. synchronous, asynchronous */ uint8_t m_mode_byte; + bool m_delayed_tx_en; - int m_cts; - int m_dsr; - int m_rxd; - int m_rxc; - int m_txc; + bool m_cts; + bool m_dsr; + bool m_rxd; + bool m_rxc; + bool m_txc; int m_rxc_count; int m_txc_count; int m_br_factor; @@ -127,6 +137,18 @@ private: uint8_t m_rx_data; /* tx buffer */ uint8_t m_tx_data; + void sync1_w(uint8_t data); + void sync2_w(uint8_t data); + uint8_t m_sync8; + uint16_t m_sync16; + // 1 = ext sync enabled via command + bool m_syndet_pin; + bool m_hunt_on; + // 1 = ext syndet pin has been set high; 0 = hunt mode activated + bool m_ext_syn_set; + // count of rxd bits + u8 m_rxd_bits; + u8 m_data_bits_count; }; class v5x_scu_device : public i8251_device diff --git a/src/emu/diserial.cpp b/src/emu/diserial.cpp index 0b83fe53962..0852a1bc666 100644 --- a/src/emu/diserial.cpp +++ b/src/emu/diserial.cpp @@ -415,10 +415,9 @@ void device_serial_interface::transmit_register_setup(u8 data_byte) } /* stop bit(s) + 1 extra bit as delay between bytes, needed to get 1 stop bit to work. */ - for (i=0; i<=m_df_stop_bit_count; i++) - { - transmit_register_add_bit(1); - } + if (m_df_stop_bit_count) // no stop bits for synchronous + for (i=0; i<=m_df_stop_bit_count; i++) // ToDo - see if the hack on this line is still needed (was added 2016-04-10) + transmit_register_add_bit(1); } -- cgit v1.2.3