#include "ppu.h" struct ppu ppu = {0}; static void vram_addr_inc(void) { ppu.regs.v += (ppu.ctrl.inc_mode) ? 32 : 1; } static uint16_t vram_addr_mirror(uint16_t addr) { uint8_t nametable; addr &= 0x2fff; addr -= 0x2000; nametable = addr / 0x400; switch (ppu.rom->mirror) { case M_HORIZONTAL: if (nametable == 1 || nametable == 2) return addr - 0x400; else if (nametable == 3) return addr - 0x800; break; case M_VERTICAL: if (nametable == 2 || nametable == 3) return addr - 0x800; default: break; }; return addr; } 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++; } if (ppu.scanlines >= 262) ppu.scanlines -= 262; } uint8_t ppu_read(uint16_t addr) { uint8_t tmp; switch (addr) { case 0x2002: tmp = (ppu.status.vblank << 7) | (ppu.status.sprite0_hit << 6) | (ppu.status.sprite_overflow << 5); ppu.status.vblank = 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(); return tmp; default: fprintf(stderr, "Invalid PPU read at address $%04\n", addr); return 0; } } void ppu_write(uint16_t addr, uint8_t byte) { 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); 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; } }