// PPUSTATUS ($2002) flags #define PPU_STATUS_VBLANK 0x80 #define PPU_STATUS_SPRITE0_HIT 0x40 #define PPU_STATUS_OVERFLOW 0x20 // PPUMASK ($2001) flags #define PPU_MASK_SHOW_BG 0x08 #define PPU_MASK_SHOW_SPRITES 0x10 // PPUCTRL ($2000) flags #define PPU_CTRL_NMI_ENABLE 0x80 #define PPU_CTRL_MASTER_SLAVE 0x40 #define PPU_CTRL_SPRITE_HEIGHT 0x20 #define PPU_CTRL_BG_TABLE 0x10 #define PPU_CTRL_SPRITE_TABLE 0x08 #define PPU_CTRL_INCREMENT 0x04 #define PPU_CTRL_NT_SELECT_Y 0x02 #define PPU_CTRL_NT_SELECT_X 0x01 struct nes_state; struct ppu_state { uint8_t pixels[240*256]; uint8_t sprite_pixels[256]; // Sprite pixel color indexes (BG priority resolved) uint8_t sprite_zero_flags[256]; // 1 if pixel came from sprite #0 and is nonzero // Sprite memory uint8_t oam[256]; uint8_t sec_oam[32]; uint8_t oam_addr; uint8_t read_buffer; uint32_t scanline; // 0–261 uint32_t dot; // 0–340 // Scroll state uint32_t coarse_x_offs; uint32_t coarse_y_offs; uint32_t fine_x; uint32_t fine_y; uint32_t nt_base_x; uint32_t nt_base_y; // Latch state for $2005/$2006 uint8_t write_toggle; uint32_t temp_coarse_x_offs; uint32_t temp_coarse_y_offs; uint32_t temp_fine_x; uint32_t temp_fine_y; uint32_t temp_nt_base_x; uint32_t temp_nt_base_y; // Background shift registers uint32_t bg_tile_lsb; uint32_t bg_tile_msb; uint32_t bg_attr_lsb; uint32_t bg_attr_msb; // Tile fetch latches uint8_t nt_byte; uint8_t attr_byte; uint8_t tile_lsb; uint8_t tile_msb; // Control and status uint8_t ctrl; // $2000 uint8_t mask; // $2001 uint8_t status; // $2002 // Flags uint8_t frame_even; uint8_t nmi_occurred; uint8_t nmi_output; }; struct cpu_state { uint32_t pc; // Program Counter uint8_t sp; // Stack Pointer uint8_t a; // Accumulator uint8_t x; // X Register uint8_t y; // Y Register uint8_t p; // Processor Status Flags (this can be expanded with separate flags if needed) uint8_t n; // Negative Flag uint8_t v; // Overflow Flag // uint8_t b; // Break Flag (Set by BRK instruction) uint8_t d; // Decimal Flag uint8_t i; // Interrupt Disable Flag uint8_t z; // Zero Flag uint8_t c; // Carry Flag // -- uint8_t die; // KIL instruction found! }; struct ines_state { uint16_t mapper; uint8_t mirroring; // 0 = H, 1 = V, 2 = 4-screen uint32_t prg_size; uint32_t chr_size; }; struct mapper { void (*init)(struct nes_state *state); uint8_t (*read)(struct nes_state *state, uint16_t addr); void (*write)(struct nes_state *state, uint16_t addr, uint8_t value); void (*tick)(struct nes_state *state); }; union mapper_data { // struct nrom_mapper nrom; // struct mmc1_mapper mmc1; // ... others }; struct nes_state { struct ines_state ines; struct cpu_state cpu; struct ppu_state ppu; struct mapper mapper; union mapper_data map; size_t cycle; uint8_t ram[2048]; uint8_t rom[4 * 1024 * 1024]; uint8_t chrrom[4 * 1024 * 1024]; uint8_t ciram[2048]; uint8_t palette[0x20]; uint8_t irq_pending; uint8_t nmi_pending; }; static const uint32_t nes_palette_argb[64] = { 0xff757575, 0xff8f1b27, 0xffab0000, 0xff9f0047, 0xff77008f, 0xff1300ab, 0xff0000a7, 0xff000b7f, 0xff002f43, 0xff004700, 0xff005100, 0xff173f00, 0xff5f3f1b, 0xff000000, 0xff000000, 0xff000000, 0xffbcbcbc, 0xffef7300, 0xffef3b23, 0xfff30083, 0xffbf00bf, 0xff5b00e7, 0xff002bdb, 0xff0f4fcb, 0xff00738b, 0xff009700, 0xff00ab00, 0xff3b9300, 0xff8b8300, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0xffffbf3f, 0xffff975f, 0xfffd8ba7, 0xffff7bff, 0xffb777ff, 0xff6377ff, 0xff3b9bff, 0xff3fbff3, 0xff13d383, 0xff4bdf4f, 0xff98f858, 0xffdbeb00, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0xffffe7ab, 0xffffd7c7, 0xffffcbd7, 0xffffc7ff, 0xffdbc7ff, 0xffb3bfff, 0xffabdbff, 0xffa3e7ff, 0xff83f7c7, 0xffb3ffbf, 0xffcfffb3, 0xfff3ff9f, 0xff000000, 0xff000000, 0xff000000 };