315-5124.c: Minor changes and fix a regression that in theory could affect zoomed sprites in TMS9918 modes. [Enik Land]

gamegear.c / sms.c:  Improve GG-SMS scaling code a little and update the Todo list.  Fixed MT#05872 regarding incorrect behavior of the Sports Pad (US model) emulation.  [Enik Land]
This commit is contained in:
Scott Stone 2015-09-14 17:54:24 -04:00
parent 54f43da62d
commit 249d8f752c
6 changed files with 134 additions and 88 deletions

View File

@ -6,6 +6,22 @@
**********************************************************************/
// 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"
@ -16,32 +32,33 @@
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)
CUSTOM_INPUT_MEMBER( sms_sports_pad_device::dir_pins_r )
void sms_sports_pad_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
UINT8 data = 0;
switch (m_read_state)
switch (id)
{
case 0:
data = m_sports_x->read() >> 4;
break;
case 1:
data = m_sports_x->read();
break;
case 2:
data = m_sports_y->read() >> 4;
break;
case 3:
data = m_sports_y->read();
break;
}
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;
}
// The returned value is inverted due to IP_ACTIVE_LOW mapping.
return ~(data & 0x0f);
break;
default:
assert_always(FALSE, "Unknown id in sms_sports_pad_device::device_timer");
}
}
@ -53,21 +70,37 @@ CUSTOM_INPUT_MEMBER( sms_sports_pad_device::th_pin_r )
INPUT_CHANGED_MEMBER( sms_sports_pad_device::th_pin_w )
{
attotime cur_time = machine().time();
if (cur_time - m_last_time > m_interval)
{
m_read_state = 0;
}
else
{
m_read_state = (m_read_state + 1) & 3;
}
m_last_time = cur_time;
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
@ -84,10 +117,10 @@ static INPUT_PORTS_START( sms_sports_pad )
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_RESET PORT_REVERSE
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_RESET PORT_REVERSE
PORT_BIT( 0xff, 0x00, IPT_TRACKBALL_Y ) PORT_SENSITIVITY(50) PORT_KEYDELTA(40) PORT_REVERSE
INPUT_PORTS_END
@ -119,6 +152,8 @@ sms_sports_pad_device::sms_sports_pad_device(const machine_config &mconfig, cons
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)
{
}
@ -130,11 +165,12 @@ sms_sports_pad_device::sms_sports_pad_device(const machine_config &mconfig, cons
void sms_sports_pad_device::device_start()
{
m_last_time = machine().time();
m_sportspad_timer = timer_alloc(TIMER_SPORTSPAD);
save_item(NAME(m_read_state));
save_item(NAME(m_last_data));
save_item(NAME(m_last_time));
save_item(NAME(m_x_axis_reset_value));
save_item(NAME(m_y_axis_reset_value));
}

View File

@ -53,8 +53,13 @@ private:
UINT8 m_read_state;
UINT8 m_last_data;
UINT8 m_x_axis_reset_value;
UINT8 m_y_axis_reset_value;
const attotime m_interval;
attotime m_last_time;
emu_timer *m_sportspad_timer;
static const device_timer_id TIMER_SPORTSPAD = 0;
void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
};

View File

@ -36,6 +36,12 @@ A scanline contains the following sections:
- right blanking 8 87-8B
- horizontal sync 17 8B-93 => HSYNC low
Although the processing done for a section happens when HCount is in the
specified range (e.g. 00-7F for active display), probably there is a delay
until its signal is shown on screen, as happens on the TMS9918 chip
according to this timing diagram:
http://www.smspower.org/Development/TMS9918MasterTimingDiagram
NTSC frame timing
256x192 256x224 256x240 (doesn't work on real hardware)
@ -911,7 +917,7 @@ void sega315_5124_device::select_sprites( int line )
if (sprite_y >= 240)
{
sprite_y -= 256;
sprite_y -= 256; /* wrap from top if y position is >= 240 */
}
if (m_sprite_zoom > 1)
@ -927,15 +933,9 @@ void sega315_5124_device::select_sprites( int line )
int sprite_tile_selected = space().read_byte( m_sprite_base + sprite_index + 2 );
UINT8 flags = space().read_byte( m_sprite_base + sprite_index + 3 );
if (flags & 0x80)
sprite_x -= 32;
int sprite_line = parse_line - sprite_y;
if (m_reg[0x01] & 0x01)
sprite_line >>= 1;
if (m_reg[0x01] & 0x02)
if (m_sprite_height == 16)
{
sprite_tile_selected &= 0xfc;
@ -971,7 +971,7 @@ void sega315_5124_device::select_sprites( int line )
if (sprite_y >= 240)
{
sprite_y -= 256; /* wrap from top if y position is > 240 */
sprite_y -= 256; /* wrap from top if y position is >= 240 */
}
if (m_sprite_zoom > 1)
@ -996,7 +996,7 @@ void sega315_5124_device::select_sprites( int line )
sprite_tile_selected += 256; /* pattern table select */
}
if (m_reg[0x01] & 0x02)
if (m_sprite_height == 16)
{
sprite_tile_selected &= 0x01fe; /* force even index */
}
@ -1148,11 +1148,13 @@ void sega315_5124_device::draw_sprites_tms9918_mode( int *line_buffer, int line
for (int sprite_buffer_index = m_sprite_count - 1; sprite_buffer_index >= 0; sprite_buffer_index--)
{
int sprite_x = m_sprite_x[sprite_buffer_index];
int sprite_tile_selected = m_sprite_tile_selected[sprite_buffer_index];
UINT16 sprite_pattern_line = m_sprite_pattern_line[sprite_buffer_index];
UINT8 flags = m_sprite_flags[sprite_buffer_index];
int pen_selected = m_palette_offset + ( flags & 0x0f );
int sprite_tile_selected = m_sprite_tile_selected[sprite_buffer_index];
UINT16 sprite_pattern_line = m_sprite_pattern_line[sprite_buffer_index];
if (flags & 0x80)
sprite_x -= 32;
for (int height = 8; height <= m_sprite_height; height += 8)
{

View File

@ -131,7 +131,7 @@ protected:
int m_pending_reg_write;
int m_pending_sprcol_x;
UINT8 m_buffer;
bool m_sega315_5124_compatibility_mode; /* Shrunk SMS screen on GG lcd mode flag */
bool m_sega315_5124_compatibility_mode; /* when true, GG VDP behaves as SMS VDP */
int m_irq_state; /* The status of the IRQ line of the VDP */
int m_vdp_mode; /* Current mode of the VDP: 0,1,2,3,4 */
int m_y_pixels; /* 192, 224, 240 */

View File

@ -17,12 +17,13 @@
- SIO interface for Game Gear (needs netplay, I guess)
- Support for other DE-9 compatible controllers, like the Mega Drive 6-Button
that has homebrew software support
- Rapid button of Japanese Master System
- Verify if disabling of the SN76489 PSG chip is possible on sms1krfm console
- Keyboard support for Sega Mark III (sg1000m3 driver)
- Sega SK-1100 keyboard support for Sega Mark III (sg1000m3 driver)
- Link between two Mark III's through keyboard, supported by F-16 Fighting Falcon
- Mark III expansion slot, used by keyboard and FM module
- Mark III expansion slot, used by keyboard and FM Sound Unit
- Verify if the SN76489 chip can be disabled on sg1000m3 and sms1krfm consoles
- Rapid button of smsj, sms1krfm and sms1kr consoles
- Software compatibility flags, by region and/or BIOS
- Samsung modem for Gam*Boy Securities Information Service System
- Sega Demo Unit II (kiosk expansion device)
- SMS 8 slot game changer (kiosk expansion device)
- SMS Disk System (floppy disk drive expansion device) - unreleased

View File

@ -1352,18 +1352,20 @@ void sms_state::screen_gg_sms_mode_scaling(screen_device &screen, bitmap_rgb32 &
const int sms_min_y = visarea_ycenter - sms_offset_min_y;
int sms_y = sms_min_y;
int plot_y_group = plot_y_first_group;
int y_min_i = plot_min_y - plot_y_first_group;
/* Auxiliary variable for vertical scaling */
int sms_y2 = sms_y - 1;
for (int plot_y = plot_min_y; plot_y <= plot_max_y;)
for (int plot_y_group = plot_y_first_group; plot_y_group <= plot_max_y; plot_y_group += 2)
{
for (int i = (plot_y - plot_y_group); i <= MIN(1, plot_max_y - plot_y_group); i++)
const int y_max_i = MIN(1, plot_max_y - plot_y_group);
for (int y_i = y_min_i; y_i <= y_max_i; y_i++)
{
/* Include additional lines that have influence over what appears on the LCD */
const int sms_min_y2 = sms_y + i - 1;
const int sms_max_y2 = sms_y + i + 2;
const int sms_min_y2 = sms_y + y_i - 1;
const int sms_max_y2 = sms_y + y_i + 2;
/* Process lines, but skip those already processed before */
for (sms_y2 = MAX(sms_min_y2, sms_y2); sms_y2 <= sms_max_y2; sms_y2++)
@ -1375,39 +1377,37 @@ void sms_state::screen_gg_sms_mode_scaling(screen_device &screen, bitmap_rgb32 &
UINT32 *vdp_buffer = &vdp_bitmap.pix32(sms_y2);
int sms_x = sms_min_x;
int plot_x_group = plot_x_first_group;
int x_min_i = plot_min_x - plot_x_first_group;
/* Do horizontal scaling */
for (int plot_x = plot_min_x; plot_x <= plot_max_x;)
for (int plot_x_group = plot_x_first_group; plot_x_group <= plot_max_x; plot_x_group += 2)
{
for (int j = (plot_x - plot_x_group); j <= MIN(1, plot_max_x - plot_x_group); j++)
{
if (sms_x + j >= vdp_bitmap.cliprect().min_x && sms_x + j + 1 <= vdp_bitmap.cliprect().max_x)
{
int combined;
const int x_max_i = MIN(1, plot_max_x - plot_x_group);
switch (j)
for (int x_i = x_min_i; x_i <= x_max_i; x_i++)
{
int combined = 0;
if (sms_x + x_i >= vdp_bitmap.cliprect().min_x && sms_x + x_i + 1 <= vdp_bitmap.cliprect().max_x)
{
switch (x_i)
{
case 0:
/* Take red and green from first pixel, and blue from second pixel */
combined = (vdp_buffer[sms_x] & 0x00ffff00) | (vdp_buffer[sms_x + 1] & 0x000000ff);
combineline_buffer[plot_x] = combined;
break;
case 1:
/* Take red from second pixel, and green and blue from third pixel */
combined = (vdp_buffer[sms_x + 1] & 0x00ff0000) | (vdp_buffer[sms_x + 2] & 0x0000ffff);
combineline_buffer[plot_x + 1] = combined;
break;
}
}
else
{
combineline_buffer[plot_x + j] = 0;
}
combineline_buffer[plot_x_group + x_i] = combined;
}
sms_x += 3;
plot_x += 2 - (plot_x - plot_x_group);
plot_x_group = plot_x;
x_min_i = 0;
}
}
else
@ -1432,29 +1432,31 @@ void sms_state::screen_gg_sms_mode_scaling(screen_device &screen, bitmap_rgb32 &
int *line1, *line2, *line3, *line4;
/* Setup our source lines */
line1 = m_line_buffer + ((sms_y + i - 1) & 0x03) * 160;
line2 = m_line_buffer + ((sms_y + i - 0) & 0x03) * 160;
line3 = m_line_buffer + ((sms_y + i + 1) & 0x03) * 160;
line4 = m_line_buffer + ((sms_y + i + 2) & 0x03) * 160;
line1 = m_line_buffer + ((sms_y + y_i - 1) & 0x03) * 160;
line2 = m_line_buffer + ((sms_y + y_i - 0) & 0x03) * 160;
line3 = m_line_buffer + ((sms_y + y_i + 1) & 0x03) * 160;
line4 = m_line_buffer + ((sms_y + y_i + 2) & 0x03) * 160;
UINT32 *p_bitmap = &bitmap.pix32(visarea.min_y + plot_y + i, visarea.min_x);
UINT32 *p_bitmap = &bitmap.pix32(visarea.min_y + plot_y_group + y_i, visarea.min_x);
for (int plot_x = plot_min_x; plot_x <= plot_max_x; plot_x++)
{
rgb_t c1 = line1[plot_x];
rgb_t c2 = line2[plot_x];
rgb_t c3 = line3[plot_x];
rgb_t c4 = line4[plot_x];
rgb_t c1 = line1[plot_x];
rgb_t c2 = line2[plot_x];
rgb_t c3 = line3[plot_x];
rgb_t c4 = line4[plot_x];
p_bitmap[plot_x] =
rgb_t( ( c1.r() / 6 + c2.r() / 3 + c3.r() / 3 + c4.r() / 6 ),
( c1.g() / 6 + c2.g() / 3 + c3.g() / 3 + c4.g() / 6 ),
( c1.b() / 6 + c2.b() / 3 + c3.b() / 3 + c4.b() / 6 ) );
rgb_t(
( c1.r() / 6 + c2.r() / 3 + c3.r() / 3 + c4.r() / 6 ),
( c1.g() / 6 + c2.g() / 3 + c3.g() / 3 + c4.g() / 6 ),
( c1.b() / 6 + c2.b() / 3 + c3.b() / 3 + c4.b() / 6 )
);
}
}
}
sms_y += 3;
plot_y += 2 - (plot_y - plot_y_group);
plot_y_group = plot_y;
y_min_i = 0;
}
}