diff options
author | MooglyGuy <therealmogminer@gmail.com> | 2019-11-14 00:29:29 +0100 |
---|---|---|
committer | MooglyGuy <therealmogminer@gmail.com> | 2019-11-14 00:30:39 +0100 |
commit | 4c53d5611de42660a0d2232b6c911c7e94b6572f (patch) | |
tree | 5263083c5683b3ea39766a0acc25149f2b2650af /src/devices | |
parent | 6018a48cc2875249b061a4a3e05df0cf18601266 (diff) |
-snes_ppu: Converted OAM code from bsnes ppu-fast, nw
Diffstat (limited to 'src/devices')
-rw-r--r-- | src/devices/video/snes_ppu.cpp | 836 | ||||
-rw-r--r-- | src/devices/video/snes_ppu.h | 105 |
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 */ |