#ifndef NESE_PPU_H_ #define NESE_PPU_H_ #include #include "serdes.h" #define nes_ppu_render_w (256U) #define nes_ppu_render_h (240U) #define nes_ppu_scanline_dots (341U) #define nes_ppu_prerender_line (0U) #define nes_ppu_visible_line (1U) #define nes_ppu_postrender_line (241U) #define nes_ppu_vblank_line (242U) #define nes_ppu_frame_end_line (262U) typedef struct { uint8_t y; uint8_t index; uint8_t attr; uint8_t x; } oam_sprite; typedef enum { oam_Attr_Pal_Mask = 0b00000011, oam_Attr_Background = 0b00100000, oam_Attr_Flip_X = 0b01000000, oam_Attr_Flip_Y = 0b10000000, } oam_Attribute; typedef enum { ppu_reg_ctrl = 0, ppu_reg_mask, ppu_reg_status, ppu_reg_oam_addr, ppu_reg_oam_data, ppu_reg_scroll, ppu_reg_addr, ppu_reg_data, } ppu_reg_offset; typedef enum { ppu_Control_Nametable_Mask = 0b00000011, ppu_Control_Scroll_Page_X = 0b00000001, ppu_Control_Scroll_Page_Y = 0b00000010, ppu_Control_VRAM_Inc = 0b00000100, ppu_Control_Sprite_Bank = 0b00001000, ppu_Control_Back_Bank = 0b00010000, ppu_Control_Sprite_Size = 0b00100000, ppu_Control_Master = 0b01000000, ppu_Control_VBlank = 0b10000000, } nes_ppu_Control; typedef enum { ppu_Mask_Greyscale = 0b00000001, ppu_Mask_Left_Back = 0b00000010, ppu_Mask_Left_Sprite = 0b00000100, ppu_Mask_Back = 0b00001000, ppu_Mask_Sprite = 0b00010000, ppu_Mask_More_Red = 0b00100000, ppu_Mask_More_Green = 0b01000000, ppu_Mask_More_Blue = 0b10000000, } nes_ppu_Mask; typedef enum { ppu_Status_Open_Bus_Mask = 0b00011111, ppu_Status_Overflow = 0b00100000, ppu_Status_Hit = 0b01000000, ppu_Status_VBlank = 0b10000000, } nes_ppu_Status; #define NES_CHR_ROM_PAGE_SIZE (0x0400U) #define NES_VRAM_PAGE_SIZE (0x0400U) #define NES_PPU_SPRITE_COUNT (64U) #define NES_PPU_CHR_SIZE (0x2000U) #define NES_PPU_VRAM_SIZE (0x1000U) #define NES_PPU_RAM_SIZE (0x4000U) #define NES_PPU_OAM_SIZE (NES_PPU_SPRITE_COUNT * 4U) #define NES_PPU_PAL_START (0x3F00U) typedef struct { // Dynamic memory banks uint8_t* chr; uint8_t* bank[16]; // Nullable buffer uint8_t* chr_ram; // Registers &c. uint8_t ctrl; uint8_t mask; uint8_t status; uint8_t oam_addr; uint16_t addr; // aka "v" uint8_t data; uint8_t x; // Fine X scroll uint16_t t; // temp VRAM addr uint8_t latch; // aka "w" - TODO: Could this be a flag? uint8_t addr_inc; // Auto-increment (1 or 32) // Static memory banks int n_chr_banks; uint8_t palette[32]; // Rendering palette with transparency masked uint8_t vram[NES_PPU_VRAM_SIZE]; uint8_t pal_bank[NES_CHR_ROM_PAGE_SIZE]; // Raw palette data mirrored in banks 12-15, also pal uint8_t oam[NES_PPU_OAM_SIZE]; } nes_PPU_Memory; static inline uint8_t* chr_page(nes_PPU_Memory* mem, int page) { return &mem->chr[page * NES_CHR_ROM_PAGE_SIZE]; } static inline uint8_t* vram_page(nes_PPU_Memory* mem, int page) { return &mem->vram[page * NES_VRAM_PAGE_SIZE]; } #define nes_PPU_Nametable_Bank_Index (8U) typedef enum { nes_Mirror_Horizontal = 0, nes_Mirror_Vertical, nes_Mirror_Only_0, nes_Mirror_Only_1, nes_Mirror_Four, } nes_Nametable_Mirroring; void nes_ppu_set_mirroring(nes_PPU_Memory* mem, nes_Nametable_Mirroring mirror); typedef struct { int scanline; int hit_line; uint8_t screen_data[nes_ppu_render_w * nes_ppu_render_h]; } nes_PPU; void nes_ppu_init(nes_PPU*, nes_PPU_Memory*); void nes_ppu_find_hit_line(nes_PPU*, nes_PPU_Memory*); void nes_ppu_render_line(nes_PPU*, nes_PPU_Memory*); extern const Serdes_Item nes_ppu_memory_serdes[]; #endif // NESE_PPU_H_