units.rsannotatedunits.rssource202 lines · 5.4 KB · raw

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
8use bincode::{Decode, Encode};
9use jgenesis_common::num::GetBit;
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}