From b3150f804e246e67db5ec011a11343a2d1cd8612 Mon Sep 17 00:00:00 2001 From: cracyc Date: Mon, 6 May 2013 00:09:10 +0000 Subject: [PATCH] (mess) psxmultitap: add multitap support [Carl] --- .gitattributes | 2 + src/mess/machine/psxcard.c | 15 +- src/mess/machine/psxcard.h | 3 + src/mess/machine/psxcport.c | 24 ++- src/mess/machine/psxcport.h | 3 + src/mess/machine/psxmultitap.c | 266 +++++++++++++++++++++++++++++++++ src/mess/machine/psxmultitap.h | 41 +++++ src/mess/mess.mak | 1 + 8 files changed, 351 insertions(+), 4 deletions(-) create mode 100644 src/mess/machine/psxmultitap.c create mode 100644 src/mess/machine/psxmultitap.h diff --git a/.gitattributes b/.gitattributes index 3ba412f82ad..bc3875cee60 100644 --- a/.gitattributes +++ b/.gitattributes @@ -7551,6 +7551,8 @@ src/mess/machine/psxcd.c svneol=native#text/plain src/mess/machine/psxcd.h svneol=native#text/plain src/mess/machine/psxcport.c svneol=native#text/plain src/mess/machine/psxcport.h svneol=native#text/plain +src/mess/machine/psxmultitap.c svneol=native#text/plain +src/mess/machine/psxmultitap.h svneol=native#text/plain src/mess/machine/radio86.c svneol=native#text/plain src/mess/machine/rm380z.c svneol=native#text/plain src/mess/machine/rmnimbus.c svneol=native#text/plain diff --git a/src/mess/machine/psxcard.c b/src/mess/machine/psxcard.c index 342f4acb23e..5f91309602f 100644 --- a/src/mess/machine/psxcard.c +++ b/src/mess/machine/psxcard.c @@ -51,6 +51,7 @@ void psxcard_device::device_start() m_ack_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(psxcard_device::ack_timer), this)); m_ack = true; + m_disabled = false; // save state registrations /* save_item(NAME(pkt)); @@ -95,7 +96,7 @@ bool psxcard_device::transfer(UINT8 to, UINT8 *from) switch (state) { case state_illegal: - if ((to == 0x81) && is_loaded()) + if (is_loaded()) { // printf("CARD: begin\n"); state = state_command; @@ -277,6 +278,12 @@ unsigned char psxcard_device::checksum_data(const unsigned char *buf, const unsi bool psxcard_device::call_load() { + if(m_disabled) + { + logerror("psxcard: port disabled\n"); + return IMAGE_INIT_FAIL; + } + if(length() != card_size) return IMAGE_INIT_FAIL; return IMAGE_INIT_PASS; @@ -287,6 +294,12 @@ bool psxcard_device::call_create(int format_type, option_resolution *format_opti UINT8 block[block_size]; int i, ret; + if(m_disabled) + { + logerror("psxcard: port disabled\n"); + return IMAGE_INIT_FAIL; + } + memset(block, '\0', block_size); for(i = 0; i < (card_size/block_size); i++) { diff --git a/src/mess/machine/psxcard.h b/src/mess/machine/psxcard.h index 06613914a3d..7763658770a 100644 --- a/src/mess/machine/psxcard.h +++ b/src/mess/machine/psxcard.h @@ -29,10 +29,13 @@ public: virtual bool call_load(); virtual bool call_create(int format_type, option_resolution *format_options); + void disable(bool state) { m_disabled = state; if(state) unload(); } + private: unsigned char pkt[0x8b], pkt_ptr, pkt_sz, cmd; unsigned short addr; int state; + bool m_disabled; UINT8 m_odata; UINT8 m_idata; diff --git a/src/mess/machine/psxcport.c b/src/mess/machine/psxcport.c index 429db565654..4847aa01786 100644 --- a/src/mess/machine/psxcport.c +++ b/src/mess/machine/psxcport.c @@ -2,6 +2,7 @@ #include "machine/psxcport.h" #include "machine/psxanalog.h" +#include "machine/psxmultitap.h" const device_type PSX_CONTROLLER_PORT = &device_creator; @@ -26,6 +27,14 @@ machine_config_constructor psx_controller_port_device::device_mconfig_additions( return MACHINE_CONFIG_NAME( psx_memory_card ); } +void psx_controller_port_device::disable_card(bool state) +{ + if(state) + popmessage("Memory card port %s is disabled\n", m_card->brief_instance_name()); + + m_card->disable(state); +} + const device_type PSXCONTROLLERPORTS = &device_creator; psxcontrollerports_device::psxcontrollerports_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : @@ -42,10 +51,19 @@ void psxcontrollerports_device::device_start() psxsiodev_device::device_start(); } +// add controllers to define so they can be connected to the multitap +#define PSX_CONTROLLERS \ + SLOT_INTERFACE("digital_pad", PSX_STANDARD_CONTROLLER) \ + SLOT_INTERFACE("dualshock_pad", PSX_DUALSHOCK) \ + SLOT_INTERFACE("analog_joystick", PSX_ANALOG_JOYSTICK) + SLOT_INTERFACE_START(psx_controllers) - SLOT_INTERFACE("digital_pad", PSX_STANDARD_CONTROLLER) - SLOT_INTERFACE("dualshock_pad", PSX_DUALSHOCK) - SLOT_INTERFACE("analog_joystick", PSX_ANALOG_JOYSTICK) + PSX_CONTROLLERS + SLOT_INTERFACE("multitap", PSX_MULTITAP) +SLOT_INTERFACE_END + +SLOT_INTERFACE_START(psx_controllers_nomulti) + PSX_CONTROLLERS SLOT_INTERFACE_END void psxcontrollerports_device::data_in( int data, int mask ) diff --git a/src/mess/machine/psxcport.h b/src/mess/machine/psxcport.h index d38cc0f3048..396cb9c72b0 100644 --- a/src/mess/machine/psxcport.h +++ b/src/mess/machine/psxcport.h @@ -20,6 +20,7 @@ class psx_controller_port_device; class device_psx_controller_interface : public device_slot_card_interface { + friend class psx_multitap_device; public: device_psx_controller_interface(const machine_config &mconfig, device_t &device); virtual ~device_psx_controller_interface(); @@ -112,6 +113,8 @@ public: DECLARE_READ_LINE_MEMBER(ack_r) { return (m_dev?m_dev->ack_r():true) && m_card->ack_r(); } DECLARE_READ_LINE_MEMBER(tx_r) { return m_tx; } + void disable_card(bool status); + protected: virtual void device_start() {} virtual void device_reset() { m_tx = true; } 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(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; +} diff --git a/src/mess/machine/psxmultitap.h b/src/mess/machine/psxmultitap.h new file mode 100644 index 00000000000..ca696c58376 --- /dev/null +++ b/src/mess/machine/psxmultitap.h @@ -0,0 +1,41 @@ +#ifndef PSXMULTITAP_H_ +#define PSXMULTITAP_H_ + +#include "machine/psxcport.h" + +SLOT_INTERFACE_EXTERN(psx_controllers_nomulti); + +class psx_multitap_device : public device_t, + public device_psx_controller_interface +{ +public: + psx_multitap_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + virtual machine_config_constructor device_mconfig_additions() const; + +protected: + virtual void device_start(); + virtual void device_stop() { device_psx_controller_interface::m_owner->disable_card(false); } + virtual void device_reset() { device_psx_controller_interface::m_owner->disable_card(true); } + virtual void device_config_complete() { m_shortname = "psx_multitap"; } + virtual void interface_pre_reset(); + +private: + virtual bool get_pad(int count, UINT8 *odata, UINT8 idata); + virtual void do_pad(); + void ack(); + void set_tx_line(bool tx, int port); + bool get_rx_line(int port); + + int m_activeport; + bool m_cack[4]; + bool m_singlemode, m_nextmode, m_tapmc; + UINT8 m_data[3][8]; // port a is passed though + required_device m_porta; + required_device m_portb; + required_device m_portc; + required_device m_portd; +}; + +extern const device_type PSX_MULTITAP; + +#endif /* PSXMULTITAP_H_ */ diff --git a/src/mess/mess.mak b/src/mess/mess.mak index 5a276d6f8d7..c53eaa19614 100644 --- a/src/mess/mess.mak +++ b/src/mess/mess.mak @@ -1771,6 +1771,7 @@ $(MESSOBJ)/sony.a: \ $(MESS_MACHINE)/psxcd.o \ $(MESS_MACHINE)/psxcard.o \ $(MESS_MACHINE)/psxanalog.o \ + $(MESS_MACHINE)/psxmultitap.o \ $(MESS_DRIVERS)/pockstat.o \ $(MESS_DRIVERS)/smc777.o \