diff options
author | 2013-05-06 00:09:10 +0000 | |
---|---|---|
committer | 2013-05-06 00:09:10 +0000 | |
commit | b3150f804e246e67db5ec011a11343a2d1cd8612 (patch) | |
tree | a8dc453f9a061083ed14f637849cdab27ebc3ab7 /src/mess/machine/psxmultitap.c | |
parent | 579de8a42882ae10ca1b66d35a30a19ad5a1afbb (diff) |
(mess) psxmultitap: add multitap support [Carl]
Diffstat (limited to 'src/mess/machine/psxmultitap.c')
-rw-r--r-- | src/mess/machine/psxmultitap.c | 266 |
1 files changed, 266 insertions, 0 deletions
diff --git a/src/mess/machine/psxmultitap.c b/src/mess/machine/psxmultitap.c new file mode 100644 index 00000000000..53dcc33353d --- /dev/null +++ b/src/mess/machine/psxmultitap.c @@ -0,0 +1,266 @@ +// psx multitap emulation + +#include "machine/psxmultitap.h" + +const device_type PSX_MULTITAP = &device_creator<psx_multitap_device>; + +psx_multitap_device::psx_multitap_device(const machine_config& mconfig, const char* tag, device_t* owner, UINT32 clock) : + device_t(mconfig, PSX_MULTITAP, "Playstation Multitap", tag, owner, clock), + device_psx_controller_interface(mconfig, *this), + m_porta(*this, "a"), + m_portb(*this, "b"), + m_portc(*this, "c"), + m_portd(*this, "d") +{ +} + +static MACHINE_CONFIG_FRAGMENT( psx_multitap ) + MCFG_PSX_CTRL_PORT_ADD("a", psx_controllers_nomulti, "digital_pad", NULL) + MCFG_PSX_CTRL_PORT_ADD("b", psx_controllers_nomulti, NULL, NULL) + MCFG_PSX_CTRL_PORT_ADD("c", psx_controllers_nomulti, NULL, NULL) + MCFG_PSX_CTRL_PORT_ADD("d", psx_controllers_nomulti, NULL, NULL) +MACHINE_CONFIG_END + +machine_config_constructor psx_multitap_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( psx_multitap ); +} + +void psx_multitap_device::device_start() +{ + m_porta->setup_ack_cb(psx_controller_port_device::void_cb(FUNC(psx_multitap_device::ack), this)); + m_portb->setup_ack_cb(psx_controller_port_device::void_cb(FUNC(psx_multitap_device::ack), this)); + m_portc->setup_ack_cb(psx_controller_port_device::void_cb(FUNC(psx_multitap_device::ack), this)); + m_portd->setup_ack_cb(psx_controller_port_device::void_cb(FUNC(psx_multitap_device::ack), this)); + m_nextmode = false; +} + +void psx_multitap_device::interface_pre_reset() +{ + m_activeport = -1; + m_singlemode = m_nextmode; + m_tapmc = false; + m_cack[0] = m_cack[1] = m_cack[2] = m_cack[3] = true; + memset(m_data, 0xff, sizeof(m_data)); + m_porta->sel_w(false); + m_portb->sel_w(false); + m_portc->sel_w(false); + m_portd->sel_w(false); + m_porta->sel_w(true); + m_portb->sel_w(true); + m_portc->sel_w(true); + m_portd->sel_w(true); + device_psx_controller_interface::interface_pre_reset(); +} + +void psx_multitap_device::set_tx_line(bool tx, int port) +{ + psx_controller_port_device *dev; + switch(port) + { + default: + case 0: + dev = m_porta; + break; + case 1: + dev = m_portb; + break; + case 2: + dev = m_portc; + break; + case 3: + dev = m_portd; + break; + } + dev->clock_w(1); + dev->tx_w(tx); + dev->clock_w(0); +} + +bool psx_multitap_device::get_rx_line(int port) +{ + psx_controller_port_device *dev; + switch(port) + { + default: + case 0: + dev = m_porta; + break; + case 1: + dev = m_portb; + break; + case 2: + dev = m_portc; + break; + case 3: + dev = m_portd; + break; + } + return dev->rx_r(); +} + +void psx_multitap_device::do_pad() +{ + bool tx = device_psx_controller_interface::m_owner->tx_r(); + + // we don't know which controller until after the first byte + if((m_singlemode || m_tapmc) && (m_count >= 1)) + { + if((m_count == 2) && !m_bit && !m_tapmc) + m_nextmode = !tx; + + set_tx_line(tx, m_activeport); + m_rx = get_rx_line(m_activeport); + m_bit = (m_bit + 1) % 8; + if(!m_bit) + m_count++; + return; + } + + if(!m_count) + { + // first send the select byte to all devices until we know whether it's accessing + // a controller or memcard + if(!m_bit) + { + m_porta->sel_w(false); + m_portb->sel_w(false); + m_portc->sel_w(false); + m_portd->sel_w(false); + } + device_psx_controller_interface::do_pad(); + set_tx_line(tx, 0); + set_tx_line(tx, 1); + set_tx_line(tx, 2); + set_tx_line(tx, 3); + if(!m_bit) + { + m_count = 1; + m_tapmc = m_memcard; + m_memcard = false; // make sure we still receive clocks + if(m_singlemode || m_tapmc) + { + m_activeport = (m_idata & 0xf) - 1; + m_porta->sel_w((m_activeport == 0) ? false : true); + m_portb->sel_w((m_activeport == 1) ? false : true); + m_portc->sel_w((m_activeport == 2) ? false : true); + m_portd->sel_w((m_activeport == 3) ? false : true); + } + } + return; + } + else if(m_count <= 2) + return device_psx_controller_interface::do_pad(); + else if(m_count < 11) + { + if((m_count == 3) && !m_bit) + m_nextmode = !m_idata; + + if((m_count < 5) && m_cack[0] && m_cack[1] && m_cack[2] && m_cack[3]) + return; // no acks? hang up. + + // all of the ports are polled here, port a is passed though. the data + // from the other ports is stored and can be retrieved at a much higher clock rate + // don't poll a port that is inactive or done + if(!m_cack[0]) + { + set_tx_line(tx, 0); + m_rx = m_porta->rx_r(); + } + else + { + m_rx = true; + m_porta->sel_w(true); + } + + if(!m_cack[1]) + { + set_tx_line(tx, 1); + m_data[0][m_count - 3] &= ~(!m_portb->rx_r() << m_bit); + } + else + m_portb->sel_w(true); + + if(!m_cack[2]) + { + set_tx_line(tx, 2); + m_data[1][m_count - 3] &= ~(!m_portc->rx_r() << m_bit); + } + else + m_portc->sel_w(true); + + if(!m_cack[3]) + { + set_tx_line(tx, 3); + m_data[2][m_count - 3] &= ~(!m_portd->rx_r() << m_bit); + } + else + m_portd->sel_w(true); + } + else if(m_count < 19) + // send stored port b data + m_rx = ((m_data[0][m_count - 11] & (1 << m_bit)) ? 1 : 0); + else if(m_count < 27) + // send stored port c data + m_rx = ((m_data[1][m_count - 19] & (1 << m_bit)) ? 1 : 0); + else + // send stored port d data + m_rx = ((m_data[2][m_count - 27] & (1 << m_bit)) ? 1 : 0); + + if(m_bit == 7) + { + // ports won't ack if they are done + m_cack[0] = m_cack[1] = m_cack[2] = m_cack[3] = true; + if(m_count < 11) + m_ack_timer->adjust(attotime::from_usec(12), 0); // give a bit of time for the ports to ack + else if(m_count < 35) + m_ack_timer->adjust(attotime::from_usec(10), 0); + } + + m_bit = (m_bit + 1) % 8; + if(!m_bit) + m_count++; +} + +bool psx_multitap_device::get_pad(int count, UINT8 *odata, UINT8 idata) +{ + if(!count) + *odata = 0x80; + else + *odata = 0x5a; + return true; +} + +void psx_multitap_device::ack() +{ + if(m_activeport != -1) + { + switch(m_activeport) + { + case 0: + m_ack = m_porta->ack_r(); + break; + case 1: + m_ack = m_portb->ack_r(); + break; + case 2: + m_ack = m_portc->ack_r(); + break; + case 3: + m_ack = m_portd->ack_r(); + break; + default: + return; + } + device_psx_controller_interface::m_owner->ack(); + return; + } + if(!m_porta->ack_r()) + m_cack[0] = false; + if(!m_portb->ack_r()) + m_cack[1] = false; + if(!m_portc->ack_r()) + m_cack[2] = false; + if(!m_portd->ack_r()) + m_cack[3] = false; +} |