#include "oam.cpp" auto PPU::Object::addressReset() -> void { ppu.io.oamAddress = ppu.io.oamBaseAddress; setFirstSprite(); } auto PPU::Object::setFirstSprite() -> void { io.firstSprite = !ppu.io.oamPriority ? 0 : uint(ppu.io.oamAddress >> 2); } auto PPU::Object::frame() -> void { io.timeOver = false; io.rangeOver = false; } auto PPU::Object::scanline() -> void { latch.firstSprite = io.firstSprite; t.x = 0; t.y = ppu.vcounter(); t.itemCount = 0; t.tileCount = 0; t.active = !t.active; auto oamItem = t.item[t.active]; auto oamTile = t.tile[t.active]; for(uint n : range(32)) oamItem[n].valid = false; for(uint n : range(34)) oamTile[n].valid = false; if(t.y == ppu.vdisp() && !ppu.io.displayDisable) addressReset(); if(t.y >= ppu.vdisp() - 1 || ppu.io.displayDisable) return; } auto PPU::Object::evaluate(uint7 index) -> void { if(ppu.io.displayDisable) return; if(t.itemCount > 32) return; auto oamItem = t.item[t.active]; auto oamTile = t.tile[t.active]; uint7 sprite = latch.firstSprite + index; if(!onScanline(oam.object[sprite])) return; ppu.latch.oamAddress = sprite; if(t.itemCount++ < 32) { oamItem[t.itemCount - 1] = {true, sprite}; } } auto PPU::Object::onScanline(PPU::OAM::Object& sprite) -> bool { if(sprite.x > 256 && sprite.x + sprite.width() - 1 < 512) return false; uint height = sprite.height() >> io.interlace; 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; } auto PPU::Object::run() -> void { output.above.priority = 0; output.below.priority = 0; auto oamTile = t.tile[!t.active]; uint x = t.x++; for(uint n : range(34)) { const auto& tile = oamTile[n]; if(!tile.valid) break; int px = x - (int9)tile.x; if(px & ~7) continue; uint color = 0, shift = tile.hflip ? px : 7 - px; color += tile.data >> shift + 0 & 1; color += tile.data >> shift + 7 & 2; color += tile.data >> shift + 14 & 4; color += tile.data >> shift + 21 & 8; if(color) { if(io.aboveEnable) { output.above.palette = tile.palette + color; output.above.priority = io.priority[tile.priority]; } if(io.belowEnable) { output.below.palette = tile.palette + color; output.below.priority = io.priority[tile.priority]; } } } } auto PPU::Object::fetch() -> void { auto oamItem = t.item[t.active]; auto oamTile = t.tile[t.active]; for(uint i : reverse(range(32))) { if(!oamItem[i].valid) continue; if(ppu.io.displayDisable || ppu.vcounter() >= ppu.vdisp() - 1) { ppu.step(8); continue; } ppu.latch.oamAddress = 0x0200 + (oamItem[i].index >> 2); const auto& sprite = oam.object[oamItem[i].index]; uint tileWidth = sprite.width() >> 3; int x = sprite.x; int y = t.y - sprite.y & 0xff; if(io.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(io.interlace) { y = !sprite.vflip ? y + ppu.field() : y - ppu.field(); } x &= 511; y &= 255; uint16 tiledataAddress = io.tiledataAddress; if(sprite.nameselect) tiledataAddress += 1 + io.nameselect << 12; uint16 chrx = (sprite.character & 15); uint16 chry = ((sprite.character >> 4) + (y >> 3) & 15) << 4; for(uint tx : range(tileWidth)) { uint sx = x + (tx << 3) & 511; if(x != 256 && sx >= 256 && sx + 7 < 512) continue; if(t.tileCount++ >= 34) break; uint n = t.tileCount - 1; oamTile[n].valid = true; oamTile[n].x = sx; oamTile[n].priority = sprite.priority; oamTile[n].palette = 128 + (sprite.palette << 4); oamTile[n].hflip = sprite.hflip; uint mx = !sprite.hflip ? tx : tileWidth - 1 - tx; uint pos = tiledataAddress + ((chry + (chrx + mx & 15)) << 4); uint16 address = (pos & 0xfff0) + (y & 7); if(!ppu.io.displayDisable) oamTile[n].data = ppu.vram[address + 0] << 0; ppu.step(4); if(!ppu.io.displayDisable) oamTile[n].data |= ppu.vram[address + 8] << 16; ppu.step(4); } } io.timeOver |= (t.tileCount > 34); io.rangeOver |= (t.itemCount > 32); } auto PPU::Object::power() -> void { for(auto& object : oam.object) { object.x = 0; object.y = 0; object.character = 0; object.nameselect = 0; object.vflip = 0; object.hflip = 0; object.priority = 0; object.palette = 0; object.size = 0; } t.x = 0; t.y = 0; t.itemCount = 0; t.tileCount = 0; t.active = 0; for(uint p : range(2)) { for(uint n : range(32)) { t.item[p][n].valid = false; t.item[p][n].index = 0; } for(uint n : range(34)) { t.tile[p][n].valid = false; t.tile[p][n].x = 0; t.tile[p][n].priority = 0; t.tile[p][n].palette = 0; t.tile[p][n].hflip = 0; t.tile[p][n].data = 0; } } io.aboveEnable = random(); io.belowEnable = random(); io.interlace = random(); io.baseSize = random(); io.nameselect = random(); io.tiledataAddress = (random() & 7) << 13; io.firstSprite = 0; for(auto& p : io.priority) p = 0; io.timeOver = false; io.rangeOver = false; latch = {}; output.above.palette = 0; output.above.priority = 0; output.below.palette = 0; output.below.priority = 0; }