bsnes/higan/sfc/ppu/background.cpp

253 lines
6.7 KiB
C++

#include "mode7.cpp"
uint4 PPU::Background::Mosaic::size;
auto PPU::Background::hires() const -> bool {
return ppu.io.bgMode == 5 || ppu.io.bgMode == 6;
}
auto PPU::Background::voffset() const -> uint16 {
return mosaic.enable ? latch.voffset : io.voffset;
}
auto PPU::Background::hoffset() const -> uint16 {
return mosaic.enable ? latch.hoffset : io.hoffset;
}
//V = 0, H = 0
auto PPU::Background::frame() -> void {
}
//H = 0
auto PPU::Background::scanline() -> void {
}
//H = 28
auto PPU::Background::begin() -> void {
x = -7;
y = ppu.vcounter();
tileCounter = 7 - (io.hoffset & 7) << hires();
for(auto& word : data) word = 0;
if(y == 1) {
mosaic.vcounter = mosaic.size + 1;
mosaic.voffset = 1;
latch.hoffset = io.hoffset;
latch.voffset = io.voffset;
} else if(--mosaic.vcounter == 0) {
mosaic.vcounter = mosaic.size + 1;
mosaic.voffset += mosaic.size + 1;
latch.hoffset = io.hoffset;
latch.voffset = io.voffset;
}
mosaic.hcounter = mosaic.size + 1;
mosaic.hoffset = 0;
if(io.mode == Mode::Mode7) return beginMode7();
if(mosaic.size == 0) {
latch.hoffset = io.hoffset;
latch.voffset = io.voffset;
}
}
auto PPU::Background::getTile() -> void {
uint paletteOffset = ppu.io.bgMode == 0 ? id << 5 : 0;
uint paletteSize = 2 << io.mode;
uint tileMask = ppu.vram.mask >> 3 + io.mode;
uint tiledataIndex = io.tiledataAddress >> 3 + io.mode;
uint tileHeight = 3 + io.tileSize;
uint tileWidth = !hires() ? tileHeight : 4;
uint width = 256 << hires();
uint hmask = (width << io.tileSize << io.screenSize.bit(0)) - 1;
uint vmask = (width << io.tileSize << io.screenSize.bit(1)) - 1;
uint px = x << hires();
uint py = mosaic.enable ? (uint)mosaic.voffset : y;
uint hscroll = hoffset();
uint vscroll = voffset();
if(hires()) {
hscroll <<= 1;
if(ppu.io.interlace) py = py << 1 | (ppu.field() & !mosaic.enable); //todo: temporary vmosaic hack
}
uint hoffset = hscroll + px;
uint voffset = vscroll + py;
if(ppu.io.bgMode == 2 || ppu.io.bgMode == 4 || ppu.io.bgMode == 6) {
uint16 offsetX = px + (hscroll & 7);
if(offsetX >= 8) {
auto hlookup = ppu.bg3.getTile((offsetX - 8) + (ppu.bg3.hoffset() & ~7), ppu.bg3.voffset() + 0);
auto vlookup = ppu.bg3.getTile((offsetX - 8) + (ppu.bg3.hoffset() & ~7), ppu.bg3.voffset() + 8);
uint valid = 13 + id;
if(ppu.io.bgMode == 4) {
if(hlookup.bit(valid)) {
if(!hlookup.bit(15)) {
hoffset = offsetX + (hlookup & ~7);
} else {
voffset = py + hlookup;
}
}
} else {
if(hlookup.bit(valid)) hoffset = offsetX + (hlookup & ~7);
if(vlookup.bit(valid)) voffset = py + vlookup;
}
}
}
hoffset &= hmask;
voffset &= vmask;
uint screenX = io.screenSize.bit(0) ? 32 << 5 : 0;
uint screenY = io.screenSize.bit(1) ? 32 << 5 + io.screenSize.bit(0) : 0;
uint tileX = hoffset >> tileWidth;
uint tileY = voffset >> tileHeight;
uint16 offset = (tileY & 0x1f) << 5 | (tileX & 0x1f);
if(tileX & 0x20) offset += screenX;
if(tileY & 0x20) offset += screenY;
uint16 address = io.screenAddress + offset;
tile = ppu.vram[address];
bool mirrorY = tile.bit(15);
bool mirrorX = tile.bit(14);
priority = io.priority[tile.bit(13)];
paletteNumber = tile.bits(10,12);
paletteIndex = paletteOffset + (paletteNumber << paletteSize);
if(tileWidth == 4 && (bool)(hoffset & 8) != mirrorX) tile += 1;
if(tileHeight == 4 && (bool)(voffset & 8) != mirrorY) tile += 16;
uint16 character = tile.bits(0,9) + tiledataIndex & tileMask;
if(mirrorY) voffset ^= 7;
offset = (character << 3 + io.mode) + (voffset & 7);
switch(io.mode) {
case Mode::BPP8:
data[1].bits(16,31) = ppu.vram[offset + 24];
data[1].bits( 0,15) = ppu.vram[offset + 16];
case Mode::BPP4:
data[0].bits(16,31) = ppu.vram[offset + 8];
case Mode::BPP2:
data[0].bits( 0,15) = ppu.vram[offset + 0];
}
if(mirrorX) for(auto n : range(2)) {
data[n] = (data[n] >> 4 & 0x0f0f0f0f) | (data[n] << 4 & 0xf0f0f0f0);
data[n] = (data[n] >> 2 & 0x33333333) | (data[n] << 2 & 0xcccccccc);
data[n] = (data[n] >> 1 & 0x55555555) | (data[n] << 1 & 0xaaaaaaaa);
}
}
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(tileCounter-- == 0) {
tileCounter = 7;
getTile();
}
if(io.mode == Mode::Mode7) return runMode7();
uint8 color = getTileColor();
Pixel pixel;
pixel.priority = priority;
pixel.palette = color ? paletteIndex + color : 0;
pixel.tile = tile;
if(x == 0) {
mosaic.hcounter = mosaic.size + 1;
mosaic.pixel = pixel;
} else if((!hires() || screen == Screen::Below) && --mosaic.hcounter == 0) {
mosaic.hcounter = mosaic.size + 1;
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::getTileColor() -> uint {
uint color = 0;
switch(io.mode) {
case Mode::BPP8:
color += data[1] >> 24 & 0x80;
color += data[1] >> 17 & 0x40;
color += data[1] >> 10 & 0x20;
color += data[1] >> 3 & 0x10;
data[1] <<= 1;
case Mode::BPP4:
color += data[0] >> 28 & 0x08;
color += data[0] >> 21 & 0x04;
case Mode::BPP2:
color += data[0] >> 14 & 0x02;
color += data[0] >> 7 & 0x01;
data[0] <<= 1;
}
return color;
}
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();
latch = {};
output.above = {};
output.below = {};
mosaic = {};
mosaic.size = random();
mosaic.enable = random();
x = 0;
y = 0;
tileCounter = 0;
tile = 0;
priority = 0;
paletteNumber = 0;
paletteIndex = 0;
for(auto& word : data) word = 0;
}
auto PPU::Background::getTile(uint x, uint y) -> uint16 {
uint tileHeight = 3 + io.tileSize;
uint tileWidth = !hires() ? tileHeight : 4;
uint screenX = io.screenSize.bit(0) ? 32 << 5 : 0;
uint screenY = io.screenSize.bit(1) ? 32 << 5 + io.screenSize.bit(0) : 0;
uint tileX = x >> tileWidth;
uint tileY = y >> tileHeight;
uint16 offset = (tileY & 0x1f) << 5 | (tileX & 0x1f);
if(tileX & 0x20) offset += screenX;
if(tileY & 0x20) offset += screenY;
uint16 address = io.screenAddress + offset;
return ppu.vram[address];
}