#ifdef PPU_CPP #include "list.cpp" void PPU::Sprite::address_reset() { self.regs.oam_addr = self.regs.oam_baseaddr; set_first_sprite(); } void PPU::Sprite::set_first_sprite() { regs.first_sprite = (self.regs.oam_priority == false ? 0 : (self.regs.oam_addr >> 2) & 127); } void PPU::Sprite::frame() { regs.time_over = false; regs.range_over = false; } void PPU::Sprite::scanline() { t.x = 0; t.y = self.vcounter(); t.item_count = 0; t.tile_count = 0; t.active = !t.active; auto oam_item = t.item[t.active]; auto oam_tile = t.tile[t.active]; if(t.y == (!self.regs.overscan ? 225 : 240) && self.regs.display_disable == false) address_reset(); if(t.y >= (!self.regs.overscan ? 224 : 239)) return; memset(oam_item, 0xff, 32); //default to invalid for(unsigned i = 0; i < 34; i++) oam_tile[i].x = 0xffff; //default to invalid for(unsigned i = 0; i < 128; i++) { unsigned sprite = (regs.first_sprite + i) & 127; if(on_scanline(list[sprite]) == false) continue; if(t.item_count++ >= 32) break; oam_item[t.item_count - 1] = sprite; } if(t.item_count > 0 && oam_item[t.item_count - 1] != 0xff) { ppu.regs.oam_iaddr = 0x0200 + (oam_item[t.item_count - 1] >> 2); } } bool PPU::Sprite::on_scanline(SpriteItem& sprite) { if(sprite.x > 256 && (sprite.x + sprite.width() - 1) < 512) return false; signed height = (regs.interlace == false ? sprite.height() : (sprite.height() >> 1)); if(t.y >= sprite.y && t.y < (sprite.y + height)) return true; if((sprite.y + height) >= 256 && t.y < ((sprite.y + height) & 255)) return true; return false; } void PPU::Sprite::run() { output.main.priority = 0; output.sub.priority = 0; auto oam_tile = t.tile[!t.active]; unsigned priority_table[] = { regs.priority0, regs.priority1, regs.priority2, regs.priority3 }; unsigned x = t.x++; for(unsigned n = 0; n < 34; n++) { auto tile = oam_tile[n]; if(tile.x == 0xffff) break; int px = x - sclip<9>(tile.x); if(px & ~7) continue; unsigned mask = 0x80 >> (tile.hflip == false ? px : 7 - px); unsigned color; color = ((bool)(tile.d0 & mask)) << 0; color |= ((bool)(tile.d1 & mask)) << 1; color |= ((bool)(tile.d2 & mask)) << 2; color |= ((bool)(tile.d3 & mask)) << 3; if(color) { if(regs.main_enable) { output.main.palette = tile.palette + color; output.main.priority = priority_table[tile.priority]; } if(regs.sub_enable) { output.sub.palette = tile.palette + color; output.sub.priority = priority_table[tile.priority]; } } } } void PPU::Sprite::tilefetch() { auto oam_item = t.item[t.active]; auto oam_tile = t.tile[t.active]; for(signed i = 31; i >= 0; i--) { if(oam_item[i] == 0xff) continue; auto sprite = list[oam_item[i]]; unsigned tile_width = sprite.width() >> 3; signed x = sprite.x; signed y = (t.y - sprite.y) & 0xff; if(regs.interlace) y <<= 1; if(sprite.vflip) { if(sprite.width() == sprite.height()) { y = (sprite.height() - 1) - y; } else if(y < sprite.width()) { y = (sprite.width() - 1) - y; } else { y = sprite.width() + ((sprite.width() - 1) - (y - sprite.width())); } } if(regs.interlace) { y = (sprite.vflip == false ? y + self.field() : y - self.field()); } x &= 511; y &= 255; uint16 tiledata_addr = regs.tiledata_addr; uint16 chrx = (sprite.character >> 0) & 15; uint16 chry = (sprite.character >> 4) & 15; if(sprite.nameselect) { tiledata_addr += (256 * 32) + (regs.nameselect << 13); } chry += (y >> 3); chry &= 15; chry <<= 4; for(unsigned tx = 0; tx < tile_width; tx++) { unsigned sx = (x + (tx << 3)) & 511; if(x != 256 && sx >= 256 && (sx + 7) < 512) continue; if(t.tile_count++ >= 34) break; unsigned n = t.tile_count - 1; oam_tile[n].x = sx; oam_tile[n].priority = sprite.priority; oam_tile[n].palette = 128 + (sprite.palette << 4); oam_tile[n].hflip = sprite.hflip; unsigned mx = (sprite.hflip == false) ? tx : ((tile_width - 1) - tx); unsigned pos = tiledata_addr + ((chry + ((chrx + mx) & 15)) << 5); uint16 addr = (pos & 0xffe0) + ((y & 7) * 2); oam_tile[n].d0 = ppu.vram[addr + 0]; oam_tile[n].d1 = ppu.vram[addr + 1]; self.add_clocks(2); oam_tile[n].d2 = ppu.vram[addr + 16]; oam_tile[n].d3 = ppu.vram[addr + 17]; self.add_clocks(2); } } if(t.tile_count < 34) self.add_clocks((34 - t.tile_count) * 4); regs.time_over |= (t.tile_count > 34); regs.range_over |= (t.item_count > 32); } void PPU::Sprite::reset() { for(unsigned i = 0; i < 128; i++) { list[i].x = 0; list[i].y = 0; list[i].character = 0; list[i].nameselect = 0; list[i].vflip = 0; list[i].hflip = 0; list[i].priority = 0; list[i].palette = 0; list[i].size = 0; } synchronize(); t.x = 0; t.y = 0; t.item_count = 0; t.tile_count = 0; t.active = 0; for(unsigned n = 0; n < 2; n++) { memset(t.item[n], 0, 32); for(unsigned i = 0; i < 34; i++) { t.tile[n][i].x = 0; t.tile[n][i].priority = 0; t.tile[n][i].palette = 0; t.tile[n][i].hflip = 0; t.tile[n][i].d0 = 0; t.tile[n][i].d1 = 0; t.tile[n][i].d2 = 0; t.tile[n][i].d3 = 0; } } regs.main_enable = random(false); regs.sub_enable = random(false); regs.interlace = random(false); regs.base_size = random(0); regs.nameselect = random(0); regs.tiledata_addr = (random(0x0000) & 3) << 14; regs.first_sprite = 0; regs.priority0 = 0; regs.priority1 = 0; regs.priority2 = 0; regs.priority3 = 0; regs.time_over = false; regs.range_over = false; output.main.palette = 0; output.main.priority = 0; output.sub.palette = 0; output.sub.priority = 0; } PPU::Sprite::Sprite(PPU& self) : self(self) { } #endif