fix(apu): clock frame sequencer at correct Hz
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				continuous-integration/drone/push Build is passing
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	continuous-integration/drone/push Build is passing
				
			This commit is contained in:
		
							
								
								
									
										56
									
								
								src/apu.rs
									
									
									
									
									
								
							
							
						
						
									
										56
									
								
								src/apu.rs
									
									
									
									
									
								
							@@ -5,7 +5,7 @@ use types::ch1::{Sweep, SweepDirection};
 | 
				
			|||||||
use types::ch3::Volume as Ch3Volume;
 | 
					use types::ch3::Volume as Ch3Volume;
 | 
				
			||||||
use types::ch4::{CounterWidth, Frequency as Ch4Frequency, PolynomialCounter};
 | 
					use types::ch4::{CounterWidth, Frequency as Ch4Frequency, PolynomialCounter};
 | 
				
			||||||
use types::common::{EnvelopeDirection, FrequencyHigh, SoundDuty, VolumeEnvelope};
 | 
					use types::common::{EnvelopeDirection, FrequencyHigh, SoundDuty, VolumeEnvelope};
 | 
				
			||||||
use types::{ChannelControl, FrameSequencerState, SoundOutput};
 | 
					use types::{ChannelControl, SoundOutput};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub mod gen;
 | 
					pub mod gen;
 | 
				
			||||||
mod types;
 | 
					mod types;
 | 
				
			||||||
