use crate::nes::cartridge::bandai::bandai_fcg::{BandaiFcgMapper, BandaiFcgVariant};
use crate::nes::cartridge::base_mapper::BaseMapper;
use crate::nes::cartridge::mapper::{Mapper, MapperCapabilities, MapperContext};
const MAPPER_NUMBER: u16 = 159;
pub struct Mapper159 {
inner: BandaiFcgMapper,
}
impl Mapper159 {
pub fn new(mut ctx: MapperContext) -> Self {
ctx.prg_ram_banks_8k = 0;
ctx.prg_ram_size_specified = false;
Self {
inner: BandaiFcgMapper::new_with_variant(ctx, BandaiFcgVariant::Lz93d50),
}
}
}
impl Mapper for Mapper159 {
fn base(&self) -> &BaseMapper {
self.inner.base()
}
fn base_mut(&mut self) -> &mut BaseMapper {
self.inner.base_mut()
}
fn mapper_number(&self) -> u16 {
MAPPER_NUMBER
}
fn read_prg(&self, addr: u16) -> u8 {
self.inner.read_prg(addr)
}
fn write_prg(&mut self, addr: u16, value: u8) {
self.inner.write_prg(addr, value);
}
fn read_chr(&mut self, addr: u16) -> u8 {
self.inner.read_chr(addr)
}
fn write_chr(&mut self, addr: u16, value: u8) {
self.inner.write_chr(addr, value);
}
fn cpu_cycle(&mut self) {
self.inner.cpu_cycle();
}
fn irq_pending(&self) -> bool {
self.inner.irq_pending()
}
fn capabilities(&self) -> MapperCapabilities {
self.inner.capabilities()
}
fn wram_size(&self) -> usize {
self.inner.wram_size()
}
fn wram_snapshot(&self) -> Vec<u8> {
self.inner.wram_snapshot()
}
fn load_wram_snapshot(&mut self, data: &[u8]) {
self.inner.load_wram_snapshot(data);
}
fn registers_snapshot(&self) -> Vec<u8> {
self.inner.registers_snapshot()
}
fn restore_registers(&mut self, data: &[u8]) {
self.inner.restore_registers(data);
}
fn initialize_ram(&mut self, mode: crate::nes::console::RamInitMode) {
self.inner.initialize_ram(mode);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::nes::cartridge::NametableLayout;
use crate::nes::cartridge::mapper::{MapperContext, create_mapper};
use crate::nes::cartridge::test_helpers::banked_data;
const PRG_BANKS_16K: usize = 8; const CHR_BANKS_1K: usize = 8;
fn make_mapper() -> Mapper159 {
Mapper159::new(
MapperContext::new_for_test(
MAPPER_NUMBER,
banked_data(16 * 1024, PRG_BANKS_16K),
banked_data(1024, CHR_BANKS_1K),
NametableLayout::Horizontal,
)
.with_unspecified_prg_ram_size(),
)
}
#[test]
fn mapper_159_is_registered_in_factory() {
let result = create_mapper(
MapperContext::new_for_test(
MAPPER_NUMBER,
banked_data(16 * 1024, PRG_BANKS_16K),
banked_data(1024, CHR_BANKS_1K),
NametableLayout::Horizontal,
)
.with_unspecified_prg_ram_size(),
);
assert!(result.is_ok(), "Mapper 159 must be creatable via factory");
}
#[test]
fn power_on_prg_bank_0_at_8000_last_bank_fixed_at_c000() {
let mapper = make_mapper();
assert_eq!(mapper.read_prg(0x8000), 0, "bank 0 at $8000 on power-on");
assert_eq!(
mapper.read_prg(0xC000),
PRG_BANKS_16K as u8 - 1,
"last PRG bank fixed at $C000"
);
}
#[test]
fn register_8008_switches_prg_bank_at_8000() {
let mut mapper = make_mapper();
mapper.write_prg(0x8008, 5);
assert_eq!(
mapper.read_prg(0x8000),
5,
"bank 5 at $8000 after write to $8008"
);
assert_eq!(
mapper.read_prg(0xC000),
PRG_BANKS_16K as u8 - 1,
"last bank fixed at $C000 unchanged"
);
}
#[test]
fn registers_8000_to_8007_switch_chr_banks() {
let mut mapper = make_mapper();
for slot in 0u16..8 {
mapper.write_prg(0x8000 + slot, slot as u8);
let ppu_addr = slot * 0x400;
assert_eq!(
mapper.read_chr(ppu_addr),
slot as u8,
"CHR slot {slot} should map to bank {slot}"
);
}
}
#[test]
fn register_8009_controls_mirroring() {
let mut mapper = make_mapper();
mapper.write_prg(0x8009, 0);
assert_eq!(mapper.get_mirroring(), NametableLayout::Vertical);
mapper.write_prg(0x8009, 1);
assert_eq!(mapper.get_mirroring(), NametableLayout::Horizontal);
}
#[test]
fn irq_fires_after_countdown_when_enabled() {
let mut mapper = make_mapper();
mapper.write_prg(0x800B, 3); mapper.write_prg(0x800C, 0); mapper.write_prg(0x800A, 1);
assert!(
!mapper.irq_pending(),
"IRQ should not be pending before countdown"
);
for _ in 0..3 {
mapper.cpu_cycle();
}
assert!(mapper.irq_pending(), "IRQ should fire after 3 CPU cycles");
}
#[test]
fn registers_snapshot_round_trips() {
let mut mapper = make_mapper();
mapper.write_prg(0x8008, 3);
mapper.write_prg(0x8009, 1);
mapper.write_prg(0x8000, 2);
let snap = mapper.registers_snapshot();
let mut restored = make_mapper();
restored.restore_registers(&snap);
assert_eq!(
restored.read_prg(0x8000),
mapper.read_prg(0x8000),
"Restored PRG bank must match"
);
assert_eq!(
restored.get_mirroring(),
mapper.get_mirroring(),
"Restored mirroring must match"
);
assert_eq!(
restored.read_chr(0x0000),
mapper.read_chr(0x0000),
"Restored CHR bank must match"
);
}
}