diff options
| -rw-r--r-- | ppu.c | 88 | ||||
| -rw-r--r-- | ppu.h | 12 |
2 files changed, 83 insertions, 17 deletions
| @@ -38,6 +38,10 @@ void | |||
| 38 | ppu_tick(void) | 38 | ppu_tick(void) |
| 39 | { | 39 | { |
| 40 | ppu.cycles++; | 40 | ppu.cycles++; |
| 41 | |||
| 42 | if (ppu.cycles > 256 && ppu.cycles <= 320) | ||
| 43 | ppu.oam_addr = 0; | ||
| 44 | |||
| 41 | if (ppu.cycles >= 341) { | 45 | if (ppu.cycles >= 341) { |
| 42 | ppu.cycles -= 341; | 46 | ppu.cycles -= 341; |
| 43 | ppu.scanlines++; | 47 | ppu.scanlines++; |
| @@ -58,23 +62,22 @@ ppu_read(uint16_t addr) | |||
| 58 | | (ppu.status.sprite_overflow << 5); | 62 | | (ppu.status.sprite_overflow << 5); |
| 59 | 63 | ||
| 60 | ppu.status.vblank = 0; | 64 | ppu.status.vblank = 0; |
| 61 | ppu.regs.w = 0; | 65 | ppu.regs.write_toggle = 0; |
| 62 | 66 | ||
| 63 | return tmp; | 67 | return tmp; |
| 64 | case 0x2004: | 68 | case 0x2004: |
| 65 | return ppu.oam[ppu.oam_addr]; | 69 | return ppu.oam[ppu.oam_addr]; |
| 66 | case 0x2007: | 70 | case 0x2007: |
| 67 | vram_addr_inc(); | 71 | tmp = ppu.last_read; |
| 68 | 72 | ||
| 69 | if (addr <= 0x1fff) { | 73 | if (addr <= 0x1fff) |
| 70 | tmp = ppu.last_read; | ||
| 71 | ppu.last_read = ppu.rom->chr_rom[ppu.regs.v]; | 74 | ppu.last_read = ppu.rom->chr_rom[ppu.regs.v]; |
| 72 | return tmp; | 75 | else if (addr <= 0x2fff) |
| 73 | } else if (addr <= 0x2fff) { | ||
| 74 | tmp = ppu.last_read; | ||
| 75 | ppu.last_read = ppu.vram[vram_addr_mirror(addr)]; | 76 | ppu.last_read = ppu.vram[vram_addr_mirror(addr)]; |
| 76 | return tmp; | 77 | |
| 77 | } | 78 | vram_addr_inc(); |
| 79 | |||
| 80 | return tmp; | ||
| 78 | default: | 81 | default: |
| 79 | fprintf(stderr, "Invalid PPU read at address $%04\n", addr); | 82 | fprintf(stderr, "Invalid PPU read at address $%04\n", addr); |
| 80 | return 0; | 83 | return 0; |
| @@ -84,7 +87,70 @@ ppu_read(uint16_t addr) | |||
| 84 | void | 87 | void |
| 85 | ppu_write(uint16_t addr, uint8_t byte) | 88 | ppu_write(uint16_t addr, uint8_t byte) |
| 86 | { | 89 | { |
| 87 | static uint8_t prev; | 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; | ||
| 88 | 133 | ||
| 89 | prev = byte; | 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 | } | ||
| 90 | } | 156 | } |
| @@ -17,11 +17,11 @@ struct ppu { | |||
| 17 | uint8_t last_read; | 17 | uint8_t last_read; |
| 18 | 18 | ||
| 19 | struct ppu_regs { | 19 | struct ppu_regs { |
| 20 | uint16_t v : 15; /* current vram address */ | 20 | uint16_t v : 15; /* current vram address */ |
| 21 | uint16_t t : 15; /* temporary vram address */ | 21 | uint16_t t : 15; /* temporary vram address */ |
| 22 | uint8_t x : 3; /* fine x scroll */ | 22 | uint16_t scroll_x : 9; /* x scroll position */ |
| 23 | uint8_t w : 1; /* first/second write toggle */ | 23 | uint16_t scroll_y : 9; /* y scroll position */ |
| 24 | uint8_t padding : 6; | 24 | uint8_t write_toggle : 1; /* first/second write toggle */ |
| 25 | } regs; | 25 | } regs; |
| 26 | 26 | ||
| 27 | struct ppu_ctrl { /* $2000 */ | 27 | struct ppu_ctrl { /* $2000 */ |
| @@ -31,7 +31,7 @@ struct ppu { | |||
| 31 | uint8_t bg_tile_sel : 1; | 31 | uint8_t bg_tile_sel : 1; |
| 32 | uint8_t sprite_tile_sel : 1; | 32 | uint8_t sprite_tile_sel : 1; |
| 33 | uint8_t inc_mode : 1; | 33 | uint8_t inc_mode : 1; |
| 34 | uint8_t nametable_sel : 2; | 34 | uint8_t nametable_base : 2; |
| 35 | } ctrl; | 35 | } ctrl; |
| 36 | struct ppu_mask { /* $2001 */ | 36 | struct ppu_mask { /* $2001 */ |
| 37 | uint8_t colour_emphasis : 3; | 37 | uint8_t colour_emphasis : 3; |
