not worth mention: snes.c: added more comments to the video code

This commit is contained in:
Fabio Priuli 2010-03-25 13:23:28 +00:00
parent a91446eaca
commit 0938457df4
2 changed files with 298 additions and 202 deletions

View File

@ -301,11 +301,11 @@ static void snes_dynamic_res_change( running_machine *machine )
attoseconds_t refresh; attoseconds_t refresh;
visarea.min_x = visarea.min_y = 0; visarea.min_x = visarea.min_y = 0;
visarea.max_y = snes_ppu.beam.last_visible_line*snes_ppu.interlace - 1; visarea.max_y = snes_ppu.beam.last_visible_line * snes_ppu.interlace - 1;
visarea.max_x = (SNES_SCR_WIDTH * 2) - 1; visarea.max_x = (SNES_SCR_WIDTH * 2) - 1;
// fixme: should compensate for SNES_DBG_video // fixme: should compensate for SNES_DBG_video
if (snes_ppu.mode == 5 || snes_ppu.mode == 6 ) if (snes_ppu.mode == 5 || snes_ppu.mode == 6 || snes_ppu.pseudo_hires)
state->htmult = 2; state->htmult = 2;
else else
state->htmult = 1; state->htmult = 1;
@ -317,9 +317,9 @@ static void snes_dynamic_res_change( running_machine *machine )
refresh = HZ_TO_ATTOSECONDS(DOTCLK_PAL) * SNES_HTOTAL * SNES_VTOTAL_PAL; refresh = HZ_TO_ATTOSECONDS(DOTCLK_PAL) * SNES_HTOTAL * SNES_VTOTAL_PAL;
if ((snes_ram[STAT78] & 0x10) == SNES_NTSC) if ((snes_ram[STAT78] & 0x10) == SNES_NTSC)
video_screen_configure(machine->primary_screen, SNES_HTOTAL*2, SNES_VTOTAL_NTSC*snes_ppu.interlace, &visarea, refresh); video_screen_configure(machine->primary_screen, SNES_HTOTAL * 2, SNES_VTOTAL_NTSC * snes_ppu.interlace, &visarea, refresh);
else else
video_screen_configure(machine->primary_screen, SNES_HTOTAL*2, SNES_VTOTAL_PAL*snes_ppu.interlace, &visarea, refresh); video_screen_configure(machine->primary_screen, SNES_HTOTAL * 2, SNES_VTOTAL_PAL * snes_ppu.interlace, &visarea, refresh);
} }
static READ8_HANDLER( snes_open_bus_r ) static READ8_HANDLER( snes_open_bus_r )

View File

