mame/src/emu/diserial.cpp
Miodrag Milanovic ddb290d5f6 NOTICE (TYPE NAME CONSOLIDATION)
Use standard uint64_t, uint32_t, uint16_t or uint8_t instead of UINT64, UINT32, UINT16 or UINT8
also use standard int64_t, int32_t, int16_t or int8_t instead of INT64, INT32, INT16 or INT8
2016-10-22 13:13:17 +02:00

509 lines
12 KiB
C++

// license:BSD-3-Clause
// copyright-holders:Carl, Miodrag Milanovic
/***************************************************************************
Serial device interface
***************************************************************************/
#include "emu.h"
device_serial_interface::device_serial_interface(const machine_config &mconfig, device_t &device)
: device_interface(device, "serial"),
m_start_bit_hack_for_external_clocks(false),
m_df_start_bit_count(0),
m_df_word_length(0),
m_df_parity(PARITY_NONE),
m_df_stop_bit_count(STOP_BITS_0),
m_rcv_register_data(0x8000),
m_rcv_flags(0),
m_rcv_bit_count_received(0),
m_rcv_bit_count(0),
m_rcv_byte_received(0),
m_rcv_framing_error(false),
m_rcv_parity_error(false),
m_tra_register_data(0),
m_tra_flags(TRANSMIT_REGISTER_EMPTY),
m_tra_bit_count_transmitted(0),
m_tra_bit_count(0),
m_rcv_clock(nullptr),
m_tra_clock(nullptr),
m_rcv_rate(attotime::never),
m_tra_rate(attotime::never),
m_rcv_line(0),
m_tra_clock_state(false),
m_rcv_clock_state(false)
{
/* if sum of all bits in the byte is even, then the data
has even parity, otherwise it has odd parity */
for (int i=0; i<256; i++)
{
int sum = 0;
int data = i;
for (int b=0; b<8; b++)
{
sum+=data & 0x01;
data = data>>1;
}
m_serial_parity_table[i] = sum & 0x01;
}
}
device_serial_interface::~device_serial_interface()
{
}
void device_serial_interface::register_save_state(save_manager &save, device_t *device)
{
const char *module = device->name();
const char *tag = device->tag();
save.save_item(device, module, tag, 0, NAME(m_df_start_bit_count));
save.save_item(device, module, tag, 0, NAME(m_df_word_length));
save.save_item(device, module, tag, 0, NAME(m_df_parity));
save.save_item(device, module, tag, 0, NAME(m_df_stop_bit_count));
save.save_item(device, module, tag, 0, NAME(m_rcv_register_data));
save.save_item(device, module, tag, 0, NAME(m_rcv_flags));
save.save_item(device, module, tag, 0, NAME(m_rcv_bit_count_received));
save.save_item(device, module, tag, 0, NAME(m_rcv_bit_count));
save.save_item(device, module, tag, 0, NAME(m_rcv_byte_received));
save.save_item(device, module, tag, 0, NAME(m_rcv_framing_error));
save.save_item(device, module, tag, 0, NAME(m_rcv_parity_error));
save.save_item(device, module, tag, 0, NAME(m_tra_register_data));
save.save_item(device, module, tag, 0, NAME(m_tra_flags));
save.save_item(device, module, tag, 0, NAME(m_tra_bit_count_transmitted));
save.save_item(device, module, tag, 0, NAME(m_tra_bit_count));
save.save_item(device, module, tag, 0, NAME(m_rcv_rate));
save.save_item(device, module, tag, 0, NAME(m_tra_rate));
save.save_item(device, module, tag, 0, NAME(m_rcv_line));
save.save_item(device, module, tag, 0, NAME(m_tra_clock_state));
save.save_item(device, module, tag, 0, NAME(m_rcv_clock_state));
}
void device_serial_interface::interface_pre_start()
{
m_rcv_clock = device().timer_alloc(RCV_TIMER_ID);
m_tra_clock = device().timer_alloc(TRA_TIMER_ID);
m_rcv_clock_state = false;
m_tra_clock_state = false;
}
void device_serial_interface::set_rcv_rate(const attotime &rate)
{
m_rcv_rate = rate/2;
receive_register_reset();
m_rcv_clock->adjust(attotime::never);
}
void device_serial_interface::set_tra_rate(const attotime &rate)
{
m_tra_rate = rate/2;
transmit_register_reset();
m_tra_clock->adjust(attotime::never);
}
void device_serial_interface::tra_edge()
{
if (!is_transmit_register_empty())
{
tra_callback();
if (is_transmit_register_empty())
tra_complete();
}
if (is_transmit_register_empty() && !m_tra_rate.is_never())
{
m_tra_clock->adjust(attotime::never);
}
}
void device_serial_interface::rcv_edge()
{
rcv_callback();
if(is_receive_register_full())
{
m_rcv_clock->adjust(attotime::never);
rcv_complete();
}
}
WRITE_LINE_MEMBER(device_serial_interface::tx_clock_w)
{
if(state != m_tra_clock_state) {
m_tra_clock_state = state;
if(m_tra_clock_state)
tra_edge();
}
}
WRITE_LINE_MEMBER(device_serial_interface::rx_clock_w)
{
if(state != m_rcv_clock_state) {
m_rcv_clock_state = state;
if(!m_rcv_clock_state)
rcv_edge();
}
}
WRITE_LINE_MEMBER(device_serial_interface::clock_w)
{
tx_clock_w(state);
rx_clock_w(state);
}
void device_serial_interface::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
switch(id) {
case TRA_TIMER_ID: tx_clock_w(!m_tra_clock_state); break;
case RCV_TIMER_ID: rx_clock_w(!m_rcv_clock_state); break;
}
}
void device_serial_interface::set_data_frame(int start_bit_count, int data_bit_count, parity_t parity, stop_bits_t stop_bits)
{
m_df_word_length = data_bit_count;
switch (stop_bits)
{
case STOP_BITS_0:
default:
m_df_stop_bit_count = 0;
break;
case STOP_BITS_1:
m_df_stop_bit_count = 1;
break;
case STOP_BITS_1_5:
m_df_stop_bit_count = 2; // TODO: support 1.5 stop bits
break;
case STOP_BITS_2:
m_df_stop_bit_count = 2;
break;
}
m_df_parity = parity;
m_df_start_bit_count = start_bit_count;
m_rcv_bit_count = m_df_word_length + m_df_stop_bit_count;
if (m_df_parity != PARITY_NONE)
{
m_rcv_bit_count++;
}
}
void device_serial_interface::receive_register_reset()
{
m_rcv_bit_count_received = 0;
m_rcv_flags &=~RECEIVE_REGISTER_FULL;
if (m_df_start_bit_count == 0)
{
m_rcv_flags |= RECEIVE_REGISTER_SYNCHRONISED;
m_rcv_flags &=~RECEIVE_REGISTER_WAITING_FOR_START_BIT;
}
else
{
m_rcv_flags &=~RECEIVE_REGISTER_SYNCHRONISED;
m_rcv_flags |= RECEIVE_REGISTER_WAITING_FOR_START_BIT;
}
}
WRITE_LINE_MEMBER(device_serial_interface::rx_w)
{
m_rcv_line = state;
if(m_rcv_flags & RECEIVE_REGISTER_SYNCHRONISED)
return;
receive_register_update_bit(state);
if(m_rcv_flags & RECEIVE_REGISTER_SYNCHRONISED)
{
if(m_rcv_clock && !(m_rcv_rate.is_never()))
// make start delay just a bit longer to make sure we are called after the sender
m_rcv_clock->adjust(((m_rcv_rate*3)/2), 0, m_rcv_rate);
else if(m_start_bit_hack_for_external_clocks)
m_rcv_bit_count_received--;
}
return;
}
/* this is generic code to be used in serial chip implementations */
/* the majority of serial chips work in the same way and this code will work */
/* for them */
/* receive a bit */
void device_serial_interface::receive_register_update_bit(int bit)
{
int previous_bit;
//LOG(("receive register receive bit: %1x\n",bit));
previous_bit = (m_rcv_register_data & 0x8000) ? 1 : 0;
/* shift previous bit 7 out */
m_rcv_register_data = m_rcv_register_data>>1;
/* shift new bit in */
m_rcv_register_data = (m_rcv_register_data & 0x7fff) | (bit<<15);
/* update bit count received */
/* asynchronous mode */
if (m_rcv_flags & RECEIVE_REGISTER_WAITING_FOR_START_BIT)
{
/* the previous bit is stored in uart.receive char bit 0 */
/* has the bit state changed? */
if (((previous_bit ^ bit) & 0x01)!=0)
{
/* yes */
if (bit==0)
{
//logerror("receive register saw start bit\n");
/* seen start bit! */
/* not waiting for start bit now! */
m_rcv_flags &=~RECEIVE_REGISTER_WAITING_FOR_START_BIT;
m_rcv_flags |=RECEIVE_REGISTER_SYNCHRONISED;
/* reset bit count received */
m_rcv_bit_count_received = 0;
m_rcv_framing_error = false;
m_rcv_parity_error = false;
}
}
}
else
if (m_rcv_flags & RECEIVE_REGISTER_SYNCHRONISED)
{
m_rcv_bit_count_received++;
if (!bit && (m_rcv_bit_count_received > (m_rcv_bit_count - m_df_stop_bit_count)))
{
m_rcv_framing_error = true;
}
/* received all bits? */
if (m_rcv_bit_count_received==m_rcv_bit_count)
{
m_rcv_bit_count_received = 0;
m_rcv_flags &=~RECEIVE_REGISTER_SYNCHRONISED;
m_rcv_flags |= RECEIVE_REGISTER_WAITING_FOR_START_BIT;
//logerror("receive register full\n");
m_rcv_flags |= RECEIVE_REGISTER_FULL;
}
}
}
void device_serial_interface::receive_register_extract()
{
uint8_t data;
receive_register_reset();
/* strip off stop bits and parity */
assert(m_rcv_bit_count >0 && m_rcv_bit_count <= 16);
data = m_rcv_register_data>>(16-m_rcv_bit_count);
/* mask off other bits so data byte has 0's in unused bits */
data &= ~(0xff<<m_df_word_length);
m_rcv_byte_received = data;
if(m_df_parity == PARITY_NONE)
return;
//unsigned char computed_parity;
//unsigned char parity_received;
/* get state of parity bit received */
//parity_received = (m_rcv_register_data>>m_df_word length) & 0x01;
/* parity enable? */
switch (m_df_parity)
{
/* check parity */
case PARITY_ODD:
case PARITY_EVEN:
{
/* compute parity for received bits */
//computed_parity = serial_helper_get_parity(data);
if (m_df_parity == PARITY_ODD)
{
/* odd parity */
}
else
{
/* even parity */
}
}
break;
case PARITY_MARK:
case PARITY_SPACE:
//computed_parity = parity_received;
break;
}
}
/***** TRANSMIT REGISTER *****/
void device_serial_interface::transmit_register_reset()
{
m_tra_flags |=TRANSMIT_REGISTER_EMPTY;
}
/* used to construct data in stream format */
void device_serial_interface::transmit_register_add_bit(int bit)
{
/* combine bit */
m_tra_register_data = m_tra_register_data<<1;
m_tra_register_data &=~1;
m_tra_register_data|=(bit & 0x01);
m_tra_bit_count++;
}
/* generate data in stream format ready for transfer */
void device_serial_interface::transmit_register_setup(uint8_t data_byte)
{
int i;
unsigned char transmit_data;
if(m_tra_clock && !m_tra_rate.is_never())
m_tra_clock->adjust(m_tra_rate, 0, m_tra_rate);
m_tra_bit_count_transmitted = 0;
m_tra_bit_count = 0;
m_tra_flags &=~TRANSMIT_REGISTER_EMPTY;
/* start bit */
for (i=0; i<m_df_start_bit_count; i++)
{
transmit_register_add_bit(0);
}
/* data bits */
transmit_data = data_byte;
for (i=0; i<m_df_word_length; i++)
{
int databit;
/* get bit from data */
databit = transmit_data & 0x01;
/* add bit to formatted byte */
transmit_register_add_bit(databit);
transmit_data = transmit_data>>1;
}
/* parity */
if (m_df_parity!=PARITY_NONE)
{
/* odd or even parity */
unsigned char parity = 0;
switch(m_df_parity)
{
case PARITY_EVEN:
case PARITY_ODD:
/* get parity */
/* if parity = 0, data has even parity - i.e. there is an even number of one bits in the data */
/* if parity = 1, data has odd parity - i.e. there is an odd number of one bits in the data */
parity = serial_helper_get_parity(data_byte);
break;
case PARITY_MARK:
parity = 1;
break;
case PARITY_SPACE:
parity = 0;
break;
}
transmit_register_add_bit(parity);
}
/* 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);
}
}
/* get a bit from the transmit register */
uint8_t device_serial_interface::transmit_register_get_data_bit()
{
int bit;
bit = (m_tra_register_data>>(m_tra_bit_count-1-m_tra_bit_count_transmitted))&1;
m_tra_bit_count_transmitted++;
/* have all bits of this stream formatted byte been sent? */
if (m_tra_bit_count_transmitted==m_tra_bit_count)
{
/* yes - generate a new byte to send */
m_tra_flags |= TRANSMIT_REGISTER_EMPTY;
}
return bit;
}
bool device_serial_interface::is_receive_register_full()
{
return (m_rcv_flags & RECEIVE_REGISTER_FULL)!=0;
}
bool device_serial_interface::is_transmit_register_empty()
{
return (m_tra_flags & TRANSMIT_REGISTER_EMPTY)!=0;
}
const char *device_serial_interface::parity_tostring(parity_t parity)
{
switch (parity)
{
case PARITY_NONE:
return "NONE";
case PARITY_ODD:
return "ODD";
case PARITY_EVEN:
return "EVEN";
case PARITY_MARK:
return "MARK";
case PARITY_SPACE:
return "SPACE";
default:
return "UNKNOWN";
}
}
const char *device_serial_interface::stop_bits_tostring(stop_bits_t stop_bits)
{
switch (stop_bits)
{
case STOP_BITS_0:
return "0";
case STOP_BITS_1:
return "1";
case STOP_BITS_1_5:
return "1.5";
case STOP_BITS_2:
return "2";
default:
return "UNKNOWN";
}
}