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
|
// 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"
namespace {
INPUT_PORTS_START( sms_sports_pad_jp )
PORT_START("SPORTS_JP_BT") // buttons
PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1 ) // TL
PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON2 ) // TR
PORT_BIT( 0x0c, IP_ACTIVE_LOW, IPT_UNUSED )
PORT_START("SPORTS_JP_X") // X axis
PORT_BIT( 0xff, 0x00, IPT_TRACKBALL_X ) PORT_SENSITIVITY(50) PORT_KEYDELTA(40)
PORT_START("SPORTS_JP_Y") // Y axis
PORT_BIT( 0xff, 0x00, IPT_TRACKBALL_Y ) PORT_SENSITIVITY(50) PORT_KEYDELTA(40)
INPUT_PORTS_END
//**************************************************************************
// TYPE DEFINITIONS
//**************************************************************************
class sms_sports_pad_jp_device : public device_t, public device_sms_control_interface
{
public:
// construction/destruction
sms_sports_pad_jp_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;
protected:
// device_t implementation
virtual ioport_constructor device_input_ports() const override { return INPUT_PORTS_NAME(sms_sports_pad_jp); }
virtual void device_start() override { }
private:
required_ioport m_buttons;
required_ioport m_x_axis;
required_ioport m_y_axis;
};
//-------------------------------------------------
// 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_interface(mconfig, *this),
m_buttons(*this, "SPORTS_JP_BT"),
m_x_axis(*this, "SPORTS_JP_X"),
m_y_axis(*this, "SPORTS_JP_Y")
{
}
//-------------------------------------------------
// sms_peripheral_r - sports pad read
//-------------------------------------------------
uint8_t sms_sports_pad_jp_device::in_r()
{
int const num_intervals = machine().time().as_ticks(20000); // time interval not verified
switch (num_intervals % 5)
{
default: // shut up dumb compilers
case 0: // X high nibble
return 0x00 | ((m_x_axis->read() >> 4) & 0x0f);
case 1: // X low nibble
return 0x10 | (m_x_axis->read() & 0x0f);
case 2: // Y high nibble
return 0x00 | ((m_y_axis->read() >> 4) & 0x0f);
case 3: // Y low nibble
return 0x10 | (m_y_axis->read() & 0x0f);
case 4: // buttons 1 and 2
return 0x30 | m_buttons->read();
}
}
} // anonymous namespace
//**************************************************************************
// DEVICE DEFINITIONS
//**************************************************************************
DEFINE_DEVICE_TYPE_PRIVATE(SMS_SPORTS_PAD_JP, device_sms_control_interface, sms_sports_pad_jp_device, "sms_sports_pad_jp", "Sega Mark III Sports Pad (Japan)")
|