diff options
-rw-r--r-- | src/apu.rs | 4 | ||||
-rw-r--r-- | src/bin.rs | 21 | ||||
-rw-r--r-- | src/cartridge.rs | 2 | ||||
-rw-r--r-- | src/mapper.rs | 377 | ||||
-rw-r--r-- | src/memory.rs | 15 | ||||
-rw-r--r-- | src/ppu.rs | 14 |
6 files changed, 325 insertions, 108 deletions
@@ -522,7 +522,7 @@ impl<'a> APU<'a> { pulse1: Pulse::new(false), pulse2: Pulse::new(true), triangle: Triangle::new(), noise: Noise::new(), - frame_lvl: 0, frame_mode: false, frame_int: false, frame_inh: false, + frame_lvl: 0, frame_mode: false, frame_int: false, frame_inh: true, cpu_sampler: Sampler::new(mos6502::CPU_FREQ, CPU_SAMPLE_FREQ), audio_sampler: Sampler::new(mos6502::CPU_FREQ, AUDIO_SAMPLE_FREQ), cycle_even: false, @@ -584,7 +584,7 @@ impl<'a> APU<'a> { } pub fn write_frame_counter(&mut self, data: u8) { - self.frame_inh = data & 0x40 == 1; + self.frame_inh = data & 0x40 == 0x40; self.frame_mode = data >> 7 == 1; if self.frame_mode { self.tick_len_swp() @@ -74,12 +74,14 @@ impl Cartridge for SimpleCart { BankType::Sram => self.sram.len() } } - fn get_bank(&mut self, base: usize, size: usize, kind: BankType) -> *mut [u8] { - &mut (match kind { - BankType::PrgRom => &mut self.prg_rom, - BankType::ChrRom => &mut self.chr_rom, - BankType::Sram => &mut self.sram, - })[base..base + size] + fn get_bank<'a>(&mut self, base: usize, size: usize, kind: BankType) -> &'a mut [u8] { + unsafe { + &mut *((&mut (match kind { + BankType::PrgRom => &mut self.prg_rom, + BankType::ChrRom => &mut self.chr_rom, + BankType::Sram => &mut self.sram, + })[base..base + size]) as *mut [u8]) + } } fn get_mirror_type(&self) -> MirrorType {self.mirror_type} fn set_mirror_type(&mut self, mt: MirrorType) {self.mirror_type = mt} @@ -258,19 +260,19 @@ impl<'a> sdl2::audio::AudioCallback for SDLAudioPlayback<'a> { { let b = &mut m.0; let l1 = (b.tail + b.buffer.len() - b.head) % b.buffer.len(); - print!("{} ", l1); + //print!("{} ", l1); for x in out.iter_mut() { *x = b.deque() } } - println!("{}", m.1); + //println!("{}", m.1); if m.1 >= AUDIO_SAMPLES { m.1 -= AUDIO_SAMPLES; self.0.time_barrier.notify_one(); } else { m.1 = 0; - println!("audio frame skipping"); + //println!("audio frame skipping"); } } } @@ -393,6 +395,7 @@ fn main() { let mut m: Box<mapper::Mapper> = match mapper_id { 0 | 2 => Box::new(mapper::Mapper2::new(cart)), 1 => Box::new(mapper::Mapper1::new(cart)), + 4 => Box::new(mapper::Mapper4::new(cart)), _ => panic!("unsupported mapper {}", mapper_id) }; diff --git a/src/cartridge.rs b/src/cartridge.rs index 95085cd..52b43ca 100644 --- a/src/cartridge.rs +++ b/src/cartridge.rs @@ -17,7 +17,7 @@ pub enum BankType { pub trait Cartridge { fn get_size(&self, kind: BankType) -> usize; - fn get_bank(&mut self, base: usize, size: usize, kind: BankType) -> *mut [u8]; + fn get_bank<'a>(&mut self, base: usize, size: usize, kind: BankType) -> &'a mut [u8]; #[inline(always)] fn get_mirror_type(&self) -> MirrorType; #[inline(always)] fn set_mirror_type(&mut self, mt: MirrorType); } diff --git a/src/mapper.rs b/src/mapper.rs index 93c6fa9..cf946a2 100644 --- a/src/mapper.rs +++ b/src/mapper.rs @@ -1,10 +1,11 @@ #![allow(dead_code)] extern crate core; -use memory::VMem; +use memory::{VMem, CPUBus}; use cartridge::{Cartridge, BankType, MirrorType}; pub trait Mapper : VMem { fn get_cart(&self) -> &Cartridge; + fn tick(&mut self, _bus: &CPUBus) {} } pub struct Mapper1<'a, C> where C: Cartridge { @@ -48,26 +49,28 @@ impl<'a, C> VMem for Mapper1<'a, C> where C: Cartridge { 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 prg_nbank = cart.get_size(BankType::PrgRom) >> 14; - let chr_nbank = cart.get_size(BankType::ChrRom) >> 13; - let null = core::mem::uninitialized(); let mut m = Mapper1{cart, prg_nbank, chr_nbank, load_reg: 0x10, ctl_reg: 0x0c, - prg_banks: [null; 2], - chr_banks: [core::mem::uninitialized(), - core::mem::uninitialized()], + prg_banks: core::mem::uninitialized(), + chr_banks: core::mem::uninitialized(), sram: core::mem::uninitialized()}; { let c = &mut m.cart; - m.prg_banks[0] = &*c.get_bank(0, 0x4000, BankType::PrgRom); - m.prg_banks[1] = &*c.get_bank((prg_nbank - 1) << 14, 0x4000, BankType::PrgRom); - m.chr_banks[0] = &mut *c.get_bank(0, 0x1000, BankType::ChrRom); - m.chr_banks[1] = &mut *c.get_bank(0x1000, 0x1000, BankType::ChrRom); - m.sram = &mut *c.get_bank(0, 0x2000, BankType::Sram); + 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(0, 0x1000, BankType::ChrRom), + c.get_bank(0x1000, 0x1000, BankType::ChrRom) + ]; + m.sram = c.get_bank(0, 0x2000, BankType::Sram); } m } @@ -81,71 +84,65 @@ impl<'a, C> Mapper1<'a, C> where C: Cartridge { } let triggered = self.load_reg & 1 == 1; self.load_reg = (self.load_reg >> 1) | ((data & 1) << 4); - if triggered { unsafe { - 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[0] = - &mut *self.cart.get_bank(base, 0x1000, BankType::ChrRom); - self.chr_banks[1] = - &mut *self.cart.get_bank(base + 0x1000, 0x1000, BankType::ChrRom) - }, - _ => - self.chr_banks[0] = &mut *self.cart.get_bank( - (load_reg as usize % (self.chr_nbank << 1)) << 12, - 0x1000, BankType::ChrRom) - } - }, - 0x2 => { - if (self.ctl_reg >> 4) & 1 == 1 { - self.chr_banks[1] = &mut *self.cart.get_bank( - (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[0] = - &*self.cart.get_bank(base, 0x4000, BankType::PrgRom); - self.prg_banks[1] = - &*self.cart.get_bank(base + 0x4000, 0x4000, BankType::PrgRom) - }, - 0x2 => { - self.prg_banks[0] = &*self.cart.get_bank(0, 0x4000, BankType::PrgRom); - self.prg_banks[1] = &*self.cart.get_bank( - (load_reg as usize % self.prg_nbank) << 14, - 0x4000, BankType::PrgRom); - }, - 0x3 => { - self.prg_banks[0] = &*self.cart.get_bank( - (load_reg as usize % self.prg_nbank) << 14, - 0x4000, BankType::PrgRom); - self.prg_banks[1] = &*self.cart.get_bank( - (self.prg_nbank - 1) << 14, - 0x4000, BankType::PrgRom); - } - _ => () - } - }, - _ => () - } - self.load_reg = 0x10; - }} + 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(base, 0x1000, BankType::ChrRom), + self.cart.get_bank(base + 0x1000, 0x1000, BankType::ChrRom)]; + }, + _ => + self.chr_banks[0] = self.cart.get_bank( + (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( + (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; } } @@ -180,11 +177,10 @@ impl<'a, C> VMem for Mapper2<'a, C> where C: Cartridge { if addr < 0x2000 { self.chr_bank[addr] = data; } else if addr >= 0x8000 { - self.prg_banks[0] = unsafe { - &*self.cart.get_bank(((data as usize) % self.prg_nbank) << 14, + self.prg_banks[0] = + self.cart.get_bank(((data as usize) % self.prg_nbank) << 14, 0x4000, BankType::PrgRom) - } } else if addr >= 0x6000 { self.sram[addr - 0x6000] = data } else { @@ -195,20 +191,21 @@ impl<'a, C> VMem for Mapper2<'a, C> where C: Cartridge { 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 nbank = cart.get_size(BankType::PrgRom) >> 14; - let null = core::mem::uninitialized(); let mut m = Mapper2{cart, prg_nbank: nbank, - prg_banks: [null; 2], + prg_banks: core::mem::uninitialized(), chr_bank: core::mem::uninitialized(), sram: core::mem::uninitialized()}; { let c = &mut m.cart; - m.prg_banks[0] = &*c.get_bank(0, 0x4000, BankType::PrgRom); - m.prg_banks[1] = &*c.get_bank((nbank - 1) << 14, 0x4000, BankType::PrgRom); - m.chr_bank = &mut *c.get_bank(0, 0x2000, BankType::ChrRom); - m.sram = &mut *c.get_bank(0, 0x2000, BankType::Sram); + 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(0, 0x2000, BankType::ChrRom); + m.sram = c.get_bank(0, 0x2000, BankType::Sram); } m } @@ -218,3 +215,209 @@ impl<'a, C> Mapper2<'a, C> where C: Cartridge { impl<'a, C> Mapper for Mapper2<'a, C> where C: Cartridge { fn get_cart(&self) -> &Cartridge {&self.cart} } + +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; + if addr < 0x2000 { + self.chr_banks[addr >> 10][addr & 0x3ff] + } else if addr >= 0x8000 { + let addr = addr - 0x8000; + self.prg_banks[addr >> 13][addr & 0x1fff] + } else if addr >= 0x6000 { + self.sram[addr - 0x6000] + } else { + panic!("unmapped address: 0x{:04x}", addr) + } + } + + fn write(&mut self, addr: u16, data: u8) { + let addr = addr as usize; + if addr < 0x2000 { + self.chr_banks[addr >> 10][addr & 0x3ff] = data + } else if addr < 0x6000 { + panic!("invalid write to address: 0x{:04x}", addr) + } else if addr < 0x8000 { + self.sram[addr - 0x6000] = data + } else if addr < 0xa000 { + match addr & 1 { + 0 => self.write_select_reg(data), + _ => self.write_bank_data(data) + } + } else if addr < 0xc000 { + match addr & 1 { + 0 => self.write_mirror(data), + _ => () + } + } else if addr < 0xe000 { + match addr & 1 { + 0 => self.irq_reload = data, + _ => self.irq_counter = 0 + } + } else { + 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>(&mut self, idx: u8) -> &'b mut [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(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(0, 0x2000, BankType::Sram); + } + m + } + } +} + +impl<'a, C> Mapper for Mapper4<'a, C> where C: Cartridge { + fn get_cart(&self) -> &Cartridge {&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(); + } + } + } +} diff --git a/src/memory.rs b/src/memory.rs index 6eb796f..fac5c28 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -37,15 +37,15 @@ impl<'a> CPUBus<'a> { self.apu = apu; } - #[inline(always)] fn get_cpu(&self) -> &'a mut CPU<'a> {unsafe{&mut *self.cpu}} - #[inline(always)] fn get_ppu(&self) -> &'a mut PPU<'a> {unsafe{&mut *self.ppu}} - #[inline(always)] fn get_apu(&self) -> &'a mut APU<'a> {unsafe{&mut *self.apu}} + #[inline(always)] pub fn get_cpu(&self) -> &'a mut CPU<'a> {unsafe{&mut *self.cpu}} + #[inline(always)] pub fn get_ppu(&self) -> &'a mut PPU<'a> {unsafe{&mut *self.ppu}} + #[inline(always)] pub fn get_apu(&self) -> &'a mut APU<'a> {unsafe{&mut *self.apu}} pub fn tick(&self) { let cpu = self.get_cpu(); let ppu = self.get_ppu(); let apu = self.get_apu(); - if ppu.tick() || ppu.tick() || ppu.tick() { + if ppu.tick(self) || ppu.tick(self) || ppu.tick(self) { cpu.trigger_nmi() } if apu.tick() { @@ -244,7 +244,12 @@ impl<'a> PPUMemory<'a> { #[inline(always)] fn write_mapper(&self, addr: u16, data: u8) { - self.mapper.borrow_mut().write(addr, data); + self.mapper.borrow_mut().write(addr, data) + } + + #[inline(always)] + pub fn tick(&self, bus: &CPUBus) { + self.mapper.borrow_mut().tick(bus) } } @@ -1,5 +1,5 @@ #![allow(dead_code)] -use memory::{VMem, PPUMemory}; +use memory::{VMem, PPUMemory, CPUBus}; use mos6502::CPU; use core::intrinsics::transmute; @@ -189,8 +189,8 @@ impl<'a> PPU<'a> { #[inline(always)] fn get_vram_inc(&self) -> u8 { (self.ppuctl >> 2) & 1} #[inline(always)] fn get_show_leftmost_bg(&self) -> bool { (self.ppumask >> 1) & 1 == 1} #[inline(always)] fn get_show_leftmost_sp(&self) -> bool { (self.ppumask >> 2) & 1 == 1} - #[inline(always)] fn get_show_bg(&self) -> bool { (self.ppumask >> 3) & 1 == 1} - #[inline(always)] fn get_show_sp(&self) -> bool { (self.ppumask >> 4) & 1 == 1} + #[inline(always)] pub fn get_show_bg(&self) -> bool { (self.ppumask >> 3) & 1 == 1} + #[inline(always)] pub fn get_show_sp(&self) -> bool { (self.ppumask >> 4) & 1 == 1} #[inline(always)] pub fn get_flag_vblank(&self) -> bool { (self.ppustatus >> 7) & 1 == 1 } #[inline(always)] fn get_oam_arr(&self) -> &[[u8; 4]; 64] { unsafe {transmute::<&[Sprite; 64], &[[u8; 4]; 64]>(&self.oam)} @@ -495,7 +495,13 @@ impl<'a> PPU<'a> { self.get_flag_vblank() && self.get_flag_nmi() } - pub fn tick(&mut self) -> bool { + pub fn tick(&mut self, bus: &CPUBus) -> bool { + let res = self._tick(); + self.mem.tick(bus); + res + } + + fn _tick(&mut self) -> bool { let cycle = self.cycle; if cycle == 0 { self.cycle = 1; |