#pragma once #include #include class NES; const unsigned int VRAM_SIZE = 2048u; const unsigned int PALETTE_SIZE = 8u; const unsigned int OAM_SIZE = 256u; const unsigned int VIDEO_WIDTH = 256u; const unsigned int VIDEO_HEIGHT = 240u; const unsigned int TOTAL_SCANLINES = 262u; const unsigned int TOTAL_DOTS = 341u; class PPU { public: PPU(); enum Register { PPUCTRL = 0x00, PPUMASK = 0x01, PPUSTATUS = 0x02, OAMADDR = 0x03, OAMDATA = 0x04, PPUSCROLL = 0x05, PPUADDR = 0x06, PPUDATA = 0x07, }; void Tick(); void WriteRegister(Register reg, uint8_t value); uint8_t ReadRegister(Register reg); void WriteMemory(uint16_t address, uint8_t value); uint8_t ReadMemory(uint16_t address); private: friend class NES; void FetchNT(); void FetchAT(); void FetchPTLow(); void FetchPTHigh(); void SetVBlank(); void SetTileX(); void SetTileY(); void LoadBackground(); void Draw(); void IncrementTileX(); void IncrementTileY(); void ClearFlags(); enum Event { FETCH_NT = 0x1, FETCH_AT = 0x2, FETCH_PT_LOW = 0x4, FETCH_PT_HIGH = 0x8, LOAD_BACKGROUND = 0x10, SHIFT_BACKGROUND = 0x20, SET_VBLANK = 0x40, SET_TILE_X = 0x80, SET_TILE_Y = 0x100, DRAW = 0x200, INCREMENT_TILE_X = 0x400, INCREMENT_TILE_Y = 0x800, CLEAR_FLAGS = 0x1000 }; struct PpuCtrl { void SetByte(uint8_t byte); uint16_t nametableBaseAddr; uint8_t vramAddrIncrement; uint8_t spritePatternTableAddr; uint8_t bgPatternTableAddr; uint8_t spriteSize; uint8_t masterSlaveSelect; uint8_t nmiEnable; }; struct PpuMask { void SetByte(uint8_t byte); uint8_t grayscale; uint8_t showBackgroundLeft; uint8_t showSpritesLeft; uint8_t showBackground; uint8_t showSprites; uint8_t redEmphasis; uint8_t greenEmphasis; uint8_t blueEmphasis; }; struct PpuStatus { uint8_t GetByte(); uint8_t vblankStarted; uint8_t sprite0Hit; uint8_t spriteOverflow; uint8_t previousLsb; }; struct Address { uint16_t GetValue(); void SetValue(uint16_t value); uint8_t scrollFineY; uint8_t nametableX; uint8_t nametableY; uint8_t scrollCoarseX; uint8_t scrollCoarseY; }; // External Registersgg PpuCtrl regPPUCTRL{}; // $2000, W PpuMask regPPUMASK{}; // $2001, W PpuStatus regPPUSTATUS{}; // $2002, R uint8_t regOAMADDR{}; // $2003, W uint8_t oldByte{}; // Internal Registers Address currentAddress{}; Address tempAddress{}; uint8_t scrollFineX{}; bool firstWrite{true}; // Timing tracking uint32_t events[TOTAL_SCANLINES][TOTAL_DOTS]{}; uint16_t currentCycle{}; uint16_t currentScanline{}; // Memory uint8_t ram[VRAM_SIZE]{}; uint8_t palette[PALETTE_SIZE][4]{}; uint8_t oam[OAM_SIZE]{}; uint32_t video[VIDEO_WIDTH * VIDEO_HEIGHT]{}; uint64_t frameNumber{}; // Shift registers and latches uint16_t patternTableHighShift{}; uint16_t patternTableLowShift{}; uint8_t atShiftLow{}; uint8_t atShiftHigh{}; uint8_t nametableLatch{}; uint8_t attributeTableLatch{}; uint8_t attribute; uint8_t patternTableLowLatch{}; uint8_t patternTableHighLatch{}; NES* nes; void ShiftBG(); };