diff options
Diffstat (limited to 'src/devices/machine/ay31015.cpp')
-rw-r--r-- | src/devices/machine/ay31015.cpp | 753 |
1 files changed, 753 insertions, 0 deletions
diff --git a/src/devices/machine/ay31015.cpp b/src/devices/machine/ay31015.cpp new file mode 100644 index 00000000000..2ecd3f311f7 --- /dev/null +++ b/src/devices/machine/ay31015.cpp @@ -0,0 +1,753 @@ +// license:BSD-3-Clause +// copyright-holders:Robbbert +/**************************************************************************** + + ay31015.c by Robbbert, May 2008. Bugs fixed by Judge. + + Code for the AY-3-1014A, AY-3-1015(D), AY-5-1013(A), and AY-6-1013 UARTs + The HD6402 UART is compatible with the AY-3-1015 UART. + + This is cycle-accurate according to the specifications. + + It supports independent receive and transmit clocks, + and transmission and reception can occur simultaneously if desired. + +***************************************************************************** + +Differences between the chip types: +- All units have pull-up resistors on the inputs, except for the AY-3-1014A which is CMOS-compatible. +- AY-3-1014A and AY-3-1015 - 1.5 stop bits mode available. +- Max baud rate of 30k, except AY-5-1013 which has 20k. +- AY-5-1013 has extended temperature ratings. +- AY-5-1013 and AY-6-1013 require a -12 volt supply on pin 2. Pin is not used otherwise. +- AY-5-1013 and AY-6-1013 do not reset the received data register when XR pin is used. + +****************************************************************************** + +It is not clear in the documentation as to which settings will reset the device. + To be safe, we will always reset whenever the control register changes. + + Also, it not clear exactly what happens under various error conditions. + +******************************************************************************** + +Device Data: + +* Common Controls: +-- Pin 1 - Vcc - 5 volts +-- Pin 2 - not used (on AY-5-1013 and AY-6-1013 this is Voo = -12 volts) +-- Pin 3 - Gnd - 0 volts +-- Pin 21 - XR - External Reset - resets all registers to initial state except for the control register +-- Pin 35 - NP - No Parity - "1" will kill any parity processing +-- Pin 36 - TSB - Number of Stop Bits - "0" = 1 stop bit; "1" = 2 stop bits. If "1", and 5 bits per character, then we have 1.5 stop bits +-- pin 37 - NB1 +-- pin 38 - NB2 - Number of bits per character = NB1 + (NB2 * 2) + 5 +-- pin 39 - EPS - Odd or Even Parity Select - "0" = Odd parity; "1" = Even parity. Has no effect if NP is high. +-- Pin 34 - CS - Control Strobe - Read NP, TSB, EPS, NB1, NB2 into the control register. + +Format of data stream: +Start bit (low), Bit 0, Bit 1... highest bit, Parity bit (if enabled), 1-2 stop bits (high) + + +* Receiver Controls: +-- Pin 17 - RCP - Clock which is 16x the desired baud rate +-- Pin 20 - SI - Serial input stream - "1" = Mark (waiting for input), "0" = Space (Start bit) initiates the transfer of a byte +-- Pin 4 - RDE - "0" causes the received data to appear on RD1 to RD8. +-- Pins 5 to 12 - RD8 to RD1 - These are the data lines (bits 7 to 0). Data is right-justified. +-- Pin 16 - SWE - Status word enable - causes the status bits (PE, FE, OR, DAV, TBMT) to appear at the pins. +-- Pin 19 - DAV - "1" indicates that a byte has been received by the UART, and should now be accepted by the computer +-- Pin 18 - RDAV - "0" will force DAV low. +-- Pin 13 - PE - Parity error - "1" indicates that a parity error occurred +-- Pin 14 - FE - Framing error - "1" Indicates that the stop bit was missing +-- Pin 15 - OR - overrun - "1" indicates that a new character has become available before the computer had accepted the previous character + +* Transmitter controls: +-- Pin 40 - TCP - Clock which is 16x the desired baud rate +-- Pin 25 - SO - Serial output stream - it will stay at "1" while no data is being transmitted +-- Pins 26 to 33 - DB1 to DB8 - These are the data lines containing the byte to be sent +-- Pin 23 - DS - Data Strobe - "0" will copy DB1 to DB8 into the transmit buffer +-- Pin 22 - TBMT - Transmit buffer Empty - "1" indicates to the computer that another byte may be sent to the UART +-- Pin 24 - EOC - End of Character - "0" means that a character is being sent. + +******************************************* COMMON CONTROLS ********************************************************/ + +#include "emu.h" +#include "ay31015.h" + + + +/* control reg */ +#define CONTROL_NB1 0x01 +#define CONTROL_NB2 0x02 +#define CONTROL_TSB 0x04 +#define CONTROL_EPS 0x08 +#define CONTROL_NP 0x10 + + +/* status reg */ +#define STATUS_TBMT 0x01 +#define STATUS_DAV 0x02 +#define STATUS_OR 0x04 +#define STATUS_FE 0x08 +#define STATUS_PE 0x10 +#define STATUS_EOC 0x20 + + + +const device_type AY31015 = &device_creator<ay31015_device>; +const device_type AY51013 = &device_creator<ay51013_device>; + +ay31015_device::ay31015_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source) + : device_t(mconfig, type, name, tag, owner, clock, shortname, source), + m_control_reg(0), + m_status_reg(0), + m_second_stop_bit(0), + m_total_pulses(0), + m_internal_sample(0), + m_rx_data(0), + m_rx_buffer(0), + m_rx_bit_count(0), + m_rx_parity(0), + m_rx_pulses(0), + m_rx_clock(0), + m_rx_timer(NULL), + m_tx_data(0), + m_tx_buffer(0), + m_tx_parity(0), + m_tx_pulses(0), + m_tx_clock(0), + m_tx_timer(NULL), + m_read_si_cb(*this), + m_write_so_cb(*this), + m_status_changed_cb(*this) +{ + for (int i = 0; i < 41; i++) + m_pins[i] = 0; +} + +ay31015_device::ay31015_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, AY31015, "AY-3-1015", tag, owner, clock, "ay31015", __FILE__), + m_control_reg(0), + m_status_reg(0), + m_second_stop_bit(0), + m_total_pulses(0), + m_internal_sample(0), + m_rx_data(0), + m_rx_buffer(0), + m_rx_bit_count(0), + m_rx_parity(0), + m_rx_pulses(0), + m_rx_clock(0), + m_rx_timer(NULL), + m_tx_data(0), + m_tx_buffer(0), + m_tx_parity(0), + m_tx_pulses(0), + m_tx_clock(0), + m_tx_timer(NULL), + m_read_si_cb(*this), + m_write_so_cb(*this), + m_status_changed_cb(*this) +{ + for (int i = 0; i < 41; i++) + m_pins[i] = 0; +} + +ay51013_device::ay51013_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : ay31015_device(mconfig, AY31015, "AY-5-1013", tag, owner, clock, "ay51013", __FILE__) +{ +} + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void ay31015_device::device_start() +{ + m_read_si_cb.resolve(); + m_write_so_cb.resolve(); + m_status_changed_cb.resolve(); + + m_rx_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(ay31015_device::rx_process),this)); + m_tx_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(ay31015_device::tx_process),this)); + + update_rx_timer(); + update_tx_timer(); + + save_item(NAME(m_pins)); + save_item(NAME(m_control_reg)); + save_item(NAME(m_status_reg)); + save_item(NAME(m_second_stop_bit)); + save_item(NAME(m_total_pulses)); + save_item(NAME(m_internal_sample)); + + save_item(NAME(m_rx_state)); + save_item(NAME(m_rx_data)); + save_item(NAME(m_rx_buffer)); + save_item(NAME(m_rx_bit_count)); + save_item(NAME(m_rx_parity)); + save_item(NAME(m_rx_pulses)); + save_item(NAME(m_rx_clock)); + + save_item(NAME(m_tx_state)); + save_item(NAME(m_tx_data)); + save_item(NAME(m_tx_buffer)); + save_item(NAME(m_tx_parity)); + save_item(NAME(m_tx_pulses)); + save_item(NAME(m_tx_clock)); +} + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void ay31015_device::device_reset() +{ + m_control_reg = 0; + m_rx_data = 0; + + internal_reset(); +} + + +inline UINT8 ay31015_device::get_si() +{ + if (!m_read_si_cb.isnull()) + m_pins[AY31015_SI] = m_read_si_cb(0) ? 1 : 0; + + return m_pins[AY31015_SI]; +} + + +inline void ay31015_device::set_so( int data ) +{ + m_pins[AY31015_SO] = data ? 1 : 0; + + if (!m_write_so_cb.isnull()) + m_write_so_cb((offs_t)0, m_pins[AY31015_SO]); +} + + +inline int ay31015_device::update_status_pin( UINT8 reg_bit, ay31015_output_pin_t pin ) +{ + int new_value = (m_status_reg & reg_bit) ? 1 : 0; + + if (new_value == m_pins[pin]) + return 0; + + m_pins[pin] = new_value; + return 1; +} + + +/*------------------------------------------------- + ay31015_update_status_pins - Update the status pins +-------------------------------------------------*/ + +void ay31015_device::update_status_pins() +{ + int status_pins_changed = 0; + + /* Should status pins be updated? */ + if (!m_pins[AY31015_SWE]) + { + status_pins_changed += update_status_pin(STATUS_PE, AY31015_PE); + status_pins_changed += update_status_pin(STATUS_FE, AY31015_FE); + status_pins_changed += update_status_pin(STATUS_OR, AY31015_OR); + status_pins_changed += update_status_pin(STATUS_DAV, AY31015_DAV); + status_pins_changed += update_status_pin(STATUS_TBMT, AY31015_TBMT); + } + status_pins_changed += update_status_pin(STATUS_EOC, AY31015_EOC); + + if (status_pins_changed && !m_status_changed_cb.isnull()) + { + m_status_changed_cb((offs_t)0, status_pins_changed); + } +} + + +/*************************************************** RECEIVE CONTROLS *************************************************/ + + +/*------------------------------------------------- + ay31015_rx_process - convert serial to parallel +-------------------------------------------------*/ + +TIMER_CALLBACK_MEMBER( ay31015_device::rx_process ) +{ + switch (m_rx_state) + { + case PREP_TIME: // assist sync by ensuring high bit occurs + m_rx_pulses--; + if (get_si()) + m_rx_state = IDLE; + return; + + case IDLE: + m_rx_pulses--; + if (!get_si()) + { + m_rx_state = START_BIT; + m_rx_pulses = 16; + } + return; + + case START_BIT: + m_rx_pulses--; + if (m_rx_pulses == 8) // start bit must be low at sample time + { + if (get_si()) + m_rx_state = IDLE; + } + else + if (!m_rx_pulses) // end of start bit + { + m_rx_state = PROCESSING; + m_rx_pulses = m_total_pulses; + m_rx_bit_count = 0; + m_rx_parity = 0; + m_rx_data = 0; + } + return; + + case PROCESSING: + m_rx_pulses--; + if (!m_rx_pulses) // end of a byte + { + m_rx_pulses = 16; + if (m_control_reg & CONTROL_NP) // see if we need to get a parity bit + m_rx_state = FIRST_STOP_BIT; + else + m_rx_state = PARITY_BIT; + } + else + if (!(m_rx_pulses & 15)) // end of a bit + m_rx_bit_count++; + else + if ((m_rx_pulses & 15) == 8) // sample input stream + { + m_internal_sample = get_si(); + m_rx_parity ^= m_internal_sample; // calculate cumulative parity + m_rx_data |= m_internal_sample << m_rx_bit_count; + } + return; + + case PARITY_BIT: + m_rx_pulses--; + + if (m_rx_pulses == 8) // sample input stream + { + m_rx_parity ^= get_si(); // calculate cumulative parity + } + else + if (!m_rx_pulses) // end of a byte + { + m_rx_pulses = 16; + m_rx_state = FIRST_STOP_BIT; + + if ((!(m_control_reg & CONTROL_EPS)) && (m_rx_parity)) + m_rx_parity = 0; // odd parity, ok + else + if ((m_control_reg & CONTROL_EPS) && (!m_rx_parity)) + m_rx_parity = 0; // even parity, ok + else + m_rx_parity = 1; // parity error + } + return; + + case FIRST_STOP_BIT: + m_rx_pulses--; + if (m_rx_pulses == 8) // sample input stream + m_internal_sample = get_si(); + else + if (m_rx_pulses == 7) // set error flags + { + if (!m_internal_sample) + { + m_status_reg |= STATUS_FE; // framing error - the stop bit not high + m_rx_state = PREP_TIME; // lost sync - start over + // return; + } + else + m_status_reg &= ~STATUS_FE; + + if ((m_rx_parity) && (!(m_control_reg & CONTROL_NP))) + m_status_reg |= STATUS_PE; // parity error + else + m_status_reg &= ~STATUS_PE; + + if (m_status_reg & STATUS_DAV) + m_status_reg |= STATUS_OR; // overrun error - previous byte still in buffer + else + m_status_reg &= ~STATUS_OR; + + m_rx_buffer = m_rx_data; // bring received byte out for computer to read + + update_status_pins(); + } + else + if (m_rx_pulses == 6) + { + m_status_reg |= STATUS_DAV; // tell computer that new byte is ready + update_status_pins(); + } + else + if (m_rx_pulses == 4) + { + if (m_second_stop_bit) + { + /* We should wait for the full first stop bit and + the beginning of the second stop bit */ + m_rx_state = SECOND_STOP_BIT; + m_rx_pulses += m_second_stop_bit - 7; + } + else + { + /* We have seen a STOP bit, go back to PREP_TIME */ + m_rx_state = PREP_TIME; + } + } + return; + + case SECOND_STOP_BIT: + m_rx_pulses--; + if (!m_rx_pulses) + m_rx_state = PREP_TIME; + return; + + } +} + + +/*************************************************** TRANSMIT CONTROLS *************************************************/ + + +/*------------------------------------------------- + ay31015_tx_process - convert parallel to serial +-------------------------------------------------*/ + +TIMER_CALLBACK_MEMBER( ay31015_device::tx_process ) +{ + UINT8 t1; + switch (m_tx_state) + { + case IDLE: + if (!(m_status_reg & STATUS_TBMT)) + { + m_tx_state = PREP_TIME; // When idle, see if a byte has been sent to us + m_tx_pulses = 1; + } + return; + + case PREP_TIME: // This phase lets the transmitter regain sync after an idle period + m_tx_pulses--; + if (!m_tx_pulses) + { + m_tx_state = START_BIT; + m_tx_pulses = 16; + } + return; + + case START_BIT: + if (m_tx_pulses == 16) // beginning of start bit + { + m_tx_data = m_tx_buffer; // load the shift register + m_status_reg |= STATUS_TBMT; // tell computer that another byte can be sent to uart + set_so(0); /* start bit begins now (we are "spacing") */ + m_status_reg &= ~STATUS_EOC; // we are no longer idle + m_tx_parity = 0; + update_status_pins(); + } + + m_tx_pulses--; + if (!m_tx_pulses) // end of start bit + { + m_tx_state = PROCESSING; + m_tx_pulses = m_total_pulses; + } + return; + + case PROCESSING: + if (!(m_tx_pulses & 15)) // beginning of a data bit + { + if (m_tx_data & 1) + { + set_so(1); + m_tx_parity++; // calculate cumulative parity + } + else + set_so(0); + + m_tx_data >>= 1; // adjust the shift register + } + + m_tx_pulses--; + if (!m_tx_pulses) // all data bits sent + { + m_tx_pulses = 16; + if (m_control_reg & CONTROL_NP) // see if we need to make a parity bit + m_tx_state = FIRST_STOP_BIT; + else + m_tx_state = PARITY_BIT; + } + + return; + + case PARITY_BIT: + if (m_tx_pulses == 16) + { + t1 = (m_control_reg & CONTROL_EPS) ? 0 : 1; + t1 ^= (m_tx_parity & 1); + if (t1) + set_so(1); /* extra bit to set the correct parity */ + else + set_so(0); /* it was already correct */ + } + + m_tx_pulses--; + if (!m_tx_pulses) + { + m_tx_state = FIRST_STOP_BIT; + m_tx_pulses = 16; + } + return; + + case FIRST_STOP_BIT: + if (m_tx_pulses == 16) + set_so(1); /* create a stop bit (marking and soon idle) */ + m_tx_pulses--; + if (!m_tx_pulses) + { + m_status_reg |= STATUS_EOC; // character is completely sent + if (m_second_stop_bit) + { + m_tx_state = SECOND_STOP_BIT; + m_tx_pulses = m_second_stop_bit; + } + else + if (m_status_reg & STATUS_TBMT) + m_tx_state = IDLE; // if nothing to send, go idle + else + { + m_tx_pulses = 16; + m_tx_state = START_BIT; // otherwise immediately start next byte + } + update_status_pins(); + } + return; + + case SECOND_STOP_BIT: + m_tx_pulses--; + if (!m_tx_pulses) + { + if (m_status_reg & STATUS_TBMT) + m_tx_state = IDLE; // if nothing to send, go idle + else + { + m_tx_pulses = 16; + m_tx_state = START_BIT; // otherwise immediately start next byte + } + } + return; + + } +} + + +/*------------------------------------------------- + ay31015_reset - reset internal state +-------------------------------------------------*/ + +void ay31015_device::internal_reset() +{ + /* total pulses = 16 * data-bits */ + UINT8 t1; + + if (m_control_reg & CONTROL_NB2) + t1 = (m_control_reg & CONTROL_NB1) ? 8 : 7; + else + t1 = (m_control_reg & CONTROL_NB1) ? 6 : 5; + + m_total_pulses = t1 << 4; /* total clock pulses to load a byte */ + m_second_stop_bit = ((m_control_reg & CONTROL_TSB) ? 16 : 0); /* 2nd stop bit */ + if ((t1 == 5) && (m_second_stop_bit == 16)) + m_second_stop_bit = 8; /* 5 data bits and 2 stop bits = 1.5 stop bits */ + m_status_reg = STATUS_EOC | STATUS_TBMT; + m_tx_data = 0; + m_rx_state = PREP_TIME; + m_tx_state = IDLE; + m_pins[AY31015_SI] = 1; + set_so(1); + + m_rx_data = 0; +} + + +void ay51013_device::internal_reset() +{ + /* total pulses = 16 * data-bits */ + UINT8 t1; + + if (m_control_reg & CONTROL_NB2) + t1 = (m_control_reg & CONTROL_NB1) ? 8 : 7; + else + t1 = (m_control_reg & CONTROL_NB1) ? 6 : 5; + + m_total_pulses = t1 << 4; /* total clock pulses to load a byte */ + m_second_stop_bit = ((m_control_reg & CONTROL_TSB) ? 16 : 0); /* 2nd stop bit */ + if ((t1 == 5) && (m_second_stop_bit == 16)) + m_second_stop_bit = 8; /* 5 data bits and 2 stop bits = 1.5 stop bits */ + m_status_reg = STATUS_EOC | STATUS_TBMT; + m_tx_data = 0; + m_rx_state = PREP_TIME; + m_tx_state = IDLE; + m_pins[AY31015_SI] = 1; + set_so(1); + // no m_rx_data = 0 in this case +} + +/*------------------------------------------------- + ay31015_transfer_control_pins - transfers contents of controls pins to the control register +-------------------------------------------------*/ + +void ay31015_device::transfer_control_pins() +{ + UINT8 control = 0; + + control |= m_pins[AY31015_NP ] ? CONTROL_NP : 0; + control |= m_pins[AY31015_TSB] ? CONTROL_TSB : 0; + control |= m_pins[AY31015_NB1] ? CONTROL_NB1 : 0; + control |= m_pins[AY31015_NB2] ? CONTROL_NB2 : 0; + control |= m_pins[AY31015_EPS] ? CONTROL_EPS : 0; + + if (m_control_reg != control) + { + m_control_reg = control; + internal_reset(); + } +} + + +/*------------------------------------------------- + ay31015_set_input_pin - set an input pin +-------------------------------------------------*/ +void ay31015_device::set_input_pin( ay31015_input_pin_t pin, int data ) +{ + data = data ? 1 : 0; + + switch (pin) + { + case AY31015_SWE: + m_pins[pin] = data; + update_status_pins(); + break; + case AY31015_RDAV: + m_pins[pin] = data; + if (!data) + { + m_status_reg &= ~STATUS_DAV; + m_pins[AY31015_DAV] = 0; + } + break; + case AY31015_SI: + m_pins[pin] = data; + break; + case AY31015_XR: + m_pins[pin] = data; + if (data) + internal_reset(); + break; + case AY31015_CS: + case AY31015_NP: + case AY31015_TSB: + case AY31015_NB1: + case AY31015_NB2: + case AY31015_EPS: + m_pins[pin] = data; + if (m_pins[AY31015_CS]) + transfer_control_pins(); + break; + } +} + + +/*------------------------------------------------- + ay31015_get_output_pin - get the status of an output pin +-------------------------------------------------*/ + +int ay31015_device::get_output_pin( ay31015_output_pin_t pin ) +{ + return m_pins[pin]; +} + + +inline void ay31015_device::update_rx_timer() +{ + if (m_rx_clock > 0.0) + { + m_rx_timer->adjust(attotime::from_hz(m_rx_clock), 0, attotime::from_hz(m_rx_clock)); + } + else + { + m_rx_timer->enable(0); + } +} + + +inline void ay31015_device::update_tx_timer() +{ + if (m_tx_clock > 0.0) + { + m_tx_timer->adjust(attotime::from_hz(m_tx_clock), 0, attotime::from_hz(m_tx_clock)); + } + else + { + m_tx_timer->enable(0); + } +} + + +/*------------------------------------------------- + ay31015_set_receiver_clock - set receive clock +-------------------------------------------------*/ + +void ay31015_device::set_receiver_clock( double new_clock ) +{ + m_rx_clock = new_clock; + update_rx_timer(); +} + + +/*------------------------------------------------- + ay31015_set_transmitter_clock - set transmit clock +-------------------------------------------------*/ + +void ay31015_device::set_transmitter_clock( double new_clock ) +{ + m_tx_clock = new_clock; + update_tx_timer(); +} + + +/*------------------------------------------------- + ay31015_get_received_data - return a byte to the computer +-------------------------------------------------*/ + +UINT8 ay31015_device::get_received_data() +{ + return m_rx_buffer; +} + + +/*------------------------------------------------- + ay31015_set_transmit_data - accept a byte to transmit, if able +-------------------------------------------------*/ +void ay31015_device::set_transmit_data( UINT8 data ) +{ + if (m_status_reg & STATUS_TBMT) + { + m_tx_buffer = data; + m_status_reg &= ~STATUS_TBMT; + update_status_pins(); + } +} |