1use crate::envelope::Envelope;
10use crate::length::LengthCounter;
11
12const DUTY_TABLE: [[u8; 8]; 4] = [
17 [0, 1, 0, 0, 0, 0, 0, 0], [0, 1, 1, 0, 0, 0, 0, 0], [0, 1, 1, 1, 1, 0, 0, 0], [1, 0, 0, 1, 1, 1, 1, 1], ];
22
23#[derive(Debug, Clone, Copy)]
25pub struct Pulse {
26 pub(crate) duty: u8,
28 pub(crate) step: u8,
30 pub(crate) timer_period: u16,
32 pub(crate) timer: u16,
34 pub envelope: Envelope,
36 pub length: LengthCounter,
38 pub(crate) sweep_enabled: bool,
40 pub(crate) sweep_period: u8,
42 pub(crate) sweep_negate: bool,
44 pub(crate) sweep_shift: u8,
46 pub(crate) sweep_reload: bool,
48 pub(crate) sweep_divider: u8,
50 pub(crate) is_pulse1: bool,
52}
53
54impl Pulse {
55 #[must_use]
58 pub const fn new(is_pulse1: bool) -> Self {
59 Self {
60 duty: 0,
61 step: 0,
62 timer_period: 0,
63 timer: 0,
64 envelope: Envelope {
65 start: false,
66 loop_flag: false,
67 constant: false,
68 volume_or_period: 0,
69 divider: 0,
70 decay: 0,
71 },
72 length: LengthCounter {
73 count: 0,
74 halt: false,
75 new_halt: false,
76 enabled: false,
77 reload_val: 0,
78 previous_count: 0,
79 },
80 sweep_enabled: false,
81 sweep_period: 0,
82 sweep_negate: false,
83 sweep_shift: 0,
84 sweep_reload: false,
85 sweep_divider: 0,
86 is_pulse1,
87 }
88 }
89
90 pub fn write_ctrl(&mut self, value: u8) {
92 self.duty = (value >> 6) & 0x03;
93 let halt = (value & 0x20) != 0;
94 self.length.set_halt(halt);
97 self.envelope.loop_flag = halt;
98 self.envelope.constant = (value & 0x10) != 0;
99 self.envelope.volume_or_period = value & 0x0F;
100 }
101
102 pub fn write_sweep(&mut self, value: u8) {
104 self.sweep_enabled = (value & 0x80) != 0;
105 self.sweep_period = (value >> 4) & 0x07;
106 self.sweep_negate = (value & 0x08) != 0;
107 self.sweep_shift = value & 0x07;
108 self.sweep_reload = true;
109 }
110
111 pub fn write_timer_lo(&mut self, value: u8) {
113 self.timer_period = (self.timer_period & 0xFF00) | u16::from(value);
114 }
115
116 pub fn write_timer_hi(&mut self, value: u8) {
118 self.timer_period = (self.timer_period & 0x00FF) | (u16::from(value & 0x07) << 8);
119 self.length.load(value);
120 self.step = 0;
121 self.envelope.start = true;
122 }
123
124 pub fn clock_timer(&mut self) {
126 if self.timer == 0 {
127 self.timer = self.timer_period;
128 self.step = (self.step + 1) & 0x07;
130 } else {
131 self.timer -= 1;
132 }
133 }
134
135 pub fn clock_half_frame(&mut self) {
137 let target = self.sweep_target();
139 if self.sweep_divider == 0 && self.sweep_enabled && self.sweep_shift > 0 && !self.muted() {
140 self.timer_period = target;
142 }
143 if self.sweep_divider == 0 || self.sweep_reload {
144 self.sweep_divider = self.sweep_period;
145 self.sweep_reload = false;
146 } else {
147 self.sweep_divider -= 1;
148 }
149 self.length.clock();
150 }
151
152 pub fn clock_quarter_frame(&mut self) {
154 self.envelope.clock();
155 }
156
157 fn sweep_target(&self) -> u16 {
159 let shifted = self.timer_period >> self.sweep_shift;
160 if self.sweep_negate {
161 if self.is_pulse1 {
162 self.timer_period.wrapping_sub(shifted).wrapping_sub(1)
163 } else {
164 self.timer_period.wrapping_sub(shifted)
165 }
166 } else {
167 self.timer_period.wrapping_add(shifted)
168 }
169 }
170
171 #[must_use]
173 pub fn muted(&self) -> bool {
174 self.timer_period < 8 || self.sweep_target() > 0x7FF
175 }
176
177 #[must_use]
179 pub fn output(&self) -> u8 {
180 if self.length.count == 0
181 || self.muted()
182 || DUTY_TABLE[self.duty as usize][self.step as usize] == 0
183 {
184 0
185 } else {
186 self.envelope.output()
187 }
188 }
189}
190
191#[cfg(test)]
192mod tests {
193 use super::*;
194
195 #[test]
196 fn write_ctrl_sets_duty_and_envelope_period() {
197 let mut p = Pulse::new(true);
198 p.write_ctrl(0b1011_0101); assert_eq!(p.duty, 2);
200 assert!(p.length.new_halt);
203 p.length.reload();
204 assert!(p.length.halt);
205 assert!(p.envelope.loop_flag);
206 assert!(p.envelope.constant);
207 assert_eq!(p.envelope.volume_or_period, 5);
208 }
209
210 #[test]
211 fn timer_underflow_advances_duty_step() {
212 let mut p = Pulse::new(true);
213 p.timer_period = 1;
214 p.timer = 0;
215 p.clock_timer();
216 assert_eq!(p.step, 1);
217 }
218
219 #[test]
220 fn pulse1_sweep_negation_is_ones_complement() {
221 let mut p = Pulse::new(true);
222 p.timer_period = 0x100;
223 p.sweep_negate = true;
224 p.sweep_shift = 1;
225 assert_eq!(p.sweep_target(), 0x7F);
227 }
228
229 #[test]
230 fn pulse2_sweep_negation_is_twos_complement() {
231 let mut p = Pulse::new(false);
232 p.timer_period = 0x100;
233 p.sweep_negate = true;
234 p.sweep_shift = 1;
235 assert_eq!(p.sweep_target(), 0x80);
237 }
238
239 #[test]
240 fn sweep_mutes_when_period_too_low() {
241 let mut p = Pulse::new(true);
242 p.timer_period = 7;
243 assert!(p.muted());
244 p.timer_period = 8;
245 assert!(!p.muted());
246 }
247
248 #[test]
249 fn sweep_mutes_when_target_above_7ff() {
250 let mut p = Pulse::new(true);
251 p.timer_period = 0x780;
252 p.sweep_negate = false;
253 p.sweep_shift = 1; assert!(p.muted());
255 }
256
257 #[test]
258 fn output_zero_when_length_zero() {
259 let mut p = Pulse::new(true);
260 p.length.count = 0;
261 p.envelope.constant = true;
262 p.envelope.volume_or_period = 15;
263 assert_eq!(p.output(), 0);
264 }
265
266 #[test]
267 fn timer_hi_write_resets_duty_phase_not_divider() {
268 let mut p = Pulse::new(true);
271 p.step = 5;
272 p.timer = 42;
273 p.write_timer_hi(0x03);
274 assert_eq!(p.step, 0, "duty sequencer phase must reset to 0");
275 assert_eq!(p.timer, 42, "timer divider must be preserved");
276 assert!(p.envelope.start, "envelope restart flag must be set");
277 }
278
279 #[test]
280 fn length_load_only_when_enabled() {
281 let mut p = Pulse::new(true);
282 p.length.enabled = false;
283 p.write_timer_hi(0x08);
284 p.length.reload();
287 assert_eq!(p.length.count, 0);
288 p.length.enabled = true;
289 p.write_timer_hi(0x08);
290 p.length.reload();
291 assert_ne!(p.length.count, 0);
292 }
293}