diff options
Diffstat (limited to 'src/devices/machine/tms5501.c')
-rw-r--r-- | src/devices/machine/tms5501.c | 529 |
1 files changed, 529 insertions, 0 deletions
diff --git a/src/devices/machine/tms5501.c b/src/devices/machine/tms5501.c new file mode 100644 index 00000000000..71f3f298cde --- /dev/null +++ b/src/devices/machine/tms5501.c @@ -0,0 +1,529 @@ +// license:BSD-3-Clause +// copyright-holders:Curt Coder +/********************************************************************** + + TMS5501 Multifunction Input/Output Controller emulation + +**********************************************************************/ + +#include "tms5501.h" + + + +//************************************************************************** +// MACROS / CONSTANTS +//************************************************************************** + +#define LOG 0 + + +const UINT8 tms5501_device::rst_vector[] = { 0xc7, 0xcf, 0xd7, 0xdf, 0xe7, 0xef, 0xf7, 0xff }; + + + +//************************************************************************** +// DEVICE DEFINITIONS +//************************************************************************** + +// device type definition +const device_type TMS5501 = &device_creator<tms5501_device>; + + +// I/O address map +DEVICE_ADDRESS_MAP_START( io_map, 8, tms5501_device ) + AM_RANGE(0x00, 0x00) AM_READ(rb_r) + AM_RANGE(0x01, 0x01) AM_READ(xi_r) + AM_RANGE(0x02, 0x02) AM_READ(rst_r) + AM_RANGE(0x03, 0x03) AM_READ(sta_r) + AM_RANGE(0x04, 0x04) AM_WRITE(cmd_w) + AM_RANGE(0x05, 0x05) AM_WRITE(rr_w) + AM_RANGE(0x06, 0x06) AM_WRITE(tb_w) + AM_RANGE(0x07, 0x07) AM_WRITE(xo_w) + AM_RANGE(0x08, 0x08) AM_WRITE(mr_w) + AM_RANGE(0x09, 0x0d) AM_WRITE(tmr_w) +ADDRESS_MAP_END + + + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +//------------------------------------------------- +// tms5501_device - constructor +//------------------------------------------------- + +tms5501_device::tms5501_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : + device_t(mconfig, TMS5501, "TMS5501", tag, owner, clock, "tms5501", __FILE__), + device_serial_interface(mconfig, *this), + m_write_irq(*this), + m_write_xmt(*this), + m_read_xi(*this), + m_write_xo(*this), + m_irq(IRQ_TB), + m_rb(0), + m_sta(STA_XBE | STA_SR), + m_cmd(0), + m_rr(0), + m_tb(0), + m_mr(0), + m_sens(0), + m_xi7(0) +{ +} + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void tms5501_device::device_start() +{ + // resolve callbacks + m_write_irq.resolve_safe(); + m_write_xmt.resolve_safe(); + m_read_xi.resolve_safe(0); + m_write_xo.resolve_safe(); + + // create timers + m_timer[TIMER_1] = timer_alloc(TIMER_1); + m_timer[TIMER_2] = timer_alloc(TIMER_2); + m_timer[TIMER_3] = timer_alloc(TIMER_3); + m_timer[TIMER_4] = timer_alloc(TIMER_4); + m_timer[TIMER_5] = timer_alloc(TIMER_5); + + // state saving + save_item(NAME(m_rb)); + save_item(NAME(m_sta)); + save_item(NAME(m_cmd)); + save_item(NAME(m_rr)); + save_item(NAME(m_tb)); + save_item(NAME(m_mr)); + save_item(NAME(m_sens)); + save_item(NAME(m_xi7)); +} + + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void tms5501_device::device_reset() +{ + receive_register_reset(); + transmit_register_reset(); + + m_write_xmt(1); + + check_interrupt(); +} + + +//------------------------------------------------- +// device_timer - handle timer events +//------------------------------------------------- + +void tms5501_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + switch (id) + { + case TIMER_1: + set_interrupt(IRQ_TMR1); + break; + + case TIMER_2: + set_interrupt(IRQ_TMR2); + break; + + case TIMER_3: + set_interrupt(IRQ_TMR3); + break; + + case TIMER_4: + set_interrupt(IRQ_TMR4); + break; + + case TIMER_5: + if (!(m_cmd & CMD_XI7)) + { + set_interrupt(IRQ_TMR5); + } + break; + + default: + device_serial_interface::device_timer(timer, id, param, ptr); + } +} + + +//------------------------------------------------- +// tra_callback - +//------------------------------------------------- + +void tms5501_device::tra_callback() +{ + m_write_xmt(transmit_register_get_data_bit()); +} + + +//------------------------------------------------- +// tra_complete - +//------------------------------------------------- + +void tms5501_device::tra_complete() +{ + if (!(m_sta & STA_XBE)) + { + transmit_register_setup(m_tb); + + m_sta |= STA_XBE; + + set_interrupt(IRQ_TB); + } +} + + +//------------------------------------------------- +// rcv_complete - +//------------------------------------------------- + +void tms5501_device::rcv_complete() +{ + receive_register_extract(); + m_rb = get_received_char(); + + if (is_receive_framing_error()) + { + m_sta |= STA_FE; + } + else + { + m_sta &= ~STA_FE; + } + + if (m_sta & STA_RBL) + { + m_sta |= STA_OE; + } + + m_sta |= (STA_RBL | STA_SR); + m_sta &= ~(STA_SBD | STA_FBD); + + set_interrupt(IRQ_RB); +} + + +//------------------------------------------------- +// rb_r - read receiver buffer +//------------------------------------------------- + +READ8_MEMBER( tms5501_device::rb_r ) +{ + m_sta &= ~STA_RBL; + m_irq &= ~IRQ_RB; + + check_interrupt(); + + return m_rb; +} + + +//------------------------------------------------- +// xi_r - read external inputs +//------------------------------------------------- + +READ8_MEMBER( tms5501_device::xi_r ) +{ + UINT8 data = m_read_xi(0); + + if (m_cmd & CMD_XI7) + { + data = (m_xi7 << 7) | (data & 0x7f); + } + + return data; +} + + +//------------------------------------------------- +// rst_r - read interrupt address +//------------------------------------------------- + +READ8_MEMBER( tms5501_device::rst_r ) +{ + return get_vector(); +} + + +//------------------------------------------------- +// sta_r - read TMS5510 status +//------------------------------------------------- + +READ8_MEMBER( tms5501_device::sta_r ) +{ + UINT8 data = m_sta; + + m_sta &= ~STA_OE; + + return data; +} + + +//------------------------------------------------- +// cmd_w - issue discrete commands +//------------------------------------------------- + +WRITE8_MEMBER( tms5501_device::cmd_w ) +{ + if (LOG) logerror("TMS5501 '%s' Command %02x\n", tag(), data); + + m_cmd = data; + + if (m_cmd & CMD_RST) + { + m_sta &= ~(STA_SBD | STA_FBD | STA_RBL | STA_OE); + m_sta |= (STA_XBE | STA_SR); + + receive_register_reset(); + transmit_register_reset(); + + m_write_xmt(1); + + m_irq = 0; + set_interrupt(IRQ_TB); + + m_timer[TIMER_1]->enable(false); + m_timer[TIMER_2]->enable(false); + m_timer[TIMER_3]->enable(false); + m_timer[TIMER_4]->enable(false); + m_timer[TIMER_5]->enable(false); + } + else if (m_cmd & CMD_BRK) + { + receive_register_reset(); + transmit_register_reset(); + + m_write_xmt(0); + } +} + + +//------------------------------------------------- +// rr_w - load rate register +//------------------------------------------------- + +WRITE8_MEMBER( tms5501_device::rr_w ) +{ + if (LOG) logerror("TMS5501 '%s' Rate Register %02x\n", tag(), data); + + m_rr = data; + + stop_bits_t stop_bits = (m_rr & RR_STOP) ? STOP_BITS_1 : STOP_BITS_2; + + set_data_frame(1, 8, PARITY_NONE, stop_bits); + + int rate = 0; + + if (m_rr & RR_9600) rate = 9600; + else if (m_rr & RR_4800) rate = 4800; + else if (m_rr & RR_2400) rate = 2400; + else if (m_rr & RR_1200) rate = 1200; + else if (m_rr & RR_300) rate = 300; + else if (m_rr & RR_150) rate = 150; + else if (m_rr & RR_110) rate = 110; + + if (m_cmd & CMD_TST1) + { + rate *= 8; + } + + set_rcv_rate(rate); + set_tra_rate(rate); +} + + +//------------------------------------------------- +// tb_w - load transmitter buffer +//------------------------------------------------- + +WRITE8_MEMBER( tms5501_device::tb_w ) +{ + if (LOG) logerror("TMS5501 '%s' Transmitter Buffer %02x\n", tag(), data); + + m_tb = data; + + if (is_transmit_register_empty()) + { + transmit_register_setup(m_tb); + + m_sta |= STA_XBE; + + set_interrupt(IRQ_TB); + } + else + { + m_sta &= ~STA_XBE; + } +} + + +//------------------------------------------------- +// xo_w - load output port +//------------------------------------------------- + +WRITE8_MEMBER( tms5501_device::xo_w ) +{ + if (LOG) logerror("TMS5501 '%s' Output %02x\n", tag(), data); + + m_write_xo(data); +} + + +//------------------------------------------------- +// mr_w - load mask register +//------------------------------------------------- + +WRITE8_MEMBER( tms5501_device::mr_w ) +{ + if (LOG) logerror("TMS5501 '%s' Mask Register %02x\n", tag(), data); + + m_mr = data; + + check_interrupt(); +} + + +//------------------------------------------------- +// tmr_w - load interval timer +//------------------------------------------------- + +WRITE8_MEMBER( tms5501_device::tmr_w ) +{ + if (LOG) logerror("TMS5501 '%s' Timer %u %02x\n", tag(), offset, data); + + m_timer[offset]->adjust(attotime::from_double((double) data / (clock() / 128.0))); +} + + +//------------------------------------------------- +// rcv_w - receive data write +//------------------------------------------------- + +WRITE_LINE_MEMBER( tms5501_device::rcv_w ) +{ + device_serial_interface::rx_w(state); + + if (is_receive_register_synchronized()) + { + m_sta |= STA_SBD; + m_sta &= ~STA_SR; + } + + if (is_receive_register_shifting()) + { + m_sta |= STA_FBD; + } +} + + +//------------------------------------------------- +// xi7_w - +//------------------------------------------------- + +WRITE_LINE_MEMBER( tms5501_device::xi7_w ) +{ + if (m_cmd & CMD_XI7) + { + if (!m_xi7 && state) + { + set_interrupt(IRQ_XI7); + } + } + + m_xi7 = state; +} + + +//------------------------------------------------- +// sens_w - +//------------------------------------------------- + +WRITE_LINE_MEMBER( tms5501_device::sens_w ) +{ + if (!m_sens && state) + { + set_interrupt(IRQ_SENS); + } + + m_sens = state; +} + + +//------------------------------------------------- +// set_interrupt - +//------------------------------------------------- + +void tms5501_device::set_interrupt(UINT8 mask) +{ + m_irq |= mask; + + if (LOG) logerror("TMS5501 '%s' Interrupt %02x\n", tag(), mask); + + check_interrupt(); +} + + +//------------------------------------------------- +// check_interrupt - +//------------------------------------------------- + +void tms5501_device::check_interrupt() +{ + int state = (m_irq & m_mr) ? ASSERT_LINE : CLEAR_LINE; + + if (state == ASSERT_LINE) + { + if (LOG) logerror("TMS5501 '%s' Interrupt Assert\n", tag()); + + m_sta |= STA_IP; + } + else + { + m_sta &= ~STA_IP; + } + + if (m_cmd & CMD_IAE) + { + m_write_irq(state); + } + else + { + m_write_irq(CLEAR_LINE); + } +} + + +//------------------------------------------------- +// get_vector - +//------------------------------------------------- + +UINT8 tms5501_device::get_vector() +{ + UINT8 rst = 0; + + for (int i = 0; i < 8; i++) + { + if (BIT((m_irq & m_mr), i)) + { + rst = rst_vector[i]; + m_irq &= ~(1 << i); + + check_interrupt(); + + if (LOG) logerror("%s: TMS5501 '%s' Interrupt Acknowledge %02x\n", machine().describe_context(), tag(), rst); + break; + } + } + + return rst; +} |