This repository has been archived by the owner. It is now read-only.
Permalink
Cannot retrieve contributors at this time
229 lines (180 sloc)
6.71 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| alwaysinline auto PPU::Background::hires() const -> bool { | |
| return ppu.io.bgMode == 5 || ppu.io.bgMode == 6; | |
| } | |
| //V = 0, H = 0 | |
| auto PPU::Background::frame() -> void { | |
| } | |
| //H = 0 | |
| auto PPU::Background::scanline() -> void { | |
| mosaic.hcounter = ppu.mosaic.size; | |
| mosaic.hoffset = 0; | |
| renderingIndex = 0; | |
| pixelCounter = (io.hoffset & 7) << hires(); | |
| opt.hoffset = 0; | |
| opt.voffset = 0; | |
| } | |
| //H = 56 | |
| auto PPU::Background::begin() -> void { | |
| //remove partial tile columns that have been scrolled offscreen | |
| for(auto& data : tiles[0].data) data >>= pixelCounter << 1; | |
| } | |
| auto PPU::Background::fetchNameTable() -> void { | |
| if(ppu.vcounter() == 0) return; | |
| uint nameTableIndex = ppu.hcounter() >> 5 << hires(); | |
| int x = (ppu.hcounter() & ~31) >> 2; | |
| uint hpixel = x << hires(); | |
| uint vpixel = ppu.vcounter(); | |
| uint hscroll = io.hoffset; | |
| uint vscroll = io.voffset; | |
| if(hires()) { | |
| hscroll <<= 1; | |
| if(ppu.io.interlace) vpixel = vpixel << 1 | (ppu.field() && !mosaic.enable); | |
| } | |
| if(mosaic.enable) { | |
| vpixel -= ppu.mosaic.voffset() << (hires() && ppu.io.interlace); | |
| } | |
| bool repeated = false; | |
| repeat: | |
| uint hoffset = hpixel + hscroll; | |
| uint voffset = vpixel + vscroll; | |
| if(ppu.io.bgMode == 2 || ppu.io.bgMode == 4 || ppu.io.bgMode == 6) { | |
| auto hlookup = ppu.bg3.opt.hoffset; | |
| auto vlookup = ppu.bg3.opt.voffset; | |
| uint valid = 13 + id; | |
| if(ppu.io.bgMode == 4) { | |
| if(hlookup.bit(valid)) { | |
| if(!hlookup.bit(15)) { | |
| hoffset = hpixel + (hlookup & ~7) + (hscroll & 7); | |
| } else { | |
| voffset = vpixel + (vlookup); | |
| } | |
| } | |
| } else { | |
| if(hlookup.bit(valid)) hoffset = hpixel + (hlookup & ~7) + (hscroll & 7); | |
| if(vlookup.bit(valid)) voffset = vpixel + (vlookup); | |
| } | |
| } | |
| uint width = 256 << hires(); | |
| uint hsize = width << io.tileSize << io.screenSize.bit(0); | |
| uint vsize = width << io.tileSize << io.screenSize.bit(1); | |
| hoffset &= hsize - 1; | |
| voffset &= vsize - 1; | |
| uint vtiles = 3 + io.tileSize; | |
| uint htiles = !hires() ? vtiles : 4; | |
| uint htile = hoffset >> htiles; | |
| uint vtile = voffset >> vtiles; | |
| uint hscreen = io.screenSize.bit(0) ? 32 << 5 : 0; | |
| uint vscreen = io.screenSize.bit(1) ? 32 << 5 + io.screenSize.bit(0) : 0; | |
| uint16 offset = (uint5)htile << 0 | (uint5)vtile << 5; | |
| if(htile & 0x20) offset += hscreen; | |
| if(vtile & 0x20) offset += vscreen; | |
| uint16 address = io.screenAddress + offset; | |
| uint16 attributes = ppu.vram[address]; | |
| auto& tile = tiles[nameTableIndex]; | |
| tile.character = attributes.bit(0,9); | |
| tile.paletteGroup = attributes.bit(10,12); | |
| tile.priority = io.priority[attributes.bit(13)]; | |
| tile.hmirror = attributes.bit(14); | |
| tile.vmirror = attributes.bit(15); | |
| if(htiles == 4 && bool(hoffset & 8) != tile.hmirror) tile.character += 1; | |
| if(vtiles == 4 && bool(voffset & 8) != tile.vmirror) tile.character += 16; | |
| uint characterMask = ppu.vram.mask >> 3 + io.mode; | |
| uint characterIndex = io.tiledataAddress >> 3 + io.mode; | |
| uint16 origin = tile.character + characterIndex & characterMask; | |
| if(tile.vmirror) voffset ^= 7; | |
| tile.address = (origin << 3 + io.mode) + (voffset & 7); | |
| uint paletteOffset = ppu.io.bgMode == 0 ? id << 5 : 0; | |
| uint paletteSize = 2 << io.mode; | |
| tile.palette = paletteOffset + (tile.paletteGroup << paletteSize); | |
| nameTableIndex++; | |
| if(hires() && !repeated) { | |
| repeated = true; | |
| hpixel += 8; | |
| goto repeat; | |
| } | |
| } | |
| auto PPU::Background::fetchOffset(uint y) -> void { | |
| if(ppu.vcounter() == 0) return; | |
| uint characterIndex = ppu.hcounter() >> 5 << hires(); | |
| uint x = characterIndex << 3; | |
| uint hoffset = x + (io.hoffset & ~7); | |
| uint voffset = y + (io.voffset); | |
| uint vtiles = 3 + io.tileSize; | |
| uint htiles = !hires() ? vtiles : 4; | |
| uint htile = hoffset >> htiles; | |
| uint vtile = voffset >> vtiles; | |
| uint hscreen = io.screenSize.bit(0) ? 32 << 5 : 0; | |
| uint vscreen = io.screenSize.bit(1) ? 32 << 5 + io.screenSize.bit(0) : 0; | |
| uint16 offset = (uint5)htile << 0 | (uint5)vtile << 5; | |
| if(htile & 0x20) offset += hscreen; | |
| if(vtile & 0x20) offset += vscreen; | |
| uint16 address = io.screenAddress + offset; | |
| if(y == 0) opt.hoffset = ppu.vram[address]; | |
| if(y == 8) opt.voffset = ppu.vram[address]; | |
| } | |
| auto PPU::Background::fetchCharacter(uint index, bool half) -> void { | |
| if(ppu.vcounter() == 0) return; | |
| uint characterIndex = (ppu.hcounter() >> 5 << hires()) + half; | |
| auto& tile = tiles[characterIndex]; | |
| uint16 data = ppu.vram[tile.address + (index << 3)]; | |
| //reverse bits so that the lowest bit is the left-most pixel | |
| if(!tile.hmirror) { | |
| data = data >> 4 & 0x0f0f | data << 4 & 0xf0f0; | |
| data = data >> 2 & 0x3333 | data << 2 & 0xcccc; | |
| data = data >> 1 & 0x5555 | data << 1 & 0xaaaa; | |
| } | |
| //interleave two bitplanes for faster planar decoding later | |
| tile.data[index] = ( | |
| ((uint8(data >> 0) * 0x0101010101010101ull & 0x8040201008040201ull) * 0x0102040810204081ull >> 49) & 0x5555 | |
| | ((uint8(data >> 8) * 0x0101010101010101ull & 0x8040201008040201ull) * 0x0102040810204081ull >> 48) & 0xaaaa | |
| ); | |
| } | |
| auto PPU::Background::run(bool screen) -> void { | |
| if(ppu.vcounter() == 0) return; | |
| if(screen == Screen::Below) { | |
| output.above.priority = 0; | |
| output.below.priority = 0; | |
| if(!hires()) return; | |
| } | |
| if(io.mode == Mode::Mode7) return runMode7(); | |
| auto& tile = tiles[renderingIndex]; | |
| uint8 color; | |
| if(io.mode >= Mode::BPP2) color.bit(0,1) = tile.data[0] & 3; tile.data[0] >>= 2; | |
| if(io.mode >= Mode::BPP4) color.bit(2,3) = tile.data[1] & 3; tile.data[1] >>= 2; | |
| if(io.mode >= Mode::BPP8) color.bit(4,5) = tile.data[2] & 3; tile.data[2] >>= 2; | |
| if(io.mode >= Mode::BPP8) color.bit(6,7) = tile.data[3] & 3; tile.data[3] >>= 2; | |
| Pixel pixel; | |
| pixel.priority = tile.priority; | |
| pixel.palette = color ? uint(tile.palette + color) : 0; | |
| pixel.paletteGroup = tile.paletteGroup; | |
| if(++pixelCounter == 0) renderingIndex++; | |
| uint x = ppu.hcounter() - 56 >> 2; | |
| if(x == 0 && (!hires() || screen == Screen::Below)) { | |
| mosaic.hcounter = ppu.mosaic.size; | |
| mosaic.pixel = pixel; | |
| } else if((!hires() || screen == Screen::Below) && --mosaic.hcounter == 0) { | |
| mosaic.hcounter = ppu.mosaic.size; | |
| mosaic.pixel = pixel; | |
| } else if(mosaic.enable) { | |
| pixel = mosaic.pixel; | |
| } | |
| if(screen == Screen::Above) x++; | |
| if(pixel.palette == 0) return; | |
| if(!hires() || screen == Screen::Above) if(io.aboveEnable) output.above = pixel; | |
| if(!hires() || screen == Screen::Below) if(io.belowEnable) output.below = pixel; | |
| } | |
| auto PPU::Background::power() -> void { | |
| io = {}; | |
| io.tiledataAddress = (random() & 0x0f) << 12; | |
| io.screenAddress = (random() & 0xfc) << 8; | |
| io.screenSize = random(); | |
| io.tileSize = random(); | |
| io.aboveEnable = random(); | |
| io.belowEnable = random(); | |
| io.hoffset = random(); | |
| io.voffset = random(); | |
| output.above = {}; | |
| output.below = {}; | |
| mosaic = {}; | |
| mosaic.enable = random(); | |
| } |