rustynes_mappers/
m151_konami_vs.rs1#![allow(clippy::doc_markdown)]
21
22use crate::cartridge::Mirroring;
23use crate::m075_vrc1::Vrc1;
24use crate::mapper::{Mapper, MapperCaps, MapperDebugInfo, MapperError};
25use alloc::{boxed::Box, vec::Vec};
26
27pub struct KonamiVs {
29 inner: Vrc1,
30}
31
32impl KonamiVs {
33 pub fn new(
40 prg_rom: Box<[u8]>,
41 chr_rom: Box<[u8]>,
42 mirroring: Mirroring,
43 ) -> Result<Self, MapperError> {
44 Ok(Self {
45 inner: Vrc1::new(prg_rom, chr_rom, mirroring)?,
46 })
47 }
48}
49
50impl Mapper for KonamiVs {
51 fn caps(&self) -> MapperCaps {
54 MapperCaps::NONE
55 }
56
57 fn cpu_read(&mut self, addr: u16) -> u8 {
58 self.inner.cpu_read(addr)
59 }
60
61 fn cpu_write(&mut self, addr: u16, value: u8) {
62 self.inner.cpu_write(addr, value);
63 }
64
65 fn ppu_read(&mut self, addr: u16) -> u8 {
66 self.inner.ppu_read(addr)
67 }
68
69 fn ppu_write(&mut self, addr: u16, value: u8) {
70 self.inner.ppu_write(addr, value);
71 }
72
73 fn current_mirroring(&self) -> Mirroring {
74 self.inner.current_mirroring()
75 }
76
77 fn save_state(&self) -> Vec<u8> {
78 self.inner.save_state()
79 }
80
81 fn load_state(&mut self, data: &[u8]) -> Result<(), MapperError> {
82 self.inner.load_state(data)
83 }
84
85 fn debug_info(&self) -> MapperDebugInfo {
86 let mut info = self.inner.debug_info();
87 info.mapper_id = 151;
88 info.name = "Konami VS / VRC1 (151)".into();
89 info
90 }
91}
92
93#[cfg(test)]
94#[allow(clippy::cast_possible_truncation)]
95mod tests {
96 use super::*;
97 use alloc::vec;
98
99 const PRG_BANK_8K: usize = 0x2000;
100 const CHR_BANK_4K: usize = 0x1000;
101
102 fn synth(banks_8k: usize) -> Box<[u8]> {
103 let mut v = vec![0u8; banks_8k * PRG_BANK_8K];
104 for b in 0..banks_8k {
105 v[b * PRG_BANK_8K] = b as u8;
106 }
107 v.into_boxed_slice()
108 }
109
110 fn synth_chr_4k(banks: usize) -> Box<[u8]> {
111 let mut v = vec![0u8; banks * CHR_BANK_4K];
112 for b in 0..banks {
113 v[b * CHR_BANK_4K] = b as u8;
114 }
115 v.into_boxed_slice()
116 }
117
118 #[test]
119 fn delegates_vrc1_banking() {
120 let mut m = KonamiVs::new(synth(8), synth_chr_4k(2), Mirroring::Vertical).unwrap();
121 m.cpu_write(0x8000, 3);
122 assert_eq!(m.cpu_read(0x8000), 3);
123 assert_eq!(m.cpu_read(0xE000), 7);
125 }
126
127 #[test]
128 fn debug_info_reports_mapper_151() {
129 let m = KonamiVs::new(synth(8), synth_chr_4k(2), Mirroring::Vertical).unwrap();
130 assert_eq!(m.debug_info().mapper_id, 151);
131 }
132
133 #[test]
134 fn save_state_round_trip() {
135 let mut m = KonamiVs::new(synth(8), synth_chr_4k(2), Mirroring::Vertical).unwrap();
136 m.cpu_write(0x8000, 5);
137 m.cpu_write(0x9000, 0x01); let blob = m.save_state();
139 let mut m2 = KonamiVs::new(synth(8), synth_chr_4k(2), Mirroring::Vertical).unwrap();
140 m2.load_state(&blob).unwrap();
141 assert_eq!(m.cpu_read(0x8000), m2.cpu_read(0x8000));
142 assert_eq!(m.current_mirroring(), m2.current_mirroring());
143 }
144
145 #[test]
150 fn save_state_carries_chr_ram() {
151 let mut m = KonamiVs::new(synth(8), Box::new([]), Mirroring::Vertical).unwrap();
152 m.ppu_write(0x0000, 0x11);
153 m.ppu_write(0x0FFF, 0x22);
154 let blob = m.save_state();
155 let mut m2 = KonamiVs::new(synth(8), Box::new([]), Mirroring::Vertical).unwrap();
156 m2.load_state(&blob).expect("round-trip");
157 assert_eq!(m2.ppu_read(0x0000), 0x11);
158 assert_eq!(m2.ppu_read(0x0FFF), 0x22);
159 }
160}