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).
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}