Common units that are used in multiple APU channels.
These include:
- A length counter, which can automatically disable the channel after a set amount of time
- An envelope, which can automatically increase or decrease the channel's volume over time
- A frequency timer which infinitely cycles through a set number of phase steps based on a divider of the CPU clock
11const LENGTH_COUNTER_LOOKUP_TABLE: [u8; 32] = [ 12 10, 254, 20, 2, 40, 4, 80, 6, 160, 8, 60, 10, 14, 12, 26, 14, 12, 16, 24, 18, 48, 20, 96, 22, 13 192, 24, 72, 26, 16, 28, 32, 30, 14]; 15 16#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode)] 17pub enum LengthCounterChannel { 18 Pulse1, 19 Pulse2, 20 Triangle, 21 Noise, 22} 23 24impl LengthCounterChannel { 25 fn snd_chn_enabled_mask(self) -> u8 { 26 match self { 27 Self::Pulse1 => 0x01, 28 Self::Pulse2 => 0x02, 29 Self::Triangle => 0x04, 30 Self::Noise => 0x08, 31 } 32 } 33} 34 35#[derive(Debug, Clone, Encode, Decode)] 36pub struct LengthCounter { 37 channel: LengthCounterChannel, 38 pub counter: u8, 39 enabled: bool, 40 halted: bool, 41} 42 43impl LengthCounter { 44 pub fn new(channel: LengthCounterChannel) -> Self { 45 Self { channel, counter: 0, enabled: false, halted: false } 46 } 47 48 pub fn process_snd_chn_update(&mut self, snd_chn_value: u8) { 49 let enabled = snd_chn_value & self.channel.snd_chn_enabled_mask() != 0; 50 self.enabled = enabled; 51 52 if !enabled { 53 self.counter = 0; 54 } 55 } 56 57 pub fn process_vol_update(&mut self, vol_value: u8) { 58 assert!(matches!( 59 self.channel, 60 LengthCounterChannel::Pulse1 61 | LengthCounterChannel::Pulse2 62 | LengthCounterChannel::Noise 63 )); 64 65 self.halted = vol_value.bit(5); 66 } 67 68 pub fn process_tri_linear_update(&mut self, tri_linear_value: u8) { 69 assert_eq!(self.channel, LengthCounterChannel::Triangle); 70 71 self.halted = tri_linear_value.bit(7); 72 } 73 74 pub fn process_hi_update(&mut self, hi_value: u8) { 75 if self.enabled { 76 self.counter = LENGTH_COUNTER_LOOKUP_TABLE[(hi_value >> 3) as usize]; 77 } 78 } 79 80 pub fn clock(&mut self) { 81 if !self.halted && self.counter > 0 { 82 self.counter -= 1; 83 } 84 } 85} 86 87#[derive(Debug, Clone, Encode, Decode)] 88pub struct Envelope { 89 divider: u8, 90 divider_period: u8, 91 decay_level_counter: u8, 92 start_flag: bool, 93 loop_flag: bool, 94 constant_volume_flag: bool, 95} 96 97impl Envelope { 98 pub fn new() -> Self { 99 Self { 100 divider: 0, 101 divider_period: 0, 102 decay_level_counter: 0, 103 start_flag: false, 104 loop_flag: false, 105 constant_volume_flag: false, 106 } 107 } 108 109 pub fn volume(&self) -> u8 { 110 if self.constant_volume_flag { self.divider_period } else { self.decay_level_counter } 111 } 112 113 pub fn process_vol_update(&mut self, vol_value: u8) { 114 self.loop_flag = vol_value.bit(5); 115 self.constant_volume_flag = vol_value.bit(4); 116 self.divider_period = vol_value & 0x0F; 117 } 118 119 pub fn process_hi_update(&mut self) { 120 self.start_flag = true; 121 } 122 123 pub fn clock(&mut self) { 124 if self.start_flag { 125 self.start_flag = false; 126 127 self.divider = self.divider_period; 128 self.decay_level_counter = 0x0F; 129 } else if self.divider == 0 { 130 self.divider = self.divider_period; 131 132 if self.decay_level_counter > 0 { 133 self.decay_level_counter -= 1; 134 } else if self.loop_flag { 135 self.decay_level_counter = 0x0F; 136 } 137 } else { 138 self.divider -= 1; 139 } 140 } 141} 142 143#[derive(Debug, Clone, Encode, Decode)] 144pub struct PhaseTimer< 145 const MAX_PHASE: u8, 146 const CPU_TICKS_PER_CLOCK: u8, 147 const DIVIDER_BITS: u8, 148 const CAN_RESET_PHASE: bool, 149> { 150 cpu_ticks: u8, 151 cpu_divider: u16, 152 pub divider_period: u16, 153 pub phase: u8, 154} 155 156impl< 157 const MAX_PHASE: u8, 158 const CPU_TICKS_PER_CLOCK: u8, 159 const DIVIDER_BITS: u8, 160 const CAN_RESET_PHASE: bool, 161> PhaseTimer<MAX_PHASE, CPU_TICKS_PER_CLOCK, DIVIDER_BITS, CAN_RESET_PHASE> 162{ 163 pub fn new() -> Self { 164 assert!(DIVIDER_BITS == 11 || DIVIDER_BITS == 12, "DIVIDER_BITS must be 11 or 12"); 165 166 Self { cpu_ticks: 0, cpu_divider: 0, divider_period: 0, phase: 0 } 167 } 168 169 pub fn process_lo_update(&mut self, lo_value: u8) { 170 self.divider_period = (self.divider_period & 0xFF00) | u16::from(lo_value); 171 } 172 173 pub fn process_hi_update(&mut self, hi_value: u8) { 174 let hi_mask = match DIVIDER_BITS { 175 11 => 0x07, 176 12 => 0x0F, 177 _ => panic!("DIVIDER_BITS must be 11 or 12"), 178 }; 179 180 self.divider_period = (u16::from(hi_value & hi_mask) << 8) | (self.divider_period & 0x00FF); 181 if CAN_RESET_PHASE { 182 self.phase = 0; 183 } 184 } 185 186 pub fn tick(&mut self, sequencer_enabled: bool) { 187 self.cpu_ticks += 1; 188 if self.cpu_ticks < CPU_TICKS_PER_CLOCK { 189 return; 190 } 191 self.cpu_ticks = 0; 192 193 if self.cpu_divider == 0 { 194 self.cpu_divider = self.divider_period; 195 if sequencer_enabled { 196 self.phase = (self.phase + 1) & (MAX_PHASE - 1); 197 } 198 } else { 199 self.cpu_divider -= 1; 200 } 201 } 202}