This one documents what I think is a real bug of the Japanese game Sports Pad Soccer: part of the player 2 input is read from player 1 input instead. The patch also reorganizes code of the Japanese Sports Pad controller device and replace some PORT_CUSTOM/PORT_CHANGED callbacks with PORT_READ_LINE/PORT_WRITE_LINE. [Enik]

This commit is contained in:
Miodrag Milanovic 2016-04-06 07:49:49 +02:00
parent 865253ccb0
commit 33c5196a42
10 changed files with 85 additions and 74 deletions

View File

@ -6128,6 +6128,7 @@
</part>
</software>
<!-- Notes: This has a player 2 input bug: it reads part of player 1 data instead (see sportsjp.cpp) -->
<software name="sportssc" cloneof="worldsoc">
<description>Sports Pad Soccer (Jpn)</description>
<year>1988</year>

View File

@ -12,6 +12,13 @@ Release data from the Sega Retro project:
Year: 1989 Country/region: BR Model code: 010470
Year: 198? Country/region: KR Model code: ?
Notes:
The Light Phaser gun doesn't work with the Japanese SMS and Sega Mark III.
There are reports about Light Phaser working on the second Korean SMS
version, and a Korean advert shows support on the first version (Gam*Boy I,
although based on Japanese SMS).
**********************************************************************/
#include "lphaser.h"
@ -29,7 +36,7 @@ const device_type SMS_LIGHT_PHASER = &device_creator<sms_light_phaser_device>;
#define LGUN_X_INTERVAL 4
CUSTOM_INPUT_MEMBER( sms_light_phaser_device::th_pin_r )
READ_LINE_MEMBER( sms_light_phaser_device::th_pin_r )
{
// The returned value is inverted due to IP_ACTIVE_LOW mapping.
return ~m_sensor_last_state;
@ -46,7 +53,7 @@ INPUT_CHANGED_MEMBER( sms_light_phaser_device::position_changed )
static INPUT_PORTS_START( sms_light_phaser )
PORT_START("CTRL_PORT")
PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON1 ) // TL (trigger)
PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_SPECIAL ) PORT_CUSTOM_MEMBER(DEVICE_SELF, sms_light_phaser_device, th_pin_r, nullptr)
PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_SPECIAL ) PORT_READ_LINE_DEVICE_MEMBER(DEVICE_SELF, sms_light_phaser_device, th_pin_r)
PORT_BIT( 0x9f, IP_ACTIVE_LOW, IPT_UNUSED )
PORT_START("LPHASER_X")

View File

@ -34,7 +34,7 @@ public:
// optional information overrides
virtual ioport_constructor device_input_ports() const override;
DECLARE_CUSTOM_INPUT_MEMBER( th_pin_r );
DECLARE_READ_LINE_MEMBER( th_pin_r );
DECLARE_INPUT_CHANGED_MEMBER( position_changed );
protected:

View File

@ -38,7 +38,7 @@ const device_type SMS_PADDLE = &device_creator<sms_paddle_device>;
#define PADDLE_INTERVAL attotime::from_hz(XTAL_53_693175MHz/15/100)
CUSTOM_INPUT_MEMBER( sms_paddle_device::dir_pins_r )
CUSTOM_INPUT_MEMBER( sms_paddle_device::rldu_pins_r )
{
UINT8 data = m_paddle_x->read();
@ -50,7 +50,7 @@ CUSTOM_INPUT_MEMBER( sms_paddle_device::dir_pins_r )
}
CUSTOM_INPUT_MEMBER( sms_paddle_device::tr_pin_r )
READ_LINE_MEMBER( sms_paddle_device::tr_pin_r )
{
// The returned value is inverted due to IP_ACTIVE_LOW mapping.
return ~m_read_state;
@ -59,11 +59,11 @@ CUSTOM_INPUT_MEMBER( sms_paddle_device::tr_pin_r )
static INPUT_PORTS_START( sms_paddle )
PORT_START("CTRL_PORT")
PORT_BIT( 0x0f, IP_ACTIVE_LOW, IPT_SPECIAL ) PORT_CUSTOM_MEMBER(DEVICE_SELF, sms_paddle_device, dir_pins_r, nullptr) // Directional pins
PORT_BIT( 0x0f, IP_ACTIVE_LOW, IPT_SPECIAL ) PORT_CUSTOM_MEMBER(DEVICE_SELF, sms_paddle_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_BUTTON1 ) // TL
PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED ) // TH
PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_SPECIAL ) PORT_CUSTOM_MEMBER(DEVICE_SELF, sms_paddle_device, tr_pin_r, nullptr)
PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_SPECIAL ) PORT_READ_LINE_DEVICE_MEMBER( DEVICE_SELF, sms_paddle_device, tr_pin_r ) // TR
PORT_START("PADDLE_X") // Paddle knob
PORT_BIT( 0xff, 0x80, IPT_PADDLE) PORT_SENSITIVITY(40) PORT_KEYDELTA(20) PORT_CENTERDELTA(0) PORT_MINMAX(0,255)

