// #define PPU_CTRL_NMI_ENABLE 0x80 // #define PPU_CTRL_MASTER_SLAVE 0x40 // #define PPU_CTRL_SPRITE_HEIGHT 0x20 // #define PPU_CTRL_BG_TILE_SELECT 0x10 // #define PPU_CTRL_SPRITE_TILE_SELECT 0x08 // #define PPU_CTRL_NT_SELECT_Y 0x04 // #define PPU_CTRL_NT_SELECT_X 0x02 // #define PPU_CTRL_VRAM_INCREMENT 0x01 // #define PPU_MASK_SHOW_BG 0x08 // #define PPU_MASK_SHOW_SPRITES 0x10 // #define PPU_STATUS_VBLANK 0x80 // #define PPU_STATUS_SPRITE0_HIT 0x40 // #define PPU_STATUS_OVERFLOW 0x20 #define PPU_CTRL_BG_TILE_SELECT 0x10 #define PPU_CTRL_SPRITE_TILE_SELECT 0x08 #define PPU_CTRL_NMI 0x80 #define PPU_CTRL_VRAM_INCREMENT 0x04 // Define constants for PPU control and mask bits #define PPU_CTRL_NMI 0x80 #define PPU_CTRL_VRAM_INCREMENT 0x04 #define PPU_CTRL_SPRITE_HEIGHT 0x20 #define PPU_CTRL_SPRITE_TILE 0x08 #define PPU_MASK_SHOW_BG 0x08 #define PPU_MASK_SHOW_SPRITES 0x10 // Define mirroring modes #define MIRROR_HORIZONTAL 0 #define MIRROR_VERTICAL 1 #define MIRROR_FOURSCREEN 2 struct nes_state; struct ppu_state { uint8_t control; uint8_t mask; uint8_t fine_x; uint8_t coarse_x; uint8_t coarse_y; uint32_t nt_x_offset; uint32_t nt_y_offset; uint8_t fine_y; uint8_t tmp_fine_x; uint8_t tmp_coarse_x; uint8_t tmp_fine_y; uint8_t tmp_coarse_y; uint32_t tmp_nt_x; uint32_t tmp_nt_y; uint32_t tmp_addr; uint32_t vram_addr; uint8_t write_latch; uint32_t cycle; uint32_t scanline; uint32_t dot; uint32_t frame; uint32_t bg_shift_lo; uint32_t bg_shift_hi; uint32_t attr_shift_lo; uint32_t attr_shift_hi; uint8_t bg_attribute_latch; uint8_t next_tile; uint8_t next_attr; uint8_t next_lo; uint8_t next_hi; uint8_t vram[2048]; uint8_t palette[32]; uint8_t oam[256]; uint8_t oam_addr; uint8_t secondary_oam[32]; uint32_t sprite_count; uint8_t sprite_zero_hit; uint8_t sprite_overflow; uint8_t pixels[256 * 240]; uint8_t vblank; uint8_t frame_ready; }; 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 { uint32_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, uint32_t addr); void (*write)(struct nes_state *state, uint32_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 uint32_t nes_palette[64] = { 0x757575ff, 0x271a75ff, 0x3b0072ff, 0x4c0f64ff, 0x400048ff, 0x600027ff, 0x600000ff, 0x500f00ff, 0x783a00ff, 0x755c00ff, 0x406c00ff, 0x504764ff, 0x005468ff, 0x000000ff, 0x000000ff, 0x000000ff, 0xbfbfbfff, 0x273aa7ff, 0x5c14a7ff, 0x7514a7ff, 0x751468ff, 0x982727ff, 0xa03a00ff, 0x986c00ff, 0x888800ff, 0x689800ff, 0x3aa700ff, 0x6c6c6cff, 0x007878ff, 0x000000ff, 0x000000ff, 0x000000ff, 0xffffffff, 0x3ab5ffff, 0x5cb5ffff, 0x9888ffff, 0xa778ffff, 0xc87878ff, 0xf05c00ff, 0xf08800ff, 0xe0a700ff, 0xb8b800ff, 0x88c800ff, 0xcccc68ff, 0x00e0d8ff, 0x000000ff, 0x000000ff, 0x000000ff, 0xffffffff, 0xa7e0ffff, 0xb8d8ffff, 0xc8c8ffff, 0xd8b8ffff, 0xd8a7a7ff, 0xf0d0b8ff, 0xf0d898ff, 0xf0c878ff, 0xd8d878ff, 0xb8e078ff, 0xd0e0b8ff, 0xb8f0f0ff, 0x000000ff, 0x000000ff, 0x000000ff };