(MESS) sms.c: Change hcount calculation to use screen timings and move it to the VDP. [Enik Land]

This commit is contained in:
Wilbert Pol 2013-05-14 20:56:54 +00:00
parent 34be6878ec
commit 335de9f804
5 changed files with 94 additions and 109 deletions

View File

@ -56,7 +56,7 @@ PAL frame timing
#define VINT_HPOS 24
#define VINT_FLAG_HPOS 23
#define HINT_HPOS 25
#define HINT_HPOS 26
#define VCOUNT_CHANGE_HPOS 23
#define SPROVR_HPOS 23
#define SPRCOL_BASEHPOS 59
@ -261,15 +261,22 @@ READ8_MEMBER( sega315_5124_device::vcount_read )
}
READ8_MEMBER( sega315_5124_device::hcount_latch_read )
READ8_MEMBER( sega315_5124_device::hcount_read )
{
return m_hcounter;
}
WRITE8_MEMBER( sega315_5124_device::hcount_latch_write )
void sega315_5124_device::hcount_latch_at_hpos( int hpos )
{
m_hcounter = data;
/* The emulation core returns a screen hpos that is one position ahead in comparison
with the expected VDP hclock value, if the same range is used (from 0 to width-1). */
int hclock = hpos - 1;
if (hclock < 0)
hclock += m_screen->width();
/* Calculate and write the new hcount. */
m_hcounter = ((hclock - 46) >> 1) & 0xff;
}
@ -440,7 +447,7 @@ void sega315_5124_device::process_line_timer()
m_tmpbitmap.fill(machine().pens[m_current_palette[BACKDROP_COLOR]], rec);
m_y1_bitmap.fill(1, rec);
select_sprites( vpos_limit, vpos - vpos_limit );
select_sprites( vpos - vpos_limit );
if ( m_draw_time > 0 )
{
m_draw_timer->adjust( m_screen->time_until_pos( vpos, m_draw_time ), vpos_limit );
@ -475,7 +482,7 @@ void sega315_5124_device::process_line_timer()
/* Draw middle of the border */
/* We need to do this through the regular drawing function so it will */
/* be included in the gamegear scaling functions */
select_sprites( vpos_limit + m_frame_timing[TOP_BORDER], vpos - (vpos_limit + m_frame_timing[TOP_BORDER]) );
select_sprites( vpos - (vpos_limit + m_frame_timing[TOP_BORDER]) );
draw_scanline( SEGA315_5124_LBORDER_START + SEGA315_5124_LBORDER_WIDTH, vpos_limit + m_frame_timing[TOP_BORDER], vpos - (vpos_limit + m_frame_timing[TOP_BORDER]) );
return;
}
@ -807,7 +814,7 @@ void sega315_5124_device::draw_scanline_mode4( int *line_buffer, int *priority_s
pixel_plot_x = (0 - (x_scroll & 0x07) + (tile_column << 3) + pixel_plot_x);
if (pixel_plot_x >= 0 && pixel_plot_x < 256)
{
// logerror("%x %x\n", pixel_plot_x + pixel_offset_x, pixel_plot_y);
//logerror("%x %x\n", pixel_plot_x, line);
line_buffer[pixel_plot_x] = m_current_palette[pen_selected];
priority_selected[pixel_plot_x] = priority_select | (pen_selected & 0x0f);
}
@ -816,7 +823,7 @@ void sega315_5124_device::draw_scanline_mode4( int *line_buffer, int *priority_s
}
void sega315_5124_device::select_sprites( int pixel_plot_y, int line )
void sega315_5124_device::select_sprites( int line )
{
int sprite_index = 0;
int max_sprites = 0;
@ -901,10 +908,10 @@ void sega315_5124_device::select_sprites( int pixel_plot_y, int line )
}
void sega315_5124_device::draw_sprites_mode4( int *line_buffer, int *priority_selected, int pixel_plot_y, int line )
void sega315_5124_device::draw_sprites_mode4( int *line_buffer, int *priority_selected, int line )
{
bool sprite_col_occurred = false;
int sprite_col_x = 1000;
int sprite_col_x = m_screen->width();
/* Draw sprite layer */
@ -1059,10 +1066,10 @@ void sega315_5124_device::draw_sprites_mode4( int *line_buffer, int *priority_se
}
void sega315_5124_device::draw_sprites_tms9918_mode( int *line_buffer, int pixel_plot_y, int line )
void sega315_5124_device::draw_sprites_tms9918_mode( int *line_buffer, int line )
{
bool sprite_col_occurred = false;
int sprite_col_x = 1000;
int sprite_col_x = m_screen->width();
UINT16 sprite_pattern_base = ((m_reg[0x06] & 0x07) << 11);
/* Draw sprite layer */
@ -1355,7 +1362,7 @@ void sega315_5124_device::draw_scanline( int pixel_offset_x, int pixel_plot_y, i
}
if ( line >= 0 || ( line >= -13 && m_y_pixels == 192 ) )
{
draw_sprites_tms9918_mode( blitline_buffer, pixel_plot_y, line );
draw_sprites_tms9918_mode( blitline_buffer, line );
}
break;
@ -1367,7 +1374,7 @@ void sega315_5124_device::draw_scanline( int pixel_offset_x, int pixel_plot_y, i
}
if ( line >= 0 || ( line >= -13 && m_y_pixels == 192 ) )
{
draw_sprites_tms9918_mode( blitline_buffer, pixel_plot_y, line );
draw_sprites_tms9918_mode( blitline_buffer, line );
}
break;
@ -1380,7 +1387,7 @@ void sega315_5124_device::draw_scanline( int pixel_offset_x, int pixel_plot_y, i
}
if ( line >= 0 || ( line >= -13 && m_y_pixels == 192 ) )
{
draw_sprites_mode4( blitline_buffer, priority_selected, pixel_plot_y, line );
draw_sprites_mode4( blitline_buffer, priority_selected, line );
if ( line >= 0 )
{
/* Fill column 0 with overscan color from m_reg[0x07] */
@ -1449,7 +1456,7 @@ void sega315_5378_device::draw_scanline( int pixel_offset_x, int pixel_plot_y, i
}
if ( line >= 0 || ( line >= -13 && m_y_pixels == 192 ) )
{
draw_sprites_tms9918_mode( blitline_buffer, pixel_plot_y, line );
draw_sprites_tms9918_mode( blitline_buffer, line );
}
break;
@ -1460,7 +1467,7 @@ void sega315_5378_device::draw_scanline( int pixel_offset_x, int pixel_plot_y, i
}
if ( line >= 0 || ( line >= -13 && m_y_pixels == 192 ) )
{
draw_sprites_tms9918_mode( blitline_buffer, pixel_plot_y, line );
draw_sprites_tms9918_mode( blitline_buffer, line );
}
break;
@ -1473,7 +1480,7 @@ void sega315_5378_device::draw_scanline( int pixel_offset_x, int pixel_plot_y, i
}
if ( line >= 0 || ( line >= -13 && m_y_pixels == 192 ) )
{
draw_sprites_mode4( blitline_buffer, priority_selected, pixel_plot_y, line );
draw_sprites_mode4( blitline_buffer, priority_selected, line );
if ( line >= 0 )
{
/* Fill column 0 with overscan color from m_reg[0x07] */

View File

@ -80,8 +80,10 @@ public:
DECLARE_READ8_MEMBER( register_read );
DECLARE_WRITE8_MEMBER( register_write );
DECLARE_READ8_MEMBER( vcount_read );
DECLARE_READ8_MEMBER( hcount_latch_read );
DECLARE_WRITE8_MEMBER( hcount_latch_write );
DECLARE_READ8_MEMBER( hcount_read );
void hcount_latch() { hcount_latch_at_hpos( m_screen->hpos() ); };
void hcount_latch_at_hpos( int hpos );
bitmap_rgb32 &get_bitmap() { return m_tmpbitmap; };
bitmap_ind8 &get_y1_bitmap() { return m_y1_bitmap; };
@ -98,11 +100,11 @@ protected:
virtual UINT16 get_name_table_address();
void process_line_timer();
void draw_scanline_mode4( int *line_buffer, int *priority_selected, int line );
void draw_sprites_mode4( int *line_buffer, int *priority_selected, int pixel_plot_y, int line );
void draw_sprites_tms9918_mode( int *line_buffer, int pixel_plot_y, int line );
void draw_sprites_mode4( int *line_buffer, int *priority_selected, int line );
void draw_sprites_tms9918_mode( int *line_buffer, int line );
void draw_scanline_mode2( int *line_buffer, int line );
void draw_scanline_mode0( int *line_buffer, int line );
void select_sprites( int pixel_plot_y, int line );
void select_sprites( int line );
void check_pending_flags( int hpos );
// device-level overrides

View File

@ -243,7 +243,7 @@ static INPUT_PORTS_START( sms )
PORT_BIT( 0xff, 0x80, IPT_LIGHTGUN_X ) PORT_CROSSHAIR( X, 1.0, 0.0, 0 ) PORT_SENSITIVITY(50) PORT_KEYDELTA(15) PORT_PLAYER(2) PORT_CHANGED_MEMBER(DEVICE_SELF, sms_state, lgun2_changed, NULL) PORT_CONDITION("CTRLSEL", 0xf0, EQUALS, 0x10)
PORT_START("LPHASER3") /* Light phaser Y - player 2 */
PORT_BIT( 0xff, 0x80, IPT_LIGHTGUN_Y ) PORT_CROSSHAIR( Y, 1.0, 0.0, 0 ) PORT_SENSITIVITY(50) PORT_KEYDELTA(25) PORT_PLAYER(2) PORT_CHANGED_MEMBER(DEVICE_SELF, sms_state, lgun2_changed, NULL) PORT_CONDITION("CTRLSEL", 0xf0, EQUALS, 0x10)
PORT_BIT( 0xff, 0x80, IPT_LIGHTGUN_Y ) PORT_CROSSHAIR( Y, 1.0, 0.0, 0 ) PORT_SENSITIVITY(50) PORT_KEYDELTA(15) PORT_PLAYER(2) PORT_CHANGED_MEMBER(DEVICE_SELF, sms_state, lgun2_changed, NULL) PORT_CONDITION("CTRLSEL", 0xf0, EQUALS, 0x10)
PORT_START("RFU") /* Rapid Fire Unit */
PORT_CONFNAME( 0x03, 0x00, "Rapid Fire Unit - Player 1" )

View File

@ -216,14 +216,12 @@ protected:
required_shared_ptr<UINT8> m_mainram;
void setup_rom();
void vdp_hcount_lphaser(int hpos);
void lphaser_hcount_latch(int hpos);
void lphaser1_sensor_check();
void lphaser2_sensor_check();
UINT16 screen_hpos_nonscaled(int scaled_hpos);
UINT16 screen_vpos_nonscaled(int scaled_vpos);
int lgun_bright_aim_area(emu_timer *timer, int lgun_x, int lgun_y);
void sms_vdp_hcount_latch(address_space &space);
UINT8 sms_vdp_hcount();
void setup_cart_banks();
void setup_banks();
void sms_get_inputs(address_space &space);

View File

@ -279,18 +279,6 @@ WRITE8_MEMBER(sms_state::sms_input_write)
}
}
/* FIXME: this function is a hack for Light Phaser emulation. Theoretically
sms_vdp_hcount_latch() should be used instead, but it returns incorrect
position for unknown reason (timing?) */
void sms_state::vdp_hcount_lphaser( int hpos )
{
int hpos_tmp = hpos + m_lphaser_x_offs;
UINT8 tmp = ((hpos_tmp - 46) >> 1) & 0xff;
//printf ("sms_vdp_hcount_lphaser: hpos %3d hpos_tmp %3d => hcount %2X\n", hpos, hpos_tmp, tmp);
m_vdp->hcount_latch_write(*m_space, 0, tmp);
}
/*
Light Phaser (light gun) emulation notes:
@ -323,12 +311,13 @@ int sms_state::lgun_bright_aim_area( emu_timer *timer, int lgun_x, int lgun_y )
int dx, dy;
int result = 0;
int pos_changed = 0;
double dx_circ;
double dx_radius;
while (1)
{
/* If beam's y isn't at a line where the aim area is, change it
the next line it enters that area. */
dy = abs(beam_y - lgun_y);
if (dy > LGUN_RADIUS || beam_y < visarea.min_y || beam_y > visarea.max_y)
{
beam_y = lgun_y - LGUN_RADIUS;
@ -337,21 +326,38 @@ int sms_state::lgun_bright_aim_area( emu_timer *timer, int lgun_x, int lgun_y )
dy = abs(beam_y - lgun_y);
pos_changed = 1;
}
/* step 1: r^2 = dx^2 + dy^2 */
/* step 2: dx^2 = r^2 - dy^2 */
/* step 3: dx = sqrt(r^2 - dy^2) */
dx_circ = ceil((float) sqrt((float) (r_x_r - (dy * dy))));
dx = abs(beam_x - lgun_x);
if (dx > dx_circ || beam_x < visarea.min_x || beam_x > visarea.max_x)
/* Caculate distance in x of the radius, relative to beam's y distance.
First try some shortcuts. */
switch (dy)
{
case LGUN_RADIUS:
dx_radius = 0;
break;
case 0:
dx_radius = LGUN_RADIUS;
break;
default:
/* step 1: r^2 = dx^2 + dy^2 */
/* step 2: dx^2 = r^2 - dy^2 */
/* step 3: dx = sqrt(r^2 - dy^2) */
dx_radius = ceil((float) sqrt((float) (r_x_r - (dy * dy))));
}
/* If beam's x isn't in the circular aim area, change it
to the next point it enters that area. */
dx = abs(beam_x - lgun_x);
if (dx > dx_radius || beam_x < visarea.min_x || beam_x > visarea.max_x)
{
/* If beam's x has passed the aim area, advance to
next line and recheck y/x coordinates. */
if (beam_x > lgun_x)
{
beam_x = 0;
beam_y++;
continue;
}
beam_x = lgun_x - dx_circ;
beam_x = lgun_x - dx_radius;
if (beam_x < visarea.min_x)
beam_x = visarea.min_x;
pos_changed = 1;
@ -389,45 +395,19 @@ int sms_state::lgun_bright_aim_area( emu_timer *timer, int lgun_x, int lgun_y )
return result;
}
UINT8 sms_state::sms_vdp_hcount()
void sms_state::lphaser_hcount_latch( int hpos )
{
UINT64 cycles_per_line;
attotime line_remaining_time;
UINT64 line_remaining_cycles;
UINT64 line_elapsed_cycles;
/* Calculate amount of CPU cycles according to screen references.
If some day the screen become always synced to the CPU, in the
proportion of their speeds, this may be replaced by the screen
hpos position only and the function be moved to the VDP file. */
cycles_per_line = m_main_cpu->attotime_to_clocks(m_main_scr->scan_period());
line_remaining_time = m_main_scr->time_until_pos(m_main_scr->vpos(), m_main_scr->width());
line_remaining_cycles = m_main_cpu->attotime_to_clocks(line_remaining_time) % cycles_per_line;
line_elapsed_cycles = cycles_per_line - line_remaining_cycles;
/* HCount equation, being hpos = VDP hclocks: "(hpos - 47) / 2"
Screen hpos based on CPU cycles: "cycles * width / cycles per line"
Do both in same line for one-step rounding only (required). */
return ((line_elapsed_cycles * m_main_scr->width() / cycles_per_line) - 47) / 2;
/* Alternative hcount equation, restricted to the SMS clock:
"(590 - (line_remaining_cycles * 3)) / 4"
Posted by Flubba on SMSPower forum. */
}
void sms_state::sms_vdp_hcount_latch( address_space &space )
{
UINT8 value = sms_vdp_hcount();
m_vdp->hcount_latch_write(space, 0, value);
/* A delay seems to occur when the Light Phaser latches the
VDP hcount, then an offset is added here to the hpos. */
m_vdp->hcount_latch_at_hpos(hpos + m_lphaser_x_offs);
}
UINT16 sms_state::screen_hpos_nonscaled(int scaled_hpos)
{
const rectangle &visarea = m_main_scr->visible_area();
int offset_x = (scaled_hpos * visarea.width()) / 255;
int offset_x = (scaled_hpos * (visarea.max_x - visarea.min_x)) / 255;
return visarea.min_x + offset_x;
}
@ -450,7 +430,7 @@ void sms_state::lphaser1_sensor_check()
if (m_lphaser_1_latch == 0)
{
m_lphaser_1_latch = 1;
vdp_hcount_lphaser(x);
lphaser_hcount_latch(x);
}
}
}
@ -465,7 +445,7 @@ void sms_state::lphaser2_sensor_check()
if (m_lphaser_2_latch == 0)
{
m_lphaser_2_latch = 1;
vdp_hcount_lphaser(x);
lphaser_hcount_latch(x);
}
}
}
@ -573,13 +553,13 @@ void sms_state::sms_get_inputs( address_space &space )
case 0x01: /* Light Phaser */
data = (ioport("CTRLIPT")->read() & 0x01) << 4;
if (!(data & 0x10))
{
if (ioport("RFU")->read() & 0x01)
data |= m_rapid_fire_state_1 & 0x10;
}
/* just consider the button (trigger) bit */
/* Check Rapid Fire setting for Trigger */
if (!(data & 0x10) && (ioport("RFU")->read() & 0x01))
data |= m_rapid_fire_state_1 & 0x10;
/* just consider the trigger (button) bit */
data |= ~0x10;
m_input_port0 = (m_input_port0 & 0xc0) | (data & 0x3f);
break;
@ -635,13 +615,13 @@ void sms_state::sms_get_inputs( address_space &space )
case 0x10: /* Light Phaser */
data = (ioport("CTRLIPT")->read() & 0x10) >> 2;
if (!(data & 0x04))
{
if (ioport("RFU")->read() & 0x04)
data |= m_rapid_fire_state_2 & 0x04;
}
/* just consider the button (trigger) bit */
/* Check Rapid Fire setting for Trigger */
if (!(data & 0x04) && (ioport("RFU")->read() & 0x04))
data |= m_rapid_fire_state_2 & 0x04;
/* just consider the trigger (button) bit */
data |= ~0x04;
m_input_port1 = (m_input_port1 & 0xf0) | (data & 0x0f);
break;
@ -708,14 +688,14 @@ READ8_MEMBER(sms_state::sms_fm_detect_r)
WRITE8_MEMBER(sms_state::sms_io_control_w)
{
bool hcount_latch = false;
bool latch_hcount = false;
if (data & 0x08)
{
/* check if TH pin level is high (1) and was low last time */
if (data & 0x80 && !(m_ctrl_reg & 0x80))
{
hcount_latch = true;
latch_hcount = true;
}
sms_input_write(space, 0, (data & 0x20) >> 5);
}
@ -724,14 +704,14 @@ WRITE8_MEMBER(sms_state::sms_io_control_w)
{
if (data & 0x20 && !(m_ctrl_reg & 0x20))
{
hcount_latch = true;
latch_hcount = true;
}
sms_input_write(space, 1, (data & 0x80) >> 7);
}
if (hcount_latch)
if (latch_hcount)
{
sms_vdp_hcount_latch(space);
m_vdp->hcount_latch();
}
m_ctrl_reg = data;
@ -741,7 +721,7 @@ WRITE8_MEMBER(sms_state::sms_io_control_w)
READ8_MEMBER(sms_state::sms_count_r)
{
if (offset & 0x01)
return m_vdp->hcount_latch_read(*m_space, offset);
return m_vdp->hcount_read(*m_space, offset);
else
return m_vdp->vcount_read(*m_space, offset);
}
@ -1531,22 +1511,22 @@ int sms_state::detect_lphaser_xoffset( UINT8 *rom )
if (!(m_bios_port & IO_CARTRIDGE) && m_cartridge[m_current_cartridge].size >= 0x8000)
{
if (!memcmp(&rom[0x7ff0], signatures[0], 16) || !memcmp(&rom[0x7ff0], signatures[1], 16))
return 40;
return 41;
if (!memcmp(&rom[0x7ff0], signatures[2], 16))
return 49;
return 50;
if (!memcmp(&rom[0x7ff0], signatures[3], 16))
return 47;
return 48;
if (!memcmp(&rom[0x7ff0], signatures[4], 16))
return 44;
return 45;
if (!memcmp(&rom[0x7ff0], signatures[5], 16))
return 53;
return 54;
}
return 50;
return 51;
}
@ -2260,9 +2240,7 @@ UINT32 sms_state::screen_update_sms(screen_device &screen, bitmap_rgb32 &bitmap,
VIDEO_START_MEMBER(sms_state,gamegear)
{
screen_device *screen = machine().first_screen();
screen->register_screen_bitmap(m_prev_bitmap);
m_main_scr->register_screen_bitmap(m_prev_bitmap);
save_item(NAME(m_prev_bitmap));
}