#![allow(dead_code)] extern crate core; use core::cell::UnsafeCell; use memory::{VMem, CPUBus}; use cartridge::{Cartridge, BankType, MirrorType}; use utils::{Read, Write, load_prefix, save_prefix}; pub trait Mapper : VMem { fn get_cart(&self) -> &Cartridge; fn get_cart_mut(&mut self) -> &mut Cartridge; fn tick(&mut self, _bus: &CPUBus) {} fn load(&mut self, reader: &mut Read) -> bool; fn save(&self, writer: &mut Write) -> bool; } pub struct RefMapper<'a> { mapper: UnsafeCell<&'a mut Mapper> } impl<'a> RefMapper<'a> { pub fn new(mapper: &'a mut Mapper) -> Self { RefMapper { mapper: UnsafeCell::new(mapper) } } #[inline(always)] pub fn get_mut(&self) -> &'a mut Mapper { unsafe { *self.mapper.get() } } } impl<'a> core::ops::Deref for RefMapper<'a> { type Target = &'a mut Mapper; #[inline(always)] fn deref(&self) -> &&'a mut Mapper { unsafe { &*self.mapper.get() } } } #[repr(C)] pub struct Mapper1<'a, C> where C: Cartridge { cart: C, prg_banks: [&'a [u8]; 2], chr_banks: [&'a mut [u8]; 2], sram: &'a mut [u8], ctl_reg: u8, load_reg: u8, prg_nbank: usize, /* num of 16k PRG ROM banks */ chr_nbank: usize /* num of 8k PRG ROM banks */ } impl<'a, C> VMem for Mapper1<'a, C> where C: Cartridge { fn read(&self, addr: u16) -> u8 { let addr = addr as usize; match addr >> 12 { /* [0x0000..0x2000) */ 0 | 1 => self.chr_banks[(addr >> 12) & 1][addr & 0xfff], /* [0x2000..0x6000) */ 2 | 3 | 4 | 5 => panic!("unmapped address: 0x{:04x}", addr), /* [0x6000..0x8000) */ 6 | 7 => self.sram[addr - 0x6000], /* [0x8000..0xffff] */ _ => self.prg_banks[(addr >> 14) & 1][addr & 0x3fff] } } fn write(&mut self, addr: u16, data: u8) { let addr = addr as usize; match addr >> 12 { /* [0x0000..0x2000) */ 0 | 1 => self.chr_banks[(addr >> 12) & 1][addr & 0xfff] = data, /* [0x2000..0x6000) */ 2 | 3 | 4 | 5 => panic!("unmapped address: 0x{:04x}", addr), /* [0x6000..0x8000) */ 6 | 7 => self.sram[addr - 0x6000] = data, /* [0x8000..0xffff] */ _ => self.write_loadreg(addr as u16, data) } } } impl<'a, C> Mapper1<'a, C> where C: Cartridge { pub fn new(cart: C) -> Self { let prg_nbank = cart.get_size(BankType::PrgRom) >> 14; let chr_nbank = cart.get_size(BankType::ChrRom) >> 13; unsafe { let mut m = Mapper1{cart, prg_nbank, chr_nbank, load_reg: 0x10, ctl_reg: 0x0c, prg_banks: core::mem::uninitialized(), chr_banks: core::mem::uninitialized(), sram: core::mem::uninitialized()}; { let c = &mut m.cart; m.prg_banks = [ c.get_bank(0, 0x4000, BankType::PrgRom), c.get_bank((prg_nbank - 1) << 14, 0x4000, BankType::PrgRom) ]; m.chr_banks = [ c.get_bank_mut(0, 0x1000, BankType::ChrRom), c.get_bank_mut(0x1000, 0x1000, BankType::ChrRom) ]; m.sram = c.get_bank_mut(0, 0x2000, BankType::Sram); } m } } fn write_loadreg(&mut self, addr: u16, data: u8) { if data & 0x80 == 0x80 { self.ctl_reg |= 0x0c; self.load_reg = 0x10; return } let triggered = self.load_reg & 1 == 1; self.load_reg = (self.load_reg >> 1) | ((data & 1) << 4); if !triggered { return } let load_reg = self.load_reg; match (addr >> 13) & 3 { 0x0 => { self.ctl_reg = load_reg; self.cart.set_mirror_type(match load_reg & 3 { 0x0 => MirrorType::Single0, 0x1 => MirrorType::Single1, 0x2 => MirrorType::Vertical, _ => MirrorType::Horizontal }); }, 0x1 => { match (self.ctl_reg >> 4) & 1 { 0x0 => { let base = ((load_reg & 0xfe) as usize % self.chr_nbank) << 13; self.chr_banks = [ self.cart.get_bank_mut(base, 0x1000, BankType::ChrRom), self.cart.get_bank_mut(base + 0x1000, 0x1000, BankType::ChrRom)]; }, _ => self.chr_banks[0] = self.cart.get_bank_mut( (load_reg as usize % (self.chr_nbank << 1)) << 12, 0x1000, BankType::ChrRom) } }, 0x2 => { if (self.ctl_reg >> 4) & 1 == 1 { self.chr_banks[1] = self.cart.get_bank_mut( (load_reg as usize % (self.chr_nbank << 1)) << 12, 0x1000, BankType::ChrRom) } }, 0x3 => { let load_reg = load_reg & 0xf; match (self.ctl_reg >> 2) & 3 { 0x0 | 0x1 => { let base = ((load_reg & 0xfe) as usize % (self.prg_nbank >> 1)) << 15; self.prg_banks = [ self.cart.get_bank(base, 0x4000, BankType::PrgRom), self.cart.get_bank(base + 0x4000, 0x4000, BankType::PrgRom) ]; }, 0x2 => self.prg_banks = [ self.cart.get_bank(0, 0x4000, BankType::PrgRom), self.cart.get_bank((load_reg as usize % self.prg_nbank) << 14, 0x4000, BankType::PrgRom)], 0x3 => self.prg_banks = [ self.cart.get_bank((load_reg as usize % self.prg_nbank) << 14, 0x4000, BankType::PrgRom), self.cart.get_bank((self.prg_nbank - 1) << 14, 0x4000, BankType::PrgRom)], _ => () } }, _ => () } self.load_reg = 0x10; } } impl<'a, C> Mapper for Mapper1<'a, C> where C: Cartridge { fn get_cart(&self) -> &Cartridge {&self.cart} fn get_cart_mut(&mut self) -> &mut Cartridge {&mut self.cart} fn load(&mut self, reader: &mut Read) -> bool { for v in self.prg_banks.iter_mut() { let mut offset: usize = 0; if !load_prefix(&mut offset, 0, reader) { return false } *v = self.cart.get_bank(offset, 0x4000, BankType::PrgRom); } for v in self.chr_banks.iter_mut() { let mut offset: usize = 0; if !load_prefix(&mut offset, 0, reader) { return false } *v = self.cart.get_bank_mut(offset, 0x1000, BankType::ChrRom); } load_prefix(&mut self.ctl_reg, 0, reader) && load_prefix(&mut self.load_reg, 0, reader) && self.cart.load(reader) } fn save(&self, writer: &mut Write) -> bool { let prg_base = self.cart.get_bank(0, 0, BankType::PrgRom).as_ptr(); let chr_base = self.cart.get_bank(0, 0, BankType::ChrRom).as_ptr(); for v in self.prg_banks.iter() { if !save_prefix(&(v.as_ptr() as usize - prg_base as usize), 0, writer) { return false } } for v in self.chr_banks.iter() { if !save_prefix(&(v.as_ptr() as usize - chr_base as usize), 0, writer) { return false } } save_prefix(&self.ctl_reg, 0, writer) && save_prefix(&self.load_reg, 0, writer) && self.cart.save(writer) } } #[repr(C)] pub struct Mapper2<'a, C> where C: Cartridge { cart: C, prg_banks: [&'a [u8]; 2], chr_bank: &'a mut [u8], sram: &'a mut [u8], prg_nbank: usize } impl<'a, C> VMem for Mapper2<'a, C> where C: Cartridge { fn read(&self, addr: u16) -> u8 { let addr = addr as usize; match addr >> 12 { /* [0x0000..0x2000) */ 0 | 1 => self.chr_bank[addr], /* [0x2000..0x6000) */ 2 | 3 | 4 | 5 => panic!("unmapped address: 0x{:04x}", addr), /* [0x6000..0x8000) */ 6 | 7 => self.sram[addr - 0x6000], /* [0x8000..0xffff] */ _ => self.prg_banks[(addr >> 14) & 1][addr & 0x3fff] } } fn write(&mut self, addr: u16, data: u8) { let addr = addr as usize; match addr >> 12 { /* [0x0000..0x2000) */ 0 | 1 => self.chr_bank[addr] = data, /* [0x2000..0x6000) */ 2 | 3 | 4 | 5 => panic!("unmapped address: 0x{:04x}", addr), /* [0x6000..0x8000) */ 6 | 7 => self.sram[addr - 0x6000] = data, /* [0x8000..0xffff] */ _ => self.prg_banks[0] = self.cart.get_bank( ((data as usize) % self.prg_nbank) << 14, 0x4000, BankType::PrgRom) } } } impl<'a, C> Mapper2<'a, C> where C: Cartridge { pub fn new(cart: C) -> Self { let nbank = cart.get_size(BankType::PrgRom) >> 14; unsafe { let mut m = Mapper2{cart, prg_nbank: nbank, prg_banks: core::mem::uninitialized(), chr_bank: core::mem::uninitialized(), sram: core::mem::uninitialized()}; { let c = &mut m.cart; m.prg_banks = [ c.get_bank(0, 0x4000, BankType::PrgRom), c.get_bank((nbank - 1) << 14, 0x4000, BankType::PrgRom) ]; m.chr_bank = c.get_bank_mut(0, 0x2000, BankType::ChrRom); m.sram = c.get_bank_mut(0, 0x2000, BankType::Sram); } m } } } impl<'a, C> Mapper for Mapper2<'a, C> where C: Cartridge { fn get_cart(&self) -> &Cartridge {&self.cart} fn get_cart_mut(&mut self) -> &mut Cartridge {&mut self.cart} fn load(&mut self, reader: &mut Read) -> bool { for v in self.prg_banks.iter_mut() { let mut offset: usize = 0; if !load_prefix(&mut offset, 0, reader) { return false } *v = self.cart.get_bank(offset, 0x4000, BankType::PrgRom); } let mut offset: usize = 0; if !load_prefix(&mut offset, 0, reader) { return false } self.chr_bank = self.cart.get_bank_mut(offset, 0x2000, BankType::ChrRom); self.cart.load(reader) } fn save(&self, writer: &mut Write) -> bool { let prg_base = self.cart.get_bank(0, 0, BankType::PrgRom).as_ptr(); let chr_base = self.cart.get_bank(0, 0, BankType::ChrRom).as_ptr(); for v in self.prg_banks.iter() { if !save_prefix(&(v.as_ptr() as usize - prg_base as usize), 0, writer) { return false } } if !save_prefix(&(self.chr_bank.as_ptr() as usize - chr_base as usize), 0, writer) { return false } self.cart.save(writer) } } #[repr(C)] pub struct Mapper4<'a, C> where C: Cartridge { cart: C, prg_banks: [&'a [u8]; 4], chr_banks: [&'a mut [u8]; 8], sram: &'a mut [u8], prg_nbank: usize, /* num of 16k PRG ROM banks */ chr_nbank: usize, /* num of 8k PRG ROM banks */ chr_inv: u8, prg_mode: u8, reg_idx: u8, regs: [u8; 8], /* 4 pairs of registers */ irq_reload: u8, irq_counter: u8, irq_enable: bool } impl<'a, C> VMem for Mapper4<'a, C> where C: Cartridge { fn read(&self, addr: u16) -> u8 { let addr = addr as usize; match addr >> 12 { /* [0x0000..0x2000) */ 0 | 1 => self.chr_banks[addr >> 10][addr & 0x3ff], /* [0x2000..0x6000) */ 2 | 3 | 4 | 5 => panic!("unmapped address: 0x{:04x}", addr), /* [0x6000..0x8000) */ 6 | 7 => self.sram[addr - 0x6000], /* [0x8000..0xffff] */ _ => { let addr = addr - 0x8000; self.prg_banks[addr >> 13][addr & 0x1fff] } } } fn write(&mut self, addr: u16, data: u8) { let addr = addr as usize; match addr >> 12 { /* [0x0000..0x2000) */ 0 | 1 => self.chr_banks[addr >> 10][addr & 0x3ff] = data, /* [0x2000..0x6000) */ 2 | 3 | 4 | 5 => panic!("unmapped address: 0x{:04x}", addr), /* [0x6000..0x8000) */ 6 | 7 => self.sram[addr - 0x6000] = data, /* [0x8000..0xa000) */ 8 | 9 => match addr & 1 { 0 => self.write_select_reg(data), _ => self.write_bank_data(data) }, /* [0xa000..0xc000) */ 0xa | 0xb => match addr & 1 { 0 => self.write_mirror(data), _ => () }, /* [0xc000..0xe000) */ 0xc | 0xd => match addr & 1 { 0 => self.irq_reload = data, _ => self.irq_counter = 0 }, /* [0xe000..0xffff] */ _ => match addr & 1 { 0 => self.irq_enable = false, _ => self.irq_enable = true } } } } impl<'a, C> Mapper4<'a, C> where C: Cartridge { #[inline(always)] fn write_select_reg(&mut self, data: u8) { self.prg_mode = (data >> 6) & 1; self.chr_inv = (data >> 7) & 1; self.reg_idx = data & 0x7; self.update_banks(); } #[inline(always)] fn write_bank_data(&mut self, data: u8) { self.regs[self.reg_idx as usize] = data; self.update_banks(); } #[inline(always)] fn write_mirror(&mut self, data: u8) { self.cart.set_mirror_type(match data & 1 { 0 => MirrorType::Vertical, _ => MirrorType::Horizontal }) } #[inline(always)] fn get_prgbank<'b>(&self, idx: u8) -> &'b [u8] { self.cart.get_bank(0x2000 * idx as usize, 0x2000, BankType::PrgRom) } #[inline(always)] fn get_chrbank<'b>(&mut self, idx: u8) -> &'b mut [u8] { self.cart.get_bank_mut(0x400 * idx as usize, 0x400, BankType::ChrRom) } fn update_banks(&mut self) { macro_rules! make_arr { ($mt: ident, $m: ident, [$($x: expr), *]) => { [$({let t = ($x as usize % self.$m) as u8; self.$mt(t)}), *] }; } self.prg_banks = match self.prg_mode { 0 => make_arr!(get_prgbank, prg_nbank, [ self.regs[6], self.regs[7], (self.prg_nbank - 2) as u8, (self.prg_nbank - 1) as u8 ]), _ => make_arr!(get_prgbank, prg_nbank, [ (self.prg_nbank - 2) as u8, self.regs[7], self.regs[6], (self.prg_nbank - 1) as u8 ]) }; self.chr_banks = match self.chr_inv { 0 => make_arr!(get_chrbank, chr_nbank, [ self.regs[0] & 0xfe, self.regs[0] | 0x01, self.regs[1] & 0xfe, self.regs[1] | 0x01, self.regs[2], self.regs[3], self.regs[4], self.regs[5] ]), _ => make_arr!(get_chrbank, chr_nbank, [ self.regs[2], self.regs[3], self.regs[4], self.regs[5], self.regs[0] & 0xfe, self.regs[0] | 0x01, self.regs[1] & 0xfe, self.regs[1] | 0x01 ]), }; } pub fn new(cart: C) -> Self { let prg_nbank = cart.get_size(BankType::PrgRom) >> 13; let chr_nbank = cart.get_size(BankType::ChrRom) >> 10; unsafe { let mut m = Mapper4{cart, prg_nbank, chr_nbank, prg_mode: 0, chr_inv: 0, reg_idx: 0, regs: [0; 8], prg_banks: core::mem::uninitialized(), chr_banks: core::mem::uninitialized(), sram: core::mem::uninitialized(), irq_reload: 0, irq_counter: 0, irq_enable: false }; m.prg_banks = [ m.get_prgbank(0), m.get_prgbank(1), m.get_prgbank((prg_nbank - 2) as u8), m.get_prgbank((prg_nbank - 1) as u8)]; m.chr_banks = [ m.get_chrbank(0), m.get_chrbank(0), m.get_chrbank(0), m.get_chrbank(0), m.get_chrbank(0), m.get_chrbank(0), m.get_chrbank(0), m.get_chrbank(0)]; { let c = &mut m.cart; m.sram = c.get_bank_mut(0, 0x2000, BankType::Sram); } m } } } impl<'a, C> Mapper for Mapper4<'a, C> where C: Cartridge { fn get_cart(&self) -> &Cartridge {&self.cart} fn get_cart_mut(&mut self) -> &mut Cartridge {&mut self.cart} fn tick(&mut self, bus: &CPUBus) { let ppu = bus.get_ppu(); if ppu.cycle != 260 { return } if ppu.scanline > 239 && ppu.scanline < 261 { return } if !ppu.get_show_bg() && !ppu.get_show_sp() { return } if self.irq_counter == 0 { self.irq_counter = self.irq_reload } else { self.irq_counter -= 1; if self.irq_counter == 0 && self.irq_enable { bus.get_cpu().trigger_irq(); } } } fn load(&mut self, reader: &mut Read) -> bool { for v in self.prg_banks.iter_mut() { let mut offset: usize = 0; if !load_prefix(&mut offset, 0, reader) { return false } *v = self.cart.get_bank(offset, 0x2000, BankType::PrgRom); } for v in self.chr_banks.iter_mut() { let mut offset: usize = 0; if !load_prefix(&mut offset, 0, reader) { return false } *v = self.cart.get_bank_mut(offset, 0x400, BankType::ChrRom); } load_prefix(&mut self.chr_inv, 0, reader) && load_prefix(&mut self.prg_mode, 0, reader) && load_prefix(&mut self.reg_idx, 0, reader) && load_prefix(&mut self.regs, 0, reader) && load_prefix(&mut self.irq_reload, 0, reader) && load_prefix(&mut self.irq_counter, 0, reader) && load_prefix(&mut self.irq_enable, 0, reader) && self.cart.load(reader) } fn save(&self, writer: &mut Write) -> bool { let prg_base = self.cart.get_bank(0, 0, BankType::PrgRom).as_ptr(); let chr_base = self.cart.get_bank(0, 0, BankType::ChrRom).as_ptr(); for v in self.prg_banks.iter() { if !save_prefix(&(v.as_ptr() as usize - prg_base as usize), 0, writer) { return false } } for v in self.chr_banks.iter() { if !save_prefix(&(v.as_ptr() as usize - chr_base as usize), 0, writer) { return false } } save_prefix(&self.chr_inv, 0, writer) && save_prefix(&self.prg_mode, 0, writer) && save_prefix(&self.reg_idx, 0, writer) && save_prefix(&self.regs, 0, writer) && save_prefix(&self.irq_reload, 0, writer) && save_prefix(&self.irq_counter, 0, writer) && save_prefix(&self.irq_enable, 0, writer) && self.cart.save(writer) } }