This page is a mirror of Tepples' nesdev forum mirror (URL TBD).
Last updated on Oct-18-2019 Download

"APU basics": register initialization question

"APU basics": register initialization question
by on (#177864)
The code at http://wiki.nesdev.com/w/index.php/APU_ ... ialization had this:
Code:
init_apu:
        lda #$0F
        sta $4015
       
        ldy #0
@loop:  lda @regs,y
        sta $4000,y
        iny
        cpy #$18
        bne @loop
       
        rts
@regs:
        .byte $30,$08,$00,$00
        .byte $30,$08,$00,$00
        .byte $80,$00,$00,$00
        .byte $30,$00,$00,$00
        .byte $00,$00,$00,$00
        .byte $00,$0F,$00,$40


There was an ominous warning saying "Do not alter this code in any way". However, I checked everything and I could find no reason at all to initialize $4015 first or to count up (requiring a CPY instruction) instead of down.

So I disregarded the warning and changed it to this:
Code:
init_apu:
        ldy #$17
@loop:  lda @regs,y
        sta $4000,y
        dey
        bpl @loop
       
        rts
@regs:
        .byte $30,$08,$00,$00
        .byte $30,$08,$00,$00
        .byte $80,$00,$00,$00
        .byte $30,$00,$00,$00
        .byte $00,$00,$00,$00
        .byte $00,$0F,$00,$40


Is there any reason why my change might be wrong, or should we let it stand?
Re: "APU basics": register initialization question
by on (#177866)
I would argue that both the old and new code are incorrect, as they write to $4014 and initiate a pointless 512-cycle sprite DMA from zeropage in the middle of the sound initialization code.

The only registers that should be initialized are $4000-$4013, $4015, and $4017 (and of those, $4009 and $400D are pointless because no registers exist there).
Re: "APU basics": register initialization question
by on (#177867)
Quote:
There was an ominous warning saying "Do not alter this code in any way"

Don't worry, this is pure bullshit. You are of course free to alter this code in any way you like.

Personally I just initialize by writing $00, then $0f to $4015. I do not think anything else has to be done to initialize sound. (EDIT : Yes, you should also write either $40 or $c0 to $4017 in order to select your sweep clock model - in most cases games just pick one and never change ever again but you could do a sound engine which toggles between both rates if you really wanted to).

EDIT :
Quote:
I would argue that both the old and new code are incorrect, as they write to $4014 and initiate a pointless 512-cycle sprite DMA from zeropage in the middle of the sound initialization code.

Indeed, the very idea of doing this is absolutely awful. This just show once more how unreliable the wiki is. There is also bullshit like "must" being in bold, which was previously declared to be inappropriate for technically documenting something.

EDIT II : Man, the quality of this wiki page is incredibly low, it's barely belivable. There is better docs about APU elsewhere, most notably the main APU page on the wiki. http://wiki.nesdev.com/w/index.php/APU
I guess the page you linked to is some sort of accident and should be permanently removed - this is just my opinion though.
Re: "APU basics": register initialization question
by on (#177870)
Yikes. I forgot $4014 is where the OAMDMA register is, just like the programmers before me. I'll bet that write to $4015 was because some earlier version of the code skipped over $4014, then someone didn't realize (or forgot) this needed to be done and added $4015 and $4017 to the table.

OK, I fixed the code and made a note in the hopes nobody will try simplifying it back to the way it was. I also toned down the warning, since I agree that it was a little strong.

Here's how it looks now:
Code:
init_apu:
        ; Init $4000-4013
        ldy #$13
@loop:  lda @regs,y
        sta $4000,y
        dey
        bpl @loop
 
        ; We have to skip over $4014 (OAMDMA)
        lda #$0f
        sta $4015
        lda #$40
        sta $4017
   
        rts
@regs:
        .byte $30,$08,$00,$00
        .byte $30,$08,$00,$00
        .byte $80,$00,$00,$00
        .byte $30,$00,$00,$00
        .byte $00,$00,$00,$00
Re: "APU basics": register initialization question
by on (#177871)
Yet, there is no point in writing to $4000-$4013 registers. It doesn't harm like writing to $4014 does, but still, writing $00 to $4015 and $40/$c0 to $4017 is enough to stop all sound and put the APU in a known state. Writing to individual channel registers is not necessary before the first sound has to be emitted.
Re: "APU basics": register initialization question
by on (#177872)
Looking at the edit history of that page, the init code presented there was specifically designed for all of the other samples further down - some revisions had comments on the "init values" table to explain what they did and why, but those got removed for some reason.
Re: "APU basics": register initialization question
by on (#177873)
Bregalad wrote:
Yet, there is no point in writing to $4000-$4013 registers. It doesn't harm like writing to $4014 does, but still, writing $00 to $4015 and $40/$c0 to $4017 is enough to stop all sound and put the APU in a known state. Writing to individual channel registers is not necessary before the first sound has to be emitted.


Other than $4015 and $4017, I think it's important to initialize the two sweep registers ($4001, $4005), especially if your audio engine is going to otherwise ignore them and never write to them. Normally their state is guaranteed by power-on, but a game genie notably breaks this assumption (e.g. causing a problem for Mega Man 2).

All the other registers don't really need initialization, since they're pretty much mandatory to set up to play any particular sound, and simply clearing $4015 will halt them and reset their length counters to 0.

No harm in initializing the rest, though, and there's a utility in putting all the channels into a length-counter-disabled "always on" state before starting, if your music engine works that way.

Personally, I never used a loop for this, just a sequence of explicit stores. I guess the loop saves a few bytes; less now with the special case for skipping $4014.
Re: "APU basics": register initialization question
by on (#177891)
You need to set proper values to the sweep registers, otherwise you won't be able to play very low notes.
Re: "APU basics": register initialization question
by on (#177893)
Dwedit wrote:
You need to set proper values to the sweep registers, otherwise you won't be able to play very low notes.

Yes. A power-on sweep ($00) basically silences the squares any time you set to top bit of pitch, eliminating the entire bottom half of usable range. Mega Man 2 actually never uses that top bit, apparently. (This may be true of several Capcom games? Mega Man 3 does properly initialize sweep though, so they'd realized the problem by 1990.)

I'm kinda wondering if there are any NES games with "hidden" bass notes because of this? I didn't see any in MM1/MM2.
Re: "APU basics": register initialization question
by on (#177898)
The page was supposed to present a simplified model of the APU (no sweep, no envelopes, no length counters, ...) for beginning programmers. Those small details of "we have to skip this and that and can't write here" can add a lot of unnecessary cognitive load. I bet this is why the comments about the purpose of the initialization values were removed, and also why it doesn't care about triggering a most likely harmless OAM DMA.
Re: "APU basics": register initialization question
by on (#177903)
If the goal is illustration would ditching the loop help? Instead of a very "opaque" table of values, you'd get something you could lightly annotate:
Code:
init_apu:
   lda #$40
   sta $4017 ; disable APU IRQ
   lda #$00
   sta $4015 ; disable all channels
   lda #$30
   sta $4000 ; initialize squares
   sta $4004
   sta $400C ; initialize noise
   lda #$80
   sta $4008 ; initialize triangle
   lda #$08
   sta $4001 ; disable sweep
   sta $4005
   lda #$00
   sta $4002 ; set low pitch to 0
   sta $4006
   sta $400A
   sta $400E
   sta $4003 ; set high pitch to 0
   sta $4007
   sta $400B
   sta $400F
   sta $4010 ; initialize DMC
   sta $4011
   sta $4012
   sta $4013
   lda #$0F
   sta $4015 ; enable all channels
   rts

The loop saves a few bytes and/or lines of code, but for teaching purposes it makes everything that it's actually doing pretty obscure. You could skip the pitch setting in this example, too, if you wanted to make it shorter (initialized pitch isn't important to the examples, AFAIK).

I think the "don't mess with this" statement that follows the code is more vexing than the code itself, though:
Quote:
This writes values that are most useful for basic use of the channels. It's not important exactly what they do, just that they establish a known state. Be careful: if you alter this code, the APU might not behave in the simpler way described on this page.

I don't think telling someone that "it's not important" or not to alter this code is helpful; it kind of gives the impression that this is mysterious and understanding shouldn't be attempted, similar to how I felt about "the skinny about NES scrolling" being presented as this advanced and unapproachable knowledge. I think something like this would suffice:
Quote:
The initialization above will prepare the APU to a known state, ready to be used by the examples below.

Someone who doesn't think they understand that code wouldn't try to alter it anyway, you don't need to pre-load them with an expectation that it's hard to understand to prevent this. Just let them look it up and learn if they're interested.
Re: "APU basics": register initialization question
by on (#177913)
I dunno. I think a coder who is ready to really get into the nitty gritty like that should already be capable of working out what the code does as it is.
Re: "APU basics": register initialization question
by on (#177918)
Quote:
The page was supposed to present a simplified model of the APU (no sweep, no envelopes, no length counters, ...) for beginning programmers. Those small details of "we have to skip this and that and can't write here" can add a lot of unnecessary cognitive load. I bet this is why the comments about the purpose of the initialization values were removed, and also why it doesn't care about triggering a most likely harmless OAM DMA.

This page is useless and should be deleted. If the real APU page is not understandable, then it should definitely be fixed. But there's no need for a "simplified" model or whathever. Just describe the APU as it is.

I forgot about sweep registers, because my own sound engine just writes to them on every new note. If you don't use them, then yes, they should be initialized to any value between $08 and $0f.

Quote:
eliminating the entire bottom half of usable range

Actually it just eliminates the lowest octave. Many games doesn't use it and use a sweep value of $00, yet I agree this is a bad approach and add useless limitations to NES sound.
Re: "APU basics": register initialization question
by on (#177921)
Bregalad wrote:
Quote:
eliminating the entire bottom half of usable range

Actually it just eliminates the lowest octave. Many games doesn't use it and use a sweep value of $00, yet I agree this is a bad approach and add useless limitations to NES sound.

Yes, in linear pitch space, half the range = one octave. I suppose I made it sound slightly worse than it is. ;)

Bregalad wrote:
This page is useless and should be deleted. If the real APU page is not understandable, then it should definitely be fixed.

I think it's supposed to be a tutorial on how to use it. The APU reference is reference, not a tutorial. I can see the purpose of both. Whether it's a good tutorial or not, or appropriately titled, that's another set of problems.
Re: "APU basics": register initialization question
by on (#177988)
I'd missed this post earlier:

thefox wrote:
I bet this is why the comments about the purpose of the initialization values were removed, and also why it doesn't care about triggering a most likely harmless OAM DMA.

This is a dangerous attitude to have. For one thing, if you're going to put up a message saying not to modify the code at all, it had better be good code. It wasn't.

Second, you're making a lot of assumptions in calling the OAM DMA "most likely harmless". Sure, it's harmless if another DMA is done right afterward, but there's no reason to presume so. There might be some tutorial (either now or in the future) where DMA is done once during initialization and then ignored, which would make sense for a program without sprites. If that DMA is done before initializing sound, the poor user might waste hours trying to find out why he's getting garbage on the screen when he runs his program. And for what? To save two bytes in the sound initializer?


Bregalad wrote:
This page is useless and should be deleted. If the real APU page is not understandable, then it should definitely be fixed. But there's no need for a "simplified" model or whathever. Just describe the APU as it is.

WTF! Are you mistaking a beginner's tutorial for reference material or what?
Re: "APU basics": register initialization question
by on (#177991)
furrykef wrote:
There might be some tutorial (either now or in the future) where DMA is done once during initialization and then ignored, which would make sense for a program without sprites. If that DMA is done before initializing sound, the poor user might waste hours trying to find out why he's getting garbage on the screen when he runs his program. And for what? To save two bytes in the sound initializer?
When the PPU first powers up, OAM contains garbage in the first place. Garbage from a nonsense DMA isn't going to be meaningfully differently deleterious...

That said, I should write that OAM cold-boot DRAM test.
Re: "APU basics": register initialization question
by on (#177992)
furrykef wrote:
thefox wrote:
I bet this is why the comments about the purpose of the initialization values were removed, and also why it doesn't care about triggering a most likely harmless OAM DMA.

This is a dangerous attitude to have. For one thing, if you're going to put up a message saying not to modify the code at all, it had better be good code. It wasn't.

Second, you're making a lot of assumptions in calling the OAM DMA "most likely harmless". Sure, it's harmless if another DMA is done right afterward, but there's no reason to presume so. There might be some tutorial (either now or in the future) where DMA is done once during initialization and then ignored, which would make sense for a program without sprites. If that DMA is done before initializing sound, the poor user might waste hours trying to find out why he's getting garbage on the screen when he runs his program. And for what? To save two bytes in the sound initializer?

I'm not really arguing whether it's good or bad, just trying to provide my guess at why those decisions were made, since many people in the thread seemed to be misunderstanding the purpose of that page. (You're going to have to ask blargg if you want the real reasons, he created the page.)

BTW, doing OAM DMA once in initialization is bad practice. It's very easy to accidentally run too much code after the DMA before enabling rendering, causing it to decay and to not have a well-defined state after all. If you're not using sprites, you can turn off their rendering in $2001 and not care about the state of OAM.
Re: "APU basics": register initialization question
by on (#177993)
thefox wrote:
I'm not really arguing whether it's good or bad

IMO the overall premise of this page is good. A fairly minimal set of steps showing how to make the APU play a sound on 4 of its channels. It's a nice little tutorial, similar to the first few programs I ever wrote to teach myself how to use the APU.

I have a laundry list of little nitpicks with it, which I left on the wiki (sorry to fragment the discussion), but I think in general the page is worth keeping and maintaining.
Re: "APU basics": register initialization question
by on (#178331)
(Move to "NES Music", please. :) )
Re: "APU basics": register initialization question
by on (#178333)
There's no music in this thread. The questions are about programming.

Is it policy that anything related to sound program belongs in "NES Music" now? I don't think it's been this way in the past... most APU programming stuff seems to be under NESdev.
Re: "APU basics": register initialization question
by on (#178335)
rainwarrior wrote:
There's no music in this thread. The questions are about programming.

Is it policy that anything related to sound program belongs in "NES Music" now? I don't think it's been this way in the past... most APU programming stuff seems to be under NESdev.

"Discuss NSF files, FamiTracker, MML tools, or anything else related to NES music."
Re: "APU basics": register initialization question
by on (#178351)
I think most people would assume that means "anything else related to creating NES music", not how to correctly poke some registers in an assembly program.
Re: "APU basics": register initialization question
by on (#178368)
Pretty sure poking around at sound registers to make output is creating music/art; regardless of standard convention. :)
Re: "APU basics": register initialization question
by on (#178449)
I did think of a reason to write to the regs in ascending order instead of descending: I forgot that the registers $4003, $4007, etc. can act as triggers, so you might not want to write to them until after writing to the other registers for that channel. Dunno if that affects how this particular code works or not.
Re: "APU basics": register initialization question
by on (#178450)
B00daW wrote:
(Move to "NES Music", please. :) )

Please don't. The discussion here has nothing to do with music.
Re: "APU basics": register initialization question
by on (#178451)
furrykef wrote:
I did think of a reason to write to the regs in ascending order instead of descending: I forgot that the registers $4003, $4007, etc. can act as triggers, so you might not want to write to them until after writing to the other registers for that channel. Dunno if that affects how this particular code works or not.

It doesn't really matter for the example, which doesn't use the triggered features, but I agree (I made the same suggestion on the wiki talk page). Those registers are canonically the "start a note" register, by design.
Re: "APU basics": register initialization question
by on (#178478)
Bregalad wrote:
B00daW wrote:
(Move to "NES Music", please. :) )

Please don't. The discussion here has nothing to do with music.

OK. Well, you used the word "nothing". Let's either reset 2a0x code without init code or play music code without clear commands during a reset.

How much of "nothing" is involved with initialization and clearing bits of APU code?

Secondary question, if PPU and/or APU code related to their respective "territories" is an instigated topic within "NESdev" of the forums, when is it not appropriate to move to respective forums of the questioned topic?
Re: "APU basics": register initialization question
by on (#178480)
B00daW wrote:
Let's either reset 2a0x code without init code or play music code without clear commands during a reset.

Lots of people make NES music without writing any code at all (and those people don't post in the Dev forum).

Programming stuff mostly go in the Dev forum. The resulting art and music mostly goes in the Graphics and Music forums.

There's a ton of PPU programming threads in the Dev forum, hardly anybody posts that kind of thing in the Graphics forum. The more technical Graphics forum posts are usually more like "what is possible on the NES", not "here's how reads from $2007 are buffered".

The Music forums are mostly music people have made, album releases, NSF rips, tools to make music, etc. Hardly anybody posts there about programming the APU.

People post against the trend in either forum now and then, but this isn't one of those. I agree that APU programming is related to music, but it's still more appropriate for the Dev forums. Ideally instead of separate forums I wish we'd just have category tags, so it could exist happily with both tags, but that just isn't the world we live in.
Re: "APU basics": register initialization question
by on (#178753)
Based on a quick look at that page I'm extremely happy that when I started out I only ever used the APU reference to do what I wanted... part of the truth was that I had already familiarized myself with most of the APU features through Famitracker so it all made sense to me.