bsnes/higan/md/vdp/sprite.cpp

88 lines
2.0 KiB
C++

auto VDP::Sprite::write(uint9 address, uint16 data) -> void {
if(address > 320) return;
auto& object = oam[address >> 2];
switch(address.bits(0,1)) {
case 0: {
object.y = data.bits(0,8);
break;
}
case 1: {
object.link = data.bits(0,6);
object.height = 1 + data.bits(8,9) << 3;
object.width = 1 + data.bits(10,11) << 3;
break;
}
case 2: {
object.address = data.bits(0,10) << 4;
object.horizontalFlip = data.bit(11);
object.verticalFlip = data.bit(12);
object.palette = data.bits(13,14);
object.priority = data.bit(15);
break;
}
case 3: {
object.x = data.bits(0,8);
break;
}
}
}
auto VDP::Sprite::scanline(uint y) -> void {
objects.reset();
uint7 link = 0;
uint tiles = 0;
do {
auto& object = oam[link];
link = object.link;
if(128 + y < object.y) continue;
if(128 + y >= object.y + object.height) continue;
if(object.x == 0) break;
objects.append(object);
tiles += object.width >> 3;
} while(link && link < 80 && objects.size() < 20 && tiles < 40);
}
auto VDP::Sprite::run(uint x, uint y) -> void {
output.priority = 0;
output.color = 0;
for(auto& o : objects) {
if(128 + x < o.x) continue;
if(128 + x >= o.x + o.width) continue;
uint objectX = 128 + x - o.x;
uint objectY = 128 + y - o.y;
if(o.horizontalFlip) objectX = (o.width - 1) - objectX;
if(o.verticalFlip) objectY = (o.height - 1) - objectY;
uint tileX = objectX >> 3;
uint tileY = objectY >> 3;
uint tileNumber = tileX * (o.height >> 3) + tileY;
uint15 tileAddress = o.address + (tileNumber << 4);
uint pixelX = objectX & 7;
uint pixelY = objectY & 7;
tileAddress += pixelY << 1 | pixelX >> 2;
uint16 tileData = vdp.vram.read(tileAddress);
uint4 color = tileData >> (((pixelX & 3) ^ 3) << 2);
if(color) {
output.color = o.palette << 4 | color;
output.priority = o.priority;
break;
}
}
}
auto VDP::Sprite::power() -> void {
memory::fill(&io, sizeof(IO));
}