diff options
Diffstat (limited to 'src/devices/bus/nes_ctrl/joypad.c')
-rw-r--r-- | src/devices/bus/nes_ctrl/joypad.c | 315 |
1 files changed, 315 insertions, 0 deletions
diff --git a/src/devices/bus/nes_ctrl/joypad.c b/src/devices/bus/nes_ctrl/joypad.c new file mode 100644 index 00000000000..efe17fb8a62 --- /dev/null +++ b/src/devices/bus/nes_ctrl/joypad.c @@ -0,0 +1,315 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/********************************************************************** + + Nintendo Family Computer & Entertainment System Joypads + + The original Famicom had two hardwired controller, with the second + controller slightly different from the first one: it featured no + Start nor Select buttons, but had a built-in microphone (with + very limited capabilities, since it basically only detected two + states: something blowing into it / nothing blowing into it) for + some games to react to users "talking" into it + + Crazy Climber Pads are not really a kind of separate controllers, + but just a couple of small sticks to be put on top of d-pads of + the regular controllers. Users should then control the game by + using both controllers, turned 90 degrees, as a couple of dual + sticks like in the arcade control panel. However, we emulate them + separately so to map the controls to a friendlier default. + + The Arcade Stick we emulate is a controller (apparently manufactured + by Hori, but possibly licensed by Nintendo, since it use the official + logo and brand) which fits into the expansion port and allows to + daisy chain a second controller to the first one, to play 4players + game (an image of such connection is shown e.g. in Nekketsu Koukou + Dodgeball Bu manual) + +**********************************************************************/ + +#include "joypad.h" + +//************************************************************************** +// DEVICE DEFINITIONS +//************************************************************************** + +const device_type NES_JOYPAD = &device_creator<nes_joypad_device>; +const device_type NES_FCPAD_P2 = &device_creator<nes_fcpad2_device>; +const device_type NES_CCPAD_LEFT = &device_creator<nes_ccpadl_device>; +const device_type NES_CCPAD_RIGHT = &device_creator<nes_ccpadr_device>; +const device_type NES_ARCSTICK = &device_creator<nes_arcstick_device>; + + +static INPUT_PORTS_START( nes_joypad ) + PORT_START("JOYPAD") + PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("A") + PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("B") + PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_SELECT ) + PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) + PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_8WAY + PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_8WAY + PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_8WAY + PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_8WAY +INPUT_PORTS_END + +static INPUT_PORTS_START( nes_fcpad_p2 ) + PORT_START("JOYPAD") + PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("A") + PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("B") + PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_NAME("Microphone") + PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED ) + PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_8WAY + PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_8WAY + PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_8WAY + PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_8WAY +INPUT_PORTS_END + +static INPUT_PORTS_START( nes_ccpad_left ) + PORT_START("JOYPAD") + PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED ) + PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED ) + PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_SELECT ) + PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) + PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_RIGHT ) PORT_8WAY + PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_LEFT ) PORT_8WAY + PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_UP ) PORT_8WAY + PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_DOWN ) PORT_8WAY +INPUT_PORTS_END + +static INPUT_PORTS_START( nes_ccpad_right ) + PORT_START("JOYPAD") + PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED ) + PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED ) + PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED ) + PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED ) + PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_RIGHT ) PORT_8WAY + PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_LEFT ) PORT_8WAY + PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_UP ) PORT_8WAY + PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICKRIGHT_DOWN ) PORT_8WAY +INPUT_PORTS_END + +static INPUT_PORTS_START( nes_arcstick ) + PORT_START("CONFIG") + PORT_CONFNAME( 0x01, 0x01, "4 Way / 8 Way Joystick") + PORT_CONFSETTING( 0x00, "4 Way" ) + PORT_CONFSETTING( 0x01, "8 Way" ) + PORT_CONFNAME( 0x02, 0x00, "Player ID") + PORT_CONFSETTING( 0x00, "Player I" ) + PORT_CONFSETTING( 0x02, "Player II" ) + + PORT_START("JOYPAD") + PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("A") + PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("B") + PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_SELECT ) + PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) + PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_8WAY PORT_CONDITION("CONFIG", 0x01, EQUALS, 0x01) + PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_8WAY PORT_CONDITION("CONFIG", 0x01, EQUALS, 0x01) + PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_8WAY PORT_CONDITION("CONFIG", 0x01, EQUALS, 0x01) + PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_8WAY PORT_CONDITION("CONFIG", 0x01, EQUALS, 0x01) + PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_4WAY PORT_CONDITION("CONFIG", 0x01, EQUALS, 0x00) + PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_4WAY PORT_CONDITION("CONFIG", 0x01, EQUALS, 0x00) + PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_4WAY PORT_CONDITION("CONFIG", 0x01, EQUALS, 0x00) + PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_4WAY PORT_CONDITION("CONFIG", 0x01, EQUALS, 0x00) +INPUT_PORTS_END + +//------------------------------------------------- +// input_ports - device-specific input ports +//------------------------------------------------- + +ioport_constructor nes_joypad_device::device_input_ports() const +{ + return INPUT_PORTS_NAME( nes_joypad ); +} + +ioport_constructor nes_fcpad2_device::device_input_ports() const +{ + return INPUT_PORTS_NAME( nes_fcpad_p2 ); +} + +ioport_constructor nes_ccpadl_device::device_input_ports() const +{ + return INPUT_PORTS_NAME( nes_ccpad_left ); +} + +ioport_constructor nes_ccpadr_device::device_input_ports() const +{ + return INPUT_PORTS_NAME( nes_ccpad_right ); +} + +ioport_constructor nes_arcstick_device::device_input_ports() const +{ + return INPUT_PORTS_NAME( nes_arcstick ); +} + + + +static SLOT_INTERFACE_START( arcstick_daisy ) + SLOT_INTERFACE("arcstick", NES_ARCSTICK) +SLOT_INTERFACE_END + +static MACHINE_CONFIG_FRAGMENT( arcstick ) + // expansion port to allow daisy chaining + MCFG_FC_EXPANSION_PORT_ADD("subexp", arcstick_daisy, NULL) +MACHINE_CONFIG_END + + +//------------------------------------------------- +// machine_config_additions - device-specific +// machine configurations +//------------------------------------------------- + +machine_config_constructor nes_arcstick_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( arcstick ); +} + + + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +//------------------------------------------------- +// nes_joypad_device - constructor +//------------------------------------------------- + +nes_joypad_device::nes_joypad_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source) : + device_t(mconfig, type, name, tag, owner, clock, shortname, source), + device_nes_control_port_interface(mconfig, *this), + m_joypad(*this, "JOYPAD") +{ +} + +nes_joypad_device::nes_joypad_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : + device_t(mconfig, NES_JOYPAD, "Nintendo NES / FC Control Pad", tag, owner, clock, "nes_joypad", __FILE__), + device_nes_control_port_interface(mconfig, *this), + m_joypad(*this, "JOYPAD") +{ +} + +nes_fcpad2_device::nes_fcpad2_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : + nes_joypad_device(mconfig, NES_FCPAD_P2, "Nintendo Family Computer P2 Pad", tag, owner, clock, "nes_fcpad2", __FILE__) +{ +} + +nes_ccpadl_device::nes_ccpadl_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : + nes_joypad_device(mconfig, NES_CCPAD_LEFT, "FC Crazy Climber Left Pad", tag, owner, clock, "nes_ccpadl", __FILE__) +{ +} + +nes_ccpadr_device::nes_ccpadr_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : + nes_joypad_device(mconfig, NES_CCPAD_RIGHT, "FC Crazy Climber Right Pad", tag, owner, clock, "nes_ccpadr", __FILE__) +{ +} + +nes_arcstick_device::nes_arcstick_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : + nes_joypad_device(mconfig, NES_ARCSTICK, "Nintendo Family Computer Arcade Stick", tag, owner, clock, "nes_arcstick", __FILE__), + m_daisychain(*this, "subexp"), + m_cfg(*this, "CONFIG") +{ +} + + +//------------------------------------------------- +// device_start +//------------------------------------------------- + +void nes_joypad_device::device_start() +{ + save_item(NAME(m_latch)); +} + + +//------------------------------------------------- +// device_reset +//------------------------------------------------- + +void nes_joypad_device::device_reset() +{ + m_latch = 0; +} + + +//------------------------------------------------- +// read +//------------------------------------------------- + +UINT8 nes_joypad_device::read_bit0() +{ + UINT8 ret = m_latch & 1; + m_latch >>= 1; + return ret; +} + +UINT8 nes_fcpad2_device::read_exp(offs_t offset) +{ + UINT8 ret = 0; + if (!offset) // microphone input + ret |= m_joypad->read() & 0x04; + + return ret; +} + +// NOTE: I haven't found any documentation about what happens when +// users connect two arcade stick both set as P1 or P2 in config. +// does the FC only acknowledge the first one, or do they conflict +// with each other? currently, we only support the following setup: +// if the first pad is set as P1, the daisy chained pad is checked +// for P2 only, and vice versa. +UINT8 nes_arcstick_device::read_exp(offs_t offset) +{ + UINT8 ret = 0; + if (offset == 0) //$4016 + { + if ((m_cfg->read() & 2) == 0) // we are P1 input + { + ret |= (m_latch & 1) << 1; + m_latch >>= 1; + } + else + ret |= m_daisychain->read_exp(0); + } + else //$4017 + { + if ((m_cfg->read() & 2) == 2) // we are P2 input + { + ret |= (m_latch & 1) << 1; + m_latch >>= 1; + } + else + ret |= m_daisychain->read_exp(1); + } + + return ret; +} + +//------------------------------------------------- +// write +//------------------------------------------------- + +void nes_joypad_device::write(UINT8 data) +{ + if (data & 0x01) + return; + + m_latch = m_joypad->read(); +} + +void nes_fcpad2_device::write(UINT8 data) +{ + if (data & 0x01) + return; + + // microphone is hooked to expansion bits, not to the controller bit + m_latch = m_joypad->read() & ~0x04; +} + +void nes_arcstick_device::write(UINT8 data) +{ + m_daisychain->write(data); + + if (data & 0x01) + return; + + m_latch = m_joypad->read(); +} |