use crate::nes::cartridge::mapper_templates::SimpleBankedPrgMapper;
pub type UxROMMapper = SimpleBankedPrgMapper<16, 2, true>;
#[cfg(test)]
mod tests {
use super::*;
use crate::nes::cartridge::mapper::MapperContext;
use crate::nes::cartridge::{Mapper, NametableLayout};
#[test]
fn test_uxrom_128kb_prg_bank_switching() {
let mut prg_rom = vec![0; 128 * 1024];
for bank in 0..8 {
let start = bank * 16 * 1024;
let end = start + 16 * 1024;
for byte in &mut prg_rom[start..end] {
*byte = bank as u8;
}
}
let mut mapper = UxROMMapper::new(
MapperContext::new_for_test(2, prg_rom, vec![], NametableLayout::Horizontal)
.with_submapper(2),
);
assert_eq!(mapper.read_prg(0x8000), 0);
assert_eq!(mapper.read_prg(0xC000), 7);
assert_eq!(mapper.read_prg(0xFFFF), 7);
mapper.write_prg(0x8000, 3);
assert_eq!(mapper.read_prg(0x8000), 3);
assert_eq!(mapper.read_prg(0xBFFF), 3);
assert_eq!(mapper.read_prg(0xC000), 7);
mapper.write_prg(0xFFFF, 5);
assert_eq!(mapper.read_prg(0x8000), 5);
assert_eq!(mapper.read_prg(0xC000), 7);
}
#[test]
fn test_uxrom_256kb_prg_bank_switching() {
let mut prg_rom = vec![0; 256 * 1024];
for bank in 0..16 {
let start = bank * 16 * 1024;
let end = start + 16 * 1024;
for byte in &mut prg_rom[start..end] {
*byte = bank as u8;
}
}
let mut mapper = UxROMMapper::new(
MapperContext::new_for_test(2, prg_rom, vec![], NametableLayout::Vertical)
.with_submapper(2),
);
assert_eq!(mapper.read_prg(0xC000), 15);
mapper.write_prg(0x8000, 10);
assert_eq!(mapper.read_prg(0x8000), 10);
assert_eq!(mapper.read_prg(0xC000), 15);
mapper.write_prg(0xA000, 0);
assert_eq!(mapper.read_prg(0x8000), 0);
}
#[test]
fn test_uxrom_chr_ram() {
let mut mapper = UxROMMapper::new(MapperContext::new_for_test(
2,
vec![0; 128 * 1024],
vec![],
NametableLayout::Horizontal,
));
mapper.write_chr(0x0000, 0xAA);
mapper.write_chr(0x1000, 0xBB);
mapper.write_chr(0x1FFF, 0xCC);
assert_eq!(mapper.read_chr(0x0000), 0xAA);
assert_eq!(mapper.read_chr(0x1000), 0xBB);
assert_eq!(mapper.read_chr(0x1FFF), 0xCC);
}
#[test]
fn test_uxrom_mirroring() {
let mapper_h = UxROMMapper::new(MapperContext::new_for_test(
2,
vec![0; 128 * 1024],
vec![],
NametableLayout::Horizontal,
));
assert_eq!(mapper_h.get_mirroring(), NametableLayout::Horizontal);
let mapper_v = UxROMMapper::new(MapperContext::new_for_test(
2,
vec![0; 128 * 1024],
vec![],
NametableLayout::Vertical,
));
assert_eq!(mapper_v.get_mirroring(), NametableLayout::Vertical);
}
#[test]
fn test_uxrom_bank_register_mask() {
let mut prg_rom = vec![0; 256 * 1024];
for bank in 0..16 {
let start = bank * 16 * 1024;
let end = start + 16 * 1024;
for byte in &mut prg_rom[start..end] {
*byte = (bank * 10) as u8;
}
}
let mut mapper = UxROMMapper::new(
MapperContext::new_for_test(2, prg_rom, vec![], NametableLayout::Horizontal)
.with_submapper(2),
);
mapper.write_prg(0x8000, 0b0000_0000); assert_eq!(mapper.read_prg(0x8000), 0);
mapper.write_prg(0x8000, 0b0000_0111); assert_eq!(mapper.read_prg(0x8000), 70);
mapper.write_prg(0x8000, 0b0000_1111); assert_eq!(mapper.read_prg(0x8000), 150);
}
#[test]
fn test_uxrom_fixed_last_bank() {
let mut prg_rom = vec![0; 256 * 1024];
for bank in 0..16 {
let start = bank * 16 * 1024;
let end = start + 16 * 1024;
for byte in &mut prg_rom[start..end] {
*byte = (bank + 100) as u8;
}
}
let mut mapper = UxROMMapper::new(
MapperContext::new_for_test(2, prg_rom, vec![], NametableLayout::Horizontal)
.with_submapper(2),
);
assert_eq!(mapper.read_prg(0xC000), 115);
assert_eq!(mapper.read_prg(0xFFFF), 115);
mapper.write_prg(0x8000, 0);
assert_eq!(mapper.read_prg(0xC000), 115);
mapper.write_prg(0x8000, 5);
assert_eq!(mapper.read_prg(0xC000), 115);
mapper.write_prg(0x8000, 10);
assert_eq!(mapper.read_prg(0xC000), 115);
}
#[test]
fn test_uxrom_registers_snapshot_restores_bank_and_chr_ram() {
let mut prg_rom = vec![0; 128 * 1024];
for bank in 0..8 {
let start = bank * 16 * 1024;
let end = start + 16 * 1024;
for byte in &mut prg_rom[start..end] {
*byte = bank as u8;
}
}
let mut mapper = UxROMMapper::new(
MapperContext::new_for_test(2, prg_rom.clone(), vec![], NametableLayout::Horizontal)
.with_submapper(2),
);
mapper.write_prg(0x8000, 3);
mapper.write_chr(0x0000, 0x5A);
let regs = mapper.registers_snapshot();
let chr = mapper.chr_ram_snapshot();
let mut restored = UxROMMapper::new(
MapperContext::new_for_test(2, prg_rom, vec![], NametableLayout::Horizontal)
.with_submapper(2),
);
restored.restore_registers(®s);
restored.restore_chr_ram(&chr);
assert_eq!(restored.read_prg(0x8000), 3);
assert_eq!(restored.read_chr(0x0000), 0x5A);
}
#[test]
fn test_uxrom_open_bus() {
let mapper = UxROMMapper::new(MapperContext::new_for_test(
2,
vec![0; 128 * 1024],
vec![],
NametableLayout::Horizontal,
));
assert_eq!(mapper.read_prg_open_bus(0x5000, 0xAA), 0xAA);
assert_eq!(mapper.read_prg_open_bus(0x5FFF, 0xBB), 0xBB);
}
#[test]
fn test_uxrom_no_prg_ram_write_ignored() {
let mut mapper = UxROMMapper::new(
MapperContext::new_for_test(
2,
vec![0; 128 * 1024],
vec![],
NametableLayout::Horizontal,
)
.with_prg_ram_banks(0),
);
mapper.write_prg(0x6000, 0xAB);
mapper.write_prg(0x7FFF, 0xCD);
assert_eq!(
mapper.read_prg(0x6000),
0,
"no PRG-RAM: write should be ignored"
);
assert_eq!(
mapper.read_prg(0x7FFF),
0,
"no PRG-RAM: write should be ignored"
);
}
#[test]
fn test_uxrom_no_prg_ram_open_bus_returns_open_bus() {
let mapper = UxROMMapper::new(
MapperContext::new_for_test(
2,
vec![0; 128 * 1024],
vec![],
NametableLayout::Horizontal,
)
.with_prg_ram_banks(0),
);
let open_bus = 0x42;
assert_eq!(
mapper.read_prg_open_bus(0x6000, open_bus),
open_bus,
"no PRG-RAM: $6000 should return open-bus"
);
assert_eq!(
mapper.read_prg_open_bus(0x7FFF, open_bus),
open_bus,
"no PRG-RAM: $7FFF should return open-bus"
);
}
#[test]
fn test_uxrom_with_prg_ram_read_write_works() {
let mut mapper = UxROMMapper::new(
MapperContext::new_for_test(
2,
vec![0; 128 * 1024],
vec![],
NametableLayout::Horizontal,
)
.with_prg_ram_banks(1),
);
mapper.write_prg(0x6000, 0x77);
mapper.write_prg(0x7FFF, 0x88);
assert_eq!(
mapper.read_prg(0x6000),
0x77,
"PRG-RAM present: write/read should work"
);
assert_eq!(
mapper.read_prg(0x7FFF),
0x88,
"PRG-RAM present: write/read should work"
);
}
#[test]
fn test_uxrom_unrom_bank_select_masked_to_3_bits() {
let mut mapper = UxROMMapper::new(
MapperContext::new_for_test(
2,
vec![0; 128 * 1024],
vec![],
NametableLayout::Horizontal,
)
.with_submapper(2)
.with_prg_ram_banks(0),
);
mapper.write_prg(0x8000, 0xFF);
let snapshot = mapper.registers_snapshot();
assert_eq!(
snapshot[0], 0x07,
"UNROM bank_select should be masked to 3 bits (0x07)"
);
}
#[test]
fn test_uxrom_uorom_bank_select_masked_to_4_bits() {
let mut mapper = UxROMMapper::new(
MapperContext::new_for_test(
2,
vec![0; 256 * 1024],
vec![],
NametableLayout::Horizontal,
)
.with_submapper(2)
.with_prg_ram_banks(0),
);
mapper.write_prg(0x8000, 0xFF);
let snapshot = mapper.registers_snapshot();
assert_eq!(
snapshot[0], 0x0F,
"UOROM bank_select should be masked to 4 bits (0x0F)"
);
}
#[test]
fn test_uxrom_bank_select_reads_correct_bank_after_masking() {
let mut prg_rom = vec![0; 256 * 1024];
for bank in 0..16usize {
let start = bank * 16 * 1024;
let end = start + 16 * 1024;
prg_rom[start..end].fill(bank as u8);
}
let mut mapper = UxROMMapper::new(
MapperContext::new_for_test(2, prg_rom, vec![], NametableLayout::Horizontal)
.with_submapper(2)
.with_prg_ram_banks(0),
);
mapper.write_prg(0x8000, 0x1F);
assert_eq!(
mapper.read_prg(0x8000),
15,
"bank_select 0x1F should be masked to bank 15 for UOROM"
);
}
#[test]
fn test_uxrom_submapper0_has_bus_conflicts() {
let prg_rom = vec![0x00; 128 * 1024]; let mut mapper = UxROMMapper::new(
MapperContext::new_for_test(2, prg_rom, vec![], NametableLayout::Horizontal)
.with_submapper(0)
.with_prg_ram_banks(0),
);
mapper.write_prg(0x8000, 3);
let snapshot = mapper.registers_snapshot();
assert_eq!(
snapshot[0], 0,
"submapper 0: bus conflict should force bank_select to 0 (write & ROM[addr])"
);
}
#[test]
fn test_uxrom_submapper2_no_bus_conflicts() {
let prg_rom = vec![0x00; 128 * 1024]; let mut mapper = UxROMMapper::new(
MapperContext::new_for_test(2, prg_rom, vec![], NametableLayout::Horizontal)
.with_submapper(2)
.with_prg_ram_banks(0),
);
mapper.write_prg(0x8000, 3);
let snapshot = mapper.registers_snapshot();
assert_eq!(
snapshot[0], 3,
"submapper 2: no bus conflict, bank_select should be 3"
);
}
#[test]
fn test_uxrom_bus_conflict_and_value_selects_correct_bank() {
let prg_rom = vec![0xFF; 128 * 1024];
let mut mapper = UxROMMapper::new(
MapperContext::new_for_test(2, prg_rom, vec![], NametableLayout::Horizontal)
.with_submapper(0)
.with_prg_ram_banks(0),
);
mapper.write_prg(0x8000, 5);
let snapshot = mapper.registers_snapshot();
assert_eq!(snapshot[0], 5, "0x05 & 0xFF = 0x05, should select bank 5");
}
}