144 lines
4.3 KiB
C++
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;
|
|
}
|
|
} |