From de541003e1a50baeaf40410065bd7415d587a5f8 Mon Sep 17 00:00:00 2001 From: MooglyGuy Date: Tue, 5 Nov 2019 22:18:26 +0100 Subject: -snes_ppu: Ported over more of bsnes's ppu-fast implementation. Fixes many bugs, possibly causes more. [byuu, Ryan Holtz] --- src/devices/video/snes_ppu.cpp | 1003 +++++++++++++++------------------------- src/devices/video/snes_ppu.h | 33 +- 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(SNES_CGRAM_SIZE/2); m_oam_ram = std::make_unique(SNES_OAM_SIZE/2); + m_light_table = std::make_unique[]>(16); + for (uint8_t l = 0; l < 16; l++) + { + m_light_table[l] = std::make_unique(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 m_oam_ram; /* Object Attribute Memory */ std::unique_ptr m_cgram; /* Palette RAM */ std::unique_ptr m_vram; /* Video RAM (TODO: Should be 16-bit, but it's easier this way) */ + std::unique_ptr[]> m_light_table; /* Luma ramp */ // device-level overrides virtual void device_start() override; -- cgit v1.2.3