summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/bus/nes_ctrl/joypad.cpp
blob: 96a22ac1f66eefe47ee76bd60c91bb3d61fddfdc (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
// 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 "emu.h"
#include "joypad.h"

//**************************************************************************
//  DEVICE DEFINITIONS
//**************************************************************************

DEFINE_DEVICE_TYPE(NES_JOYPAD,      nes_joypad_device,   "nes_joypad",   "Nintendo NES / FC Control Pad")
DEFINE_DEVICE_TYPE(NES_FCPAD_P2,    nes_fcpad2_device,   "nes_fcpad2",   "Nintendo Family Computer P2 Pad")
DEFINE_DEVICE_TYPE(NES_CCPAD_LEFT,  nes_ccpadl_device,   "nes_ccpadl",   "FC Crazy Climber Left Pad")
DEFINE_DEVICE_TYPE(NES_CCPAD_RIGHT, nes_ccpadr_device,   "nes_ccpadr",   "FC Crazy Climber Right Pad")
DEFINE_DEVICE_TYPE(NES_ARCSTICK,    nes_arcstick_device, "nes_arcstick", "Nintendo Family Computer Arcade Stick")


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


//-------------------------------------------------
//  device_add_mconfig - add device configuration
//-------------------------------------------------

MACHINE_CONFIG_START(nes_arcstick_device::device_add_mconfig)
	// expansion port to allow daisy chaining
	MCFG_FC_EXPANSION_PORT_ADD("subexp", arcstick_daisy, nullptr)
MACHINE_CONFIG_END


//**************************************************************************
//  LIVE DEVICE
//**************************************************************************

//-------------------------------------------------
//  nes_joypad_device - constructor
//-------------------------------------------------

nes_joypad_device::nes_joypad_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) :
	device_t(mconfig, type, tag, owner, clock),
	device_nes_control_port_interface(mconfig, *this),
	m_joypad(*this, "JOYPAD"), m_latch(0)
{
}

nes_joypad_device::nes_joypad_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
	nes_joypad_device(mconfig, NES_JOYPAD, tag, owner, clock)
{
}

nes_fcpad2_device::nes_fcpad2_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
	nes_joypad_device(mconfig, NES_FCPAD_P2, tag, owner, clock)
{
}

nes_ccpadl_device::nes_ccpadl_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
	nes_joypad_device(mconfig, NES_CCPAD_LEFT, tag, owner, clock)
{
}

nes_ccpadr_device::nes_ccpadr_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
	nes_joypad_device(mconfig, NES_CCPAD_RIGHT, tag, owner, clock)
{
}

nes_arcstick_device::nes_arcstick_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
	nes_joypad_device(mconfig, NES_ARCSTICK, tag, owner, clock),
	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_t nes_joypad_device::read_bit0()
{
	uint8_t ret = m_latch & 1;
	m_latch >>= 1;
	return ret;
}

uint8_t nes_fcpad2_device::read_exp(offs_t offset)
{
	uint8_t 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_t nes_arcstick_device::read_exp(offs_t offset)
{
	uint8_t 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_t data)
{
	if (data & 0x01)
		return;

	m_latch = m_joypad->read();
}

void nes_fcpad2_device::write(uint8_t 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_t data)
{
	m_daisychain->write(data);

	if (data & 0x01)
		return;

	m_latch = m_joypad->read();
}