diff --git a/ppu.c b/ppu.c index 52837ad..866e382 100644 --- a/ppu.c +++ b/ppu.c @@ -38,6 +38,10 @@ void ppu_tick(void) { ppu.cycles++; + + if (ppu.cycles > 256 && ppu.cycles <= 320) + ppu.oam_addr = 0; + if (ppu.cycles >= 341) { ppu.cycles -= 341; ppu.scanlines++; @@ -58,23 +62,22 @@ ppu_read(uint16_t addr) | (ppu.status.sprite_overflow << 5); ppu.status.vblank = 0; - ppu.regs.w = 0; + ppu.regs.write_toggle = 0; return tmp; case 0x2004: return ppu.oam[ppu.oam_addr]; case 0x2007: + tmp = ppu.last_read; + + if (addr <= 0x1fff) + ppu.last_read = ppu.rom->chr_rom[ppu.regs.v]; + else if (addr <= 0x2fff) + ppu.last_read = ppu.vram[vram_addr_mirror(addr)]; + vram_addr_inc(); - if (addr <= 0x1fff) { - tmp = ppu.last_read; - ppu.last_read = ppu.rom->chr_rom[ppu.regs.v]; - return tmp; - } else if (addr <= 0x2fff) { - tmp = ppu.last_read; - ppu.last_read = ppu.vram[vram_addr_mirror(addr)]; - return tmp; - } + return tmp; default: fprintf(stderr, "Invalid PPU read at address $%04\n", addr); return 0; @@ -84,7 +87,70 @@ ppu_read(uint16_t addr) void ppu_write(uint16_t addr, uint8_t byte) { - static uint8_t prev; + switch (addr) { + case 0x2000: + ppu.ctrl.nametable_base = byte & 3; + ppu.ctrl.inc_mode = byte & (1 << 2); + ppu.ctrl.sprite_tile_sel = byte & (1 << 3); + ppu.ctrl.bg_tile_sel = byte & (1 << 4); + ppu.ctrl.sprite_height = byte & (1 << 5); + ppu.ctrl.master_slave = byte & (1 << 6); + ppu.ctrl.nmi_enable = byte & (1 << 7); - prev = byte; + if (ppu.ctrl.nametable_base & 1) + ppu.regs.scroll_x += 256; + if (ppu.ctrl.nametable_base & 2) + ppu.regs.scroll_y += 240; + + /* + * TODO: send NMI if vblank flag still set and nmi_enable + * changes from 0 to 1 + */ + break; + case 0x2001: + ppu.mask.grayscale = byte & 1; + ppu.mask.bg_left_enable = byte & (1 << 1); + ppu.mask.sprite_left_enable = byte & (1 << 2); + ppu.mask.bg_enable = byte & (1 << 3); + ppu.mask.sprite_enable = byte & (1 << 4); + ppu.mask.colour_emphasis = byte & (1 << 5); + + break; + case 0x2003: + ppu.oam_addr = byte; + + break; + case 0x2004: + ppu.oam[ppu.oam_addr++] = byte; + + break; + case 0x2005: + if (ppu.regs.write_toggle == 0) + ppu.regs.scroll_x = byte; + else + ppu.regs.scroll_y = byte; + ppu.regs.write_toggle = !ppu.regs.write_toggle; + + break; + case 0x2006: + if (ppu.regs.write_toggle == 0) + ppu.regs.v = (byte << 8); + else { + ppu.regs.v |= byte; + ppu.regs.v &= 0x3fff; + } + ppu.regs.write_toggle = !ppu.regs.write_toggle; + + break; + case 0x2007: + ppu.vram[ppu.regs.v] = byte; + vram_addr_inc(); + + break; + case 0x4014: + break; + default: + fprintf(stderr, "Invalid PPU write at address $%04X\n", addr); + return; + } } diff --git a/ppu.h b/ppu.h index c5483e1..d7cd93c 100644 --- a/ppu.h +++ b/ppu.h @@ -17,11 +17,11 @@ struct ppu { uint8_t last_read; struct ppu_regs { - uint16_t v : 15; /* current vram address */ - uint16_t t : 15; /* temporary vram address */ - uint8_t x : 3; /* fine x scroll */ - uint8_t w : 1; /* first/second write toggle */ - uint8_t padding : 6; + uint16_t v : 15; /* current vram address */ + uint16_t t : 15; /* temporary vram address */ + uint16_t scroll_x : 9; /* x scroll position */ + uint16_t scroll_y : 9; /* y scroll position */ + uint8_t write_toggle : 1; /* first/second write toggle */ } regs; struct ppu_ctrl { /* $2000 */ @@ -31,7 +31,7 @@ struct ppu { uint8_t bg_tile_sel : 1; uint8_t sprite_tile_sel : 1; uint8_t inc_mode : 1; - uint8_t nametable_sel : 2; + uint8_t nametable_base : 2; } ctrl; struct ppu_mask { /* $2001 */ uint8_t colour_emphasis : 3;