Last Week:
APU Overview and Square 1 BasicsThis week: We will learn how to makes sounds with the Square 2 and Triangle channels.
Square 2Last
time we produced a beep on the Square 1 channel by making writes to
$4000-$4003. Now we'll learn how to do it with Square 2. This is very
easy because Square 1 and Square 2 are almost identical. We control
the Square 2 channel with ports $4004-$4007, and they more or less
mirror Square 1's $4000-4003.
SQ2_ENV ($4004)76543210||||||||||||++++- Volume|||+----- 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
SQ2_SWEEP ($4005)Skip this for now. This port, incidentally, is where Square 2 differs from Square 1.
SQ2_LO ($4006)76543210||||||||++++++++- Low 8-bits of period
SQ2_HI ($4007)76543210|||||||||||||+++- High 3-bits of period+++++---- Length CounterTo produce a sound, first we enable the channel via $4015:
lda #%00000010 ;enable Square 2 sta $4015 Then we write to the Square 2 ports:
lda #%00111000 ;Duty Cycle 00, Volume 8 (half volume) sta $4004 lda #$A9 ;$0A9 is an E in NTSC mode sta $4006 lda #$00 sta $4007 Except for sweeps, the Square 2 channel works just like the Square 1 channel.
TriangleThe
Triangle channel produces triangle waveforms which have a smooth sound
to them. Think of the flute-like melody in the Dragon Warrior overland
song. That's the Triangle.
Unlike the Square channels, we have
no control over the Triangle channel's volume or tone. It makes only
one type of sound and it's either on (playing) or off (silent). We
manipulate the Triangle channel via ports $4008-$400B.
TRI_CTRL ($4008)76543210|||||||||+++++++- Value+-------- Control Flag (0: use internal counters; 1: disable internal counters)The
triangle channel has two internal counters that can be used to
automatically control note duration. We are going to disable them so
that we can control note length manually. We will set the
Control Flag to 1 and forget about it.
When the internal counters are disabled,
Value
controls whether the channel is on or off. To silence the channel, set
Value to 0. To turn the channel on (ie, unsilence), set Value to any
non-zero value. Here are some examples:
lda #%10000000 ;silence the Triangle channel sta $4008 lda #%10000001 ;Triangle channel on sta $4008 lda #%10001111 ;Triangle channel on sta $4008 lda #%11111111 ;Triangle channel on sta $4008 Note that the last three examples are functionally the same. Any non-zero value in Value makes the Triangle channel play.
Unused Port$4009 is unused
Setting the Note$400A
and $400B control the period of the wave, or in other words what note
you hear (A, C#, G, etc). Like the Squares, Triangle periods are
11-bits long. $400A holds the low 8-bits and $400B holds the high
3-bits of the period. We'll learn more about periods next week, but
for now just know that changing the values written to these ports will
change the note that is played.
TRI_LO ($400A)76543210||||||||++++++++- Low 8-bits of periodTRI_HI ($400B)76543210|||||||||||||+++- High 3-bits of period+++++---- Length CounterThe
Length Counter, if enabled, controls how long the note is played. We
disabled it up in the $4008 section, so we can forget about it for now.
Here is some code to play an eternal beep on the Triangle channel:
lda #%00000100 ;enable Triangle channel sta $4015 lda #%10000001 ;disable counters, non-zero Value turns channel on sta $4008 lda #$42 ;a period of $042 plays a G# in NTSC mode. sta $400A lda #$00 sta $400B Multiple BeepsWe
now know how to use the Square 1, Square 2 and Triangle channels to
make sound. It doesn't take too much extra work to make them all play
at the same time. We just have to enable all three channels via $4015
and then write to the ports. Here's some code that will play a C#m
chord (C# E G#) using the knowledge we have gained up to now:
lda #%00000111 ;enable Sq1, Sq2 and Tri channels sta $4015 ;Square 1 lda #%00111000 ;Duty 00, Volume 8 (half volume) sta $4000 lda #$C9 ;$0C9 is a C# in NTSC mode sta $4002 ;low 8 bits of period lda #$00 sta $4003 ;high 3 bits of period ;Square 2 lda #%01110110 ;Duty 01, Volume 6 sta $4004 lda #$A9 ;$0A9 is an E in NTSC mode sta $4006 lda #$00 sta $4007 ;Triangle lda #%10000001 ;Triangle channel on sta $4008 lda #$42 ;$042 is a G# in NTSC mode sta $400A lda #$00 sta $400B Putting It All TogetherDownload
and unzip the
triad.zip sample files. All the code above is in the
triad.asm file. Make sure triad.asm and triad.bat are all in the same
folder as NESASM3, then double click triad.bat. That will run NESASM3
and should produce the triad.nes file. Run that NES file in FCEUXD SP
to listen to your C#m chord! Edit triad.asm to change the Volume and
Duty Cycle for the square waves. Try changing the Periods to produce
different notes.
Try to silence the various channels by either disabling them via $4015 or silencing them via $4000/$4004/$4008.
Finally try writing some code that will silence/unsilence the individual channels based on user input, like so:
A: toggle Square 1 channel on/off
B: toggle Square 2 channel on/off
Select: toggle Triangle channel on/off
Next Week:
Periods and Lookup Tables