View File

@ -33,8 +33,8 @@ public:
// optional information overrides
virtual ioport_constructor device_input_ports() const override;
DECLARE_CUSTOM_INPUT_MEMBER( dir_pins_r );
DECLARE_CUSTOM_INPUT_MEMBER( tr_pin_r );
DECLARE_CUSTOM_INPUT_MEMBER( rldu_pins_r ); // Right, Left, Down and Up lines.
DECLARE_READ_LINE_MEMBER( tr_pin_r );
protected:
// device-level overrides

View File

@ -82,21 +82,21 @@ void sms_sports_pad_device::device_timer(emu_timer &timer, device_timer_id id, i
}
CUSTOM_INPUT_MEMBER( sms_sports_pad_device::th_pin_r )
READ_LINE_MEMBER( sms_sports_pad_device::th_pin_r )
{
return m_last_data;
return m_th_pin_state;
}
INPUT_CHANGED_MEMBER( sms_sports_pad_device::th_pin_w )
WRITE_LINE_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;
m_th_pin_state = state;
}
CUSTOM_INPUT_MEMBER( sms_sports_pad_device::dir_pins_r )
CUSTOM_INPUT_MEMBER( sms_sports_pad_device::rldu_pins_r )
{
UINT8 data = 0;
@ -123,17 +123,17 @@ CUSTOM_INPUT_MEMBER( sms_sports_pad_device::dir_pins_r )
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, nullptr) // Directional pins
PORT_BIT( 0x0f, IP_ACTIVE_LOW, IPT_SPECIAL ) PORT_CUSTOM_MEMBER(DEVICE_SELF, sms_sports_pad_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_BUTTON1 ) // TL (Button 1)
PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_SPECIAL ) PORT_CUSTOM_MEMBER(DEVICE_SELF, sms_sports_pad_device, th_pin_r, nullptr)
PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_SPECIAL ) PORT_READ_LINE_DEVICE_MEMBER(DEVICE_SELF, sms_sports_pad_device, th_pin_r) // TH
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, nullptr)
PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_OUTPUT ) PORT_WRITE_LINE_DEVICE_MEMBER(DEVICE_SELF, sms_sports_pad_device, th_pin_w) // TH
PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED ) // TR (Button 2)
PORT_START("SPORTS_X") /* Sports Pad X axis */
@ -171,7 +171,7 @@ sms_sports_pad_device::sms_sports_pad_device(const machine_config &mconfig, cons
m_sports_x(*this, "SPORTS_X"),
m_sports_y(*this, "SPORTS_Y"),
m_read_state(0),
m_last_data(0),
m_th_pin_state(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)
@ -188,7 +188,7 @@ 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_th_pin_state));
save_item(NAME(m_x_axis_reset_value));
save_item(NAME(m_y_axis_reset_value));
}

View File

@ -33,9 +33,9 @@ public:
// optional information overrides
virtual ioport_constructor device_input_ports() const override;
DECLARE_CUSTOM_INPUT_MEMBER( dir_pins_r );
DECLARE_CUSTOM_INPUT_MEMBER( th_pin_r );
DECLARE_INPUT_CHANGED_MEMBER( th_pin_w );
DECLARE_CUSTOM_INPUT_MEMBER( rldu_pins_r ); // Right, Left, Down and Up lines.
DECLARE_READ_LINE_MEMBER( th_pin_r );
DECLARE_WRITE_LINE_MEMBER( th_pin_w );
protected:
// device-level overrides
@ -52,7 +52,7 @@ private:
required_ioport m_sports_y;
UINT8 m_read_state;
UINT8 m_last_data;
UINT8 m_th_pin_state;
UINT8 m_x_axis_reset_value;
UINT8 m_y_axis_reset_value;
const attotime m_interval;

View File