@@ -26,8 +26,7 @@ pub struct Apu {
 | 
				
			|||||||
    ch4: Channel4,
 | 
					    ch4: Channel4,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Frame Sequencer
 | 
					    // Frame Sequencer
 | 
				
			||||||
    frame_seq_state: FrameSequencerState,
 | 
					    div_prev: Option<u16>,
 | 
				
			||||||
    div_prev: Option<u8>,
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    prod: Option<SampleProducer<f32>>,
 | 
					    prod: Option<SampleProducer<f32>>,
 | 
				
			||||||
    sample_counter: u64,
 | 
					    sample_counter: u64,
 | 
				
			||||||
@@ -95,41 +94,30 @@ impl BusIo for Apu {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
impl Apu {
 | 
					impl Apu {
 | 
				
			||||||
    pub(crate) fn tick(&mut self, div: u16) {
 | 
					    pub(crate) fn tick(&mut self, div: u16) {
 | 
				
			||||||
        use FrameSequencerState::*;
 | 
					 | 
				
			||||||
        self.sample_counter += SAMPLE_INCREMENT;
 | 
					        self.sample_counter += SAMPLE_INCREMENT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // the 5th bit of the high byte
 | 
					        // Length Control (256Hz)
 | 
				
			||||||
        let bit_5 = (div >> 13 & 0x01) as u8;
 | 
					        if self.falling_edge(13, div) {
 | 
				
			||||||
 | 
					            self.handle_length();
 | 
				
			||||||
        if let Some(0x01) = self.div_prev {
 | 
					 | 
				
			||||||
            if bit_5 == 0x00 {
 | 
					 | 
				
			||||||
                // Falling Edge, step the Frame Sequencer
 | 
					 | 
				
			||||||
                self.frame_seq_state.step();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                match self.frame_seq_state {
 | 
					 | 
				
			||||||
                    Step0Length => self.handle_length(),
 | 
					 | 
				
			||||||
                    Step2LengthAndSweep => {
 | 
					 | 
				
			||||||
                        self.handle_length();
 | 
					 | 
				
			||||||
                        self.handle_sweep();
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    Step4Length => self.handle_length(),
 | 
					 | 
				
			||||||
                    Step6LengthAndSweep => {
 | 
					 | 
				
			||||||
                        self.handle_length();
 | 
					 | 
				
			||||||
                        self.handle_sweep();
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    Step7VolumeEnvelope => self.handle_volume(),
 | 
					 | 
				
			||||||
                    Step1Nothing | Step3Nothing | Step5Nothing => {}
 | 
					 | 
				
			||||||
                };
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Sweep (128Hz)
 | 
				
			||||||
 | 
					        if self.falling_edge(14, div) {
 | 
				
			||||||
 | 
					            self.handle_sweep();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Volume Envelope (64Hz)
 | 
				
			||||||
 | 
					        if self.falling_edge(15, div) {
 | 
				
			||||||
 | 
					            self.handle_volume();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.div_prev = Some(div);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.ch1.clock();
 | 
					        self.ch1.clock();
 | 
				
			||||||
        self.ch2.clock();
 | 
					        self.ch2.clock();
 | 
				
			||||||
        self.ch3.clock();
 | 
					        self.ch3.clock();
 | 
				
			||||||
        self.ch4.clock();
 | 
					        self.ch4.clock();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.div_prev = Some(bit_5);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if self.sample_counter >= SM83_CLOCK_SPEED {
 | 
					        if self.sample_counter >= SM83_CLOCK_SPEED {
 | 
				
			||||||
            self.sample_counter %= SM83_CLOCK_SPEED;
 | 
					            self.sample_counter %= SM83_CLOCK_SPEED;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -176,7 +164,8 @@ impl Apu {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        if self.ctrl.enabled {
 | 
					        if self.ctrl.enabled {
 | 
				
			||||||
            // Frame Sequencer reset to Step 0
 | 
					            // Frame Sequencer reset to Step 0
 | 
				
			||||||
            self.frame_seq_state = Default::default();
 | 
					            // TODO: With the current implementation of the frame sequencer,
 | 
				
			||||||
 | 
					            // what does this even mean?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Square Duty units are reset to first step
 | 
					            // Square Duty units are reset to first step
 | 
				
			||||||
            self.ch1.duty_pos = 0;
 | 
					            self.ch1.duty_pos = 0;
 | 
				
			||||||
@@ -338,6 +327,13 @@ impl Apu {
 | 
				
			|||||||
            &mut self.ch4.current_volume,
 | 
					            &mut self.ch4.current_volume,
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn falling_edge(&self, bit: u8, div: u16) -> bool {
 | 
				
			||||||
 | 
					        match self.div_prev {
 | 
				
			||||||
 | 
					            Some(p) => (p >> bit & 0x01) == 0x01 && (div >> bit & 0x01) == 0x00,
 | 
				
			||||||
 | 
					            None => false,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug, Default)]
 | 
					#[derive(Debug, Default)]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -427,41 +427,6 @@ pub(crate) mod common {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug, Clone, Copy)]
 | 
					 | 
				
			||||||
pub(crate) enum FrameSequencerState {
 | 
					 | 
				
			||||||
    Step0Length,
 | 
					 | 
				
			||||||
    Step1Nothing,
 | 
					 | 
				
			||||||
    Step2LengthAndSweep,
 | 
					 | 
				
			||||||
    Step3Nothing,
 | 
					 | 
				
			||||||
    Step4Length,
 | 
					 | 
				
			||||||
    Step5Nothing,
 | 
					 | 
				
			||||||
    Step6LengthAndSweep,
 | 
					 | 
				
			||||||
    Step7VolumeEnvelope,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl FrameSequencerState {
 | 
					 | 
				
			||||||
    pub(crate) fn step(&mut self) {
 | 
					 | 
				
			||||||
        use FrameSequencerState::*;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        *self = match *self {
 | 
					 | 
				
			||||||
            Step0Length => Step1Nothing,
 | 
					 | 
				
			||||||
            Step1Nothing => Step2LengthAndSweep,
 | 
					 | 
				
			||||||
            Step2LengthAndSweep => Step3Nothing,
 | 
					 | 
				
			||||||
            Step3Nothing => Step4Length,
 | 
					 | 
				
			||||||
            Step4Length => Step5Nothing,
 | 
					 | 
				
			||||||
            Step5Nothing => Step6LengthAndSweep,
 | 
					 | 
				
			||||||
            Step6LengthAndSweep => Step7VolumeEnvelope,
 | 
					 | 
				
			||||||
            Step7VolumeEnvelope => Step0Length,
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl Default for FrameSequencerState {
 | 
					 | 
				
			||||||
    fn default() -> Self {
 | 
					 | 
				
			||||||
        Self::Step0Length
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
bitfield! {
 | 
					bitfield! {
 | 
				
			||||||
    pub struct SoundOutput(u8);
 | 
					    pub struct SoundOutput(u8);
 | 
				
			||||||
    impl Debug;
 | 
					    impl Debug;
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user