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
|
// license:BSD-3-Clause
// copyright-holders:Fabio Priuli
/**********************************************************************
Sega Master System "Sports Pad" (US model) emulation
**********************************************************************/
// The games designed for the US model of the Sports Pad controller use the
// TH line of the controller port as output, to select which nibble, of the
// two axis bytes, will be read at a time. The Japanese cartridge Sports Pad
// Soccer uses a different mode, because the Sega Mark III lacks TH output, so
// there is a different Sports Pad model released in Japan (see sportsjp.c).
// It was discovered that games designed for the Paddle Controller, released
// in Japan, switch to a mode incompatible with the original Paddle when
// detect the system region as Export. Similar to how the US model of the
// Sports Pad works, that mode uses the TH line as output to select which
// nibble of the X axis will be read. So, on an Export console version, paddle
// games are somewhat playable with the US Sport Pad model, though it needs to
// be used inverted and the trackball needs to be moved slowly, else the
// software for the paddle think it's moving backward.
// See http://mametesters.org/view.php?id=5872 for discussion.
#include "sports.h"
//**************************************************************************
// DEVICE DEFINITIONS
//**************************************************************************
const device_type SMS_SPORTS_PAD = &device_creator<sms_sports_pad_device>;
// time interval not verified
#define SPORTS_PAD_INTERVAL attotime::from_hz(XTAL_53_693175MHz/15/512)
void sms_sports_pad_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
switch (id)
{
case TIMER_SPORTSPAD:
// values for x and y axis need to be resetted for Sports Pad games, but
// are not resetted for paddle games, so it was assumed the reset occurs
// only when this timer fires after the read state reached maximum value.
if (m_read_state == 3)
{
m_x_axis_reset_value = m_sports_x->read();
m_y_axis_reset_value = m_sports_y->read();
}
else
{
// set to maximum value, so it wraps to 0 at next increment
m_read_state = 3;
}
break;
default:
assert_always(FALSE, "Unknown id in sms_sports_pad_device::device_timer");
}
}
CUSTOM_INPUT_MEMBER( sms_sports_pad_device::th_pin_r )
{
return m_last_data;
}
INPUT_CHANGED_MEMBER( sms_sports_pad_device::th_pin_w )
{
m_read_state = (m_read_state + 1) & 3;
m_sportspad_timer->adjust(m_interval);
m_last_data = newval;
}
CUSTOM_INPUT_MEMBER( sms_sports_pad_device::dir_pins_r )
{
UINT8 data = 0;
switch (m_read_state)
{
case 0:
data = (m_sports_x->read() - m_x_axis_reset_value) >> 4;
break;
case 1:
data = (m_sports_x->read() - m_x_axis_reset_value);
break;
case 2:
data = (m_sports_y->read() - m_y_axis_reset_value) >> 4;
break;
case 3:
data = (m_sports_y->read() - m_y_axis_reset_value);
break;
}
// The returned value is inverted due to IP_ACTIVE_LOW mapping.
return ~(data & 0x0f);
}
static INPUT_PORTS_START( sms_sports_pad )
PORT_START("SPORTS_IN")
PORT_BIT( 0x0f, IP_ACTIVE_LOW, IPT_SPECIAL ) PORT_CUSTOM_MEMBER(DEVICE_SELF, sms_sports_pad_device, dir_pins_r, NULL) // Directional pins
PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED ) // Vcc
PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON1 ) // TL (Button 1)
PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_SPECIAL ) PORT_CUSTOM_MEMBER(DEVICE_SELF, sms_sports_pad_device, th_pin_r, NULL)
PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_BUTTON2 ) // TR (Button 2)
PORT_START("SPORTS_OUT")
PORT_BIT( 0x0f, IP_ACTIVE_LOW, IPT_UNUSED ) // Directional pins
PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED ) // Vcc
PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED ) // TL (Button 1)
PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_OUTPUT ) PORT_CHANGED_MEMBER(DEVICE_SELF, sms_sports_pad_device, th_pin_w, NULL)
PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED ) // TR (Button 2)
PORT_START("SPORTS_X") /* Sports Pad X axis */
PORT_BIT( 0xff, 0x00, IPT_TRACKBALL_X ) PORT_SENSITIVITY(50) PORT_KEYDELTA(40) PORT_REVERSE
PORT_START("SPORTS_Y") /* Sports Pad Y axis */
PORT_BIT( 0xff, 0x00, IPT_TRACKBALL_Y ) PORT_SENSITIVITY(50) PORT_KEYDELTA(40) PORT_REVERSE
INPUT_PORTS_END
//-------------------------------------------------
// input_ports - device-specific input ports
//-------------------------------------------------
ioport_constructor sms_sports_pad_device::device_input_ports() const
{
return INPUT_PORTS_NAME( sms_sports_pad );
}
//**************************************************************************
// LIVE DEVICE
//**************************************************************************
//-------------------------------------------------
// sms_sports_pad_device - constructor
//-------------------------------------------------
sms_sports_pad_device::sms_sports_pad_device(const machine_config &mconfig, std::string tag, device_t *owner, UINT32 clock) :
device_t(mconfig, SMS_SPORTS_PAD, "Sega SMS Sports Pad US", tag, owner, clock, "sms_sports_pad", __FILE__),
device_sms_control_port_interface(mconfig, *this),
m_sports_in(*this, "SPORTS_IN"),
m_sports_out(*this, "SPORTS_OUT"),
m_sports_x(*this, "SPORTS_X"),
m_sports_y(*this, "SPORTS_Y"),
m_read_state(0),
m_last_data(0),
m_x_axis_reset_value(0x80), // value 0x80 helps when start playing paddle games.
m_y_axis_reset_value(0x80),
m_interval(SPORTS_PAD_INTERVAL), m_sportspad_timer(nullptr)
{
}
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
void sms_sports_pad_device::device_start()
{
m_sportspad_timer = timer_alloc(TIMER_SPORTSPAD);
save_item(NAME(m_read_state));
save_item(NAME(m_last_data));
save_item(NAME(m_x_axis_reset_value));
save_item(NAME(m_y_axis_reset_value));
}
//-------------------------------------------------
// sms_peripheral_r - sports pad read
//-------------------------------------------------
UINT8 sms_sports_pad_device::peripheral_r()
{
return m_sports_in->read();
}
//-------------------------------------------------
// sms_peripheral_w - sports pad write
//-------------------------------------------------
void sms_sports_pad_device::peripheral_w(UINT8 data)
{
m_sports_out->write(data);
}
|