From 5826c45ea48778bd9bf513fef1311a77c447ff8e Mon Sep 17 00:00:00 2001 From: Curt Coder Date: Mon, 8 Mar 2010 16:47:53 +0000 Subject: [PATCH] Imported Z80 DART and Z80 STI from MESS. (no whatsnew) --- .gitattributes | 4 + src/emu/emu.mak | 2 + src/emu/machine/z80dart.c | 1504 +++++++++++++++++++++++++++++++++++++ src/emu/machine/z80dart.h | 132 ++++ src/emu/machine/z80sti.c | 849 +++++++++++++++++++++ src/emu/machine/z80sti.h | 116 +++ 6 files changed, 2607 insertions(+) create mode 100644 src/emu/machine/z80dart.c create mode 100644 src/emu/machine/z80dart.h create mode 100644 src/emu/machine/z80sti.c create mode 100644 src/emu/machine/z80sti.h diff --git a/.gitattributes b/.gitattributes index 5dc3843ebf2..38e3a8c288e 100644 --- a/.gitattributes +++ b/.gitattributes @@ -756,12 +756,16 @@ src/emu/machine/x76f100.c svneol=native#text/plain src/emu/machine/x76f100.h svneol=native#text/plain src/emu/machine/z80ctc.c svneol=native#text/plain src/emu/machine/z80ctc.h svneol=native#text/plain +src/emu/machine/z80dart.c svneol=native#text/plain +src/emu/machine/z80dart.h svneol=native#text/plain src/emu/machine/z80dma.c svneol=native#text/plain src/emu/machine/z80dma.h svneol=native#text/plain src/emu/machine/z80pio.c svneol=native#text/plain src/emu/machine/z80pio.h svneol=native#text/plain src/emu/machine/z80sio.c svneol=native#text/plain src/emu/machine/z80sio.h svneol=native#text/plain +src/emu/machine/z80sti.c svneol=native#text/plain +src/emu/machine/z80sti.h svneol=native#text/plain src/emu/mame.c svneol=native#text/plain src/emu/mame.h svneol=native#text/plain src/emu/mconfig.c svneol=native#text/plain diff --git a/src/emu/emu.mak b/src/emu/emu.mak index a330e3cf634..5c0f89efe36 100644 --- a/src/emu/emu.mak +++ b/src/emu/emu.mak @@ -179,9 +179,11 @@ EMUMACHINEOBJS = \ $(EMUMACHINE)/x76f041.o \ $(EMUMACHINE)/x76f100.o \ $(EMUMACHINE)/z80ctc.o \ + $(EMUMACHINE)/z80dart.o \ $(EMUMACHINE)/z80dma.o \ $(EMUMACHINE)/z80pio.o \ $(EMUMACHINE)/z80sio.o \ + $(EMUMACHINE)/z80sti.o \ EMUVIDEOOBJS = \ $(EMUVIDEO)/generic.o \ diff --git a/src/emu/machine/z80dart.c b/src/emu/machine/z80dart.c new file mode 100644 index 00000000000..9e9e3c791a5 --- /dev/null +++ b/src/emu/machine/z80dart.c @@ -0,0 +1,1504 @@ +/*************************************************************************** + + Z80 DART Dual Asynchronous Receiver/Transmitter emulation + + Copyright (c) 2008, The MESS Team. + Visit http://mamedev.org for licensing and usage restrictions. + +***************************************************************************/ + +/* + + TODO: + + - break detection + - wr0 reset tx interrupt pending + - wait/ready + - 1.5 stop bits + +*/ + +#include "emu.h" +#include "z80dart.h" +#include "cpu/z80/z80.h" +#include "cpu/z80/z80daisy.h" + +/*************************************************************************** + PARAMETERS +***************************************************************************/ + +#define VERBOSE 0 + +#define LOG(x) do { if (VERBOSE) logerror x; } while (0) + +enum +{ + STATE_START = 0, + STATE_DATA, + STATE_PARITY, + STATE_STOP, + STATE_STOP2 +}; + +enum +{ + INT_TRANSMIT = 0, + INT_EXTERNAL, + INT_RECEIVE, + INT_SPECIAL +}; + +#define Z80DART_RR0_RX_CHAR_AVAILABLE 0x01 +#define Z80DART_RR0_INTERRUPT_PENDING 0x02 +#define Z80DART_RR0_TX_BUFFER_EMPTY 0x04 +#define Z80DART_RR0_DCD 0x08 +#define Z80DART_RR0_RI 0x10 +#define Z80DART_RR0_CTS 0x20 +#define Z80DART_RR0_BREAK 0x80 /* not supported */ + +#define Z80DART_RR1_ALL_SENT 0x01 +#define Z80DART_RR1_PARITY_ERROR 0x10 +#define Z80DART_RR1_RX_OVERRUN_ERROR 0x20 +#define Z80DART_RR1_FRAMING_ERROR 0x40 + +#define Z80DART_WR0_REGISTER_MASK 0x07 +#define Z80DART_WR0_COMMAND_MASK 0x38 +#define Z80DART_WR0_NULL_CODE 0x00 +#define Z80DART_WR0_RESET_EXT_STATUS 0x10 +#define Z80DART_WR0_CHANNEL_RESET 0x18 +#define Z80DART_WR0_ENABLE_INT_NEXT_RX 0x20 +#define Z80DART_WR0_RESET_TX_INT 0x28 /* not supported */ +#define Z80DART_WR0_ERROR_RESET 0x30 +#define Z80DART_WR0_RETURN_FROM_INT 0x38 /* not supported */ + +#define Z80DART_WR1_EXT_INT_ENABLE 0x01 +#define Z80DART_WR1_TX_INT_ENABLE 0x02 +#define Z80DART_WR1_STATUS_VECTOR 0x04 +#define Z80DART_WR1_RX_INT_ENABLE_MASK 0x18 +#define Z80DART_WR1_RX_INT_DISABLE 0x00 +#define Z80DART_WR1_RX_INT_FIRST 0x08 +#define Z80DART_WR1_RX_INT_ALL_PARITY 0x10 /* not supported */ +#define Z80DART_WR1_RX_INT_ALL 0x18 +#define Z80DART_WR1_WRDY_ON_RX_TX 0x20 /* not supported */ +#define Z80DART_WR1_WRDY_FUNCTION 0x40 /* not supported */ +#define Z80DART_WR1_WRDY_ENABLE 0x80 /* not supported */ + +#define Z80DART_WR3_RX_ENABLE 0x01 +#define Z80DART_WR3_AUTO_ENABLES 0x20 +#define Z80DART_WR3_RX_WORD_LENGTH_MASK 0xc0 +#define Z80DART_WR3_RX_WORD_LENGTH_5 0x00 +#define Z80DART_WR3_RX_WORD_LENGTH_7 0x40 +#define Z80DART_WR3_RX_WORD_LENGTH_6 0x80 +#define Z80DART_WR3_RX_WORD_LENGTH_8 0xc0 + +#define Z80DART_WR4_PARITY_ENABLE 0x01 /* not supported */ +#define Z80DART_WR4_PARITY_EVEN 0x02 /* not supported */ +#define Z80DART_WR4_STOP_BITS_MASK 0x0c +#define Z80DART_WR4_STOP_BITS_1 0x04 +#define Z80DART_WR4_STOP_BITS_1_5 0x08 /* not supported */ +#define Z80DART_WR4_STOP_BITS_2 0x0c +#define Z80DART_WR4_CLOCK_MODE_MASK 0xc0 +#define Z80DART_WR4_CLOCK_MODE_X1 0x00 +#define Z80DART_WR4_CLOCK_MODE_X16 0x40 +#define Z80DART_WR4_CLOCK_MODE_X32 0x80 +#define Z80DART_WR4_CLOCK_MODE_X64 0xc0 + +#define Z80DART_WR5_RTS 0x02 +#define Z80DART_WR5_TX_ENABLE 0x08 +#define Z80DART_WR5_SEND_BREAK 0x10 +#define Z80DART_WR5_TX_WORD_LENGTH_MASK 0xc0 +#define Z80DART_WR5_TX_WORD_LENGTH_5 0x00 +#define Z80DART_WR5_TX_WORD_LENGTH_7 0x40 +#define Z80DART_WR5_TX_WORD_LENGTH_6 0x80 +#define Z80DART_WR5_TX_WORD_LENGTH_8 0xc0 +#define Z80DART_WR5_DTR 0x80 + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +typedef struct _dart_channel dart_channel; +struct _dart_channel +{ + devcb_resolved_read_line in_rxd_func; + devcb_resolved_write_line out_txd_func; + devcb_resolved_write_line out_dtr_func; + devcb_resolved_write_line out_rts_func; + devcb_resolved_write_line out_wrdy_func; + + /* register state */ + UINT8 rr[3]; /* read register */ + UINT8 wr[6]; /* write register */ + + /* receiver state */ + UINT8 rx_data_fifo[3]; /* receive data FIFO */ + UINT8 rx_error_fifo[3]; /* receive error FIFO */ + UINT8 rx_shift; /* 8-bit receive shift register */ + UINT8 rx_error; /* current receive error */ + int rx_fifo; /* receive FIFO pointer */ + + int rx_clock; /* receive clock pulse count */ + int rx_state; /* receive state */ + int rx_bits; /* bits received */ + int rx_first; /* first character received */ + int rx_parity; /* received data parity */ + int rx_break; /* receive break condition */ + UINT8 rx_rr0_latch; /* read register 0 latched */ + + int ri; /* ring indicator latch */ + int cts; /* clear to send latch */ + int dcd; /* data carrier detect latch */ + + /* transmitter state */ + UINT8 tx_data; /* transmit data register */ + UINT8 tx_shift; /* transmit shift register */ + + int tx_clock; /* transmit clock pulse count */ + int tx_state; /* transmit state */ + int tx_bits; /* bits transmitted */ + int tx_parity; /* transmitted data parity */ + + int dtr; /* data terminal ready */ + int rts; /* request to send */ +}; + +typedef struct _z80dart_t z80dart_t; +struct _z80dart_t +{ + devcb_resolved_write_line out_int_func; + + dart_channel channel[2]; /* channels */ + + int int_state[8]; /* interrupt state */ + + /* timers */ + emu_timer *rxca_timer; + emu_timer *txca_timer; + emu_timer *rxtxcb_timer; +}; + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ + +static int z80dart_irq_state(running_device *device); +static void z80dart_irq_reti(running_device *device); + +/*************************************************************************** + INLINE FUNCTIONS +***************************************************************************/ + +INLINE z80dart_t *get_safe_token(running_device *device) +{ + assert(device != NULL); + assert(device->token != NULL); + assert(device->type == Z80DART); + return (z80dart_t *)device->token; +} + +INLINE const z80dart_interface *get_interface(running_device *device) +{ + assert(device != NULL); + assert((device->type == Z80DART)); + return (const z80dart_interface *) device->baseconfig().static_config; +} + +/*************************************************************************** + IMPLEMENTATION +***************************************************************************/ + +#define RXD \ + devcb_call_read_line(&ch->in_rxd_func) + +#define TXD(_state) \ + devcb_call_write_line(&ch->out_txd_func, _state) + +#define RTS(_state) \ + devcb_call_write_line(&ch->out_rts_func, _state) + +#define DTR(_state) \ + devcb_call_write_line(&ch->out_dtr_func, _state) + +/*------------------------------------------------- + check_interrupts - control interrupt line +-------------------------------------------------*/ + +static void check_interrupts(running_device *device) +{ + z80dart_t *z80dart = get_safe_token(device); + int state = (z80dart_irq_state(device) & Z80_DAISY_INT) ? ASSERT_LINE : CLEAR_LINE; + + devcb_call_write_line(&z80dart->out_int_func, state); +} + +/*------------------------------------------------- + take_interrupt - trigger interrupt +-------------------------------------------------*/ + +static void take_interrupt(running_device *device, int channel, int level) +{ + z80dart_t *z80dart = get_safe_token(device); + dart_channel *ch = &z80dart->channel[channel]; + UINT8 vector = ch->wr[2]; + int priority = (channel << 2) | level; + + LOG(("Z80DART \"%s\" Channel %c : Interrupt Request %u\n", device->tag.cstr(), 'A' + channel, level)); + + if ((channel == Z80DART_CH_B) && (ch->wr[1] & Z80DART_WR1_STATUS_VECTOR)) + { + /* status affects vector */ + vector = (ch->wr[2] & 0xf1) | (!channel << 3) | (level << 1); + } + + /* update vector register */ + ch->rr[2] = vector; + + /* trigger interrupt */ + z80dart->int_state[priority] |= Z80_DAISY_INT; + z80dart->channel[Z80DART_CH_A].rr[0] |= Z80DART_RR0_INTERRUPT_PENDING; + + /* check for interrupt */ + check_interrupts(device); +} + +/*------------------------------------------------- + get_clock_mode - get clock divisor +-------------------------------------------------*/ + +static int get_clock_mode(dart_channel *ch) +{ + int clocks = 1; + + switch (ch->wr[4] & Z80DART_WR4_CLOCK_MODE_MASK) + { + case Z80DART_WR4_CLOCK_MODE_X1: clocks = 1; break; + case Z80DART_WR4_CLOCK_MODE_X16: clocks = 16; break; + case Z80DART_WR4_CLOCK_MODE_X32: clocks = 32; break; + case Z80DART_WR4_CLOCK_MODE_X64: clocks = 64; break; + } + + return clocks; +} + +/*------------------------------------------------- + get_stop_bits - get number of stop bits +-------------------------------------------------*/ + +static float get_stop_bits(dart_channel *ch) +{ + float bits = 1; + + switch (ch->wr[4] & Z80DART_WR4_STOP_BITS_MASK) + { + case Z80DART_WR4_STOP_BITS_1: bits = 1; break; + case Z80DART_WR4_STOP_BITS_1_5: bits = 1.5; break; + case Z80DART_WR4_STOP_BITS_2: bits = 2; break; + } + + return bits; +} + +/*------------------------------------------------- + get_rx_word_length - get receive word length +-------------------------------------------------*/ + +static int get_rx_word_length(dart_channel *ch) +{ + int bits = 5; + + switch (ch->wr[3] & Z80DART_WR3_RX_WORD_LENGTH_MASK) + { + case Z80DART_WR3_RX_WORD_LENGTH_5: bits = 5; break; + case Z80DART_WR3_RX_WORD_LENGTH_6: bits = 6; break; + case Z80DART_WR3_RX_WORD_LENGTH_7: bits = 7; break; + case Z80DART_WR3_RX_WORD_LENGTH_8: bits = 8; break; + } + + return bits; +} + +/*------------------------------------------------- + get_rx_word_length - get transmit word length +-------------------------------------------------*/ + +static int get_tx_word_length(dart_channel *ch) +{ + int bits = 5; + + switch (ch->wr[5] & Z80DART_WR5_TX_WORD_LENGTH_MASK) + { + case Z80DART_WR5_TX_WORD_LENGTH_5: bits = 5; break; + case Z80DART_WR5_TX_WORD_LENGTH_6: bits = 6; break; + case Z80DART_WR5_TX_WORD_LENGTH_7: bits = 7; break; + case Z80DART_WR5_TX_WORD_LENGTH_8: bits = 8; break; + } + + return bits; +} + +/*------------------------------------------------- + reset_channel - reset channel status +-------------------------------------------------*/ + +static void reset_channel(running_device *device, int channel) +{ + z80dart_t *z80dart = get_safe_token(device); + dart_channel *ch = &z80dart->channel[channel]; + + /* disable receiver */ + ch->wr[3] &= ~Z80DART_WR3_RX_ENABLE; + ch->rx_state = STATE_START; + + /* disable transmitter */ + ch->wr[5] &= ~Z80DART_WR5_TX_ENABLE; + ch->tx_state = STATE_START; + + /* reset external lines */ + RTS(1); + DTR(1); + + if (channel == Z80DART_CH_A) + { + /* reset interrupt logic */ + int i; + + for (i = 0; i < 8; i++) + { + z80dart->int_state[i] = 0; + } + + check_interrupts(device); + } +} + +/*------------------------------------------------- + detect_start_bit - detect start bit +-------------------------------------------------*/ + +static int detect_start_bit(dart_channel *ch) +{ + if (!(ch->wr[3] & Z80DART_WR3_RX_ENABLE)) return 0; + + return !RXD; +} + +/*------------------------------------------------- + shift_data_in - shift in serial data +-------------------------------------------------*/ + +static void shift_data_in(dart_channel *ch) +{ + if (ch->rx_bits < 8) + { + int rxd = RXD; + + ch->rx_shift >>= 1; + ch->rx_shift = (rxd << 7) | (ch->rx_shift & 0x7f); + ch->rx_parity ^= rxd; + ch->rx_bits++; + } +} + +/*------------------------------------------------- + character_completed - check if complete + data word has been transferred +-------------------------------------------------*/ + +static int character_completed(dart_channel *ch) +{ + return ch->rx_bits == get_rx_word_length(ch); +} + +/*------------------------------------------------- + detect_parity_error - detect parity error +-------------------------------------------------*/ + +static void detect_parity_error(running_device *device, int channel) +{ + z80dart_t *z80dart = get_safe_token(device); + dart_channel *ch = &z80dart->channel[channel]; + + int parity = (ch->wr[1] & Z80DART_WR4_PARITY_EVEN) ? 1 : 0; + + if (RXD != (ch->rx_parity ^ parity)) + { + /* parity error detected */ + ch->rx_error |= Z80DART_RR1_PARITY_ERROR; + + switch (ch->wr[1] & Z80DART_WR1_RX_INT_ENABLE_MASK) + { + case Z80DART_WR1_RX_INT_FIRST: + if (!ch->rx_first) + { + take_interrupt(device, channel, INT_SPECIAL); + } + break; + + case Z80DART_WR1_RX_INT_ALL_PARITY: + take_interrupt(device, channel, INT_SPECIAL); + break; + + case Z80DART_WR1_RX_INT_ALL: + take_interrupt(device, channel, INT_RECEIVE); + break; + } + } +} + +/*------------------------------------------------- + detect_framing_error - detect framing error +-------------------------------------------------*/ + +static void detect_framing_error(running_device *device, int channel) +{ + z80dart_t *z80dart = get_safe_token(device); + dart_channel *ch = &z80dart->channel[channel]; + + if (!RXD) + { + /* framing error detected */ + ch->rx_error |= Z80DART_RR1_FRAMING_ERROR; + + switch (ch->wr[1] & Z80DART_WR1_RX_INT_ENABLE_MASK) + { + case Z80DART_WR1_RX_INT_FIRST: + if (!ch->rx_first) + { + take_interrupt(device, channel, INT_SPECIAL); + } + break; + + case Z80DART_WR1_RX_INT_ALL_PARITY: + case Z80DART_WR1_RX_INT_ALL: + take_interrupt(device, channel, INT_SPECIAL); + break; + } + } +} + +/*------------------------------------------------- + receive - receive serial data +-------------------------------------------------*/ + +static void receive(running_device *device, int channel) +{ + z80dart_t *z80dart = get_safe_token(device); + dart_channel *ch = &z80dart->channel[channel]; + + float stop_bits = get_stop_bits(ch); + + switch (ch->rx_state) + { + case STATE_START: + /* check for start bit */ + if (detect_start_bit(ch)) + { + /* start bit detected */ + ch->rx_shift = 0; + ch->rx_error = 0; + ch->rx_bits = 0; + ch->rx_parity = 0; + + /* next bit is a data bit */ + ch->rx_state = STATE_DATA; + } + break; + + case STATE_DATA: + /* shift bit into shift register */ + shift_data_in(ch); + + if (character_completed(ch)) + { + /* all data bits received */ + if (ch->wr[4] & Z80DART_WR4_PARITY_ENABLE) + { + /* next bit is the parity bit */ + ch->rx_state = STATE_PARITY; + } + else + { + /* next bit is a STOP bit */ + if (stop_bits == 1) + ch->rx_state = STATE_STOP2; + else + ch->rx_state = STATE_STOP; + } + } + break; + + case STATE_PARITY: + /* shift bit into shift register */ + shift_data_in(ch); + + /* check for parity error */ + detect_parity_error(device, channel); + + /* next bit is a STOP bit */ + if (stop_bits == 1) + ch->rx_state = STATE_STOP2; + else + ch->rx_state = STATE_STOP; + break; + + case STATE_STOP: + /* shift bit into shift register */ + shift_data_in(ch); + + /* check for framing error */ + detect_framing_error(device, channel); + + /* next bit is the second STOP bit */ + ch->rx_state = STATE_STOP2; + break; + + case STATE_STOP2: + /* shift bit into shift register */ + shift_data_in(ch); + + /* check for framing error */ + detect_framing_error(device, channel); + + /* store data into FIFO */ + z80dart_receive_data(device, channel, ch->rx_shift); + + /* next bit is the START bit */ + ch->rx_state = STATE_START; + break; + } +} + +/*------------------------------------------------- + transmit - transmit serial data +-------------------------------------------------*/ + +static void transmit(running_device *device, int channel) +{ + z80dart_t *z80dart = get_safe_token(device); + dart_channel *ch = &z80dart->channel[channel]; + + int word_length = get_tx_word_length(ch); + float stop_bits = get_stop_bits(ch); + + switch (ch->tx_state) + { + case STATE_START: + if ((ch->wr[5] & Z80DART_WR5_TX_ENABLE) && !(ch->rr[0] & Z80DART_RR0_TX_BUFFER_EMPTY)) + { + /* transmit start bit */ + TXD(0); + + ch->tx_bits = 0; + ch->tx_shift = ch->tx_data; + + /* empty transmit buffer */ + ch->rr[0] |= Z80DART_RR0_TX_BUFFER_EMPTY; + + if (ch->wr[1] & Z80DART_WR1_TX_INT_ENABLE) + take_interrupt(device, channel, INT_TRANSMIT); + + ch->tx_state = STATE_DATA; + } + else if (ch->wr[5] & Z80DART_WR5_SEND_BREAK) + { + /* transmit break */ + TXD(0); + } + else + { + /* transmit marking line */ + TXD(1); + } + + break; + + case STATE_DATA: + /* transmit data bit */ + TXD(BIT(ch->tx_shift, 0)); + + /* shift data */ + ch->tx_shift >>= 1; + ch->tx_bits++; + + if (ch->rx_bits == word_length) + { + if (ch->wr[4] & Z80DART_WR4_PARITY_ENABLE) + ch->tx_state = STATE_PARITY; + else + { + if (stop_bits == 1) + ch->tx_state = STATE_STOP2; + else + ch->tx_state = STATE_STOP; + } + } + break; + + case STATE_PARITY: + // TODO: calculate parity + if (stop_bits == 1) + ch->tx_state = STATE_STOP2; + else + ch->tx_state = STATE_STOP; + break; + + case STATE_STOP: + /* transmit stop bit */ + TXD(1); + + ch->tx_state = STATE_STOP2; + break; + + case STATE_STOP2: + /* transmit stop bit */ + TXD(1); + + /* if transmit buffer is empty */ + if (ch->rr[0] & Z80DART_RR0_TX_BUFFER_EMPTY) + { + /* then all characters have been sent */ + ch->rr[1] |= Z80DART_RR1_ALL_SENT; + + /* when the RTS bit is reset, the _RTS output goes high after the transmitter empties */ + if (!ch->rts) + RTS(1); + } + + ch->tx_state = STATE_START; + break; + } +} + +/*------------------------------------------------- + z80dart_c_r - read control register +-------------------------------------------------*/ + +READ8_DEVICE_HANDLER( z80dart_c_r ) +{ + z80dart_t *z80dart = get_safe_token(device); + int channel = offset & 0x01; + dart_channel *ch = &z80dart->channel[channel]; + UINT8 data = 0; + + int reg = ch->wr[0] & Z80DART_WR0_REGISTER_MASK; + + switch (reg) + { + case 0: + case 1: + data = ch->rr[reg]; + break; + + case 2: + /* channel B only */ + if (channel == Z80DART_CH_B) + data = ch->rr[reg]; + break; + } + + LOG(("Z80DART \"%s\" Channel %c : Control Register Read '%02x'\n", device->tag.cstr(), 'A' + channel, data)); + + return data; +} + +/*------------------------------------------------- + z80dart_c_w - write control register +-------------------------------------------------*/ + +WRITE8_DEVICE_HANDLER( z80dart_c_w ) +{ + z80dart_t *z80dart = get_safe_token(device); + int channel = offset & 0x01; + dart_channel *ch = &z80dart->channel[channel]; + + int reg = ch->wr[0] & Z80DART_WR0_REGISTER_MASK; + + LOG(("Z80DART \"%s\" Channel %c : Control Register Write '%02x'\n", device->tag.cstr(), 'A' + channel, data)); + + /* write data to selected register */ + ch->wr[reg] = data; + + if (reg != 0) + { + /* mask out register index */ + ch->wr[0] &= ~Z80DART_WR0_REGISTER_MASK; + } + + switch (reg) + { + case 0: + switch (data & Z80DART_WR0_COMMAND_MASK) + { + case Z80DART_WR0_RESET_EXT_STATUS: + /* reset external/status interrupt */ + ch->rr[0] &= ~(Z80DART_RR0_DCD | Z80DART_RR0_RI | Z80DART_RR0_CTS | Z80DART_RR0_BREAK); + + if (!ch->dcd) ch->rr[0] |= Z80DART_RR0_DCD; + if (ch->ri) ch->rr[0] |= Z80DART_RR0_RI; + if (ch->cts) ch->rr[0] |= Z80DART_RR0_CTS; + + ch->rx_rr0_latch = 0; + + LOG(("Z80DART \"%s\" Channel %c : Reset External/Status Interrupt\n", device->tag.cstr(), 'A' + channel)); + break; + + case Z80DART_WR0_CHANNEL_RESET: + /* channel reset */ + LOG(("Z80DART \"%s\" Channel %c : Channel Reset\n", device->tag.cstr(), 'A' + channel)); + reset_channel(device, channel); + break; + + case Z80DART_WR0_ENABLE_INT_NEXT_RX: + /* enable interrupt on next receive character */ + LOG(("Z80DART \"%s\" Channel %c : Enable Interrupt on Next Received Character\n", device->tag.cstr(), 'A' + channel)); + ch->rx_first = 1; + break; + + case Z80DART_WR0_RESET_TX_INT: + /* reset transmitter interrupt pending */ + LOG(("Z80DART \"%s\" Channel %c : Reset Transmitter Interrupt Pending\n", device->tag.cstr(), 'A' + channel)); + break; + + case Z80DART_WR0_ERROR_RESET: + /* error reset */ + LOG(("Z80DART \"%s\" Channel %c : Error Reset\n", device->tag.cstr(), 'A' + channel)); + ch->rr[1] &= ~(Z80DART_RR1_FRAMING_ERROR | Z80DART_RR1_RX_OVERRUN_ERROR | Z80DART_RR1_PARITY_ERROR); + break; + + case Z80DART_WR0_RETURN_FROM_INT: + /* return from interrupt */ + LOG(("Z80DART \"%s\" Channel %c : Return from Interrupt\n", device->tag.cstr(), 'A' + channel)); + z80dart_irq_reti(device); + break; + } + break; + + case 1: + LOG(("Z80DART \"%s\" Channel %c : External Interrupt Enable %u\n", device->tag.cstr(), 'A' + channel, (data & Z80DART_WR1_EXT_INT_ENABLE) ? 1 : 0)); + LOG(("Z80DART \"%s\" Channel %c : Transmit Interrupt Enable %u\n", device->tag.cstr(), 'A' + channel, (data & Z80DART_WR1_TX_INT_ENABLE) ? 1 : 0)); + LOG(("Z80DART \"%s\" Channel %c : Status Affects Vector %u\n", device->tag.cstr(), 'A' + channel, (data & Z80DART_WR1_STATUS_VECTOR) ? 1 : 0)); + LOG(("Z80DART \"%s\" Channel %c : Wait/Ready Enable %u\n", device->tag.cstr(), 'A' + channel, (data & Z80DART_WR1_WRDY_ENABLE) ? 1 : 0)); + + switch (data & Z80DART_WR1_RX_INT_ENABLE_MASK) + { + case Z80DART_WR1_RX_INT_DISABLE: + LOG(("Z80DART \"%s\" Channel %c : Receiver Interrupt Disabled\n", device->tag.cstr(), 'A' + channel)); + break; + + case Z80DART_WR1_RX_INT_FIRST: + LOG(("Z80DART \"%s\" Channel %c : Receiver Interrupt on First Character\n", device->tag.cstr(), 'A' + channel)); + break; + + case Z80DART_WR1_RX_INT_ALL_PARITY: + LOG(("Z80DART \"%s\" Channel %c : Receiver Interrupt on All Characters, Parity Affects Vector\n", device->tag.cstr(), 'A' + channel)); + break; + + case Z80DART_WR1_RX_INT_ALL: + LOG(("Z80DART \"%s\" Channel %c : Receiver Interrupt on All Characters\n", device->tag.cstr(), 'A' + channel)); + break; + } + + check_interrupts(device); + break; + + case 2: + /* interrupt vector */ + check_interrupts(device); + LOG(("Z80DART \"%s\" Channel %c : Interrupt Vector %02x\n", device->tag.cstr(), 'A' + channel, data)); + break; + + case 3: + LOG(("Z80DART \"%s\" Channel %c : Receiver Enable %u\n", device->tag.cstr(), 'A' + channel, (data & Z80DART_WR3_RX_ENABLE) ? 1 : 0)); + LOG(("Z80DART \"%s\" Channel %c : Auto Enables %u\n", device->tag.cstr(), 'A' + channel, (data & Z80DART_WR3_AUTO_ENABLES) ? 1 : 0)); + LOG(("Z80DART \"%s\" Channel %c : Receiver Bits/Character %u\n", device->tag.cstr(), 'A' + channel, get_rx_word_length(ch))); + break; + + case 4: + LOG(("Z80DART \"%s\" Channel %c : Parity Enable %u\n", device->tag.cstr(), 'A' + channel, (data & Z80DART_WR4_PARITY_ENABLE) ? 1 : 0)); + LOG(("Z80DART \"%s\" Channel %c : Parity %s\n", device->tag.cstr(), 'A' + channel, (data & Z80DART_WR4_PARITY_EVEN) ? "Even" : "Odd")); + LOG(("Z80DART \"%s\" Channel %c : Stop Bits %f\n", device->tag.cstr(), 'A' + channel, get_stop_bits(ch))); + LOG(("Z80DART \"%s\" Channel %c : Clock Mode %uX\n", device->tag.cstr(), 'A' + channel, get_clock_mode(ch))); + break; + + case 5: + LOG(("Z80DART \"%s\" Channel %c : Transmitter Enable %u\n", device->tag.cstr(), 'A' + channel, (data & Z80DART_WR5_TX_ENABLE) ? 1 : 0)); + LOG(("Z80DART \"%s\" Channel %c : Transmitter Bits/Character %u\n", device->tag.cstr(), 'A' + channel, get_tx_word_length(ch))); + LOG(("Z80DART \"%s\" Channel %c : Send Break %u\n", device->tag.cstr(), 'A' + channel, (data & Z80DART_WR5_SEND_BREAK) ? 1 : 0)); + LOG(("Z80DART \"%s\" Channel %c : Request to Send %u\n", device->tag.cstr(), 'A' + channel, (data & Z80DART_WR5_RTS) ? 1 : 0)); + LOG(("Z80DART \"%s\" Channel %c : Data Terminal Ready %u\n", device->tag.cstr(), 'A' + channel, (data & Z80DART_WR5_DTR) ? 1 : 0)); + + if (data & Z80DART_WR5_RTS) + { + /* when the RTS bit is set, the _RTS output goes low */ + RTS(0); + + ch->rts = 1; + } + else + { + /* when the RTS bit is reset, the _RTS output goes high after the transmitter empties */ + ch->rts = 0; + } + + /* data terminal ready output follows the state programmed into the DTR bit*/ + ch->dtr = (data & Z80DART_WR5_DTR) ? 0 : 1; + DTR(ch->dtr); + break; + } +} + +/*------------------------------------------------- + z80dart_d_r - read data register +-------------------------------------------------*/ + +READ8_DEVICE_HANDLER( z80dart_d_r ) +{ + z80dart_t *z80dart = get_safe_token(device); + int channel = offset & 0x01; + dart_channel *ch = &z80dart->channel[channel]; + UINT8 data = 0; + + if (ch->rx_fifo >= 0) + { + /* load data from the FIFO */ + data = ch->rx_data_fifo[ch->rx_fifo]; + + /* load error status from the FIFO, retain overrun and parity errors */ + ch->rr[1] = (ch->rr[1] & (Z80DART_RR1_RX_OVERRUN_ERROR | Z80DART_RR1_PARITY_ERROR)) | ch->rx_error_fifo[ch->rx_fifo]; + + /* decrease FIFO pointer */ + ch->rx_fifo--; + + if (ch->rx_fifo < 0) + { + /* no more characters available in the FIFO */ + ch->rr[0] &= ~ Z80DART_RR0_RX_CHAR_AVAILABLE; + } + } + + LOG(("Z80DART \"%s\" Channel %c : Data Register Read '%02x'\n", device->tag.cstr(), 'A' + channel, data)); + + return data; +} + +/*------------------------------------------------- + z80dart_d_w - write data register +-------------------------------------------------*/ + +WRITE8_DEVICE_HANDLER( z80dart_d_w ) +{ + z80dart_t *z80dart = get_safe_token(device); + int channel = offset & 0x01; + dart_channel *ch = &z80dart->channel[channel]; + + ch->tx_data = data; + + ch->rr[0] &= ~Z80DART_RR0_TX_BUFFER_EMPTY; + ch->rr[1] &= ~Z80DART_RR1_ALL_SENT; + + LOG(("Z80DART \"%s\" Channel %c : Data Register Write '%02x'\n", device->tag.cstr(), 'A' + channel, data)); +} + +/*------------------------------------------------- + z80dart_receive_data - receive data word +-------------------------------------------------*/ + +void z80dart_receive_data(running_device *device, int channel, UINT8 data) +{ + z80dart_t *z80dart = get_safe_token(device); + dart_channel *ch = &z80dart->channel[channel]; + + LOG(("Z80DART \"%s\" Channel %c : Receive Data Byte '%02x'\n", device->tag.cstr(), 'A' + channel, data)); + + if (ch->rx_fifo == 2) + { + /* receive overrun error detected */ + ch->rx_error |= Z80DART_RR1_RX_OVERRUN_ERROR; + + switch (ch->wr[1] & Z80DART_WR1_RX_INT_ENABLE_MASK) + { + case Z80DART_WR1_RX_INT_FIRST: + if (!ch->rx_first) + { + take_interrupt(device, channel, INT_SPECIAL); + } + break; + + case Z80DART_WR1_RX_INT_ALL_PARITY: + case Z80DART_WR1_RX_INT_ALL: + take_interrupt(device, channel, INT_SPECIAL); + break; + } + } + else + { + ch->rx_fifo++; + } + + /* store received character and error status into FIFO */ + ch->rx_data_fifo[ch->rx_fifo] = data; + ch->rx_error_fifo[ch->rx_fifo] = ch->rx_error; + + ch->rr[0] |= Z80DART_RR0_RX_CHAR_AVAILABLE; + + /* receive interrupt */ + switch (ch->wr[1] & Z80DART_WR1_RX_INT_ENABLE_MASK) + { + case Z80DART_WR1_RX_INT_FIRST: + if (ch->rx_first) + { + take_interrupt(device, channel, INT_RECEIVE); + + ch->rx_first = 0; + } + break; + + case Z80DART_WR1_RX_INT_ALL_PARITY: + case Z80DART_WR1_RX_INT_ALL: + take_interrupt(device, channel, INT_RECEIVE); + break; + } +} + +/*------------------------------------------------- + cts_w - clear to send handler +-------------------------------------------------*/ + +static void cts_w(running_device *device, int channel, int state) +{ + z80dart_t *z80dart = get_safe_token(device); + dart_channel *ch = &z80dart->channel[channel]; + + LOG(("Z80DART \"%s\" Channel %c : CTS %u\n", device->tag.cstr(), 'A' + channel, state)); + + if (ch->cts != state) + { + /* enable transmitter if in auto enables mode */ + if (!state) + if (ch->wr[3] & Z80DART_WR3_AUTO_ENABLES) + ch->wr[5] |= Z80DART_WR5_TX_ENABLE; + + /* set clear to send */ + ch->cts = state; + + if (!ch->rx_rr0_latch) + { + if (!ch->cts) + ch->rr[0] |= Z80DART_RR0_CTS; + else + ch->rr[0] &= ~Z80DART_RR0_CTS; + + /* trigger interrupt */ + if (ch->wr[1] & Z80DART_WR1_EXT_INT_ENABLE) + { + /* trigger interrupt */ + take_interrupt(device, channel, INT_EXTERNAL); + + /* latch read register 0 */ + ch->rx_rr0_latch = 1; + } + } + } +} + +/*------------------------------------------------- + z80dart_ctsa_w - clear to send (channel A) +-------------------------------------------------*/ + +WRITE_LINE_DEVICE_HANDLER( z80dart_ctsa_w ) +{ + cts_w(device, Z80DART_CH_A, state); +} + +/*------------------------------------------------- + z80dart_ctsb_w - clear to send (channel B) +-------------------------------------------------*/ + +WRITE_LINE_DEVICE_HANDLER( z80dart_ctsb_w ) +{ + cts_w(device, Z80DART_CH_B, state); +} + +/*------------------------------------------------- + dcd_w - data carrier detected handler +-------------------------------------------------*/ + +static void dcd_w(running_device *device, int channel, int state) +{ + z80dart_t *z80dart = get_safe_token(device); + dart_channel *ch = &z80dart->channel[channel]; + + LOG(("Z80DART \"%s\" Channel %c : DCD %u\n", device->tag.cstr(), 'A' + channel, state)); + + if (ch->dcd != state) + { + /* enable receiver if in auto enables mode */ + if (!state) + if (ch->wr[3] & Z80DART_WR3_AUTO_ENABLES) + ch->wr[3] |= Z80DART_WR3_RX_ENABLE; + + /* set data carrier detect */ + ch->dcd = state; + + if (!ch->rx_rr0_latch) + { + if (ch->dcd) + ch->rr[0] |= Z80DART_RR0_DCD; + else + ch->rr[0] &= ~Z80DART_RR0_DCD; + + if (ch->wr[1] & Z80DART_WR1_EXT_INT_ENABLE) + { + /* trigger interrupt */ + take_interrupt(device, channel, INT_EXTERNAL); + + /* latch read register 0 */ + ch->rx_rr0_latch = 1; + } + } + } +} + +/*------------------------------------------------- + z80dart_dcda_w - data carrier detected + (channel A) +-------------------------------------------------*/ + +WRITE_LINE_DEVICE_HANDLER( z80dart_dcda_w ) +{ + dcd_w(device, Z80DART_CH_A, state); +} + +/*------------------------------------------------- + z80dart_dcdb_w - data carrier detected + (channel B) +-------------------------------------------------*/ + +WRITE_LINE_DEVICE_HANDLER( z80dart_dcdb_w ) +{ + dcd_w(device, Z80DART_CH_B, state); +} + +/*------------------------------------------------- + ri_w - ring indicator handler +-------------------------------------------------*/ + +static void ri_w(running_device *device, int channel, int state) +{ + z80dart_t *z80dart = get_safe_token(device); + dart_channel *ch = &z80dart->channel[channel]; + + LOG(("Z80DART \"%s\" Channel %c : RI %u\n", device->tag.cstr(), 'A' + channel, state)); + + if (ch->ri != state) + { + /* set ring indicator state */ + ch->ri = state; + + if (!ch->rx_rr0_latch) + { + if (ch->ri) + ch->rr[0] |= Z80DART_RR0_RI; + else + ch->rr[0] &= ~Z80DART_RR0_RI; + + if (ch->wr[1] & Z80DART_WR1_EXT_INT_ENABLE) + { + /* trigger interrupt */ + take_interrupt(device, channel, INT_EXTERNAL); + + /* latch read register 0 */ + ch->rx_rr0_latch = 1; + } + } + } +} + +/*------------------------------------------------- + z80dart_ria_w - ring indicator (channel A) +-------------------------------------------------*/ + +WRITE_LINE_DEVICE_HANDLER( z80dart_ria_w ) +{ + ri_w(device, Z80DART_CH_A, state); +} + +/*------------------------------------------------- + z80dart_rib_w - ring indicator (channel B) +-------------------------------------------------*/ + +WRITE_LINE_DEVICE_HANDLER( z80dart_rib_w ) +{ + ri_w(device, Z80DART_CH_B, state); +} + +/*------------------------------------------------- + z80dart_rxca_w - receive clock (channel A) +-------------------------------------------------*/ + +WRITE_LINE_DEVICE_HANDLER( z80dart_rxca_w ) +{ + z80dart_t *z80dart = get_safe_token(device); + dart_channel *ch = &z80dart->channel[Z80DART_CH_A]; + + int clocks = get_clock_mode(ch); + + if (!state) return; + + LOG(("Z80DART \"%s\" Channel A : Receiver Clock Pulse\n", device->tag.cstr())); + + ch->rx_clock++; + + if (ch->rx_clock == clocks) + { + ch->rx_clock = 0; + + /* receive data */ + receive(device, Z80DART_CH_A); + } +} + +/*------------------------------------------------- + z80dart_txca_w - transmit clock (channel A) +-------------------------------------------------*/ + +WRITE_LINE_DEVICE_HANDLER( z80dart_txca_w ) +{ + z80dart_t *z80dart = get_safe_token(device); + dart_channel *ch = &z80dart->channel[Z80DART_CH_A]; + + int clocks = get_clock_mode(ch); + + if (!state) return; + + LOG(("Z80DART \"%s\" Channel A : Transmitter Clock Pulse\n", device->tag.cstr())); + + ch->tx_clock++; + + if (ch->tx_clock == clocks) + { + ch->tx_clock = 0; + + /* transmit data */ + transmit(device, Z80DART_CH_A); + } +} + +/*------------------------------------------------- + z80dart_rxtxcb_w - receive/transmit clock + (channel B) +-------------------------------------------------*/ + +WRITE_LINE_DEVICE_HANDLER( z80dart_rxtxcb_w ) +{ + z80dart_t *z80dart = get_safe_token(device); + dart_channel *ch = &z80dart->channel[Z80DART_CH_B]; + + int clocks = get_clock_mode(ch); + + if (!state) return; + + LOG(("Z80DART \"%s\" Channel A : Receiver/Transmitter Clock Pulse\n", device->tag.cstr())); + + ch->rx_clock++; + ch->tx_clock++; + + if (ch->rx_clock == clocks) + { + ch->rx_clock = 0; + ch->tx_clock = 0; + + /* receive and transmit data */ + receive(device, Z80DART_CH_B); + transmit(device, Z80DART_CH_B); + } +} + +/*------------------------------------------------- + TIMER_CALLBACK( rxca_tick ) +-------------------------------------------------*/ + +static TIMER_CALLBACK( rxca_tick ) +{ + z80dart_rxca_w((running_device *)ptr, 1); +} + +/*------------------------------------------------- + TIMER_CALLBACK( txca_tick ) +-------------------------------------------------*/ + +static TIMER_CALLBACK( txca_tick ) +{ + z80dart_txca_w((running_device *)ptr, 1); +} + +/*------------------------------------------------- + TIMER_CALLBACK( rxtxcb_tick ) +-------------------------------------------------*/ + +static TIMER_CALLBACK( rxtxcb_tick ) +{ + z80dart_rxtxcb_w((running_device *)ptr, 1); +} + +/*------------------------------------------------- + z80dart_irq_state - get interrupt status +-------------------------------------------------*/ + +static int z80dart_irq_state(running_device *device) +{ + z80dart_t *z80dart = get_safe_token( device ); + int state = 0; + int i; + + LOG(("Z80DART \"%s\" : Interrupt State B:%d%d%d%d A:%d%d%d%d\n", device->tag.cstr(), + z80dart->int_state[0], z80dart->int_state[1], z80dart->int_state[2], z80dart->int_state[3], + z80dart->int_state[4], z80dart->int_state[5], z80dart->int_state[6], z80dart->int_state[7])); + + /* loop over all interrupt sources */ + for (i = 0; i < 8; i++) + { + /* if we're servicing a request, don't indicate more interrupts */ + if (z80dart->int_state[i] & Z80_DAISY_IEO) + { + state |= Z80_DAISY_IEO; + break; + } + state |= z80dart->int_state[i]; + } + + LOG(("Z80DART \"%s\" : Interrupt State %u\n", device->tag.cstr(), state)); + + return state; +} + +/*------------------------------------------------- + z80dart_irq_ack - interrupt acknowledge +-------------------------------------------------*/ + +static int z80dart_irq_ack(running_device *device) +{ + z80dart_t *z80dart = get_safe_token( device ); + int i; + + LOG(("Z80DART \"%s\" Interrupt Acknowledge\n", device->tag.cstr())); + + /* loop over all interrupt sources */ + for (i = 0; i < 8; i++) + { + /* find the first channel with an interrupt requested */ + if (z80dart->int_state[i] & Z80_DAISY_INT) + { + /* clear interrupt, switch to the IEO state, and update the IRQs */ + z80dart->int_state[i] = Z80_DAISY_IEO; + z80dart->channel[Z80DART_CH_A].rr[0] &= ~Z80DART_RR0_INTERRUPT_PENDING; + check_interrupts(device); + + LOG(("Z80DART \"%s\" : Interrupt Acknowledge Vector %02x\n", device->tag.cstr(), z80dart->channel[Z80DART_CH_B].rr[2])); + + return z80dart->channel[Z80DART_CH_B].rr[2]; + } + } + + logerror("z80dart_irq_ack: failed to find an interrupt to ack!\n"); + + return z80dart->channel[Z80DART_CH_B].rr[2]; +} + +/*------------------------------------------------- + z80dart_irq_reti - return from interrupt +-------------------------------------------------*/ + +static void z80dart_irq_reti(running_device *device) +{ + z80dart_t *z80dart = get_safe_token( device ); + int i; + + LOG(("Z80DART \"%s\" Return from Interrupt\n", device->tag.cstr())); + + /* loop over all interrupt sources */ + for (i = 0; i < 8; i++) + { + /* find the first channel with an IEO pending */ + if (z80dart->int_state[i] & Z80_DAISY_IEO) + { + /* clear the IEO state and update the IRQs */ + z80dart->int_state[i] &= ~Z80_DAISY_IEO; + check_interrupts(device); + return; + } + } + + logerror("z80dart_irq_reti: failed to find an interrupt to clear IEO on!\n"); +} + +/*------------------------------------------------- + z80dart_cd_ba_r - register read +-------------------------------------------------*/ + +READ8_DEVICE_HANDLER( z80dart_cd_ba_r ) +{ + return (offset & 2) ? z80dart_c_r(device, offset & 1) : z80dart_d_r(device, offset & 1); +} + +/*------------------------------------------------- + z80dart_cd_ba_w - register write +-------------------------------------------------*/ + +WRITE8_DEVICE_HANDLER( z80dart_cd_ba_w ) +{ + if (offset & 2) + z80dart_c_w(device, offset & 1, data); + else + z80dart_d_w(device, offset & 1, data); +} + +/*------------------------------------------------- + z80dart_ba_cd_r - register read +-------------------------------------------------*/ + +READ8_DEVICE_HANDLER( z80dart_ba_cd_r ) +{ + int channel = BIT(offset, 1); + + return (offset & 1) ? z80dart_c_r(device, channel) : z80dart_d_r(device, channel); +} + +/*------------------------------------------------- + z80dart_ba_cd_w - register write +-------------------------------------------------*/ + +WRITE8_DEVICE_HANDLER( z80dart_ba_cd_w ) +{ + int channel = BIT(offset, 1); + + if (offset & 1) + z80dart_c_w(device, channel, data); + else + z80dart_d_w(device, channel, data); +} + +/*------------------------------------------------- + DEVICE_START( z80dart ) +-------------------------------------------------*/ + +static DEVICE_START( z80dart ) +{ + z80dart_t *z80dart = get_safe_token(device); + const z80dart_interface *intf = (const z80dart_interface *)device->baseconfig().static_config; + int channel; + + /* resolve callbacks */ + devcb_resolve_read_line(&z80dart->channel[Z80DART_CH_A].in_rxd_func, &intf->in_rxda_func, device); + devcb_resolve_write_line(&z80dart->channel[Z80DART_CH_A].out_txd_func, &intf->out_txda_func, device); + devcb_resolve_write_line(&z80dart->channel[Z80DART_CH_A].out_dtr_func, &intf->out_dtra_func, device); + devcb_resolve_write_line(&z80dart->channel[Z80DART_CH_A].out_rts_func, &intf->out_rtsa_func, device); + devcb_resolve_write_line(&z80dart->channel[Z80DART_CH_A].out_wrdy_func, &intf->out_wrdya_func, device); + devcb_resolve_read_line(&z80dart->channel[Z80DART_CH_B].in_rxd_func, &intf->in_rxdb_func, device); + devcb_resolve_write_line(&z80dart->channel[Z80DART_CH_B].out_txd_func, &intf->out_txdb_func, device); + devcb_resolve_write_line(&z80dart->channel[Z80DART_CH_B].out_dtr_func, &intf->out_dtrb_func, device); + devcb_resolve_write_line(&z80dart->channel[Z80DART_CH_B].out_rts_func, &intf->out_rtsb_func, device); + devcb_resolve_write_line(&z80dart->channel[Z80DART_CH_B].out_wrdy_func, &intf->out_wrdyb_func, device); + devcb_resolve_write_line(&z80dart->out_int_func, &intf->out_int_func, device); + + if (intf->rx_clock_a) + { + /* allocate channel A receive timer */ + z80dart->rxca_timer = timer_alloc(device->machine, rxca_tick, (void *)device); + timer_adjust_periodic(z80dart->rxca_timer, attotime_zero, 0, ATTOTIME_IN_HZ(intf->rx_clock_a)); + } + + if (intf->tx_clock_a) + { + /* allocate channel A transmit timer */ + z80dart->txca_timer = timer_alloc(device->machine, txca_tick, (void *)device); + timer_adjust_periodic(z80dart->txca_timer, attotime_zero, 0, ATTOTIME_IN_HZ(intf->tx_clock_a)); + } + + if (intf->rx_tx_clock_b) + { + /* allocate channel B receive/transmit timer */ + z80dart->rxtxcb_timer = timer_alloc(device->machine, rxtxcb_tick, (void *)device); + timer_adjust_periodic(z80dart->rxtxcb_timer, attotime_zero, 0, ATTOTIME_IN_HZ(intf->rx_tx_clock_b)); + } + + /* register for state saving */ + for (channel = Z80DART_CH_A; channel <= Z80DART_CH_B; channel++) + { + state_save_register_device_item_array(device, channel, z80dart->channel[channel].rr); + state_save_register_device_item_array(device, channel, z80dart->channel[channel].wr); + state_save_register_device_item_array(device, channel, z80dart->channel[channel].rx_data_fifo); + state_save_register_device_item_array(device, channel, z80dart->channel[channel].rx_error_fifo); + state_save_register_device_item(device, channel, z80dart->channel[channel].rx_shift); + state_save_register_device_item(device, channel, z80dart->channel[channel].rx_error); + state_save_register_device_item(device, channel, z80dart->channel[channel].rx_fifo); + state_save_register_device_item(device, channel, z80dart->channel[channel].rx_clock); + state_save_register_device_item(device, channel, z80dart->channel[channel].rx_state); + state_save_register_device_item(device, channel, z80dart->channel[channel].rx_bits); + state_save_register_device_item(device, channel, z80dart->channel[channel].rx_first); + state_save_register_device_item(device, channel, z80dart->channel[channel].rx_parity); + state_save_register_device_item(device, channel, z80dart->channel[channel].rx_break); + state_save_register_device_item(device, channel, z80dart->channel[channel].rx_rr0_latch); + state_save_register_device_item(device, channel, z80dart->channel[channel].ri); + state_save_register_device_item(device, channel, z80dart->channel[channel].cts); + state_save_register_device_item(device, channel, z80dart->channel[channel].dcd); + state_save_register_device_item(device, channel, z80dart->channel[channel].tx_data); + state_save_register_device_item(device, channel, z80dart->channel[channel].tx_shift); + state_save_register_device_item(device, channel, z80dart->channel[channel].tx_clock); + state_save_register_device_item(device, channel, z80dart->channel[channel].tx_state); + state_save_register_device_item(device, channel, z80dart->channel[channel].tx_bits); + state_save_register_device_item(device, channel, z80dart->channel[channel].tx_parity); + state_save_register_device_item(device, channel, z80dart->channel[channel].dtr); + state_save_register_device_item(device, channel, z80dart->channel[channel].rts); + } + + state_save_register_device_item_array(device, 0, z80dart->int_state); +} + +/*------------------------------------------------- + DEVICE_RESET( z80dart ) +-------------------------------------------------*/ + +static DEVICE_RESET( z80dart ) +{ + int channel; + + LOG(("Z80DART \"%s\" Reset\n", device->tag.cstr())); + + for (channel = Z80DART_CH_A; channel <= Z80DART_CH_B; channel++) + { + reset_channel(device, channel); + } + + check_interrupts(device); +} + +/*------------------------------------------------- + DEVICE_GET_INFO( z80dart ) +-------------------------------------------------*/ + +DEVICE_GET_INFO( z80dart ) +{ + switch (state) + { + /* --- the following bits of info are returned as 64-bit signed integers --- */ + case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(z80dart_t); break; + case DEVINFO_INT_INLINE_CONFIG_BYTES: info->i = 0; break; + case DEVINFO_INT_CLASS: info->i = DEVICE_CLASS_PERIPHERAL; break; + + /* --- the following bits of info are returned as pointers to data or functions --- */ + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(z80dart); break; + case DEVINFO_FCT_STOP: /* Nothing */ break; + case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME(z80dart); break; + case DEVINFO_FCT_IRQ_STATE: info->f = (genf *)z80dart_irq_state; break; + case DEVINFO_FCT_IRQ_ACK: info->f = (genf *)z80dart_irq_ack; break; + case DEVINFO_FCT_IRQ_RETI: info->f = (genf *)z80dart_irq_reti; break; + + /* --- the following bits of info are returned as NULL-terminated strings --- */ + case DEVINFO_STR_NAME: strcpy(info->s, "Zilog Z80 DART"); break; + case DEVINFO_STR_FAMILY: strcpy(info->s, "Z80"); break; + case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; + case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; + case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright the MESS Team"); break; + } +} diff --git a/src/emu/machine/z80dart.h b/src/emu/machine/z80dart.h new file mode 100644 index 00000000000..c6da23f6d6f --- /dev/null +++ b/src/emu/machine/z80dart.h @@ -0,0 +1,132 @@ +/*************************************************************************** + + Z80 DART Dual Asynchronous Receiver/Transmitter implementation + + Copyright (c) 2008, The MESS Team. + Visit http://mamedev.org for licensing and usage restrictions. + +**************************************************************************** + _____ _____ + D1 1 |* \_/ | 40 D0 + D3 2 | | 39 D2 + D5 3 | | 38 D4 + D7 4 | | 37 D6 + _INT 5 | | 36 _IORQ + IEI 6 | | 35 _CE + IEO 7 | | 34 B/_A + _M1 8 | | 33 C/_D + Vdd 9 | | 32 _RD + _W/RDYA 10 | Z80-DART | 31 GND + _RIA 11 | | 30 _W/RDYB + RxDA 12 | | 29 _RIB + _RxCA 13 | | 28 RxDB + _TxCA 14 | | 27 _RxTxCB + TxDA 15 | | 26 TxDB + _DTRA 16 | | 25 _DTRB + _RTSA 17 | | 24 _RTSB + _CTSA 18 | | 23 _CTSB + _DCDA 19 | | 22 _DCDB + CLK 20 |_____________| 21 _RESET + +***************************************************************************/ + +#ifndef __Z80DART_H_ +#define __Z80DART_H_ + +#include "devcb.h" + +/*************************************************************************** + MACROS / CONSTANTS +***************************************************************************/ + +#define Z80DART DEVICE_GET_INFO_NAME(z80dart) +/* +#define Z8470 DEVICE_GET_INFO_NAME(z8470) +#define LH0081 DEVICE_GET_INFO_NAME(lh0088) +#define MK3881 DEVICE_GET_INFO_NAME(mk3888) +*/ + +#define MDRV_Z80DART_ADD(_tag, _clock, _config) \ + MDRV_DEVICE_ADD(_tag, Z80DART, _clock) \ + MDRV_DEVICE_CONFIG(_config) + +#define MDRV_Z80DART_REMOVE(_tag) \ + MDRV_DEVICE_REMOVE(_tag) + +#define Z80DART_INTERFACE(_name) \ + const z80dart_interface (_name) = + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +enum +{ + Z80DART_CH_A = 0, + Z80DART_CH_B +}; + +typedef struct _z80dart_interface z80dart_interface; +struct _z80dart_interface +{ + int rx_clock_a; /* channel A receive clock */ + int tx_clock_a; /* channel A transmit clock */ + int rx_tx_clock_b; /* channel B receive/transmit clock */ + + devcb_read_line in_rxda_func; + devcb_write_line out_txda_func; + devcb_write_line out_dtra_func; + devcb_write_line out_rtsa_func; + devcb_write_line out_wrdya_func; + + devcb_read_line in_rxdb_func; + devcb_write_line out_txdb_func; + devcb_write_line out_dtrb_func; + devcb_write_line out_rtsb_func; + devcb_write_line out_wrdyb_func; + + devcb_write_line out_int_func; +}; + +/*************************************************************************** + PROTOTYPES +***************************************************************************/ + +/* register access */ +READ8_DEVICE_HANDLER( z80dart_cd_ba_r ); +WRITE8_DEVICE_HANDLER( z80dart_cd_ba_w ); + +READ8_DEVICE_HANDLER( z80dart_ba_cd_r ); +WRITE8_DEVICE_HANDLER( z80dart_ba_cd_w ); + +/* control register access */ +WRITE8_DEVICE_HANDLER( z80dart_c_w ); +READ8_DEVICE_HANDLER( z80dart_c_r ); + +/* data register access */ +WRITE8_DEVICE_HANDLER( z80dart_d_w ); +READ8_DEVICE_HANDLER( z80dart_d_r ); + +/* serial clocks */ +WRITE_LINE_DEVICE_HANDLER( z80dart_rxca_w ); +WRITE_LINE_DEVICE_HANDLER( z80dart_txca_w ); +WRITE_LINE_DEVICE_HANDLER( z80dart_rxtxcb_w ); + +/* ring indicator */ +WRITE_LINE_DEVICE_HANDLER( z80dart_ria_w ); +WRITE_LINE_DEVICE_HANDLER( z80dart_rib_w ); + +/* data carrier detected */ +WRITE_LINE_DEVICE_HANDLER( z80dart_dcda_w ); +WRITE_LINE_DEVICE_HANDLER( z80dart_dcdb_w ); + +/* clear to send */ +WRITE_LINE_DEVICE_HANDLER( z80dart_ctsa_w ); +WRITE_LINE_DEVICE_HANDLER( z80dart_ctsb_w ); + +/* receive data byte HACK */ +void z80dart_receive_data(running_device *device, int channel, UINT8 data); + +DEVICE_GET_INFO( z80dart ); + +#endif diff --git a/src/emu/machine/z80sti.c b/src/emu/machine/z80sti.c new file mode 100644 index 00000000000..2738ec8a1e9 --- /dev/null +++ b/src/emu/machine/z80sti.c @@ -0,0 +1,849 @@ +/*************************************************************************** + + Mostek MK3801 Serial Timer Interrupt Controller (Z80-STI) emulation + + Copyright MESS Team. + Visit http://mamedev.org for licensing and usage restrictions. + +***************************************************************************/ + +/* + + TODO: + + - timers (other than delay mode) + - serial I/O + - reset behavior + +*/ + +#include "emu.h" +#include "z80sti.h" +#include "cpu/z80/z80.h" +#include "cpu/z80/z80daisy.h" + +/*************************************************************************** + PARAMETERS +***************************************************************************/ + +#define VERBOSE 0 + +#define LOG(x) do { if (VERBOSE) logerror x; } while (0) + +/* registers */ +enum +{ + Z80STI_REGISTER_IR = 0, + Z80STI_REGISTER_GPIP, + Z80STI_REGISTER_IPRB, + Z80STI_REGISTER_IPRA, + Z80STI_REGISTER_ISRB, + Z80STI_REGISTER_ISRA, + Z80STI_REGISTER_IMRB, + Z80STI_REGISTER_IMRA, + Z80STI_REGISTER_PVR, + Z80STI_REGISTER_TABC, + Z80STI_REGISTER_TBDR, + Z80STI_REGISTER_TADR, + Z80STI_REGISTER_UCR, + Z80STI_REGISTER_RSR, + Z80STI_REGISTER_TSR, + Z80STI_REGISTER_UDR +}; + +/* variable registers */ +enum +{ + Z80STI_REGISTER_IR_SCR = 0, + Z80STI_REGISTER_IR_TDDR, + Z80STI_REGISTER_IR_TCDR, + Z80STI_REGISTER_IR_AER, + Z80STI_REGISTER_IR_IERB, + Z80STI_REGISTER_IR_IERA, + Z80STI_REGISTER_IR_DDR, + Z80STI_REGISTER_IR_TCDC +}; + +/* timers */ +enum +{ + TIMER_A = 0, + TIMER_B, + TIMER_C, + TIMER_D, + TIMER_COUNT +}; + +/* interrupt levels */ +enum +{ + Z80STI_IR_P0 = 0, + Z80STI_IR_P1, + Z80STI_IR_P2, + Z80STI_IR_P3, + Z80STI_IR_TD, + Z80STI_IR_TC, + Z80STI_IR_P4, + Z80STI_IR_P5, + Z80STI_IR_TB, + Z80STI_IR_XE, + Z80STI_IR_XB, + Z80STI_IR_RE, + Z80STI_IR_RB, + Z80STI_IR_TA, + Z80STI_IR_P6, + Z80STI_IR_P7 +}; + +/* timer C/D control register */ +#define Z80STI_TCDC_TARS 0x80 +#define Z80STI_TCDC_TBRS 0x08 + +/* interrupt vector register */ +#define Z80STI_PVR_ISE 0x08 +#define Z80STI_PVR_VR4 0x10 + +/* general purpose I/O interrupt levels */ +static const int INT_LEVEL_GPIP[] = +{ + Z80STI_IR_P0, Z80STI_IR_P1, Z80STI_IR_P2, Z80STI_IR_P3, Z80STI_IR_P4, Z80STI_IR_P5, Z80STI_IR_P6, Z80STI_IR_P7 +}; + +/* timer interrupt levels */ +static const int INT_LEVEL_TIMER[] = +{ + Z80STI_IR_TA, Z80STI_IR_TB, Z80STI_IR_TC, Z80STI_IR_TD +}; + +/* interrupt vectors */ +static const UINT8 INT_VECTOR[] = +{ + 0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, + 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e +}; + +/* timer prescaler divisors */ +static const int PRESCALER[] = { 0, 4, 10, 16, 50, 64, 100, 200 }; + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +typedef struct _z80sti_t z80sti_t; +struct _z80sti_t +{ + /* device callbacks */ + devcb_resolved_read8 in_gpio_func; + devcb_resolved_write8 out_gpio_func; + devcb_resolved_read_line in_si_func; + devcb_resolved_write_line out_so_func; + devcb_resolved_write_line out_tao_func; + devcb_resolved_write_line out_tbo_func; + devcb_resolved_write_line out_tco_func; + devcb_resolved_write_line out_tdo_func; + devcb_resolved_write_line out_int_func; + + /* I/O state */ + UINT8 gpip; /* general purpose I/O register */ + UINT8 aer; /* active edge register */ + UINT8 ddr; /* data direction register */ + + /* interrupt state */ + UINT16 ier; /* interrupt enable register */ + UINT16 ipr; /* interrupt pending register */ + UINT16 isr; /* interrupt in-service register */ + UINT16 imr; /* interrupt mask register */ + UINT8 pvr; /* interrupt vector register */ + int int_state[16]; /* interrupt state */ + + /* timer state */ + UINT8 tabc; /* timer A/B control register */ + UINT8 tcdc; /* timer C/D control register */ + UINT8 tdr[TIMER_COUNT]; /* timer data registers */ + UINT8 tmc[TIMER_COUNT]; /* timer main counters */ + int to[TIMER_COUNT]; /* timer out latch */ + + /* serial state */ + UINT8 scr; /* synchronous character register */ + UINT8 ucr; /* USART control register */ + UINT8 tsr; /* transmitter status register */ + UINT8 rsr; /* receiver status register */ + UINT8 udr; /* USART data register */ + + /* timers */ + emu_timer *timer[TIMER_COUNT]; /* counter timers */ + emu_timer *rx_timer; /* serial receive timer */ + emu_timer *tx_timer; /* serial transmit timer */ +}; + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ + +static int z80sti_irq_state(running_device *device); +static void z80sti_irq_reti(running_device *device); + +/*************************************************************************** + INLINE FUNCTIONS +***************************************************************************/ + +INLINE z80sti_t *get_safe_token(running_device *device) +{ + assert(device != NULL); + assert(device->token != NULL); + assert(device->type == Z80STI); + return (z80sti_t *)device->token; +} + +INLINE const z80sti_interface *get_interface(running_device *device) +{ + assert(device != NULL); + assert((device->type == Z80STI)); + return (const z80sti_interface *) device->baseconfig().static_config; +} + +/*************************************************************************** + IMPLEMENTATION +***************************************************************************/ + +/*------------------------------------------------- + check_interrupts - set the interrupt request + line state +-------------------------------------------------*/ + +static void check_interrupts(z80sti_t *z80sti) +{ + if (z80sti->ipr & z80sti->imr) + { + devcb_call_write_line(&z80sti->out_int_func, ASSERT_LINE); + } + else + { + devcb_call_write_line(&z80sti->out_int_func, CLEAR_LINE); + } +} + +/*------------------------------------------------- + take_interrupt - mark an interrupt pending +-------------------------------------------------*/ + +static void take_interrupt(z80sti_t *z80sti, int level) +{ + /* set interrupt pending register bit */ + z80sti->ipr |= 1 << level; + + /* trigger interrupt */ + z80sti->int_state[level] |= Z80_DAISY_INT; + + check_interrupts(z80sti); +} + +/*------------------------------------------------- + serial_receive - receive serial bit +-------------------------------------------------*/ + +static void serial_receive(z80sti_t *z80sti) +{ +} + +/*------------------------------------------------- + TIMER_CALLBACK( rx_tick ) +-------------------------------------------------*/ + +static TIMER_CALLBACK( rx_tick ) +{ + running_device *device = (running_device *)ptr; + z80sti_t *z80sti = get_safe_token(device); + + serial_receive(z80sti); +} + +/*------------------------------------------------- + serial_transmit - transmit serial bit +-------------------------------------------------*/ + +static void serial_transmit(z80sti_t *z80sti) +{ +} + +/*------------------------------------------------- + TIMER_CALLBACK( tx_tick ) +-------------------------------------------------*/ + +static TIMER_CALLBACK( tx_tick ) +{ + running_device *device = (running_device *)ptr; + z80sti_t *z80sti = get_safe_token(device); + + serial_transmit(z80sti); +} + +/*------------------------------------------------- + z80sti_r - register read +-------------------------------------------------*/ + +READ8_DEVICE_HANDLER( z80sti_r ) +{ + z80sti_t *z80sti = get_safe_token(device); + + switch (offset & 0x0f) + { + case Z80STI_REGISTER_IR: + switch (z80sti->pvr & 0x07) + { + case Z80STI_REGISTER_IR_SCR: return z80sti->scr; + case Z80STI_REGISTER_IR_TDDR: return z80sti->tmc[TIMER_D]; + case Z80STI_REGISTER_IR_TCDR: return z80sti->tmc[TIMER_C]; + case Z80STI_REGISTER_IR_AER: return z80sti->aer; + case Z80STI_REGISTER_IR_IERB: return z80sti->ier & 0xff; + case Z80STI_REGISTER_IR_IERA: return z80sti->ier >> 8; + case Z80STI_REGISTER_IR_DDR: return z80sti->ddr; + case Z80STI_REGISTER_IR_TCDC: return z80sti->tcdc; + } + break; + + case Z80STI_REGISTER_GPIP: z80sti->gpip = (devcb_call_read8(&z80sti->in_gpio_func, 0) & ~z80sti->ddr) | (z80sti->gpip & z80sti->ddr); return z80sti->gpip; + case Z80STI_REGISTER_IPRB: return z80sti->ipr & 0xff; + case Z80STI_REGISTER_IPRA: return z80sti->ipr >> 8; + case Z80STI_REGISTER_ISRB: return z80sti->isr & 0xff; + case Z80STI_REGISTER_ISRA: return z80sti->isr >> 8; + case Z80STI_REGISTER_IMRB: return z80sti->imr & 0xff; + case Z80STI_REGISTER_IMRA: return z80sti->imr >> 8; + case Z80STI_REGISTER_PVR: return z80sti->pvr; + case Z80STI_REGISTER_TABC: return z80sti->tabc; + case Z80STI_REGISTER_TBDR: return z80sti->tmc[TIMER_B]; + case Z80STI_REGISTER_TADR: return z80sti->tmc[TIMER_A]; + case Z80STI_REGISTER_UCR: return z80sti->ucr; + case Z80STI_REGISTER_RSR: return z80sti->rsr; + case Z80STI_REGISTER_TSR: return z80sti->tsr; + case Z80STI_REGISTER_UDR: return z80sti->udr; + } + + return 0; +} + +/*------------------------------------------------- + z80sti_w - register write +-------------------------------------------------*/ + +WRITE8_DEVICE_HANDLER( z80sti_w ) +{ + z80sti_t *z80sti = get_safe_token(device); + + switch (offset & 0x0f) + { + case Z80STI_REGISTER_IR: + switch (z80sti->pvr & 0x07) + { + case Z80STI_REGISTER_IR_SCR: + LOG(("Z80STI '%s' Sync Character Register: %x\n", device->tag.cstr(), data)); + z80sti->scr = data; + break; + + case Z80STI_REGISTER_IR_TDDR: + LOG(("Z80STI '%s' Timer D Data Register: %x\n", device->tag.cstr(), data)); + z80sti->tdr[TIMER_D] = data; + break; + + case Z80STI_REGISTER_IR_TCDR: + LOG(("Z80STI '%s' Timer C Data Register: %x\n", device->tag.cstr(), data)); + z80sti->tdr[TIMER_C] = data; + break; + + case Z80STI_REGISTER_IR_AER: + LOG(("Z80STI '%s' Active Edge Register: %x\n", device->tag.cstr(), data)); + z80sti->aer = data; + break; + + case Z80STI_REGISTER_IR_IERB: + LOG(("Z80STI '%s' Interrupt Enable Register B: %x\n", device->tag.cstr(), data)); + z80sti->ier = (z80sti->ier & 0xff00) | data; + check_interrupts(z80sti); + break; + + case Z80STI_REGISTER_IR_IERA: + LOG(("Z80STI '%s' Interrupt Enable Register A: %x\n", device->tag.cstr(), data)); + z80sti->ier = (data << 8) | (z80sti->ier & 0xff); + check_interrupts(z80sti); + break; + + case Z80STI_REGISTER_IR_DDR: + LOG(("Z80STI '%s' Data Direction Register: %x\n", device->tag.cstr(), data)); + z80sti->ddr = data; + break; + + case Z80STI_REGISTER_IR_TCDC: + { + int tcc = PRESCALER[(data >> 4) & 0x07]; + int tdc = PRESCALER[data & 0x07]; + + z80sti->tcdc = data; + + LOG(("Z80STI '%s' Timer C Prescaler: %u\n", device->tag.cstr(), tcc)); + LOG(("Z80STI '%s' Timer D Prescaler: %u\n", device->tag.cstr(), tdc)); + + if (tcc) + timer_adjust_periodic(z80sti->timer[TIMER_C], ATTOTIME_IN_HZ(device->clock / tcc), 0, ATTOTIME_IN_HZ(device->clock / tcc)); + else + timer_enable(z80sti->timer[TIMER_C], 0); + + if (tdc) + timer_adjust_periodic(z80sti->timer[TIMER_D], ATTOTIME_IN_HZ(device->clock / tdc), 0, ATTOTIME_IN_HZ(device->clock / tdc)); + else + timer_enable(z80sti->timer[TIMER_D], 0); + + if (BIT(data, 7)) + { + LOG(("Z80STI '%s' Timer A Reset\n", device->tag.cstr())); + z80sti->to[TIMER_A] = 0; + + devcb_call_write_line(&z80sti->out_tao_func, z80sti->to[TIMER_A]); + } + + if (BIT(data, 3)) + { + LOG(("Z80STI '%s' Timer B Reset\n", device->tag.cstr())); + z80sti->to[TIMER_B] = 0; + + devcb_call_write_line(&z80sti->out_tbo_func, z80sti->to[TIMER_B]); + } + } + break; + } + break; + + case Z80STI_REGISTER_GPIP: + LOG(("Z80STI '%s' General Purpose I/O Register: %x\n", device->tag.cstr(), data)); + z80sti->gpip = data & z80sti->ddr; + devcb_call_write8(&z80sti->out_gpio_func, 0, z80sti->gpip); + break; + + case Z80STI_REGISTER_IPRB: + { + int i; + LOG(("Z80STI '%s' Interrupt Pending Register B: %x\n", device->tag.cstr(), data)); + z80sti->ipr &= (z80sti->ipr & 0xff00) | data; + + for (i = 0; i < 16; i++) + { + if (!BIT(z80sti->ipr, i) && (z80sti->int_state[i] == Z80_DAISY_INT)) z80sti->int_state[i] = 0; + } + + check_interrupts(z80sti); + } + break; + + case Z80STI_REGISTER_IPRA: + { + int i; + LOG(("Z80STI '%s' Interrupt Pending Register A: %x\n", device->tag.cstr(), data)); + z80sti->ipr &= (data << 8) | (z80sti->ipr & 0xff); + + for (i = 0; i < 16; i++) + { + if (!BIT(z80sti->ipr, i) && (z80sti->int_state[i] == Z80_DAISY_INT)) z80sti->int_state[i] = 0; + } + + check_interrupts(z80sti); + } + break; + + case Z80STI_REGISTER_ISRB: + LOG(("Z80STI '%s' Interrupt In-Service Register B: %x\n", device->tag.cstr(), data)); + z80sti->isr &= (z80sti->isr & 0xff00) | data; + break; + + case Z80STI_REGISTER_ISRA: + LOG(("Z80STI '%s' Interrupt In-Service Register A: %x\n", device->tag.cstr(), data)); + z80sti->isr &= (data << 8) | (z80sti->isr & 0xff); + break; + + case Z80STI_REGISTER_IMRB: + LOG(("Z80STI '%s' Interrupt Mask Register B: %x\n", device->tag.cstr(), data)); + z80sti->imr = (z80sti->imr & 0xff00) | data; + z80sti->isr &= z80sti->imr; + check_interrupts(z80sti); + break; + + case Z80STI_REGISTER_IMRA: + LOG(("Z80STI '%s' Interrupt Mask Register A: %x\n", device->tag.cstr(), data)); + z80sti->imr = (data << 8) | (z80sti->imr & 0xff); + z80sti->isr &= z80sti->imr; + check_interrupts(z80sti); + break; + + case Z80STI_REGISTER_PVR: + LOG(("Z80STI '%s' Interrupt Vector: %02x\n", device->tag.cstr(), data & 0xe0)); + LOG(("Z80STI '%s' IR Address: %01x\n", device->tag.cstr(), data & 0x07)); + z80sti->pvr = data; + break; + + case Z80STI_REGISTER_TABC: + { + int tac = PRESCALER[(data >> 4) & 0x07]; + int tbc = PRESCALER[data & 0x07]; + + z80sti->tabc = data; + + LOG(("Z80STI '%s' Timer A Prescaler: %u\n", device->tag.cstr(), tac)); + LOG(("Z80STI '%s' Timer B Prescaler: %u\n", device->tag.cstr(), tbc)); + + if (tac) + timer_adjust_periodic(z80sti->timer[TIMER_A], ATTOTIME_IN_HZ(device->clock / tac), 0, ATTOTIME_IN_HZ(device->clock / tac)); + else + timer_enable(z80sti->timer[TIMER_A], 0); + + if (tbc) + timer_adjust_periodic(z80sti->timer[TIMER_B], ATTOTIME_IN_HZ(device->clock / tbc), 0, ATTOTIME_IN_HZ(device->clock / tbc)); + else + timer_enable(z80sti->timer[TIMER_B], 0); + } + break; + + case Z80STI_REGISTER_TBDR: + LOG(("Z80STI '%s' Timer B Data Register: %x\n", device->tag.cstr(), data)); + z80sti->tdr[TIMER_B] = data; + break; + + case Z80STI_REGISTER_TADR: + LOG(("Z80STI '%s' Timer A Data Register: %x\n", device->tag.cstr(), data)); + z80sti->tdr[TIMER_A] = data; + break; +#if 0 + case Z80STI_REGISTER_UCR: + z80sti->ucr = data; + break; + + case Z80STI_REGISTER_RSR: + z80sti->rsr = data; + break; + + case Z80STI_REGISTER_TSR: + z80sti->tsr = data; + break; + + case Z80STI_REGISTER_UDR: + z80sti->udr = data; + break; +#endif + default: + LOG(("Z80STI '%s' Unsupported Register %x\n", device->tag.cstr(), offset & 0x0f)); + } +} + +/*------------------------------------------------- + z80sti_rc_w - receiver clock write +-------------------------------------------------*/ + +WRITE_LINE_DEVICE_HANDLER( z80sti_rc_w ) +{ + z80sti_t *z80sti = get_safe_token(device); + + if (state) + { + serial_receive(z80sti); + } +} + +/*------------------------------------------------- + z80sti_tc_w - transmitter clock write +-------------------------------------------------*/ + +WRITE_LINE_DEVICE_HANDLER( z80sti_tc_w ) +{ + z80sti_t *z80sti = get_safe_token(device); + + if (state) + { + serial_transmit(z80sti); + } +} + +/*------------------------------------------------- + timer_count - timer count down +-------------------------------------------------*/ + +static void timer_count(running_device *device, int index) +{ + z80sti_t *z80sti = get_safe_token(device); + + if (z80sti->tmc[index] == 0x01) + { + //LOG(("Z80STI '%s' Timer %c Expired\n", device->tag, 'A' + index)); + + /* toggle timer output signal */ + z80sti->to[index] = !z80sti->to[index]; + + switch (index) + { + case TIMER_A: devcb_call_write_line(&z80sti->out_tao_func, z80sti->to[index]); break; + case TIMER_B: devcb_call_write_line(&z80sti->out_tbo_func, z80sti->to[index]); break; + case TIMER_C: devcb_call_write_line(&z80sti->out_tco_func, z80sti->to[index]); break; + case TIMER_D: devcb_call_write_line(&z80sti->out_tdo_func, z80sti->to[index]); break; + } + + if (z80sti->ier & (1 << INT_LEVEL_TIMER[index])) + { + LOG(("Z80STI '%s' Interrupt Pending for Timer %c\n", device->tag.cstr(), 'A' + index)); + + /* signal timer elapsed interrupt */ + take_interrupt(z80sti, INT_LEVEL_TIMER[index]); + } + + /* load timer main counter */ + z80sti->tmc[index] = z80sti->tdr[index]; + } + else + { + /* count down */ + z80sti->tmc[index]--; + } +} + +/*------------------------------------------------- + TIMER_CALLBACK( timer_# ) +-------------------------------------------------*/ + +static TIMER_CALLBACK( timer_a ) { timer_count((running_device *)ptr, TIMER_A); } +static TIMER_CALLBACK( timer_b ) { timer_count((running_device *)ptr, TIMER_B); } +static TIMER_CALLBACK( timer_c ) { timer_count((running_device *)ptr, TIMER_C); } +static TIMER_CALLBACK( timer_d ) { timer_count((running_device *)ptr, TIMER_D); } + +/*------------------------------------------------- + gpip_input - GPIP input line write +-------------------------------------------------*/ + +static void gpip_input(running_device *device, int bit, int state) +{ + z80sti_t *z80sti = get_safe_token(device); + + int aer = BIT(z80sti->aer, bit); + int old_state = BIT(z80sti->gpip, bit); + + if ((old_state ^ aer) && !(state ^ aer)) + { + LOG(("Z80STI '%s' Edge Transition Detected on Bit: %u\n", device->tag.cstr(), bit)); + + if (z80sti->ier & (1 << INT_LEVEL_GPIP[bit])) + { + LOG(("Z80STI '%s' Interrupt Pending for P%u\n", device->tag.cstr(), bit)); + + take_interrupt(z80sti, INT_LEVEL_GPIP[bit]); + } + } + + z80sti->gpip = (z80sti->gpip & ~(1 << bit)) | (state << bit); +} + +/*------------------------------------------------- + z80sti_i#_w - GPIP input line # write +-------------------------------------------------*/ + +WRITE_LINE_DEVICE_HANDLER( z80sti_i0_w ) { gpip_input(device, 0, state); } +WRITE_LINE_DEVICE_HANDLER( z80sti_i1_w ) { gpip_input(device, 1, state); } +WRITE_LINE_DEVICE_HANDLER( z80sti_i2_w ) { gpip_input(device, 2, state); } +WRITE_LINE_DEVICE_HANDLER( z80sti_i3_w ) { gpip_input(device, 3, state); } +WRITE_LINE_DEVICE_HANDLER( z80sti_i4_w ) { gpip_input(device, 4, state); } +WRITE_LINE_DEVICE_HANDLER( z80sti_i5_w ) { gpip_input(device, 5, state); } +WRITE_LINE_DEVICE_HANDLER( z80sti_i6_w ) { gpip_input(device, 6, state); } +WRITE_LINE_DEVICE_HANDLER( z80sti_i7_w ) { gpip_input(device, 7, state); } + +/*------------------------------------------------- + z80sti_irq_state - get interrupt status +-------------------------------------------------*/ + +static int z80sti_irq_state(running_device *device) +{ + z80sti_t *z80sti = get_safe_token(device); + int state = 0, i; + + /* loop over all interrupt sources */ + for (i = 15; i >= 0; i--) + { + /* if we're servicing a request, don't indicate more interrupts */ + if (z80sti->int_state[i] & Z80_DAISY_IEO) + { + state |= Z80_DAISY_IEO; + break; + } + + if (BIT(z80sti->imr, i)) + { + state |= z80sti->int_state[i]; + } + } + + LOG(("Z80STI '%s' Interrupt State: %u\n", device->tag.cstr(), state)); + + return state; +} + +/*------------------------------------------------- + z80sti_irq_ack - interrupt acknowledge +-------------------------------------------------*/ + +static int z80sti_irq_ack(running_device *device) +{ + z80sti_t *z80sti = get_safe_token(device); + int i; + + /* loop over all interrupt sources */ + for (i = 15; i >= 0; i--) + { + /* find the first channel with an interrupt requested */ + if (z80sti->int_state[i] & Z80_DAISY_INT) + { + UINT8 vector = (z80sti->pvr & 0xe0) | INT_VECTOR[i]; + + /* clear interrupt, switch to the IEO state, and update the IRQs */ + z80sti->int_state[i] = Z80_DAISY_IEO; + + /* clear interrupt pending register bit */ + z80sti->ipr &= ~(1 << i); + + /* set interrupt in-service register bit */ + z80sti->isr |= (1 << i); + + check_interrupts(z80sti); + + LOG(("Z80STI '%s' Interrupt Acknowledge Vector: %02x\n", device->tag.cstr(), vector)); + + return vector; + } + } + + logerror("z80sti_irq_ack: failed to find an interrupt to ack!\n"); + + return 0; +} + +/*------------------------------------------------- + z80sti_irq_reti - return from interrupt +-------------------------------------------------*/ + +static void z80sti_irq_reti(running_device *device) +{ + z80sti_t *z80sti = get_safe_token(device); + int i; + + LOG(("Z80STI '%s' Return from Interrupt\n", device->tag.cstr())); + + /* loop over all interrupt sources */ + for (i = 15; i >= 0; i--) + { + /* find the first channel with an IEO pending */ + if (z80sti->int_state[i] & Z80_DAISY_IEO) + { + /* clear the IEO state and update the IRQs */ + z80sti->int_state[i] &= ~Z80_DAISY_IEO; + + /* clear interrupt in-service register bit */ + z80sti->isr &= ~(1 << i); + + check_interrupts(z80sti); + return; + } + } + + logerror("z80sti_irq_reti: failed to find an interrupt to clear IEO on!\n"); +} + +/*------------------------------------------------- + DEVICE_START( z80sti ) +-------------------------------------------------*/ + +static DEVICE_START( z80sti ) +{ + z80sti_t *z80sti = get_safe_token(device); + const z80sti_interface *intf = (const z80sti_interface *)device->baseconfig().static_config; + + /* resolve callbacks */ + devcb_resolve_read8(&z80sti->in_gpio_func, &intf->in_gpio_func, device); + devcb_resolve_write8(&z80sti->out_gpio_func, &intf->out_gpio_func, device); + devcb_resolve_read_line(&z80sti->in_si_func, &intf->in_si_func, device); + devcb_resolve_write_line(&z80sti->out_so_func, &intf->out_so_func, device); + devcb_resolve_write_line(&z80sti->out_tao_func, &intf->out_tao_func, device); + devcb_resolve_write_line(&z80sti->out_tbo_func, &intf->out_tbo_func, device); + devcb_resolve_write_line(&z80sti->out_tco_func, &intf->out_tco_func, device); + devcb_resolve_write_line(&z80sti->out_tdo_func, &intf->out_tdo_func, device); + devcb_resolve_write_line(&z80sti->out_int_func, &intf->out_int_func, device); + + /* create the counter timers */ + z80sti->timer[TIMER_A] = timer_alloc(device->machine, timer_a, (void *)device); + z80sti->timer[TIMER_B] = timer_alloc(device->machine, timer_b, (void *)device); + z80sti->timer[TIMER_C] = timer_alloc(device->machine, timer_c, (void *)device); + z80sti->timer[TIMER_D] = timer_alloc(device->machine, timer_d, (void *)device); + + /* create serial receive clock timer */ + if (intf->rx_clock > 0) + { + z80sti->rx_timer = timer_alloc(device->machine, rx_tick, (void *)device); + timer_adjust_periodic(z80sti->rx_timer, attotime_zero, 0, ATTOTIME_IN_HZ(intf->rx_clock)); + } + + /* create serial transmit clock timer */ + if (intf->tx_clock > 0) + { + z80sti->tx_timer = timer_alloc(device->machine, tx_tick, (void *)device); + timer_adjust_periodic(z80sti->tx_timer, attotime_zero, 0, ATTOTIME_IN_HZ(intf->tx_clock)); + } + + /* register for state saving */ + state_save_register_device_item(device, 0, z80sti->gpip); + state_save_register_device_item(device, 0, z80sti->aer); + state_save_register_device_item(device, 0, z80sti->ddr); + state_save_register_device_item(device, 0, z80sti->ier); + state_save_register_device_item(device, 0, z80sti->ipr); + state_save_register_device_item(device, 0, z80sti->isr); + state_save_register_device_item(device, 0, z80sti->imr); + state_save_register_device_item(device, 0, z80sti->pvr); + state_save_register_device_item_array(device, 0, z80sti->int_state); + state_save_register_device_item(device, 0, z80sti->tabc); + state_save_register_device_item(device, 0, z80sti->tcdc); + state_save_register_device_item_array(device, 0, z80sti->tdr); + state_save_register_device_item_array(device, 0, z80sti->tmc); + state_save_register_device_item_array(device, 0, z80sti->to); + state_save_register_device_item(device, 0, z80sti->scr); + state_save_register_device_item(device, 0, z80sti->ucr); + state_save_register_device_item(device, 0, z80sti->rsr); + state_save_register_device_item(device, 0, z80sti->tsr); + state_save_register_device_item(device, 0, z80sti->udr); +} + +/*------------------------------------------------- + DEVICE_RESET( z80sti ) +-------------------------------------------------*/ + +static DEVICE_RESET( z80sti ) +{ +} + +/*------------------------------------------------- + DEVICE_GET_INFO( z80sti ) +-------------------------------------------------*/ + +DEVICE_GET_INFO( z80sti ) +{ + switch (state) + { + /* --- the following bits of info are returned as 64-bit signed integers --- */ + case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(z80sti_t); break; + case DEVINFO_INT_INLINE_CONFIG_BYTES: info->i = 0; break; + case DEVINFO_INT_CLASS: info->i = DEVICE_CLASS_PERIPHERAL; break; + + /* --- the following bits of info are returned as pointers to data or functions --- */ + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(z80sti); break; + case DEVINFO_FCT_STOP: /* Nothing */ break; + case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME(z80sti); break; + case DEVINFO_FCT_IRQ_STATE: info->f = (genf *)z80sti_irq_state; break; + case DEVINFO_FCT_IRQ_ACK: info->f = (genf *)z80sti_irq_ack; break; + case DEVINFO_FCT_IRQ_RETI: info->f = (genf *)z80sti_irq_reti; break; + + /* --- the following bits of info are returned as NULL-terminated strings --- */ + case DEVINFO_STR_NAME: strcpy(info->s, "Mostek MK3801"); break; + case DEVINFO_STR_FAMILY: strcpy(info->s, "Z80-STI"); break; + case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; + case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; + case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright the MESS Team"); break; + } +} diff --git a/src/emu/machine/z80sti.h b/src/emu/machine/z80sti.h new file mode 100644 index 00000000000..4b9df8bd818 --- /dev/null +++ b/src/emu/machine/z80sti.h @@ -0,0 +1,116 @@ +/********************************************************************** + + Mostek MK3801 Serial Timer Interrupt Controller (Z80-STI) emulation + + Copyright MESS Team. + Visit http://mamedev.org for licensing and usage restrictions. + +********************************************************************** + _____ _____ + TAO 1 |* \_/ | 40 Vcc + TBO 2 | | 39 RC + TCO 3 | | 38 SI + TDO 4 | | 37 SO + TCK 5 | | 36 TC + _M1 6 | | 35 A0 + _RES 7 | | 34 A1 + I0 8 | | 33 A2 + I1 9 | | 32 A3 + I2 10 | MK3801 | 31 _WR + I3 11 | Z80-STI | 30 _CE + I4 12 | | 29 _RD + I5 13 | | 28 D7 + I6 14 | | 27 D6 + I7 15 | | 26 D5 + IEI 16 | | 25 D4 + _INT 17 | | 24 D3 + IEO 18 | | 23 D2 + _IORQ 19 | | 22 D1 + Vss 20 |_____________| 21 D0 + +**********************************************************************/ + +#ifndef __Z80STI__ +#define __Z80STI__ + +#include "emu.h" +#include "devcb.h" + +/*************************************************************************** + MACROS / CONSTANTS +***************************************************************************/ + +#define Z80STI DEVICE_GET_INFO_NAME( z80sti ) + +#define MDRV_Z80STI_ADD(_tag, _clock, _config) \ + MDRV_DEVICE_ADD((_tag), Z80STI, _clock) \ + MDRV_DEVICE_CONFIG(_config) + +#define Z80STI_INTERFACE(name) const z80sti_interface (name) = + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +typedef struct _z80sti_interface z80sti_interface; +struct _z80sti_interface +{ + int rx_clock; /* serial receive clock */ + int tx_clock; /* serial transmit clock */ + + /* this gets called on each change of the _INT pin (pin 17) */ + devcb_write_line out_int_func; + + /* this is called on each read of the GPIO pins */ + devcb_read8 in_gpio_func; + + /* this is called on each write of the GPIO pins */ + devcb_write8 out_gpio_func; + + /* this gets called for each read of the SI pin (pin 38) */ + devcb_read_line in_si_func; + + /* this gets called for each change of the SO pin (pin 37) */ + devcb_write_line out_so_func; + + /* this gets called for each change of the TAO pin (pin 1) */ + devcb_write_line out_tao_func; + + /* this gets called for each change of the TBO pin (pin 2) */ + devcb_write_line out_tbo_func; + + /* this gets called for each change of the TCO pin (pin 3) */ + devcb_write_line out_tco_func; + + /* this gets called for each change of the TDO pin (pin 4) */ + devcb_write_line out_tdo_func; +}; + +/*************************************************************************** + PROTOTYPES +***************************************************************************/ + +/* device interface */ +DEVICE_GET_INFO( z80sti ); + +/* register access */ +READ8_DEVICE_HANDLER( z80sti_r ); +WRITE8_DEVICE_HANDLER( z80sti_w ); + +/* receive clock */ +WRITE_LINE_DEVICE_HANDLER( z80sti_rc_w ); + +/* transmit clock */ +WRITE_LINE_DEVICE_HANDLER( z80sti_tc_w ); + +/* GPIP input lines */ +WRITE_LINE_DEVICE_HANDLER( z80sti_i0_w ); +WRITE_LINE_DEVICE_HANDLER( z80sti_i1_w ); +WRITE_LINE_DEVICE_HANDLER( z80sti_i2_w ); +WRITE_LINE_DEVICE_HANDLER( z80sti_i3_w ); +WRITE_LINE_DEVICE_HANDLER( z80sti_i4_w ); +WRITE_LINE_DEVICE_HANDLER( z80sti_i5_w ); +WRITE_LINE_DEVICE_HANDLER( z80sti_i6_w ); +WRITE_LINE_DEVICE_HANDLER( z80sti_i7_w ); + +#endif