APU behavior

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
APU behavior
by on (#101787)
Gah... I'm pulling my hair out trying to find bugs in my audio emulation.

When a channel's correspond bit is written to as zero on register $4015, the channel length counter and audio is disabled - right? Bit 1 -> 0, set length counter to 0 and disable channel audio output and even handling. What about from 0 -> 1, re-enabling it? Is anything re-enabled or reloaded?

For audio channels with envelope support, if LC = 00b and then the 4th reg of that corresponding channel is written to, then envelope volume is reset to 15 and the decay counter is reloaded as well - correct? What about writes to any other registers of such channel? As in; writing to the first register of the channel immediately resets the envelope decay counter(and volume back to max) and length counter reloaded (assuming LC=00b).

Between the nesdev wiki and the apu_ref.txt doc, I'm not sure I got all the behaviors down correctly :/

Edit:

Ok, one specific example: Megaman 2 Metalman stage. The noise channel gets written to every frame. $00 -> $400c, $0f -> $4015, and $03 -> $400e. But the noise channel doesn't appear to restart the volume/audio at all per frame. About every 5 frame, $07 is written to $4015 (and then on the next frame back to $0f. So disables the channel for a frame then re-enables it next frame and waits a series of frames before doing it again). And the length counter is set to $FE ticks (LLLL L--- is $08).
Re: APU behavior
by on (#101790)
As I understand it:

1. Clearing the enable bit in $4015 immediately sets the length counter to 0. Nothing gets reset or reloaded when you set the enable bit again (length counter will remain at 0). The channel will be silent for as long as the length counter remains at 0.

2. Writing the fourth byte of the channel will reload the length counter and tell the envelope to reset when clocked next by the frame counter.

3. Writing to the other three bytes do not have any immediate side effects on the counters. Writing to the first byte does not reset the envelope or length counter in any way.


I try to keep everything I know up to date here on the wiki: http://wiki.nesdev.com/w/index.php/APU

This may or may not be a better reference than the old .txt files at the moment, but if you find any place it isn't, let me know and I can update it.
Re: APU behavior
by on (#101792)
What about the C (contant volume) flag? If the channel was setup with an envelope setting and starting playing the waveform, then the code changes the flag on that specific channel's register and writes in a constant volume value - does this take effect immediately? Or for that matter, if the channel was already in Constant volume mode - does the fourth reg always need to be written to for the change in reg 1's new volume value to take effect?
Re: APU behavior
by on (#101802)
I try to describe any internal state beyond the last value written to the I /O register. The only volume state is the last value written to the first register, the envelope counter, and the start flag for the envelope. Thus, switching to constant volume takes effect immediately; to behave any other way would require more internal state. I take this approach because it's more concise than describing all of the possible situations and behaviors in them, and doing the latter is more likely to result in inconsistencies.
Quote:
The envelope unit's volume output depends on the constant volume flag: if set, the envelope parameter directly sets the volume, otherwise the counter's value is the current volume.

Maybe slightly clearer if it says that the envelope parameter is the volume.
Re: APU behavior
by on (#101901)
Thanks :)

Does this logic look correct?

The frame sequencer decrements the length counter. If the length counter halt flag is clear and the counter is zero, and an internal state is changed causing the waveform counter is no longer to be clocked for this channel (similarly, $4015 also sets this internal counter for the halt action as well). Else if the length counter halt flag is set, the counter is simply reloaded with the last value written to the corresponding reg (fourth reg IIRC) - giving it infinite "length". At any point that the internal waveform counter is disabled (by an expired length counter or from $4015), volume or envelope or period updates do nothing as they don't reset the internal state of the waveform output counter. Well, they appear to do nothing as nothing would be heard in the change. And so writing to the fourth reg (or whatever reg holds the length counter value) resets the internal state of the waveform pointer - assuming the corresponding bit in $4015 is set (else the waveform counter remains in the current state of suspension). Halting a channel via $4015 takes place immediately, but enabling the channel through $4015 does not (again, needs fourth reg written too).

I don't know if L and C flags should be copied to an internal register during the write of the fourth reg, but I have it as of current that the frame sequencer checks the actual reg and not an internal copy. I.e. If the channel is originally setup with a length counter value (and length counting enabled), but then program/6502 code changes the L flag to disable it (before length counter expires in the sequencer) - that the changes take effect and the channel no longer has a length counter/expire configuration.

As far as the triangle channel, the linear counter and length counter both run at the same time (according to NESDEV wiki) and which ever expires first halts the channel (assuming length counter flag is cleared).

Also, should this be moved to the emu section?
Re: APU behavior
by on (#101903)
Early on I learned that the APU isn't like software, where you only do necessary things; it's a bunch of parallel units, so you try to leave as much running as possible, reducing extra logic to unnecessarily disable parts. Disclaimer: I haven't done any NES stuff in a couple of years now.
Quote:
The frame sequencer decrements the length counter. If the length counter halt flag is clear and the counter is zero, and an internal state is changed causing the waveform counter is no longer to be clocked for this channel (similarly, $4015 also sets this internal counter for the halt action as well). Else if the length counter halt flag is set, the counter is simply reloaded with the last value written to the corresponding reg (fourth reg IIRC) - giving it infinite "length".

In this case, there's a chain of things that can force the output to the mixer (DAC) to 0. If none of those are forcing it to 0, it outputs the current volume from the envelope. So the length counter being zero merely forces the output to the DAC to 0, and doesn't mess with the sequencer etc. whose timer keeps running. When the halt flag is set, the Wiki says that the counter isn't decremented (rather than decrementing and wrapping around), so that it could be cleared and resume counting down.

Or are you attempting to get reasonably close in a way that's easy to implement?
Re: APU behavior
by on (#102002)
Heh, yeah that makes sense. I didn't think of the hardware in parallel logic.

Quote:
Or are you attempting to get reasonably close in a way that's easy to implement?


Pretty much. But I did want to fully understand how everything works first. I'm in the process of re-writing the APU emulation core because it's currently in really bad shape.

You know, something just dawned on me today; I could just write NES test roms to observe the behavior and get all the answers I need. So with that in mind, what's a good emulator that has accurate APU emulation?
Re: APU behavior
by on (#102007)
tomaitheous wrote:
You know, something just dawned on me today; I could just write NES test roms to observe the behavior and get all the answers I need. So with that in mind, what's a good emulator that has accurate APU emulation?

If you want to run test ROMs for any aspect of NES behavior other than power-up state or mappers, the best emulator is probably a PowerPak in an NES.