reznez 0.0.0

The high accuracy NES Emulator
Documentation
use crate::apu::length_counter::LengthCounter;
use crate::apu::timer::Timer;
use crate::util::integer::U4;

const NTSC_PERIODS: [u16; 16] =
    [4, 8, 16, 32, 64, 96, 128, 160, 202, 254, 380, 508, 762, 1016, 2034, 4068];

pub struct NoiseChannel {
    pub(super) enabled: bool,

    constant_volume: bool,
    volume_or_envelope: U4,

    mode: bool,
    timer: Timer,
    pub(super) length_counter: LengthCounter,

    shift_register: u16,
}

impl Default for NoiseChannel {
    fn default() -> NoiseChannel {
        NoiseChannel {
            enabled: false,

            constant_volume: false,
            volume_or_envelope: U4::default(),

            mode: false,
            timer: Timer::default(),
            length_counter: LengthCounter::default(),

            shift_register: 0b000_0000_0000_0001,
        }
    }
}

impl NoiseChannel {
    pub fn write_control_byte(&mut self, value: u8) {
        self.length_counter.set_halt((value & 0b0010_0000) != 0);
        self.constant_volume =       (value & 0b0001_0000) != 0;
        self.volume_or_envelope =    (value & 0b0000_1111).into();
    }

    pub fn write_loop_and_period_byte(&mut self, value: u8) {
        self.mode = (value & 0b1000_0000) != 0;
        let period = NTSC_PERIODS[(value & 0b0000_1111) as usize];
        self.timer.set_period_and_reset_index(period);
    }

    pub fn write_length_byte(&mut self, value: u8) {
        if self.enabled {
            self.length_counter.set_count_from_lookup((value & 0b1111_1000) >> 3);
        }
    }

    pub(super) fn set_enabled(&mut self, enabled: bool) {
        self.enabled = enabled;
        if !self.enabled {
            self.length_counter.set_to_zero();
        }
    }

    pub(super) fn active(&self) -> bool {
        !self.length_counter.is_zero()
    }

    pub(super) fn on_cycle_step(&mut self) {
        let wrapped_around = self.timer.tick();
        if wrapped_around {
            let mut feedback = self.shift_register & 1;
            if self.mode {
                feedback ^= (self.shift_register & 0b100_0000) >> 6;
            } else {
                feedback ^= (self.shift_register & 0b000_0010) >> 1;
            };

            feedback <<= 14;

            self.shift_register >>= 1;
            self.shift_register |= feedback;
        }
    }

    pub(super) fn sample_volume(&self) -> f32 {
        if self.length_counter.is_zero() || self.shift_register & 1 == 0 {
            0.0
        } else {
            f32::from(self.volume_or_envelope.to_u8())
        }
    }
}