summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices
diff options
context:
space:
mode:
author MooglyGuy <therealmogminer@gmail.com>2019-11-14 00:29:29 +0100
committer MooglyGuy <therealmogminer@gmail.com>2019-11-14 00:30:39 +0100
commit4c53d5611de42660a0d2232b6c911c7e94b6572f (patch)
tree5263083c5683b3ea39766a0acc25149f2b2650af /src/devices
parent6018a48cc2875249b061a4a3e05df0cf18601266 (diff)
-snes_ppu: Converted OAM code from bsnes ppu-fast, nw
Diffstat (limited to 'src/devices')
-rw-r--r--src/devices/video/snes_ppu.cpp836
-rw-r--r--src/devices/video/snes_ppu.h105
2 files changed, 334 insertions, 607 deletions
diff --git a/src/devices/video/snes_ppu.cpp b/src/devices/video/snes_ppu.cpp
index 1fd564beea5..5230fdff8d1 100644
--- a/src/devices/video/snes_ppu.cpp
+++ b/src/devices/video/snes_ppu.cpp
@@ -212,7 +212,6 @@ void snes_ppu_device::device_start()
m_vram = std::make_unique<uint8_t[]>(SNES_VRAM_SIZE);
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++)
@@ -272,20 +271,20 @@ void snes_ppu_device::device_start()
save_item(NAME(m_clipmasks[i]), i);
}
- save_item(NAME(m_oam.address_low));
- save_item(NAME(m_oam.address_high));
- save_item(NAME(m_oam.saved_address_low));
- save_item(NAME(m_oam.saved_address_high));
save_item(NAME(m_oam.address));
+ save_item(NAME(m_oam.base_address));
save_item(NAME(m_oam.priority_rotation));
- save_item(NAME(m_oam.next_charmap));
- save_item(NAME(m_oam.next_size));
- save_item(NAME(m_oam.size));
- save_item(NAME(m_oam.next_name_select));
+ save_item(NAME(m_oam.tile_data_address));
save_item(NAME(m_oam.name_select));
- save_item(NAME(m_oam.first_sprite));
- save_item(NAME(m_oam.flip));
+ save_item(NAME(m_oam.base_size));
+ save_item(NAME(m_oam.first));
save_item(NAME(m_oam.write_latch));
+ save_item(NAME(m_oam.data_latch));
+ save_item(NAME(m_oam.interlace));
+ save_item(NAME(m_oam.priority[0]));
+ save_item(NAME(m_oam.priority[1]));
+ save_item(NAME(m_oam.priority[2]));
+ save_item(NAME(m_oam.priority[3]));
save_item(NAME(m_beam.latch_horz));
save_item(NAME(m_beam.latch_vert));
@@ -306,27 +305,17 @@ void snes_ppu_device::device_start()
save_item(NAME(m_mode7.ver_offset));
save_item(NAME(m_mode7.extbg));
- for (int i = 0; i < ARRAY_LENGTH(m_oam_spritelist); i++)
+ for (int i = 0; i < ARRAY_LENGTH(m_objects); i++)
{
- save_item(NAME(m_oam_spritelist[i].tile), i);
- save_item(NAME(m_oam_spritelist[i].x), i);
- save_item(NAME(m_oam_spritelist[i].y), i);
- save_item(NAME(m_oam_spritelist[i].size), i);
- save_item(NAME(m_oam_spritelist[i].vflip), i);
- save_item(NAME(m_oam_spritelist[i].hflip), i);
- save_item(NAME(m_oam_spritelist[i].priority_bits), i);
- save_item(NAME(m_oam_spritelist[i].pal), i);
- save_item(NAME(m_oam_spritelist[i].height), i);
- save_item(NAME(m_oam_spritelist[i].width), i);
- }
-
- for (int i = 0; i < ARRAY_LENGTH(m_oam_tilelist); i++)
- {
- save_item(NAME(m_oam_tilelist[i].x), i);
- save_item(NAME(m_oam_tilelist[i].priority), i);
- save_item(NAME(m_oam_tilelist[i].pal), i);
- save_item(NAME(m_oam_tilelist[i].tileaddr), i);
- save_item(NAME(m_oam_tilelist[i].hflip), i);
+ save_item(NAME(m_objects[i].x), i);
+ save_item(NAME(m_objects[i].y), i);
+ save_item(NAME(m_objects[i].character), i);
+ save_item(NAME(m_objects[i].name_select), i);
+ save_item(NAME(m_objects[i].vflip), i);
+ save_item(NAME(m_objects[i].hflip), i);
+ save_item(NAME(m_objects[i].pri), i);
+ save_item(NAME(m_objects[i].pal), i);
+ save_item(NAME(m_objects[i].size), i);
}
save_item(NAME(m_mosaic_size));
@@ -347,12 +336,8 @@ void snes_ppu_device::device_start()
save_item(NAME(m_window2_left));
save_item(NAME(m_window2_right));
- save_item(NAME(m_update_windows));
- save_item(NAME(m_update_offsets));
- save_item(NAME(m_update_oam_list));
save_item(NAME(m_mode));
save_item(NAME(m_interlace));
- save_item(NAME(m_obj_interlace));
save_item(NAME(m_screen_brightness));
save_item(NAME(m_screen_disabled));
save_item(NAME(m_pseudo_hires));
@@ -376,7 +361,6 @@ void snes_ppu_device::device_start()
save_pointer(NAME(m_vram), SNES_VRAM_SIZE);
save_pointer(NAME(m_cgram), SNES_CGRAM_SIZE/2);
- save_pointer(NAME(m_oam_ram), SNES_OAM_SIZE/2);
}
void snes_ppu_device::device_reset()
@@ -386,7 +370,6 @@ void snes_ppu_device::device_reset()
#endif
/* Inititialize registers/variables */
- m_update_windows = 1;
m_beam.latch_vert = 0;
m_beam.latch_horz = 0;
m_beam.current_vert = 0;
@@ -420,12 +403,9 @@ void snes_ppu_device::device_reset()
/* Init Palette RAM */
memset((uint8_t *)m_cgram.get(), 0, SNES_CGRAM_SIZE);
- /* Init oam RAM */
- memset((uint8_t *)m_oam_ram.get(), 0xff, SNES_OAM_SIZE);
-
// other initializations to 0
memset(m_regs, 0, sizeof(m_regs));
- memset(m_oam_itemlist, 0, sizeof(m_oam_itemlist));
+ memset(m_objects, 0, sizeof(object) * 128);
memset(&m_oam, 0, sizeof(m_oam));
memset(&m_mode7, 0, sizeof(m_mode7));
@@ -461,67 +441,6 @@ void snes_ppu_device::device_reset()
memset(m_clipmasks[i], 0, SNES_SCR_WIDTH);
}
-
- for (auto & elem : m_oam_spritelist)
- {
- elem.tile = 0;
- elem.x = 0;
- elem.y = 0;
- elem.size = 0;
- elem.vflip = 0;
- elem.hflip = 0;
- elem.priority_bits = 0;
- elem.pal = 0;
- elem.height = 0;
- elem.width = 0;
- }
-
- for (auto & elem : m_oam_tilelist)
- {
- elem.x = 0;
- elem.priority = 0;
- elem.pal = 0;
- elem.tileaddr = 0;
- elem.hflip = 0;
- }
-}
-
-/*****************************************
- * get_bgcolor()
- *
- * Get the proper color (direct or from cgram)
- *****************************************/
-
-inline uint16_t snes_ppu_device::get_bgcolor( uint8_t direct_colors, uint16_t palette, uint8_t color )
-{
- uint16_t c;
-
- if (direct_colors)
- {
- /* format is 0 | BBb00 | GGGg0 | RRRr0, HW confirms that the data is zero padded. */
- c = ((color & 0x07) << 2) | ((color & 0x38) << 4) | ((color & 0xc0) << 7);
- c |= ((palette & 0x04) >> 1) | ((palette & 0x08) << 3) | ((palette & 0x10) << 8);
- }
- else
- c = m_cgram[(palette + color) % FIXED_COLOUR];
-
- return c;
-}
-
-/*****************************************
- * set_scanline_pixel()
- *
- * Store pixel color, priority, layer and
- * color math exception (for OAM) in the
- * proper scanline
- *****************************************/
-
-inline void snes_ppu_device::set_scanline_pixel( int screen, int16_t x, uint16_t color, uint8_t priority, uint8_t layer, int blend )
-{
- m_scanlines[screen].buffer[x] = color;
- m_scanlines[screen].priority[x] = priority;
- m_scanlines[screen].layer[x] = layer;
- m_scanlines[screen].blend_exception[x] = blend;
}
/*************************************************************************************************
@@ -592,53 +511,6 @@ void snes_ppu_device::render_window(uint16_t layer_idx, uint8_t enable, uint8_t
}
}
-/*****************************************
- * draw_oamtile()
- *
- * Draw 8 pixels from the expected OAM
- * tile into priority/palette buffers
- * by reading the color planes from vram
- * and by calling draw_oamtile
- *****************************************/
-
-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[4];
-
- for (int16_t ii = 0; ii < 2; ii++)
- {
- 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 (int16_t x = 0; x < 8; x++)
- {
- tile_x &= 0x01ff;
- if (tile_x < 256)
- {
- uint8_t colour = 0;
-
- if (flip)
- {
- for (int16_t jj = 0; jj < 4; jj++)
- colour |= BIT(plane[jj], x) ? (1 << jj) : 0;
- }
- else
- {
- 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;
- }
- }
- tile_x++;
- }
-}
-
/*************************************************************************************************
* SNES BG layers
*
@@ -978,365 +850,180 @@ void snes_ppu_device::update_line_mode7( uint16_t curline, uint8_t layer_idx )
}
}
-/*************************************************************************************************
- * SNES Sprites
- *
- * 1. First of all: sprites are drawn one line in advance. We emulate this by caching the
- * starting vram address, the sprite size and the "name select" at each line, and by using
- * them the next line to output the proper sprites - see update_obsel.
- *
- * 2. Each line can select its sprites among 128 available ones in oam_ram, hence we start
- * by creating a list of the available objects (each one with its x,y coordinate, its size,
- * its tile address, etc.) - see oam_list_build.
- *
- * 3. Next, we start finding out which sprites will appear in the line: starting from
- * FirstSprite, we count 32 OBJs which intersect our line and we store their indexes in the
- * oam_itemlist array (if more than 32 sprites intersect our line, we set the Range Over
- * flag); then, selecting among these sprites, we count 34 8x8 tiles which are visible
- * in our line (i.e. whose x coord is between -size and 256) and we store the corresponding
- * coordinates/priorities/palettes/etc. in the oam_tilelist array (if more than 34 tiles would
- * appear on screen, we set the Time Over flag).
- * Notice that when we populate oam_tilelist, we proceed from oam_itemlist[31] (or from the last
- * item which intersects the scanline), towards oam_itemlist[0], i.e. the higher tiles (say
- * oam_tilelist[34], or the last tile which appear on screen) will contain FirstSprite object,
- * or the sprites with closer index to FirstSprite which get displayed. This will play an
- * important role for sprite priority - see update_objects_rto.
- *
- * 4. All the above happens at the beginning of each VIDEO_UPDATE. When we finally draw the
- * scanline, we pass through the oam_tilelist and we store the displayed pixels in our scanline
- * buffer. Notice that, for each pixel of a SNES sprite, only the priority of the topmost sprite
- * is tested against the priority of the BG pixel (because FirstSprite is on top of FirstSprite+1,
- * which is on top of FirstSprite+2, etc., and therefore other sprites are already covered by the
- * topmost one). To emulate this, we draw each tile over the previous ones no matter what
- * priorities are (differently from what we did with BGs): in the end, we will have in each pixel z
- * its topmost sprite and scanline.priority[z] will be the topmost sprite priority as expected.
- * Of course, sprite drawing must happen before BG drawing, so that afterwords BG pixels properly
- * test their priority with the one of the correct sprite - see update_objects.
- *************************************************************************************************/
-
-
/*********************************************
- * update_obsel()
+ * update_objects()
*
- * Update sprite settings for next line.
+ * Update an entire line of sprites.
*********************************************/
-void snes_ppu_device::update_obsel( void )
+void snes_ppu_device::update_objects( uint16_t curline )
{
- m_layer[SNES_OAM].charmap = m_oam.next_charmap;
- m_oam.name_select = m_oam.next_name_select;
-
- if (m_oam.size != m_oam.next_size)
- {
- m_oam.size = m_oam.next_size;
- m_update_oam_list = 1;
- }
-}
+#if SNES_LAYER_DEBUG
+ if (m_debug_options.bg_disabled[SNES_OAM])
+ return;
+#endif /* SNES_LAYER_DEBUG */
-/*********************************************
- * oam_list_build()
- *
- * Build a list of the available obj in OAM ram.
- *********************************************/
+ if (!m_layer[SNES_OAM].main_bg_enabled && !m_layer[SNES_OAM].sub_bg_enabled)
+ return;
-void snes_ppu_device::oam_list_build( void )
-{
- uint8_t *oamram = (uint8_t *)m_oam_ram.get();
- int16_t oam = 0x1ff;
- uint16_t oam_extra = oam + 0x20;
- uint16_t extra = 0;
- int ii;
+ 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);
- m_update_oam_list = 0; // eventually, we can optimize the code by only calling this function when there is a change in size
+ uint32_t item_count = 0;
+ uint32_t tile_count = 0;
+ object_item items[32];
+ object_tile tiles[34];
+ memset(items, 0, sizeof(object_item) * 32);
+ memset(tiles, 0, sizeof(object_tile) * 34);
- for (ii = 127; ii >= 0; ii--)
+ for (uint32_t n = 0; n < 128; n++)
{
- if (((ii + 1) % 4) == 0)
- extra = oamram[oam_extra--];
-
- m_oam_spritelist[ii].vflip = (oamram[oam] & 0x80) >> 7;
- m_oam_spritelist[ii].hflip = (oamram[oam] & 0x40) >> 6;
- m_oam_spritelist[ii].priority_bits = (oamram[oam] & 0x30) >> 4;
- m_oam_spritelist[ii].pal = 128 + ((oamram[oam] & 0x0e) << 3);
- m_oam_spritelist[ii].tile = (oamram[oam--] & 0x1) << 8;
- m_oam_spritelist[ii].tile |= oamram[oam--];
- m_oam_spritelist[ii].y = oamram[oam--] + 1;
- m_oam_spritelist[ii].x = oamram[oam--];
- m_oam_spritelist[ii].size = (extra & 0x80) >> 7;
- extra <<= 1;
- m_oam_spritelist[ii].x |= ((extra & 0x80) << 1);
- extra <<= 1;
-
- m_oam_spritelist[ii].y *= m_obj_interlace;
- m_oam_spritelist[ii].y &= 0x1ff;
-
- m_oam_spritelist[ii].x &= 0x1ff;
-
- /* Determine object size */
- switch (m_oam.size)
+ object_item item = { true, (uint8_t)((m_oam.first + n) & 0x7f), 0, 0 };
+ const object &obj = m_objects[item.index];
+
+ if (obj.size == 0)
{
- case 0: /* 8x8 or 16x16 */
- m_oam_spritelist[ii].width = m_oam_spritelist[ii].size ? 2 : 1;
- m_oam_spritelist[ii].height = m_oam_spritelist[ii].size ? 2 : 1;
- break;
- case 1: /* 8x8 or 32x32 */
- m_oam_spritelist[ii].width = m_oam_spritelist[ii].size ? 4 : 1;
- m_oam_spritelist[ii].height = m_oam_spritelist[ii].size ? 4 : 1;
- break;
- case 2: /* 8x8 or 64x64 */
- m_oam_spritelist[ii].width = m_oam_spritelist[ii].size ? 8 : 1;
- m_oam_spritelist[ii].height = m_oam_spritelist[ii].size ? 8 : 1;
- break;
- case 3: /* 16x16 or 32x32 */
- m_oam_spritelist[ii].width = m_oam_spritelist[ii].size ? 4 : 2;
- m_oam_spritelist[ii].height = m_oam_spritelist[ii].size ? 4 : 2;
- break;
- case 4: /* 16x16 or 64x64 */
- m_oam_spritelist[ii].width = m_oam_spritelist[ii].size ? 8 : 2;
- m_oam_spritelist[ii].height = m_oam_spritelist[ii].size ? 8 : 2;
- break;
- case 5: /* 32x32 or 64x64 */
- m_oam_spritelist[ii].width = m_oam_spritelist[ii].size ? 8 : 4;
- m_oam_spritelist[ii].height = m_oam_spritelist[ii].size ? 8 : 4;
- break;
- case 6: /* undocumented: 16x32 or 32x64 */
- m_oam_spritelist[ii].width = m_oam_spritelist[ii].size ? 4 : 2;
- m_oam_spritelist[ii].height = m_oam_spritelist[ii].size ? 8 : 4;
- if (m_obj_interlace && !m_oam_spritelist[ii].size)
- m_oam_spritelist[ii].height = 2;
- break;
- case 7: /* undocumented: 16x32 or 32x32 */
- m_oam_spritelist[ii].width = m_oam_spritelist[ii].size ? 4 : 2;
- m_oam_spritelist[ii].height = m_oam_spritelist[ii].size ? 4 : 4;
- if (m_obj_interlace && !m_oam_spritelist[ii].size)
- m_oam_spritelist[ii].height = 2;
- break;
- default:
- /* we should never enter here... */
- logerror("Object size unsupported: %d\n", m_oam.size);
- break;
+ static const uint8_t s_widths[8] = { 8, 8, 8, 16, 16, 32, 16, 16 };
+ static const uint8_t s_heights[8] = { 8, 8, 8, 16, 16, 32, 32, 32 };
+ item.width = s_widths [m_oam.base_size];
+ item.height = s_heights[m_oam.base_size];
+ if (m_oam.interlace && m_oam.base_size >= 6) item.height = 16; // hardware quirk
+ }
+ else
+ {
+ static const uint8_t s_widths[8] = { 16, 32, 64, 32, 64, 64, 32, 32 };
+ static const uint8_t s_heights[8] = { 16, 32, 64, 32, 64, 64, 64, 32 };
+ item.width = s_widths [m_oam.base_size];
+ item.height = s_heights[m_oam.base_size];
}
- }
-}
-
-/*********************************************
- * is_sprite_on_scanline()
- *
- * Check if a given sprite intersects current
- * scanline
- *********************************************/
-
-int snes_ppu_device::is_sprite_on_scanline( uint16_t curline, uint8_t sprite )
-{
- //if sprite is entirely offscreen and doesn't wrap around to the left side of the screen,
- //then it is not counted. this *should* be 256, and not 255, even though dot 256 is offscreen.
- int spr_height = (m_oam_spritelist[sprite].height << 3);
-
- if (m_oam_spritelist[sprite].x > 256 && (m_oam_spritelist[sprite].x + (m_oam_spritelist[sprite].width << 3) - 1) < 512)
- return 0;
-
- if (curline >= m_oam_spritelist[sprite].y && curline < (m_oam_spritelist[sprite].y + spr_height))
- return 1;
-
- if ((m_oam_spritelist[sprite].y + spr_height) >= 256 && curline < ((m_oam_spritelist[sprite].y + spr_height) & 255))
- return 1;
-
- return 0;
-}
-
-/*********************************************
- * update_objects_rto()
- *
- * Determine which OBJs will be drawn on this
- * scanline.
- *********************************************/
-
-void snes_ppu_device::update_objects_rto( uint16_t curline )
-{
- int ii, jj, active_sprite;
- uint8_t range_over, time_over;
- int8_t xs, ys;
- uint8_t line;
- uint8_t height, width, vflip, hflip, priority, pal;
- uint16_t tile;
- int16_t x, y;
-
- oam_list_build();
-
- /* initialize counters */
- range_over = 0;
- time_over = 0;
-
- /* setup the proper line */
- curline /= m_interlace;
- curline *= m_obj_interlace;
- /* reset the list of first 32 objects which intersect current scanline */
- memset(m_oam_itemlist, 0xff, 32);
+ if (obj.x > SNES_SCR_WIDTH && (obj.x + item.width - 1) < (SNES_SCR_WIDTH * 2)) continue;
+ uint32_t height = item.height >> m_oam.interlace;
+ if ((curline >= obj.y && curline < (obj.y + height)) || ((obj.y + height) >= 256 && curline < ((obj.y + height) & 255)))
+ {
+ if (item_count++ >= 32) break;
+ items[item_count - 1] = item;
+ }
+ }
- /* populate the list of 32 objects */
- for (ii = 0; ii < 128; ii++)
+ for (int n = 31; n >= 0; n--)
{
- active_sprite = (ii + m_oam.first_sprite) & 0x7f;
-
- if (!is_sprite_on_scanline(curline, active_sprite))
- continue;
+ object_item &item = items[n];
+ if (!item.valid) continue;
- if (range_over++ >= 32)
- break;
-
- m_oam_itemlist[range_over - 1] = active_sprite;
- }
+ const object &obj = m_objects[item.index];
+ uint32_t tile_width = item.width >> 3;
+ uint16_t x = obj.x;
+ uint16_t y = (curline - obj.y) & 0xff;
+ if (m_oam.interlace) y <<= 1;
- /* reset the list of first 34 tiles to be drawn */
- for (ii = 0; ii < 34; ii++)
- m_oam_tilelist[ii].tileaddr = 0xffff;
+ if (obj.vflip)
+ {
+ if (item.width == item.height)
+ y = item.height - 1 - y;
+ else if (y < item.width)
+ y = item.width - 1 - y;
+ else
+ y = item.width + (item.width - 1) - (y - item.width);
+ }
- /* populate the list of 34 tiles */
- for (ii = 31; ii >= 0; ii--)
- {
- if (m_oam_itemlist[ii] == 0xff)
- continue;
-
- active_sprite = m_oam_itemlist[ii];
-
- tile = m_oam_spritelist[active_sprite].tile;
- x = m_oam_spritelist[active_sprite].x;
- y = m_oam_spritelist[active_sprite].y;
- height = m_oam_spritelist[active_sprite].height;
- width = m_oam_spritelist[active_sprite].width;
- vflip = m_oam_spritelist[active_sprite].vflip;
- hflip = m_oam_spritelist[active_sprite].hflip;
- priority = m_oam_spritelist[active_sprite].priority_bits;
- pal = m_oam_spritelist[active_sprite].pal;
-
- /* Adjust y, if past maximum position (for sprites which overlap between top & bottom) */
- if (y >= (0x100 - 16) * m_interlace)
- y -= (0x100) * m_interlace;
-
- if (curline >= y && curline < (y + (height << 3)))
+ if (m_oam.interlace)
{
- /* Only objects using tiles over 255 use name select */
- uint32_t name_sel = (tile < 256) ? 0 : ((1 + m_oam.name_select) << 12);
+ y = !obj.vflip ? (y + BIT(m_stat78, 7)) : (y - BIT(m_stat78, 7));
+ }
- ys = (curline - y) >> 3;
- line = (curline - y) % 8;
- if (vflip)
- {
- ys = height - ys - 1;
- line = 7 - line;
- }
+ x &= 0x1ff;
+ y &= 0x0ff;
- uint16_t character_x = tile & 15;
- uint16_t character_y = (((tile >> 4) + ys) & 15) << 4;
+ uint16_t tile_data_address = m_oam.tile_data_address;
+ if (obj.name_select) tile_data_address += (m_oam.name_select + 1) << 12;
+ uint16_t character_x = (obj.character & 15);
+ uint16_t character_y = (((obj.character >> 4) + (y >> 3)) & 15) << 4;
- for (jj = 0; jj < width; jj++)
- {
- int16_t xx = (x + (jj << 3)) & 0x1ff;
-
- if (x != 256 && xx >= 256 && (xx + 7) < 512)
- continue;
-
- if (time_over++ >= 34)
- break;
-
- xs = (hflip) ? (width - 1 - jj) : jj;
- 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;
- m_oam_tilelist[time_over - 1].priority = priority;
- }
+ for (uint32_t tile_x = 0; tile_x < tile_width; tile_x++)
+ {
+ uint32_t object_x = (x + (tile_x << 3)) & 0x1ff;
+ if (x != SNES_SCR_WIDTH && object_x >= SNES_SCR_WIDTH && (object_x + 7) < (SNES_SCR_WIDTH * 2)) continue;
+
+ object_tile tile = { true, 0, 0, 0, 0, 0, 0 };
+ tile.x = object_x;
+ tile.y = y;
+ tile.pri = obj.pri;
+ tile.pal = 128 + (obj.pal << 4);
+ tile.hflip = obj.hflip;
+
+ uint32_t mirror_x = !obj.hflip ? tile_x : (tile_width - 1 - tile_x);
+ uint32_t address = tile_data_address + ((character_y + ((character_x + mirror_x) & 15)) << 4);
+ address = ((address & 0x7ff0) + (y & 7)) << 1;
+ tile.data = m_vram[address + 0] << 0;
+ tile.data |= m_vram[address + 1] << 8;
+ tile.data |= m_vram[address + 16] << 16;
+ tile.data |= m_vram[address + 17] << 24;
+
+ if (tile_count++ >= 34) break;
+ tiles[tile_count - 1] = tile;
}
}
/* set Range Over flag if necessary */
- if (range_over > 32)
+ if (item_count > 32)
m_stat77 |= 0x40;
/* set Time Over flag if necessary */
- if (time_over > 34)
+ if (tile_count > 34)
m_stat77 |= 0x80;
-}
-
-/*********************************************
- * update_objects()
- *
- * Update an entire line of sprites.
- *********************************************/
-
-void snes_ppu_device::update_objects( uint8_t priority_oam0, uint8_t priority_oam1, uint8_t priority_oam2, uint8_t priority_oam3 )
-{
- uint8_t pri, priority[4];
- uint32_t charaddr;
- int ii;
-
-#if SNES_LAYER_DEBUG
- if (m_debug_options.bg_disabled[SNES_OAM])
- return;
-#endif /* SNES_LAYER_DEBUG */
-
- m_scanlines[SNES_MAINSCREEN].enable = m_layer[SNES_OAM].main_bg_enabled;
- m_scanlines[SNES_SUBSCREEN].enable = m_layer[SNES_OAM].sub_bg_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(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++)
+ for (uint32_t n = 0; n < 34; n++)
{
- int tile = ii;
-
-#if SNES_LAYER_DEBUG
- if (m_debug_options.sprite_reversed)
- tile = 33 - ii;
-#endif /* SNES_LAYER_DEBUG */
+ object_tile &tile = tiles[n];
+ if (!tile.valid) continue;
- if (m_oam_tilelist[tile].tileaddr == 0xffff)
- continue;
-
- pri = priority[m_oam_tilelist[tile].priority];
-
-#if SNES_LAYER_DEBUG
- if (m_debug_options.select_pri[SNES_OAM])
+ uint32_t tile_x = tile.x;
+ for (uint32_t x = 0; x < 8; x++)
{
- int oam_draw = m_debug_options.select_pri[SNES_OAM] - 1;
- if (oam_draw != m_oam_tilelist[tile].priority)
- continue;
+ tile_x &= 0x1ff;
+ if (tile_x < SNES_SCR_WIDTH)
+ {
+ uint32_t color, shift = tile.hflip ? x : (7 - x);
+ color = (tile.data >> (shift + 0)) & 0x01;
+ color |= (tile.data >> (shift + 7)) & 0x02;
+ color |= (tile.data >> (shift + 14)) & 0x04;
+ color |= (tile.data >> (shift + 21)) & 0x08;
+ if (color)
+ {
+ palbuf[tile_x] = tile.pal + color;
+ pribuf[tile_x] = m_oam.priority[tile.pri];
+ }
+ }
+ tile_x++;
}
-#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_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++)
+ for (uint32_t x = 0; x < SNES_SCR_WIDTH; x++)
{
- if (pribuf[x] == 0) continue;
- uint16_t c = m_cgram[palbuf[x]];
+ if (!pribuf[x]) continue;
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);
+ if (m_layer[SNES_OAM].main_bg_enabled && window_above[x] == 0) plot_above(x, SNES_OAM, pribuf[x], m_cgram[palbuf[x]], blend);
+ if (m_layer[SNES_OAM].sub_bg_enabled && window_below[x] == 0) plot_below(x, SNES_OAM, pribuf[x], m_cgram[palbuf[x]], blend);
}
}
+void snes_ppu_device::oam_address_reset( void )
+{
+ m_oam.address = m_oam.base_address;
+ oam_set_first_object();
+}
+
+void snes_ppu_device::oam_set_first_object( void )
+{
+ m_oam.first = (!m_oam.priority_rotation ? 0 : ((m_oam.address >> 2) & 0x7f));
+}
+
/*********************************************
* snes_update_mode_X()
@@ -1355,7 +1042,6 @@ void snes_ppu_device::update_mode_0( uint16_t curline )
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 )
@@ -1370,14 +1056,12 @@ void snes_ppu_device::update_mode_1( uint16_t curline )
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_line(curline, SNES_BG1, 0);
update_line(curline, SNES_BG2, 0);
update_line(curline, SNES_BG3, 0);
- update_objects(2, 3, 6, 9);
}
}
@@ -1390,7 +1074,6 @@ void snes_ppu_device::update_mode_2( uint16_t curline )
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 )
@@ -1402,7 +1085,6 @@ void snes_ppu_device::update_mode_3( uint16_t curline )
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 )
@@ -1414,7 +1096,6 @@ void snes_ppu_device::update_mode_4( uint16_t curline )
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 )
@@ -1426,7 +1107,6 @@ void snes_ppu_device::update_mode_5( uint16_t curline )
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 )
@@ -1437,7 +1117,6 @@ void snes_ppu_device::update_mode_6( uint16_t curline )
#endif /* SNES_LAYER_DEBUG */
update_line(curline, SNES_BG1, 0);
- update_objects(1, 3, 4, 6);
}
void snes_ppu_device::update_mode_7( uint16_t curline )
@@ -1450,13 +1129,11 @@ void snes_ppu_device::update_mode_7( uint16_t curline )
if (!m_mode7.extbg)
{
update_line_mode7(curline, SNES_BG1);
- update_objects(1, 3, 4, 5);
}
else
{
update_line_mode7(curline, SNES_BG1);
update_line_mode7(curline, SNES_BG2);
- update_objects(2, 4, 6, 7);
}
}
@@ -1635,13 +1312,11 @@ void snes_ppu_device::refresh_scanline( bitmap_rgb32 &bitmap, uint16_t curline )
update_color_windowmasks(m_clip_to_black, window_above);
update_color_windowmasks(m_prevent_color_math, window_below);
- /* Draw backgrounds and OAM */
+ /* Draw backgrounds */
draw_screens(curline);
- /* Prepare OAM for this scanline */
- update_objects_rto(curline);
-
- update_obsel();
+ /* Draw OAM */
+ update_objects(curline);
/* Draw the scanline to screen */
uint16_t prev = 0;
@@ -1906,53 +1581,84 @@ WRITE8_MEMBER( snes_ppu_device::vram_write )
Uniracers and it expects accesses to map to
offset 0x0218. Hence, following byuu's choice
we rerouted OAM accesses during active display
- to 0x0218 (0x010c in our snes_oam).
+ to 0x0218.
This is a hack, but it is more accurate than
writing to the 'expected' address set by
$2102,$2103.
-
- Notice that, since PPU_REG(OAMDATA) is never
- read/written directly, we use it as an index
- to choose the high/low byte of the snes_oam word.
*************************************************/
-READ8_MEMBER( snes_ppu_device::oam_read )
+uint8_t snes_ppu_device::read_oam( uint16_t address )
{
- offset &= 0x1ff;
-
- if (offset & 0x100)
- offset &= 0x10f;
+ if (!m_screen_disabled && screen().vpos() < m_beam.last_visible_line) address = m_oam.write_latch;
+ return read_object(address);
+}
- if (!m_screen_disabled)
+uint8_t snes_ppu_device::read_object( uint16_t address )
+{
+ if (!(address & 0x0200))
{
- uint16_t v = screen().vpos();
-
- if (v < m_beam.last_visible_line)
- offset = 0x010c;
+ uint8_t n = (address >> 2) & 0x7f;
+ object &obj = m_objects[n];
+ switch (address & 3)
+ {
+ case 0: return (uint8_t)obj.x;
+ case 1: return obj.y;
+ case 2: return obj.character;
+ default: return (obj.vflip << 7) | (obj.hflip << 6) | (obj.pri << 4) | (obj.pal << 1) | obj.name_select;
+ }
+ }
+ else
+ {
+ uint8_t n = (address & 0x1f) << 2;
+ return (BIT(m_objects[n + 0].x, 8) << 0) |
+ (BIT(m_objects[n + 1].x, 8) << 2) |
+ (BIT(m_objects[n + 2].x, 8) << 4) |
+ (BIT(m_objects[n + 3].x, 8) << 6) |
+ (m_objects[n + 0].size << 1) |
+ (m_objects[n + 1].size << 3) |
+ (m_objects[n + 2].size << 5) |
+ (m_objects[n + 3].size << 7);
}
-
- return (m_oam_ram[offset] >> (PPU_REG(OAMDATA) << 3)) & 0xff;
}
-WRITE8_MEMBER( snes_ppu_device::oam_write )
+void snes_ppu_device::write_oam( uint16_t address, uint8_t data )
{
- offset &= 0x1ff;
-
- if (offset & 0x100)
- offset &= 0x10f;
+ if (!m_screen_disabled && screen().vpos() < m_beam.last_visible_line) address = 0x0218;
+ return write_object(address, data);
+}
- if (!m_screen_disabled)
+void snes_ppu_device::write_object( uint16_t address, uint8_t data )
+{
+ if (!(address & 0x0200))
{
- uint16_t v = screen().vpos();
-
- if (v < m_beam.last_visible_line)
- offset = 0x010c;
+ const uint8_t n = (address >> 2) & 0x7f;
+ object &obj = m_objects[n];
+ switch (address & 3)
+ {
+ case 0: obj.x = (obj.x & 0x100) | data; return;
+ case 1: obj.y = data + 1; return; // +1: rendering happens one scanline late
+ case 2: obj.character = data; return;
+ default:
+ obj.name_select = BIT(data, 0);
+ obj.pal = (data >> 1) & 7;
+ obj.pri = (data >> 4) & 3;
+ obj.hflip = BIT(data, 6);
+ obj.vflip = BIT(data, 7);
+ return;
+ }
}
-
- if (!(PPU_REG(OAMDATA)))
- m_oam_ram[offset] = (m_oam_ram[offset] & 0xff00) | (data << 0);
else
- m_oam_ram[offset] = (m_oam_ram[offset] & 0x00ff) | (data << 8);
+ {
+ const uint8_t n = (address & 0x1f) << 2;
+ m_objects[n + 0].x = (m_objects[n + 0].x & 0xff) | ((data << 8) & 0x100);
+ m_objects[n + 1].x = (m_objects[n + 1].x & 0xff) | ((data << 6) & 0x100);
+ m_objects[n + 2].x = (m_objects[n + 2].x & 0xff) | ((data << 4) & 0x100);
+ m_objects[n + 3].x = (m_objects[n + 3].x & 0xff) | ((data << 2) & 0x100);
+ m_objects[n + 0].size = BIT(data, 1);
+ m_objects[n + 1].size = BIT(data, 3);
+ m_objects[n + 2].size = BIT(data, 5);
+ m_objects[n + 3].size = BIT(data, 7);
+ }
}
/*************************************************
@@ -2065,6 +1771,7 @@ void snes_ppu_device::update_video_mode()
switch (m_mode)
{
case 0:
+ {
m_layer[SNES_BG1].tile_mode = SNES_COLOR_DEPTH_2BPP;
m_layer[SNES_BG2].tile_mode = SNES_COLOR_DEPTH_2BPP;
m_layer[SNES_BG3].tile_mode = SNES_COLOR_DEPTH_2BPP;
@@ -2073,8 +1780,12 @@ void snes_ppu_device::update_video_mode()
m_layer[SNES_BG2].priority[0] = 7; m_layer[SNES_BG2].priority[1] = 10;
m_layer[SNES_BG3].priority[0] = 2; m_layer[SNES_BG3].priority[1] = 5;
m_layer[SNES_BG4].priority[0] = 1; m_layer[SNES_BG4].priority[1] = 4;
+ static const uint8_t s_oam_priority[4] = { 3, 6, 9, 12 };
+ memcpy(m_oam.priority, s_oam_priority, 4);
break;
+ }
case 1:
+ {
m_layer[SNES_BG1].tile_mode = SNES_COLOR_DEPTH_4BPP;
m_layer[SNES_BG2].tile_mode = SNES_COLOR_DEPTH_4BPP;
m_layer[SNES_BG3].tile_mode = SNES_COLOR_DEPTH_2BPP;
@@ -2084,54 +1795,80 @@ void snes_ppu_device::update_video_mode()
m_layer[SNES_BG1].priority[0] = 5; m_layer[SNES_BG1].priority[1] = 8;
m_layer[SNES_BG2].priority[0] = 4; m_layer[SNES_BG2].priority[1] = 7;
m_layer[SNES_BG3].priority[0] = 1; m_layer[SNES_BG3].priority[1] = 10;
+ static const uint8_t s_oam_priority[4] = { 2, 3, 6, 9 };
+ memcpy(m_oam.priority, s_oam_priority, 4);
}
else
{
m_layer[SNES_BG1].priority[0] = 6; m_layer[SNES_BG1].priority[1] = 9;
m_layer[SNES_BG2].priority[0] = 5; m_layer[SNES_BG2].priority[1] = 8;
m_layer[SNES_BG3].priority[0] = 1; m_layer[SNES_BG3].priority[1] = 3;
+ static const uint8_t s_oam_priority[4] = { 2, 4, 7, 10 };
+ memcpy(m_oam.priority, s_oam_priority, 4);
}
break;
+ }
case 2:
+ {
m_layer[SNES_BG1].tile_mode = SNES_COLOR_DEPTH_4BPP;
m_layer[SNES_BG2].tile_mode = SNES_COLOR_DEPTH_4BPP;
m_layer[SNES_BG3].tile_mode = SNES_COLOR_DEPTH_NONE;
m_layer[SNES_BG4].tile_mode = SNES_COLOR_DEPTH_NONE;
m_layer[SNES_BG1].priority[0] = 3; m_layer[SNES_BG1].priority[1] = 7;
m_layer[SNES_BG2].priority[0] = 1; m_layer[SNES_BG2].priority[1] = 5;
+ static const uint8_t s_oam_priority[4] = { 2, 4, 6, 8 };
+ memcpy(m_oam.priority, s_oam_priority, 4);
break;
+ }
case 3:
+ {
m_layer[SNES_BG1].tile_mode = SNES_COLOR_DEPTH_8BPP;
m_layer[SNES_BG2].tile_mode = SNES_COLOR_DEPTH_4BPP;
m_layer[SNES_BG3].tile_mode = SNES_COLOR_DEPTH_NONE;
m_layer[SNES_BG4].tile_mode = SNES_COLOR_DEPTH_NONE;
m_layer[SNES_BG1].priority[0] = 3; m_layer[SNES_BG1].priority[1] = 7;
m_layer[SNES_BG2].priority[0] = 1; m_layer[SNES_BG2].priority[1] = 5;
+ static const uint8_t s_oam_priority[4] = { 2, 4, 6, 8 };
+ memcpy(m_oam.priority, s_oam_priority, 4);
break;
+ }
case 4:
+ {
m_layer[SNES_BG1].tile_mode = SNES_COLOR_DEPTH_8BPP;
m_layer[SNES_BG2].tile_mode = SNES_COLOR_DEPTH_2BPP;
m_layer[SNES_BG3].tile_mode = SNES_COLOR_DEPTH_NONE;
m_layer[SNES_BG4].tile_mode = SNES_COLOR_DEPTH_NONE;
m_layer[SNES_BG1].priority[0] = 3; m_layer[SNES_BG1].priority[1] = 7;
m_layer[SNES_BG2].priority[0] = 1; m_layer[SNES_BG2].priority[1] = 5;
+ static const uint8_t s_oam_priority[4] = { 2, 4, 6, 8 };
+ memcpy(m_oam.priority, s_oam_priority, 4);
break;
+ }
case 5:
+ {
m_layer[SNES_BG1].tile_mode = SNES_COLOR_DEPTH_4BPP;
m_layer[SNES_BG2].tile_mode = SNES_COLOR_DEPTH_2BPP;
m_layer[SNES_BG3].tile_mode = SNES_COLOR_DEPTH_NONE;
m_layer[SNES_BG4].tile_mode = SNES_COLOR_DEPTH_NONE;
m_layer[SNES_BG1].priority[0] = 3; m_layer[SNES_BG1].priority[1] = 7;
m_layer[SNES_BG2].priority[0] = 1; m_layer[SNES_BG2].priority[1] = 5;
+ static const uint8_t s_oam_priority[4] = { 2, 4, 6, 8 };
+ memcpy(m_oam.priority, s_oam_priority, 4);
break;
+ }
case 6:
+ {
m_layer[SNES_BG1].tile_mode = SNES_COLOR_DEPTH_4BPP;
m_layer[SNES_BG2].tile_mode = SNES_COLOR_DEPTH_NONE;
m_layer[SNES_BG3].tile_mode = SNES_COLOR_DEPTH_NONE;
m_layer[SNES_BG4].tile_mode = SNES_COLOR_DEPTH_NONE;
m_layer[SNES_BG1].priority[0] = 2; m_layer[SNES_BG1].priority[1] = 5;
+ static const uint8_t s_oam_priority[4] = { 1, 3, 4, 6 };
+ memcpy(m_oam.priority, s_oam_priority, 4);
break;
+ }
case 7:
+ {
if (!m_mode7.extbg)
{
m_layer[SNES_BG1].tile_mode = SNES_COLOR_DEPTH_MODE7;
@@ -2139,6 +1876,8 @@ void snes_ppu_device::update_video_mode()
m_layer[SNES_BG3].tile_mode = SNES_COLOR_DEPTH_NONE;
m_layer[SNES_BG4].tile_mode = SNES_COLOR_DEPTH_NONE;
m_layer[SNES_BG1].priority[0] = 2; m_layer[SNES_BG1].priority[1] = 2;
+ static const uint8_t s_oam_priority[4] = { 1, 3, 4, 5 };
+ memcpy(m_oam.priority, s_oam_priority, 4);
}
else
{
@@ -2148,9 +1887,12 @@ void snes_ppu_device::update_video_mode()
m_layer[SNES_BG4].tile_mode = SNES_COLOR_DEPTH_NONE;
m_layer[SNES_BG1].priority[0] = 3; m_layer[SNES_BG1].priority[1] = 3;
m_layer[SNES_BG2].priority[0] = 1; m_layer[SNES_BG2].priority[1] = 5;
+ static const uint8_t s_oam_priority[4] = { 2, 4, 6, 7 };
+ memcpy(m_oam.priority, s_oam_priority, 4);
}
break;
}
+ }
}
uint8_t snes_ppu_device::read(address_space &space, uint32_t offset, uint8_t wrio_bit7)
@@ -2205,15 +1947,13 @@ uint8_t snes_ppu_device::read(address_space &space, uint32_t offset, uint8_t wri
return m_openbus_cb(space, 0); /* Return value is meaningless */
case ROAMDATA: /* Read data from OAM (DR) */
- m_ppu1_open_bus = oam_read(space, m_oam.address);
- PPU_REG(OAMDATA) = (PPU_REG(OAMDATA) + 1) % 2;
- if (!PPU_REG(OAMDATA))
- {
- m_oam.address++;
- m_oam.address &= 0x1ff;
- m_oam.first_sprite = m_oam.priority_rotation ? (m_oam.address >> 1) & 127 : 0;
- }
+ {
+ const uint8_t data = read_oam(m_oam.address);
+ m_oam.address = (m_oam.address + 1) & 0x3ff;
+ oam_set_first_object();
+ m_ppu1_open_bus = data;
return m_ppu1_open_bus;
+ }
case RVMDATAL: /* Read data from VRAM (low) */
{
uint32_t addr = get_vram_address();
@@ -2309,57 +2049,46 @@ void snes_ppu_device::write(address_space &space, uint32_t offset, uint8_t data)
case INIDISP: /* Initial settings for screen */
if ((m_screen_disabled & 0x80) && (!(data & 0x80))) //a 1->0 force blank transition causes a reset OAM address
{
- space.write_byte(OAMADDL, m_oam.saved_address_low);
- space.write_byte(OAMADDH, m_oam.saved_address_high);
- m_oam.first_sprite = m_oam.priority_rotation ? (m_oam.address >> 1) & 127 : 0;
+ oam_address_reset();
}
m_screen_disabled = data & 0x80;
m_screen_brightness = data & 0x0f;
break;
case OBSEL: /* Object size and data area designation */
- m_oam.next_charmap = (data << 13) & 0x6000;
- m_oam.next_name_select = (data >> 3) & 3;
- m_oam.next_size = (data >> 5) & 7;
+ m_oam.tile_data_address = (data << 13) & 0x6000;
+ m_oam.name_select = (data >> 3) & 3;
+ m_oam.base_size = (data >> 5) & 7;
break;
case OAMADDL: /* Address for accessing OAM (low) */
- m_oam.saved_address_low = data;
- m_oam.address = (m_oam.address & 0xff00) + data;
- m_oam.first_sprite = m_oam.priority_rotation ? (m_oam.address >> 1) & 127 : 0;
- PPU_REG(OAMDATA) = 0;
+ m_oam.base_address = (m_oam.base_address & 0x0200) | (data << 1);
+ oam_address_reset();
break;
case OAMADDH: /* Address for accessing OAM (high) */
- m_oam.saved_address_high = data;
- m_oam.address = (m_oam.address & 0x00ff) | ((data & 0x01) << 8);
- m_oam.priority_rotation = BIT(data, 7);
- m_oam.first_sprite = m_oam.priority_rotation ? (m_oam.address >> 1) & 127 : 0;
- PPU_REG(OAMDATA) = 0;
+ m_oam.base_address = ((data & 1) << 9) | (m_oam.base_address & 0x01fe);
+ m_oam.priority_rotation = (data >> 7) & 1;
+ oam_address_reset();
break;
case OAMDATA: /* Data for OAM write (DW) */
- if (m_oam.address >= 0x100)
- oam_write(space, m_oam.address, data);
- else
+ {
+ uint8_t latch_bit = m_oam.address & 1;
+ uint16_t address = m_oam.address;
+ m_oam.address = (m_oam.address + 1) & 0x03ff;
+ if (latch_bit == 0)
{
- if (!PPU_REG(OAMDATA))
- m_oam.write_latch = data;
- else
- {
- // in this case, we not only write data to the upper byte of the word,
- // but also m_oam.write_latch to the lower byte (recall that
- // PPU_REG(OAMDATA) is used to select high/low byte)
- oam_write(space, m_oam.address, data);
- PPU_REG(OAMDATA) = 0;
- oam_write(space, m_oam.address, m_oam.write_latch);
- PPU_REG(OAMDATA) = 1;
- }
+ m_oam.data_latch = data;
}
- PPU_REG(OAMDATA) = (PPU_REG(OAMDATA) + 1) % 2;
- if (!PPU_REG(OAMDATA))
+ if (address & 0x0200)
{
- m_oam.address++;
- m_oam.address &= 0x1ff;
- m_oam.first_sprite = m_oam.priority_rotation ? (m_oam.address >> 1) & 127 : 0;
+ write_oam(address, data);
}
+ else if (latch_bit == 1)
+ {
+ write_oam((address & ~1) + 0, m_oam.data_latch);
+ write_oam((address & ~1) + 1, data);
+ }
+ oam_set_first_object();
return;
+ }
case BGMODE: /* BG mode and character size settings */
m_mode = data & 0x07;
dynamic_res_change();
@@ -2672,7 +2401,7 @@ void snes_ppu_device::write(address_space &space, uint32_t offset, uint8_t data)
} break;
case SETINI: /* Screen mode/video select */
m_interlace = (data & 0x01) ? 2 : 1;
- m_obj_interlace = (data & 0x02) ? 2 : 1;
+ m_oam.interlace = BIT(data, 1);
// TODO: this should actually be always 240, then fill black remaining lines if bit is 0.
//m_beam.last_visible_line = (m_stat78 & SNES_PAL) ? 240 : (data & 0x04) ? 240 : 225;
m_beam.last_visible_line = (data & 0x04) ? 240 : 225;
@@ -2729,7 +2458,6 @@ uint8_t snes_ppu_device::dbg_video( uint16_t curline )
toggles = m_debug3.read_safe(0);
DEBUG_TOGGLE(2, m_debug_options.mosaic_disabled, ("Debug: Disabled Mosaic.\n"), ("Debug: Enabled Mosaic.\n"))
- m_debug_options.sprite_reversed = BIT(toggles, 7);
m_debug_options.select_pri[SNES_OAM] = (toggles & 0x70) >> 4;
#ifdef MAME_DEBUG
diff --git a/src/devices/video/snes_ppu.h b/src/devices/video/snes_ppu.h
index 471d3ea00d4..31f2b99af64 100644
--- a/src/devices/video/snes_ppu.h
+++ b/src/devices/video/snes_ppu.h
@@ -61,8 +61,9 @@ public:
bool screen_disabled() const { return bool(m_screen_disabled); }
uint8_t last_visible_line() const { return m_beam.last_visible_line; }
uint16_t current_vert() const { return m_beam.current_vert; }
- uint8_t saved_oam_address_low() const { return m_oam.saved_address_low; }
- uint8_t saved_oam_address_high() const { return m_oam.saved_address_high; }
+
+ void oam_address_reset();
+ void oam_set_first_object();
void clear_time_range_over() { m_stat77 &= 0x3f; }
void toggle_field() { m_stat78 ^= 0x80; }
@@ -70,10 +71,9 @@ public:
{
m_htmult = 1;
m_interlace = 1;
- m_obj_interlace = 1;
+ m_oam.interlace = 0;
}
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:
/* offset-per-tile modes */
@@ -146,20 +146,17 @@ protected:
struct
{
- uint8_t address_low;
- uint8_t address_high;
- uint8_t saved_address_low;
- uint8_t saved_address_high;
uint16_t address;
- uint16_t priority_rotation;
- uint16_t next_charmap;
- uint8_t next_size;
- uint8_t size;
- uint32_t next_name_select;
- uint32_t name_select;
- uint8_t first_sprite;
- uint8_t flip;
+ uint16_t base_address;
+ uint8_t priority_rotation;
+ uint16_t tile_data_address;
+ uint8_t name_select;
+ uint8_t base_size;
+ uint8_t first;
uint16_t write_latch;
+ uint8_t data_latch;
+ uint8_t interlace;
+ uint8_t priority[4];
} m_oam;
struct
@@ -187,27 +184,6 @@ protected:
uint8_t extbg;
} m_mode7;
- struct OAM
- {
- uint16_t tile;
- int16_t x, y;
- uint8_t size, vflip, hflip, priority_bits, pal;
- uint8_t nameselect;
- int height, width;
- };
-
- struct OAM m_oam_spritelist[SNES_SCR_WIDTH / 2];
-
- uint8_t m_oam_itemlist[32];
-
- struct TILELIST {
- int16_t x;
- uint16_t priority, pal, tileaddr;
- int hflip;
- };
-
- struct TILELIST m_oam_tilelist[34];
-
#if SNES_LAYER_DEBUG
struct DEBUGOPTS
{
@@ -217,7 +193,6 @@ protected:
uint8_t windows_disabled;
uint8_t mosaic_disabled;
uint8_t colormath_disabled;
- uint8_t sprite_reversed;
uint8_t select_pri[5];
};
struct DEBUGOPTS m_debug_options;
@@ -240,12 +215,8 @@ protected:
uint16_t m_mosaic_table[16][4096];
uint8_t m_clipmasks[6][SNES_SCR_WIDTH];
- uint8_t m_update_windows;
- uint8_t m_update_offsets;
- uint8_t m_update_oam_list;
uint8_t m_mode;
uint8_t m_interlace; //doubles the visible resolution
- uint8_t m_obj_interlace;
uint8_t m_screen_brightness;
uint8_t m_screen_disabled;
uint8_t m_pseudo_hires;
@@ -265,17 +236,42 @@ protected:
uint16_t m_vram_read_buffer;
uint16_t m_vmadd;
- 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_oamtile(uint32_t tileaddr, int16_t tile_x, uint8_t priority, uint8_t flip, uint16_t pal, uint8_t *palbuf, uint8_t *pribuf);
+ struct object_item
+ {
+ bool valid;
+ uint8_t index;
+ uint8_t width;
+ uint8_t height;
+ };
+
+ struct object_tile
+ {
+ bool valid;
+ uint16_t x;
+ uint8_t y;
+ uint8_t pri;
+ uint8_t pal;
+ uint8_t hflip;
+ uint32_t data;
+ };
+
+ struct object
+ {
+ uint16_t x;
+ uint8_t y;
+ uint8_t character;
+ uint8_t name_select;
+ uint8_t vflip;
+ uint8_t hflip;
+ uint8_t pri;
+ uint8_t pal;
+ uint8_t size;
+ };
+
inline uint32_t get_tile(uint8_t layer_idx, uint32_t hoffset, uint32_t voffset);
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);
- void update_objects_rto(uint16_t curline);
- void update_objects(uint8_t priority_oam0, uint8_t priority_oam1, uint8_t priority_oam2, uint8_t priority_oam3);
+ void update_objects(uint16_t curline);
void update_mode_0(uint16_t curline);
void update_mode_1(uint16_t curline);
void update_mode_2(uint16_t curline);
@@ -298,13 +294,16 @@ protected:
void dynamic_res_change();
inline uint32_t get_vram_address();
- DECLARE_READ8_MEMBER( oam_read );
- DECLARE_WRITE8_MEMBER( oam_write );
+ uint8_t read_oam(uint16_t address);
+ void write_oam(uint16_t address, uint8_t data);
+ uint8_t read_object(uint16_t address);
+ void write_object(uint16_t address, uint8_t data);
+
DECLARE_READ8_MEMBER( cgram_read );
DECLARE_WRITE8_MEMBER( cgram_write );
DECLARE_READ8_MEMBER( vram_read );
DECLARE_WRITE8_MEMBER( vram_write );
- std::unique_ptr<uint16_t[]> m_oam_ram; /* Object Attribute Memory */
+ object m_objects[128]; /* Object Attribute Memory (OAM) */
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 */