2020-01-15 02:51:59 +00:00
use super ::envelope ::Envelope ;
2019-12-18 02:38:15 +00:00
const DUTY_CYCLE_SEQUENCES : [ [ u8 ; 8 ] ; 4 ] = [
2019-12-17 04:06:00 +00:00
[ 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 ] ,
] ;
2020-02-29 23:23:51 +00:00
#[ derive(serde::Serialize, serde::Deserialize, Clone) ]
2019-12-17 04:06:00 +00:00
pub struct Square {
2020-01-15 02:51:59 +00:00
pub sample : u16 , // output value that gets sent to the mixer
2019-12-22 00:02:32 +00:00
pub enabled : bool ,
2019-12-17 04:06:00 +00:00
constant_volume_flag : bool , // (0: use volume from envelope; 1: use constant volume)
2020-01-15 02:51:59 +00:00
first_channel : bool , // hack to detect timing difference in clock_sweep()
2019-12-22 00:02:32 +00:00
timer : u16 ,
timer_period : u16 ,
2020-01-15 02:51:59 +00:00
duty_cycle : [ u8 ; 8 ] , // "sequencer", set to one of the lines in DUTY_CYCLE_SEQUENCES
duty_counter : usize , // current index within the duty_cycle
pub length_counter : u8 ,
2019-12-31 18:57:02 +00:00
target_period : u16 ,
2020-01-15 02:51:59 +00:00
sweep_period : u16 ,
2019-12-23 23:46:14 +00:00
sweep_counter : u16 ,
2019-12-22 00:02:32 +00:00
shift_count : u8 ,
sweep_enabled : bool ,
sweep_negate : bool ,
sweep_reload : bool ,
2019-12-22 04:14:47 +00:00
2020-01-15 02:51:59 +00:00
pub envelope : Envelope ,
2019-12-17 04:06:00 +00:00
}
impl Square {
2020-01-15 02:51:59 +00:00
pub fn new ( first_channel : bool ) -> Self {
2019-12-17 04:06:00 +00:00
Square {
2019-12-22 00:02:32 +00:00
sample : 0 ,
enabled : false ,
2019-11-27 01:11:51 +00:00
constant_volume_flag : false ,
2020-01-15 02:51:59 +00:00
first_channel : first_channel ,
2019-11-27 01:11:51 +00:00
timer : 0 ,
2019-12-22 00:02:32 +00:00
timer_period : 0 ,
2020-01-15 02:51:59 +00:00
duty_cycle : DUTY_CYCLE_SEQUENCES [ 0 ] ,
duty_counter : 0 ,
length_counter : 0 ,
2019-12-31 18:57:02 +00:00
target_period : 0 ,
2020-01-15 02:51:59 +00:00
sweep_period : 0 ,
2019-12-22 04:14:47 +00:00
sweep_counter : 0 ,
2019-12-23 23:46:14 +00:00
shift_count : 0 ,
2019-12-22 00:02:32 +00:00
sweep_enabled : false ,
sweep_negate : false ,
sweep_reload : false ,
2020-01-15 02:51:59 +00:00
envelope : Envelope ::new ( ) ,
2019-11-27 01:11:51 +00:00
}
}
2019-12-05 02:57:05 +00:00
pub fn clock ( & mut self ) {
2019-12-22 00:02:32 +00:00
// The sequencer is clocked by an 11-bit timer. Given the timer value t = HHHLLLLLLLL formed by timer high and timer low, this timer is updated every APU cycle
2020-01-02 03:20:47 +00:00
// (i.e., every second CPU cycle), and counts t, t-1, ..., 0, t, t-1, ..., clocking the waveform generator when it goes from 0 to t.
2019-12-22 00:02:32 +00:00
if self . timer = = 0 {
self . timer = self . timer_period ;
self . duty_counter = ( self . duty_counter + 1 ) % 8 ;
} else {
self . timer - = 1 ;
}
// Update volume for this channel
// The mixer receives the current envelope volume except when
2019-12-31 02:10:27 +00:00
self . sample = if self . duty_cycle [ self . duty_counter ] = = 0 // the sequencer output is zero, or
2019-12-31 23:22:44 +00:00
| | self . timer_period > 0x7FF // overflow from the sweep unit's adder is silencing the channel,
2019-12-23 23:46:14 +00:00
| | self . length_counter = = 0 // the length counter is zero, or
2020-01-02 03:20:47 +00:00
| | self . timer_period < 8 // the timer has a value less than eight.
{
2019-12-22 00:02:32 +00:00
0
2019-12-31 18:57:02 +00:00
} else if self . constant_volume_flag {
2020-01-15 02:51:59 +00:00
self . envelope . period
2019-12-22 00:02:32 +00:00
} else {
2020-01-15 02:51:59 +00:00
self . envelope . decay_counter
2019-12-22 00:02:32 +00:00
} ;
2019-12-17 05:43:10 +00:00
}
2019-12-31 18:57:02 +00:00
pub fn clock_length_counter ( & mut self ) {
2020-01-15 02:51:59 +00:00
if ! ( self . length_counter = = 0 | | self . envelope . length_counter_halt ) {
2019-12-31 18:57:02 +00:00
self . length_counter - = 1 ;
2019-12-22 04:14:47 +00:00
}
2019-12-31 18:57:02 +00:00
}
pub fn clock_sweep ( & mut self ) {
2019-12-31 23:22:44 +00:00
self . calculate_target_period ( ) ;
2019-12-31 18:57:02 +00:00
// When the frame counter sends a half-frame clock (at 120 or 96 Hz), two things happen.
2019-12-23 23:46:14 +00:00
// If the divider's counter is zero, the sweep is enabled, and the sweep unit is not muting the channel: The pulse's period is adjusted.
2020-01-01 21:28:28 +00:00
if self . sweep_counter = = 0 & & self . sweep_enabled & & ! ( self . timer_period < 8 | | self . target_period > 0x7FF ) {
2019-12-31 18:57:02 +00:00
self . timer_period = self . target_period ;
2019-12-23 23:46:14 +00:00
}
// If the divider's counter is zero or the reload flag is true: The counter is set to P and the reload flag is cleared. Otherwise, the counter is decremented.
if self . sweep_counter = = 0 | | self . sweep_reload {
2020-01-15 02:51:59 +00:00
self . sweep_counter = self . sweep_period ;
2019-12-23 23:46:14 +00:00
self . sweep_reload = false ;
2020-01-01 21:28:28 +00:00
if self . sweep_enabled { self . timer_period = self . target_period ; } // This fixes the DK walking sound. Why? Not reflected in documentation.
2019-12-23 23:46:14 +00:00
} else {
self . sweep_counter - = 1 ;
2019-12-22 04:14:47 +00:00
}
2019-12-31 23:22:44 +00:00
}
2019-12-31 18:57:02 +00:00
2020-01-30 06:00:12 +00:00
// Whenever the current period changes for any reason, whether by $400x writes or by sweep, the target period also changes.
2019-12-31 23:22:44 +00:00
pub fn calculate_target_period ( & mut self ) {
2019-12-31 18:57:02 +00:00
// The sweep unit continuously calculates each channel's target period in this way:
// A barrel shifter shifts the channel's 11-bit raw timer period right by the shift count, producing the change amount.
let change = self . timer_period > > self . shift_count ;
// If the negate flag is true, the change amount is made negative.
// The target period is the sum of the current period and the change amount.
if self . sweep_negate {
self . target_period = self . timer_period - change ;
// The two pulse channels have their adders' carry inputs wired differently,
// which produces different results when each channel's change amount is made negative:
2020-01-15 02:51:59 +00:00
// Pulse 1 adds the ones' complement (-c - 1). Making 20 negative produces a change amount of -21.
// Pulse 2 adds the two's complement (-c). Making 20 negative produces a change amount of -20.
2020-01-20 23:37:27 +00:00
if self . first_channel & & self . target_period > = 1 {
2019-12-31 18:57:02 +00:00
self . target_period - = 1 ;
}
} else {
self . target_period = self . timer_period + change ;
}
2019-12-22 00:02:32 +00:00
}
2019-12-17 05:43:10 +00:00
// $4000/$4004
2019-12-22 00:02:32 +00:00
pub fn write_duty ( & mut self , value : u8 ) {
2019-12-31 02:10:27 +00:00
// The duty cycle is changed (see table below), but the sequencer's current position isn't affected.
2019-12-18 02:38:15 +00:00
self . duty_cycle = DUTY_CYCLE_SEQUENCES [ ( value > > 6 ) as usize ] ;
2020-01-15 02:51:59 +00:00
self . envelope . length_counter_halt = value & ( 1 < < 5 ) ! = 0 ;
2019-12-17 04:06:00 +00:00
self . constant_volume_flag = value & ( 1 < < 4 ) ! = 0 ;
2020-01-15 02:51:59 +00:00
self . envelope . period = value as u16 & 0b1111 ;
2019-12-05 02:57:05 +00:00
}
2019-12-17 05:43:10 +00:00
// $4001/$4005
2019-12-22 00:02:32 +00:00
pub fn write_sweep ( & mut self , value : u8 ) {
self . sweep_enabled = value > > 7 = = 1 ;
2020-01-15 02:51:59 +00:00
self . sweep_period = ( ( value as u16 > > 4 ) & 0b111 ) + 1 ;
2019-12-22 00:02:32 +00:00
self . sweep_negate = value & 0b1000 ! = 0 ;
self . shift_count = value & 0b111 ;
2019-12-22 04:14:47 +00:00
self . sweep_reload = true ;
2019-12-05 02:57:05 +00:00
}
2019-12-17 05:43:10 +00:00
// $4002/$4006
2019-12-22 00:02:32 +00:00
pub fn write_timer_low ( & mut self , value : u8 ) {
2019-12-31 02:10:27 +00:00
self . timer_period & = 0b00000111_00000000 ; // mask off everything but high 3 bits of 11-bit timer
self . timer_period | = value as u16 ; // apply low 8 bits
2019-12-31 23:22:44 +00:00
self . calculate_target_period ( ) ;
2019-12-05 02:57:05 +00:00
}
2019-12-17 05:43:10 +00:00
// $4003/$4007
2019-12-22 00:02:32 +00:00
pub fn write_timer_high ( & mut self , value : u8 ) {
// LLLL.Lttt Pulse channel 1 length counter load and timer (write)
2019-12-31 23:22:44 +00:00
// TODO: thought the below meant that the length counter was only set if the channel was enabled, but apparently not as not having it fixes start game noise in DK.
// When the enabled bit is cleared (via $4015), the length counter is forced to 0 and cannot be changed until enabled is set again (the length counter's previous value is lost).
2019-12-23 23:46:14 +00:00
if self . enabled {
self . length_counter = super ::LENGTH_COUNTER_TABLE [ value as usize > > 3 ] ;
}
2019-12-22 00:02:32 +00:00
let timer_high = value as u16 & 0b0000_0111 ;
2019-12-31 02:10:27 +00:00
self . timer_period & = 0b11111000_11111111 ; // mask off high 3 bits of 11-bit timer
self . timer_period | = timer_high < < 8 ; // apply high timer bits in their place
2019-12-31 23:22:44 +00:00
self . calculate_target_period ( ) ;
2019-12-22 00:02:32 +00:00
// The sequencer is immediately restarted at the first value of the current sequence. The envelope is also restarted. The period divider is not reset.
self . duty_counter = 0 ;
2020-01-15 02:51:59 +00:00
self . envelope . start = true ;
2019-12-05 02:57:05 +00:00
}
2019-12-10 03:22:53 +00:00
}