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
|
// license:BSD-3-Clause
// copyright-holders:Fabio Priuli
/**********************************************************************
Sega Master System "Sports Pad" (US model) emulation
Release data from the Sega Retro project:
Year: 1987 Country/region: US Model code: 3040
TODO:
- For low-level emulation, a device for the TMP42C66P, a Toshiba 4-bit
microcontroller, is needed along with a dump of its internal ROM.
- Auto-repeat and Control/Sports mode switches are not emulated.
Notes:
Games designed for the US model of the Sports Pad controller use the
TH line of the controller port to select which nibble of the two axis
to read. The Japanese cartridge Sports Pad Soccer uses a different mode
when it does not detect use by a Japanese SMS console, because the Sega
Mark III lacks the TH line. There was a different Sports Pad model released
in Japan and no information was found about it supporting both modes, so
that model is currently emulated as a different device (see sportsjp.cpp).
It was discovered that games designed for the Paddle Controller, released
in Japan, will switch to a mode incompatible with the original Paddle when
the system region is detected as Export. Similar to how the US model of the
Sports Pad works, that mode uses the TH line as an 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,
otherwise the software for the paddle think it's moving backward.
See http://mametesters.org/view.php?id=5872 for discussion.
**********************************************************************/
#include "emu.h"
#include "sports.h"
//#define VERBOSE 1
//#define LOG_OUTPUT_FUNC osd_printf_info
#include "logmacro.h"
namespace {
INPUT_PORTS_START( sms_sports_pad )
PORT_START("SPORTS_BT")
PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON1 ) // TL
PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON2 ) // TR
PORT_START("SPORTS_X") // X axis
PORT_BIT( 0xff, 0x00, IPT_TRACKBALL_X ) PORT_SENSITIVITY(50) PORT_KEYDELTA(40) PORT_REVERSE
PORT_START("SPORTS_Y") // Y axis
PORT_BIT( 0xff, 0x00, IPT_TRACKBALL_Y ) PORT_SENSITIVITY(50) PORT_KEYDELTA(40) PORT_REVERSE
INPUT_PORTS_END
//**************************************************************************
// TYPE DEFINITIONS
//**************************************************************************
class sms_sports_pad_device : public device_t, public device_sms_control_interface
{
public:
// construction/destruction
sms_sports_pad_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
// device_sms_control_interface implementation
virtual uint8_t in_r() override;
virtual void out_w(uint8_t data, uint8_t mem_mask) override;
protected:
// device_t implementation
virtual ioport_constructor device_input_ports() const override { return INPUT_PORTS_NAME(sms_sports_pad); }
virtual void device_start() override ATTR_COLD;
private:
TIMER_CALLBACK_MEMBER(timeout);
required_ioport m_buttons;
required_ioport_array<2> m_axes;
emu_timer *m_timeout_timer;
uint8_t m_th_state;
uint8_t m_phase;
uint8_t m_output;
uint8_t m_base[2];
uint8_t m_data[2];
};
//-------------------------------------------------
// sms_sports_pad_device - constructor
//-------------------------------------------------
sms_sports_pad_device::sms_sports_pad_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
device_t(mconfig, SMS_SPORTS_PAD, tag, owner, clock),
device_sms_control_interface(mconfig, *this),
m_buttons(*this, "SPORTS_BT"),
m_axes(*this, "SPORTS_%c", 'X'),
m_timeout_timer(nullptr),
m_th_state(1),
m_phase(0),
m_output(0x0f)
{
}
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
void sms_sports_pad_device::device_start()
{
m_timeout_timer = timer_alloc(FUNC(sms_sports_pad_device::timeout), this);
m_phase = 0;
m_output = 0x0f;
m_base[0] = 0x80; // value 0x80 helps when starting paddle games.
m_base[1] = 0x80;
m_data[0] = 0x00;
m_data[1] = 0x00;
save_item(NAME(m_th_state));
save_item(NAME(m_phase));
save_item(NAME(m_output));
save_item(NAME(m_base));
save_item(NAME(m_data));
}
uint8_t sms_sports_pad_device::in_r()
{
return m_buttons->read() | m_output;
}
void sms_sports_pad_device::out_w(uint8_t data, uint8_t mem_mask)
{
// Sequence used to read Sports Pad:
// * TH = 0, spin for 80 microseconds, read X high nybble
// * TH = 1, spin for 40 microseconds, read X low nybble
// * TH = 0, spin for 40 microseconds, read Y high nybble
// * TH = 1, spin for 40 microseconds, read Y low nybble
uint8_t const th_state = BIT(data, 6);
if (th_state != m_th_state)
{
if (!th_state)
{
LOG(
"%s: TH falling, output %c high nybble\n",
machine().describe_context(),
m_phase ? 'Y' : 'X');
m_timeout_timer->reset();
m_data[m_phase] = m_axes[m_phase]->read();
m_output = BIT(m_data[m_phase] - m_base[m_phase], 4, 4);
}
else
{
LOG(
"%s: TH rising, output %c low nybble%s\n",
machine().describe_context(),
m_phase ? 'Y' : 'X',
m_phase ? ", zero counts" : "");
m_timeout_timer->adjust(attotime::from_hz(XTAL(10'738'635) / 3 / 512)); // timeout not verified
m_output = BIT(m_data[m_phase] - m_base[m_phase], 0, 4);
if (m_phase)
{
// assume the rising edge when Y is output zeroes the axes
m_base[0] = m_data[0];
m_base[1] = m_data[1];
}
m_phase ^= 1;
}
}
m_th_state = th_state;
}
TIMER_CALLBACK_MEMBER(sms_sports_pad_device::timeout)
{
// assume it just flips back to X, allowing paddle games to "work"
LOG("timeout, select X axis\n");
m_phase = 0;
}
} // anonymous namespace
//**************************************************************************
// DEVICE DEFINITIONS
//**************************************************************************
DEFINE_DEVICE_TYPE_PRIVATE(SMS_SPORTS_PAD, device_sms_control_interface, sms_sports_pad_device, "sms_sports_pad", "Sega SMS Sports Pad (US)")
|