use crate::memory::mapper::*;
use crate::memory::mappers::common::mmc1::{ShiftRegister, ShiftStatus};
const PRG_LAYOUT_FIXED_LAST: PrgLayout = PrgLayout::new(&[
PrgWindow::new(0x6000, 0x7FFF, 8 * KIBIBYTE, Bank::WORK_RAM.status_register(S0)),
PrgWindow::new(0x8000, 0xBFFF, 16 * KIBIBYTE, Bank::switchable_rom(P0)),
PrgWindow::new(0xC000, 0xFFFF, 16 * KIBIBYTE, Bank::fixed_rom(BankIndex::LAST)),
]);
const PRG_LAYOUT_FIXED_FIRST: PrgLayout = PrgLayout::new(&[
PrgWindow::new(0x6000, 0x7FFF, 8 * KIBIBYTE, Bank::WORK_RAM.status_register(S0)),
PrgWindow::new(0x8000, 0xBFFF, 16 * KIBIBYTE, Bank::fixed_rom(BankIndex::FIRST)),
PrgWindow::new(0xC000, 0xFFFF, 16 * KIBIBYTE, Bank::switchable_rom(P0)),
]);
const PRG_LAYOUT_ONE_BIG: PrgLayout = PrgLayout::new(&[
PrgWindow::new(0x6000, 0x7FFF, 8 * KIBIBYTE, Bank::WORK_RAM.status_register(S0)),
PrgWindow::new(0x8000, 0xFFFF, 32 * KIBIBYTE, Bank::switchable_rom(P0)),
]);
const CHR_LAYOUT_ONE_BIG: ChrLayout = ChrLayout::new(&[
ChrWindow::new(0x0000, 0x1FFF, 8 * KIBIBYTE, Bank::switchable_ram(C0)),
]);
const CHR_LAYOUT_TWO_SMALL: ChrLayout = ChrLayout::new(&[
ChrWindow::new(0x0000, 0x0FFF, 4 * KIBIBYTE, Bank::switchable_ram(C0)),
ChrWindow::new(0x1000, 0x1FFF, 4 * KIBIBYTE, Bank::switchable_ram(C1)),
]);
const PRG_LAYOUTS: [PrgLayout; 4] =
[PRG_LAYOUT_ONE_BIG, PRG_LAYOUT_ONE_BIG, PRG_LAYOUT_FIXED_FIRST, PRG_LAYOUT_FIXED_LAST];
const CHR_LAYOUTS: [ChrLayout; 2] =
[CHR_LAYOUT_ONE_BIG, CHR_LAYOUT_TWO_SMALL];
const MIRRORINGS: [NameTableMirroring; 4] = [
NameTableMirroring::OneScreenRightBank,
NameTableMirroring::OneScreenLeftBank,
NameTableMirroring::Vertical,
NameTableMirroring::Horizontal,
];
const RAM_STATUSES: [RamStatus; 2] = [
RamStatus::ReadWrite,
RamStatus::Disabled,
];
pub struct Mapper001_0 {
shift_register: ShiftRegister,
}
impl Mapper for Mapper001_0 {
fn initial_layout(&self) -> InitialLayout {
InitialLayout::builder()
.prg_max_bank_count(16)
.prg_bank_size(16 * KIBIBYTE)
.prg_windows(PRG_LAYOUT_FIXED_LAST)
.chr_max_bank_count(32)
.chr_bank_size(4 * KIBIBYTE)
.chr_windows(CHR_LAYOUT_ONE_BIG)
.name_table_mirroring_source(NameTableMirroring::OneScreenRightBank.to_source())
.build()
}
fn write_to_cartridge_space(&mut self, params: &mut MapperParams, address: CpuAddress, value: u8) {
if matches!(address.to_raw(), 0x6000..=0x7FFF) {
params.write_prg(address, value);
return;
}
match self.shift_register.shift(value) {
ShiftStatus::Clear => params.set_prg_layout(PRG_LAYOUT_FIXED_LAST),
ShiftStatus::Continue => { }
ShiftStatus::Done { finished_value } => match address.to_raw() {
0x0000..=0x401F => unreachable!(),
0x4020..=0x5FFF => { }
0x6000..=0x7FFF => unreachable!(),
0x8000..=0x9FFF => {
let fields = splitbits!(finished_value, "...cppmm");
params.set_chr_layout(CHR_LAYOUTS[fields.c as usize]);
params.set_prg_layout(PRG_LAYOUTS[fields.p as usize]);
params.set_name_table_mirroring(MIRRORINGS[fields.m as usize]);
}
0xA000..=0xBFFF => params.set_bank_register(C0, finished_value),
0xC000..=0xDFFF => params.set_bank_register(C1, finished_value),
0xE000..=0xFFFF => {
let fields = splitbits!(finished_value, "...rbbbb");
params.set_ram_status(S0, RAM_STATUSES[fields.r as usize]);
params.set_bank_register(P0, fields.b);
}
}
}
}
}
impl Mapper001_0 {
pub fn new(cartridge: &Cartridge) -> Self {
let prg_rom_size = cartridge.prg_rom_size() / KIBIBYTE;
let prg_ram_size = cartridge.prg_ram_size() / KIBIBYTE;
let chr_ram_size = cartridge.chr_ram_size() / KIBIBYTE;
let board = match (prg_rom_size, prg_ram_size, chr_ram_size) {
(128, 8, 8) => Board::Snrom,
(256, 8, 8) => Board::Snrom,
( _, 16, _) => Board::Sorom,
( _, 32, _) => Board::Sxrom,
_ => Board::Standard,
};
assert_eq!(board, Board::Standard, "MMC1 {board:?} is not yet supported.");
Self { shift_register: ShiftRegister::new() }
}
}
#[derive(PartialEq, Eq, Debug)]
enum Board {
Standard,
Snrom,
Sorom,
Sxrom
}