ppu.c (2951B)
1 #include "ppu.h" 2 3 struct ppu ppu = {0}; 4 5 static void 6 vram_addr_inc(void) 7 { 8 ppu.regs.v += (ppu.ctrl.inc_mode) ? 32 : 1; 9 } 10 11 static uint16_t 12 vram_addr_mirror(uint16_t addr) 13 { 14 uint8_t nametable; 15 16 addr &= 0x2fff; 17 addr -= 0x2000; 18 nametable = addr / 0x400; 19 20 switch (ppu.rom->mirror) { 21 case M_HORIZONTAL: 22 if (nametable == 1 || nametable == 2) 23 return addr - 0x400; 24 else if (nametable == 3) 25 return addr - 0x800; 26 break; 27 case M_VERTICAL: 28 if (nametable == 2 || nametable == 3) 29 return addr - 0x800; 30 default: 31 break; 32 }; 33 34 return addr; 35 } 36 37 void 38 ppu_tick(void) 39 { 40 ppu.cycles++; 41 42 if (ppu.cycles > 256 && ppu.cycles <= 320) 43 ppu.oam_addr = 0; 44 45 if (ppu.cycles >= 341) { 46 ppu.cycles -= 341; 47 ppu.scanlines++; 48 } 49 if (ppu.scanlines >= 262) 50 ppu.scanlines -= 262; 51 } 52 53 uint8_t 54 ppu_read(uint16_t addr) 55 { 56 uint8_t tmp; 57 58 switch (addr) { 59 case 0x2002: 60 tmp = (ppu.status.vblank << 7) 61 | (ppu.status.sprite0_hit << 6) 62 | (ppu.status.sprite_overflow << 5); 63 64 ppu.status.vblank = 0; 65 ppu.regs.write_toggle = 0; 66 67 return tmp; 68 case 0x2004: 69 return ppu.oam[ppu.oam_addr]; 70 case 0x2007: 71 tmp = ppu.last_read; 72 73 if (addr <= 0x1fff) 74 ppu.last_read = ppu.rom->chr_rom[ppu.regs.v]; 75 else if (addr <= 0x2fff) 76 ppu.last_read = ppu.vram[vram_addr_mirror(addr)]; 77 78 vram_addr_inc(); 79 80 return tmp; 81 default: 82 fprintf(stderr, "Invalid PPU read at address $%04\n", addr); 83 return 0; 84 } 85 } 86 87 void 88 ppu_write(uint16_t addr, uint8_t byte) 89 { 90 switch (addr) { 91 case 0x2000: 92 ppu.ctrl.nametable_base = byte & 3; 93 ppu.ctrl.inc_mode = byte & (1 << 2); 94 ppu.ctrl.sprite_tile_sel = byte & (1 << 3); 95 ppu.ctrl.bg_tile_sel = byte & (1 << 4); 96 ppu.ctrl.sprite_height = byte & (1 << 5); 97 ppu.ctrl.master_slave = byte & (1 << 6); 98 ppu.ctrl.nmi_enable = byte & (1 << 7); 99 100 if (ppu.ctrl.nametable_base & 1) 101 ppu.regs.scroll_x += 256; 102 if (ppu.ctrl.nametable_base & 2) 103 ppu.regs.scroll_y += 240; 104 105 /* 106 * TODO: send NMI if vblank flag still set and nmi_enable 107 * changes from 0 to 1 108 */ 109 break; 110 case 0x2001: 111 ppu.mask.grayscale = byte & 1; 112 ppu.mask.bg_left_enable = byte & (1 << 1); 113 ppu.mask.sprite_left_enable = byte & (1 << 2); 114 ppu.mask.bg_enable = byte & (1 << 3); 115 ppu.mask.sprite_enable = byte & (1 << 4); 116 ppu.mask.colour_emphasis = byte & (1 << 5); 117 118 break; 119 case 0x2003: 120 ppu.oam_addr = byte; 121 122 break; 123 case 0x2004: 124 ppu.oam[ppu.oam_addr++] = byte; 125 126 break; 127 case 0x2005: 128 if (ppu.regs.write_toggle == 0) 129 ppu.regs.scroll_x = byte; 130 else 131 ppu.regs.scroll_y = byte; 132 ppu.regs.write_toggle = !ppu.regs.write_toggle; 133 134 break; 135 case 0x2006: 136 if (ppu.regs.write_toggle == 0) 137 ppu.regs.v = (byte << 8); 138 else { 139 ppu.regs.v |= byte; 140 ppu.regs.v &= 0x3fff; 141 } 142 ppu.regs.write_toggle = !ppu.regs.write_toggle; 143 144 break; 145 case 0x2007: 146 ppu.vram[ppu.regs.v] = byte; 147 vram_addr_inc(); 148 149 break; 150 case 0x4014: 151 break; 152 default: 153 fprintf(stderr, "Invalid PPU write at address $%04X\n", addr); 154 return; 155 } 156 }