#pragma once #include #include "Types.hpp" class Bus; class Screen; enum class ScanlineType { PreRender, Visible, PostRender, VBlank }; enum class CycleType { Idle, Fetching, SpriteFetching, PreFetching, UnknownFetching }; enum class FetchingPhase { NametableByte, AttributeTableByte, PatternTableLo, PatternTableHi }; union VRAMAddress { struct { Word CoarseX : 5; Word CoarseY : 5; Word NametableSel : 2; Word FineY : 3; }; Word Raw; }; union ShiftRegister { struct { Byte Lo; Byte Hi; }; Word Raw; }; struct Sprite { Byte OAMPosition; Byte Lo, Hi; union { struct { Byte Palette : 2; Byte Unimplemented : 3; Byte Priority : 1; Byte FlipHorizontally : 1; Byte FlipVertically : 1; }; Byte Raw; } Latch; Byte Counter; Byte FineX; }; struct Pixel { Byte color; Byte palette; Byte priority; bool isZeroSprite; }; /** * @brief The PPU of the NES. */ class PPU { friend class PPUWatcher; friend class OAMViewer; public: static const std::vector colorTable; public: PPU(Bus* bus, Screen* screen); /** * @brief Powerup PPU. * Internal state corresponds to powerup state */ void Powerup(); /** * @brief Powerup PPU. * Internal state corresponds to reset state */ void Reset(); /** * @brief Tick PPU forward once. */ void Tick(); /** * @brief Read from memory mapped PPU regs. */ Byte ReadRegister(Byte id); /** * @brief Write to memory mapped PPU regs. */ void WriteRegister(Byte id, Byte val); Byte ReadOAM(Byte offset); void WriteOAM(Byte offset, Byte val); /** * @brief Check whether the PPU finished rendering a frame. * Returns true if the VBlankStart cycle was hit previously. The function resets * the boolearn when it is called */ inline bool IsFrameDone() { bool returnVal = isFrameDone; isFrameDone = false; return returnVal; } private: /** * @brief Wraps Bus::ReadPPU. */ Byte Read(Word addr); /** * @brief Wraps Bus::WritePPU. */ void Write(Word addr, Byte val); void UpdateState(); void EvaluateBackgroundTiles(); void EvaluateSprites(); Pixel GetBackgroundPixel(); Pixel GetSpritePixel(); Color MultiplexPixel(Pixel background, Pixel sprite); private: // Registers union { struct { Byte BaseNametableAddr : 2; Byte VRAMAddrIncrement : 1; Byte SpritePatternTableAddr : 1; Byte BackgrPatternTableAddr : 1; Byte SpriteSize : 1; Byte MasterSlaveSelect : 1; Byte VBlankNMI : 1; } Flag; Byte Raw; } ppuctrl; union { struct { Byte Greyscale : 1; Byte BackgroundOnLeft : 1; Byte SpriteOnLeft : 1; Byte ShowBackground : 1; Byte ShowSprites : 1; Byte EmphasizeRed : 1; Byte EmphasizeGreen : 1; Byte EmphasizeBlue : 1; } Flag; Byte Raw; } ppumask; union { struct { Byte Unused : 5; Byte SpriteOverflow : 1; Byte SpriteZeroHit : 1; Byte VBlankStarted : 1; } Flag; Byte Raw; } ppustatus; struct { Byte x; Byte y; } ppuscroll; Address ppuaddr; Byte oamaddr; VRAMAddress current{ 0 }; VRAMAddress temporary{ 0 }; uint16_t fineX; bool addressLatch = false; Byte latch = 0; Word x, y; Byte nametableByte = 0x00; Byte attributeTableByte = 0x00; Byte patternTableLo = 0x00; Byte patternTableHi = 0x00; ShiftRegister loTile{ 0 }; ShiftRegister hiTile{ 0 }; ShiftRegister hiAttribute{ 0 }; ShiftRegister loAttribute{ 0 }; std::vector OAM; std::vector secondaryOAM; std::vector sprites; Byte OAMOverrideSignal = 0x00; Byte freeSecondaryOAMSlot = 0x00; Byte currentlyEvaluatedSprite = 0x00; private: ScanlineType scanlineType; CycleType cycleType; FetchingPhase fetchPhase; uint8_t memoryAccessLatch = 0; bool isFrameDone = false; Bus* bus; Screen* screen; };