diff options
Diffstat (limited to 'src/emu/machine/z80sio.c')
-rw-r--r-- | src/emu/machine/z80sio.c | 805 |
1 files changed, 805 insertions, 0 deletions
diff --git a/src/emu/machine/z80sio.c b/src/emu/machine/z80sio.c new file mode 100644 index 00000000000..1da4fa1eb5d --- /dev/null +++ b/src/emu/machine/z80sio.c @@ -0,0 +1,805 @@ +/*************************************************************************** + + Z80 SIO (Z8440) implementation + + Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. + Visit http://mamedev.org for licensing and usage restrictions. + +***************************************************************************/ + +#include "driver.h" +#include "z80sio.h" +#include "cpu/z80/z80.h" +#include "cpu/z80/z80daisy.h" + + + +/*************************************************************************** + DEBUGGING +***************************************************************************/ + +#define VERBOSE 1 + +#if VERBOSE +#define VPRINTF(x) logerror x +#else +#define VPRINTF(x) +#endif + + + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +/* interrupt states */ +#define INT_CHB_TRANSMIT 0x00 /* not confirmed */ +#define INT_CHB_STATUS 0x01 +#define INT_CHB_RECEIVE 0x02 +#define INT_CHB_ERROR 0x03 + +#define INT_CHA_TRANSMIT 0x04 /* not confirmed */ +#define INT_CHA_STATUS 0x05 +#define INT_CHA_RECEIVE 0x06 +#define INT_CHA_ERROR 0x07 + +/* SIO write register 0 */ +#define SIO_WR0_RESET_MASK 0xc0 /* D7-D6: Reset control */ +#define SIO_WR0_RESET_NULL 0x00 /* 00 = NULL code */ +#define SIO_WR0_RESET_RX_CRC 0x40 /* 01 = Reset Rx CRC checker */ +#define SIO_WR0_RESET_TX_CRC 0x80 /* 10 = Reset Tx CRC generator */ +#define SIO_WR0_RESET_TX_LATCH 0xc0 /* 11 = Reset Tx Underrun/EOM latch */ +#define SIO_WR0_COMMAND_MASK 0x38 /* D5-D3: Command */ +#define SIO_WR0_COMMAND_NULL 0x00 /* 000 = NULL code */ +#define SIO_WR0_COMMAND_SET_ABORT 0x08 /* 001 = Set abort (SDLC) */ +#define SIO_WR0_COMMAND_RES_STATUS_INT 0x10 /* 010 = reset ext/status interrupts */ +#define SIO_WR0_COMMAND_CH_RESET 0x18 /* 011 = Channel reset */ +#define SIO_WR0_COMMAND_ENA_RX_INT 0x20 /* 100 = Enable int on next Rx character */ +#define SIO_WR0_COMMAND_RES_TX_INT 0x28 /* 101 = Reset Tx int pending */ +#define SIO_WR0_COMMAND_RES_ERROR 0x30 /* 110 = Error reset */ +#define SIO_WR0_COMMAND_RETI 0x38 /* 111 = Return from int (CH-A only) */ +#define SIO_WR0_REGISTER_MASK 0x07 /* D2-D0: Register select (0-7) */ + +/* SIO write register 1 */ +#define SIO_WR1_READY_WAIT_ENA 0x80 /* D7 = READY/WAIT enable */ +#define SIO_WR1_READY_WAIT_FUNCTION 0x40 /* D6 = READY/WAIT function */ +#define SIO_WR1_READY_WAIT_ON_RT 0x20 /* D5 = READY/WAIT on R/T */ +#define SIO_WR1_RXINT_MASK 0x18 /* D4-D3 = Rx int control */ +#define SIO_WR1_RXINT_DISABLE 0x00 /* 00 = Rx int disable */ +#define SIO_WR1_RXINT_FIRST 0x08 /* 01 = Rx int on first character */ +#define SIO_WR1_RXINT_ALL_PARITY 0x10 /* 10 = int on all Rx characters (parity affects vector) */ +#define SIO_WR1_RXINT_ALL_NOPARITY 0x18 /* 11 = int on all Rx characters (parity ignored) */ +#define SIO_WR1_STATUS_AFFECTS_VECTOR 0x04 /* D2 = Status affects vector (CH-B only) */ +#define SIO_WR1_TXINT_ENABLE 0x02 /* D1 = Tx int enable */ +#define SIO_WR1_STATUSINT_ENABLE 0x01 /* D0 = Ext int enable */ + +/* SIO write register 2 (CH-B only) */ +#define SIO_WR2_INT_VECTOR_MASK 0xff /* D7-D0 = interrupt vector */ + +/* SIO write register 3 */ +#define SIO_WR3_RX_DATABITS_MASK 0xc0 /* D7-D6 = Rx Data bits */ +#define SIO_WR3_RX_DATABITS_5 0x00 /* 00 = Rx 5 bits/character */ +#define SIO_WR3_RX_DATABITS_7 0x40 /* 01 = Rx 7 bits/character */ +#define SIO_WR3_RX_DATABITS_6 0x80 /* 10 = Rx 6 bits/character */ +#define SIO_WR3_RX_DATABITS_8 0xc0 /* 11 = Rx 8 bits/character */ +#define SIO_WR3_AUTO_ENABLES 0x20 /* D5 = Auto enables */ +#define SIO_WR3_ENTER_HUNT_PHASE 0x10 /* D4 = Enter hunt phase */ +#define SIO_WR3_RX_CRC_ENABLE 0x08 /* D3 = Rx CRC enable */ +#define SIO_WR3_ADDR_SEARCH_MODE 0x04 /* D2 = Address search mode (SDLC) */ +#define SIO_WR3_SYNC_LOAD_INHIBIT 0x02 /* D1 = Sync character load inhibit */ +#define SIO_WR3_RX_ENABLE 0x01 /* D0 = Rx enable */ + +/* SIO write register 4 */ +#define SIO_WR4_CLOCK_MODE_MASK 0xc0 /* D7-D6 = Clock mode */ +#define SIO_WR4_CLOCK_MODE_x1 0x00 /* 00 = x1 clock mode */ +#define SIO_WR4_CLOCK_MODE_x16 0x40 /* 01 = x16 clock mode */ +#define SIO_WR4_CLOCK_MODE_x32 0x80 /* 10 = x32 clock mode */ +#define SIO_WR4_CLOCK_MODE_x64 0xc0 /* 11 = x64 clock mode */ +#define SIO_WR4_SYNC_MODE_MASK 0x30 /* D5-D4 = Sync mode */ +#define SIO_WR4_SYNC_MODE_8BIT 0x00 /* 00 = 8 bit sync character */ +#define SIO_WR4_SYNC_MODE_16BIT 0x10 /* 01 = 16 bit sync character */ +#define SIO_WR4_SYNC_MODE_SDLC 0x20 /* 10 = SDLC mode (01111110 flag) */ +#define SIO_WR4_SYNC_MODE_EXTERNAL 0x30 /* 11 = External sync mode */ +#define SIO_WR4_STOPBITS_MASK 0x0c /* D3-D2 = Stop bits */ +#define SIO_WR4_STOPBITS_SYNC 0x00 /* 00 = Sync modes enable */ +#define SIO_WR4_STOPBITS_1 0x04 /* 01 = 1 stop bit/character */ +#define SIO_WR4_STOPBITS_15 0x08 /* 10 = 1.5 stop bits/character */ +#define SIO_WR4_STOPBITS_2 0x0c /* 11 = 2 stop bits/character */ +#define SIO_WR4_PARITY_EVEN 0x02 /* D1 = Parity even/odd */ +#define SIO_WR4_PARITY_ENABLE 0x01 /* D0 = Parity enable */ + +/* SIO write register 5 */ +#define SIO_WR5_DTR 0x80 /* D7 = DTR */ +#define SIO_WR5_TX_DATABITS_MASK 0x60 /* D6-D5 = Tx Data bits */ +#define SIO_WR5_TX_DATABITS_5 0x00 /* 00 = Tx 5 bits/character */ +#define SIO_WR5_TX_DATABITS_7 0x20 /* 01 = Tx 7 bits/character */ +#define SIO_WR5_TX_DATABITS_6 0x40 /* 10 = Tx 6 bits/character */ +#define SIO_WR5_TX_DATABITS_8 0x60 /* 11 = Tx 8 bits/character */ +#define SIO_WR5_SEND_BREAK 0x10 /* D4 = Send break */ +#define SIO_WR5_TX_ENABLE 0x08 /* D3 = Tx Enable */ +#define SIO_WR5_CRC16_SDLC 0x04 /* D2 = CRC-16/SDLC */ +#define SIO_WR5_RTS 0x02 /* D1 = RTS */ +#define SIO_WR5_TX_CRC_ENABLE 0x01 /* D0 = Tx CRC enable */ + +/* SIO write register 6 */ +#define SIO_WR6_SYNC_7_0_MASK 0xff /* D7-D0 = Sync bits 7-0 */ + +/* SIO write register 7 */ +#define SIO_WR7_SYNC_15_8_MASK 0xff /* D7-D0 = Sync bits 15-8 */ + +/* SIO read register 0 */ +#define SIO_RR0_BREAK_ABORT 0x80 /* D7 = Break/abort */ +#define SIO_RR0_TX_UNDERRUN 0x40 /* D6 = Tx underrun/EOM */ +#define SIO_RR0_CTS 0x20 /* D5 = CTS */ +#define SIO_RR0_SYNC_HUNT 0x10 /* D4 = Sync/hunt */ +#define SIO_RR0_DCD 0x08 /* D3 = DCD */ +#define SIO_RR0_TX_BUFFER_EMPTY 0x04 /* D2 = Tx buffer empty */ +#define SIO_RR0_INT_PENDING 0x02 /* D1 = int pending (CH-A only) */ +#define SIO_RR0_RX_CHAR_AVAILABLE 0x01 /* D0 = Rx character available */ + +/* SIO read register 1 */ +#define SIO_RR1_END_OF_FRAME 0x80 /* D7 = End of frame (SDLC) */ +#define SIO_RR1_CRC_FRAMING_ERROR 0x40 /* D6 = CRC/Framing error */ +#define SIO_RR1_RX_OVERRUN_ERROR 0x20 /* D5 = Rx overrun error */ +#define SIO_RR1_PARITY_ERROR 0x10 /* D4 = Parity error */ +#define 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 */ +#define SIO_RR1_ALL_SENT 0x01 /* D0 = All sent */ + +/* SIO read register 2 (CH-B only) */ +#define SIO_RR2_VECTOR_MASK 0xff /* D7-D0 = Interrupt vector */ + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +typedef struct _sio_channel sio_channel; +struct _sio_channel +{ + UINT8 regs[8]; /* 8 writeable registers */ + UINT8 status[4]; /* 3 readable registers */ + int inbuf; /* input buffer */ + int outbuf; /* output buffer */ + UINT8 int_on_next_rx; /* interrupt on next rx? */ + emu_timer *receive_timer; /* timer to clock data in */ + UINT8 receive_buffer[16]; /* buffer for incoming data */ + UINT8 receive_inptr; /* index of data coming in */ + UINT8 receive_outptr; /* index of data going out */ +}; + + +typedef struct _z80sio z80sio; +struct _z80sio +{ + sio_channel chan[2]; /* 2 channels */ + UINT8 int_state[8]; /* interrupt states */ + + void (*irq_cb)(int state); + write8_handler dtr_changed_cb; + write8_handler rts_changed_cb; + write8_handler break_changed_cb; + write8_handler transmit_cb; + int (*receive_poll_cb)(int which); +}; + + + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ + +static TIMER_CALLBACK( serial_callback ); + + + +/*************************************************************************** + GLOBAL VARIABLES +***************************************************************************/ + +static z80sio sios[MAX_SIO]; + + +/* + + 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 +***************************************************************************/ + +INLINE void interrupt_check(z80sio *sio) +{ + /* if we have a callback, update it with the current state */ + if (sio->irq_cb != NULL) + (*sio->irq_cb)((z80sio_irq_state(sio - sios) & Z80_DAISY_INT) ? ASSERT_LINE : CLEAR_LINE); +} + + +INLINE attotime compute_time_per_character(z80sio *sio, int which) +{ + /* fix me -- should compute properly and include data, stop, parity bits */ + return attotime_mul(ATTOTIME_IN_HZ(9600), 10); +} + + + +/*************************************************************************** + INITIALIZATION/CONFIGURATION +***************************************************************************/ + +/*------------------------------------------------- + z80sio_init - initialize a single SIO chip +-------------------------------------------------*/ + +void z80sio_init(int which, z80sio_interface *intf) +{ + z80sio *sio = sios + which; + + assert(which < MAX_SIO); + + memset(sio, 0, sizeof(*sio)); + + sio->chan[0].receive_timer = timer_alloc(serial_callback); + sio->chan[1].receive_timer = timer_alloc(serial_callback); + + sio->irq_cb = intf->irq_cb; + sio->dtr_changed_cb = intf->dtr_changed_cb; + sio->rts_changed_cb = intf->rts_changed_cb; + sio->break_changed_cb = intf->break_changed_cb; + sio->transmit_cb = intf->transmit_cb; + sio->receive_poll_cb = intf->receive_poll_cb; + + z80sio_reset(which); +} + + +/*------------------------------------------------- + reset_channel - reset a single SIO channel +-------------------------------------------------*/ + +static void reset_channel(z80sio *sio, int ch) +{ + attotime tpc = compute_time_per_character(sio, ch); + sio_channel *chan = &sio->chan[ch]; + + chan->status[0] = SIO_RR0_TX_BUFFER_EMPTY; + chan->status[1] = 0x00; + chan->status[2] = 0x00; + chan->int_on_next_rx = 0; + chan->outbuf = -1; + + sio->int_state[0 + 4*ch] = 0; + sio->int_state[1 + 4*ch] = 0; + sio->int_state[2 + 4*ch] = 0; + sio->int_state[3 + 4*ch] = 0; + + interrupt_check(sio); + + /* start the receive timer running */ + timer_adjust(chan->receive_timer, tpc, ((sio - sios) << 1) | ch, tpc); +} + + +/*------------------------------------------------- + z80sio_reset - reset a single SIO chip +-------------------------------------------------*/ + +void z80sio_reset(int which) +{ + z80sio *sio = sios + which; + int ch; + + assert(which < MAX_SIO); + + /* loop over channels */ + for (ch = 0; ch < 2; ch++) + reset_channel(sio, ch); +} + + + +/*************************************************************************** + CONTROL REGISTER READ/WRITE +***************************************************************************/ + +/*------------------------------------------------- + z80sio_c_w - write to a control register +-------------------------------------------------*/ + +void z80sio_c_w(int which, int ch, UINT8 data) +{ + z80sio *sio = sios + which; + sio_channel *chan = &sio->chan[ch]; + int reg = chan->regs[0] & 7; + UINT8 old = chan->regs[reg]; + + if (reg != 0 || (reg & 0xf8)) + VPRINTF(("%04X:sio_reg_w(%c,%d) = %02X\n", activecpu_get_pc(), 'A' + ch, reg, data)); + + /* write a new value to the selected register */ + chan->regs[reg] = data; + + /* clear the register number for the next write */ + if (reg != 0) + chan->regs[0] &= ~7; + + /* switch off the register for live state changes */ + switch (reg) + { + /* SIO write register 0 */ + case 0: + switch (data & SIO_WR0_COMMAND_MASK) + { + case SIO_WR0_COMMAND_CH_RESET: + VPRINTF(("%04X:SIO reset channel %c\n", activecpu_get_pc(), 'A' + ch)); + reset_channel(sio, ch); + break; + + case SIO_WR0_COMMAND_RES_STATUS_INT: + sio->int_state[INT_CHA_STATUS - 4*ch] &= ~Z80_DAISY_INT; + interrupt_check(sio); + break; + + case SIO_WR0_COMMAND_ENA_RX_INT: + chan->int_on_next_rx = TRUE; + interrupt_check(sio); + break; + + case SIO_WR0_COMMAND_RES_TX_INT: + sio->int_state[INT_CHA_TRANSMIT - 4*ch] &= ~Z80_DAISY_INT; + interrupt_check(sio); + break; + + case SIO_WR0_COMMAND_RES_ERROR: + sio->int_state[INT_CHA_ERROR - 4*ch] &= ~Z80_DAISY_INT; + interrupt_check(sio); + break; + } + break; + + /* SIO write register 1 */ + case 1: + interrupt_check(sio); + break; + + /* SIO write register 5 */ + case 5: + if (((old ^ data) & SIO_WR5_DTR) && sio->dtr_changed_cb) + (*sio->dtr_changed_cb)(ch, (data & SIO_WR5_DTR) != 0); + if (((old ^ data) & SIO_WR5_SEND_BREAK) && sio->break_changed_cb) + (*sio->break_changed_cb)(ch, (data & SIO_WR5_SEND_BREAK) != 0); + if (((old ^ data) & SIO_WR5_RTS) && sio->rts_changed_cb) + (*sio->rts_changed_cb)(ch, (data & SIO_WR5_RTS) != 0); + break; + } +} + + +/*------------------------------------------------- + z80sio_c_r - read from a control register +-------------------------------------------------*/ + +UINT8 z80sio_c_r(int which, int ch) +{ + z80sio *sio = sios + which; + sio_channel *chan = &sio->chan[ch]; + int reg = chan->regs[0] & 7; + UINT8 result = chan->status[reg]; + + /* switch off the register for live state changes */ + switch (reg) + { + /* SIO read register 0 */ + case 0: + result &= ~SIO_RR0_INT_PENDING; + if (z80sio_irq_state(which) & Z80_DAISY_INT) + result |= SIO_RR0_INT_PENDING; + break; + } + + VPRINTF(("%04X:sio_reg_r(%c,%d) = %02x\n", activecpu_get_pc(), 'A' + ch, reg, chan->status[reg])); + + return chan->status[reg]; +} + + + + +/*************************************************************************** + DATA REGISTER READ/WRITE +***************************************************************************/ + +/*------------------------------------------------- + z80sio_d_w - write to a data register +-------------------------------------------------*/ + +void z80sio_d_w(int which, int ch, UINT8 data) +{ + z80sio *sio = sios + which; + sio_channel *chan = &sio->chan[ch]; + + VPRINTF(("%04X:sio_data_w(%c) = %02X\n", activecpu_get_pc(), 'A' + ch, data)); + + /* if tx not enabled, just ignore it */ + if (!(chan->regs[5] & SIO_WR5_TX_ENABLE)) + return; + + /* update the status register */ + chan->status[0] &= ~SIO_RR0_TX_BUFFER_EMPTY; + + /* reset the transmit interrupt */ + sio->int_state[INT_CHA_TRANSMIT - 4*ch] &= ~Z80_DAISY_INT; + interrupt_check(sio); + + /* stash the character */ + chan->outbuf = data; +} + + +/*------------------------------------------------- + z80sio_d_r - read from a data register +-------------------------------------------------*/ + +UINT8 z80sio_d_r(int which, int ch) +{ + z80sio *sio = sios + which; + sio_channel *chan = &sio->chan[ch]; + + /* update the status register */ + chan->status[0] &= ~SIO_RR0_RX_CHAR_AVAILABLE; + + /* reset the receive interrupt */ + sio->int_state[INT_CHA_RECEIVE - 4*ch] &= ~Z80_DAISY_INT; + interrupt_check(sio); + + VPRINTF(("%04X:sio_data_r(%c) = %02X\n", activecpu_get_pc(), 'A' + ch, chan->inbuf)); + + return chan->inbuf; +} + + + +/*************************************************************************** + CONTROL LINE READ/WRITE +***************************************************************************/ + +/*------------------------------------------------- + z80sio_get_dtr - return the state of the DTR + line +-------------------------------------------------*/ + +int z80sio_get_dtr(int which, int ch) +{ + z80sio *sio = sios + which; + sio_channel *chan = &sio->chan[ch]; + return ((chan->regs[5] & SIO_WR5_DTR) != 0); +} + + +/*------------------------------------------------- + z80sio_get_rts - return the state of the RTS + line +-------------------------------------------------*/ + +int z80sio_get_rts(int which, int ch) +{ + z80sio *sio = sios + which; + sio_channel *chan = &sio->chan[ch]; + return ((chan->regs[5] & SIO_WR5_RTS) != 0); +} + + +/*------------------------------------------------- + z80sio_set_cts - set the state of the CTS + line +-------------------------------------------------*/ + +static TIMER_CALLBACK( change_input_line ) +{ + z80sio *sio = sios + ((param >> 1) & 0x3f); + sio_channel *chan = &sio->chan[param & 1]; + UINT8 line = (param >> 8) & 0xff; + int state = (param >> 7) & 1; + int ch = param & 1; + UINT8 old; + + VPRINTF(("sio_change_input_line(%c, %s) = %d\n", 'A' + ch, (line == SIO_RR0_CTS) ? "CTS" : "DCD", state)); + + /* remember the old value */ + old = chan->status[0]; + + /* set the bit in the status register */ + chan->status[0] &= ~line; + if (state) + chan->status[0] |= line; + + /* if state change interrupts are enabled, signal */ + if (((old ^ chan->status[0]) & line) && (chan->regs[1] & SIO_WR1_STATUSINT_ENABLE)) + { + sio->int_state[INT_CHA_STATUS - 4*ch] |= Z80_DAISY_INT; + interrupt_check(sio); + } +} + + +/*------------------------------------------------- + z80sio_set_cts - set the state of the CTS + line +-------------------------------------------------*/ + +void z80sio_set_cts(int which, int ch, int state) +{ + /* operate deferred */ + timer_call_after_resynch((SIO_RR0_CTS << 8) + (state != 0) * 0x80 + which * 2 + ch, change_input_line); +} + + +/*------------------------------------------------- + z80sio_set_dcd - set the state of the DCD + line +-------------------------------------------------*/ + +void z80sio_set_dcd(int which, int ch, int state) +{ + /* operate deferred */ + timer_call_after_resynch((SIO_RR0_DCD << 8) + (state != 0) * 0x80 + which * 2 + ch, change_input_line); +} + + +/*------------------------------------------------- + z80sio_receive_data - receive data on the + input lines +-------------------------------------------------*/ + +void z80sio_receive_data(int which, int ch, UINT8 data) +{ + z80sio *sio = sios + which; + sio_channel *chan = &sio->chan[ch]; + int newinptr; + + /* put it on the queue */ + newinptr = (chan->receive_inptr + 1) % ARRAY_LENGTH(chan->receive_buffer); + if (newinptr != chan->receive_outptr) + { + chan->receive_buffer[chan->receive_inptr] = data; + chan->receive_inptr = newinptr; + } + else + logerror("z80sio_receive_data: buffer overrun\n"); +} + + +/*------------------------------------------------- + serial_callback - callback to pump + data through +-------------------------------------------------*/ + +static TIMER_CALLBACK( serial_callback ) +{ + z80sio *sio = sios + (param >> 1); + sio_channel *chan = &sio->chan[param & 1]; + int ch = param & 1; + int data = -1; + + /* first perform any outstanding transmits */ + if (chan->outbuf != -1) + { + VPRINTF(("serial_callback(%c): Transmitting %02x\n", 'A' + ch, chan->outbuf)); + + /* actually transmit the character */ + if (sio->transmit_cb != NULL) + (*sio->transmit_cb)(ch, chan->outbuf); + + /* update the status register */ + chan->status[0] |= SIO_RR0_TX_BUFFER_EMPTY; + + /* set the transmit buffer empty interrupt if enabled */ + if (chan->regs[1] & SIO_WR1_TXINT_ENABLE) + { + sio->int_state[INT_CHA_TRANSMIT - 4*ch] |= Z80_DAISY_INT; + interrupt_check(sio); + } + + /* reset the output buffer */ + chan->outbuf = -1; + } + + /* ask the polling callback if there is data to receive */ + if (sio->receive_poll_cb != NULL) + data = (*sio->receive_poll_cb)(ch); + + /* if we have buffered data, pull it */ + if (chan->receive_inptr != chan->receive_outptr) + { + data = chan->receive_buffer[chan->receive_outptr]; + chan->receive_outptr = (chan->receive_outptr + 1) % ARRAY_LENGTH(chan->receive_buffer); + } + + /* if we have data, receive it */ + if (data != -1) + { + VPRINTF(("serial_callback(%c): Receiving %02x\n", 'A' + ch, data)); + + /* if rx not enabled, just ignore it */ + if (!(chan->regs[3] & SIO_WR3_RX_ENABLE)) + { + VPRINTF((" (ignored because receive is disabled)\n")); + return; + } + + /* stash the data and update the status */ + chan->inbuf = data; + chan->status[0] |= SIO_RR0_RX_CHAR_AVAILABLE; + + /* update our interrupt state */ + switch (chan->regs[1] & SIO_WR1_RXINT_MASK) + { + case SIO_WR1_RXINT_FIRST: + if (!chan->int_on_next_rx) + break; + + case SIO_WR1_RXINT_ALL_NOPARITY: + case SIO_WR1_RXINT_ALL_PARITY: + sio->int_state[INT_CHA_RECEIVE - 4*ch] |= Z80_DAISY_INT; + interrupt_check(sio); + break; + } + chan->int_on_next_rx = FALSE; + } +} + + + +/*************************************************************************** + DAISY CHAIN INTERFACE +***************************************************************************/ + +static const UINT8 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 +}; + + +int z80sio_irq_state(int which) +{ + z80sio *sio = sios + which; + int state = 0; + int i; + + VPRINTF(("sio IRQ state = B:%d%d%d%d A:%d%d%d%d\n", + sio->int_state[0], sio->int_state[1], sio->int_state[2], sio->int_state[3], + sio->int_state[4], sio->int_state[5], sio->int_state[6], sio->int_state[7])); + + /* loop over all interrupt sources */ + for (i = 0; i < 8; i++) + { + int inum = int_priority[i]; + + /* if we're servicing a request, don't indicate more interrupts */ + if (sio->int_state[inum] & Z80_DAISY_IEO) + { + state |= Z80_DAISY_IEO; + break; + } + state |= sio->int_state[inum]; + } + + return state; +} + + +int z80sio_irq_ack(int which) +{ + z80sio *sio = sios + which; + int i; + + /* loop over all interrupt sources */ + for (i = 0; i < 8; i++) + { + int inum = int_priority[i]; + + /* find the first channel with an interrupt requested */ + if (sio->int_state[inum] & Z80_DAISY_INT) + { + VPRINTF(("sio IRQAck %d\n", inum)); + + /* clear interrupt, switch to the IEO state, and update the IRQs */ + sio->int_state[inum] = Z80_DAISY_IEO; + interrupt_check(sio); + return sio->chan[1].regs[2] + inum * 2; + } + } + + logerror("z80sio_irq_ack: failed to find an interrupt to ack!\n"); + return sio->chan[1].regs[2]; +} + + +void z80sio_irq_reti(int which) +{ + z80sio *sio = sios + which; + int i; + + /* loop over all interrupt sources */ + for (i = 0; i < 8; i++) + { + int inum = int_priority[i]; + + /* find the first channel with an IEO pending */ + if (sio->int_state[inum] & Z80_DAISY_IEO) + { + VPRINTF(("sio IRQReti %d\n", inum)); + + /* clear the IEO state and update the IRQs */ + sio->int_state[inum] &= ~Z80_DAISY_IEO; + interrupt_check(sio); + return; + } + } + + logerror("z80sio_irq_reti: failed to find an interrupt to clear IEO on!\n"); +} |