APU Pulse: Difference between revisions

From NESdev Wiki
Jump to navigationJump to search
(Constant volume flag was missing, making it look like the volume/envelope field is 5 bits long)
 
(22 intermediate revisions by 6 users not shown)
Line 2: Line 2:
Each of the two [[APU|NES APU]] pulse (square) wave channels generate a pulse wave with variable duty.
Each of the two [[APU|NES APU]] pulse (square) wave channels generate a pulse wave with variable duty.


Each pulse channel contains the following: [[APU Envelope|envelope generator]], [[APU Sweep|sweep unit]], [[APU Misc|timer]], 8-step [[APU Misc|sequencer]], [[APU Length Counter|length counter]].
Each pulse channel contains the following:  
 
*[[APU Envelope|envelope generator]]
*[[APU Sweep|sweep unit]]
*[[APU Misc|timer]]
*8-step [[APU Misc|sequencer]]
*[[APU Length Counter|length counter]]


<pre>
<pre>
Line 15: Line 21:
     Envelope -------> Gate -----> Gate -------> Gate --->(to mixer)
     Envelope -------> Gate -----> Gate -------> Gate --->(to mixer)
</pre>
</pre>
== Registers ==
'''Note''': the addresses below are ''write-only!'' Reading from these addresses exhibits [[Open bus behavior|open-bus behavior]].


