1use alloc::vec;
2use alloc::vec::Vec;
3use super::Ppu;
4use crate::{
5 common::{ResetKind, NesRegion, Regional, Reset},
6 mapper::{Mapped, MappedRead, MappedWrite, Mapper, MemMap},
7 mem::{Access, Mem},
8 ppu::Mirroring,
9};
10use serde::{Deserialize, Serialize};
11
12pub trait PpuAddr {
13 fn is_attr(&self) -> bool;
15 fn is_palette(&self) -> bool;
16}
17
18impl PpuAddr for u16 {
19 #[inline]
20 fn is_attr(&self) -> bool {
21 (*self & 0x03FF) >= 0x03C0
22 }
23 #[inline]
24 fn is_palette(&self) -> bool {
25 *self >= 0x3F00
26 }
27}
28
29#[derive(Clone, Serialize, Deserialize)]
30#[must_use]
31pub struct PpuBus {
32 mapper: Mapper,
33 mirror_shift: usize,
34 ciram: Vec<u8>, palette: [u8; Self::PALETTE_SIZE],
36 chr_rom: Vec<u8>,
37 chr_ram: Vec<u8>,
38 exram: Vec<u8>,
39 open_bus: u8,
40}
41
42impl Default for PpuBus {
43 fn default() -> Self {
44 Self::new()
45 }
46}
47
48impl PpuBus {
49 const VRAM_SIZE: usize = 0x0800; const PALETTE_SIZE: usize = 32; pub fn new() -> Self {
53 Self {
54 mapper: Mapper::none(),
55 mirror_shift: Mirroring::default() as usize,
56 ciram: vec![0x00; Self::VRAM_SIZE],
57 palette: [0x00; Self::PALETTE_SIZE],
58 chr_rom: vec![],
59 chr_ram: vec![],
60 exram: vec![],
61 open_bus: 0x00,
62 }
63 }
64
65 #[inline]
66 pub fn mirroring(&self) -> Mirroring {
67 self.mapper.mirroring()
68 }
69
70 #[inline]
71 pub fn update_mirroring(&mut self) {
72 self.mirror_shift = self.mirroring() as usize;
73 }
74
75 #[inline]
76 pub fn load_chr_rom(&mut self, chr_rom: Vec<u8>) {
77 self.chr_rom = chr_rom;
78 }
79
80 #[inline]
81 pub fn load_chr_ram(&mut self, chr_ram: Vec<u8>) {
82 self.chr_ram = chr_ram;
83 }
84
85 #[inline]
86 pub fn load_ex_ram(&mut self, ex_ram: Vec<u8>) {
87 self.exram = ex_ram;
88 }
89
90 #[inline]
91 pub fn load_mapper(&mut self, mapper: Mapper) {
92 self.mapper = mapper;
93 }
94
95 #[inline]
96 pub const fn mapper(&self) -> &Mapper {
97 &self.mapper
98 }
99
100 #[inline]
101 pub fn mapper_mut(&mut self) -> &mut Mapper {
102 &mut self.mapper
103 }
104
105 #[inline]
123 const fn ciram_mirror(&self, addr: usize) -> usize {
124 let nametable = (addr >> self.mirror_shift) & (Ppu::NT_SIZE as usize);
125 (nametable) | (!nametable & addr & 0x03FF)
126 }
127
128 #[inline]
129 const fn palette_mirror(&self, addr: usize) -> usize {
130 let addr = addr & 0x001F;
131 if addr >= 16 && addr.trailing_zeros() >= 2 {
132 addr - 16
133 } else {
134 addr
135 }
136 }
137}
138
139impl Mem for PpuBus {
140 fn read(&mut self, addr: u16, _access: Access) -> u8 {
141 let val = match addr {
142 0x0000..=0x1FFF => {
143 let addr = if let MappedRead::Chr(addr) = self.mapper.map_read(addr) {
144 addr
145 } else {
146 addr.into()
147 };
148 if self.chr_rom.is_empty() {
149 self.chr_ram[addr]
150 } else {
151 self.chr_rom[addr]
152 }
153 }
154 0x2000..=0x3EFF => match self.mapper.map_read(addr) {
155 MappedRead::CIRam(addr) => self.ciram[addr & 0x07FF],
156 MappedRead::ExRam(addr) => self.exram[addr & 0x03FF],
157 MappedRead::Data(data) => data,
158 _ => {
159 if self.mirroring() == Mirroring::FourScreen {
160 0x00
161 } else {
162 self.ciram[self.ciram_mirror(addr as usize)]
163 }
164 }
165 },
166 0x3F00..=0x3FFF => self.palette[self.palette_mirror(addr as usize)],
167 _ => {
168 log::error!("unexpected PPU memory access at ${:04X}", addr);
169 0x00
170 }
171 };
172 self.open_bus = val;
173 val
174 }
175
176 fn peek(&self, addr: u16, _access: Access) -> u8 {
177 match addr {
178 0x2000..=0x3EFF => match self.mapper.map_peek(addr) {
179 MappedRead::CIRam(addr) => self.ciram[addr & 0x07FF],
180 MappedRead::ExRam(addr) => self.exram[addr & 0x03FF],
181 MappedRead::Data(data) => data,
182 _ => {
183 if self.mirroring() == Mirroring::FourScreen {
184 0x00
185 } else {
186 self.ciram[self.ciram_mirror(addr as usize)]
187 }
188 }
189 },
190 0x0000..=0x1FFF => {
191 let addr = if let MappedRead::Chr(addr) = self.mapper.map_peek(addr) {
192 addr
193 } else {
194 addr.into()
195 };
196 if !self.chr_ram.is_empty() {
197 self.chr_ram[addr]
198 } else {
199 self.chr_rom[addr]
200 }
201 }
202 0x3F00..=0x3FFF => self.palette[self.palette_mirror(addr as usize)],
203 _ => {
204 log::error!("unexpected PPU memory access at ${:04X}", addr);
205 0x00
206 }
207 }
208 }
209
210 fn write(&mut self, addr: u16, val: u8, _access: Access) {
211 match addr {
212 0x2000..=0x3EFF => match self.mapper.map_write(addr, val) {
213 MappedWrite::CIRam(addr, val) => self.ciram[addr] = val,
214 MappedWrite::ExRam(addr, val) => self.exram[addr] = val,
215 _ => {
216 if self.mirroring() != Mirroring::FourScreen {
217 let addr = self.ciram_mirror(addr as usize);
218 self.ciram[addr] = val;
219 }
220 }
221 },
222 0x0000..=0x1FFF => {
223 if !self.chr_ram.is_empty() {
224 if let MappedWrite::Chr(addr, val) = self.mapper.map_write(addr, val) {
225 self.chr_ram[addr] = val;
226 }
227 }
228 }
229 0x3F00..=0x3FFF => {
230 self.palette[self.palette_mirror(addr as usize)] = val;
231 }
232 _ => log::error!("unexpected PPU memory access at ${:04X}", addr),
233 }
234 self.mapper.ppu_bus_write(addr, val);
235 self.open_bus = val;
236 }
237}
238
239impl Regional for PpuBus {
240 #[inline]
241 fn region(&self) -> NesRegion {
242 self.mapper.region()
243 }
244
245 fn set_region(&mut self, region: NesRegion) {
246 self.mapper.set_region(region);
247 }
248}
249
250impl Reset for PpuBus {
251 fn reset(&mut self, kind: ResetKind) {
252 self.open_bus = 0x00;
253 self.mapper.reset(kind);
254 }
255}
256
257impl core::fmt::Debug for PpuBus {
258 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
259 f.debug_struct("PpuBus")
260 .field("mapper", &self.mapper)
261 .field("ciram_len", &self.ciram.len())
262 .field("palette_len", &self.palette.len())
263 .field("chr_rom_len", &self.chr_rom.len())
264 .field("chr_ram_len", &self.chr_ram.len())
265 .field("ex_ram_len", &self.exram.len())
266 .field("open_bus", &self.open_bus)
267 .finish()
268 }
269}
270
271#[cfg(test)]
272mod tests {
273 use super::*;
274
275 #[test]
276 fn ciram_mirror_horizontal() {
277 let mut ppu_bus = PpuBus::new();
278 ppu_bus.mirror_shift = Mirroring::Horizontal as usize;
279
280 assert_eq!(ppu_bus.ciram_mirror(0x2000), 0x0000);
281 assert_eq!(ppu_bus.ciram_mirror(0x2005), 0x0005);
282 assert_eq!(ppu_bus.ciram_mirror(0x23FF), 0x03FF);
283 assert_eq!(ppu_bus.ciram_mirror(0x2400), 0x0000);
284 assert_eq!(ppu_bus.ciram_mirror(0x2405), 0x0005);
285 assert_eq!(ppu_bus.ciram_mirror(0x27FF), 0x03FF);
286
287 assert_eq!(ppu_bus.ciram_mirror(0x2800), 0x0400);
288 assert_eq!(ppu_bus.ciram_mirror(0x2805), 0x0405);
289 assert_eq!(ppu_bus.ciram_mirror(0x2BFF), 0x07FF);
290 assert_eq!(ppu_bus.ciram_mirror(0x2C00), 0x0400);
291 assert_eq!(ppu_bus.ciram_mirror(0x2C05), 0x0405);
292 assert_eq!(ppu_bus.ciram_mirror(0x2FFF), 0x07FF);
293 }
294
295 #[test]
296 fn ciram_mirror_vertical() {
297 let mut ppu_bus = PpuBus::new();
298 ppu_bus.mirror_shift = Mirroring::Vertical as usize;
299
300 assert_eq!(ppu_bus.ciram_mirror(0x2000), 0x0000);
301 assert_eq!(ppu_bus.ciram_mirror(0x2005), 0x0005);
302 assert_eq!(ppu_bus.ciram_mirror(0x23FF), 0x03FF);
303 assert_eq!(ppu_bus.ciram_mirror(0x2800), 0x0000);
304 assert_eq!(ppu_bus.ciram_mirror(0x2805), 0x0005);
305 assert_eq!(ppu_bus.ciram_mirror(0x2BFF), 0x03FF);
306
307 assert_eq!(ppu_bus.ciram_mirror(0x2400), 0x0400);
308 assert_eq!(ppu_bus.ciram_mirror(0x2405), 0x0405);
309 assert_eq!(ppu_bus.ciram_mirror(0x27FF), 0x07FF);
310 assert_eq!(ppu_bus.ciram_mirror(0x2C00), 0x0400);
311 assert_eq!(ppu_bus.ciram_mirror(0x2C05), 0x0405);
312 assert_eq!(ppu_bus.ciram_mirror(0x2FFF), 0x07FF);
313 }
314
315 #[test]
316 fn ciram_mirror_single_screen_a() {
317 let mut ppu_bus = PpuBus::new();
318 ppu_bus.mirror_shift = Mirroring::SingleScreenA as usize;
319
320 assert_eq!(ppu_bus.ciram_mirror(0x2000), 0x0000);
321 assert_eq!(ppu_bus.ciram_mirror(0x2005), 0x0005);
322 assert_eq!(ppu_bus.ciram_mirror(0x23FF), 0x03FF);
323 assert_eq!(ppu_bus.ciram_mirror(0x2800), 0x0000);
324 assert_eq!(ppu_bus.ciram_mirror(0x2805), 0x0005);
325 assert_eq!(ppu_bus.ciram_mirror(0x2BFF), 0x03FF);
326 assert_eq!(ppu_bus.ciram_mirror(0x2400), 0x0000);
327 assert_eq!(ppu_bus.ciram_mirror(0x2405), 0x0005);
328 assert_eq!(ppu_bus.ciram_mirror(0x27FF), 0x03FF);
329 assert_eq!(ppu_bus.ciram_mirror(0x2C00), 0x0000);
330 assert_eq!(ppu_bus.ciram_mirror(0x2C05), 0x0005);
331 assert_eq!(ppu_bus.ciram_mirror(0x2FFF), 0x03FF);
332 }
333
334 #[test]
335 fn ciram_mirror_single_screen_b() {
336 let mut ppu_bus = PpuBus::new();
337 ppu_bus.mirror_shift = Mirroring::SingleScreenB as usize;
338
339 assert_eq!(ppu_bus.ciram_mirror(0x2000), 0x0400);
340 assert_eq!(ppu_bus.ciram_mirror(0x2005), 0x0405);
341 assert_eq!(ppu_bus.ciram_mirror(0x23FF), 0x07FF);
342 assert_eq!(ppu_bus.ciram_mirror(0x2800), 0x0400);
343 assert_eq!(ppu_bus.ciram_mirror(0x2805), 0x0405);
344 assert_eq!(ppu_bus.ciram_mirror(0x2BFF), 0x07FF);
345 assert_eq!(ppu_bus.ciram_mirror(0x2400), 0x0400);
346 assert_eq!(ppu_bus.ciram_mirror(0x2405), 0x0405);
347 assert_eq!(ppu_bus.ciram_mirror(0x27FF), 0x07FF);
348 assert_eq!(ppu_bus.ciram_mirror(0x2C00), 0x0400);
349 assert_eq!(ppu_bus.ciram_mirror(0x2C05), 0x0405);
350 assert_eq!(ppu_bus.ciram_mirror(0x2FFF), 0x07FF);
351 }
352}