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(sms_sports_pad_jp_device, rldu_pins_r) // R,L,D,U
PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED ) // Vcc
PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_CUSTOM ) PORT_READ_LINE_MEMBER(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_MEMBER(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, const XTAL &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();
}
|