nes-sim 0.1.4

A NES (Famicom) emulator core library written in pure Rust.
Documentation
use super::LENGTH_TABLE;

const TRIANGLE_TABLE: [u8; 32] = [
    15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
    13, 14, 15,
];

#[derive(Clone, Copy, Default)]
pub(super) struct TriangleChannel {
    pub(super) enabled: bool,
    pub(super) length_counter: u8,
    pub(super) timer_reload: u16,
    pub(super) timer_counter: u16,
    pub(super) seq_step: u8,
    pub(super) control_flag: bool,
    pub(super) linear_reload_value: u8,
    pub(super) linear_counter: u8,
    pub(super) linear_reload_flag: bool,
}

impl TriangleChannel {
    pub(super) fn write_linear_control(&mut self, value: u8) {
        self.control_flag = (value & 0x80) != 0;
        self.linear_reload_value = value & 0x7F;
    }

    pub(super) fn write_timer_low(&mut self, value: u8) {
        self.timer_reload = (self.timer_reload & 0x0700) | value as u16;
    }

    pub(super) fn write_timer_high(&mut self, value: u8, length_enabled: bool) {
        self.timer_reload = (self.timer_reload & 0x00FF) | (((value & 0x07) as u16) << 8);
        if length_enabled {
            self.length_counter = LENGTH_TABLE[(value >> 3) as usize];
        }
        self.linear_reload_flag = true;
    }

    pub(super) fn tick_timer(&mut self) {
        if self.timer_reload < 2 {
            return;
        }
        if self.timer_counter == 0 {
            self.timer_counter = self.timer_reload;
            if self.length_counter > 0 && self.linear_counter > 0 {
                self.seq_step = (self.seq_step + 1) & 0x1F;
            }
        } else {
            self.timer_counter -= 1;
        }
    }

    pub(super) fn quarter_frame_tick(&mut self) {
        if self.linear_reload_flag {
            self.linear_counter = self.linear_reload_value;
        } else if self.linear_counter > 0 {
            self.linear_counter -= 1;
        }

        if !self.control_flag {
            self.linear_reload_flag = false;
        }
    }

    pub(super) fn half_frame_tick(&mut self) {
        if !self.control_flag && self.length_counter > 0 {
            self.length_counter -= 1;
        }
    }

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

    #[allow(dead_code)]
    pub(super) fn output(&self) -> u8 {
        if !self.enabled
            || self.timer_reload < 2
            || self.length_counter == 0
            || self.linear_counter == 0
        {
            return 0;
        }
        TRIANGLE_TABLE[self.seq_step as usize]
    }
}