blob: 866e382f596c2271003c6bd8a8b93b3b30f12c56 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
|
#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;
}
}
|