summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--src/devices/video/snes_ppu.cpp1003
-rw-r--r--src/devices/video/snes_ppu.h33
2 files changed, 383 insertions, 653 deletions
diff --git a/src/devices/video/snes_ppu.cpp b/src/devices/video/snes_ppu.cpp
index 43fbeeac74f..ad2493c4735 100644
--- a/src/devices/video/snes_ppu.cpp
+++ b/src/devices/video/snes_ppu.cpp
@@ -82,7 +82,7 @@
#define SNES_CLIP_OUT 2
#define SNES_CLIP_ALWAYS 3
-#define SNES_VRAM_SIZE 0x10000 /* 128kb of video ram */
+#define SNES_VRAM_SIZE 0x10000 /* 64kb of video ram */
#define SNES_CGRAM_SIZE 0x202 /* 256 16-bit colours + 1 tacked on 16-bit colour for fixed colour */
#define SNES_OAM_SIZE 0x440 /* 1088 bytes of Object Attribute Memory */
#define FIXED_COLOUR 256 /* Position in cgram for fixed colour */
@@ -214,7 +214,6 @@ snes_ppu_device::snes_ppu_device(const machine_config &mconfig, const char *tag,
{
}
-
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
@@ -227,6 +226,26 @@ void snes_ppu_device::device_start()
m_cgram = std::make_unique<uint16_t[]>(SNES_CGRAM_SIZE/2);
m_oam_ram = std::make_unique<uint16_t[]>(SNES_OAM_SIZE/2);
+ m_light_table = std::make_unique<std::unique_ptr<uint16_t[]>[]>(16);
+ for (uint8_t l = 0; l < 16; l++)
+ {
+ m_light_table[l] = std::make_unique<uint16_t[]>(32768);
+ for (uint8_t r = 0; r < 32; r++)
+ {
+ for (uint8_t g = 0; g < 32; g++)
+ {
+ for (uint8_t b = 0; b < 32; b++)
+ {
+ double luma = (double)l / 15.0;
+ uint8_t ar = (uint8_t)(luma * r + 0.5);
+ uint8_t ag = (uint8_t)(luma * g + 0.5);
+ uint8_t ab = (uint8_t)(luma * b + 0.5);
+ m_light_table[l][r << 10 | g << 5 | b << 0] = ar << 10 | ag << 5 | ab << 0;
+ }
+ }
+ }
+ }
+
for (int i = 0; i < 2; i++)
{
save_item(NAME(m_scanlines[i].enable), i);
@@ -528,178 +547,107 @@ inline void snes_ppu_device::set_scanline_pixel( int screen, int16_t x, uint16_t
*************************************************************************************************/
/*****************************************
- * draw_bgtile_lores()
- * draw_bgtile_hires()
- * draw_oamtile_()
+ * render_window()
*
- * Check if a pixel is clipped or not, and
- * copy it to the scanline buffer when
- * appropriate. The actual way to perform
- * such operations depends on the source
- * (BG or OAM) and on the resolution (hires
- * or lores)
+ * An example of how windows work:
+ * Win1: ...#####......
+ * Win2: ......#####...
+ * IN OUT
+ * OR: ...########... ###........###
+ * AND: ......##...... ######..######
+ * XOR: ...###..###... ###...##...###
+ * XNOR: ###...##...### ...###..###...
*****************************************/
-inline void snes_ppu_device::draw_bgtile_lores( uint8_t layer, int16_t ii, uint8_t colour, uint16_t pal, uint8_t direct_colors, uint8_t priority )
+void snes_ppu_device::render_window(uint16_t layer_idx, uint8_t enable, uint8_t *output)
{
- for (int screen = SNES_MAINSCREEN; screen <= SNES_SUBSCREEN; screen++)
+ layer_t &self = m_layer[layer_idx];
+ if (!enable || (!self.window1_enabled && !self.window2_enabled))
{
- if (ii >= 0 && ii < SNES_SCR_WIDTH && m_scanlines[screen].enable)
- {
- if (m_scanlines[screen].priority[ii] <= priority)
- {
- uint8_t clr = colour;
- uint8_t clipmask = m_clipmasks[layer][ii];
-
-#if SNES_LAYER_DEBUG
- if (m_debug_options.windows_disabled)
- clipmask = 0xff;
-#endif /* SNES_LAYER_DEBUG */
-
- /* Clip to windows */
- if (m_scanlines[screen].clip)
- clr &= clipmask;
+ memset(output, 0, 256);
+ return;
+ }
- /* Only draw if we have a colour (0 == transparent) */
- if (clr)
- {
- uint16_t c = get_bgcolor(direct_colors, pal, clr);
- set_scanline_pixel(screen, ii, c, priority, layer, 0);
- }
- }
+ if (self.window1_enabled && !self.window2_enabled)
+ {
+ const uint8_t set = 1 ^ self.window1_invert;
+ const uint8_t clear = 1 - set;
+ for (uint16_t x = 0; x < 256; x++)
+ {
+ output[x] = (x >= m_window1_left && x <= m_window1_right) ? set : clear;
}
+ return;
}
-}
-inline void snes_ppu_device::draw_bgtile_hires( uint8_t layer, int16_t ii, uint8_t colour, uint16_t pal, uint8_t direct_colors, uint8_t priority )
-{
- for (int screen = SNES_MAINSCREEN; screen <= SNES_SUBSCREEN; screen++)
+ if (self.window2_enabled && !self.window1_enabled)
{
- // odd pixels to main screen, even pixels to sub screen
- if (ii >= 0 && ii < (SNES_SCR_WIDTH << 1) && ((ii & 1) ^ screen) && m_scanlines[screen].enable)
+ const uint8_t set = 1 ^ self.window2_invert;
+ const uint8_t clear = 1 - set;
+ for (uint16_t x = 0; x < 256; x++)
{
- if (m_scanlines[screen].priority[ii >> 1] <= priority)
- {
- uint8_t clr = colour;
- uint8_t clipmask = m_clipmasks[layer][ii >> 1];
-
-#if SNES_LAYER_DEBUG
- if (m_debug_options.windows_disabled)
- clipmask = 0xff;
-#endif /* SNES_LAYER_DEBUG */
-
- /* Clip to windows */
- if (m_scanlines[screen].clip)
- clr &= clipmask;
-
- /* Only draw if we have a colour (0 == transparent) */
- if (clr)
- {
- uint16_t c = get_bgcolor(direct_colors, pal, clr);
- set_scanline_pixel(screen, ii >> 1, c, priority, layer, 0);
- }
- }
+ output[x] = (x >= m_window2_left && x <= m_window2_right) ? set : clear;
}
+ return;
}
-}
-
-inline void snes_ppu_device::draw_oamtile( int16_t ii, uint8_t colour, uint16_t pal, uint8_t priority )
-{
- int16_t pos = ii & 0x1ff;
- for (int screen = SNES_MAINSCREEN; screen <= SNES_SUBSCREEN; screen++)
+ for (uint16_t x = 0; x < 256; x++)
{
- if (pos >= 0 && pos < SNES_SCR_WIDTH && m_scanlines[screen].enable)
+ uint8_t one_mask = ((x >= m_window1_left && x <= m_window1_right) ? 1 : 0) ^ self.window1_invert;
+ uint8_t two_mask = ((x >= m_window2_left && x <= m_window2_right) ? 1 : 0) ^ self.window2_invert;
+ switch (self.wlog_mask)
{
- uint8_t clr = colour;
- uint8_t clipmask = m_clipmasks[SNES_OAM][pos];
-
-#if SNES_LAYER_DEBUG
- if (m_debug_options.windows_disabled)
- clipmask = 0xff;
-#endif /* SNES_LAYER_DEBUG */
-
- /* Clip to windows */
- if (m_scanlines[screen].clip)
- clr &= clipmask;
-
- /* Only draw if we have a colour (0 == transparent) */
- if (clr)
- {
- uint16_t c = m_cgram[(pal + clr) % FIXED_COLOUR];
- int blend = (pal + clr < 192) ? 1 : 0;
- set_scanline_pixel(screen, pos, c, priority, SNES_OAM, blend);
- }
+ case 0: output[x] = (one_mask | two_mask); break;
+ case 1: output[x] = (one_mask & two_mask); break;
+ case 2: output[x] = (one_mask ^ two_mask); break;
+ case 3: output[x] = 1 - (one_mask ^ two_mask); break;
}
}
}
/*****************************************
- * draw_tile()
+ * draw_oamtile()
*
- * Draw 8 pixels from the expected tile
+ * Draw 8 pixels from the expected OAM
+ * tile into priority/palette buffers
* by reading the color planes from vram
- * and by calling the appropriate routine
- * (depending on layer and resolution)
+ * and by calling draw_oamtile
*****************************************/
-inline void snes_ppu_device::draw_tile( uint8_t planes, uint8_t layer, uint32_t tileaddr, int16_t x, uint8_t priority, uint8_t flip, uint8_t direct_colors, uint16_t pal, uint8_t hires )
+inline void snes_ppu_device::draw_oamtile( uint32_t tileaddr, int16_t tile_x, uint8_t priority, uint8_t flip, uint16_t pal, uint8_t *palbuf, uint8_t *pribuf )
{
- uint8_t plane[8];
- int16_t ii, jj;
- int x_mos;
+ uint8_t plane[4];
- for (ii = 0; ii < planes / 2; ii++)
+ for (int16_t ii = 0; ii < 2; ii++)
{
- plane[2 * ii + 0] = m_vram[(tileaddr + 16 * ii + 0) % SNES_VRAM_SIZE];
- plane[2 * ii + 1] = m_vram[(tileaddr + 16 * ii + 1) % SNES_VRAM_SIZE];
+ plane[2 * ii + 0] = m_vram[((tileaddr << 1) + 16 * ii + 0) % SNES_VRAM_SIZE];
+ plane[2 * ii + 1] = m_vram[((tileaddr << 1) + 16 * ii + 1) % SNES_VRAM_SIZE];
}
- for (ii = x; ii < (x + 8); ii++)
+ for (int16_t x = 0; x < 8; x++)
{
- uint8_t colour = 0;
- uint8_t mosaic = m_layer[layer].mosaic_enabled;
-
-#if SNES_LAYER_DEBUG
- if (m_debug_options.mosaic_disabled)
- mosaic = 0;
-#endif /* SNES_LAYER_DEBUG */
-
- if (flip)
- {
- for (jj = 0; jj < planes; jj++)
- colour |= BIT(plane[jj], ii - x) ? (1 << jj) : 0;
- }
- else
+ tile_x &= 0x01ff;
+ if (tile_x < 256)
{
- for (jj = 0; jj < planes; jj++)
- colour |= BIT(plane[jj], 7 - (ii - x)) ? (1 << jj) : 0;
- }
+ uint8_t colour = 0;
- if (layer == SNES_OAM)
- draw_oamtile(ii, colour, pal, priority);
- else if (!hires)
- {
- if (mosaic)
+ if (flip)
{
- for (x_mos = 0; x_mos < (m_mosaic_size + 1); x_mos++)
- draw_bgtile_lores(layer, ii + x_mos, colour, pal, direct_colors, priority);
- ii += x_mos - 1;
+ for (int16_t jj = 0; jj < 4; jj++)
+ colour |= BIT(plane[jj], x) ? (1 << jj) : 0;
}
else
- draw_bgtile_lores(layer, ii, colour, pal, direct_colors, priority);
- }
- else /* hires */
- {
- if (mosaic)
{
- for (x_mos = 0; x_mos < (m_mosaic_size + 1); x_mos++)
- draw_bgtile_hires(layer, ii + x_mos, colour, pal, direct_colors, priority);
- ii += x_mos - 1;
+ for (int16_t jj = 0; jj < 4; jj++)
+ colour |= BIT(plane[jj], 7 - x) ? (1 << jj) : 0;
+ }
+
+ if (colour)
+ {
+ palbuf[tile_x] = pal + colour;
+ pribuf[tile_x] = priority;
}
- else
- draw_bgtile_hires(layer, ii, colour, pal, direct_colors, priority);
}
+ tile_x++;
}
}
@@ -738,26 +686,39 @@ inline uint32_t snes_ppu_device::get_tile( uint8_t layer_idx, uint32_t hoffset,
}
/*********************************************
- * get_tmap_addr()
+ * plot_above()
*
- * Find the address in VRAM of the tile (x,y)
+ * Update a main-screen pixel based on
+ * priority.
*********************************************/
-inline uint32_t snes_ppu_device::get_tmap_addr( uint8_t layer, uint8_t hires, uint8_t tile_size, uint32_t base, uint32_t x, uint32_t y )
+inline void snes_ppu_device::plot_above( uint16_t x, uint8_t source, uint8_t priority, uint16_t color, int blend_exception )
{
- uint32_t res = base;
- x >>= (3 + tile_size);
- y >>= (3 + tile_size);
-
- res += (m_layer[layer].tilemap_size & 2) ? ((y & 0x20) << ((m_layer[layer].tilemap_size & 1) ? 7 : 6)) : 0;
- /* Scroll vertically */
- res += (y & 0x1f) << 6;
- /* Offset horizontally */
- res += (m_layer[layer].tilemap_size & 1) ? ((x & 0x20) << 6) : 0;
- /* Scroll horizontally */
- res += (x & 0x1f) << 1;
+ if (priority > m_scanlines[SNES_MAINSCREEN].priority[x])
+ {
+ m_scanlines[SNES_MAINSCREEN].priority[x] = priority;
+ m_scanlines[SNES_MAINSCREEN].buffer[x] = color;
+ m_scanlines[SNES_MAINSCREEN].layer[x] = source;
+ m_scanlines[SNES_MAINSCREEN].blend_exception[x] = blend_exception;
+ }
+}
- return res;
+/*********************************************
+ * plot_below()
+ *
+ * Update a sub-screen pixel based on
+ * priority.
+ *********************************************/
+
+inline void snes_ppu_device::plot_below( uint16_t x, uint8_t source, uint8_t priority, uint16_t color, int blend_exception )
+{
+ if (priority > m_scanlines[SNES_SUBSCREEN].priority[x])
+ {
+ m_scanlines[SNES_SUBSCREEN].priority[x] = priority;
+ m_scanlines[SNES_SUBSCREEN].buffer[x] = color;
+ m_scanlines[SNES_SUBSCREEN].layer[x] = source;
+ m_scanlines[SNES_SUBSCREEN].blend_exception[x] = blend_exception;
+ }
}
/*********************************************
@@ -766,7 +727,7 @@ inline uint32_t snes_ppu_device::get_tmap_addr( uint8_t layer, uint8_t hires, ui
* Update an entire line of tiles.
*********************************************/
-inline void snes_ppu_device::update_line( uint16_t curline, uint8_t layer_idx, uint8_t direct_colors )
+void snes_ppu_device::update_line( uint16_t curline, uint8_t layer_idx, uint8_t direct_colors )
{
layer_t &layer = m_layer[layer_idx];
@@ -779,14 +740,17 @@ inline void snes_ppu_device::update_line( uint16_t curline, uint8_t layer_idx, u
m_scanlines[SNES_MAINSCREEN].enable = layer.main_bg_enabled;
m_scanlines[SNES_SUBSCREEN].enable = layer.sub_bg_enabled;
- m_scanlines[SNES_MAINSCREEN].clip = layer.main_window_enabled;
- m_scanlines[SNES_SUBSCREEN].clip = layer.sub_window_enabled;
if (!m_scanlines[SNES_MAINSCREEN].enable && !m_scanlines[SNES_SUBSCREEN].enable)
{
return;
}
+ uint8_t window_above[256];
+ uint8_t window_below[256];
+ render_window(layer_idx, layer.main_window_enabled, window_above);
+ render_window(layer_idx, layer.sub_window_enabled, window_below);
+
bool hires = m_mode == 5 || m_mode == 6;
bool opt_mode = m_mode == 2 || m_mode == 4 || m_mode == 6;
bool direct_color_mode = direct_colors && layer_idx == SNES_BG1 && (m_mode == 3 || m_mode == 4);
@@ -808,7 +772,6 @@ inline void snes_ppu_device::update_line( uint16_t curline, uint8_t layer_idx, u
uint32_t y = layer.mosaic_enabled ? layer.mosaic_offset : curline;
- //printf("h:%d i:%d l:%d ", hires ? 1 : 0, m_interlace, curline);
if (hires)
{
hscroll <<= 1;
@@ -919,50 +882,26 @@ inline void snes_ppu_device::update_line( uint16_t curline, uint8_t layer_idx, u
}
else
{
- mosaic_color = m_cgram[palette_index + mosaic_palette];
+ mosaic_color = m_cgram[(palette_index + mosaic_palette) % FIXED_COLOUR];
}
}
if (!mosaic_palette) continue;
if (!hires)
{
- if (layer.main_bg_enabled && mosaic_priority > m_scanlines[SNES_MAINSCREEN].priority[x])
- {
- if (!m_scanlines[SNES_MAINSCREEN].clip || m_clipmasks[layer_idx][x])
- {
- set_scanline_pixel(SNES_MAINSCREEN, x, mosaic_color, mosaic_priority, layer_idx, 0);
- }
- }
- if (layer.sub_bg_enabled && mosaic_priority > m_scanlines[SNES_SUBSCREEN].priority[x])
- {
- if (!m_scanlines[SNES_SUBSCREEN].clip || m_clipmasks[layer_idx][x])
- {
- set_scanline_pixel(SNES_SUBSCREEN, x, mosaic_color, mosaic_priority, layer_idx, 0);
- }
- }
+ if (layer.main_bg_enabled && window_above[x] == 0) plot_above(x, layer_idx, mosaic_priority, mosaic_color);
+ if (layer.sub_bg_enabled && window_below[x] == 0) plot_below(x, layer_idx, mosaic_priority, mosaic_color);
}
else
{
uint32_t _x = x >> 1;
if (x & 1)
{
- if (layer.main_bg_enabled && mosaic_priority > m_scanlines[SNES_MAINSCREEN].priority[_x])
- {
- if (!m_scanlines[SNES_MAINSCREEN].clip || m_clipmasks[layer_idx][_x])
- {
- set_scanline_pixel(SNES_MAINSCREEN, _x, mosaic_color, mosaic_priority, layer_idx, 0);
- }
- }
+ if (layer.main_bg_enabled && window_above[_x] == 0) plot_above(_x, layer_idx, mosaic_priority, mosaic_color);
}
else
{
- if (layer.sub_bg_enabled && mosaic_priority > m_scanlines[SNES_SUBSCREEN].priority[_x])
- {
- if (!m_scanlines[SNES_SUBSCREEN].clip || m_clipmasks[layer_idx][_x])
- {
- set_scanline_pixel(SNES_SUBSCREEN, _x, mosaic_color, mosaic_priority, layer_idx, 0);
- }
- }
+ if (layer.sub_bg_enabled && window_below[_x] == 0) plot_below(_x, layer_idx, mosaic_priority, mosaic_color);
}
}
}
@@ -978,168 +917,76 @@ inline void snes_ppu_device::update_line( uint16_t curline, uint8_t layer_idx, u
#define MODE7_CLIP(x) (((x) & 0x2000) ? ((x) | ~0x03ff) : ((x) & 0x03ff))
-void snes_ppu_device::update_line_mode7( uint16_t curline, uint8_t layer )
+void snes_ppu_device::update_line_mode7( uint16_t curline, uint8_t layer_idx )
{
- uint8_t priority_a = m_layer[layer].priority[0];
- uint8_t priority_b = m_layer[layer].priority[1];
- uint32_t tiled;
- int16_t ma, mb, mc, md;
- int32_t xc, yc, tx, ty, sx, sy, hs, vs, xpos, xdir, x0, y0;
- uint8_t priority = priority_b;
- uint8_t colour = 0;
- uint16_t *mosaic_x, *mosaic_y;
- uint16_t c;
- int screen;
-
-#if SNES_LAYER_DEBUG
- if (m_debug_options.bg_disabled[layer])
- return;
-#endif /* SNES_LAYER_DEBUG */
-
- m_scanlines[SNES_MAINSCREEN].enable = m_layer[layer].main_bg_enabled;
- m_scanlines[SNES_SUBSCREEN].enable = m_layer[layer].sub_bg_enabled;
- m_scanlines[SNES_MAINSCREEN].clip = m_layer[layer].main_window_enabled;
- m_scanlines[SNES_SUBSCREEN].clip = m_layer[layer].sub_window_enabled;
-
- if (!m_scanlines[SNES_MAINSCREEN].enable && !m_scanlines[SNES_SUBSCREEN].enable)
- return;
-
- ma = m_mode7.matrix_a;
- mb = m_mode7.matrix_b;
- mc = m_mode7.matrix_c;
- md = m_mode7.matrix_d;
- xc = m_mode7.origin_x;
- yc = m_mode7.origin_y;
- hs = m_mode7.hor_offset;
- vs = m_mode7.ver_offset;
-
- /* Sign extend */
- xc <<= 19;
- xc >>= 19;
- yc <<= 19;
- yc >>= 19;
- hs <<= 19;
- hs >>= 19;
- vs <<= 19;
- vs >>= 19;
-
- /* Vertical flip */
- if (m_mode7.vflip)
- sy = 255 - curline;
- else
- sy = curline;
-
- /* Horizontal flip */
- if (m_mode7.hflip)
- {
- xpos = 255;
- xdir = -1;
- }
- else
- {
- xpos = 0;
- xdir = 1;
- }
+ layer_t &self = m_layer[layer_idx];
+ int _y = self.mosaic_enabled ? self.mosaic_offset : curline;
+ int y = !m_mode7.vflip ? _y : 255 - _y;
+
+ int a = m_mode7.matrix_a;
+ int b = m_mode7.matrix_b;
+ int c = m_mode7.matrix_c;
+ int d = m_mode7.matrix_d;
+ int hcenter = (m_mode7.origin_x << 19) >> 19;
+ int vcenter = (m_mode7.origin_y << 19) >> 19;
+ int hoffset = (m_mode7.hor_offset << 19) >> 19;
+ int voffset = (m_mode7.ver_offset << 19) >> 19;
- /* MOSAIC - to be verified */
- if (layer == SNES_BG2) // BG2 use two different bits for horizontal and vertical mosaic
- {
- mosaic_x = m_mosaic_table[m_layer[SNES_BG2].mosaic_enabled ? m_mosaic_size : 0];
- mosaic_y = m_mosaic_table[m_layer[SNES_BG1].mosaic_enabled ? m_mosaic_size : 0];
- }
- else // BG1 works as usual
- {
- mosaic_x = m_mosaic_table[m_layer[SNES_BG1].mosaic_enabled ? m_mosaic_size : 0];
- mosaic_y = m_mosaic_table[m_layer[SNES_BG1].mosaic_enabled ? m_mosaic_size : 0];
- }
+ uint32_t mosaic_counter = 1;
+ uint32_t mosaic_palette = 0;
+ uint8_t mosaic_priority = 0;
+ uint16_t mosaic_color = 0;
-#if SNES_LAYER_DEBUG
- if (m_debug_options.mosaic_disabled)
- {
- mosaic_x = m_mosaic_table[0];
- mosaic_y = m_mosaic_table[0];
- }
-#endif /* SNES_LAYER_DEBUG */
+ int origin_x = (a * MODE7_CLIP(hoffset - hcenter) & ~63) + (b * MODE7_CLIP(voffset - vcenter) & ~63) + (b * y & ~63) + (hcenter << 8);
+ int origin_y = (c * MODE7_CLIP(hoffset - hcenter) & ~63) + (d * MODE7_CLIP(voffset - vcenter) & ~63) + (d * y & ~63) + (vcenter << 8);
- /* Let's do some mode7 drawing huh? */
- /* These can be computed only once, since they do not depend on sx */
- x0 = ((ma * MODE7_CLIP(hs - xc)) & ~0x3f) + ((mb * mosaic_y[sy]) & ~0x3f) + ((mb * MODE7_CLIP(vs - yc)) & ~0x3f) + (xc << 8);
- y0 = ((mc * MODE7_CLIP(hs - xc)) & ~0x3f) + ((md * mosaic_y[sy]) & ~0x3f) + ((md * MODE7_CLIP(vs - yc)) & ~0x3f) + (yc << 8);
+ uint8_t window_above[256];
+ uint8_t window_below[256];
+ render_window(layer_idx, self.main_window_enabled, window_above);
+ render_window(layer_idx, self.sub_window_enabled, window_below);
- for (sx = 0; sx < 256; sx++, xpos += xdir)
+ for (int _x = 0; _x < 256; _x++)
{
- tx = (x0 + (ma * mosaic_x[sx])) >> 8;
- ty = (y0 + (mc * mosaic_x[sx])) >> 8;
-
- switch (m_mode7.repeat)
+ int x = !m_mode7.hflip ? _x : 255 - _x;
+ int pixel_x = (origin_x + (a * x)) >> 8;
+ int pixel_y = (origin_y + (c * x)) >> 8;
+ int tile_x = (pixel_x >> 3) & 0x7f;
+ int tile_y = (pixel_y >> 3) & 0x7f;
+ bool out_of_bounds = (pixel_x | pixel_y) & ~0x03ff;
+ uint16_t tile_address = (tile_y * 0x80 + tile_x) << 1;
+ uint16_t palette_address = (((pixel_y & 7) << 3) + (pixel_x & 7)) & 0x7fff;
+ uint8_t tile = m_mode7.repeat == 3 && out_of_bounds ? 0 : m_vram[tile_address];
+ uint8_t palette = m_mode7.repeat == 2 && out_of_bounds ? 0 : m_vram[((tile << 6 | palette_address) << 1) | 1];
+
+ uint8_t priority = 0;
+ if (layer_idx == SNES_BG1)
{
- case 0x00: /* Repeat if outside screen area */
- case 0x01: /* Repeat if outside screen area */
- tx &= 0x3ff;
- ty &= 0x3ff;
- tiled = m_vram[((((tx >> 3) & 0x7f) + (((ty >> 3) & 0x7f) * 128)) * 2) % SNES_VRAM_SIZE] << 7;
- colour = m_vram[(tiled + ((tx & 0x07) * 2) + ((ty & 0x07) * 16) + 1) % SNES_VRAM_SIZE];
- break;
- case 0x02: /* Single colour backdrop screen if outside screen area */
- if ((tx >= 0) && (tx < 1024) && (ty >= 0) && (ty < 1024))
- {
- tiled = m_vram[((((tx >> 3) & 0x7f) + (((ty >> 3) & 0x7f) * 128)) * 2) % SNES_VRAM_SIZE] << 7;
- colour = m_vram[(tiled + ((tx & 0x07) * 2) + ((ty & 0x07) * 16) + 1) % SNES_VRAM_SIZE];
- }
- else
- colour = 0;
- break;
- case 0x03: /* Character 0x00 repeat if outside screen area */
- if ((tx >= 0) && (tx < 1024) && (ty >= 0) && (ty < 1024))
- tiled = m_vram[((((tx >> 3) & 0x7f) + (((ty >> 3) & 0x7f) * 128)) * 2) % SNES_VRAM_SIZE] << 7;
- else
- tiled = 0;
-
- colour = m_vram[(tiled + ((tx & 0x07) * 2) + ((ty & 0x07) * 16) + 1) % SNES_VRAM_SIZE];
- break;
+ priority = self.priority[0];
}
-
- /* The last bit is for priority in EXTBG mode (used only for BG2) */
- if (layer == SNES_BG2)
+ else if (layer_idx == SNES_BG2)
{
- priority = ((colour & 0x80) >> 7) ? priority_a : priority_b;
- colour &= 0x7f;
-
-#if SNES_LAYER_DEBUG
- /* if we want to draw only one of the priorities of this layer */
- if (((m_debug_options.select_pri[layer] & 0x01) && (priority == priority_a)) ||
- ((m_debug_options.select_pri[layer] & 0x02) && (priority == priority_b)))
- continue;
-#endif /* SNES_LAYER_DEBUG */
+ priority = self.priority[palette >> 7];
+ palette &= 0x7f;
}
- for (screen = SNES_MAINSCREEN; screen <= SNES_SUBSCREEN; screen++)
+ if (!self.mosaic_enabled || --mosaic_counter == 0)
{
- if (m_scanlines[screen].enable)
+ mosaic_counter = 1 + m_mosaic_size;
+ mosaic_palette = palette;
+ mosaic_priority = priority;
+ if (m_direct_color && layer_idx == SNES_BG1)
{
- uint8_t clr = colour;
- uint8_t clipmask = m_clipmasks[layer][xpos];
-
-#if SNES_LAYER_DEBUG
- if (m_debug_options.windows_disabled)
- clipmask = 0xff;
-#endif /* SNES_LAYER_DEBUG */
-
- /* Clip to windows */
- if (m_scanlines[screen].clip)
- clr &= clipmask;
-
- /* Draw pixel if appropriate */
- if (m_scanlines[screen].priority[xpos] <= priority && clr > 0)
- {
- /* Direct select, but only outside EXTBG! */
- // Direct color format is: 0 | BB000 | GGG00 | RRR00, HW confirms that the data is zero padded.
- // In other words, like normal direct color, with pal = 0
- c = get_bgcolor(m_direct_color && layer == SNES_BG1, 0, clr);
- set_scanline_pixel(screen, xpos, c, priority, layer, 0);
- }
+ mosaic_color = direct_color(0, palette);
+ }
+ else
+ {
+ mosaic_color = m_cgram[palette];
}
}
+ if (!mosaic_palette) continue;
+
+ if (self.main_bg_enabled && window_above[_x] == 0) plot_above(_x, layer_idx, mosaic_priority, mosaic_color);
+ if (self.sub_bg_enabled && window_below[_x] == 0) plot_below(_x, layer_idx, mosaic_priority, mosaic_color);
}
}
@@ -1287,7 +1134,7 @@ void snes_ppu_device::oam_list_build( void )
/*********************************************
* is_sprite_on_scanline()
*
- * Check if a given sprites intersect current
+ * Check if a given sprite intersects current
* scanline
*********************************************/
@@ -1325,7 +1172,6 @@ void snes_ppu_device::update_objects_rto( uint16_t curline )
uint8_t height, width, vflip, hflip, priority, pal;
uint16_t tile;
int16_t x, y;
- uint32_t name_sel;
oam_list_build();
@@ -1383,7 +1229,7 @@ void snes_ppu_device::update_objects_rto( uint16_t curline )
if (curline >= y && curline < (y + (height << 3)))
{
/* Only objects using tiles over 255 use name select */
- name_sel = (tile < 256) ? 0 : m_oam.name_select;
+ uint32_t name_sel = (tile < 256) ? 0 : ((1 + m_oam.name_select) << 12);
ys = (curline - y) >> 3;
line = (curline - y) % 8;
@@ -1392,8 +1238,9 @@ void snes_ppu_device::update_objects_rto( uint16_t curline )
ys = height - ys - 1;
line = 7 - line;
}
- line <<= 1;
- tile <<= 5;
+
+ uint16_t character_x = tile & 15;
+ uint16_t character_y = (((tile >> 4) + ys) & 15) << 4;
for (jj = 0; jj < width; jj++)
{
@@ -1406,7 +1253,9 @@ void snes_ppu_device::update_objects_rto( uint16_t curline )
break;
xs = (hflip) ? (width - 1 - jj) : jj;
- m_oam_tilelist[time_over - 1].tileaddr = name_sel + tile + table_obj_offset[ys][xs] + line;
+ m_oam_tilelist[time_over - 1].tileaddr = name_sel + ((character_y + ((character_x + xs) & 15)) << 4);
+ m_oam_tilelist[time_over - 1].tileaddr &= 0x7ff0;
+ m_oam_tilelist[time_over - 1].tileaddr += line;
m_oam_tilelist[time_over - 1].hflip = hflip;
m_oam_tilelist[time_over - 1].x = xx;
m_oam_tilelist[time_over - 1].pal = pal;
@@ -1443,19 +1292,25 @@ void snes_ppu_device::update_objects( uint8_t priority_oam0, uint8_t priority_oa
m_scanlines[SNES_MAINSCREEN].enable = m_layer[SNES_OAM].main_bg_enabled;
m_scanlines[SNES_SUBSCREEN].enable = m_layer[SNES_OAM].sub_bg_enabled;
- m_scanlines[SNES_MAINSCREEN].clip = m_layer[SNES_OAM].main_window_enabled;
- m_scanlines[SNES_SUBSCREEN].clip = m_layer[SNES_OAM].sub_window_enabled;
if (!m_scanlines[SNES_MAINSCREEN].enable && !m_scanlines[SNES_SUBSCREEN].enable)
return;
- charaddr = m_layer[SNES_OAM].charmap << 13;
+ uint8_t window_above[256];
+ uint8_t window_below[256];
+ render_window(SNES_OAM, m_layer[SNES_OAM].main_window_enabled, window_above);
+ render_window(SNES_OAM, m_layer[SNES_OAM].sub_window_enabled, window_below);
+
+ charaddr = m_layer[SNES_OAM].charmap;
priority[0] = priority_oam0;
priority[1] = priority_oam1;
priority[2] = priority_oam2;
priority[3] = priority_oam3;
+ uint8_t palbuf[256] = {};
+ uint8_t pribuf[256] = {};
+
/* finally draw the tiles from the tilelist */
for (ii = 0; ii < 34; ii++)
{
@@ -1480,7 +1335,16 @@ void snes_ppu_device::update_objects( uint8_t priority_oam0, uint8_t priority_oa
#endif /* SNES_LAYER_DEBUG */
/* OAM tiles have fixed planes (4), no direct color and no hires, but otherwise work the same as BG ones */
- draw_tile(4, SNES_OAM, charaddr + m_oam_tilelist[tile].tileaddr, m_oam_tilelist[tile].x, pri, m_oam_tilelist[tile].hflip, 0, m_oam_tilelist[tile].pal, 0);
+ draw_oamtile(charaddr + m_oam_tilelist[tile].tileaddr, m_oam_tilelist[tile].x, pri, m_oam_tilelist[tile].hflip, m_oam_tilelist[tile].pal, palbuf, pribuf);
+ }
+
+ for (uint16_t x = 0; x < SNES_SCR_WIDTH; x++)
+ {
+ if (pribuf[x] == 0) continue;
+ uint16_t c = m_cgram[palbuf[x] % FIXED_COLOUR];
+ int blend = (palbuf[x] < 192) ? 1 : 0;
+ if (m_layer[SNES_OAM].main_bg_enabled && window_above[x] == 0) plot_above(x, SNES_OAM, pribuf[x], c, blend);
+ if (m_layer[SNES_OAM].sub_bg_enabled && window_below[x] == 0) plot_below(x, SNES_OAM, pribuf[x], c, blend);
}
}
@@ -1498,11 +1362,11 @@ void snes_ppu_device::update_mode_0( uint16_t curline )
return;
#endif /* SNES_LAYER_DEBUG */
- update_objects(3, 6, 9, 12);
update_line(curline, SNES_BG1, 0);
update_line(curline, SNES_BG2, 0);
update_line(curline, SNES_BG3, 0);
update_line(curline, SNES_BG4, 0);
+ update_objects(3, 6, 9, 12);
}
void snes_ppu_device::update_mode_1( uint16_t curline )
@@ -1514,17 +1378,17 @@ void snes_ppu_device::update_mode_1( uint16_t curline )
if (!m_bg_priority)
{
- update_objects(2, 4, 7, 10);
update_line(curline, SNES_BG1, 0);
update_line(curline, SNES_BG2, 0);
update_line(curline, SNES_BG3, 0);
+ update_objects(2, 4, 7, 10);
}
else
{
- update_objects(2, 3, 6, 9);
update_line(curline, SNES_BG1, 0);
update_line(curline, SNES_BG2, 0);
update_line(curline, SNES_BG3, 0);
+ update_objects(2, 3, 6, 9);
}
}
@@ -1535,9 +1399,9 @@ void snes_ppu_device::update_mode_2( uint16_t curline )
return;
#endif /* SNES_LAYER_DEBUG */
- update_objects(2, 4, 6, 8);
update_line(curline, SNES_BG1, 0);
update_line(curline, SNES_BG2, 0);
+ update_objects(2, 4, 6, 8);
}
void snes_ppu_device::update_mode_3( uint16_t curline )
@@ -1547,9 +1411,9 @@ void snes_ppu_device::update_mode_3( uint16_t curline )
return;
#endif /* SNES_LAYER_DEBUG */
- update_objects(2, 4, 6, 8);
update_line(curline, SNES_BG1, m_direct_color);
update_line(curline, SNES_BG2, 0);
+ update_objects(2, 4, 6, 8);
}
void snes_ppu_device::update_mode_4( uint16_t curline )
@@ -1559,9 +1423,9 @@ void snes_ppu_device::update_mode_4( uint16_t curline )
return;
#endif /* SNES_LAYER_DEBUG */
- update_objects(2, 4, 6, 8);
update_line(curline, SNES_BG1, m_direct_color);
update_line(curline, SNES_BG2, 0);
+ update_objects(2, 4, 6, 8);
}
void snes_ppu_device::update_mode_5( uint16_t curline )
@@ -1571,9 +1435,9 @@ void snes_ppu_device::update_mode_5( uint16_t curline )
return;
#endif /* SNES_LAYER_DEBUG */
- update_objects(2, 4, 6, 8);
update_line(curline, SNES_BG1, 0);
update_line(curline, SNES_BG2, 0);
+ update_objects(2, 4, 6, 8);
}
void snes_ppu_device::update_mode_6( uint16_t curline )
@@ -1583,8 +1447,8 @@ void snes_ppu_device::update_mode_6( uint16_t curline )
return;
#endif /* SNES_LAYER_DEBUG */
- update_objects(1, 3, 4, 6);
update_line(curline, SNES_BG1, 0);
+ update_objects(1, 3, 4, 6);
}
void snes_ppu_device::update_mode_7( uint16_t curline )
@@ -1596,14 +1460,14 @@ void snes_ppu_device::update_mode_7( uint16_t curline )
if (!m_mode7.extbg)
{
- update_objects(1, 3, 4, 5);
update_line_mode7(curline, SNES_BG1);
+ update_objects(1, 3, 4, 5);
}
else
{
- update_objects(2, 4, 6, 7);
update_line_mode7(curline, SNES_BG1);
update_line_mode7(curline, SNES_BG2);
+ update_objects(2, 4, 6, 7);
}
}
@@ -1629,7 +1493,7 @@ void snes_ppu_device::draw_screens( uint16_t curline )
}
/*********************************************
- * update_windowmasks()
+ * update_color_windowmasks()
*
* An example of how windows work:
* Win1: ...#####......
@@ -1641,201 +1505,63 @@ void snes_ppu_device::draw_screens( uint16_t curline )
* XNOR: ###...##...### ...###..###...
*********************************************/
-void snes_ppu_device::update_windowmasks( void )
+void snes_ppu_device::update_color_windowmasks( uint8_t mask, uint8_t *output )
{
- m_update_windows = 0; /* reset the flag */
-
- for (uint16_t ii = 0; ii < SNES_SCR_WIDTH; ii++)
+ layer_t &self = m_layer[SNES_COLOR];
+ uint8_t set = 0, clear = 0;
+ switch (mask)
{
- /* update obj & color windows */
- /* jj = layer */
- for (uint16_t jj = 0; jj < 6; jj++)
- {
- m_clipmasks[jj][ii] = 0xff; /* let's start from un-masked */
- int8_t w1 = -1;
- int8_t w2 = -1;
-
- if (m_layer[jj].window1_enabled)
- {
- /* Default to mask area inside */
- if ((ii < m_window1_left) || (ii > m_window1_right))
- w1 = 0;
- else
- w1 = 1;
-
- /* If mask area is outside then swap */
- if (m_layer[jj].window1_invert)
- w1 = !w1;
- }
-
- if (m_layer[jj].window2_enabled)
- {
- if ((ii < m_window2_left) || (ii > m_window2_right))
- w2 = 0;
- else
- w2 = 1;
- if (m_layer[jj].window2_invert)
- w2 = !w2;
- }
-
- /* mask if the appropriate expression is true */
- if (w1 >= 0 && w2 >= 0)
- {
- switch (m_layer[jj].wlog_mask)
- {
- case 0x00: /* OR */
- m_clipmasks[jj][ii] = (w1 | w2) ? 0x00 : 0xff;
- break;
- case 0x01: /* AND */
- m_clipmasks[jj][ii] = (w1 & w2) ? 0x00 : 0xff;
- break;
- case 0x02: /* XOR */
- m_clipmasks[jj][ii] = (w1 ^ w2) ? 0x00 : 0xff;
- break;
- case 0x03: /* XNOR */
- m_clipmasks[jj][ii] = !(w1 ^ w2) ? 0x00 : 0xff;
- break;
- }
- }
- else if (w1 >= 0)
- m_clipmasks[jj][ii] = w1 ? 0x00 : 0xff;
- else if (w2 >= 0)
- m_clipmasks[jj][ii] = w2 ? 0x00 : 0xff;
- }
+ case 0: memset(output, 1, 256); return; // always
+ case 1: set = 1; clear = 0; break;
+ case 2: set = 0; clear = 1; break;
+ case 3: memset(output, 0, 256); return; // never
}
-}
-
-/*********************************************
- * update_offsets()
- *
- * Update the offsets with the latest changes.
- * This is currently unused, but it could
- * possibly be handy for some minor optimization
- *********************************************/
-void snes_ppu_device::update_offsets( void )
-{
- int ii;
- for (ii = 0; ii < 4; ii++)
+ if (!self.window1_enabled && !self.window2_enabled)
{
- }
- m_update_offsets = 0;
-}
-
-/*****************************************
- * draw_blend()
- *
- * Routine for additive/subtractive blending
- * between the main and sub screens, i.e.
- * color math.
- *****************************************/
-
-inline void snes_ppu_device::draw_blend( uint16_t offset, uint16_t *colour, uint8_t prevent_color_math, uint8_t black_pen_clip, int switch_screens )
-{
-#if SNES_LAYER_DEBUG
- if (m_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 refresh_scanline */
- if (switch_screens && offset > 0)
- offset -= 1;
-
- if ((black_pen_clip == SNES_CLIP_ALWAYS) ||
- (black_pen_clip == SNES_CLIP_IN && m_clipmasks[SNES_COLOR][offset]) ||
- (black_pen_clip == SNES_CLIP_OUT && !m_clipmasks[SNES_COLOR][offset]))
- *colour = 0; //clip to black before color math
-
- if (prevent_color_math == SNES_CLIP_ALWAYS) // blending mode 3 == always OFF
+ memset(output, clear, 256);
return;
+ }
- if ((prevent_color_math == SNES_CLIP_NEVER) ||
- (prevent_color_math == SNES_CLIP_IN && !m_clipmasks[SNES_COLOR][offset]) ||
- (prevent_color_math == SNES_CLIP_OUT && m_clipmasks[SNES_COLOR][offset]))
+ if (self.window1_enabled && !self.window2_enabled)
{
- uint16_t r, g, b;
- struct SNES_SCANLINE *subscreen;
- int clip_max = 0; // if add then clip to 0x1f, if sub then clip to 0
-
-#if SNES_LAYER_DEBUG
- /* Toggle drawing of SNES_SUBSCREEN or SNES_MAINSCREEN */
- if (m_debug_options.draw_subscreen)
+ if (self.window1_invert)
{
- subscreen = switch_screens ? &m_scanlines[SNES_SUBSCREEN] : &m_scanlines[SNES_MAINSCREEN];
+ set ^= 1;
+ clear ^= 1;
}
- else
-#endif /* SNES_LAYER_DEBUG */
+ for (uint16_t x = 0; x < 256; x++)
{
- subscreen = switch_screens ? &m_scanlines[SNES_MAINSCREEN] : &m_scanlines[SNES_SUBSCREEN];
+ output[x] = (x >= m_window1_left && x <= m_window1_right) ? set : clear;
}
+ return;
+ }
- if (m_sub_add_mode) /* SNES_SUBSCREEN*/
+ if (self.window2_enabled && !self.window1_enabled)
+ {
+ if (self.window2_invert)
{
- if (!BIT(m_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(m_color_modes, 6) && (subscreen->buffer[offset] != m_cgram[FIXED_COLOUR]))
- {
- r >>= 1;
- g >>= 1;
- b >>= 1;
- }
+ set ^= 1;
+ clear ^= 1;
}
- else /* Fixed colour */
+ for (uint16_t x = 0; x < 256; x++)
{
- if (!BIT(m_color_modes, 7))
- {
- /* 0x00 add */
- r = (*colour & 0x1f) + (m_cgram[FIXED_COLOUR] & 0x1f);
- g = ((*colour & 0x3e0) >> 5) + ((m_cgram[FIXED_COLOUR] & 0x3e0) >> 5);
- b = ((*colour & 0x7c00) >> 10) + ((m_cgram[FIXED_COLOUR] & 0x7c00) >> 10);
- clip_max = 1;
- }
- else
- {
- /* 0x80: sub */
- r = (*colour & 0x1f) - (m_cgram[FIXED_COLOUR] & 0x1f);
- g = ((*colour & 0x3e0) >> 5) - ((m_cgram[FIXED_COLOUR] & 0x3e0) >> 5);
- b = ((*colour & 0x7c00) >> 10) - ((m_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(m_color_modes, 6))
- {
- r >>= 1;
- g >>= 1;
- b >>= 1;
- }
+ output[x] = (x >= m_window2_left && x <= m_window2_right) ? set : clear;
}
+ return;
+ }
- /* 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)
+ for (uint16_t x = 0; x < 256; x++)
+ {
+ uint8_t one_mask = ((x >= m_window1_left && x <= m_window1_right) ? 1 : 0) ^ self.window1_invert;
+ uint8_t two_mask = ((x >= m_window2_left && x <= m_window2_right) ? 1 : 0) ^ self.window2_invert;
+ switch (self.wlog_mask)
{
- if (r > 0x1f) r = 0x1f;
- if (g > 0x1f) g = 0x1f;
- if (b > 0x1f) b = 0x1f;
+ case 0: output[x] = (one_mask | two_mask) == 1 ? set : clear; break;
+ case 1: output[x] = (one_mask & two_mask) == 1 ? set : clear; break;
+ case 2: output[x] = (one_mask ^ two_mask) == 1 ? set : clear; break;
+ case 3: output[x] = (one_mask ^ two_mask) == 0 ? set : clear; break;
}
-
- *colour = ((r & 0x1f) | ((g & 0x1f) << 5) | ((b & 0x1f) << 10));
}
}
@@ -1871,12 +1597,8 @@ void snes_ppu_device::refresh_scanline( bitmap_rgb32 &bitmap, uint16_t curline )
bitmap.pix32(0, x) = rgb_t::black();
else
{
- /* Update clip window masks if necessary */
- if (m_update_windows)
- update_windowmasks();
- /* Update the offsets if necessary */
- if (m_update_offsets)
- update_offsets();
+ uint8_t window_above[256];
+ uint8_t window_below[256];
/* Clear priority */
memset(m_scanlines[SNES_MAINSCREEN].priority, 0, SNES_SCR_WIDTH);
@@ -1890,27 +1612,8 @@ void snes_ppu_device::refresh_scanline( bitmap_rgb32 &bitmap, uint16_t curline )
memset(m_scanlines[SNES_MAINSCREEN].blend_exception, 0, SNES_SCR_WIDTH);
memset(m_scanlines[SNES_SUBSCREEN].blend_exception, 0, SNES_SCR_WIDTH);
- /* Draw back colour */
- for (uint16_t ii = 0; ii < SNES_SCR_WIDTH; ii++)
- {
- if (m_mode == 5 || m_mode == 6 || m_pseudo_hires)
- m_scanlines[SNES_SUBSCREEN].buffer[ii] = m_cgram[0];
- else
- m_scanlines[SNES_SUBSCREEN].buffer[ii] = m_cgram[FIXED_COLOUR];
-
- m_scanlines[SNES_MAINSCREEN].buffer[ii] = m_cgram[0];
- }
-
- /* Prepare OAM for this scanline */
- update_objects_rto(curline);
-
- /* Draw scanline */
- draw_screens(curline);
-
- update_obsel();
-
- struct SNES_SCANLINE *scanline1 = &m_scanlines[SNES_MAINSCREEN];
- struct SNES_SCANLINE *scanline2 = &m_scanlines[SNES_SUBSCREEN];
+ struct SNES_SCANLINE *above = &m_scanlines[SNES_MAINSCREEN];
+ struct SNES_SCANLINE *below = &m_scanlines[SNES_SUBSCREEN];
#if SNES_LAYER_DEBUG
if (dbg_video(curline))
{
@@ -1921,87 +1624,87 @@ void snes_ppu_device::refresh_scanline( bitmap_rgb32 &bitmap, uint16_t curline )
/* Toggle drawing of SNES_SUBSCREEN or SNES_MAINSCREEN */
if (m_debug_options.draw_subscreen)
{
- scanline1 = &m_scanlines[SNES_SUBSCREEN];
- scanline2 = &m_scanlines[SNES_MAINSCREEN];
+ above = &m_scanlines[SNES_SUBSCREEN];
+ below = &m_scanlines[SNES_MAINSCREEN];
}
#endif
- /* Draw the scanline to screen */
+ const bool hires = m_mode == 5 || m_mode == 6 || m_pseudo_hires;
+ uint16_t above_color = m_cgram[0];
+ uint16_t below_color = hires ? m_cgram[0] : m_cgram[FIXED_COLOUR];
+ for (int x = 0; x < SNES_SCR_WIDTH; x++)
+ {
+ above->buffer[x] = above_color;
+ above->priority[x] = 0;
+ above->layer[x] = SNES_COLOR;
+
+ below->buffer[x] = below_color;
+ below->priority[x] = 0;
+ below->layer[x] = SNES_COLOR;
+ }
- int fade = m_screen_brightness;
- uint16_t prev_colour = 0;
+ update_color_windowmasks(m_clip_to_black, window_above);
+ update_color_windowmasks(m_prevent_color_math, window_below);
+
+ /* Draw backgrounds and OAM */
+ draw_screens(curline);
+
+ /* Prepare OAM for this scanline */
+ update_objects_rto(curline);
+
+ update_obsel();
+
+ /* Draw the scanline to screen */
+ uint16_t prev = 0;
+ uint16_t *luma = &m_light_table[m_screen_brightness][0];
for (int x = 0; x < SNES_SCR_WIDTH; x++)
{
- const bool hires = (m_mode != 5 && m_mode != 6 && !m_pseudo_hires) ? false : true;
-
/* in hires, the first pixel (of 512) is subscreen pixel, then the first mainscreen pixel follows, and so on... */
if (!hires)
{
- uint16_t c = scanline1->buffer[x];
-
- /* perform color math if the layer wants it (except if it's an object > 192) */
- if (!scanline1->blend_exception[x] && m_layer[scanline1->layer[x]].color_math)
- draw_blend(x, &c, m_prevent_color_math, m_clip_to_black, 0);
-
- int r = ((c & 0x1f) * fade) >> 4;
- int g = (((c & 0x3e0) >> 5) * fade) >> 4;
- int b = (((c & 0x7c00) >> 10) * fade) >> 4;
+ const uint16_t c = luma[pixel(x, above, below, window_above, window_below)];
+ const int r = (c & 0x1f);
+ const int g = (c & 0x3e0) >> 5;
+ const int b = (c & 0x7c00) >> 10;
bitmap.pix32(0, x * 2 + 0) = rgb_t(pal5bit(r), pal5bit(g), pal5bit(b));
bitmap.pix32(0, x * 2 + 1) = rgb_t(pal5bit(r), pal5bit(g), pal5bit(b));
}
+ else if (!blurring)
+ {
+ const uint16_t c0 = luma[pixel(x, below, above, window_above, window_below)];
+ const uint16_t c1 = luma[pixel(x, above, below, window_above, window_below)];
+ const int r0 = (c0 & 0x1f);
+ const int r1 = (c1 & 0x1f);
+ const int g0 = (c0 & 0x3e0) >> 5;
+ const int g1 = (c1 & 0x3e0) >> 5;
+ const int b0 = (c0 & 0x7c00) >> 10;
+ const int b1 = (c1 & 0x7c00) >> 10;
+
+ bitmap.pix32(0, x * 2 + 0) = rgb_t(pal5bit(r0), pal5bit(g0), pal5bit(b0));
+ bitmap.pix32(0, x * 2 + 1) = rgb_t(pal5bit(r1), pal5bit(g1), pal5bit(b1));
+ }
else
{
- /* prepare the pixel from main screen */
- uint16_t c = scanline1->buffer[x];
+ uint16_t curr = luma[pixel(x, below, above, window_above, window_below)];
- /* perform color math if the layer wants it (except if it's an object > 192) */
- if (!scanline1->blend_exception[x] && m_layer[scanline1->layer[x]].color_math)
- draw_blend(x, &c, m_prevent_color_math, m_clip_to_black, 0);
+ uint16_t c0 = (prev + curr - ((prev ^ curr) & 0x0421)) >> 1;
+ const int r0 = (c0 & 0x1f);
+ const int g0 = (c0 & 0x3e0) >> 5;
+ const int b0 = (c0 & 0x7c00) >> 10;
+ bitmap.pix32(0, x * 2 + 0) = rgb_t(pal5bit(r0), pal5bit(g0), pal5bit(b0));
- uint16_t tmp_col1 = c;
-
- /* prepare the pixel from sub screen */
- c = scanline2->buffer[x];
-
- /* in hires/pseudo-hires, subscreen pixels are blended as well: for each subscreen pixel, color math
- is applied if it had been applied to the previous mainscreen pixel. What happens at subscreen pixel 0
- (which has no previous mainscreen pixel) is undocumented. Until more info are discovered, we (arbitrarily)
- 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] && m_layer[scanline1->layer[0]].color_math)
- draw_blend(0, &c, m_prevent_color_math, m_clip_to_black, 1);
- else if (x > 0 && !scanline1->blend_exception[x - 1] && m_layer[scanline1->layer[x - 1]].color_math)
- draw_blend(x, &c, m_prevent_color_math, m_clip_to_black, 1);
-
- uint16_t tmp_col0 = c;
-
- /* average the first pixel if required, or draw it directly*/
- if (blurring)
- c = (prev_colour + tmp_col0 - ((prev_colour ^ tmp_col0) & 0x0421)) >> 1; // Hack code to mimic TV pixel blurring
- else
- c = tmp_col0;
+ prev = curr;
+ curr = luma[pixel(x, above, below, window_above, window_below)];
- int r = ((c & 0x1f) * fade) >> 4;
- int g = (((c & 0x3e0) >> 5) * fade) >> 4;
- int b = (((c & 0x7c00) >> 10) * fade) >> 4;
+ uint16_t c1 = (prev + curr - ((prev ^ curr) & 0x0421)) >> 1;
+ const int r1 = (c1 & 0x1f);
+ const int g1 = (c1 & 0x3e0) >> 5;
+ const int b1 = (c1 & 0x7c00) >> 10;
+ bitmap.pix32(0, x * 2 + 1) = rgb_t(pal5bit(r1), pal5bit(g1), pal5bit(b1));
- bitmap.pix32(0, x * 2 + 0) = rgb_t(pal5bit(r), pal5bit(g), pal5bit(b));
- prev_colour = tmp_col0;
-
- /* average the second pixel if required, or draw it directly*/
- if (blurring)
- c = (prev_colour + tmp_col1 - ((prev_colour ^ tmp_col1) & 0x0421)) >> 1; // Hack code to mimic TV pixel blurring
- else
- c = tmp_col1;
-
- r = ((c & 0x1f) * fade) >> 4;
- g = (((c & 0x3e0) >> 5) * fade) >> 4;
- b = (((c & 0x7c00) >> 10) * fade) >> 4;
-
- bitmap.pix32(0, x * 2 + 1) = rgb_t(pal5bit(r), pal5bit(g), pal5bit(b));
- prev_colour = tmp_col1;
+ prev = curr;
}
}
}
@@ -2009,6 +1712,44 @@ void snes_ppu_device::refresh_scanline( bitmap_rgb32 &bitmap, uint16_t curline )
g_profiler.stop();
}
+uint16_t snes_ppu_device::pixel(uint16_t x, SNES_SCANLINE *above, SNES_SCANLINE *below, uint8_t *window_above, uint8_t *window_below)
+{
+ if (!window_above[x]) above->buffer[x] = 0;
+ if (!window_below[x]) return above->buffer[x];
+ if (!m_layer[above->layer[x]].color_math) return above->buffer[x];
+ if (!m_sub_add_mode) return blend(above->buffer[x], m_cgram[FIXED_COLOUR], BIT(m_color_modes, 0) != 0 && window_above[x] != 0);
+ return blend(above->buffer[x], below->buffer[x], BIT(m_color_modes, 0) != 0 && window_above[x] != 0 && below->layer[x] != SNES_COLOR);
+}
+
+inline uint16_t snes_ppu_device::blend( uint16_t x, uint16_t y, bool halve )
+{
+ if (!BIT(m_color_modes, 1)) // add
+ {
+ if (!halve)
+ {
+ uint16_t sum = x + y;
+ uint16_t carry = (sum - ((x ^ y) & 0x0421)) & 0x8420;
+ return (sum - carry) | (carry - (carry >> 5));
+ }
+ else
+ {
+ return (x + y - ((x ^ y) & 0x0421)) >> 1;
+ }
+ }
+ else // sub
+ {
+ uint16_t diff = x - y + 0x8420;
+ uint16_t borrow = (diff - ((x ^ y) & 0x8420)) & 0x8420;
+ if (!halve)
+ {
+ return (diff - borrow) & (borrow - (borrow >> 5));
+ }
+ else
+ {
+ return (((diff - borrow) & (borrow - (borrow >> 5))) & 0x7bde) >> 1;
+ }
+ }
+}
/* CPU <-> PPU comms */
@@ -2308,6 +2049,11 @@ uint16_t snes_ppu_device::direct_color(uint16_t palette, uint16_t group)
+ (palette << 2 & 0x001c) + (group << 1 & 0x0002);
}
+void snes_ppu_device::set_current_vert(uint16_t value)
+{
+ m_beam.current_vert = value;
+}
+
void snes_ppu_device::cache_background()
{
for (int i = SNES_BG1; i <= SNES_BG4; i++)
@@ -2397,7 +2143,7 @@ void snes_ppu_device::update_video_mode()
m_layer[SNES_BG1].priority[0] = 2; m_layer[SNES_BG1].priority[1] = 5;
break;
case 7:
- if (m_mode7.extbg)
+ if (!m_mode7.extbg)
{
m_layer[SNES_BG1].tile_mode = SNES_COLOR_DEPTH_MODE7;
m_layer[SNES_BG2].tile_mode = SNES_COLOR_DEPTH_NONE;
@@ -2579,12 +2325,12 @@ void snes_ppu_device::write(address_space &space, uint32_t offset, uint8_t data)
m_oam.first_sprite = m_oam.priority_rotation ? (m_oam.address >> 1) & 127 : 0;
}
m_screen_disabled = data & 0x80;
- m_screen_brightness = (data & 0x0f) + 1;
+ m_screen_brightness = data & 0x0f;
break;
case OBSEL: /* Object size and data area designation */
- m_oam.next_charmap = (data & 0x03) << 1;
- m_oam.next_name_select = (((data & 0x18) >> 3) * 0x1000) << 1;
- m_oam.next_size = (data & 0xe0) >> 5;
+ m_oam.next_charmap = (data << 13) & 0x6000;
+ m_oam.next_name_select = (data >> 3) & 3;
+ m_oam.next_size = (data >> 5) & 7;
break;
case OAMADDL: /* Address for accessing OAM (low) */
m_oam.saved_address_low = data;
@@ -2666,7 +2412,6 @@ void snes_ppu_device::write(address_space &space, uint32_t offset, uint8_t data)
/* In Mode 7 we use mode7_last_scroll as Prev */
m_mode7.hor_offset = (data << 8) | (m_mode7_last_scroll & ~7) | ((m_mode7.hor_offset >> 8) & 7);
m_mode7_last_scroll = data;
- m_update_offsets = 1;
return;
case BG1VOFS: /* BG1 - vertical scroll (DW) */
/* In Mode 0->6 we use ppu_last_scroll as Prev */
@@ -2675,37 +2420,30 @@ void snes_ppu_device::write(address_space &space, uint32_t offset, uint8_t data)
/* In Mode 7 we use mode7_last_scroll as Prev */
m_mode7.ver_offset = (data << 8) | m_mode7_last_scroll;
m_mode7_last_scroll = data;
- m_update_offsets = 1;
return;
case BG2HOFS: /* BG2 - horizontal scroll (DW) */
m_layer[SNES_BG2].hoffs = (data << 8) | (m_ppu_last_scroll & ~7) | ((m_layer[SNES_BG2].hoffs >> 8) & 7);
m_ppu_last_scroll = data;
- m_update_offsets = 1;
return;
case BG2VOFS: /* BG2 - vertical scroll (DW) */
m_layer[SNES_BG2].voffs = (data << 8) | (m_ppu_last_scroll);
m_ppu_last_scroll = data;
- m_update_offsets = 1;
return;
case BG3HOFS: /* BG3 - horizontal scroll (DW) */
m_layer[SNES_BG3].hoffs = (data << 8) | (m_ppu_last_scroll & ~7) | ((m_layer[SNES_BG3].hoffs >> 8) & 7);
m_ppu_last_scroll = data;
- m_update_offsets = 1;
return;
case BG3VOFS: /* BG3 - vertical scroll (DW) */
m_layer[SNES_BG3].voffs = (data << 8) | (m_ppu_last_scroll);
m_ppu_last_scroll = data;
- m_update_offsets = 1;
return;
case BG4HOFS: /* BG4 - horizontal scroll (DW) */
m_layer[SNES_BG4].hoffs = (data << 8) | (m_ppu_last_scroll & ~7) | ((m_layer[SNES_BG4].hoffs >> 8) & 7);
m_ppu_last_scroll = data;
- m_update_offsets = 1;
return;
case BG4VOFS: /* BG4 - vertical scroll (DW) */
m_layer[SNES_BG4].voffs = (data << 8) | (m_ppu_last_scroll);
m_ppu_last_scroll = data;
- m_update_offsets = 1;
return;
case VMAIN: /* VRAM address increment value designation */
m_vram_fgr_high = (data & 0x80);
@@ -2810,7 +2548,6 @@ void snes_ppu_device::write(address_space &space, uint32_t offset, uint8_t data)
m_layer[SNES_BG2].window1_enabled = BIT(data, 5);
m_layer[SNES_BG2].window2_invert = BIT(data, 6);
m_layer[SNES_BG2].window2_enabled = BIT(data, 7);
- m_update_windows = 1;
}
break;
case W34SEL: /* Window mask settings for BG3-4 */
@@ -2824,7 +2561,6 @@ void snes_ppu_device::write(address_space &space, uint32_t offset, uint8_t data)
m_layer[SNES_BG4].window1_enabled = BIT(data, 5);
m_layer[SNES_BG4].window2_invert = BIT(data, 6);
m_layer[SNES_BG4].window2_enabled = BIT(data, 7);
- m_update_windows = 1;
}
break;
case WOBJSEL: /* Window mask settings for objects */
@@ -2838,35 +2574,30 @@ void snes_ppu_device::write(address_space &space, uint32_t offset, uint8_t data)
m_layer[SNES_COLOR].window1_enabled = BIT(data, 5);
m_layer[SNES_COLOR].window2_invert = BIT(data, 6);
m_layer[SNES_COLOR].window2_enabled = BIT(data, 7);
- m_update_windows = 1;
}
break;
case WH0: /* Window 1 left position */
if (data != PPU_REG(WH0))
{
m_window1_left = data;
- m_update_windows = 1;
}
break;
case WH1: /* Window 1 right position */
if (data != PPU_REG(WH1))
{
m_window1_right = data;
- m_update_windows = 1;
}
break;
case WH2: /* Window 2 left position */
if (data != PPU_REG(WH2))
{
m_window2_left = data;
- m_update_windows = 1;
}
break;
case WH3: /* Window 2 right position */
if (data != PPU_REG(WH3))
{
m_window2_right = data;
- m_update_windows = 1;
}
break;
case WBGLOG: /* Window mask logic for BG's */
@@ -2876,7 +2607,6 @@ void snes_ppu_device::write(address_space &space, uint32_t offset, uint8_t data)
m_layer[SNES_BG2].wlog_mask = (data & 0x0c) >> 2;
m_layer[SNES_BG3].wlog_mask = (data & 0x30) >> 4;
m_layer[SNES_BG4].wlog_mask = (data & 0xc0) >> 6;
- m_update_windows = 1;
}
break;
case WOBJLOG: /* Window mask logic for objects */
@@ -2884,7 +2614,6 @@ void snes_ppu_device::write(address_space &space, uint32_t offset, uint8_t data)
{
m_layer[SNES_OAM].wlog_mask = data & 0x03;
m_layer[SNES_COLOR].wlog_mask = (data & 0x0c) >> 2;
- m_update_windows = 1;
}
break;
case TM: /* Main screen designation */
@@ -2926,7 +2655,7 @@ void snes_ppu_device::write(address_space &space, uint32_t offset, uint8_t data)
#endif
break;
case CGADSUB: /* Addition/Subtraction designation for each screen */
- m_color_modes = data & 0xc0;
+ m_color_modes = (data >> 6) & 0x03;
m_layer[SNES_BG1].color_math = BIT(data, 0);
m_layer[SNES_BG2].color_math = BIT(data, 1);
m_layer[SNES_BG3].color_math = BIT(data, 2);
diff --git a/src/devices/video/snes_ppu.h b/src/devices/video/snes_ppu.h
index 6085b848965..471d3ea00d4 100644
--- a/src/devices/video/snes_ppu.h
+++ b/src/devices/video/snes_ppu.h
@@ -48,7 +48,7 @@ public:
void refresh_scanline(bitmap_rgb32 &bitmap, uint16_t curline);
- int16_t current_x() const { return screen().hpos() / m_htmult; }
+ int16_t current_x() const { return screen().hpos(); }
int16_t current_y() const { return screen().vpos(); }
void set_latch_hv(int16_t x, int16_t y);
@@ -72,7 +72,7 @@ public:
m_interlace = 1;
m_obj_interlace = 1;
}
- void set_current_vert(uint16_t value) { m_beam.current_vert = value; }
+ void set_current_vert(uint16_t value);
void set_first_sprite() { m_oam.first_sprite = m_oam.priority_rotation ? ((m_oam.address >> 1) & 127) : 0; }
protected:
@@ -152,7 +152,7 @@ protected:
uint8_t saved_address_high;
uint16_t address;
uint16_t priority_rotation;
- uint8_t next_charmap;
+ uint16_t next_charmap;
uint8_t next_size;
uint8_t size;
uint32_t next_name_select;
@@ -182,8 +182,8 @@ protected:
int16_t matrix_d;
int16_t origin_x;
int16_t origin_y;
- uint16_t hor_offset;
- uint16_t ver_offset;
+ int16_t hor_offset;
+ int16_t ver_offset;
uint8_t extbg;
} m_mode7;
@@ -192,6 +192,7 @@ protected:
uint16_t tile;
int16_t x, y;
uint8_t size, vflip, hflip, priority_bits, pal;
+ uint8_t nameselect;
int height, width;
};
@@ -266,14 +267,10 @@ protected:
inline uint16_t get_bgcolor(uint8_t direct_colors, uint16_t palette, uint8_t color);
inline void set_scanline_pixel(int screen, int16_t x, uint16_t color, uint8_t priority, uint8_t layer, int blend);
- inline void draw_bgtile_lores(uint8_t layer, int16_t ii, uint8_t colour, uint16_t pal, uint8_t direct_colors, uint8_t priority);
- inline void draw_bgtile_hires(uint8_t layer, int16_t ii, uint8_t colour, uint16_t pal, uint8_t direct_colors, uint8_t priority);
- inline void draw_oamtile(int16_t ii, uint8_t colour, uint16_t pal, uint8_t priority);
- inline void draw_tile(uint8_t planes, uint8_t layer, uint32_t tileaddr, int16_t x, uint8_t priority, uint8_t flip, uint8_t direct_colors, uint16_t pal, uint8_t hires);
- inline uint32_t get_tmap_addr(uint8_t layer, uint8_t hires, uint8_t tile_size, uint32_t base, uint32_t x, uint32_t y);
+ inline void draw_oamtile(uint32_t tileaddr, int16_t tile_x, uint8_t priority, uint8_t flip, uint16_t pal, uint8_t *palbuf, uint8_t *pribuf);
inline uint32_t get_tile(uint8_t layer_idx, uint32_t hoffset, uint32_t voffset);
- inline void update_line(uint16_t curline, uint8_t layer, uint8_t direct_colors);
- void update_line_mode7(uint16_t curline, uint8_t layer);
+ void update_line(uint16_t curline, uint8_t layer, uint8_t direct_colors);
+ void update_line_mode7(uint16_t curline, uint8_t layer_idx);
void update_obsel(void);
void oam_list_build(void);
int is_sprite_on_scanline(uint16_t curline, uint8_t sprite);
@@ -288,12 +285,15 @@ protected:
void update_mode_6(uint16_t curline);
void update_mode_7(uint16_t curline);
void draw_screens(uint16_t curline);
- void update_windowmasks(void);
- void update_offsets(void);
+ void render_window(uint16_t layer_idx, uint8_t enable, uint8_t *output);
+ inline void plot_above(uint16_t x, uint8_t source, uint8_t priority, uint16_t color, int blend_exception = 0);
+ inline void plot_below(uint16_t x, uint8_t source, uint8_t priority, uint16_t color, int blend_exception = 0);
+ void update_color_windowmasks(uint8_t mask, uint8_t *output);
void update_video_mode(void);
- void cache_background(void);
+ void cache_background();
+ uint16_t pixel(uint16_t x, SNES_SCANLINE *above, SNES_SCANLINE *below, uint8_t *window_above, uint8_t *window_below);
uint16_t direct_color(uint16_t palette, uint16_t group);
- inline void draw_blend(uint16_t offset, uint16_t *colour, uint8_t prevent_color_math, uint8_t black_pen_clip, int switch_screens);
+ inline uint16_t blend(uint16_t x, uint16_t y, bool halve);
void dynamic_res_change();
inline uint32_t get_vram_address();
@@ -307,6 +307,7 @@ protected:
std::unique_ptr<uint16_t[]> m_oam_ram; /* Object Attribute Memory */
std::unique_ptr<uint16_t[]> m_cgram; /* Palette RAM */
std::unique_ptr<uint8_t[]> m_vram; /* Video RAM (TODO: Should be 16-bit, but it's easier this way) */
+ std::unique_ptr<std::unique_ptr<uint16_t[]>[]> m_light_table; /* Luma ramp */
// device-level overrides
virtual void device_start() override;