Music and sound effects on the NES are generated by the APU
(Audio Processing Unit), the sound chip inside the CPU. The CPU "talks" to the APU through a series of I/O ports, much like it does with the PPU and joypads.
APU: $4000-$4015, $4017
The APU has 5 channels: Square 1, Square 2, Triangle, Noise and DMC. The first four play waves and are used in just about every game. The DMC channel plays samples (pre-recorded sounds) and is used less often.
The square channels produce square waveforms. A square wave is named for its shape. It looks like this:
As you can see the wave transitions instantaneously from its high point to its low point (where the lines are vertical). This gives it a hollow sound like a woodwind or an electric guitar.
The triangle channel produces triangle waveforms. A triangle wave is also named for its shape. It looks like this:
The sound of a triangle wave is smoother and less harsh than a square wave. On the NES, the triangle channel is often used for bass lines (in low octaves) or a flute (in high octaves). It can also be used for drums.
The noise channel has a random generator, which makes the waves it produces sound like.. noise. This channel is generally used for percussion and explosion sounds.
The DMC channel plays samples, which are pre-recorded sounds. It is often used to play voice recordings ("Blades of Steel") and percussion samples. Samples take up a lot of ROM space, so not many games make use of the DMC channel.
Before you can use the channels to produce sounds, you need to enable them. Channels are toggled on and off via port $4015:
||||+- Square 1 (0: disable; 1: enable)
|||+-- Square 2
Here are some code examples using $4015 to enable and disable channels:
sta $4015 ;enable Square 1 channel, disable others
sta $4015 ;enable Square 2, Triangle and DMC channels. Disable Square 1 and Noise.
sta $4015 ;disable all channels
sta $4015 ;enable Square 1, Square 2, Triangle and Noise channels. Disable DMC.
;this is the most common usage.
Try opening up some of your favorite games in FCEUXD SP and set a breakpoint on writes to $4015. Take a look at what values are getting written there. If you don't know how to do this, follow these steps:
1. Open FCEUXD SP
2. Load a ROM
3. Open up the Debugger by pressing F1 or going to Tools->Debugger
4. In the top right corner of the debugger, under "BreakPoints", click the "Add..." button
5. Type "4015" in the first box after "Address:"
6. Check the checkbox next to "Write"
7. Set "Memory" to "CPU Mem"
8. Leave "Condition" and "Name" blank and click "OK"
Now FCEUX will pause emulation and snap the debugger anytime your game makes a write (usually via STA) to $4015. The debugger will tell you the contents of the registers at that moment, so you can check what value will be written to $4015. Some games will write to $4015 every frame, and some only do so once at startup. Try resetting the game if your debugger isn't snapping.
What values are being written to $4015? Can you tell what channels your game is using?
Square 1 Channel
Let's make a beep. This week we'll learn how to produce a sound on the Square 1 channel. The Square channels are everybody's favorites because you can control the volume and tone and perform sweeps on them. You can produce a lot of interesting effects using the Squares.
Square 1 is controlled via ports $4000-$4003. The first port, $4000, controls the duty cycle (ie, tone) and volume for the channel. It looks like this:
|||+----- Saw Envelope Disable (0: use internal counter for volume; 1: use Volume for volume)
||+------ Length Counter Disable (0: use Length Counter; 1: disable Length Counter)
++------- Duty Cycle
For our purposes, we will focus on Volume and Duty Cycle. We will set Saw Envelope Disable and Length Counter Disable to 1 and then forget about them. If we leave Saw Envelopes on, the volume of the channel will be controlled by an internal counter. If we turn them off, WE have control of the volume. If WE have control, we can code our own envelopes (much more versatile). Same thing with the Length Counter. If we disable it, we have more control over note lengths. If that didn't make sense, don't worry. It will become clearer later. For now we're just going to disable and forget about them.
controls the channel's volume. It's 4 bits long so it can have a value from 0-F. A volume of 0 silences the channel. 1 is very quiet and F is loud.
controls the tone of the Square channel. It's 2 bits long, so there are four possible values:
00 = a weak, grainy tone. Think of the engine sounds in RC Pro-Am. (12.5% Duty)
01 = a solid mid-strength tone. (25% Duty)
10 = a strong, full tone, like a clarinet or a lead guitar (50% Duty)
11 = sounds a lot like 01 (25% Duty negated)
The best way to know the difference in sound is to listen yourself. I recommend downloading FamiTracker
and playing with the different Duty settings in the Instrument Editor.
For those interested, Duty Cycle actually refers to the percentage of time that the wave is in "up" position vs. "down" position. Here are some pictures:
Don't sweat it if graphs and waves aren't your thing. Use your ears instead.
Here's a code snippet that sets the Duty and Volume for the Square 1 channel:
lda #%10111111; Duty 10 (50%), volume F (max!)
$4001 controls sweeps for Square 1. We'll skip them for now.
Setting the Note
$4002 and $4003 control the period of the wave, or in other words what note you hear (A, C#, G, etc). Periods are 11-bits long. $4002 holds the low 8-bits and $4003 holds the high 3-bits of the period. We'll get into more detail in a future tutorial, but for now just know that changing the values written to these ports will change the note that is played.
++++++++- Low 8-bits of period
|||||+++- High 3-bits of period
+++++---- Length Counter
The Length Counter, if enabled, controls how long the note is played. We disabled it up in the $4000 section, so we can forget about it for now.
Here is some code that will produce an eternal beep on the Square 1 channel:
sta $4015 ;enable square 1
lda #%10111111 ;Duty 10, Volume F
lda #$C9 ;0C9 is a C# in NTSC mode
Putting It All Together
Download and unzip the square1.zip
sample files. All the code above is in the square1.asm file. Make sure square1.asm and square1.bat are all in the same folder as NESASM3, then double click square1.bat. That will run NESASM3 and should produce the square1.nes file. Run that NES file in FCEUXD SP to listen to your beep! Edit square1.asm to change the Volume (0 to F), or to change the Duty Cycle for the square wave. Try changing the period to produce different notes.
: Square 2 and Triangle. Multiple beeps!