@ -22,6 +22,13 @@ Notes:
used by the US model, due to the missing TH line on Sega Mark III
controller ports.
A bug was discovered in the player 2 input handling code 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 "sportsjp.h"
@ -39,38 +46,24 @@ const device_type SMS_SPORTS_PAD_JP = &device_creator<sms_sports_pad_jp_device>;
DECLARE_CUSTOM_INPUT_MEMBER( sms_sports_pad_jp_device::dir_pins_r )
{
UINT8 data = 0;
switch (m_read_state)
{
case 0:
data = m_sports_jp_x->read() >> 4;
break;
case 1:
data = m_sports_jp_x->read();
break;
case 2:
data = m_sports_jp_y->read() >> 4;
break;
case 3:
data = m_sports_jp_y->read();
break;
}
// The returned value is inverted due to IP_ACTIVE_LOW mapping.
return ~(data & 0x0f);
}
// 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_SPECIAL ) PORT_CUSTOM_MEMBER(DEVICE_SELF, sms_sports_pad_jp_device, dir_pins_r, nullptr) // Directional pins
PORT_BIT( 0x0f, IP_ACTIVE_LOW, IPT_SPECIAL ) 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_BUTTON1 ) // TL (Button 1)
PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_SPECIAL ) 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_BUTTON2 ) // TR (Button 2)
PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_SPECIAL ) 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)
@ -103,9 +96,12 @@ sms_sports_pad_jp_device::sms_sports_pad_jp_device(const machine_config &mconfig
device_t(mconfig, SMS_SPORTS_PAD_JP, "Sega SMS Sports Pad JP", tag, owner, clock, "sms_sports_pad_jp", __FILE__),
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_read_state(0),
m_rldu_pins_state(0x0f),
m_tl_pin_state(1),
m_tr_pin_state(1),
m_interval(SPORTS_PAD_JP_INTERVAL)
{
}
@ -120,7 +116,9 @@ void sms_sports_pad_jp_device::device_start()
m_start_time = machine().time();
save_item(NAME(m_start_time));
save_item(NAME(m_read_state));
save_item(NAME(m_rldu_pins_state));
save_item(NAME(m_tl_pin_state));
save_item(NAME(m_tr_pin_state));
}
@ -130,41 +128,41 @@ void sms_sports_pad_jp_device::device_start()
UINT8 sms_sports_pad_jp_device::peripheral_r()
{
UINT8 data;
int num_intervals = (machine().time() - m_start_time).as_double() / m_interval.as_double();
m_read_state = num_intervals % 5;
data = m_sports_jp_in->read();
switch (m_read_state)
switch (num_intervals % 5)
{
case 0:
// X high nibble
data &= ~0x20; // TL 0
data &= ~0x80; // TR 0
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
data |= 0x20; // TL 1
data &= ~0x80; // TR 0
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
data &= ~0x20; // TL 0
data &= ~0x80; // TR 0
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
data |= 0x20; // TL 1
data &= ~0x80; // TR 0
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
data = (data & 0x20) >> 5 | (data & 0x80) >> 6 | 0xfc;
data |= 0x20; // TL 1
data |= 0x80; // TR 1
m_rldu_pins_state = m_sports_jp_bt->read();
m_tl_pin_state = 1;
m_tr_pin_state = 1;
break;
}
return data;
return m_sports_jp_in->read();
}

View File

@ -33,7 +33,9 @@ public:
// optional information overrides
virtual ioport_constructor device_input_ports() const override;
CUSTOM_INPUT_MEMBER( dir_pins_r );
DECLARE_CUSTOM_INPUT_MEMBER( rldu_pins_r ); // Right, Left, Down and Up lines.
DECLARE_READ_LINE_MEMBER( tl_pin_r );
DECLARE_READ_LINE_MEMBER( tr_pin_r );
protected:
// device-level overrides
@ -44,10 +46,13 @@ protected:
private:
required_ioport m_sports_jp_in;
required_ioport m_sports_jp_bt;
required_ioport m_sports_jp_x;
required_ioport m_sports_jp_y;
UINT8 m_read_state;
UINT8 m_rldu_pins_state;
UINT8 m_tl_pin_state;
UINT8 m_tr_pin_state;
attotime m_start_time;
const attotime m_interval;
};

View File

@ -60,7 +60,7 @@ General compatibility issues on real hardware (not emulation bugs):
Paddle controller;
- Few games of the ones with FM support need to detect the system region as
Japanese to play FM sound;
- The Light Phaser gun doesn't work with the Japanese SMS;
- The Light Phaser gun doesn't work with the Japanese SMS and Sega Mark III;
- There are reports about Light Phaser working on the second Korean SMS
version, and a Korean advert shows support on the first version (Gam*Boy I,
although based on Japanese SMS);
@ -337,10 +337,10 @@ ADDRESS_MAP_END
// The first Korean SMS version also seems to lack I/O port $3F. Games execute
// a region detection procedure that, through that port, sets the mode used by
// the TH bits of port $DD and tests their behavior. The region of the first SMS
// the TH bits of port $DD and tests their behaviour. The region of the first SMS
// version is detected as Japanese (opposite to the second version). However,
// as it supports Light Phaser games, it doesn't have the same behavior of the
// Japanese SMS. If it had the behavior of other SMS versions, the system
// as it supports Light Phaser games, it doesn't have the same behaviour of the
// Japanese SMS. If it had the behaviour of other SMS versions, the system
// region would be detected as Export, so it probably lacks the port.
static ADDRESS_MAP_START( sms1kr_io, AS_IO, 8, sms_state )
AM_IMPORT_FROM(sg1000m3_io)