233 lines
6.7 KiB
C++
233 lines
6.7 KiB
C++
#include "mode7.cpp"
|
|
|
|
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 = 1 << 13 + id;
|
|
|
|
if(ppu.io.bgMode == 4) {
|
|
if(hlookup & valid) {
|
|
if(!(hlookup & 0x8000)) {
|
|
hoffset = hpixel + (hlookup & ~7) + (hscroll & 7);
|
|
} else {
|
|
voffset = vpixel + (vlookup);
|
|
}
|
|
}
|
|
} else {
|
|
if(hlookup & valid) hoffset = hpixel + (hlookup & ~7) + (hscroll & 7);
|
|
if(vlookup & 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 & 0x03ff;
|
|
tile.paletteGroup = attributes >> 10 & 7;
|
|
tile.priority = io.priority[attributes >> 13 & 1];
|
|
tile.hmirror = bool(attributes & 0x4000);
|
|
tile.vmirror = bool(attributes & 0x8000);
|
|
|
|
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 = 0;
|
|
if(io.mode >= Mode::BPP2) color |= (tile.data[0] & 3) << 0; tile.data[0] >>= 2;
|
|
if(io.mode >= Mode::BPP4) color |= (tile.data[1] & 3) << 2; tile.data[1] >>= 2;
|
|
if(io.mode >= Mode::BPP8) color |= (tile.data[2] & 3) << 4; tile.data[2] >>= 2;
|
|
if(io.mode >= Mode::BPP8) color |= (tile.data[3] & 3) << 6; 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();
|
|
}
|