nes/src/Oam.cpp
2023-09-30 15:31:30 +03:00

144 lines
4.3 KiB
C++

//
// Created by Selim Mustafaev on 24.09.2023.
//
#include "Oam.h"
#include "Ppu.h"
#include <cstring>
namespace nes {
Oam::Oam(Ppu *ppu): _ppu{ppu} {
}
void Oam::setAddress(uint8_t address) {
_address = address;
}
uint8_t Oam::read() const {
return reinterpret_cast<const uint8_t*>(_data)[_address];
}
void Oam::write(uint8_t value) {
reinterpret_cast<uint8_t*>(_data)[_address] = value;
}
void Oam::write(uint8_t address, uint8_t value) {
reinterpret_cast<uint8_t*>(_data)[address] = value;
}
bool Oam::detectVisibleSprites(int16_t scanline, bool bigSprite) {
memset(_visibleSprites, 0xFF, MAX_SPRITES_PER_SCANLINE*sizeof(SpriteInfo));
_visibleSpriteCount = 0;
for (auto& shifter: _spriteShifters) {
shifter.reset();
}
size_t oamIndex = 0;
uint8_t spriteHeight = bigSprite ? 16 : 8;
bool spriteOverflow = false;
_spriteZeroVisible = false;
while (oamIndex < OAM_SIZE && _visibleSpriteCount < MAX_SPRITES_PER_SCANLINE + 1) {
uint8_t yStart = _data[oamIndex].y;
uint8_t yEnd = yStart + spriteHeight;
if(scanline >= yStart && scanline < yEnd) {
if(_visibleSpriteCount < MAX_SPRITES_PER_SCANLINE) {
_visibleSprites[_visibleSpriteCount] = _data[oamIndex];
_visibleSpriteCount++;
if(oamIndex == 0) {
_spriteZeroVisible = true;
}
} else {
spriteOverflow = true;
}
}
oamIndex++;
}
return spriteOverflow;
}
void Oam::loadShifters(int16_t scanline, bool bigSprite, bool patternSprite) {
for(uint8_t i = 0; i < _visibleSpriteCount; ++i) {
auto sprite = _visibleSprites[i];
bool flippedVertically = sprite.attr & 0x80;
bool flippedHorizontally = sprite.attr & 0x40;
bool isTopHalf = (scanline - sprite.y) < 8;
uint8_t tileOffset = isTopHalf ? 0 : 1;
uint8_t rowOffset = flippedVertically ? (7 - scanline + sprite.y) : (scanline - sprite.y);
uint16_t patternAddress = 0;
if(bigSprite) {
patternAddress = (sprite.id & 0x01) << 12
| ((sprite.id & 0xFE) + tileOffset) << 4
| (rowOffset & 0x07);
} else {
patternAddress = patternSprite << 12
| sprite.id << 4
| rowOffset;
}
uint8_t patternBitsLo = _ppu->internalRead(patternAddress);
uint8_t patternBitsHi = _ppu->internalRead(patternAddress + 8);
if(flippedHorizontally) {
patternBitsLo = flipByte(patternBitsLo);
patternBitsHi = flipByte(patternBitsHi);
}
_spriteShifters[i].loadHighByte(patternBitsLo, patternBitsHi);
}
}
uint8_t Oam::flipByte(uint8_t byte) const {
byte = (byte & 0xF0) >> 4 | (byte & 0x0F) << 4;
byte = (byte & 0xCC) >> 2 | (byte & 0x33) << 2;
byte = (byte & 0xAA) >> 1 | (byte & 0x55) << 1;
return byte;
}
void Oam::updateShifters() {
for (size_t i = 0; i < _visibleSpriteCount; ++i) {
if(_visibleSprites[i].x > 0) {
_visibleSprites[i].x--;
} else {
_spriteShifters[i].shift();
}
}
}
SpritePixelInfo Oam::getPixel() {
SpritePixelInfo pixel;
_firstVisibleSpriteBeingRendered = false;
for (size_t i = 0; i < _visibleSpriteCount; ++i) {
if(_visibleSprites[i].x != 0) {
continue;
}
pixel.pattern = _spriteShifters[i].getValue(0);
pixel.palette = (_visibleSprites[i].attr & 0x03) + SPRITE_PALETTE_OFFSET;
pixel.priority = (_visibleSprites[i].attr & 0x20) == 0;
if(pixel.pattern > 0) {
_firstVisibleSpriteBeingRendered = (i == 0);
break;
}
}
return pixel;
}
bool Oam::spriteZeroBeingRendered() const {
return _spriteZeroVisible && _firstVisibleSpriteBeingRendered;
}
}