-
Notifications
You must be signed in to change notification settings - Fork 100
/
sfc_ppu.c
223 lines (211 loc) · 8.06 KB
/
sfc_ppu.c
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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
#include "sfc_ppu.h"
#include <assert.h>
#include <string.h>
/// <summary>
/// StepFC: 读取PPU地址空间
/// </summary>
/// <param name="address">The address.</param>
/// <param name="data">The data.</param>
/// <param name="ppu">The ppu.</param>
uint8_t sfc_read_ppu_address(uint16_t address, sfc_ppu_t* ppu) {
const uint16_t real_address = address & (uint16_t)0x3FFF;
// 使用BANK读取
if (real_address < (uint16_t)0x3F00) {
const uint16_t index = real_address >> 10;
const uint16_t offset = real_address & (uint16_t)0x3FF;
assert(ppu->banks[index]);
const uint8_t data = ppu->pseudo;
ppu->pseudo = ppu->banks[index][offset];
return data;
}
// 调色板索引
else {
// 更新处于调色板"下方"的伪缓存值
const uint16_t underneath = real_address - 0x1000;
const uint16_t index = real_address >> 10;
const uint16_t offset = real_address & (uint16_t)0x3FF;
assert(ppu->banks[index]);
ppu->pseudo = ppu->banks[index][offset];
// 读取调色板能返回即时值
return ppu->spindexes[real_address & (uint16_t)0x1f];
}
}
/// <summary>
/// StepFC: 写入PPU地址空间
/// </summary>
/// <param name="address">The address.</param>
/// <param name="data">The data.</param>
/// <param name="ppu">The ppu.</param>
void sfc_write_ppu_address(uint16_t address, uint8_t data, sfc_ppu_t* ppu) {
const uint16_t real_address = address & (uint16_t)0x3FFF;
// 使用BANK写入
if (real_address < (uint16_t)0x3F00) {
const uint16_t index = real_address >> 10;
const uint16_t offset = real_address & (uint16_t)0x3FF;
assert(ppu->banks[index]);
ppu->banks[index][offset] = data;
}
// 调色板索引
else {
// 独立地址
if (real_address & (uint16_t)0x03) {
ppu->spindexes[real_address & (uint16_t)0x1f] = data;
}
// 镜像$3F00/$3F04/$3F08/$3F0C
else {
const uint16_t offset = real_address & (uint16_t)0x0f;
ppu->spindexes[offset] = data;
ppu->spindexes[offset | (uint16_t)0x10] = data;
}
}
}
/// <summary>
/// StepFC: 使用CPU地址空间读取PPU寄存器
/// </summary>
/// <param name="address">The address.</param>
/// <param name="ppu">The ppu.</param>
/// <returns></returns>
uint8_t sfc_read_ppu_register_via_cpu(uint16_t address, sfc_ppu_t* ppu) {
uint8_t data = 0x00;
// 8字节镜像
switch (address & (uint16_t)0x7)
{
case 0:
// 0x2000: Controller ($2000) > write
// 只写寄存器
case 1:
// 0x2001: Mask ($2001) > write
// 只写寄存器
assert(!"write only!");
break;
case 2:
// 0x2002: Status ($2002) < read
// 只读状态寄存器
data = ppu->status;
// 读取后会清除VBlank状态
ppu->status &= ~(uint8_t)SFC_PPU2002_VBlank;
// wiki.nesdev.com/w/index.php/PPU_scrolling: $2002 read
ppu->writex2 = 0;
break;
case 3:
// 0x2003: OAM address port ($2003) > write
// 只写寄存器
assert(!"write only!");
break;
case 4:
// 0x2004: OAM data ($2004) <> read/write
// 读写寄存器
// - [???] Address should not increment on $2004 read
//data = ppu->sprites[ppu->oamaddr++];
data = ppu->sprites[ppu->oamaddr];
break;
case 5:
// 0x2005: Scroll ($2005) >> write x2
// 双写寄存器
case 6:
// 0x2006: Address ($2006) >> write x2
// 双写寄存器
assert(!"write only!");
break;
case 7:
// 0x2007: Data ($2007) <> read/write
// PPU VRAM读写端口
data = sfc_read_ppu_address(ppu->vramaddr, ppu);
ppu->vramaddr += (uint16_t)((ppu->ctrl & SFC_PPU2000_VINC32) ? 32 : 1);
break;
}
return data;
}
/// <summary>
/// StepFC: 使用CPU地址空间写入PPU寄存器
/// </summary>
/// <param name="address">The address.</param>
/// <param name="data">The data.</param>
/// <param name="ppu">The ppu.</param>
void sfc_write_ppu_register_via_cpu(uint16_t address, uint8_t data, sfc_ppu_t* ppu) {
switch (address & (uint16_t)0x7)
{
case 0:
// PPU 控制寄存器
// 0x2000: Controller ($2000) > write
ppu->ctrl = data;
ppu->nametable_select = data & 3;
break;
case 1:
// PPU 掩码寄存器
// 0x2001: Mask ($2001) > write
ppu->mask = data;
break;
case 2:
// 0x2002: Status ($2002) < read
// 只读
assert(!"read only");
break;
case 3:
// 0x2003: OAM address port ($2003) > write
// PPU OAM 地址端口
ppu->oamaddr = data;
break;
case 4:
// 0x2004: OAM data ($2004) <> read/write
// PPU OAM 数据端口
ppu->sprites[ppu->oamaddr++] = data;
break;
case 5:
// 0x2005: Scroll ($2005) >> write x2
// PPU 滚动位置寄存器 - 双写
ppu->scroll[ppu->writex2 & 1] = data;
++ppu->writex2;
break;
case 6:
// 0x2006: Address ($2006) >> write x2
// PPU 地址寄存器 - 双写
// 写入高字节
if (ppu->writex2 & 1) {
const uint16_t tmp = (ppu->vramaddr & (uint16_t)0xFF00) | (uint16_t)data;
ppu->vramaddr = tmp;
// A-B 位
ppu->nametable_select = (tmp >> 10) & 3;
// 0-4位
ppu->scroll[0] = (ppu->scroll[0] & (uint8_t)7) | ((uint8_t)tmp & (uint8_t)0x1f) << 3;
// 5-9 C-E 位
ppu->scroll[1] = ((tmp & (uint16_t)0x3e0) >> 2) | ((tmp & (uint16_t)0x7000) >> 12);
}
// 写入低字节
else {
ppu->vramaddr = (ppu->vramaddr & (uint16_t)0x00FF) | ((uint16_t)data << 8);
}
++ppu->writex2;
break;
case 7:
// 0x2007: Data ($2007) <> read/write
// PPU VRAM数据端
sfc_write_ppu_address(ppu->vramaddr, data, ppu);
ppu->vramaddr += (uint16_t)((ppu->ctrl & SFC_PPU2000_VINC32) ? 32 : 1);
break;
}
}
/// <summary>
/// 调色板数据
/// </summary>
const union sfc_palette_data {
struct { uint8_t r, g, b, a; };
uint32_t data;
} sfc_stdpalette[64] = {
{ 0x7F, 0x7F, 0x7F, 0xFF }, { 0x20, 0x00, 0xB0, 0xFF }, { 0x28, 0x00, 0xB8, 0xFF }, { 0x60, 0x10, 0xA0, 0xFF },
{ 0x98, 0x20, 0x78, 0xFF }, { 0xB0, 0x10, 0x30, 0xFF }, { 0xA0, 0x30, 0x00, 0xFF }, { 0x78, 0x40, 0x00, 0xFF },
{ 0x48, 0x58, 0x00, 0xFF }, { 0x38, 0x68, 0x00, 0xFF }, { 0x38, 0x6C, 0x00, 0xFF }, { 0x30, 0x60, 0x40, 0xFF },
{ 0x30, 0x50, 0x80, 0xFF }, { 0x00, 0x00, 0x00, 0xFF }, { 0x00, 0x00, 0x00, 0xFF }, { 0x00, 0x00, 0x00, 0xFF },
{ 0xBC, 0xBC, 0xBC, 0xFF }, { 0x40, 0x60, 0xF8, 0xFF }, { 0x40, 0x40, 0xFF, 0xFF }, { 0x90, 0x40, 0xF0, 0xFF },
{ 0xD8, 0x40, 0xC0, 0xFF }, { 0xD8, 0x40, 0x60, 0xFF }, { 0xE0, 0x50, 0x00, 0xFF }, { 0xC0, 0x70, 0x00, 0xFF },
{ 0x88, 0x88, 0x00, 0xFF }, { 0x50, 0xA0, 0x00, 0xFF }, { 0x48, 0xA8, 0x10, 0xFF }, { 0x48, 0xA0, 0x68, 0xFF },
{ 0x40, 0x90, 0xC0, 0xFF }, { 0x00, 0x00, 0x00, 0xFF }, { 0x00, 0x00, 0x00, 0xFF }, { 0x00, 0x00, 0x00, 0xFF },
{ 0xFF, 0xFF, 0xFF, 0xFF }, { 0x60, 0xA0, 0xFF, 0xFF }, { 0x50, 0x80, 0xFF, 0xFF }, { 0xA0, 0x70, 0xFF, 0xFF },
{ 0xF0, 0x60, 0xFF, 0xFF }, { 0xFF, 0x60, 0xB0, 0xFF }, { 0xFF, 0x78, 0x30, 0xFF }, { 0xFF, 0xA0, 0x00, 0xFF },
{ 0xE8, 0xD0, 0x20, 0xFF }, { 0x98, 0xE8, 0x00, 0xFF }, { 0x70, 0xF0, 0x40, 0xFF }, { 0x70, 0xE0, 0x90, 0xFF },
{ 0x60, 0xD0, 0xE0, 0xFF }, { 0x60, 0x60, 0x60, 0xFF }, { 0x00, 0x00, 0x00, 0xFF }, { 0x00, 0x00, 0x00, 0xFF },
{ 0xFF, 0xFF, 0xFF, 0xFF }, { 0x90, 0xD0, 0xFF, 0xFF }, { 0xA0, 0xB8, 0xFF, 0xFF }, { 0xC0, 0xB0, 0xFF, 0xFF },
{ 0xE0, 0xB0, 0xFF, 0xFF }, { 0xFF, 0xB8, 0xE8, 0xFF }, { 0xFF, 0xC8, 0xB8, 0xFF }, { 0xFF, 0xD8, 0xA0, 0xFF },
{ 0xFF, 0xF0, 0x90, 0xFF }, { 0xC8, 0xF0, 0x80, 0xFF }, { 0xA0, 0xF0, 0xA0, 0xFF }, { 0xA0, 0xFF, 0xC8, 0xFF },
{ 0xA0, 0xFF, 0xF0, 0xFF }, { 0xA0, 0xA0, 0xA0, 0xFF }, { 0x00, 0x00, 0x00, 0xFF }, { 0x00, 0x00, 0x00, 0xFF }
};