@ -132,126 +132,16 @@ enum
SNES_COLOR_DEPTH_8BPP SNES_COLOR_DEPTH_8BPP
}; };
/***************************************** /**********************************************************************************
* snes_draw_blend() * TODO: the draw_tile routines below should be merged/reworked to reduce everything
* * to a single routine with the additional parameters: OAM/BG (to decide if checking
* Routine for additive/subtractive blending * against priority or not) and tile length (16 for hires BG, 8 otherwise)
* between the main and sub screens. **********************************************************************************/
*****************************************/
INLINE void snes_draw_blend( UINT16 offset, UINT16 *colour, UINT8 prevent_color_math, UINT8 black_pen_clip, int switch_screens )
{
#ifdef SNES_LAYER_DEBUG
if (debug_options.colormath_disabled)
return;
#endif /* SNES_LAYER_DEBUG */
/* when color math is applied to subscreen pixels, the blending depends on the blending used by the previous mainscreen
pixel, except for subscreen pixel 0 which has no previous mainscreen pixel, see comments in snes_refresh_scanline */
if (switch_screens && offset > 0)
offset -= 1;
if ((black_pen_clip == SNES_CLIP_ALWAYS) ||
(black_pen_clip == SNES_CLIP_IN && snes_ppu.clipmasks[SNES_COLOR][offset]) ||
(black_pen_clip == SNES_CLIP_OUT && !snes_ppu.clipmasks[SNES_COLOR][offset]))
*colour = 0; //clip to black before color math
if (prevent_color_math == SNES_CLIP_ALWAYS) // blending mode 3 == always OFF
return;
if ((prevent_color_math == SNES_CLIP_NEVER) ||
(prevent_color_math == SNES_CLIP_IN && !snes_ppu.clipmasks[SNES_COLOR][offset]) ||
(prevent_color_math == SNES_CLIP_OUT && snes_ppu.clipmasks[SNES_COLOR][offset]))
{
UINT16 r, g, b;
struct SCANLINE *subscreen;
int clip_max = 0; // if add then clip to 0x1f, if sub then clip to 0
#ifdef SNES_LAYER_DEBUG
/* Toggle drawing of SNES_SUBSCREEN or SNES_MAINSCREEN */
if (debug_options.draw_subscreen)
{
subscreen = switch_screens ? &scanlines[SNES_SUBSCREEN] : &scanlines[SNES_MAINSCREEN];
}
else
#endif /* SNES_LAYER_DEBUG */
{
subscreen = switch_screens ? &scanlines[SNES_MAINSCREEN] : &scanlines[SNES_SUBSCREEN];
}
if (snes_ppu.sub_add_mode) /* SNES_SUBSCREEN*/
{
if (!BIT(snes_ppu.color_modes, 7))
{
/* 0x00 add */
r = (*colour & 0x1f) + (subscreen->buffer[offset] & 0x1f);
g = ((*colour & 0x3e0) >> 5) + ((subscreen->buffer[offset] & 0x3e0) >> 5);
b = ((*colour & 0x7c00) >> 10) + ((subscreen->buffer[offset] & 0x7c00) >> 10);
clip_max = 1;
}
else
{
/* 0x80 sub */
r = (*colour & 0x1f) - (subscreen->buffer[offset] & 0x1f);
g = ((*colour & 0x3e0) >> 5) - ((subscreen->buffer[offset] & 0x3e0) >> 5);
b = ((*colour & 0x7c00) >> 10) - ((subscreen->buffer[offset] & 0x7c00) >> 10);
if (r > 0x1f) r = 0;
if (g > 0x1f) g = 0;
if (b > 0x1f) b = 0;
}
/* only halve if the color is not the back colour */
if (BIT(snes_ppu.color_modes, 6) && (subscreen->buffer[offset] != snes_cgram[FIXED_COLOUR]))
{
r >>= 1;
g >>= 1;
b >>= 1;
}
}
else /* Fixed colour */
{
if (!BIT(snes_ppu.color_modes, 7))
{
/* 0x00 add */
r = (*colour & 0x1f) + (snes_cgram[FIXED_COLOUR] & 0x1f);
g = ((*colour & 0x3e0) >> 5) + ((snes_cgram[FIXED_COLOUR] & 0x3e0) >> 5);
b = ((*colour & 0x7c00) >> 10) + ((snes_cgram[FIXED_COLOUR] & 0x7c00) >> 10);
clip_max = 1;
}
else
{
/* 0x80: sub */
r = (*colour & 0x1f) - (snes_cgram[FIXED_COLOUR] & 0x1f);
g = ((*colour & 0x3e0) >> 5) - ((snes_cgram[FIXED_COLOUR] & 0x3e0) >> 5);
b = ((*colour & 0x7c00) >> 10) - ((snes_cgram[FIXED_COLOUR] & 0x7c00) >> 10);
if (r > 0x1f) r = 0;
if (g > 0x1f) g = 0;
if (b > 0x1f) b = 0;
}
/* halve if necessary */
if (BIT(snes_ppu.color_modes, 6))
{
r >>= 1;
g >>= 1;
b >>= 1;
}
}
/* according to anomie's docs, after addition has been performed, division by 2 happens *before* clipping to max, hence we clip now */
if (clip_max)
{
if (r > 0x1f) r = 0x1f;
if (g > 0x1f) g = 0x1f;
if (b > 0x1f) b = 0x1f;
}
*colour = ((r & 0x1f) | ((g & 0x1f) << 5) | ((b & 0x1f) << 10));
}
}
/***************************************** /*****************************************
* snes_draw_tile() * snes_draw_tile()
* *
* Draw tiles with variable bit planes * Draw BG tiles with variable bit planes
*****************************************/ *****************************************/
INLINE void snes_draw_tile( UINT8 planes, UINT8 layer, UINT16 tileaddr, INT16 x, UINT8 priority, UINT8 flip, UINT8 direct_colors, UINT16 pal, UINT8 hires ) INLINE void snes_draw_tile( UINT8 planes, UINT8 layer, UINT16 tileaddr, INT16 x, UINT8 priority, UINT8 flip, UINT8 direct_colors, UINT16 pal, UINT8 hires )
@ -546,9 +436,7 @@ INLINE void snes_draw_tile_x2( UINT8 planes, UINT8 layer, UINT16 tileaddr, INT16
/***************************************** /*****************************************
* snes_draw_tile_object() * snes_draw_tile_object()
* *
* Draw tiles with 4 bit planes(16 colors) * Draw OAM tiles with 4 bit planes(16 colors)
* The same as snes_draw_tile_4() except
* that it takes a blend parameter.
*****************************************/ *****************************************/
INLINE void snes_draw_tile_object( UINT16 tileaddr, INT16 x, UINT8 priority, UINT8 flip, UINT16 pal ) INLINE void snes_draw_tile_object( UINT16 tileaddr, INT16 x, UINT8 priority, UINT8 flip, UINT16 pal )
@ -648,6 +536,23 @@ INLINE void snes_draw_tile_object( UINT16 tileaddr, INT16 x, UINT8 priority, UIN
} }
} }
/*************************************************************************************************
* SNES BG layers
*
* BG drawing theory of each scanline is quite easy: depending on the graphics Mode (0-7), there
* are up to 4 background layers. Pixels for each BG layer can have two different priorities.
* Depending on the line and on the BGHOFS and BGVOFS PPU registers, we first determine the tile
* address in snes_vram (by determining x,y coord and tile size and by calling snes_get_tmap_addr).
* Then, we load the correspondent data and we determine the tile properties: which priority to
* use, which palette etc. Finally, for each pixel of the tile appearing on screen, we check if
* the tile priority is higher than the BG/OAM already stored in that pixel for that line. If so
* we store the pixel in the buffer, otherwise we discard it.
*
* Of course, depending on the graphics Mode, it might be easier or harder to determine the proper
* tile address in vram (Mode 7 uses different registers, Mode 2, 4 and 6 uses OPT effect, etc.),
* but in general it works as described.
*************************************************************************************************/
/********************************************* /*********************************************
* snes_get_tmap_addr() * snes_get_tmap_addr()
* *
@ -1071,12 +976,41 @@ static void snes_update_line_mode7( UINT16 curline, UINT8 layer, UINT8 priority_
} }
} }
/********************************************* /*************************************************************************************************
* snes_update_objects() * SNES Sprites
* *
* Update an entire line of sprites. * 1. First of all: sprites are drawn one line in advance. We emulate this by caching the
* FIXME: We need to support high priority bit * starting vram address, the sprite size and the "name select" at each line, and by using
*********************************************/ * them the next line to output the proper sprites - see snes_update_obsel.
*
* 2. Each line can select its sprites among 128 available ones in oam_ram, hence we start
* by creating a list of the available objects (each one with its x,y coordinate, its size,
* its tile address, etc.) - see snes_oam_list_build.
*
* 3. Next, we start finding out which sprites will appear in the line: starting from
* FirstSprite, we count 32 OBJs which intersect our line and we store their indexes in the
* oam_itemlist array (if more than 32 sprites intersect our line, we set the Range Over
* flag); then, selecting among these sprites, we count 34 8x8 tiles which are visible
* in our line (i.e. whose x coord is between -size and 256) and we store the corresponding
* coordinates/priorities/palettes/etc. in the oam_tilelist array (if more than 34 tiles would
* appear on screen, we set the Time Over flag).
* Notice that when we populate oam_tilelist, we proceed from oam_itemlist[31] (or from the last
* item which intersects the scanline), towards oam_itemlist[0], i.e. the higher tiles (say
* oam_tilelist[34], or the last tile which appear on screen) will contain FirstSprite object,
* or the sprites with closer index to FirstSprite which get displayed. This will play an
* important role for sprite priority - see snes_update_objects_rto.
*
* 4. All the above happens at the beginning of each VIDEO_UPDATE. When we finally draw the
* scanline, we pass through the oam_tilelist and we store the displayed pixels in our scanline
* buffer. Notice that, for each pixel of a SNES sprite, only the priority of the topmost sprite
* is tested against the priority of the BG pixel (because FirstSprite is on top of FirstSprite+1,
* which is on top of FirstSprite+2, etc., and therefore other sprites are already covered by the
* topmost one). To emulate this, we draw each tile over the previous ones no matter what
* priorities are (differently from what we did with BGs): in the end, we will have in each pixel z
* its topmost sprite and scanline.priority[z] will be the topmost sprite priority as expected.
* Of course, sprite drawing must happen before BG drawing, so that afterwords BG pixels properly
* test their priority with the one of the correct sprite - see snes_update_objects.
*************************************************************************************************/
struct OAM struct OAM
{ {
@ -1088,6 +1022,22 @@ struct OAM
static struct OAM oam_spritelist[SNES_SCR_WIDTH / 2]; static struct OAM oam_spritelist[SNES_SCR_WIDTH / 2];
static UINT8 oam_itemlist[32];
struct TILELIST {
INT16 x;
UINT16 priority, pal, tileaddr;
int hflip;
};
struct TILELIST oam_tilelist[34];
/*********************************************
* snes_update_obsel()
*
* Update sprite settings for next line.
*********************************************/
static void snes_update_obsel( void ) static void snes_update_obsel( void )
{ {
snes_ppu.layer[SNES_OAM].charmap = snes_ppu.oam.next_charmap; snes_ppu.layer[SNES_OAM].charmap = snes_ppu.oam.next_charmap;
@ -1100,80 +1050,86 @@ static void snes_update_obsel( void )
} }
} }
/*********************************************
* snes_oam_list_build()
*
* Build a list of the available obj in OAM ram.
*********************************************/
static void snes_oam_list_build( void ) static void snes_oam_list_build( void )
{ {
UINT8 *oamram = (UINT8 *)snes_oam; UINT8 *oamram = (UINT8 *)snes_oam;
INT16 oam = 0x1ff; INT16 oam = 0x1ff;
UINT16 oam_extra = oam + 0x20; UINT16 oam_extra = oam + 0x20;
UINT16 extra = 0; UINT16 extra = 0;
int i; int ii;
snes_ppu.update_oam_list = 0; // eventually, we can optimize the code by only calling this function when there is a change in size snes_ppu.update_oam_list = 0; // eventually, we can optimize the code by only calling this function when there is a change in size
for (i = 127; i >= 0; i--) for (ii = 127; ii >= 0; ii--)
{ {
if (((i + 1) % 4) == 0) if (((ii + 1) % 4) == 0)
extra = oamram[oam_extra--]; extra = oamram[oam_extra--];
oam_spritelist[i].vflip = (oamram[oam] & 0x80) >> 7; oam_spritelist[ii].vflip = (oamram[oam] & 0x80) >> 7;
oam_spritelist[i].hflip = (oamram[oam] & 0x40) >> 6; oam_spritelist[ii].hflip = (oamram[oam] & 0x40) >> 6;
oam_spritelist[i].priority_bits = (oamram[oam] & 0x30) >> 4; oam_spritelist[ii].priority_bits = (oamram[oam] & 0x30) >> 4;
oam_spritelist[i].pal = 128 + ((oamram[oam] & 0x0e) << 3); oam_spritelist[ii].pal = 128 + ((oamram[oam] & 0x0e) << 3);
oam_spritelist[i].tile = (oamram[oam--] & 0x1) << 8; oam_spritelist[ii].tile = (oamram[oam--] & 0x1) << 8;
oam_spritelist[i].tile |= oamram[oam--]; oam_spritelist[ii].tile |= oamram[oam--];
oam_spritelist[i].y = oamram[oam--] + 1; /* We seem to need to add one here.... */ oam_spritelist[ii].y = oamram[oam--] + 1; /* We seem to need to add one here.... */
oam_spritelist[i].x = oamram[oam--]; oam_spritelist[ii].x = oamram[oam--];
oam_spritelist[i].size = (extra & 0x80) >> 7; oam_spritelist[ii].size = (extra & 0x80) >> 7;
extra <<= 1; extra <<= 1;
oam_spritelist[i].x |= ((extra & 0x80) << 1); oam_spritelist[ii].x |= ((extra & 0x80) << 1);
extra <<= 1; extra <<= 1;
oam_spritelist[i].y *= snes_ppu.obj_interlace; oam_spritelist[ii].y *= snes_ppu.obj_interlace;
/* Adjust if past maximum position */ /* Adjust if past maximum position */
if (oam_spritelist[i].y >= snes_ppu.beam.last_visible_line * snes_ppu.interlace) if (oam_spritelist[ii].y >= snes_ppu.beam.last_visible_line * snes_ppu.interlace)
oam_spritelist[i].y -= 256 * snes_ppu.interlace; oam_spritelist[ii].y -= 256 * snes_ppu.interlace;
oam_spritelist[i].x &= 0x1ff; oam_spritelist[ii].x &= 0x1ff;
/* Determine object size */ /* Determine object size */
switch (snes_ppu.oam.size) switch (snes_ppu.oam.size)
{ {
case 0: /* 8x8 or 16x16 */ case 0: /* 8x8 or 16x16 */
oam_spritelist[i].width = oam_spritelist[i].size ? 2 : 1; oam_spritelist[ii].width = oam_spritelist[ii].size ? 2 : 1;
oam_spritelist[i].height = oam_spritelist[i].size ? 2 : 1; oam_spritelist[ii].height = oam_spritelist[ii].size ? 2 : 1;
break; break;
case 1: /* 8x8 or 32x32 */ case 1: /* 8x8 or 32x32 */
oam_spritelist[i].width = oam_spritelist[i].size ? 4 : 1; oam_spritelist[ii].width = oam_spritelist[ii].size ? 4 : 1;
oam_spritelist[i].height = oam_spritelist[i].size ? 4 : 1; oam_spritelist[ii].height = oam_spritelist[ii].size ? 4 : 1;
break; break;
case 2: /* 8x8 or 64x64 */ case 2: /* 8x8 or 64x64 */
oam_spritelist[i].width = oam_spritelist[i].size ? 8 : 1; oam_spritelist[ii].width = oam_spritelist[ii].size ? 8 : 1;
oam_spritelist[i].height = oam_spritelist[i].size ? 8 : 1; oam_spritelist[ii].height = oam_spritelist[ii].size ? 8 : 1;
break; break;
case 3: /* 16x16 or 32x32 */ case 3: /* 16x16 or 32x32 */
oam_spritelist[i].width = oam_spritelist[i].size ? 4 : 2; oam_spritelist[ii].width = oam_spritelist[ii].size ? 4 : 2;
oam_spritelist[i].height = oam_spritelist[i].size ? 4 : 2; oam_spritelist[ii].height = oam_spritelist[ii].size ? 4 : 2;
break; break;
case 4: /* 16x16 or 64x64 */ case 4: /* 16x16 or 64x64 */
oam_spritelist[i].width = oam_spritelist[i].size ? 8 : 2; oam_spritelist[ii].width = oam_spritelist[ii].size ? 8 : 2;
oam_spritelist[i].height = oam_spritelist[i].size ? 8 : 2; oam_spritelist[ii].height = oam_spritelist[ii].size ? 8 : 2;
break; break;
case 5: /* 32x32 or 64x64 */ case 5: /* 32x32 or 64x64 */
oam_spritelist[i].width = oam_spritelist[i].size ? 8 : 4; oam_spritelist[ii].width = oam_spritelist[ii].size ? 8 : 4;
oam_spritelist[i].height = oam_spritelist[i].size ? 8 : 4; oam_spritelist[ii].height = oam_spritelist[ii].size ? 8 : 4;
break; break;
case 6: /* undocumented: 16x32 or 32x64 */ case 6: /* undocumented: 16x32 or 32x64 */
oam_spritelist[i].width = oam_spritelist[i].size ? 4 : 2; oam_spritelist[ii].width = oam_spritelist[ii].size ? 4 : 2;
oam_spritelist[i].height = oam_spritelist[i].size ? 8 : 4; oam_spritelist[ii].height = oam_spritelist[ii].size ? 8 : 4;
if (snes_ppu.obj_interlace && !oam_spritelist[i].size) if (snes_ppu.obj_interlace && !oam_spritelist[ii].size)
oam_spritelist[i].height = 2; oam_spritelist[ii].height = 2;
break; break;
case 7: /* undocumented: 16x32 or 32x32 */ case 7: /* undocumented: 16x32 or 32x32 */
oam_spritelist[i].width = oam_spritelist[i].size ? 4 : 2; oam_spritelist[ii].width = oam_spritelist[ii].size ? 4 : 2;
oam_spritelist[i].height = oam_spritelist[i].size ? 4 : 4; oam_spritelist[ii].height = oam_spritelist[ii].size ? 4 : 4;
if (snes_ppu.obj_interlace && !oam_spritelist[i].size) if (snes_ppu.obj_interlace && !oam_spritelist[ii].size)
oam_spritelist[i].height = 2; oam_spritelist[ii].height = 2;
break; break;
default: default:
/* we should never enter here... */ /* we should never enter here... */
@ -1183,15 +1139,12 @@ static void snes_oam_list_build( void )
} }
} }
static UINT8 oam_itemlist[32]; /*********************************************
* is_sprite_on_scanline()
struct TILELIST { *
INT16 x; * Check if a given sprites intersect current
UINT16 priority, pal, tileaddr; * scanline
int hflip; *********************************************/
};
struct TILELIST oam_tilelist[34];
static int is_sprite_on_scanline( UINT16 curline, UINT8 sprite ) static int is_sprite_on_scanline( UINT16 curline, UINT8 sprite )
{ {
@ -1211,15 +1164,22 @@ static int is_sprite_on_scanline( UINT16 curline, UINT8 sprite )
return 0; return 0;
} }
/*********************************************
* snes_update_objects_rto()
*
* Determine which OBJs will be drawn on this
* scanline.
*********************************************/
static void snes_update_objects_rto( UINT16 curline ) static void snes_update_objects_rto( UINT16 curline )
{ {
int active_sprite; int ii, jj, active_sprite;
UINT8 range_over, time_over; UINT8 range_over, time_over;
INT8 xs, ys; INT8 xs, ys;
UINT8 line; UINT8 line;
UINT8 height, width, vflip, hflip, priority, pal; UINT8 height, width, vflip, hflip, priority, pal;
UINT16 tile; UINT16 tile;
INT16 i, x, y; INT16 x, y;
UINT32 name_sel = 0; UINT32 name_sel = 0;
snes_oam_list_build(); snes_oam_list_build();
@ -1236,9 +1196,9 @@ static void snes_update_objects_rto( UINT16 curline )
memset(oam_itemlist, 0xff, 32); memset(oam_itemlist, 0xff, 32);
/* populate the list of 32 objects */ /* populate the list of 32 objects */
for (i = 0; i < 128; i++) for (ii = 0; ii < 128; ii++)
{ {
active_sprite = (i + snes_ppu.oam.first_sprite) & 0x7f; active_sprite = (ii + snes_ppu.oam.first_sprite) & 0x7f;
if (!is_sprite_on_scanline(curline, active_sprite)) if (!is_sprite_on_scanline(curline, active_sprite))
continue; continue;
@ -1250,16 +1210,16 @@ static void snes_update_objects_rto( UINT16 curline )
} }
/* reset the list of first 34 tiles to be drawn */ /* reset the list of first 34 tiles to be drawn */
for (i = 0; i < 34; i++) for (ii = 0; ii < 34; ii++)
oam_tilelist[i].tileaddr = 0xffff; oam_tilelist[ii].tileaddr = 0xffff;
/* populate the list of 34 tiles */ /* populate the list of 34 tiles */
for (i = 31; i >= 0; i--) for (ii = 31; ii >= 0; ii--)
{ {
if (oam_itemlist[i] == 0xff) if (oam_itemlist[ii] == 0xff)
continue; continue;
active_sprite = oam_itemlist[i]; active_sprite = oam_itemlist[ii];
tile = oam_spritelist[active_sprite].tile; tile = oam_spritelist[active_sprite].tile;
x = oam_spritelist[active_sprite].x; x = oam_spritelist[active_sprite].x;
@ -1284,11 +1244,9 @@ static void snes_update_objects_rto( UINT16 curline )
line <<= 1; line <<= 1;
tile <<= 5; tile <<= 5;
int ii; for (jj = 0; jj < width; jj++)
for (ii = 0; ii < width; ii++)
{ {
INT16 xx = (x + (ii << 3)) & 0x1ff; INT16 xx = (x + (jj << 3)) & 0x1ff;
if (x != 256 && xx >= 256 && (xx + 7) < 512) if (x != 256 && xx >= 256 && (xx + 7) < 512)
continue; continue;
@ -1296,7 +1254,7 @@ static void snes_update_objects_rto( UINT16 curline )
if (time_over++ >= 34) if (time_over++ >= 34)
break; break;
xs = (hflip) ? (width - 1 - ii) : ii; xs = (hflip) ? (width - 1 - jj) : jj;
oam_tilelist[time_over - 1].tileaddr = name_sel + tile + table_obj_offset[ys][xs] + line; oam_tilelist[time_over - 1].tileaddr = name_sel + tile + table_obj_offset[ys][xs] + line;
oam_tilelist[time_over - 1].hflip = hflip; oam_tilelist[time_over - 1].hflip = hflip;
oam_tilelist[time_over - 1].x = xx; oam_tilelist[time_over - 1].x = xx;
@ -1314,11 +1272,17 @@ static void snes_update_objects_rto( UINT16 curline )
snes_ppu.stat77_flags |= 0x80; snes_ppu.stat77_flags |= 0x80;
} }
/*********************************************
* snes_update_objects()
*
* Update an entire line of sprites.
*********************************************/
static void snes_update_objects( UINT8 priority_oam0, UINT8 priority_oam1, UINT8 priority_oam2, UINT8 priority_oam3 ) static void snes_update_objects( UINT8 priority_oam0, UINT8 priority_oam1, UINT8 priority_oam2, UINT8 priority_oam3 )
{ {
UINT8 pri, priority[4]; UINT8 pri, priority[4];
UINT32 charaddr; UINT32 charaddr;
int i; int ii;
#ifdef SNES_LAYER_DEBUG #ifdef SNES_LAYER_DEBUG
if (debug_options.bg_disabled[SNES_OAM]) if (debug_options.bg_disabled[SNES_OAM])
@ -1341,12 +1305,12 @@ static void snes_update_objects( UINT8 priority_oam0, UINT8 priority_oam1, UINT8
priority[3] = priority_oam3; priority[3] = priority_oam3;
/* finally draw the tiles from the tilelist */ /* finally draw the tiles from the tilelist */
for (i = 0; i < 34; i++) for (ii = 0; ii < 34; ii++)
{ {
int tile = i; int tile = ii;
#ifdef SNES_LAYER_DEBUG #ifdef SNES_LAYER_DEBUG
if (debug_options.sprite_reversed) if (debug_options.sprite_reversed)
tile = 33 - i; tile = 33 - ii;
#endif /* SNES_LAYER_DEBUG */ #endif /* SNES_LAYER_DEBUG */
if (oam_tilelist[tile].tileaddr == 0xffff) if (oam_tilelist[tile].tileaddr == 0xffff)
@ -1594,6 +1558,8 @@ static void snes_update_windowmasks( void )
* snes_update_offsets() * snes_update_offsets()
* *
* Update the offsets with the latest changes. * Update the offsets with the latest changes.
* This is currently unused, but it could
* possibly be handy for some minor optimization
*********************************************/ *********************************************/
static void snes_update_offsets( void ) static void snes_update_offsets( void )
@ -1605,11 +1571,141 @@ static void snes_update_offsets( void )
snes_ppu.update_offsets = 0; snes_ppu.update_offsets = 0;
} }
/*****************************************
* snes_draw_blend()
*
* Routine for additive/subtractive blending
* between the main and sub screens, i.e.
* color math.
*****************************************/
INLINE void snes_draw_blend( UINT16 offset, UINT16 *colour, UINT8 prevent_color_math, UINT8 black_pen_clip, int switch_screens )
{
#ifdef SNES_LAYER_DEBUG
if (debug_options.colormath_disabled)
return;
#endif /* SNES_LAYER_DEBUG */
/* when color math is applied to subscreen pixels, the blending depends on the blending used by the previous mainscreen
pixel, except for subscreen pixel 0 which has no previous mainscreen pixel, see comments in snes_refresh_scanline */
if (switch_screens && offset > 0)
offset -= 1;
if ((black_pen_clip == SNES_CLIP_ALWAYS) ||
(black_pen_clip == SNES_CLIP_IN && snes_ppu.clipmasks[SNES_COLOR][offset]) ||
(black_pen_clip == SNES_CLIP_OUT && !snes_ppu.clipmasks[SNES_COLOR][offset]))
*colour = 0; //clip to black before color math
if (prevent_color_math == SNES_CLIP_ALWAYS) // blending mode 3 == always OFF
return;
if ((prevent_color_math == SNES_CLIP_NEVER) ||
(prevent_color_math == SNES_CLIP_IN && !snes_ppu.clipmasks[SNES_COLOR][offset]) ||
(prevent_color_math == SNES_CLIP_OUT && snes_ppu.clipmasks[SNES_COLOR][offset]))
{
UINT16 r, g, b;
struct SCANLINE *subscreen;
int clip_max = 0; // if add then clip to 0x1f, if sub then clip to 0
#ifdef SNES_LAYER_DEBUG
/* Toggle drawing of SNES_SUBSCREEN or SNES_MAINSCREEN */
if (debug_options.draw_subscreen)
{
subscreen = switch_screens ? &scanlines[SNES_SUBSCREEN] : &scanlines[SNES_MAINSCREEN];
}
else
#endif /* SNES_LAYER_DEBUG */
{
subscreen = switch_screens ? &scanlines[SNES_MAINSCREEN] : &scanlines[SNES_SUBSCREEN];
}
if (snes_ppu.sub_add_mode) /* SNES_SUBSCREEN*/
{
if (!BIT(snes_ppu.color_modes, 7))
{
/* 0x00 add */
r = (*colour & 0x1f) + (subscreen->buffer[offset] & 0x1f);
g = ((*colour & 0x3e0) >> 5) + ((subscreen->buffer[offset] & 0x3e0) >> 5);
b = ((*colour & 0x7c00) >> 10) + ((subscreen->buffer[offset] & 0x7c00) >> 10);
clip_max = 1;
}
else
{
/* 0x80 sub */
r = (*colour & 0x1f) - (subscreen->buffer[offset] & 0x1f);
g = ((*colour & 0x3e0) >> 5) - ((subscreen->buffer[offset] & 0x3e0) >> 5);
b = ((*colour & 0x7c00) >> 10) - ((subscreen->buffer[offset] & 0x7c00) >> 10);
if (r > 0x1f) r = 0;
if (g > 0x1f) g = 0;
if (b > 0x1f) b = 0;
}
/* only halve if the color is not the back colour */
if (BIT(snes_ppu.color_modes, 6) && (subscreen->buffer[offset] != snes_cgram[FIXED_COLOUR]))
{
r >>= 1;
g >>= 1;
b >>= 1;
}
}
else /* Fixed colour */
{
if (!BIT(snes_ppu.color_modes, 7))
{
/* 0x00 add */
r = (*colour & 0x1f) + (snes_cgram[FIXED_COLOUR] & 0x1f);
g = ((*colour & 0x3e0) >> 5) + ((snes_cgram[FIXED_COLOUR] & 0x3e0) >> 5);
b = ((*colour & 0x7c00) >> 10) + ((snes_cgram[FIXED_COLOUR] & 0x7c00) >> 10);
clip_max = 1;
}
else
{
/* 0x80: sub */
r = (*colour & 0x1f) - (snes_cgram[FIXED_COLOUR] & 0x1f);
g = ((*colour & 0x3e0) >> 5) - ((snes_cgram[FIXED_COLOUR] & 0x3e0) >> 5);
b = ((*colour & 0x7c00) >> 10) - ((snes_cgram[FIXED_COLOUR] & 0x7c00) >> 10);
if (r > 0x1f) r = 0;
if (g > 0x1f) g = 0;
if (b > 0x1f) b = 0;
}
/* halve if necessary */
if (BIT(snes_ppu.color_modes, 6))
{
r >>= 1;
g >>= 1;
b >>= 1;
}
}
/* according to anomie's docs, after addition has been performed, division by 2 happens *before* clipping to max, hence we clip now */
if (clip_max)
{
if (r > 0x1f) r = 0x1f;
if (g > 0x1f) g = 0x1f;
if (b > 0x1f) b = 0x1f;
}
*colour = ((r & 0x1f) | ((g & 0x1f) << 5) | ((b & 0x1f) << 10));
}
}
/********************************************* /*********************************************
* snes_refresh_scanline() * snes_refresh_scanline()
* *
* Redraw the current line. * Redraw the current line.
*********************************************/ *********************************************/
/*********************************************
* Notice that in hires and pseudo hires modes,
* i.e. when 512 different pixels are present
* in a scanline, a crt TV monitor would end
* up blending adjacent pixels. To mimic this,
* we add a small (optional) hack which enters
* only in the very last stage of the scanline
* drawing and which simulates the TV by
* replacing the exact pixel color with an
* average of the current and next pixel colors.
* Credits (and thanks) to Blargg and Byuu for
* the optimized averaging algorithm.
*********************************************/
static void snes_refresh_scanline( running_machine *machine, bitmap_t *bitmap, UINT16 curline ) static void snes_refresh_scanline( running_machine *machine, bitmap_t *bitmap, UINT16 curline )
{ {
@ -1619,7 +1715,7 @@ static void snes_refresh_scanline( running_machine *machine, bitmap_t *bitmap, U
struct SCANLINE *scanline1, *scanline2; struct SCANLINE *scanline1, *scanline2;
UINT16 c; UINT16 c;
UINT16 prev_colour = 0; UINT16 prev_colour = 0;
int average = input_port_read_safe(machine, "OPTIONS", 0) & 0x01; int blurring = input_port_read_safe(machine, "OPTIONS", 0) & 0x01;
profiler_mark_start(PROFILER_VIDEO); profiler_mark_start(PROFILER_VIDEO);
@ -1726,10 +1822,11 @@ static void snes_refresh_scanline( running_machine *machine, bitmap_t *bitmap, U
/* prepare the pixel from sub screen */ /* prepare the pixel from sub screen */
c = scanline2->buffer[x]; c = scanline2->buffer[x];
/* in hires, subscreen pixels are blended as well: for each subscreen pixel, color math is applied if /* in hires/pseudo-hires, subscreen pixels are blended as well: for each subscreen pixel, color math
it had been applied to the previous mainscreen pixel. What happens at subscreen pixel 0 (which has no is applied if it had been applied to the previous mainscreen pixel. What happens at subscreen pixel 0
previous mainscreen pixel) is undocumented. Until more info are discovered, we (arbitrarily) apply to it (which has no previous mainscreen pixel) is undocumented. Until more info are discovered, we (arbitrarily)
the same color math as the *next* mainscreen pixel (i.e. mainscreen pixel 0) */ apply to it the same color math as the *next* mainscreen pixel (i.e. mainscreen pixel 0), which seems as good as
any other choice */
if (x == 0 && !scanline1->blend_exception[0] && snes_ppu.layer[scanline1->layer[0]].color_math) if (x == 0 && !scanline1->blend_exception[0] && snes_ppu.layer[scanline1->layer[0]].color_math)
snes_draw_blend(0, &c, snes_ppu.prevent_color_math, snes_ppu.clip_to_black, 1); snes_draw_blend(0, &c, snes_ppu.prevent_color_math, snes_ppu.clip_to_black, 1);
else if (x > 0 && !scanline1->blend_exception[x - 1] && snes_ppu.layer[scanline1->layer[x - 1]].color_math) else if (x > 0 && !scanline1->blend_exception[x - 1] && snes_ppu.layer[scanline1->layer[x - 1]].color_math)
@ -1738,8 +1835,8 @@ static void snes_refresh_scanline( running_machine *machine, bitmap_t *bitmap, U
tmp_col[0] = c; tmp_col[0] = c;
/* average the first pixel if required, or draw it directly*/ /* average the first pixel if required, or draw it directly*/
if (average) if (blurring)
c = (prev_colour + tmp_col[0] - ((prev_colour ^ tmp_col[0]) & 0x0421)) >> 1; c = (prev_colour + tmp_col[0] - ((prev_colour ^ tmp_col[0]) & 0x0421)) >> 1; // Hack code to mimic TV pixel blurring
else else
c = tmp_col[0]; c = tmp_col[0];
@ -1750,10 +1847,9 @@ static void snes_refresh_scanline( running_machine *machine, bitmap_t *bitmap, U
*BITMAP_ADDR32(bitmap, curline, x * 2 + 0) = MAKE_RGB(pal5bit(r), pal5bit(g), pal5bit(b)); *BITMAP_ADDR32(bitmap, curline, x * 2 + 0) = MAKE_RGB(pal5bit(r), pal5bit(g), pal5bit(b));
prev_colour = tmp_col[0]; prev_colour = tmp_col[0];
/* average the second pixel if required, or draw it directly*/ /* average the second pixel if required, or draw it directly*/
if (average) if (blurring)
c = (prev_colour + tmp_col[1] - ((prev_colour ^ tmp_col[1]) & 0x0421)) >> 1; c = (prev_colour + tmp_col[1] - ((prev_colour ^ tmp_col[1]) & 0x0421)) >> 1; // Hack code to mimic TV pixel blurring
else else
c = tmp_col[1]; c = tmp_col[1];