The APU's triangle wave generator channel, which constantly cycles through a 32-step waveform going from 15 down to 0 then back up to 15. Period is configurable.

In addition to a length counter, this channel also has a "linear counter" which works similarly but is clocked every quarter-frame rather than every half-frame.

Channel output values are between 0 and 15 (inclusive).

9use crate::apu::units::{LengthCounter, LengthCounterChannel, PhaseTimer};
10use bincode::{Decode, Encode};
11use jgenesis_common::num::GetBit;
13type TrianglePhaseTimer = PhaseTimer<32, 1, 11, false>;
14
15#[derive(Debug, Clone, Encode, Decode)]
16struct LinearCounter {
17    counter: u8,
18    reload_value: u8,
19    control_flag: bool,
20    reload_flag: bool,
21}
22
23impl LinearCounter {
24    fn new() -> Self {
25        Self { counter: 0, reload_value: 0, control_flag: false, reload_flag: false }
26    }
27
28    fn process_tri_linear_update(&mut self, tri_linear_value: u8) {
29        self.control_flag = tri_linear_value.bit(7);
30        self.reload_value = tri_linear_value & 0x7F;
31    }
32
33    fn process_hi_update(&mut self) {
34        self.reload_flag = true;
35    }
36
37    fn clock(&mut self) {
38        if self.reload_flag {
39            self.counter = self.reload_value;
40        } else if self.counter > 0 {
41            self.counter -= 1;
42        }
43
44        if !self.control_flag {
45            self.reload_flag = false;
46        }
47    }
48}
49
50const TRIANGLE_WAVEFORM: [u8; 32] = [
51    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,
52    13, 14, 15,
53];
54
55#[derive(Debug, Clone, Encode, Decode)]
56pub struct TriangleChannel {
57    timer: TrianglePhaseTimer,
58    linear_counter: LinearCounter,
59    length_counter: LengthCounter,
60}
61
62impl TriangleChannel {
63    pub fn new() -> Self {
64        Self {
65            timer: TrianglePhaseTimer::new(),
66            linear_counter: LinearCounter::new(),
67            length_counter: LengthCounter::new(LengthCounterChannel::Triangle),
68        }
69    }
70
71    pub fn process_tri_linear_update(&mut self, tri_linear_value: u8) {
72        self.linear_counter.process_tri_linear_update(tri_linear_value);
73        self.length_counter.process_tri_linear_update(tri_linear_value);
74    }
75
76    pub fn process_lo_update(&mut self, lo_value: u8) {
77        self.timer.process_lo_update(lo_value);
78    }
79
80    pub fn process_hi_update(&mut self, hi_value: u8) {
81        self.timer.process_hi_update(hi_value);
82        self.linear_counter.process_hi_update();
83        self.length_counter.process_hi_update(hi_value);
84    }
85
86    pub fn process_snd_chn_update(&mut self, snd_chn_value: u8) {
87        self.length_counter.process_snd_chn_update(snd_chn_value);
88    }
89
90    pub fn clock_quarter_frame(&mut self) {
91        self.linear_counter.clock();
92    }
93
94    pub fn clock_half_frame(&mut self) {
95        self.length_counter.clock();
96    }
97
98    fn silenced(&self, silence_ultrasonic_output: bool) -> bool {
99        self.linear_counter.counter == 0
100            || self.length_counter.counter == 0
101            || (silence_ultrasonic_output && self.timer.divider_period < 2)
102    }
103
104    pub fn tick_cpu(&mut self, silence_ultrasonic_output: bool) {
105        self.timer.tick(!self.silenced(silence_ultrasonic_output));
106    }
107
108    pub fn sample(&self) -> u8 {
109        TRIANGLE_WAVEFORM[self.timer.phase as usize]
110    }
111
112    pub fn length_counter(&self) -> u8 {
113        self.length_counter.counter
114    }
115
116    pub fn reset(&mut self) {
117        self.timer.phase = 0;
118    }
119}