summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/bus/sms_ctrl/sportsjp.cpp
blob: 76c8e607e578056151eef837643464a4b2d9ec47 (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
// license:BSD-3-Clause
// copyright-holders:Fabio Priuli
/**********************************************************************

    Sega Master System "Sports Pad" (Japanese model) emulation


Release data from the Sega Retro project:

  Year: 1988    Country/region: JP    Model code: SP-500

TODO:

- For low-level emulation, a device for the TMP42C66P, a Toshiba 4bit
  microcontroller, needs to be created, but a dump of its internal ROM
  seems to be required.

Notes:

  This Japanese Sports Pad controller device is only required to play the
  cartridge Sports Pad Soccer, released in Japan, on non-SMSJ consoles without
  ROM header validation, like the Sega Mark III and the Korean SMS2 version.

  The Japanese version of Sports Pad Soccer has code to operate the Sports
  Pad controller in two diffent modes. When it detects a Japanese SMS (testing
  if port $F2 has two bits for mute control), the operation is the same used
  by US Sports Pad games. Otherwise, it uses a mode that polls bits TR and TL
  of the controller ports, compatible with the Sega Mark III, that lacks the
  TH line used by the US Sports Pad mode. This Mark III mode is also used on
  other non-SMSJ consoles, like the Korean SMS2 version. The two controller
  modes are significantly different from each other and no information was
  found about support for both modes on the Japanese Sports Pad model, so that
  model is currently emulated as a different device.

  A bug was discovered in the player 2 input handling code of the Mark III
  compatible mode of the only known good ROM dump of Sports Pad Soccer (JP):
  size="131072" crc="41c948bf" sha1="7634ce39e87049dad1ee4f32a80d728e4bd1f81f"
  At address $12D1, instead read the upper 2 bits of port $DC and lower 2 bits
  of port $DD (to obtain the lower nibble of the current axis for player 2),
  the code wrongly reads the lower nibble of port $DC, that is player 1 data.

**********************************************************************/

#include "emu.h"
#include "sportsjp.h"



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

DEFINE_DEVICE_TYPE(SMS_SPORTS_PAD_JP, sms_sports_pad_jp_device, "sms_sports_pad_jp", "Sega SMS Sports Pad (JP)")

// time interval not verified
#define SPORTS_PAD_JP_INTERVAL attotime::from_hz(20000)



// The returned value is inverted due to IP_ACTIVE_LOW mapping.
READ_LINE_MEMBER( sms_sports_pad_jp_device::tl_pin_r ) { return ~m_tl_pin_state; }
READ_LINE_MEMBER( sms_sports_pad_jp_device::tr_pin_r ) { return ~m_tr_pin_state; }
CUSTOM_INPUT_MEMBER( sms_sports_pad_jp_device::rldu_pins_r ) { return ~(m_rldu_pins_state & 0x0f); }


static INPUT_PORTS_START( sms_sports_pad_jp )
	PORT_START("SPORTS_JP_IN")
	PORT_BIT( 0x0f, IP_ACTIVE_LOW, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(DEVICE_SELF, sms_sports_pad_jp_device, rldu_pins_r, nullptr) // R,L,D,U
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED ) // Vcc
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER( DEVICE_SELF, sms_sports_pad_jp_device, tl_pin_r ) // TL
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )  // TH
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER( DEVICE_SELF, sms_sports_pad_jp_device, tr_pin_r ) // TR

	PORT_START("SPORTS_JP_BT")    /* Sports Pad buttons nibble */
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1 )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON2 )
	PORT_BIT( 0x0c, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("SPORTS_JP_X")    /* Sports Pad X axis */
	PORT_BIT( 0xff, 0x00, IPT_TRACKBALL_X ) PORT_SENSITIVITY(50) PORT_KEYDELTA(40)

	PORT_START("SPORTS_JP_Y")    /* Sports Pad Y axis */
	PORT_BIT( 0xff, 0x00, IPT_TRACKBALL_Y ) PORT_SENSITIVITY(50) PORT_KEYDELTA(40)
INPUT_PORTS_END


//-------------------------------------------------
//  input_ports - device-specific input ports
//-------------------------------------------------

ioport_constructor sms_sports_pad_jp_device::device_input_ports() const
{
	return INPUT_PORTS_NAME( sms_sports_pad_jp );
}



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

//-------------------------------------------------
//  sms_sports_pad_jp_device - constructor
//-------------------------------------------------

sms_sports_pad_jp_device::sms_sports_pad_jp_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
	device_t(mconfig, SMS_SPORTS_PAD_JP, tag, owner, clock),
	device_sms_control_port_interface(mconfig, *this),
	m_sports_jp_in(*this, "SPORTS_JP_IN"),
	m_sports_jp_bt(*this, "SPORTS_JP_BT"),
	m_sports_jp_x(*this, "SPORTS_JP_X"),
	m_sports_jp_y(*this, "SPORTS_JP_Y"),
	m_rldu_pins_state(0x0f),
	m_tl_pin_state(1),
	m_tr_pin_state(1),
	m_interval(SPORTS_PAD_JP_INTERVAL)
{
}


//-------------------------------------------------
//  device_start - device-specific startup
//-------------------------------------------------

void sms_sports_pad_jp_device::device_start()
{
	m_start_time = machine().time();

	save_item(NAME(m_start_time));
	save_item(NAME(m_rldu_pins_state));
	save_item(NAME(m_tl_pin_state));
	save_item(NAME(m_tr_pin_state));
}


//-------------------------------------------------
//  sms_peripheral_r - sports pad read
//-------------------------------------------------

uint8_t sms_sports_pad_jp_device::peripheral_r()
{
	int num_intervals = (machine().time() - m_start_time).as_double() / m_interval.as_double();

	switch (num_intervals % 5)
	{
	case 0:
		// X high nibble
		m_rldu_pins_state = m_sports_jp_x->read() >> 4;
		m_tl_pin_state = 0;
		m_tr_pin_state = 0;
		break;
	case 1:
		// X low nibble
		m_rldu_pins_state = m_sports_jp_x->read();
		m_tl_pin_state = 1;
		m_tr_pin_state = 0;
		break;
	case 2:
		// Y high nibble
		m_rldu_pins_state = m_sports_jp_y->read() >> 4;
		m_tl_pin_state = 0;
		m_tr_pin_state = 0;
		break;
	case 3:
		// Y low nibble
		m_rldu_pins_state = m_sports_jp_y->read();
		m_tl_pin_state = 1;
		m_tr_pin_state = 0;
		break;
	case 4:
		// buttons 1 and 2
		m_rldu_pins_state = m_sports_jp_bt->read();
		m_tl_pin_state = 1;
		m_tr_pin_state = 1;
		break;
	}

	return m_sports_jp_in->read();
}