i8251: Preliminary addition of synchronous support.

- Can transmit
- Can receive with one sync byte
- bisync still being worked on
This commit is contained in:
Robbbert 2019-09-06 13:15:45 +10:00
parent fd882987e0
commit 1076f566a4
3 changed files with 305 additions and 199 deletions

View File

@ -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
*/
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
*/
m_data_bits_count = ((data >> 2) & 0x03) + 5;
LOG("Character length: %d\n", m_data_bits_count);
parity_t parity = PARITY_NONE;
switch (data & 0x30)
{
case 0x10:
LOG("Enable ODD parity checking.\n");
parity = PARITY_ODD;
break;
case 0x30:
LOG("Enable EVEN parity checking.\n");
parity = PARITY_EVEN;
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;
/* Synchronous or Asynchronous? */
if (data & 0x03)
{
LOG("I8251: Asynchronous operation\n");
const int data_bits_count = ((data >> 2) & 0x03) + 5;
LOG("Character length: %d\n", data_bits_count);
switch (data & 0xc0)
{
case 0x40:
stop_bits = STOP_BITS_1;
LOG("stop bit: 1 bit\n");
break;
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;
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;
}
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();
set_data_frame(1, m_data_bits_count, parity, stop_bits);
switch (data & 0x03)
{
case 1: m_br_factor = 1; break;
case 2: m_br_factor = 16; break;
case 3: m_br_factor = 64; 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;
}
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;

View File

@ -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

View File

@ -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);
}