{| class="tabular"
{| class="tabular"
! Address || Bitfield || Description
! Address || Bitfield || Description
|-
|-
| '''$4000''' || <tt>DDlc.eeee</tt> || '''Pulse 1''' duty, [[APU Length Counter|length counter halt]], constant volume/envelope flag, and volume/[[APU Envelope|envelope]] divider period (write)
| '''$4000''' || <tt>DDlc.vvvv</tt> || '''Pulse 1''' '''D'''uty cycle, [[APU Length Counter|length counter halt]], '''c'''onstant volume/[[APU Envelope|envelope]] flag, and '''v'''olume/envelope divider period
|-
|-
| '''$4004''' || <tt>DDlc.eeee</tt> || '''Pulse 2''' duty, [[APU Length Counter|length counter halt]], constant volume/envelope flag, and volume/[[APU Envelope|envelope]] divider period (write)
| '''$4004''' || <tt>DDlc.vvvv</tt> || '''Pulse 2''' '''D'''uty cycle, [[APU Length Counter|length counter halt]], '''c'''onstant volume/[[APU Envelope|envelope]] flag, and '''v'''olume/envelope divider period
|-
|-
| bits 7-6 || <tt>DD-- ----</tt> || The duty cycle is set (see table below). The sequencer's current position is not changed.
|colspan=2| Side effects || The duty cycle is changed (see table below), but the sequencer's current position isn't affected.
|-
|-
|colspan=3| &nbsp;
|colspan=3| &nbsp;
|-
|-
| '''$4002''' || <tt>LLLL.LLLL</tt> || '''Pulse 1''' timer low (write)
| '''$4001''' || <tt>EPPP.NSSS</tt> || See [[APU Sweep]]
|-
|-
| '''$4006''' || <tt>LLLL.LLLL</tt> || '''Pulse 2''' timer low (write)
| '''$4005''' || <tt>EPPP.NSSS</tt> || See [[APU Sweep]]
|-
|-
| bits 7-0 || <tt>LLLL LLLL</tt> || Timer low 8 bits
|colspan=3| &nbsp;
|-
| '''$4002''' || <tt>LLLL.LLLL</tt> || '''Pulse 1''' timer '''L'''ow 8 bits
|-
| '''$4006''' || <tt>LLLL.LLLL</tt> || '''Pulse 2''' timer '''L'''ow 8 bits
|-
|-
|colspan=3| &nbsp;
|colspan=3| &nbsp;
|-
|-
| '''$4003''' || <tt>llll.lHHH</tt> || '''Pulse 1''' [[APU Length Counter|length counter load]] and timer high (write)
| '''$4003''' || <tt>llll.lHHH</tt> || '''Pulse 1''' [[APU Length Counter|length counter load]] and timer '''H'''igh 3 bits
|-
| '''$4007''' || <tt>llll.lHHH</tt> || '''Pulse 2''' [[APU Length Counter|length counter load]] and timer high (write)
|-
|-
| bits 2-0 || <tt>---- -HHH</tt> || Timer high 3 bits
| '''$4007''' || <tt>llll.lHHH</tt> || '''Pulse 2''' [[APU Length Counter|length counter load]] and timer '''H'''igh 3 bits
|-
|-
|colspan=2| Side effects || The sequencer is restarted at the first value of the current sequence. The [[APU Envelope|envelope]] is also restarted.
|colspan=2| Side effects || The sequencer is immediately restarted at the first value of the current sequence. The [[APU Envelope|envelope]] is also restarted. The period divider is ''not'' reset.[//forums.nesdev.org/viewtopic.php?p=186129#p186129]
|}
|}
== Sequencer behavior ==
The sequencer is clocked by an 11-bit [[APU Misc|timer]]. Given the timer value ''t = HHHLLLLLLLL'' formed by timer high and timer low,
this timer is updated every APU cycle (i.e., every second CPU cycle), and counts ''t, t-1, ..., 0, t, t-1, ...'', clocking the waveform
generator when it goes from 0 to t. Since the period of the timer is ''t+1'' APU cycles and the sequencer has 8 steps, the period of the waveform is
''8*(t+1)'' APU cycles, or equivalently ''16*(t+1)'' CPU cycles.
Hence
* f<sub>pulse</sub> = f<sub>CPU</sub>/(16*(t+1)) (where f<sub>CPU</sub> is 1.789773 MHz for NTSC, 1.662607 MHz for PAL, and 1.773448 MHz for Dendy)
* t = f<sub>CPU</sub>/(16*f<sub>pulse</sub>) - 1
'''Note:''' A period of ''t < 8'', either set explicitly or via a sweep period update, '''silences the corresponding pulse channel'''. The highest frequency a pulse channel can output is hence about 12.4 kHz for NTSC. ('''TODO:''' PAL behavior?)


'''Duty Cycle Sequences'''
'''Duty Cycle Sequences'''
{|
{| class="wikitable"
! Duty || Waveform sequence
! Duty || Output waveform
|-
|-
| 0 || <tt>0 1 0 0 0 0 0 0</tt> (12.5%)
| 0 || <samp>0 1 0 0 0 0 0 0</samp> (12.5%)
|-
|-
| 1 || <tt>0 1 1 0 0 0 0 0</tt> (25%)
| 1 || <samp>0 1 1 0 0 0 0 0</samp> (25%)
|-
|-
| 2 || <tt>0 1 1 1 1 0 0 0</tt> (50%)
| 2 || <samp>0 1 1 1 1 0 0 0</samp> (50%)
|-
|-
| 3 || <tt>1 0 0 1 1 1 1 1</tt> (25% negated)
| 3 || <samp>1 0 0 1 1 1 1 1</samp> (25% negated)
|}
|}
:The reason for these odd sequences is that the sequence counter is initialized to zero but counts ''downward'' rather than upward


The sequencer is clocked by an 11-bit [[APU Misc|timer]]. Given the timer value ''t = HHHLLLLLLLL'' formed by timer high and timer low,
Notice that a few Famiclone units have swapped APU duty cycles, as 12.5 [0], 50 [1], 25 [2] and 25 negated [3] instead.
this timer is updated every APU cycle (i.e., every second CPU cycle), and counts ''t, t-1, ..., 0, t, t-1, ...'', clocking the waveform
 
generator when it reaches 0. Since the period of the timer is ''t+1'' APU cycles and the sequencer has 8 steps, the period of the waveform is
 
''8*(t+1)'' APU cycles, or equivalently ''16*(t+1)'' CPU cycles.
''' Implementation details '''


Hence
The reason for the odd output from the sequencer is that the counter is initialized to zero but counts ''downward'' rather than upward. Thus it reads the sequence lookup table in the order 0, 7, 6, 5, 4, 3, 2, 1.


* f<sub>pulse</sub> = f<sub>CPU</sub>/(16*(t+1)) (where f<sub>CPU</sub> is 1.78977267 MHz for NTSC, 1.662607 MHz for PAL, and 1.773448 MHz for Dendy)
{| class="wikitable"
* t = f<sub>CPU</sub>/(16*f<sub>pulse</sub>) - 1
! Duty || Sequence lookup table || Output waveform
|-
| 0 || <code>0 0 0 0 0 0 0 1</code> || <samp>0 1 0 0 0 0 0 0</samp> (12.5%)
|-
| 1 || <code>0 0 0 0 0 0 1 1</code> || <samp>0 1 1 0 0 0 0 0</samp> (25%)
|-
| 2 || <code>0 0 0 0 1 1 1 1</code> || <samp>0 1 1 1 1 0 0 0</samp> (50%)
|-
| 3 || <code>1 1 1 1 1 1 0 0</code> || <samp>1 0 0 1 1 1 1 1</samp> (25% negated)
|}


A period of ''t < 8'', either set explicitly or via a sweep period update, silences the corresponding pulse channel. The highest frequency a pulse channel can output is hence about 12.4 kHz for NTSC. ('''TODO:''' PAL behavior?)
== Pulse channel output to mixer ==


The [[APU Mixer|mixer]] receives the current [[APU Envelope|envelope volume]] except when
The [[APU Mixer|mixer]] receives the pulse channel's current [[APU Envelope|envelope volume]] (lower 4 bits from $4000 or $4004) except when
* The sequencer output is zero, or
* The sequencer output is zero, or
* The [[APU Sweep|sweep]] unit is silencing the channel, or
* overflow from the [[APU Sweep|sweep]] unit's adder is silencing the channel, or
* The [[APU Length Counter|length counter]] is zero
* the [[APU Length Counter|length counter]] is zero, or
* the timer has a value less than eight (''t<8'', noted above).
 
If any of the above are true, then the pulse channel sends zero (silence) to the mixer.
 
== Pulse channel 1 vs Pulse channel 2 behavior ==


The behavior of the two pulse channels differs only in the effect of the negate mode of their [[APU Sweep|sweep units]].
The behavior of the two pulse channels differs only in the effect of the negate mode of their [[APU Sweep|sweep units]].
== See Also ==
* [[Pulse Channel frequency chart]]

Latest revision as of 04:08, 6 October 2022

Each of the two NES APU pulse (square) wave channels generate a pulse wave with variable duty.

Each pulse channel contains the following:

                     Sweep -----> Timer
                       |            |
                       |            |
                       |            v 
                       |        Sequencer   Length Counter
                       |            |             |
                       |            |             |
                       v            v             v
    Envelope -------> Gate -----> Gate -------> Gate --->(to mixer)

Registers

Note: the addresses below are write-only! Reading from these addresses exhibits open-bus behavior.

Address Bitfield Description
$4000 DDlc.vvvv Pulse 1 Duty cycle, length counter halt, constant volume/envelope flag, and volume/envelope divider period
$4004 DDlc.vvvv Pulse 2 Duty cycle, length counter halt, constant volume/envelope flag, and volume/envelope divider period
Side effects The duty cycle is changed (see table below), but the sequencer's current position isn't affected.
 
$4001 EPPP.NSSS See APU Sweep
$4005 EPPP.NSSS See APU Sweep
 
$4002 LLLL.LLLL Pulse 1 timer Low 8 bits
$4006 LLLL.LLLL Pulse 2 timer Low 8 bits
 
$4003 llll.lHHH Pulse 1 length counter load and timer High 3 bits
$4007 llll.lHHH Pulse 2 length counter load and timer High 3 bits
Side effects The sequencer is immediately restarted at the first value of the current sequence. The envelope is also restarted. The period divider is not reset.[1]

Sequencer behavior

The sequencer is clocked by an 11-bit timer. Given the timer value t = HHHLLLLLLLL formed by timer high and timer low, this timer is updated every APU cycle (i.e., every second CPU cycle), and counts t, t-1, ..., 0, t, t-1, ..., clocking the waveform generator when it goes from 0 to t. Since the period of the timer is t+1 APU cycles and the sequencer has 8 steps, the period of the waveform is 8*(t+1) APU cycles, or equivalently 16*(t+1) CPU cycles.

Hence

  • fpulse = fCPU/(16*(t+1)) (where fCPU is 1.789773 MHz for NTSC, 1.662607 MHz for PAL, and 1.773448 MHz for Dendy)
  • t = fCPU/(16*fpulse) - 1


Note: A period of t < 8, either set explicitly or via a sweep period update, silences the corresponding pulse channel. The highest frequency a pulse channel can output is hence about 12.4 kHz for NTSC. (TODO: PAL behavior?)


Duty Cycle Sequences

Duty Output waveform
0 0 1 0 0 0 0 0 0 (12.5%)
1 0 1 1 0 0 0 0 0 (25%)
2 0 1 1 1 1 0 0 0 (50%)
3 1 0 0 1 1 1 1 1 (25% negated)

Notice that a few Famiclone units have swapped APU duty cycles, as 12.5 [0], 50 [1], 25 [2] and 25 negated [3] instead.


Implementation details

The reason for the odd output from the sequencer is that the counter is initialized to zero but counts downward rather than upward. Thus it reads the sequence lookup table in the order 0, 7, 6, 5, 4, 3, 2, 1.

Duty Sequence lookup table Output waveform
0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 (12.5%)
1 0 0 0 0 0 0 1 1 0 1 1 0 0 0 0 0 (25%)
2 0 0 0 0 1 1 1 1 0 1 1 1 1 0 0 0 (50%)
3 1 1 1 1 1 1 0 0 1 0 0 1 1 1 1 1 (25% negated)

Pulse channel output to mixer

The mixer receives the pulse channel's current envelope volume (lower 4 bits from $4000 or $4004) except when

  • The sequencer output is zero, or
  • overflow from the sweep unit's adder is silencing the channel, or
  • the length counter is zero, or
  • the timer has a value less than eight (t<8, noted above).

If any of the above are true, then the pulse channel sends zero (silence) to the mixer.

Pulse channel 1 vs Pulse channel 2 behavior

The behavior of the two pulse channels differs only in the effect of the negate mode of their sweep units.

See Also