mame/src/emu/machine/z80sio.c

901 lines
28 KiB
C

/***************************************************************************
Z80 SIO (Z8440) implementation
Copyright Nicola Salmoria and the MAME Team.
Visit http://mamedev.org for licensing and usage restrictions.
***************************************************************************/
#include "emu.h"
#include "z80sio.h"
#include "cpu/z80/z80.h"
#include "cpu/z80/z80daisy.h"
//**************************************************************************
// DEBUGGING
//**************************************************************************
#define VERBOSE 0
#define VPRINTF(x) do { if (VERBOSE) logerror x; } while (0)
//**************************************************************************
// CONSTANTS
//**************************************************************************
// interrupt states
const int INT_TRANSMIT = 0x00; // not confirmed
const int INT_STATUS = 0x01;
const int INT_RECEIVE = 0x02;
const int INT_ERROR = 0x03;
const int INT_CHB_TRANSMIT = 0 + INT_TRANSMIT;
const int INT_CHB_STATUS = 0 + INT_STATUS;
const int INT_CHB_RECEIVE = 0 + INT_RECEIVE;
const int INT_CHB_ERROR = 0 + INT_ERROR;
const int INT_CHA_TRANSMIT = 4 + INT_TRANSMIT;
const int INT_CHA_STATUS = 4 + INT_STATUS;
const int INT_CHA_RECEIVE = 4 + INT_RECEIVE;
const int INT_CHA_ERROR = 4 + INT_ERROR;
// SIO write register 0
const int SIO_WR0_RESET_MASK = 0xc0; // D7-D6: Reset control
const int SIO_WR0_RESET_NULL = 0x00; // 00 = NULL code
const int SIO_WR0_RESET_RX_CRC = 0x40; // 01 = Reset Rx CRC checker
const int SIO_WR0_RESET_TX_CRC = 0x80; // 10 = Reset Tx CRC generator
const int SIO_WR0_RESET_TX_LATCH = 0xc0; // 11 = Reset Tx Underrun/EOM latch
const int SIO_WR0_COMMAND_MASK = 0x38; // D5-D3: Command
const int SIO_WR0_COMMAND_NULL = 0x00; // 000 = NULL code
const int SIO_WR0_COMMAND_SET_ABORT = 0x08; // 001 = Set abort (SDLC)
const int SIO_WR0_COMMAND_RES_STATUS_INT = 0x10; // 010 = reset ext/status interrupts
const int SIO_WR0_COMMAND_CH_RESET = 0x18; // 011 = Channel reset
const int SIO_WR0_COMMAND_ENA_RX_INT = 0x20; // 100 = Enable int on next Rx character
const int SIO_WR0_COMMAND_RES_TX_INT = 0x28; // 101 = Reset Tx int pending
const int SIO_WR0_COMMAND_RES_ERROR = 0x30; // 110 = Error reset
const int SIO_WR0_COMMAND_RETI = 0x38; // 111 = Return from int (CH-A only)
const int SIO_WR0_REGISTER_MASK = 0x07; // D2-D0: Register select (0-7)
// SIO write register 1
const int SIO_WR1_READY_WAIT_ENA = 0x80; // D7 = READY/WAIT enable
const int SIO_WR1_READY_WAIT_FUNCTION = 0x40; // D6 = READY/WAIT function
const int SIO_WR1_READY_WAIT_ON_RT = 0x20; // D5 = READY/WAIT on R/T
const int SIO_WR1_RXINT_MASK = 0x18; // D4-D3 = Rx int control
const int SIO_WR1_RXINT_DISABLE = 0x00; // 00 = Rx int disable
const int SIO_WR1_RXINT_FIRST = 0x08; // 01 = Rx int on first character
const int SIO_WR1_RXINT_ALL_PARITY = 0x10; // 10 = int on all Rx characters (parity affects vector)
const int SIO_WR1_RXINT_ALL_NOPARITY = 0x18; // 11 = int on all Rx characters (parity ignored)
const int SIO_WR1_STATUS_AFFECTS_VECTOR = 0x04; // D2 = Status affects vector (CH-B only)
const int SIO_WR1_TXINT_ENABLE = 0x02; // D1 = Tx int enable
const int SIO_WR1_STATUSINT_ENABLE = 0x01; // D0 = Ext int enable
// SIO write register 2 (CH-B only)
const int SIO_WR2_INT_VECTOR_MASK = 0xff; // D7-D0 = interrupt vector
// SIO write register 3
const int SIO_WR3_RX_DATABITS_MASK = 0xc0; // D7-D6 = Rx Data bits
const int SIO_WR3_RX_DATABITS_5 = 0x00; // 00 = Rx 5 bits/character
const int SIO_WR3_RX_DATABITS_7 = 0x40; // 01 = Rx 7 bits/character
const int SIO_WR3_RX_DATABITS_6 = 0x80; // 10 = Rx 6 bits/character
const int SIO_WR3_RX_DATABITS_8 = 0xc0; // 11 = Rx 8 bits/character
const int SIO_WR3_AUTO_ENABLES = 0x20; // D5 = Auto enables
const int SIO_WR3_ENTER_HUNT_PHASE = 0x10; // D4 = Enter hunt phase
const int SIO_WR3_RX_CRC_ENABLE = 0x08; // D3 = Rx CRC enable
const int SIO_WR3_ADDR_SEARCH_MODE = 0x04; // D2 = Address search mode (SDLC)
const int SIO_WR3_SYNC_LOAD_INHIBIT = 0x02; // D1 = Sync character load inhibit
const int SIO_WR3_RX_ENABLE = 0x01; // D0 = Rx enable
// SIO write register 4
const int SIO_WR4_CLOCK_MODE_MASK = 0xc0; // D7-D6 = Clock mode
const int SIO_WR4_CLOCK_MODE_x1 = 0x00; // 00 = x1 clock mode
const int SIO_WR4_CLOCK_MODE_x16 = 0x40; // 01 = x16 clock mode
const int SIO_WR4_CLOCK_MODE_x32 = 0x80; // 10 = x32 clock mode
const int SIO_WR4_CLOCK_MODE_x64 = 0xc0; // 11 = x64 clock mode
const int SIO_WR4_SYNC_MODE_MASK = 0x30; // D5-D4 = Sync mode
const int SIO_WR4_SYNC_MODE_8BIT = 0x00; // 00 = 8 bit sync character
const int SIO_WR4_SYNC_MODE_16BIT = 0x10; // 01 = 16 bit sync character
const int SIO_WR4_SYNC_MODE_SDLC = 0x20; // 10 = SDLC mode (01111110 flag)
const int SIO_WR4_SYNC_MODE_EXTERNAL = 0x30; // 11 = External sync mode
const int SIO_WR4_STOPBITS_MASK = 0x0c; // D3-D2 = Stop bits
const int SIO_WR4_STOPBITS_SYNC = 0x00; // 00 = Sync modes enable
const int SIO_WR4_STOPBITS_1 = 0x04; // 01 = 1 stop bit/character
const int SIO_WR4_STOPBITS_15 = 0x08; // 10 = 1.5 stop bits/character
const int SIO_WR4_STOPBITS_2 = 0x0c; // 11 = 2 stop bits/character
const int SIO_WR4_PARITY_EVEN = 0x02; // D1 = Parity even/odd
const int SIO_WR4_PARITY_ENABLE = 0x01; // D0 = Parity enable
// SIO write register 5
const int SIO_WR5_DTR = 0x80; // D7 = DTR
const int SIO_WR5_TX_DATABITS_MASK = 0x60; // D6-D5 = Tx Data bits
const int SIO_WR5_TX_DATABITS_5 = 0x00; // 00 = Tx 5 bits/character
const int SIO_WR5_TX_DATABITS_7 = 0x20; // 01 = Tx 7 bits/character
const int SIO_WR5_TX_DATABITS_6 = 0x40; // 10 = Tx 6 bits/character
const int SIO_WR5_TX_DATABITS_8 = 0x60; // 11 = Tx 8 bits/character
const int SIO_WR5_SEND_BREAK = 0x10; // D4 = Send break
const int SIO_WR5_TX_ENABLE = 0x08; // D3 = Tx Enable
const int SIO_WR5_CRC16_SDLC = 0x04; // D2 = CRC-16/SDLC
const int SIO_WR5_RTS = 0x02; // D1 = RTS
const int SIO_WR5_TX_CRC_ENABLE = 0x01; // D0 = Tx CRC enable
// SIO write register 6
const int SIO_WR6_SYNC_7_0_MASK = 0xff; // D7-D0 = Sync bits 7-0
// SIO write register 7
const int SIO_WR7_SYNC_15_8_MASK = 0xff; // D7-D0 = Sync bits 15-8
// SIO read register 0
const int SIO_RR0_BREAK_ABORT = 0x80; // D7 = Break/abort
const int SIO_RR0_TX_UNDERRUN = 0x40; // D6 = Tx underrun/EOM
const int SIO_RR0_CTS = 0x20; // D5 = CTS
const int SIO_RR0_SYNC_HUNT = 0x10; // D4 = Sync/hunt
const int SIO_RR0_DCD = 0x08; // D3 = DCD
const int SIO_RR0_TX_BUFFER_EMPTY = 0x04; // D2 = Tx buffer empty
const int SIO_RR0_INT_PENDING = 0x02; // D1 = int pending (CH-A only)
const int SIO_RR0_RX_CHAR_AVAILABLE = 0x01; // D0 = Rx character available
// SIO read register 1
const int SIO_RR1_END_OF_FRAME = 0x80; // D7 = End of frame (SDLC)
const int SIO_RR1_CRC_FRAMING_ERROR = 0x40; // D6 = CRC/Framing error
const int SIO_RR1_RX_OVERRUN_ERROR = 0x20; // D5 = Rx overrun error
const int SIO_RR1_PARITY_ERROR = 0x10; // D4 = Parity error
const int SIO_RR1_IFIELD_BITS_MASK = 0x0e; // D3-D1 = I field bits
// 100 = 0 in prev, 3 in 2nd prev
// 010 = 0 in prev, 4 in 2nd prev
// 110 = 0 in prev, 5 in 2nd prev
// 001 = 0 in prev, 6 in 2nd prev
// 101 = 0 in prev, 7 in 2nd prev
// 011 = 0 in prev, 8 in 2nd prev
// 111 = 1 in prev, 8 in 2nd prev
// 000 = 2 in prev, 8 in 2nd prev
const int SIO_RR1_ALL_SENT = 0x01; // D0 = All sent
// SIO read register 2 (CH-B only)
const int SIO_RR2_VECTOR_MASK = 0xff; // D7-D0 = Interrupt vector
const UINT8 z80sio_device::k_int_priority[] =
{
INT_CHA_RECEIVE,
INT_CHA_TRANSMIT,
INT_CHA_STATUS,
INT_CHA_ERROR,
INT_CHB_RECEIVE,
INT_CHB_TRANSMIT,
INT_CHB_STATUS,
INT_CHB_ERROR
};
/***************************************************************************
FUNCTION PROTOTYPES
***************************************************************************/
/*
Interrupt priorities:
Ch A receive
Ch A transmit
Ch A external/status
Ch B receive
Ch B transmit
Ch B external/status
Initial configuration (both channels):
005D:sio_reg_w(0,4) = 44
01 = x16 clock mode
00 = 8 bit sync character
01 = 1 stop bit/character
Parity odd
Parity disabled
005D:sio_reg_w(0,3) = C1
11 = Rx 8 bits/character
No auto enables
No enter hunt phase
No Rx CRC enable
No address search mode
No sync character load inhibit
Rx enable
005D:sio_reg_w(0,5) = 68
DTR = 0
11 = Tx 8 bits/character
No send break
Tx enable
SDLC
No RTS
No CRC enable
005D:sio_reg_w(0,2) = 40
Vector = 0x40
005D:sio_reg_w(0,1) = 1D
No READY/WAIT
No READY/WAIT function
No READY/WAIT on R/T
11 = int on all Rx characters (parity ignored)
Status affects vector
No Tx int enable
Ext int enable
*/
//**************************************************************************
// INLINE FUNCTIONS
//**************************************************************************
//-------------------------------------------------
// update_interrupt_state - update the interrupt
// state to the external world
//-------------------------------------------------
inline void z80sio_device::update_interrupt_state()
{
// if we have a callback, update it with the current state
if (m_config.m_irq_cb != NULL)
(*m_config.m_irq_cb)(this, (z80daisy_irq_state() & Z80_DAISY_INT) ? ASSERT_LINE : CLEAR_LINE);
}
//-------------------------------------------------
// set_interrupt - set the given interrupt state
// on this channel and update the overall device
// state
//-------------------------------------------------
inline void z80sio_device::sio_channel::set_interrupt(int type)
{
int inum = (this == &m_device->m_channel[0] ? 4 : 0) + type;
m_device->m_int_state[inum] = Z80_DAISY_INT;
m_device->update_interrupt_state();
}
//-------------------------------------------------
// clear_interrupt - clear the given interrupt
// state on this channel and update the overall
// device state
//-------------------------------------------------
inline void z80sio_device::sio_channel::clear_interrupt(int type)
{
int inum = (this == &m_device->m_channel[0] ? 4 : 0) + type;
m_device->m_int_state[inum] &= ~Z80_DAISY_INT;
m_device->update_interrupt_state();
}
//-------------------------------------------------
// compute_time_per_character - compute the
// serial clocking period
//-------------------------------------------------
inline attotime z80sio_device::sio_channel::compute_time_per_character()
{
// fix me -- should compute properly and include data, stop, parity bit
return attotime_mul(ATTOTIME_IN_HZ(9600), 10);
}
//**************************************************************************
// DEVICE CONFIGURATION
//**************************************************************************
//-------------------------------------------------
// z80sio_device_config - constructor
//-------------------------------------------------
z80sio_device_config::z80sio_device_config(const machine_config &mconfig, const char *tag, const device_config *owner, UINT32 clock)
: device_config(mconfig, static_alloc_device_config, "Zilog Z80 SIO", tag, owner, clock),
device_config_z80daisy_interface(mconfig, *this)
{
}
//-------------------------------------------------
// static_alloc_device_config - allocate a new
// configuration object
//-------------------------------------------------
device_config *z80sio_device_config::static_alloc_device_config(const machine_config &mconfig, const char *tag, const device_config *owner, UINT32 clock)
{
return global_alloc(z80sio_device_config(mconfig, tag, owner, clock));
}
//-------------------------------------------------
// alloc_device - allocate a new device object
//-------------------------------------------------
device_t *z80sio_device_config::alloc_device(running_machine &machine) const
{
return auto_alloc(&machine, z80sio_device(machine, *this));
}
//-------------------------------------------------
// device_config_complete - perform any
// operations now that the configuration is
// complete
//-------------------------------------------------
void z80sio_device_config::device_config_complete()
{
// inherit a copy of the static data
const z80sio_interface *intf = reinterpret_cast<const z80sio_interface *>(static_config());
if (intf != NULL)
*static_cast<z80sio_interface *>(this) = *intf;
// or initialize to defaults if none provided
else
{
m_irq_cb = NULL;
m_dtr_changed_cb = NULL;
m_rts_changed_cb = NULL;
m_break_changed_cb = NULL;
m_transmit_cb = NULL;
m_receive_poll_cb = NULL;
}
}
//**************************************************************************
// LIVE DEVICE
//**************************************************************************
//-------------------------------------------------
// z80sio_device - constructor
//-------------------------------------------------
z80sio_device::z80sio_device(running_machine &_machine, const z80sio_device_config &config)
: device_t(_machine, config),
device_z80daisy_interface(_machine, config, *this),
m_config(config)
{
}
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
void z80sio_device::device_start()
{
m_channel[0].start(this, 0);
m_channel[1].start(this, 1);
}
//-------------------------------------------------
// device_reset - device-specific reset
//-------------------------------------------------
void z80sio_device::device_reset()
{
// loop over channels
for (int ch = 0; ch < 2; ch++)
m_channel[ch].reset();
}
//**************************************************************************
// DAISY CHAIN INTERFACE
//**************************************************************************
//-------------------------------------------------
// z80daisy_irq_state - return the overall IRQ
// state for this device
//-------------------------------------------------
int z80sio_device::z80daisy_irq_state()
{
int state = 0;
VPRINTF(("sio IRQ state = B:%d%d%d%d A:%d%d%d%d\n",
m_int_state[0], m_int_state[1], m_int_state[2], m_int_state[3],
m_int_state[4], m_int_state[5], m_int_state[6], m_int_state[7]));
// loop over all interrupt sources
for (int irqsource = 0; irqsource < 8; irqsource++)
{
int inum = k_int_priority[irqsource];
// if we're servicing a request, don't indicate more interrupts
if (m_int_state[inum] & Z80_DAISY_IEO)
{
state |= Z80_DAISY_IEO;
break;
}
state |= m_int_state[inum];
}
return state;
}
//-------------------------------------------------
// z80daisy_irq_ack - acknowledge an IRQ and
// return the appropriate vector
//-------------------------------------------------
int z80sio_device::z80daisy_irq_ack()
{
// loop over all interrupt sources
for (int irqsource = 0; irqsource < 8; irqsource++)
{
int inum = k_int_priority[irqsource];
// find the first channel with an interrupt requested
if (m_int_state[inum] & Z80_DAISY_INT)
{
VPRINTF(("sio IRQAck %d\n", inum));
// clear interrupt, switch to the IEO state, and update the IRQs
m_int_state[inum] = Z80_DAISY_IEO;
update_interrupt_state();
return m_channel[1].m_regs[2] + inum * 2;
}
}
logerror("z80sio_irq_ack: failed to find an interrupt to ack!\n");
return m_channel[1].m_regs[2];
}
//-------------------------------------------------
// z80daisy_irq_reti - clear the interrupt
// pending state to allow other interrupts through
//-------------------------------------------------
void z80sio_device::z80daisy_irq_reti()
{
// loop over all interrupt sources
for (int irqsource = 0; irqsource < 8; irqsource++)
{
int inum = k_int_priority[irqsource];
// find the first channel with an IEO pending
if (m_int_state[inum] & Z80_DAISY_IEO)
{
VPRINTF(("sio IRQReti %d\n", inum));
// clear the IEO state and update the IRQs
m_int_state[inum] &= ~Z80_DAISY_IEO;
update_interrupt_state();
return;
}
}
logerror("z80sio_irq_reti: failed to find an interrupt to clear IEO on!\n");
}
//**************************************************************************
// SIO CHANNEL
//**************************************************************************
//-------------------------------------------------
// sio_channel - constructor
//-------------------------------------------------
z80sio_device::sio_channel::sio_channel()
: m_device(NULL),
m_index(0),
m_inbuf(-1),
m_outbuf(-1),
m_int_on_next_rx(false),
m_receive_timer(NULL),
m_receive_inptr(0),
m_receive_outptr(0)
{
memset(m_regs, 0, sizeof(m_regs));
memset(m_status, 0, sizeof(m_status));
memset(m_receive_buffer, 0, sizeof(m_receive_buffer));
}
//-------------------------------------------------
// start - channel-specific startup
//-------------------------------------------------
void z80sio_device::sio_channel::start(z80sio_device *device, int index)
{
m_device = device;
m_index = index;
m_receive_timer = timer_alloc(&m_device->m_machine, static_serial_callback, this);
}
//-------------------------------------------------
// reset - reset a single SIO channel
//-------------------------------------------------
void z80sio_device::sio_channel::reset()
{
m_status[0] = SIO_RR0_TX_BUFFER_EMPTY;
m_status[1] = 0x00;
m_status[2] = 0x00;
m_int_on_next_rx = false;
m_outbuf = -1;
// reset interrupts
clear_interrupt(INT_TRANSMIT);
clear_interrupt(INT_STATUS);
clear_interrupt(INT_RECEIVE);
clear_interrupt(INT_ERROR);
// start the receive timer running
attotime tpc = compute_time_per_character();
timer_adjust_periodic(m_receive_timer, tpc, 0, tpc);
}
//-------------------------------------------------
// control_write - write to a control register
//-------------------------------------------------
void z80sio_device::sio_channel::control_write(UINT8 data)
{
int regnum = m_regs[0] & 7;
if (regnum != 0 || (regnum & 0xf8) != 0)
VPRINTF(("%s:sio_reg_w(%c,%d) = %02X\n", cpuexec_describe_context(&m_device->m_machine), 'A' + m_index, regnum, data));
// write a new value to the selected register
UINT8 old = m_regs[regnum];
m_regs[regnum] = data;
// clear the register number for the next write
if (regnum != 0)
m_regs[0] &= ~7;
// switch off the register for live state changes
switch (regnum)
{
// SIO write register 0
case 0:
switch (data & SIO_WR0_COMMAND_MASK)
{
case SIO_WR0_COMMAND_CH_RESET:
VPRINTF(("%s:SIO reset channel %c\n", cpuexec_describe_context(&m_device->m_machine), 'A' + m_index));
reset();
break;
case SIO_WR0_COMMAND_RES_STATUS_INT:
clear_interrupt(INT_STATUS);
break;
case SIO_WR0_COMMAND_ENA_RX_INT:
m_int_on_next_rx = true;
m_device->update_interrupt_state();
break;
case SIO_WR0_COMMAND_RES_TX_INT:
clear_interrupt(INT_TRANSMIT);
break;
case SIO_WR0_COMMAND_RES_ERROR:
clear_interrupt(INT_ERROR);
break;
}
break;
// SIO write register 1
case 1:
m_device->update_interrupt_state();
break;
// SIO write register 5
case 5:
if (((old ^ data) & SIO_WR5_DTR) && m_device->m_config.m_dtr_changed_cb)
(*m_device->m_config.m_dtr_changed_cb)(m_device, m_index, (data & SIO_WR5_DTR) != 0);
if (((old ^ data) & SIO_WR5_SEND_BREAK) && m_device->m_config.m_break_changed_cb)
(*m_device->m_config.m_break_changed_cb)(m_device, m_index, (data & SIO_WR5_SEND_BREAK) != 0);
if (((old ^ data) & SIO_WR5_RTS) && m_device->m_config.m_rts_changed_cb)
(*m_device->m_config.m_rts_changed_cb)(m_device, m_index, (data & SIO_WR5_RTS) != 0);
break;
}
}
//-------------------------------------------------
// control_read - read from a control register
//-------------------------------------------------
UINT8 z80sio_device::sio_channel::control_read()
{
int regnum = m_regs[0] & 7;
UINT8 result = m_status[regnum];
// switch off the register for live state changes
switch (regnum)
{
// SIO read register 0
case 0:
result &= ~SIO_RR0_INT_PENDING;
if (m_device->z80daisy_irq_state() & Z80_DAISY_INT)
result |= SIO_RR0_INT_PENDING;
break;
}
VPRINTF(("%s:sio_reg_r(%c,%d) = %02x\n", cpuexec_describe_context(&m_device->m_machine), 'A' + m_index, regnum, m_status[regnum]));
return m_status[regnum];
}
//-------------------------------------------------
// data_write - write to a data register
//-------------------------------------------------
void z80sio_device::sio_channel::data_write(UINT8 data)
{
VPRINTF(("%s:sio_data_w(%c) = %02X\n", cpuexec_describe_context(&m_device->m_machine), 'A' + m_index, data));
// if tx not enabled, just ignore it
if (!(m_regs[5] & SIO_WR5_TX_ENABLE))
return;
// update the status register
m_status[0] &= ~SIO_RR0_TX_BUFFER_EMPTY;
// reset the transmit interrupt
clear_interrupt(INT_TRANSMIT);
// stash the character
m_outbuf = data;
}
//-------------------------------------------------
// data_read - read from a data register
//-------------------------------------------------
UINT8 z80sio_device::sio_channel::data_read()
{
// update the status register
m_status[0] &= ~SIO_RR0_RX_CHAR_AVAILABLE;
// reset the receive interrupt
clear_interrupt(INT_RECEIVE);
VPRINTF(("%s:sio_data_r(%c) = %02X\n", cpuexec_describe_context(&m_device->m_machine), 'A' + m_index, m_inbuf));
return m_inbuf;
}
//-------------------------------------------------
// dtr - return the state of the DTR line
//-------------------------------------------------
int z80sio_device::sio_channel::dtr()
{
return ((m_regs[5] & SIO_WR5_DTR) != 0);
}
//-------------------------------------------------
// rts - return the state of the RTS line
//-------------------------------------------------
int z80sio_device::sio_channel::rts()
{
return ((m_regs[5] & SIO_WR5_RTS) != 0);
}
//-------------------------------------------------
// set_cts - set the state of the CTS line
//-------------------------------------------------
void z80sio_device::sio_channel::set_cts(int state)
{
timer_call_after_resynch(&m_device->m_machine, this, (SIO_RR0_CTS << 1) + (state != 0), static_change_input_line);
}
//-------------------------------------------------
// set_dcd - set the state of the DCD line
//-------------------------------------------------
void z80sio_device::sio_channel::set_dcd(int state)
{
timer_call_after_resynch(&m_device->m_machine, this, (SIO_RR0_DCD << 1) + (state != 0), static_change_input_line);
}
//-------------------------------------------------
// receive_data - receive data on the input lines
//-------------------------------------------------
void z80sio_device::sio_channel::receive_data(int data)
{
// put it on the queue
int newinptr = (m_receive_inptr + 1) % ARRAY_LENGTH(m_receive_buffer);
if (newinptr != m_receive_outptr)
{
m_receive_buffer[m_receive_inptr] = data;
m_receive_inptr = newinptr;
}
else
logerror("z80sio_receive_data: buffer overrun\n");
}
//-------------------------------------------------
// change_input_line - generically change the
// state of an input line; designed to be called
// from a timer callback
//-------------------------------------------------
void z80sio_device::sio_channel::change_input_line(int line, int state)
{
VPRINTF(("sio_change_input_line(%c, %s) = %d\n", 'A' + m_index, (line == SIO_RR0_CTS) ? "CTS" : "DCD", state));
// remember the old value
UINT8 old = m_status[0];
// set the bit in the status register
m_status[0] &= ~line;
if (state)
m_status[0] |= line;
// if state change interrupts are enabled, signal
if (((old ^ m_status[0]) & line) && (m_regs[1] & SIO_WR1_STATUSINT_ENABLE))
set_interrupt(INT_STATUS);
}
//-------------------------------------------------
// serial_callback - callback to pump
// data through
//-------------------------------------------------
void z80sio_device::sio_channel::serial_callback()
{
int data = -1;
// first perform any outstanding transmits
if (m_outbuf != -1)
{
VPRINTF(("serial_callback(%c): Transmitting %02x\n", 'A' + m_index, m_outbuf));
// actually transmit the character
if (m_device->m_config.m_transmit_cb != NULL)
(*m_device->m_config.m_transmit_cb)(m_device, m_index, m_outbuf);
// update the status register
m_status[0] |= SIO_RR0_TX_BUFFER_EMPTY;
// set the transmit buffer empty interrupt if enabled
if (m_regs[1] & SIO_WR1_TXINT_ENABLE)
set_interrupt(INT_TRANSMIT);
// reset the output buffer
m_outbuf = -1;
}
// ask the polling callback if there is data to receive
if (m_device->m_config.m_receive_poll_cb != NULL)
data = (*m_device->m_config.m_receive_poll_cb)(m_device, m_index);
// if we have buffered data, pull it
if (m_receive_inptr != m_receive_outptr)
{
data = m_receive_buffer[m_receive_outptr];
m_receive_outptr = (m_receive_outptr + 1) % ARRAY_LENGTH(m_receive_buffer);
}
// if we have data, receive it
if (data != -1)
{
VPRINTF(("serial_callback(%c): Receiving %02x\n", 'A' + m_index, data));
// if rx not enabled, just ignore it
if (!(m_regs[3] & SIO_WR3_RX_ENABLE))
{
VPRINTF((" (ignored because receive is disabled)\n"));
return;
}
// stash the data and update the status
m_inbuf = data;
m_status[0] |= SIO_RR0_RX_CHAR_AVAILABLE;
// update our interrupt state
switch (m_regs[1] & SIO_WR1_RXINT_MASK)
{
case SIO_WR1_RXINT_FIRST:
if (!m_int_on_next_rx)
break;
case SIO_WR1_RXINT_ALL_NOPARITY:
case SIO_WR1_RXINT_ALL_PARITY:
set_interrupt(INT_RECEIVE);
break;
}
m_int_on_next_rx = false;
}
}
//**************************************************************************
// GLOBAL STUBS
//**************************************************************************
WRITE8_DEVICE_HANDLER( z80sio_c_w ) { downcast<z80sio_device *>(device)->control_write(offset & 1, data); }
WRITE8_DEVICE_HANDLER( z80sio_d_w ) { downcast<z80sio_device *>(device)->data_write(offset & 1, data); }
READ8_DEVICE_HANDLER( z80sio_c_r ) { return downcast<z80sio_device *>(device)->control_read(offset & 1); }
READ8_DEVICE_HANDLER( z80sio_d_r ) { return downcast<z80sio_device *>(device)->data_read(offset & 1); }
READ8_DEVICE_HANDLER( z80sio_get_dtr ) { return downcast<z80sio_device *>(device)->dtr(offset & 1); }
READ8_DEVICE_HANDLER( z80sio_get_rts ) { return downcast<z80sio_device *>(device)->rts(offset & 1); }
WRITE8_DEVICE_HANDLER( z80sio_set_cts ) { downcast<z80sio_device *>(device)->set_cts(offset & 1, data); }
WRITE8_DEVICE_HANDLER( z80sio_set_dcd ) { downcast<z80sio_device *>(device)->set_dcd(offset & 1, data); }
WRITE8_DEVICE_HANDLER( z80sio_receive_data ) { downcast<z80sio_device *>(device)->receive_data(offset & 1, data); }
READ8_DEVICE_HANDLER( z80sio_cd_ba_r )
{
switch (offset & 3)
{
case 0: return z80sio_d_r(device, 0);
case 1: return z80sio_d_r(device, 1);
case 2: return z80sio_c_r(device, 0);
case 3: return z80sio_c_r(device, 1);
}
return 0xff;
}
WRITE8_DEVICE_HANDLER( z80sio_cd_ba_w )
{
switch (offset & 3)
{
case 0: z80sio_d_w(device, 0, data); break;
case 1: z80sio_d_w(device, 1, data); break;
case 2: z80sio_c_w(device, 0, data); break;
case 3: z80sio_c_w(device, 1, data); break;
}
}
READ8_DEVICE_HANDLER( z80sio_ba_cd_r )
{
switch (offset & 3)
{
case 0: return z80sio_d_r(device, 0);
case 1: return z80sio_c_r(device, 0);
case 2: return z80sio_d_r(device, 1);
case 3: return z80sio_c_r(device, 1);
}
return 0xff;
}
WRITE8_DEVICE_HANDLER( z80sio_ba_cd_w )
{
switch (offset & 3)
{
case 0: z80sio_d_w(device, 0, data); break;
case 1: z80sio_c_w(device, 0, data); break;
case 2: z80sio_d_w(device, 1, data); break;
case 3: z80sio_c_w(device, 1, data); break;
}
}
const device_type Z80SIO = z80sio_device_config::static_alloc_device_config;