#ifndef YAZE_APP_EMU_PPU_H #define YAZE_APP_EMU_PPU_H #include #include #include #include "app/emu/memory/memory.h" #include "app/emu/video/ppu_registers.h" #include "app/rom.h" namespace yaze { namespace emu { class PpuInterface { public: virtual ~PpuInterface() = default; // Memory Interactions virtual void Write(uint16_t address, uint8_t data) = 0; virtual uint8_t Read(uint16_t address) const = 0; }; // Enum representing different background modes enum class BackgroundMode { Mode0, // 4 layers, each 2bpp (4 colors) Mode1, // 2 layers, 4bpp (16 colors), 1 layer, 2bpp (4 colors) Mode2, // 2 layers, 4bpp (16 colors), 1 layer for offset-per-tile Mode3, // 1 layer, 8bpp (256 colors), 1 layer, 4bpp (16 colors) Mode4, // 1 layer, 8bpp (256 colors), 1 layer, 2bpp (4 colors) // 1 layer for offset-per-tile Mode5, // 1 layer, 4bpp (16 colors), 1 layer, 2bpp (4 colors) hi-res Mode6, // 1 layer, 4bpp (16 colors), 1 layer for offset-per-tile, hi-res Mode7, // 1 layer, 8bpp (256 colors), rotation/scaling }; // Enum representing sprite sizes enum class SpriteSize { Size8x8, Size16x16, Size32x32, Size64x64 }; // Struct representing a sprite's attributes struct SpriteAttributes { uint8_t x; // X position of the sprite uint8_t y; // Y position of the sprite uint16_t tile; // Tile number for the sprite uint8_t palette; // Palette number for the sprite uint8_t priority; // Priority for the sprite bool hFlip; // Horizontal flip flag bool vFlip; // Vertical flip flag }; // Struct representing a tilemap entry struct TilemapEntry { uint16_t tileNumber; // Tile number for the tile uint8_t palette; // Palette number for the tile uint8_t priority; // Priority for the tile bool hFlip; // Horizontal flip flag bool vFlip; // Vertical flip flag }; // Struct representing a tilemap struct Tilemap { std::vector entries; // Entries for the tilemap }; // Struct representing a color struct Color { uint8_t r; // Red component uint8_t g; // Green component uint8_t b; // Blue component }; // Registers struct OAMSize { uint8_t base_selection : 3; uint8_t name_selection : 2; uint8_t object_size : 3; }; struct OAMAddress { uint8_t oam_address_low : 8; uint8_t oam_address_msb : 1; uint8_t oam_priority_rotation : 1; uint8_t unused : 6; }; struct TileMapLocation { uint8_t SC_size : 2; uint8_t tile_map_address : 5; uint8_t unused : 1; }; struct CharacterLocation { uint8_t BG1_address : 4; uint8_t BG2_address : 4; uint8_t BG3_address : 4; uint8_t BG4_address : 4; }; struct VideoPortControl { uint8_t increment_rate : 2; uint8_t full_graphic : 2; uint8_t increment_mode : 1; uint8_t unused : 3; }; struct ScreenDisplay { uint8_t brightness : 4; uint8_t disable_screen : 1; uint8_t unused : 3; }; struct ScreenMode { uint8_t general_screen_mode : 3; uint8_t priority : 1; uint8_t BG1_tile_size : 1; uint8_t BG2_tile_size : 1; uint8_t BG3_tile_size : 1; uint8_t BG4_tile_size : 1; }; struct ScrollRegister { uint8_t offset : 8; uint8_t mode7_bits : 3; uint8_t unused : 5; }; struct MainSubScreenDesignation { uint8_t BG1_enable : 1; uint8_t BG2_enable : 1; uint8_t BG3_enable : 1; uint8_t BG4_enable : 1; uint8_t sprites_enable : 1; uint8_t unused : 3; }; struct WindowMaskSettings { uint8_t BG1_clip_in_out : 1; uint8_t BG1_enable : 1; uint8_t BG2_clip_in_out : 1; uint8_t BG2_enable : 1; uint8_t BG3_clip_in_out : 1; uint8_t BG3_enable : 1; uint8_t BG4_clip_in_out : 1; uint8_t BG4_enable : 1; }; struct WindowMaskSettings2 { uint8_t sprites_clip_in_out : 1; uint8_t sprites_enable : 1; uint8_t color_windows_clip_in_out : 1; uint8_t color_windows_enable : 1; uint8_t unused : 4; }; struct WindowPosition { uint8_t position : 8; }; struct MaskLogicSettings { uint8_t BG1_mask_logic : 2; uint8_t BG2_mask_logic : 2; uint8_t BG3_mask_logic : 2; uint8_t BG4_mask_logic : 2; }; // Counter/IRQ/NMI Registers struct CounterIrqNmiRegisters { uint8_t softwareLatchHvCounter; // Register $2137 uint16_t horizontalScanLocation; // Register $213C uint16_t verticalScanLocation; // Register $213D uint8_t counterEnable; // Register $4200 uint16_t horizontalIrqTrigger; // Register $4207/$4208 uint16_t verticalIrqTrigger; // Register $4209/$420A uint8_t nmiRegister; // Register $4210 uint8_t irqRegister; // Register $4211 uint8_t statusRegisterIrq; // Register $4212 }; // Joypad Registers struct JoypadRegisters { uint16_t joypadData[4]; // Register $4218 to $421F uint8_t oldStyleJoypadRegisters[2]; // Registers $4016/$4217 }; // DMA Registers struct DmaRegisters { uint8_t startDmaTransfer; // Register $420B uint8_t enableHDmaTransfer; // Register $420C uint8_t dmacontrol_register_ister[8]; // Register $43?0 uint8_t dmaDestinationAddress[8]; // Register $43?1 uint32_t dmaSourceAddress[8]; // Register $43?2/$43?3/$43?4 uint16_t bytesToTransfer[8]; // Register $43?5/$43?6/$43?7 uint16_t hdmaCountPointer[8]; // Register $43?8/$43?9 uint8_t scanlinesLeft[8]; // Register $43?A }; // WRAM access Registers struct WramAccessRegisters { uint8_t dataByte; // Register $2180 uint32_t address; // Register $2181/$2182/$2183 }; struct Tile { uint16_t index; // Index of the tile in VRAM uint8_t palette; // Palette number used for this tile bool flip_x; // Horizontal flip flag bool flip_y; // Vertical flip flag uint8_t priority; // Priority of this tile }; typedef struct Layer { bool mainScreenEnabled; bool subScreenEnabled; bool mainScreenWindowed; bool subScreenWindowed; } Layer; typedef struct BgLayer { uint16_t hScroll; uint16_t vScroll; bool tilemapWider; bool tilemapHigher; uint16_t tilemapAdr; uint16_t tileAdr; bool bigTiles; bool mosaicEnabled; } BgLayer; typedef struct WindowLayer { bool window1enabled; bool window2enabled; bool window1inversed; bool window2inversed; uint8_t maskLogic; } WindowLayer; struct BackgroundLayer { enum class Size { SIZE_32x32, SIZE_64x32, SIZE_32x64, SIZE_64x64 }; enum class ColorDepth { BPP_2, BPP_4, BPP_8 }; Size size; // Size of the background layer ColorDepth color_depth; // Color depth of the background layer std::vector tilemap; // Tilemap data std::vector tile_data; // Tile data in VRAM uint16_t tilemap_base_address; // Base address of the tilemap in VRAM uint16_t tile_data_base_address; // Base address of the tile data in VRAM uint8_t scroll_x; // Horizontal scroll offset uint8_t scroll_y; // Vertical scroll offset bool enabled; // Whether the background layer is enabled }; class Ppu { public: // Initializes the PPU with the necessary resources and dependencies Ppu(Memory& memory) : memory_(memory) {} // Initialize the frame buffer void Init() { frame_buffer_.resize(256 * 240, 0); // Set to BGRX format (0) for compatibility with SDL_PIXELFORMAT_ARGB8888 // Format 0 = BGRX: [B][G][R][A] byte order in memory (little-endian) pixelOutputFormat = 0; } void Reset(); void HandleFrameStart(); void StartLine(int line); void CatchUp(int h_pos); void RunLine(int line); void HandlePixel(int x, int y); void LatchHV() { h_count_ = memory_.h_pos() / 4; v_count_ = memory_.v_pos(); counters_latched_ = true; } int GetPixel(int x, int y, bool sub, int* r, int* g, int* b); void EvaluateSprites(int line); void CalculateMode7Starts(int y); bool GetWindowState(int layer, int x); // if we are overscanning this frame (determined at 0,225) bool frame_overscan_ = false; bool overscan_ = false; // settings bool forced_blank_; uint8_t brightness; uint8_t mode; bool bg3priority; bool even_frame; bool pseudo_hires_; bool interlace; bool frame_interlace; // if we are interlacing this frame (determined at // start vblank) bool direct_color_; bool CheckOverscan() { frame_overscan_ = overscan_; return frame_overscan_; } void HandleVblank(); void HandleOPT(int layer, int* lx, int* ly); uint16_t GetOffsetValue(int col, int row); int GetPixelForBgLayer(int x, int y, int layer, bool priority); uint8_t Read(uint8_t adr, bool latch); void Write(uint8_t adr, uint8_t val); uint16_t GetVramRemap(); void PutPixels(uint8_t* pixel_data); // Returns the pixel data for the current frame const std::vector& GetFrameBuffer() const { return frame_buffer_; } // Set pixel output format (0 = BGRX, 1 = XBGR) void SetPixelFormat(uint8_t format) { pixelOutputFormat = format; } int GetPixelForMode7(int x, int layer, bool priority); const int cyclesPerScanline = 341; // SNES PPU has 341 cycles per scanline const int totalScanlines = 262; // SNES PPU has 262 scanlines per frame const int visibleScanlines = 224; // SNES PPU renders 224 visible scanlines bool enable_forced_blanking_ = false; int cycle_count_ = 0; int current_scanline_ = 0; // vram access uint16_t vram[0x8000]; uint16_t vram_pointer; bool vram_increment_on_high_; uint16_t vram_increment_; uint8_t vram_remap_mode_; uint16_t vram_read_buffer_; // cgram access uint16_t cgram[0x100]; private: int last_rendered_x_ = 0; uint8_t cgram_pointer_; bool cgram_second_write_; uint8_t cgram_buffer_; // oam access uint16_t oam[0x100]; uint8_t high_oam_[0x20]; uint8_t oam_adr_; uint8_t oam_adr_written_; bool oam_in_high_; bool oam_in_high_written_; bool oam_second_write_; uint8_t oam_buffer_; // Objects / Sprites bool time_over_ = false; bool range_over_ = false; bool obj_interlace_; bool obj_priority_; uint16_t obj_tile_adr1_; uint16_t obj_tile_adr2_; uint8_t obj_size_; std::array obj_pixel_buffer_; std::array obj_priority_buffer_; // Color Math uint8_t clip_mode_ = 0; uint8_t prevent_math_mode_ = 0; bool math_enabled_array_[6] = {false, false, false, false, false, false}; bool add_subscreen_ = false; bool subtract_color_; bool half_color_; uint8_t fixed_color_r_; uint8_t fixed_color_g_; uint8_t fixed_color_b_; // layers Layer layer_[5]; // mode 7 int16_t m7matrix[8]; // a, b, c, d, x, y, h, v uint8_t m7prev; bool m7largeField; bool m7charFill; bool m7xFlip; bool m7yFlip; bool m7extBg; // mode 7 internal int32_t m7startX; int32_t m7startY; // windows WindowLayer windowLayer[6]; uint8_t window1left; uint8_t window1right; uint8_t window2left; uint8_t window2right; // Background Layers std::array bg_layers_; uint8_t mosaic_startline_ = 1; BgLayer bg_layer_[4]; uint8_t scroll_prev_; uint8_t scroll_prev2_; uint8_t mosaic_size_; // pixel buffer (xbgr) // times 2 for even and odd frame uint8_t pixelBuffer[512 * 4 * 239 * 2]; uint8_t pixelOutputFormat = 0; // latching uint16_t h_count_; uint16_t v_count_; bool h_count_second_; bool v_count_second_; bool counters_latched_; uint8_t ppu1_open_bus_; uint8_t ppu2_open_bus_; uint16_t tile_data_size_; uint16_t vram_base_address_; uint16_t tilemap_base_address_; uint16_t screen_brightness_ = 0x00; Memory& memory_; Tilemap tilemap_; BackgroundMode bg_mode_; std::vector sprites_; std::vector tile_data_; std::vector frame_buffer_; // PPU registers OAMSize oam_size_; OAMAddress oam_address_; Mosaic mosaic_; std::array bgsc_; std::array bgnba_; std::array bghofs_; std::array bgvofs_; }; } // namespace emu } // namespace yaze #endif // YAZE_APP_EMU_PPU_H