#[cfg(test)]
mod tests {
use crate::cartridge::NametableLayout;
use crate::cartridge::mapper::{Mapper, MapperContext, create_mapper};
use crate::cartridge::test_helpers::banked_data;
const PRG_BANKS: usize = 11;
const CHR_BANKS: usize = 1;
fn make_mapper() -> Box<dyn Mapper> {
let prg = banked_data(8 * 1024, PRG_BANKS);
let chr = banked_data(8 * 1024, CHR_BANKS);
create_mapper(MapperContext::new_for_test(
84,
prg,
chr,
NametableLayout::Vertical,
))
.expect("Mapper 84 should be implemented")
}
#[test]
fn mapper_84_is_registered_in_factory() {
let result = create_mapper(MapperContext::new_for_test(
84,
banked_data(8 * 1024, PRG_BANKS),
banked_data(8 * 1024, CHR_BANKS),
NametableLayout::Vertical,
));
assert!(
result.is_ok(),
"Mapper 84 must be registered in the factory"
);
}
#[test]
fn prg_6000_is_fixed_bank_6() {
let mapper = make_mapper();
assert_eq!(
mapper.read_prg(0x6000),
6,
"$6000 must read from fixed bank 6"
);
}
#[test]
fn prg_8000_is_fixed_bank_4() {
let mapper = make_mapper();
assert_eq!(
mapper.read_prg(0x8000),
4,
"$8000 must read from fixed bank 4"
);
}
#[test]
fn prg_a000_is_fixed_bank_5() {
let mapper = make_mapper();
assert_eq!(
mapper.read_prg(0xA000),
5,
"$A000 must read from fixed bank 5"
);
}
#[test]
fn prg_e000_is_fixed_bank_7() {
let mapper = make_mapper();
assert_eq!(
mapper.read_prg(0xE000),
7,
"$E000 must read from fixed bank 7"
);
}
#[test]
fn prg_c000_defaults_to_bank_0() {
let mapper = make_mapper();
assert_eq!(
mapper.read_prg(0xC000),
0,
"$C000 defaults to bank 0 on power-on"
);
}
#[test]
fn prg_c000_selects_bank_via_e000_register() {
let mut mapper = make_mapper();
mapper.write_prg(0xE000, 3);
assert_eq!(
mapper.read_prg(0xC000),
3,
"$E000 write must select bank 3 at $C000"
);
}
#[test]
fn prg_bank_selection_wraps_at_total_bank_count() {
let mut mapper = make_mapper();
mapper.write_prg(0xE000, 11); assert_eq!(
mapper.read_prg(0xC000),
0,
"bank index must wrap mod total banks"
);
}
#[test]
fn mirroring_is_preserved_from_header() {
let mapper = create_mapper(MapperContext::new_for_test(
84,
banked_data(8 * 1024, PRG_BANKS),
banked_data(8 * 1024, CHR_BANKS),
NametableLayout::Horizontal,
))
.expect("Mapper 84 should be implemented");
assert_eq!(mapper.get_mirroring(), NametableLayout::Horizontal);
}
#[test]
fn irq_not_pending_initially() {
let mapper = make_mapper();
assert!(!mapper.irq_pending(), "IRQ must not be pending on power-on");
}
#[test]
fn irq_does_not_fire_while_disabled() {
let mut mapper = make_mapper();
for _ in 0..8192 {
mapper.cpu_cycle();
}
assert!(!mapper.irq_pending(), "IRQ must not fire when disabled");
}
#[test]
fn irq_fires_after_4096_cycles_when_enabled() {
let mut mapper = make_mapper();
mapper.write_prg(0xA000, 0); for _ in 0..4096 {
mapper.cpu_cycle();
}
assert!(mapper.irq_pending(), "IRQ must fire after 4096 CPU cycles");
}
#[test]
fn irq_does_not_fire_before_4096_cycles() {
let mut mapper = make_mapper();
mapper.write_prg(0xA000, 0); for _ in 0..4095 {
mapper.cpu_cycle();
}
assert!(
!mapper.irq_pending(),
"IRQ must not fire before 4096 cycles"
);
}
#[test]
fn irq_self_acknowledges_at_8192_cycles() {
let mut mapper = make_mapper();
mapper.write_prg(0xA000, 0); for _ in 0..8192 {
mapper.cpu_cycle();
}
assert!(
!mapper.irq_pending(),
"IRQ must self-acknowledge after 8192 cycles"
);
}
#[test]
fn irq_acknowledged_and_disabled_by_8000_write() {
let mut mapper = make_mapper();
mapper.write_prg(0xA000, 0); for _ in 0..4096 {
mapper.cpu_cycle();
}
assert!(mapper.irq_pending());
mapper.write_prg(0x8000, 0); assert!(!mapper.irq_pending(), "IRQ must clear after $8000 write");
}
#[test]
fn irq_counter_resets_when_re_enabled() {
let mut mapper = make_mapper();
mapper.write_prg(0xA000, 0);
for _ in 0..4096 {
mapper.cpu_cycle();
}
mapper.write_prg(0x8000, 0); mapper.write_prg(0xA000, 0); for _ in 0..4095 {
mapper.cpu_cycle();
}
assert!(
!mapper.irq_pending(),
"IRQ must not fire until 4096 cycles after re-enable"
);
}
#[test]
fn registers_snapshot_and_restore() {
let mut mapper = make_mapper();
mapper.write_prg(0xE000, 2);
mapper.write_prg(0xA000, 0); for _ in 0..100 {
mapper.cpu_cycle();
}
let snap = mapper.registers_snapshot();
let mut restored = make_mapper();
restored.restore_registers(&snap);
assert_eq!(
restored.read_prg(0xC000),
mapper.read_prg(0xC000),
"Restored $C000 bank must match"
);
assert_eq!(
restored.irq_pending(),
mapper.irq_pending(),
"Restored IRQ pending state must match"
);
}
}