1use alloc::vec;
7use alloc::vec::Vec;
8use crate::{
9 apu::{
10 dmc::Dmc,
11 pulse::{OutputFreq, Pulse, PulseChannel},
12 PULSE_TABLE,
13 },
14 audio::Audio,
15 cart::Cart,
16 common::{Clock, ResetKind, NesRegion, Regional, Reset},
17 cpu::Cpu,
18 mapper::{Mapped, MappedRead, MappedWrite, Mapper, MemMap},
19 mem::MemBanks,
20 ppu::{bus::PpuAddr, Mirroring, Ppu},
21};
22use bitflags::bitflags;
23use serde::{Deserialize, Serialize};
24
25#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
26#[must_use]
27enum PrgMode {
28 Bank32k,
29 Bank16k,
30 Bank16_8k,
31 Bank8k,
32}
33
34#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
35#[must_use]
36enum ChrMode {
37 Bank8k,
38 Bank4k,
39 Bank2k,
40 Bank1k,
41}
42
43#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
44#[must_use]
45enum ChrBank {
46 Spr,
47 Bg,
48}
49
50bitflags! {
51 #[derive(Default, Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq)]
52 #[must_use]
53 struct ExRamRW: u8 {
54 const W = 0x01;
55 const R = 0x02;
56 const RW = Self::R.bits() | Self::W.bits();
57 }
58}
59
60#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
61#[must_use]
62struct ExRamMode {
63 bits: u8,
64 nametable: bool,
65 attr: bool,
66 rw: ExRamRW,
67}
68
69impl ExRamMode {
70 const fn new() -> Self {
71 Self {
72 bits: 0x00,
73 nametable: false,
74 attr: false,
75 rw: ExRamRW::W,
76 }
77 }
78
79 fn set(&mut self, val: u8) {
80 let val = val & 0x03;
81 self.bits = val;
82 self.nametable = matches!(val, 0b00 | 0b01);
83 self.attr = val == 0b01;
84 self.rw = match val {
85 0b00 | 0b01 => ExRamRW::W,
86 0b10 => ExRamRW::RW,
87 0b11 => ExRamRW::R,
88 _ => unreachable!("invalid exram_mode"),
89 };
90 }
91}
92
93#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
94#[must_use]
95enum Nametable {
96 ScreenA,
97 ScreenB,
98 ExRam,
99 Fill,
100}
101
102#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
103#[must_use]
104struct NametableMapping {
105 mode: u8,
106 select: [Nametable; 4],
107}
108
109impl NametableMapping {
110 const fn new() -> Self {
111 Self {
112 mode: 0x00,
113 select: [Nametable::ScreenA; 4],
114 }
115 }
116
117 fn set(&mut self, val: u8) {
118 let nametable = |val: u8| match val & 0x03 {
119 0 => Nametable::ScreenA,
120 1 => Nametable::ScreenB,
121 2 => Nametable::ExRam,
122 3 => Nametable::Fill,
123 _ => unreachable!("invalid Nametable value"),
124 };
125 self.mode = val;
126 self.select = [
127 nametable(val),
128 nametable(val >> 2),
129 nametable(val >> 4),
130 nametable(val >> 6),
131 ];
132 }
133}
134
135#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
136#[must_use]
137struct Fill {
138 tile: u8, attr: usize, }
141
142impl Fill {
143 const fn new() -> Self {
144 Self {
145 attr: 0x03,
146 tile: 0xFF,
147 }
148 }
149}
150
151#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
152#[must_use]
153enum Side {
154 Left,
155 Right,
156}
157
158#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
159#[must_use]
160struct VSplit {
161 mode: u8, enabled: bool, side: Side, tile: u8, scroll: u8, bank: u8, in_region: bool,
168}
169
170impl VSplit {
171 const fn new() -> Self {
172 Self {
173 mode: 0x00,
174 enabled: false,
175 side: Side::Left,
176 tile: 0x00,
177 scroll: 0x00,
178 bank: 0x00,
179 in_region: false,
180 }
181 }
182}
183
184#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
185#[must_use]
186struct ExRegs {
187 prg_mode: PrgMode, chr_mode: ChrMode, prg_ram_protect: [u8; 2], exram_mode: ExRamMode, nametable_mapping: NametableMapping, fill: Fill, prg_banks: [usize; 5], chr_banks: [usize; 16], chr_hi: usize, vsplit: VSplit, irq_scanline: u16, irq_enabled: bool, multiplicand: u8, multiplier: u8, mult_result: u16, }
203
204impl ExRegs {
205 const fn new() -> Self {
206 Self {
207 prg_mode: PrgMode::Bank8k,
208 chr_mode: ChrMode::Bank1k,
209 prg_ram_protect: [0x00; 2],
210 exram_mode: ExRamMode::new(),
211 nametable_mapping: NametableMapping::new(),
212 fill: Fill::new(),
213 prg_banks: [0x00; 5],
214 chr_banks: [0x00; 16],
215 chr_hi: 0x00,
216 vsplit: VSplit::new(),
217 irq_scanline: 0x00,
218 irq_enabled: false,
219 multiplicand: 0xFF,
220 multiplier: 0xFF,
221 mult_result: 0xFE01, }
223 }
224}
225
226#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
227#[must_use]
228struct PpuStatus {
229 fetch_count: u32,
230 prev_addr: u16,
231 prev_match: u8,
232 reading: bool,
233 idle: u8,
234 sprite8x16: bool, rendering: bool,
236 scanline: u16,
237 in_frame: bool,
238}
239
240impl PpuStatus {
241 fn write(&mut self, addr: u16, val: u8) {
242 match addr {
243 0x2000 => self.sprite8x16 = val & 0x20 > 0,
244 0x2001 => {
245 self.rendering = val & 0x18 > 0; if !self.rendering {
247 self.in_frame = false;
248 self.prev_addr = 0x0000;
249 }
250 }
251 _ => (),
252 }
253 }
254}
255
256#[derive(Clone, Serialize, Deserialize)]
257#[must_use]
258pub struct Exrom {
259 regs: ExRegs,
260 mirroring: Mirroring,
261 irq_pending: bool,
262 ppu_status: PpuStatus,
263 exram: Vec<u8>,
264 prg_ram_banks: MemBanks,
265 prg_rom_banks: MemBanks,
266 chr_banks: MemBanks,
267 tile_cache: usize,
268 last_chr_write: ChrBank,
269 region: NesRegion,
270 pulse1: Pulse,
271 pulse2: Pulse,
272 dmc: Dmc,
273 dmc_mode: u8,
274 cpu_cycle: usize,
275 pulse_timer: f32,
276}
277
278impl Exrom {
279 const PRG_WINDOW: usize = 0x2000;
280 const PRG_RAM_SIZE: usize = 0x10000; const EXRAM_SIZE: usize = 0x0400;
282 const CHR_WINDOW: usize = 0x0400;
283
284 const ROM_SELECT_MASK: usize = 0x80; const BANK_MASK: usize = 0x7F; const SPR_FETCH_START: u32 = 64;
288 const SPR_FETCH_END: u32 = 81;
289
290 const ATTR_MIRROR: [u8; 4] = [0x00, 0x55, 0xAA, 0xFF];
293
294 pub fn load(cart: &mut Cart) -> Mapper {
324 cart.add_prg_ram(Self::PRG_RAM_SIZE);
325
326 let mut exrom = Self {
327 regs: ExRegs::new(),
328 mirroring: cart.mirroring(),
329 irq_pending: false,
330 ppu_status: PpuStatus {
331 fetch_count: 0x00,
332 prev_addr: 0xFFFF,
333 prev_match: 0x0000,
334 reading: false,
335 idle: 0x00,
336 sprite8x16: false,
337 rendering: false,
338 scanline: 0x0000,
339 in_frame: false,
340 },
341 exram: vec![0x00; Self::EXRAM_SIZE],
342 prg_ram_banks: MemBanks::new(0x6000, 0xFFFF, cart.prg_ram.len(), Self::PRG_WINDOW),
343 prg_rom_banks: MemBanks::new(0x8000, 0xFFFF, cart.prg_rom.len(), Self::PRG_WINDOW),
344 chr_banks: MemBanks::new(0x0000, 0x1FFF, cart.chr_rom.len(), Self::CHR_WINDOW),
345 tile_cache: 0,
346 last_chr_write: ChrBank::Spr,
347 region: NesRegion::default(),
348 pulse1: Pulse::new(PulseChannel::One, OutputFreq::Ultrasonic),
349 pulse2: Pulse::new(PulseChannel::Two, OutputFreq::Ultrasonic),
350 dmc: Dmc::new(),
351 dmc_mode: 0x01, cpu_cycle: 0,
353 pulse_timer: 0.0,
354 };
355 exrom.regs.prg_banks[4] = exrom.prg_rom_banks.last() | Self::ROM_SELECT_MASK;
356 exrom.update_prg_banks();
357 exrom.into()
358 }
359
360 fn update_prg_banks(&mut self) {
371 let mode = self.regs.prg_mode;
372 let banks = self.regs.prg_banks;
373
374 self.prg_ram_banks.set(0, banks[0]); match mode {
376 PrgMode::Bank32k => self.prg_rom_banks.set_range(0, 3, banks[4]),
378 PrgMode::Bank16k => {
379 self.set_prg_bank_range(0, 1, banks[2]);
380 self.prg_rom_banks
381 .set_range(2, 3, banks[4] & Self::BANK_MASK);
382 }
383 PrgMode::Bank16_8k => {
384 self.set_prg_bank_range(0, 1, banks[2]);
385 self.set_prg_bank_range(2, 2, banks[3]);
386 self.prg_rom_banks.set(3, banks[4] & Self::BANK_MASK);
387 }
388 PrgMode::Bank8k => {
389 self.set_prg_bank_range(0, 0, banks[1]);
390 self.set_prg_bank_range(1, 1, banks[2]);
391 self.set_prg_bank_range(2, 2, banks[3]);
392 self.prg_rom_banks.set(3, banks[4] & Self::BANK_MASK);
393 }
394 };
395 }
396
397 fn set_prg_bank_range(&mut self, start: usize, end: usize, bank: usize) {
398 let rom = bank & Self::ROM_SELECT_MASK == Self::ROM_SELECT_MASK;
399 let bank = bank & Self::BANK_MASK;
400 if rom {
401 self.prg_rom_banks.set_range(start, end, bank);
402 } else {
403 self.prg_ram_banks.set_range(start + 1, end + 1, bank);
404 }
405 }
406
407 fn rom_select(&self, addr: u16) -> bool {
408 let mode = self.regs.prg_mode;
409 if matches!(addr, 0x6000..=0x7FFF) {
410 false
411 } else if matches!(addr, 0xE000..=0xFFFF) || mode == PrgMode::Bank32k {
412 true
413 } else {
414 use PrgMode::{Bank16_8k, Bank16k, Bank8k};
415 let banks = self.regs.prg_banks;
416 let bank = match (addr, mode) {
417 (0x8000..=0x9FFF, Bank8k) => banks[1],
418 (0x8000..=0xBFFF, Bank16k | Bank16_8k) | (0xA000..=0xBFFF, Bank8k) => banks[2],
419 (0xC000..=0xDFFF, Bank8k | Bank16_8k) => banks[3],
420 (0xC000..=0xDFFF, Bank16k) => banks[4],
421 _ => 0x00,
422 };
423 bank & Self::ROM_SELECT_MASK == Self::ROM_SELECT_MASK
424 }
425 }
426
427 fn update_chr_banks(&mut self, chr_bank: ChrBank) {
451 let hi = self.regs.chr_hi;
452 let banks = match chr_bank {
453 ChrBank::Spr => &self.regs.chr_banks[0..8],
454 ChrBank::Bg => &self.regs.chr_banks[8..16],
455 };
456 match self.regs.chr_mode {
458 ChrMode::Bank8k => self.chr_banks.set_range(0, 7, hi | banks[7] << 3),
459 ChrMode::Bank4k => {
460 self.chr_banks.set_range(0, 3, hi | banks[3] << 2);
461 self.chr_banks.set_range(4, 7, hi | banks[7] << 2);
462 }
463 ChrMode::Bank2k => {
464 self.chr_banks.set_range(0, 1, hi | banks[1] << 1);
465 self.chr_banks.set_range(2, 3, hi | banks[3] << 1);
466 self.chr_banks.set_range(4, 5, hi | banks[5] << 1);
467 self.chr_banks.set_range(6, 7, hi | banks[7] << 1);
468 }
469 ChrMode::Bank1k => {
470 self.chr_banks.set(0, hi | banks[0]);
471 self.chr_banks.set(1, hi | banks[1]);
472 self.chr_banks.set(2, hi | banks[2]);
473 self.chr_banks.set(3, hi | banks[3]);
474 self.chr_banks.set(4, hi | banks[4]);
475 self.chr_banks.set(5, hi | banks[5]);
476 self.chr_banks.set(6, hi | banks[6]);
477 self.chr_banks.set(7, hi | banks[7]);
478 }
479 };
480 }
481
482 #[inline]
483 fn read_exram(&self, addr: u16) -> u8 {
484 self.exram[(addr & 0x03FF) as usize]
485 }
486
487 #[inline]
488 fn write_exram(&mut self, addr: u16, val: u8) {
489 self.exram[(addr & 0x03FF) as usize] = val;
490 }
491
492 #[inline]
493 fn inc_fetch_count(&mut self) {
494 self.ppu_status.fetch_count += 1;
495 }
496
497 #[inline]
498 const fn fetch_count(&self) -> u32 {
499 self.ppu_status.fetch_count
500 }
501
502 #[inline]
503 const fn sprite8x16(&self) -> bool {
504 self.ppu_status.sprite8x16
505 }
506
507 #[inline]
508 fn spr_fetch(&self) -> bool {
509 (Self::SPR_FETCH_START..Self::SPR_FETCH_END).contains(&self.fetch_count())
510 }
511
512 #[inline]
513 const fn nametable_select(&self, addr: u16) -> Nametable {
514 self.regs.nametable_mapping.select[((addr >> 10) & 0x03) as usize]
515 }
516}
517
518impl Mapped for Exrom {
519 #[inline]
520 fn irq_pending(&self) -> bool {
521 self.regs.irq_enabled && self.irq_pending
522 }
523
524 #[inline]
525 fn mirroring(&self) -> Mirroring {
526 self.mirroring
527 }
528
529 #[inline]
530 fn set_mirroring(&mut self, mirroring: Mirroring) {
531 self.mirroring = mirroring;
532 }
533
534 #[inline]
535 fn cpu_bus_write(&mut self, addr: u16, val: u8) {
536 self.ppu_status.write(addr, val);
537 }
538}
539
540impl Regional for Exrom {
541 #[inline]
542 fn region(&self) -> NesRegion {
543 self.dmc.region()
544 }
545
546 #[inline]
547 fn set_region(&mut self, region: NesRegion) {
548 self.dmc.set_region(region);
549 }
550}
551
552impl MemMap for Exrom {
553 fn map_read(&mut self, addr: u16) -> MappedRead {
601 match addr {
602 0x0000..=0x1FFF => {
603 self.inc_fetch_count();
604 if self.sprite8x16() {
605 match self.fetch_count() {
606 Self::SPR_FETCH_START => self.update_chr_banks(ChrBank::Spr),
607 Self::SPR_FETCH_END => self.update_chr_banks(ChrBank::Bg),
608 _ => (),
609 }
610 }
611 }
612 0x2000..=0x3EFF => {
613 let is_attr = addr.is_attr();
614 if self.regs.exram_mode.attr && !is_attr && !self.spr_fetch() {
616 self.tile_cache = (addr & 0x03FF).into();
617 }
618
619 let status = &mut self.ppu_status;
628 if !is_attr && addr == status.prev_addr {
629 status.prev_match += 1;
630 if status.prev_match == 2 {
631 if status.in_frame {
632 status.scanline = status.scanline.wrapping_add(1);
633 if status.scanline == self.regs.irq_scanline {
634 self.irq_pending = true;
635 }
636 } else {
637 status.in_frame = true;
638 status.scanline = 0;
639 }
640 status.fetch_count = 0;
641 }
642 } else {
643 status.prev_match = 0;
644 }
645 status.prev_addr = addr;
646 status.reading = true;
647 }
648 0xFFFA | 0xFFFB => {
649 self.ppu_status.in_frame = false; self.ppu_status.prev_addr = 0x0000;
651 }
652 _ => (),
653 }
654 let val = self.map_peek(addr);
655 match addr {
656 0x5204 => self.irq_pending = false, 0x5010 => self.dmc.acknowledge_irq(),
658 _ => (),
659 }
660 val
661 }
662
663 fn map_peek(&self, addr: u16) -> MappedRead {
664 match addr {
665 0x0000..=0x1FFF => {
666 if self.regs.exram_mode.attr && !self.spr_fetch() {
667 let bank_hi = self.regs.chr_hi << 10;
669 let bank_lo = ((self.exram[self.tile_cache] & 0x3F) as usize) << 12;
671 let addr = bank_hi | bank_lo | (addr as usize) & 0x0FFF;
672 MappedRead::Chr(addr)
673 } else {
674 MappedRead::Chr(self.chr_banks.translate(addr))
675 }
676 }
677 0x2000..=0x3EFF => {
678 let is_attr = addr.is_attr();
679 if self.regs.vsplit.in_region {
680 if is_attr {
681 todo!()
682 } else {
688 MappedRead::Data(self.read_exram(self.regs.vsplit.tile.into()))
689 }
690 } else if self.regs.exram_mode.attr && is_attr && !self.spr_fetch() {
691 let attr = (self.exram[self.tile_cache] >> 6) & 0x03;
693 MappedRead::Data(Self::ATTR_MIRROR[attr as usize])
694 } else {
695 let nametable_mode = self.regs.exram_mode.nametable;
696 match self.nametable_select(addr) {
697 Nametable::ScreenA => MappedRead::CIRam((addr & 0x03FF).into()),
698 Nametable::ScreenB => {
699 MappedRead::CIRam((Ppu::NT_SIZE | (addr & 0x03FF)).into())
700 }
701 Nametable::ExRam if nametable_mode => {
702 MappedRead::Data(self.read_exram(addr))
703 }
704 Nametable::Fill if nametable_mode => MappedRead::Data(if is_attr {
705 Self::ATTR_MIRROR[self.regs.fill.attr]
706 } else {
707 self.regs.fill.tile
708 }),
709 _ => MappedRead::Data(0x00),
711 }
712 }
713 }
714 0x5010 => {
715 let irq = self.dmc.irq_pending() && self.dmc.irq_enabled();
719 MappedRead::Data(u8::from(irq) << 7 | self.dmc_mode)
720 }
721 0x5100 => MappedRead::Data(self.regs.prg_mode as u8),
722 0x5101 => MappedRead::Data(self.regs.chr_mode as u8),
723 0x5104 => MappedRead::Data(self.regs.exram_mode.bits),
724 0x5105 => MappedRead::Data(self.regs.nametable_mapping.mode),
725 0x5106 => MappedRead::Data(self.regs.fill.tile),
726 0x5107 => MappedRead::Data(self.regs.fill.attr as u8),
727 0x5015 => {
728 let mut status = 0x00;
730 if self.pulse1.length_counter() > 0 {
731 status |= 0x01;
732 }
733 if self.pulse2.length_counter() > 0 {
734 status |= 0x02;
735 }
736 MappedRead::Data(status)
737 }
738 0x5113..=0x5117 => {
739 MappedRead::Data(self.regs.prg_banks[(addr - 0x5113) as usize] as u8)
740 }
741 0x5120..=0x512B => {
742 MappedRead::Data(self.regs.chr_banks[(addr - 0x5120) as usize] as u8)
743 }
744 0x5130 => MappedRead::Data(self.regs.chr_hi as u8),
745 0x5200 => MappedRead::Data(self.regs.vsplit.mode),
746 0x5201 => MappedRead::Data(self.regs.vsplit.scroll),
747 0x5202 => MappedRead::Data(self.regs.vsplit.bank),
748 0x5203 => MappedRead::Data(self.regs.irq_scanline as u8),
749 0x5204 => {
750 MappedRead::Data(
757 u8::from(self.irq_pending) << 7 | u8::from(self.ppu_status.in_frame) << 6,
758 )
759 }
760 0x5205 => MappedRead::Data((self.regs.mult_result & 0xFF) as u8),
761 0x5206 => MappedRead::Data(((self.regs.mult_result >> 8) & 0xFF) as u8),
762 0x5C00..=0x5FFF if matches!(self.regs.exram_mode.rw, ExRamRW::R | ExRamRW::RW) => {
763 MappedRead::Data(self.read_exram(addr))
765 }
766 0x6000..=0xDFFF => {
767 if self.rom_select(addr) {
768 MappedRead::PrgRom(self.prg_rom_banks.translate(addr))
769 } else {
770 MappedRead::PrgRam(self.prg_ram_banks.translate(addr))
771 }
772 }
773 0xE000..=0xFFFF => MappedRead::PrgRom(self.prg_rom_banks.translate(addr)),
774 0x5207..=0x5209 => MappedRead::Data(0),
775 _ => MappedRead::None,
776 }
777 }
778
779 fn map_write(&mut self, addr: u16, val: u8) -> MappedWrite {
780 match addr {
781 0x2000..=0x3EFF => match self.nametable_select(addr) {
782 Nametable::ScreenA => return MappedWrite::CIRam((addr & 0x03FF).into(), val),
783 Nametable::ScreenB => {
784 return MappedWrite::CIRam((Ppu::NT_SIZE | (addr & 0x03FF)).into(), val)
785 }
786 Nametable::ExRam if self.regs.exram_mode.nametable => {
787 self.write_exram(addr, val);
788 }
789 _ => (),
790 },
791 0x5000 => self.pulse1.write_ctrl(val),
792 0x5002 => self.pulse1.write_timer_lo(val),
794 0x5003 => self.pulse1.write_timer_hi(val),
795 0x5004 => self.pulse2.write_ctrl(val),
796 0x5006 => self.pulse2.write_timer_lo(val),
798 0x5007 => self.pulse2.write_timer_hi(val),
799 0x5010 => {
800 self.dmc_mode = val & 0x01;
804 self.dmc.set_enabled(val & 0x80 == 0x80, self.cpu_cycle);
805 }
806 0x5011 => {
807 if self.dmc_mode == 0 && val != 0x00 {
810 self.dmc.write_output(val);
811 }
812 }
813 0x5015 => {
814 self.pulse1.set_enabled(val & 0x01 == 0x01);
816 self.pulse2.set_enabled(val & 0x02 == 0x02);
817 }
818 0x5100 => {
819 self.regs.prg_mode = match val & 0x03 {
821 0 => PrgMode::Bank32k,
822 1 => PrgMode::Bank16k,
823 2 => PrgMode::Bank16_8k,
824 3 => PrgMode::Bank8k,
825 _ => {
826 log::warn!("invalid PrgMode value: ${:02X}", val);
827 self.regs.prg_mode
828 }
829 };
830 self.update_prg_banks();
831 }
832 0x5101 => {
833 if self.regs.exram_mode.attr {
835 self.regs.chr_mode = ChrMode::Bank4k;
837 } else {
838 self.regs.chr_mode = match val & 0x03 {
839 0 => ChrMode::Bank8k,
840 1 => ChrMode::Bank4k,
841 2 => ChrMode::Bank2k,
842 3 => ChrMode::Bank1k,
843 _ => {
844 log::warn!("invalid ChrMode value: ${:02X}", val);
845 self.regs.chr_mode
846 }
847 };
848 }
849 self.update_chr_banks(self.last_chr_write);
850 }
851 0x5102 | 0x5103 => {
852 self.regs.prg_ram_protect[(addr - 0x5102) as usize] = val & 0x03;
855 let writable =
860 self.regs.prg_ram_protect[0] == 0b10 && self.regs.prg_ram_protect[1] == 0b01;
861 return MappedWrite::PrgRamProtect(!writable);
862 }
863 0x5104 => {
864 self.regs.exram_mode.set(val);
871 }
872 0x5105 => {
873 self.regs.nametable_mapping.set(val);
886
887 self.mirroring = match val {
896 0x50 => Mirroring::Horizontal,
897 0x44 => Mirroring::Vertical,
898 0x00 => Mirroring::SingleScreenA,
899 0x55 => Mirroring::SingleScreenB,
900 _ => Mirroring::FourScreen,
902 };
903 }
904 0x5106 => self.regs.fill.tile = val, 0x5107 => self.regs.fill.attr = (val & 0x03).into(), 0x5113..=0x5117 => {
907 let bank = (addr - 0x5113) as usize;
914 self.regs.prg_banks[bank] = val as usize;
915 self.update_prg_banks();
916 }
917 0x5120..=0x512B => {
918 let bank = (addr - 0x5120) as usize;
919 self.regs.chr_banks[bank] = val as usize;
920 if addr < 0x5128 {
921 self.update_chr_banks(ChrBank::Spr);
922 } else {
923 self.regs.chr_banks[bank + 4] = self.regs.chr_banks[bank];
925 self.update_chr_banks(ChrBank::Bg);
926 }
927 }
928 0x5130 => self.regs.chr_hi = (val as usize & 0x03) << 8, 0x5200 => {
930 self.regs.vsplit.enabled = val & 0x80 == 0x80;
935 self.regs.vsplit.side = if val & 0x40 == 0x40 {
936 Side::Right
937 } else {
938 Side::Left
939 };
940 self.regs.vsplit.tile = val & 0x1F;
941 }
942 0x5201 => self.regs.vsplit.scroll = val, 0x5202 => self.regs.vsplit.bank = val, 0x5203 => self.regs.irq_scanline = u16::from(val), 0x5204 => self.regs.irq_enabled = val & 0x80 > 0, 0x5205 => {
947 self.regs.multiplicand = val;
948 self.regs.mult_result =
949 u16::from(self.regs.multiplicand) * u16::from(self.regs.multiplier);
950 }
951 0x5206 => {
952 self.regs.multiplier = val;
953 self.regs.mult_result =
954 u16::from(self.regs.multiplicand) * u16::from(self.regs.multiplier);
955 }
956 0x5207..=0x5209 => {}
957 0x5C00..=0x5FFF => match self.regs.exram_mode.rw {
958 ExRamRW::W => {
959 let val = if self.ppu_status.rendering { val } else { 0x00 };
960 self.write_exram(addr, val);
961 }
962 ExRamRW::RW => self.write_exram(addr, val),
963 _ => (),
964 },
965 0x6000..=0xDFFF if !self.rom_select(addr) => {
966 return MappedWrite::PrgRam(self.prg_ram_banks.translate(addr), val);
967 }
968 _ => (),
969 }
970 MappedWrite::None
971 }
972}
973
974impl Audio for Exrom {
975 #[must_use]
976 fn output(&self) -> f32 {
977 let pulse1 = self.pulse1.output();
978 let pulse2 = self.pulse2.output();
979 let dmc = self.dmc.output();
980 let pulse_scale = PULSE_TABLE[PULSE_TABLE.len() - 1] / 15.0;
981 let out = -(pulse1 + pulse2 + dmc);
982 pulse_scale * out
983 }
984}
985
986impl Clock for Exrom {
987 fn clock(&mut self) -> usize {
988 if self.ppu_status.reading {
989 self.ppu_status.idle = 0;
990 } else {
991 self.ppu_status.idle += 1;
992 if self.ppu_status.idle == 3 {
994 self.ppu_status.idle = 0;
995 self.ppu_status.in_frame = false;
996 self.ppu_status.prev_addr = 0x0000;
997 }
998 }
999 self.ppu_status.reading = false;
1000 if self.cpu_cycle & 0x01 == 0x00 {
1001 self.pulse1.clock();
1002 self.pulse2.clock();
1003 self.dmc.clock();
1004 }
1005 self.pulse_timer -= 1.0;
1006 if self.pulse_timer <= 0.0 {
1007 self.pulse1.clock_quarter_frame();
1008 self.pulse1.clock_half_frame();
1009 self.pulse2.clock_quarter_frame();
1010 self.pulse2.clock_half_frame();
1011 self.pulse_timer = Cpu::region_clock_rate(self.region) / 240.0;
1012 }
1013 self.cpu_cycle = self.cpu_cycle.wrapping_add(1);
1014 1
1015 }
1016}
1017
1018impl Reset for Exrom {
1019 fn reset(&mut self, _kind: ResetKind) {
1020 self.regs.prg_mode = PrgMode::Bank8k;
1021 self.regs.chr_mode = ChrMode::Bank1k;
1022 }
1023}
1024
1025impl core::fmt::Debug for Exrom {
1026 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1027 f.debug_struct("Exrom")
1028 .field("regs", &self.regs)
1029 .field("mirroring", &self.mirroring)
1030 .field("irq_pending", &self.irq_pending)
1031 .field("ppu_status", &self.ppu_status)
1032 .field("exram_len", &self.exram.len())
1033 .field("prg_ram_banks", &self.prg_ram_banks)
1034 .field("prg_rom_banks", &self.prg_rom_banks)
1035 .field("chr_banks", &self.chr_banks)
1036 .field("tile_cache", &self.tile_cache)
1037 .field("last_chr_write", &self.last_chr_write)
1038 .field("region", &self.region)
1039 .field("pulse1", &self.pulse1)
1040 .field("pulse2", &self.pulse2)
1041 .field("dmc", &self.dmc)
1042 .field("dmc_mode", &self.dmc_mode)
1043 .field("cpu_cycle", &self.cpu_cycle)
1044 .field("pulse_timer", &self.pulse_timer)
1045 .finish()
1046 }
1047}