diff options
author | Determinant <[email protected]> | 2017-12-31 15:54:39 +0800 |
---|---|---|
committer | Determinant <[email protected]> | 2017-12-31 15:54:39 +0800 |
commit | e2d2242864b38770566b0b6ad48998c72bce7e4e (patch) | |
tree | 60080964fcc2d9dfb7c0d1cd58725fa70413cf6f | |
parent | db54d0d0d79f1817bfa41ebea7445912cdaea3d1 (diff) |
add DMC channel
-rw-r--r-- | Cargo.toml | 2 | ||||
-rw-r--r-- | src/apu.rs | 144 | ||||
-rw-r--r-- | src/memory.rs | 6 |
3 files changed, 141 insertions, 11 deletions
@@ -1,6 +1,6 @@ [package] name = "runes" -version = "0.1.2" +version = "0.1.3" authors = ["Determinant <[email protected]>"] description = "A no-std NES emulator library written purely in Rust." repository = "https://github.com/Determinant/runes" @@ -1,5 +1,6 @@ #![allow(dead_code)] -use mos6502; +use mos6502::{CPU_FREQ, CPU}; +use memory::CPUBus; pub trait Speaker { fn queue(&mut self, sample: u16); @@ -38,6 +39,10 @@ const NOISE_PERIOD_TABLE: [u16; 16] = [ 4, 8, 16, 32, 64, 96, 128, 160, 202, 254, 380, 508, 762, 1016, 2034, 4068 ]; +const DMC_TABLE: [u16; 16] = [ + 214, 190, 170, 160, 143, 127, 113, 107, 95, 80, 71, 64, 53, 42, 36, 27, +]; + const TND_TABLE: [u16; 203] = [ 0x0000, 0x01b7, 0x036a, 0x051a, 0x06c6, 0x086f, 0x0a15, 0x0bb7, 0x0d56, 0x0ef2, 0x108a, 0x121f, @@ -500,12 +505,125 @@ impl Noise { } } +pub struct DMC { + dmc_loop: bool, + dmc_cnt: u8, + irq_enabled: bool, + sample_addr: u16, + sample_len: u16, + shift_reg: u8, + cur_addr: u16, + rem_len: u16, + level: u8, + /* timer */ + timer_lvl: u16, + timer_period: u16, + /* channel */ + enabled: bool +} + +impl DMC { + pub fn new() -> Self { + DMC { + dmc_loop: false, dmc_cnt: 8, + irq_enabled: false, sample_addr: 0, sample_len: 0, + shift_reg: 0, cur_addr: 0, rem_len: 0, level: 0, + timer_lvl: 0, timer_period: 0, enabled: false + } + } + + pub fn write_reg1(&mut self, data: u8) { + self.irq_enabled = (data >> 7) == 1; + self.dmc_loop = data & 0x40 == 0x40; + self.timer_period = DMC_TABLE[(data & 0xf) as usize]; + } + + pub fn write_reg2(&mut self, data: u8) { + self.level = data & 0x7f + } + + pub fn write_reg3(&mut self, data: u8) { + self.sample_addr = 0xc000 | ((data as u16) << 6) + } + + pub fn write_reg4(&mut self, data: u8) { + self.sample_len = ((data as u16) << 4) | 0x1 + } + + fn restart(&mut self) { + self.cur_addr = self.sample_addr; + self.rem_len = self.sample_len; + } + + fn try_refill(&mut self, cpu: &mut CPU) { + if self.rem_len > 0 && self.dmc_cnt == 0 { + cpu.cycle += 4; + self.shift_reg = cpu.mem.read_without_tick(self.cur_addr); + self.dmc_cnt = 8; + self.cur_addr = self.cur_addr.wrapping_add(1); + if self.cur_addr == 0x0 { + self.cur_addr = 0x8000 + } + self.rem_len -= 1; + if self.rem_len == 0 && self.dmc_loop { + self.restart() + } + } + } + + fn shift(&mut self) { + if self.dmc_cnt == 0 { return } + if self.shift_reg & 1 == 1 { + if self.level < 126 { + self.level += 2 + } + } else { + if self.level > 1 { + self.level -= 2 + } + } + self.shift_reg >>= 1; + self.dmc_cnt -= 1; + } + + fn tick_timer(&mut self, cpu: &mut CPU) { + if !self.enabled { return } + self.try_refill(cpu); + if self.timer_lvl == 0 { + self.timer_lvl = self.timer_period; + self.shift(); + } else { + self.timer_lvl -= 1 + } + } + + #[inline(always)] + fn get_len(&self) -> u16 { self.rem_len } + + #[inline(always)] + fn disable(&mut self) { + self.enabled = false; + self.rem_len = 0; + } + + #[inline(always)] + fn enable(&mut self) { + self.enabled = true; + if self.rem_len == 0 { + self.restart() + } + } + + #[inline(always)] + fn output(&self) -> u8 { self.level } +} pub struct APU<'a> { pub pulse1: Pulse, pub pulse2: Pulse, pub triangle: Triangle, pub noise: Noise, + pub dmc: DMC, frame_lvl: u8, frame_mode: bool, /* true for 5-step mode */ frame_inh: bool, @@ -513,24 +631,25 @@ pub struct APU<'a> { cpu_sampler: Sampler, audio_sampler: Sampler, cycle_even: bool, - spkr: &'a mut Speaker + spkr: &'a mut Speaker, } impl<'a> APU<'a> { - pub fn new(spkr: &'a mut Speaker) -> Self { + pub fn new(spkr: &'a mut Speaker/*, bus: &'a CPUBus<'a>*/) -> Self { APU { pulse1: Pulse::new(false), pulse2: Pulse::new(true), triangle: Triangle::new(), noise: Noise::new(), + dmc: DMC::new(), 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), + cpu_sampler: Sampler::new(CPU_FREQ, CPU_SAMPLE_FREQ), + audio_sampler: Sampler::new(CPU_FREQ, AUDIO_SAMPLE_FREQ), cycle_even: false, spkr } } - pub fn tick(&mut self) -> bool { + pub fn tick(&mut self, bus: &CPUBus) -> bool { let mut irq = false; if let (true, _) = self.cpu_sampler.tick() { irq = self.tick_frame_counter(); @@ -539,7 +658,7 @@ impl<'a> APU<'a> { let sample = self.output(); self.spkr.queue(sample); } - self.tick_timer(); + self.tick_timer(bus.get_cpu()); self.cycle_even = !self.cycle_even; irq } @@ -548,7 +667,8 @@ impl<'a> APU<'a> { let pulse_out = PULSE_TABLE[(self.pulse1.output() + self.pulse2.output()) as usize]; let tnd_out = TND_TABLE[(self.triangle.output() * 3 + - self.noise.output() * 2) as usize]; + self.noise.output() * 2 + + self.dmc.output()) as usize]; pulse_out + tnd_out } @@ -557,6 +677,7 @@ impl<'a> APU<'a> { (if self.pulse2.get_len() > 0 { 1 } else { 0 }) << 1 | (if self.triangle.get_len() > 0 { 1 } else { 0 }) << 2 | (if self.noise.get_len() > 0 { 1 } else { 0 }) << 3 | + (if self.dmc.get_len() > 0 { 1 } else { 0 }) << 4 | (if self.frame_int { 1 } else { 0 }) << 6; if self.frame_lvl != 3 { self.frame_int = false; /* clear interrupt flag */ @@ -581,6 +702,10 @@ impl<'a> APU<'a> { 0 => self.noise.disable(), _ => self.noise.enable() } + match data & 0x10 { + 0 => self.dmc.disable(), + _ => self.dmc.enable() + } } pub fn write_frame_counter(&mut self, data: u8) { @@ -591,11 +716,12 @@ impl<'a> APU<'a> { } } - fn tick_timer(&mut self) { + fn tick_timer(&mut self, cpu: &mut CPU) { if self.cycle_even { self.pulse1.tick_timer(); self.pulse2.tick_timer(); self.noise.tick_timer(); + self.dmc.tick_timer(cpu); } self.triangle.tick_timer(); } diff --git a/src/memory.rs b/src/memory.rs index fac5c28..99a6bae 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -48,7 +48,7 @@ impl<'a> CPUBus<'a> { if ppu.tick(self) || ppu.tick(self) || ppu.tick(self) { cpu.trigger_nmi() } - if apu.tick() { + if apu.tick(self) { cpu.trigger_irq() } if let (true, _) = self.ppu_sampler.borrow_mut().tick() { @@ -149,6 +149,10 @@ impl<'a> CPUMemory<'a> { 0x400c => apu.noise.write_reg1(data), 0x400e => apu.noise.write_reg3(data), 0x400f => apu.noise.write_reg4(data), + 0x4010 => apu.dmc.write_reg1(data), + 0x4011 => apu.dmc.write_reg2(data), + 0x4012 => apu.dmc.write_reg3(data), + 0x4013 => apu.dmc.write_reg4(data), 0x4015 => apu.write_status(data), 0x4017 => apu.write_frame_counter(data), 0x4014 => ppu.write_oamdma(data, cpu), |