// // Created by selim on 8/22/23. // #ifndef NES_PPU_H #define NES_PPU_H #include "Cartridge.h" #include "Shifter.h" #include "Oam.h" #include #include #include #include namespace nes { struct Pixel { uint8_t R = 0; uint8_t G = 0; uint8_t B = 0; uint8_t A = 0xFF; Pixel(): R{}, G{}, B{}, A{0xFF} {} Pixel(uint8_t R, uint8_t G, uint8_t B): R{R}, G{G}, B{B}, A{0xFF} {} }; class Ppu { public: static constexpr uint16_t SCREEN_WIDTH = 256; static constexpr uint16_t SCREEN_HEIGHT = 240; enum ControlAddress: uint16_t { Control = 0x0000, Mask = 0x0001, Status = 0x0002, OamAddress = 0x0003, OamData = 0x0004, Scroll = 0x0005, PpuAddress = 0x0006, PpuData = 0x0007 }; union StatusRegister { struct { uint8_t unused: 5; uint8_t spriteOverflow: 1; uint8_t spriteZeroHit: 1; uint8_t verticalBlank: 1; }; uint8_t value; }; union ControlRegister { struct { uint8_t nameTableX: 1; uint8_t nameTableY: 1; uint8_t incrementMode: 1; uint8_t patternSprite: 1; uint8_t patternBackground: 1; uint8_t spriteSize: 1; uint8_t slaveMode: 1; uint8_t enableNmi: 1; }; uint8_t value; }; union MaskRegister { struct { uint8_t grayscale: 1; uint8_t renderBackgroundLeft: 1; uint8_t renderSpritesLeft: 1; uint8_t renderBackground: 1; uint8_t renderSprites: 1; uint8_t enhanceRed: 1; uint8_t enhanceGreen: 1; uint8_t enhanceBlue: 1; }; uint8_t value; }; union LoopyRegister { struct { uint16_t coarseX : 5; uint16_t coarseY : 5; uint16_t nameTableX : 1; uint16_t nameTableY : 1; uint16_t fineY : 3; uint16_t unused : 1; }; uint16_t value; }; struct TileInfo { uint8_t id; uint8_t attribute; uint8_t lsb; uint8_t msb; }; public: Ppu(); bool tick(); void write(uint16_t address, uint8_t value); uint8_t read(uint16_t address); void setPixel(uint16_t row, uint16_t column, Pixel pixel); void connect(Cartridge* cartridge); void reset(); void writeOam(uint8_t address, uint8_t data); uint8_t internalRead(uint16_t address); void internalWrite(uint16_t address, uint8_t value); #ifdef NES_LOGGING [[nodiscard]] std::string state() const; #endif public: std::function onNewFrame; private: Pixel getColor(uint8_t palette, uint8_t pixel); void prepareNextBgTile(uint16_t column); void incrementScrollX(); void incrementScrollY(); void updateScrollX(); void updateScrollY(); private: int16_t _column; int16_t _scanline; StatusRegister _status; ControlRegister _control; MaskRegister _mask; LoopyRegister _vRamAddress; LoopyRegister _tRamAddress; bool _addressWriteInProgress; uint8_t _dataTemp; uint8_t _fineX; TileInfo _nextBgTile; Shifter _bgPatternShifter; Shifter _bgAttribShifter; bool _needEmitNmi; private: std::unique_ptr _nameTable; std::unique_ptr _nameTable2; std::vector _palette; std::unique_ptr _paletteTable; std::unique_ptr _oam; private: std::unique_ptr _frameBuffer; Cartridge* _cartridge; }; } #endif //NES_PPU_H