1use crate::{
4 common::{NesRegion, Regional, Reset, ResetKind},
5 mapper::{BusKind, MapRead, MapWrite, MappedRead, MappedWrite, Mapper, Mirrored, OnBusWrite},
6 mem::{ConstSlice, Memory, RamState, Read, Write},
7 ppu::{Mirroring, Ppu},
8};
9use serde::{Deserialize, Serialize};
10use tracing::error;
11
12pub trait PpuAddr {
13 fn is_attr(&self) -> bool;
15 fn is_palette(&self) -> bool;
16}
17
18impl PpuAddr for u16 {
19 fn is_attr(&self) -> bool {
20 (*self & (Ppu::NT_SIZE - 1)) >= Ppu::ATTR_OFFSET
21 }
22
23 fn is_palette(&self) -> bool {
24 *self >= Ppu::PALETTE_START
25 }
26}
27
28#[derive(Debug, Clone, Serialize, Deserialize)]
29#[must_use]
30pub struct Bus {
31 pub mapper: Mapper,
32 pub chr: Memory<Vec<u8>>,
33 pub ciram: Memory<ConstSlice<u8, 0x0800>>, pub palette: Memory<ConstSlice<u8, 32>>,
35 #[serde(skip)]
36 pub exram: Memory<Vec<u8>>,
37 pub open_bus: u8,
38}
39
40impl Default for Bus {
41 fn default() -> Self {
42 Self::new(RamState::default())
43 }
44}
45
46impl Bus {
47 pub const VRAM_SIZE: usize = 0x0800; pub const PALETTE_SIZE: usize = 32; pub fn new(ram_state: RamState) -> Self {
51 Self {
52 mapper: Mapper::none(),
53 chr: Memory::rom(),
54 ciram: Memory::ram_const(ram_state),
55 palette: Memory::ram_const(ram_state),
56 exram: Memory::ram(ram_state),
57 open_bus: 0x00,
58 }
59 }
60
61 pub fn mirroring(&self) -> Mirroring {
62 self.mapper.mirroring()
63 }
64
65 pub fn load_chr(&mut self, chr: Memory<Vec<u8>>) {
66 self.chr = chr;
67 }
68
69 pub fn load_ex_ram(&mut self, ex_ram: Memory<Vec<u8>>) {
70 self.exram = ex_ram;
71 }
72
73 pub const fn ciram_mirror(addr: u16, mirroring: Mirroring) -> usize {
92 let nametable = (addr >> mirroring as u16) & Ppu::NT_SIZE;
93 (nametable | (!nametable & addr & 0x03FF)) as usize
94 }
95
96 const fn palette_mirror(&self, addr: u16) -> usize {
97 let addr = addr & 0x001F;
98 let addr = if addr >= 16 && addr.trailing_zeros() >= 2 {
99 addr - 16
100 } else {
101 addr
102 };
103 addr as usize
104 }
105
106 pub fn read_ciram(&mut self, addr: u16) -> u8 {
107 match self.mapper.map_read(addr) {
108 MappedRead::Bus => self.ciram[Self::ciram_mirror(addr, self.mirroring())],
109 MappedRead::CIRam(addr) => self.ciram[addr],
110 MappedRead::ExRam(addr) => self.exram[addr],
111 MappedRead::Data(data) => data,
112 MappedRead::Chr(addr) => self.chr[addr],
113 MappedRead::PrgRom(mapped) => {
114 tracing::warn!("unexpected mapped PRG-ROM read at ${addr:04X} ${mapped:04X}");
115 0
116 }
117 MappedRead::PrgRam(mapped) => {
118 tracing::warn!("unexpected mapped PRG-RAM read at ${addr:04X} ${mapped:04X}");
119 0
120 }
121 }
122 }
123
124 pub fn peek_ciram(&self, addr: u16) -> u8 {
125 match self.mapper.map_peek(addr) {
126 MappedRead::Bus => self.ciram[Self::ciram_mirror(addr, self.mirroring())],
127 MappedRead::CIRam(addr) => self.ciram[addr],
128 MappedRead::ExRam(addr) => self.exram[addr],
129 MappedRead::Data(data) => data,
130 MappedRead::Chr(addr) => self.chr[addr],
131 MappedRead::PrgRom(mapped) => {
132 tracing::warn!("unexpected mapped PRG-ROM read at ${addr:04X} ${mapped:04X}");
133 0
134 }
135 MappedRead::PrgRam(mapped) => {
136 tracing::warn!("unexpected mapped PRG-RAM read at ${addr:04X} ${mapped:04X}");
137 0
138 }
139 }
140 }
141
142 pub fn read_chr(&mut self, addr: u16) -> u8 {
143 let addr = if let MappedRead::Chr(addr) = self.mapper.map_read(addr) {
144 addr
145 } else {
146 addr.into()
147 };
148 self.chr[addr]
149 }
150
151 pub fn peek_chr(&self, addr: u16) -> u8 {
152 let addr = if let MappedRead::Chr(addr) = self.mapper.map_peek(addr) {
153 addr
154 } else {
155 addr.into()
156 };
157 self.chr[addr]
158 }
159
160 #[inline]
161 #[allow(clippy::missing_const_for_fn)] pub fn peek_palette(&self, addr: u16) -> u8 {
163 self.palette[self.palette_mirror(addr)]
164 }
165}
166
167impl Read for Bus {
168 fn read(&mut self, addr: u16) -> u8 {
169 let val = match addr {
170 0x2000..=0x3EFF => self.read_ciram(addr),
171 0x0000..=0x1FFF => self.read_chr(addr),
172 0x3F00..=0x3FFF => self.peek_palette(addr),
173 _ => {
174 error!("unexpected PPU memory access at ${:04X}", addr);
175 0x00
176 }
177 };
178 self.open_bus = val;
179 val
180 }
181
182 fn peek(&self, addr: u16) -> u8 {
183 match addr {
184 0x2000..=0x3EFF => self.peek_ciram(addr),
185 0x0000..=0x1FFF => self.peek_chr(addr),
186 0x3F00..=0x3FFF => self.peek_palette(addr),
187 _ => {
188 error!("unexpected PPU memory access at ${:04X}", addr);
189 0x00
190 }
191 }
192 }
193}
194
195impl Write for Bus {
196 fn write(&mut self, addr: u16, val: u8) {
197 match addr {
198 0x0000..=0x3EFF => match self.mapper.map_write(addr, val) {
199 MappedWrite::Bus => {
200 let addr = Self::ciram_mirror(addr, self.mirroring());
201 self.ciram[addr] = val;
202 }
203 MappedWrite::CIRam(addr, val) => self.ciram[addr] = val,
204 MappedWrite::ExRam(addr, val) => self.exram[addr] = val,
205 MappedWrite::ChrRam(addr, val) => {
206 if self.chr.is_ram() {
207 self.chr[addr] = val;
208 }
209 }
210 MappedWrite::PrgRam(mapped, val) => {
211 tracing::warn!(
212 "unexpected mapped PRG-RAM write at ${addr:04X} for ${mapped:04X} with ${val:02X}"
213 );
214 }
215 MappedWrite::PrgRamProtect(val) => {
216 tracing::warn!(
217 "unexpected mapped PRG-RAM Protect write at ${addr:04X} with {val}"
218 );
219 }
220 MappedWrite::None => (),
221 },
222 0x3F00..=0x3FFF => {
223 let addr = self.palette_mirror(addr);
224 self.palette[addr] = val;
225 }
226 _ => error!("unexpected PPU memory access at ${:04X}", addr),
227 }
228 self.mapper.on_bus_write(addr, val, BusKind::Ppu);
229 self.open_bus = val;
230 }
231}
232
233impl Regional for Bus {
234 fn region(&self) -> NesRegion {
235 self.mapper.region()
236 }
237
238 fn set_region(&mut self, region: NesRegion) {
239 self.mapper.set_region(region);
240 }
241}
242
243impl Reset for Bus {
244 fn reset(&mut self, kind: ResetKind) {
245 self.open_bus = 0x00;
246 if self.chr.is_ram() {
247 self.chr.reset(kind);
248 }
249 self.mapper.reset(kind);
250 }
251}
252
253#[cfg(test)]
254mod tests {
255 use super::*;
256
257 #[test]
258 fn ciram_mirror_horizontal() {
259 assert_eq!(Bus::ciram_mirror(0x2000, Mirroring::Horizontal), 0x0000);
260 assert_eq!(Bus::ciram_mirror(0x2005, Mirroring::Horizontal), 0x0005);
261 assert_eq!(Bus::ciram_mirror(0x23FF, Mirroring::Horizontal), 0x03FF);
262 assert_eq!(Bus::ciram_mirror(0x2400, Mirroring::Horizontal), 0x0000);
263 assert_eq!(Bus::ciram_mirror(0x2405, Mirroring::Horizontal), 0x0005);
264 assert_eq!(Bus::ciram_mirror(0x27FF, Mirroring::Horizontal), 0x03FF);
265 assert_eq!(Bus::ciram_mirror(0x2800, Mirroring::Horizontal), 0x0400);
266 assert_eq!(Bus::ciram_mirror(0x2805, Mirroring::Horizontal), 0x0405);
267 assert_eq!(Bus::ciram_mirror(0x2BFF, Mirroring::Horizontal), 0x07FF);
268 assert_eq!(Bus::ciram_mirror(0x2C00, Mirroring::Horizontal), 0x0400);
269 assert_eq!(Bus::ciram_mirror(0x2C05, Mirroring::Horizontal), 0x0405);
270 assert_eq!(Bus::ciram_mirror(0x2FFF, Mirroring::Horizontal), 0x07FF);
271 }
272
273 #[test]
274 fn ciram_mirror_vertical() {
275 assert_eq!(Bus::ciram_mirror(0x2000, Mirroring::Vertical), 0x0000);
276 assert_eq!(Bus::ciram_mirror(0x2005, Mirroring::Vertical), 0x0005);
277 assert_eq!(Bus::ciram_mirror(0x23FF, Mirroring::Vertical), 0x03FF);
278 assert_eq!(Bus::ciram_mirror(0x2800, Mirroring::Vertical), 0x0000);
279 assert_eq!(Bus::ciram_mirror(0x2805, Mirroring::Vertical), 0x0005);
280 assert_eq!(Bus::ciram_mirror(0x2BFF, Mirroring::Vertical), 0x03FF);
281 assert_eq!(Bus::ciram_mirror(0x2400, Mirroring::Vertical), 0x0400);
282 assert_eq!(Bus::ciram_mirror(0x2405, Mirroring::Vertical), 0x0405);
283 assert_eq!(Bus::ciram_mirror(0x27FF, Mirroring::Vertical), 0x07FF);
284 assert_eq!(Bus::ciram_mirror(0x2C00, Mirroring::Vertical), 0x0400);
285 assert_eq!(Bus::ciram_mirror(0x2C05, Mirroring::Vertical), 0x0405);
286 assert_eq!(Bus::ciram_mirror(0x2FFF, Mirroring::Vertical), 0x07FF);
287 }
288
289 #[test]
290 fn ciram_mirror_single_screen_a() {
291 assert_eq!(Bus::ciram_mirror(0x2000, Mirroring::SingleScreenA), 0x0000);
292 assert_eq!(Bus::ciram_mirror(0x2005, Mirroring::SingleScreenA), 0x0005);
293 assert_eq!(Bus::ciram_mirror(0x23FF, Mirroring::SingleScreenA), 0x03FF);
294 assert_eq!(Bus::ciram_mirror(0x2800, Mirroring::SingleScreenA), 0x0000);
295 assert_eq!(Bus::ciram_mirror(0x2805, Mirroring::SingleScreenA), 0x0005);
296 assert_eq!(Bus::ciram_mirror(0x2BFF, Mirroring::SingleScreenA), 0x03FF);
297 assert_eq!(Bus::ciram_mirror(0x2400, Mirroring::SingleScreenA), 0x0000);
298 assert_eq!(Bus::ciram_mirror(0x2405, Mirroring::SingleScreenA), 0x0005);
299 assert_eq!(Bus::ciram_mirror(0x27FF, Mirroring::SingleScreenA), 0x03FF);
300 assert_eq!(Bus::ciram_mirror(0x2C00, Mirroring::SingleScreenA), 0x0000);
301 assert_eq!(Bus::ciram_mirror(0x2C05, Mirroring::SingleScreenA), 0x0005);
302 assert_eq!(Bus::ciram_mirror(0x2FFF, Mirroring::SingleScreenA), 0x03FF);
303 }
304
305 #[test]
306 fn ciram_mirror_single_screen_b() {
307 assert_eq!(Bus::ciram_mirror(0x2000, Mirroring::SingleScreenB), 0x0400);
308 assert_eq!(Bus::ciram_mirror(0x2005, Mirroring::SingleScreenB), 0x0405);
309 assert_eq!(Bus::ciram_mirror(0x23FF, Mirroring::SingleScreenB), 0x07FF);
310 assert_eq!(Bus::ciram_mirror(0x2800, Mirroring::SingleScreenB), 0x0400);
311 assert_eq!(Bus::ciram_mirror(0x2805, Mirroring::SingleScreenB), 0x0405);
312 assert_eq!(Bus::ciram_mirror(0x2BFF, Mirroring::SingleScreenB), 0x07FF);
313 assert_eq!(Bus::ciram_mirror(0x2400, Mirroring::SingleScreenB), 0x0400);
314 assert_eq!(Bus::ciram_mirror(0x2405, Mirroring::SingleScreenB), 0x0405);
315 assert_eq!(Bus::ciram_mirror(0x27FF, Mirroring::SingleScreenB), 0x07FF);
316 assert_eq!(Bus::ciram_mirror(0x2C00, Mirroring::SingleScreenB), 0x0400);
317 assert_eq!(Bus::ciram_mirror(0x2C05, Mirroring::SingleScreenB), 0x0405);
318 assert_eq!(Bus::ciram_mirror(0x2FFF, Mirroring::SingleScreenB), 0x07FF);
319 }
320}