tetanes_core/apu/
triangle.rs

1//! APU Triangle Channel implementation.
2//!
3//! See: <https://www.nesdev.org/wiki/APU_Triangle>
4
5use crate::{
6    apu::{
7        Channel,
8        length_counter::LengthCounter,
9        timer::{Timer, TimerCycle},
10    },
11    common::{Clock, Reset, ResetKind, Sample},
12};
13use serde::{Deserialize, Serialize};
14
15/// APU Triangle Channel provides triangle wave generation.
16///
17/// See: <https://www.nesdev.org/wiki/APU_Triangle>
18#[derive(Debug, Clone, Serialize, Deserialize)]
19#[must_use]
20pub struct Triangle {
21    pub timer: Timer,
22    pub sequence: u8,
23    pub length: LengthCounter,
24    pub linear: LinearCounter,
25    pub force_silent: bool,
26}
27
28impl Default for Triangle {
29    fn default() -> Self {
30        Self::new()
31    }
32}
33
34impl Triangle {
35    const SEQUENCE: [u8; 32] = [
36        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,
37        12, 13, 14, 15,
38    ];
39
40    pub const fn new() -> Self {
41        Self {
42            timer: Timer::new(0),
43            sequence: 0,
44            length: LengthCounter::new(Channel::Triangle),
45            linear: LinearCounter::new(),
46            force_silent: false,
47        }
48    }
49
50    #[must_use]
51    pub const fn silent(&self) -> bool {
52        self.force_silent
53    }
54
55    pub const fn set_silent(&mut self, silent: bool) {
56        self.force_silent = silent;
57    }
58
59    pub fn clock_quarter_frame(&mut self) {
60        self.linear.clock();
61    }
62
63    pub fn clock_half_frame(&mut self) {
64        self.clock_quarter_frame();
65        self.length.clock();
66    }
67
68    /// $4008 Linear counter control
69    pub const fn write_linear_counter(&mut self, val: u8) {
70        self.linear.control = (val & 0x80) == 0x80; // D7
71        self.linear.write(val & 0x7F); // D6..D0;
72        self.length.write_ctrl(self.linear.control); // !D7
73    }
74
75    /// $400A Triangle timer lo
76    pub fn write_timer_lo(&mut self, val: u8) {
77        self.timer.period = (self.timer.period & 0xFF00) | u64::from(val); // D7..D0
78    }
79
80    /// $400B Triangle timer high
81    pub fn write_timer_hi(&mut self, val: u8) {
82        self.length.write(val >> 3);
83        self.timer.period = (self.timer.period & 0x00FF) | (u64::from(val & 0x07) << 8); // D2..D0
84        self.linear.reload = true;
85    }
86
87    pub const fn set_enabled(&mut self, enabled: bool) {
88        self.length.set_enabled(enabled);
89    }
90}
91
92impl Sample for Triangle {
93    fn output(&self) -> f32 {
94        if self.silent() {
95            0.0
96        } else if self.timer.period < 2 {
97            // This is normally silenced by a lowpass filter on real hardware
98            // See: https://forums.nesdev.org/viewtopic.php?t=10658
99            7.5
100        } else {
101            f32::from(Self::SEQUENCE[self.sequence as usize])
102        }
103    }
104}
105
106impl TimerCycle for Triangle {
107    fn cycle(&self) -> u64 {
108        self.timer.cycle
109    }
110}
111
112impl Clock for Triangle {
113    //       Linear Counter   Length Counter
114    //             |                |
115    //             v                v
116    // Timer ---> Gate ----------> Gate ---> Sequencer ---> (to mixer)
117    fn clock(&mut self) -> u64 {
118        if self.timer.clock() > 0 && self.length.counter > 0 && self.linear.counter > 0 {
119            self.sequence = (self.sequence + 1) & 0x1F;
120            1
121        } else {
122            0
123        }
124    }
125}
126
127impl Reset for Triangle {
128    fn reset(&mut self, kind: ResetKind) {
129        self.length.reset(kind);
130        self.linear.reset(kind);
131        self.sequence = 0;
132    }
133}
134
135/// APU Linear Counter provides duration control for the APU triangle channel.
136///
137/// See: <https://www.nesdev.org/wiki/APU_Triangle>
138#[derive(Default, Debug, Clone, Serialize, Deserialize)]
139#[must_use]
140pub struct LinearCounter {
141    pub reload: bool,
142    pub control: bool,
143    pub counter_reload: u8,
144    pub counter: u8,
145}
146
147impl LinearCounter {
148    pub const fn new() -> Self {
149        Self {
150            reload: false,
151            control: false,
152            counter_reload: 0u8,
153            counter: 0u8,
154        }
155    }
156
157    pub const fn write(&mut self, val: u8) {
158        self.counter_reload = val;
159    }
160}
161
162impl Clock for LinearCounter {
163    fn clock(&mut self) -> u64 {
164        let mut clock = 0;
165        if self.reload {
166            self.counter = self.counter_reload;
167            clock = 1;
168        } else if self.counter > 0 {
169            self.counter -= 1;
170        }
171        if !self.control {
172            self.reload = false;
173        }
174        clock
175    }
176}
177
178impl Reset for LinearCounter {
179    fn reset(&mut self, _kind: ResetKind) {
180        self.counter = 0;
181        self.counter_reload = 0;
182        self.reload = false;
183        self.control = false;
184    }
185}