From 249d8f752cd1e4fe12e7212d29138c6058f5c3ba Mon Sep 17 00:00:00 2001 From: Scott Stone Date: Mon, 14 Sep 2015 17:54:24 -0400 Subject: [PATCH] 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] --- src/devices/bus/sms_ctrl/sports.c | 106 ++++++++++++++++++++---------- src/devices/bus/sms_ctrl/sports.h | 7 +- src/devices/video/315_5124.c | 26 ++++---- src/devices/video/315_5124.h | 2 +- src/mess/drivers/sms.c | 9 +-- src/mess/machine/sms.c | 72 ++++++++++---------- 6 files changed, 134 insertions(+), 88 deletions(-) diff --git a/src/devices/bus/sms_ctrl/sports.c b/src/devices/bus/sms_ctrl/sports.c index f5bc15e170b..4050159beb1 100644 --- a/src/devices/bus/sms_ctrl/sports.c +++ b/src/devices/bus/sms_ctrl/sports.c @@ -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; - +// 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)); } diff --git a/src/devices/bus/sms_ctrl/sports.h b/src/devices/bus/sms_ctrl/sports.h index b0efde2abe8..f303b140a5b 100644 --- a/src/devices/bus/sms_ctrl/sports.h +++ b/src/devices/bus/sms_ctrl/sports.h @@ -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); }; diff --git a/src/devices/video/315_5124.c b/src/devices/video/315_5124.c index dac2027d126..27508e79e52 100644 --- a/src/devices/video/315_5124.c +++ b/src/devices/video/315_5124.c @@ -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) { diff --git a/src/devices/video/315_5124.h b/src/devices/video/315_5124.h index 409dc2e08a3..a28e61a5e5a 100644 --- a/src/devices/video/315_5124.h +++ b/src/devices/video/315_5124.h @@ -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 */ diff --git a/src/mess/drivers/sms.c b/src/mess/drivers/sms.c index 7caf7f2de33..f93674b584c 100644 --- a/src/mess/drivers/sms.c +++ b/src/mess/drivers/sms.c @@ -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 diff --git a/src/mess/machine/sms.c b/src/mess/machine/sms.c index 89e478c9ba2..32081091088 100644 --- a/src/mess/machine/sms.c +++ b/src/mess/machine/sms.c @@ -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; } }