// // Created by Selim Mustafaev on 24.09.2023. // #include "Oam.h" #include "Ppu.h" #include namespace nes { Oam::Oam(Ppu *ppu): _ppu{ppu} { } void Oam::setAddress(uint8_t address) { _address = address; } uint8_t Oam::read() const { return reinterpret_cast(_data)[_address]; } void Oam::write(uint8_t value) { reinterpret_cast(_data)[_address] = value; } void Oam::write(uint8_t address, uint8_t value) { reinterpret_cast(_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; } }