Disch wrote:
My previous method is I have a flag which indicates if the sweep unit is silencing the channel (1 if channel is silenced by sweep unit, 0 otherwise). I refreshed the value by checking the conditions on every $4001/2/3 write...as well as on sweep unit clocks.
That seems fine. In the code below it would be equivalent to caching the value of is_silenced() and updating it at the end of clock_sweep() and write_4001(). In the recording of the NES there
are slight clicks before the noise burst at the middle. My guess is that you aren't silencing until the period is set above 0x7ff, thus this doesn't happen until the next sweep clock; the correct behavior is to silence the channel if the shifter/adder's output is above 0x7ff, even before the sweep clock has occurred (see code below).
Quote:
The tweak I added makes it so the flag can only go from 1->0 on Sweep Unit clocks... meaning if the sweep unit is silenceing the channel... it will always be silent until at least the next sweep unit clock.
Definitely incorrect, since silencing has no relation to clocking. The only thing clocking does is drive the actual sweeping (adjusting the period in third and fourth registers).
Here's the code I use for sweep handling (which matches what is described in the APU reference). Note the comment in clock_sweep() about the different behavior of the second square channel.
Code:
int square_period; // value in $4002/$4003
int sweep_period = 1; // must never be zero
int sweep_delay = 1; // must never be zero
int sweep_shift;
bool sweep_enabled;
bool sweep_reload;
bool sweep_negate;
void clock_sweep()
{
if ( --sweep_delay == 0 )
{
sweep_delay = sweep_period;
if ( sweep_enabled && square_period >= 8 )
{
int offset = square_period >> sweep_shift;
if ( sweep_negate ) {
// no + 1 for second square channel
square_period -= offset + 1;
}
else if ( square_period + offset < 0x800 ) {
square_period += offset;
}
}
}
if ( sweep_reload ) {
sweep_reload = false;
sweep_delay = sweep_period;
}
}
bool is_silenced()
{
int offset = square_period >> sweep_shift;
return square_period < 8 ||
(!sweep_negate && square_period + offset >= 0x800);
}
void write_4001( int n )
{
sweep_negate = (n >> 3) & 1;
sweep_shift = n & 7;
sweep_period = ((n >> 4) & 7) + 1;
sweep_enabled = (n & 0x80) && sweep_shift;
sweep_reload = true;
}