1#![allow(clippy::cast_possible_truncation, clippy::missing_const_for_fn)]
35
36use crate::cartridge::Mirroring;
37use crate::m001_mmc1::Mmc1;
38use crate::mapper::{Mapper, MapperCaps, MapperDebugInfo, MapperError};
39use alloc::{boxed::Box, format, vec, vec::Vec};
40
41const PRG_BANK_16K: usize = 0x4000;
42const CHIP: usize = 0x2_0000;
43const CHR_RAM: usize = 0x2000;
44const WRAM: usize = 0x2000;
45const SAVE_STATE_VERSION: u8 = 1;
46const MAX_TARGET: u32 = 0x3E00_0000;
48
49pub const NWC_TOURNAMENT_DIP: u8 = 0b0100;
51
52pub struct NesEvent105 {
54 mmc1: Mmc1,
55 prg_rom: Box<[u8]>,
56 chr_ram: Box<[u8]>,
57 wram: Box<[u8]>,
58 unlocked: bool,
59 seen_i_low: bool,
62 counter: u32,
63 irq_pending: bool,
64 dip: u8,
65}
66
67impl NesEvent105 {
68 pub fn new(prg_rom: Box<[u8]>, mirroring: Mirroring) -> Result<Self, MapperError> {
74 if prg_rom.is_empty() || !prg_rom.len().is_multiple_of(2 * PRG_BANK_16K) {
75 return Err(MapperError::Invalid(format!(
76 "mapper 105 PRG-ROM size {} is not a non-zero multiple of 32 KiB",
77 prg_rom.len()
78 )));
79 }
80 let mmc1 = Mmc1::new(
83 vec![0u8; 2 * PRG_BANK_16K].into_boxed_slice(),
84 Box::new([]),
85 mirroring,
86 0,
87 )?;
88 Ok(Self {
89 mmc1,
90 prg_rom,
91 chr_ram: vec![0u8; CHR_RAM].into_boxed_slice(),
92 wram: vec![0u8; WRAM].into_boxed_slice(),
93 unlocked: false,
94 seen_i_low: false,
95 counter: 0,
96 irq_pending: false,
97 dip: NWC_TOURNAMENT_DIP,
98 })
99 }
100
101 pub fn set_dip(&mut self, dip: u8) {
103 self.dip = dip & 0x0F;
104 }
105
106 fn target(&self) -> u32 {
107 0x2000_0000 | (u32::from(self.dip) << 25)
108 }
109
110 fn i_bit(&self) -> bool {
111 self.mmc1.registers().1 & 0x10 != 0
112 }
113
114 fn prg_offset(&self, addr: u16) -> usize {
115 let (control, chr0, _, prg) = self.mmc1.registers();
116 let within = usize::from(addr & 0x3FFF);
117 let high = addr & 0x4000 != 0;
118 let bank16 = if !self.unlocked {
119 usize::from(high)
120 } else if chr0 & 0x08 == 0 {
121 usize::from((chr0 >> 1) & 0x03) * 2 + usize::from(high)
122 } else {
123 let chip = CHIP / PRG_BANK_16K;
124 let p = usize::from(prg & 0x07);
125 let inner = match (control >> 2) & 0x03 {
126 0 | 1 => (p & !1) + usize::from(high),
127 2 => {
128 if high {
129 p
130 } else {
131 0
132 }
133 }
134 _ => {
135 if high {
136 chip - 1
137 } else {
138 p
139 }
140 }
141 };
142 chip + inner
143 };
144 (bank16 * PRG_BANK_16K + within) % self.prg_rom.len()
145 }
146
147 fn wram_disabled(&self) -> bool {
148 self.mmc1.registers().3 & 0x10 != 0
149 }
150}
151
152impl Mapper for NesEvent105 {
153 fn sram(&self) -> &[u8] {
154 &self.wram
155 }
156
157 fn sram_mut(&mut self) -> &mut [u8] {
158 &mut self.wram
159 }
160
161 fn caps(&self) -> MapperCaps {
162 MapperCaps::CYCLE_IRQ
163 }
164
165 fn reset(&mut self) {
166 self.unlocked = false;
167 self.seen_i_low = false;
168 }
169
170 fn cpu_read_unmapped(&self, addr: u16) -> bool {
171 match addr {
172 0x6000..=0x7FFF => self.wram_disabled(),
173 _ => addr < 0x6000,
174 }
175 }
176
177 fn cpu_read(&mut self, addr: u16) -> u8 {
178 match addr {
179 0x6000..=0x7FFF => self.wram[usize::from(addr - 0x6000)],
180 0x8000..=0xFFFF => self.prg_rom[self.prg_offset(addr)],
181 _ => 0,
182 }
183 }
184
185 fn cpu_write(&mut self, addr: u16, value: u8) {
186 match addr {
187 0x6000..=0x7FFF if !self.wram_disabled() => {
188 self.wram[usize::from(addr - 0x6000)] = value;
189 }
190 0x8000..=0xFFFF => {
191 let before = self.mmc1.shift_count();
192 self.mmc1.cpu_write(addr, value);
193 let committed = before == 4 && self.mmc1.shift_count() == 0 && value & 0x80 == 0;
194 if !(committed && addr & 0xE000 == 0xA000) {
195 return;
196 }
197 if self.i_bit() {
199 self.counter = 0;
200 self.irq_pending = false;
201 if self.seen_i_low {
202 self.unlocked = true;
203 }
204 } else {
205 self.seen_i_low = true;
206 }
207 }
208 _ => {}
209 }
210 }
211
212 fn notify_cpu_cycle(&mut self) {
213 self.mmc1.notify_cpu_cycle();
214 if self.i_bit() || self.irq_pending {
215 return;
216 }
217 self.counter += 1;
218 if self.counter >= self.target() {
219 self.irq_pending = true;
220 }
221 }
222
223 fn irq_pending(&self) -> bool {
224 self.irq_pending
225 }
226
227 fn ppu_read(&mut self, addr: u16) -> u8 {
228 let addr = addr & 0x3FFF;
229 if addr < 0x2000 {
230 self.chr_ram[usize::from(addr)]
231 } else {
232 self.mmc1.ppu_read(addr)
233 }
234 }
235
236 fn ppu_write(&mut self, addr: u16, value: u8) {
237 let addr = addr & 0x3FFF;
238 if addr < 0x2000 {
239 self.chr_ram[usize::from(addr)] = value;
240 } else {
241 self.mmc1.ppu_write(addr, value);
242 }
243 }
244
245 fn nametable_address(&self, addr: u16) -> u16 {
246 self.mmc1.nametable_address(addr)
247 }
248
249 fn current_mirroring(&self) -> Mirroring {
250 self.mmc1.current_mirroring()
251 }
252
253 fn debug_info(&self) -> MapperDebugInfo {
254 let mut info = self.mmc1.debug_info();
255 info.mapper_id = 105;
256 info.name = "NES-EVENT (105)".into();
257 info.irq_state
258 .push(("timer".into(), format!("{:#010x}", self.counter)));
259 info.irq_state
260 .push(("target".into(), format!("{:#010x}", self.target())));
261 info.extra
262 .push(("unlocked".into(), format!("{}", self.unlocked)));
263 info
264 }
265
266 fn save_state(&self) -> Vec<u8> {
267 let inner = self.mmc1.save_state();
268 let mut out = Vec::with_capacity(16 + inner.len() + CHR_RAM + WRAM);
269 out.push(SAVE_STATE_VERSION);
270 out.push(u8::from(self.unlocked));
271 out.push(u8::from(self.seen_i_low));
272 out.extend_from_slice(&self.counter.to_le_bytes());
273 out.push(u8::from(self.irq_pending));
274 out.push(self.dip);
275 out.extend_from_slice(&(inner.len() as u32).to_le_bytes());
276 out.extend_from_slice(&inner);
277 out.extend_from_slice(&self.chr_ram);
278 out.extend_from_slice(&self.wram);
279 out
280 }
281
282 fn load_state(&mut self, data: &[u8]) -> Result<(), MapperError> {
283 const HEAD: usize = 13;
284 if data.len() < HEAD {
285 return Err(MapperError::WrongLength {
286 expected: HEAD,
287 got: data.len(),
288 });
289 }
290 if data[0] != SAVE_STATE_VERSION {
291 return Err(MapperError::UnsupportedVersion(data[0]));
292 }
293 let inner_len = u32::from_le_bytes([data[9], data[10], data[11], data[12]]) as usize;
294 let expected = HEAD
298 .checked_add(inner_len)
299 .and_then(|n| n.checked_add(CHR_RAM + WRAM));
300 if expected != Some(data.len()) {
301 return Err(MapperError::WrongLength {
302 expected: expected.unwrap_or(usize::MAX),
303 got: data.len(),
304 });
305 }
306 let counter = u32::from_le_bytes([data[3], data[4], data[5], data[6]]);
310 if counter > MAX_TARGET {
311 return Err(MapperError::Invalid(format!(
312 "mapper 105 timer {counter:#010x} is past the largest target {MAX_TARGET:#010x}"
313 )));
314 }
315 self.mmc1.load_state(&data[HEAD..HEAD + inner_len])?;
316 self.unlocked = data[1] != 0;
317 self.seen_i_low = data[2] != 0;
318 self.counter = counter;
319 self.irq_pending = data[7] != 0;
320 self.dip = data[8] & 0x0F;
321 let cur = HEAD + inner_len;
322 self.chr_ram.copy_from_slice(&data[cur..cur + CHR_RAM]);
323 self.wram.copy_from_slice(&data[cur + CHR_RAM..]);
324 Ok(())
325 }
326}
327
328#[cfg(test)]
329mod tests {
330 use super::*;
331
332 fn board() -> NesEvent105 {
333 let mut prg = vec![0u8; 2 * CHIP];
334 for b in 0..(2 * CHIP / PRG_BANK_16K) {
335 prg[b * PRG_BANK_16K] = b as u8;
336 }
337 NesEvent105::new(prg.into_boxed_slice(), Mirroring::Horizontal).unwrap()
338 }
339
340 fn mmc1(m: &mut NesEvent105, addr: u16, value: u8) {
342 for i in 0..5 {
343 m.cpu_write(addr, (value >> i) & 1);
344 m.notify_cpu_cycle();
346 m.notify_cpu_cycle();
347 }
348 }
349
350 #[test]
351 fn locked_to_the_first_32k_until_i_goes_low_then_high() {
352 let mut m = board();
353 mmc1(&mut m, 0xA000, 0x16); assert_eq!(m.cpu_read(0x8000), 0);
355 assert_eq!(m.cpu_read(0xC000), 1);
356 mmc1(&mut m, 0xA000, 0x06); assert_eq!(m.cpu_read(0x8000), 0, "I=0 alone does not unlock");
358 mmc1(&mut m, 0xA000, 0x16); assert_eq!(m.cpu_read(0x8000), 6, "32 KiB bank 3 of chip 0");
360 assert_eq!(m.cpu_read(0xC000), 7);
361 m.reset();
362 assert_eq!(m.cpu_read(0x8000), 0, "reset locks it again");
363 }
364
365 #[test]
366 fn o_bit_selects_chip_1_with_mmc1_banking() {
367 let mut m = board();
368 mmc1(&mut m, 0xA000, 0x00);
369 mmc1(&mut m, 0xA000, 0x18); mmc1(&mut m, 0x8000, 0x0C); mmc1(&mut m, 0xE000, 0x02);
372 assert_eq!(m.cpu_read(0x8000), 8 + 2);
373 assert_eq!(m.cpu_read(0xC000), 15, "fixed last bank of chip 1");
374 mmc1(&mut m, 0x8000, 0x08); assert_eq!(m.cpu_read(0x8000), 8);
376 assert_eq!(m.cpu_read(0xC000), 10);
377 mmc1(&mut m, 0x8000, 0x00); mmc1(&mut m, 0xE000, 0x05);
379 assert_eq!(m.cpu_read(0x8000), 8 + 4);
380 assert_eq!(m.cpu_read(0xC000), 8 + 5);
381 }
382
383 #[test]
384 fn timer_counts_while_i_is_low_and_fires_at_the_dip_target() {
385 let mut m = board();
386 m.set_dip(0);
387 mmc1(&mut m, 0xA000, 0x00); m.counter = 0x2000_0000 - 3;
389 m.notify_cpu_cycle();
390 m.notify_cpu_cycle();
391 assert!(!m.irq_pending());
392 m.notify_cpu_cycle();
393 assert!(m.irq_pending(), "$20000000 with every switch open");
394 mmc1(&mut m, 0xA000, 0x10); assert!(!m.irq_pending());
396 assert_eq!(m.counter, 0);
397 m.notify_cpu_cycle();
398 assert_eq!(m.counter, 0, "held while I=1");
399 }
400
401 #[test]
402 fn the_tournament_dip_is_the_default() {
403 let m = board();
404 assert_eq!(m.target(), 0x2800_0000);
405 }
406
407 #[test]
408 fn chr_is_unbanked_ram_and_wram_obeys_e000_bit4() {
409 let mut m = board();
410 mmc1(&mut m, 0x8000, 0x10); mmc1(&mut m, 0xC000, 0x01);
412 m.ppu_write(0x1234, 0x5A);
413 assert_eq!(m.ppu_read(0x1234), 0x5A);
414 m.cpu_write(0x6000, 0x77);
415 assert_eq!(m.cpu_read(0x6000), 0x77);
416 mmc1(&mut m, 0xE000, 0x10);
417 assert!(m.cpu_read_unmapped(0x6000));
418 }
419
420 #[test]
424 fn state_refuses_a_counter_past_the_largest_target() {
425 let good = board().save_state();
426 let mut blob = good.clone();
427 blob[3..7].copy_from_slice(&0x3E00_0001u32.to_le_bytes());
428 let mut m = board();
429 assert!(matches!(m.load_state(&blob), Err(MapperError::Invalid(_))));
430 assert_eq!(m.save_state(), good, "refused before any assignment");
431 blob[3..7].copy_from_slice(&0x3E00_0000u32.to_le_bytes());
432 board().load_state(&blob).unwrap();
433 }
434
435 #[test]
439 fn state_refuses_an_embedded_length_that_overflows() {
440 let mut blob = board().save_state();
441 blob[9..13].copy_from_slice(&u32::MAX.to_le_bytes());
442 assert!(board().load_state(&blob).is_err());
443 }
444
445 #[test]
446 fn state_round_trips() {
447 let mut a = board();
448 mmc1(&mut a, 0xA000, 0x00);
449 mmc1(&mut a, 0xA000, 0x1A);
450 a.ppu_write(0x0001, 3);
451 a.cpu_write(0x6001, 4);
452 let blob = a.save_state();
453 let mut b = board();
454 b.load_state(&blob).unwrap();
455 assert_eq!(b.cpu_read(0x8000), a.cpu_read(0x8000));
456 assert_eq!(b.save_state(), blob);
457 }
458}