FDS audio: Difference between revisions

From NESdev Wiki
Jump to navigationJump to search
m (expansion audio category)
(Update sections on modulation/wavetable functions, volume gain is confirmed to be PWM)
(40 intermediate revisions by 4 users not shown)
Line 2: Line 2:
The '''Famicom Disk System audio''' is an audio channel generated by the 2C33 chip on the [[Famicom Disk System]]'s RAM card and output through the Famicom cart edge connector's expansion audio pins.
The '''Famicom Disk System audio''' is an audio channel generated by the 2C33 chip on the [[Famicom Disk System]]'s RAM card and output through the Famicom cart edge connector's expansion audio pins.


This channel rapidly repeats a [[wavetable]] set up by the CPU in a manner similar to channel 3 of the Game Boy but with more sophisticated modulation. By changing the waveform, the program can have it simulate many different instruments. Many NSF composers have found this useful.
This channel rapidly repeats a [[wikipedia:wavetable synthesis|wavetable]] set up by the CPU in a manner similar to channel 3 of the Game Boy but with more sophisticated modulation. By changing the waveform, the program can have it simulate many different instruments, such as in the musical game Otocky.


== Registers ==
== Registers ==
=== Master I/O enable ($4023) ===
This register must be written to with bit 1 set for the sound registers to function. The FDS bios initializes this by writing $00 followed by $83 to it.
7  bit  0
---------
xxxx xxSD
        ||
        |+- Enable disk I/O registers
        +-- Enable sound I/O registers


=== Wavetable RAM ($4040-$407F) ===
=== Wavetable RAM ($4040-$407F) ===
The 64-step waveform to be fed to the DAC. Each step consists of an unsigned value in the range [0, 63]. This can always be read by the CPU. However, it cannot be modified unless it is write-enabled, and it cannot be write-enabled while the sound is being played. (See also $4089 below.)
 
The 64-step waveform to be fed to the DAC. Each step consists of an unsigned sample value in the range [0, 63]. However, it cannot be modified unless it is write-enabled, and it cannot be write-enabled while the sound is being played. When writing is disabled ($4089.7), reading anywhere in 4040-407F returns the value at the current wave position. (See also $4089 below.)
 
  7  bit  0  (read/write)
  7  bit  0  (read/write)
  ---- ----
  ---- ----
Line 16: Line 29:


=== Volume envelope ($4080) ===
=== Volume envelope ($4080) ===
The volume register can range from 0 to 63; however, volume values above 32 are clipped to 32 before output.
 
The envelope speed is set by this register whether or not the envelope is enabled by the high bit,
but the current volume is set only if the high bit is set.
 
The volume gain can range from 0 to 63; however, volume values above 32 are clamped to 32 before output.
 
Changes to the volume envelope only take effect while the wavetable pointer (top 6 bits of wave accumulator) is 0. The volume envelope is basically a PWM unit, so apparently (wave_addr==0) is its comparator latch signal.
 
Writing to this register immediately resets the clock timer that ticks the volume envelope (delaying the next tick slightly).
 
  7  bit  0  (write; read through $4090)
  7  bit  0  (write; read through $4090)
  ---- ----
  ---- ----
  DMVV VVVV
  MDVV VVVV
  |||| ||||
  |||| ||||
  ||++-++++- (D=0) Volume envelope speed
  ||++-++++- (M=0) Volume envelope speed
  ||        (D=1) Current volume level
  ||        (M=1) Volume gain and envelope speed.
  |+-------- Volume change direction (0: decrease; 1: increase)
  |+-------- Volume change direction (0: decrease; 1: increase)
  +--------- Volume envelope mode (0: on; 1: off)
  +--------- Volume envelope mode (0: on; 1: off)


=== Frequency low ($4082) ===
=== Frequency low ($4082) ===
  7  bit  0  (write)
  7  bit  0  (write)
  ---- ----
  ---- ----
Line 34: Line 57:


=== Frequency high ($4083) ===
=== Frequency high ($4083) ===
The high bit of this register halts the waveform and resets its phase to 0.
Note that if halted it will output the constant value at $4040, and
writes to the volume register $4080 or master volume $4089 will affect the output.
The envelopes are not ticked while the waveform is halted.
Bit 6 halts just the envelopes without halting the waveform, and also
resets both of their timers.
  7  bit  0  (write)
  7  bit  0  (write)
  ---- ----
  ---- ----
Line 40: Line 72:
  ||  ++++- Bits 8-11 of frequency
  ||  ++++- Bits 8-11 of frequency
  |+-------- Disable volume and sweep envelopes (but not modulation)
  |+-------- Disable volume and sweep envelopes (but not modulation)
  +--------- 1: Silence channel (or halt counter?)
  +--------- When enabled, envelopes run 4x faster. Also stops the mod table accumulator.
 
=== Mod envelope ($4084) ===
 
The envelope speed is set by this register whether or not the envelope is enabled by the high bit,
but the current mod gain is set only if the high bit is set.
 
Writing to this register immediately resets the clock timer that ticks the modulator envelope (delaying the next tick slightly).


=== Sweep envelope ($4084) ===
  7  bit  0  (write; read through $4092)
  7  bit  0  (write; read through $4092)
  ---- ----
  ---- ----
  DMSS SSSS
  MDSS SSSS
  |||| ||||
  |||| ||||
  ||++-++++- (D=0) Sweep envelope speed
  ||++-++++- (M=0) Mod envelope speed
  ||        (D=1) Sweep gain
  ||        (M=1) Mod gain and envelope speed.
  |+-------- Sweep direction (0: decrease; 1: increase)
  |+-------- Mod envelope direction (0: decrease; 1: increase)
  +--------- Sweep envelope mode (0: on; 1: off)
  +--------- Mod envelope mode (0: on; 1: off)
 
=== Mod counter ($4085) ===
 
This directly sets the 7-bit signed modulator counter that is otherwise controlled by the mod unit.
 
Because the current playback position of the modulator unit is generally hard to predict while active,
it is bad practice to write $4085 unless the mod unit is disabled via $4087,
because it will generally result in a detuned note.
Bio Miracle Bokutte Upa does this, and it requires cycle-accurate timing to emulate correctly.
Some emulators incorrectly treat $4085 as a phase-reset for the mod table, which
will obviate this timing issue.
 
It is generally good practice to write 0 to $4085 to reset the counter after
writing the mod table via $4088.


=== Sweep bias ($4085) ===
A write resets the modulation unit's address to zero (see $4088) and changes the intensity and direction of modulation. See also "Modulation Unit".
  7  bit  0  (write)
  7  bit  0  (write)
  ---- ----
  ---- ----
  xBBB BBBB
  xBBB BBBB
   ||| ||||
   ||| ||||
   +++-++++- Sweep bias (7-bit signed; minimum $40; maximum $3F)
   +++-++++- Mod counter (7-bit signed; minimum $40; maximum $3F)
 
=== Mod frequency low ($4086) ===
 
If the 12-bit frequency is set to 0, mod table counter is stopped. The freq mod formula of the modulation unit is always in effect, $4084/$4085 still modify the wave frequency.


=== Modulation frequency low ($4086) ===
If the 12-bit frequency is set to 0, modulation is disabled.
  7  bit  0  (write)
  7  bit  0  (write)
  ---- ----
  ---- ----
  FFFF FFFF
  FFFF FFFF
  |||| ||||
  |||| ||||
  ++++-++++- Bits 0-7 of modulation frequency
  ++++-++++- Bits 0-7 of modulation unit frequency
 
=== Mod frequency high ($4087) ===
 
Setting the high bit of this register halts the mod unit,
and allows the mod table to be written via $4088.
 
Disabling the resets its timer accumulator, delaying the first tick after re-enabling.


=== Modulation frequency high ($4087) ===
  7  bit  0  (write)
  7  bit  0  (write)
  ---- ----
  ---- ----
  Dxxx FFFF
  HFxx FFFF
  |   ||||
  |||||
  |   ++++- Bits 8-11 of modulation frequency
  |++++- Bits 8-11 of modulation frequency
  +--------- Disable modulation
|+-------- Force a carry out from bit 11 of mod accumulator. Step every clock.
  +--------- Halt mod table counter. (Freq mod table of modulation unit is always in effect.)
 
On a carry out from bit 11 of the mod, update the mod counter (increment $4085 with mod table).
 
=== Mod table write ($4088) ===
 
This register has no effect unless the mod unit is disabled via the high bit of $4087.
 
The mod table is a ring buffer containing 32 entries.
Writing to this register replaces an entry at the current
mod table playback position with the written value, then advances the playback position
to the following entry.
 
The position of the mod table actually has 64 steps, but the least significant bit is not used to index the 32 entry table. Each entry will get applied twice as the mod table is stepped through.
 
Writing to this register 32 times will effectively reset the phase of the mod table,
having advanced the playback position back to its starting point. You should normally
always write all 32 entries at once, since the starting write position is not easily
predictable.
 
Writing $4088 also increments the address (bits 13-17 of wave accumulator) when $4087.7=1.


=== Modulation append ($4088) ===
The modulation table is a ring buffer 32 entries in length. Writing to the modulation append register removes the first entry, shifts each entry one space toward the front, and adds the written value as the last entry. However, the modulation unit uses each entry twice, so it appears that writes happen twice in a 64-entry ring buffer.
  7  bit  0  (write)
  7  bit  0  (write)
  ---- ----
  ---- ----
Line 84: Line 161:
       +++- Modulation input
       +++- Modulation input


=== Waveform write enable ($4089) ===
=== Wave write / master volume ($4089) ===
 
When the high bit is set, the current waveform output is held at
its current level until the bit is cleared again. During this time,
the wave unit will continue to run, even though the output level
is held.
 
  7  bit  0  (write)
  7  bit  0  (write)
  ---- ----
  ---- ----
Line 92: Line 175:
  |          Output volume = current volume (see $4080 above) * master volume
  |          Output volume = current volume (see $4080 above) * master volume
  +--------- Wavetable write enable
  +--------- Wavetable write enable
             (0: write protect RAM; 1: write enable RAM and silence channel)
             (0: write protect RAM; 1: write enable RAM and hold channel)


=== Envelope speed ($408A) ===
=== Envelope speed ($408A) ===
Few FDS NSFs write to this register. BIOS or game code appears to set this to $FF.
 
This sets a clock multiplier for the volume and modulator envelopes.
Few FDS NSFs write to this register. The BIOS initializes this to $E8.
 
  7  bit  0  (write)
  7  bit  0  (write)
  ---- ----
  ---- ----
Line 104: Line 190:


=== Volume gain ($4090) ===
=== Volume gain ($4090) ===
  7  bit  0  (read; write through $4080)
  7  bit  0  (read; write through $4080)
  ---- ----
  ---- ----
Line 111: Line 198:
  ++-------- Returns 01 on read, likely from open bus
  ++-------- Returns 01 on read, likely from open bus


=== Sweep gain ($4092) ===
=== Wave accumlator ($4091) ===
 
7  bit  0  (read)
---- ----
AAAA AAAA
|||| ||||
++++-++++- Bits 12-19 of the wavetable address accumulator
 
=== Mod gain ($4092) ===
 
  7  bit  0  (read; write through $4084)
  7  bit  0  (read; write through $4084)
  ---- ----
  ---- ----
  OOVV VVVV
  OOVV VVVV
  |||| ||||
  |||| ||||
  ||++-++++- Current sweep gain level
  ||++-++++- Current mod gain level
  ++-------- Returns 01 on read, likely from open bus
  ++-------- Returns 01 on read, likely from open bus


== Frequency calculation and timing ==
=== Mod table address accumulator ($4093) ===
The 4 different areas of the FDS Sound channel (Volume Envelope, Sweep Envelope,
 
Modulation Unit, and Main Unit) are clocked at independent rates. This section covers
7  bit  0  (read)
how to calculate the number of clocks per second (in Hz) from the various values
---- ----
written to the registers.
OAAA AAAA
|||| ||||
|+++-++++- Bits 5-11 of the modtable address accumulator
+--------- Returns 0 on read, likely from open bus
 
=== Mod counter*gain result ($4094) ===
 
The mod unit uses a sequential multiplier. By reading $4094 at different times, you can see the result.
 
7  bit  0  (read)
---- ----
MMMM MMMM
|||| ||||
++++-++++- Bits 4-11 of mod counter*gain intermediate result.
 
=== Mod counter increment ($4095) ===
 
This shows the mod table contents at its current position, translated to mod counter increment value (0,1,2,3,4,5,6,7 ==> 0,1,2,4,C,C,E,F). In other words, what will be added to the counter (sign extend to 7 bits) on the next address tick.
 
7  bit  0  (read)
---- ----
???? MMMM
|||| ||||
|||| ++++- Next mod counter ($4085) increment.
++++------ Unknown counter
 
=== Wavetable value ($4096) ===
 
7  bit  0  (read)
---- ----
OOVV VVVV
|||| ||||
||++-++++- Value at current wavetable position, masked by PWM from volume envelope. (What's being fed to the DAC, probably.)
++-------- Returns 01 on read, likely from open bus.
 
=== Mod counter value ($4097) ===


=== Volume envelope unit ===
7  bit  0  (read)
Hz = (232 * 960) / (Envelope_Speed * (Volume_Envelope_Speed + 1)), where
---- ----
* Hz = how many times per second a clock occurs
OCCC CCCC
* Envelope_Speed = value written to $408A (if zero, Envelope is disabled)
|||| ||||
* Volume_Envelope_Speed = value set by writes to $4080
|+++-++++- Current mod counter ($4085) value
+--------- Returns 0 from read, likely from open bus.


When Disch was working with this formula to fit in his NSF player, he was converting the formula to work with NES CPU cycles (~1798772 a second).  This involved taking the reciprocal of the above formula and dividing by the NES frequency.  This produced a somewhat simpler formula:
== Unit tick ==


Cyc = 8 * Envelope_Speed * (Volume_Envelope_Speed + 1), where
=== Envelopes ===
* Cyc = Number of NES CPU cycles that need to pass for 1 clock to occur


For this alternate formula in Hertz:
If enabled, when the volume or mod table envelope is ticked by its timer,
it will do one of the following based on $4080/$4084 bit 6:


Hz = NES / Cyc, where
*Increase: if gain is less than 32, increase it by 1
* NES = NTSC NES CPU frequency (1789772.7272, or half the [[NTSC_video|NTSC]] color subcarrier frequency)
*Decrease: if gain is more than 0, decrease it by 1


Since all the other NES channels revolve around the CPU clock rate, this second formula seems more likely, since it also uses the NES clock as the base. In any case, the 2 formulas produce very similar results.  If you work the conversion yourself... you end up simplifying:
Note that the gains manually can be set higher than 32, but they can no longer be increased at this point.
The gain can still be decreased if above 32.


1789772.7272 / (232 * 960) = ~8.036
The volume gain's final output will always be clamped to 32, even though the
internal gain can be higher. This is because the pulse width modulation has a
period of 32 CPU cycles, and the volume gain is applied as the duty cycle.
(32/32 = 100%).


Otherwise, the volume and mod envelopes are functionally identical.


=== Sweep envelope unit ===
Volume gain changes will only take effect when the current wavetable position is
Hz = (232 * 960) / (Envelope_Speed * (Sweep_Envelope_Speed + 1)), where
0.
*Sweep_Envelope_Speed = value set by writes to $4084
:''or''
Cyc = 8 * Envelope_Speed * (Sweep_Envelope_Speed + 1)<br>
Hz = NES / Cyc


=== Modulation unit ===
=== Modulation unit ===
Hz = NES * ModFreq / 65536, where
*ModFreq = 12-bit Modulation Frequency value set by $4086/$4087


Note that this frequency is how many times per second the modulation unit gets
When the modulation unit is ticked, it advances to the next position in its
clocked...  not how many times it goes through the entire Frequency Modulation
modulation table according to the address value in its accumulator.
Table (which would be this value / 64, since there are 32 entries in the table and each is used twice)
 
The modulation unit is ticked every 16 CPU cycles.
 
The modulation unit applies the modulation value to the counter when bit 11 of
its accumulator carries over (i.e. the first 12 bits gets set back to 0 and bit
12 gets incremented).


=== Main unit ===
Note that because the least significant bit is not used to index the 32 entry
Hz = NES * ( (Freq + Mod) / 65536 ), where
modulation table, the same value will always be written twice on consecutive
* Freq = 12-bit Frequency set by $4082/$4083
ticks.
* Mod = Frequency change based on the Modulation unit (see next section)


Note again that this is the how many times per second the main unit gets
Each 3-bit value in the mod table corresponds to one of the following adjustment of the mod counter when ticked:
clocked (taking 1 step further in the table).  The entire table is 64 entries,
so the frequency of it playing the entire wave would be this value / 64.


== What each unit does in a clock ==
0 = %000 -->  0
1 = %001 --> +1
2 = %010 --> +2
3 = %011 --> +4
4 = %100 --> reset to 0
5 = %101 --> -4
6 = %110 --> -2
7 = %111 --> -1


=== Volume envelope unit ===
The mod counter is a signed 7-bit value, and will wrap if overflowed, i.e. 63 + 1 = -64 after wrap, and -64 - 1 = 63.


Every clock, the Volume Envelope Unit alters the Volume_Gain depending on which mode we're in ($4080.D6).
The value of the mod counter will modify the pitch of the wave output unit in a complicated way,
described by the following C-style code:


*Increase Mode: If Volume Gain is less than 32, it's increased by 1
  // pitch  = $4082/4083 (12-bit unsigned pitch value)
*Decrease ModeIf Volume Gain is greater than 0, it's decreased by 1
// counter = $4085 (7-bit signed mod counter)
// gain    = $4084 (6-bit unsigned mod gain)
// 1. multiply counter by gain
temp = counter * gain;
range: -4032 to 3969
  // 2. round up to 6 bits (ignoring bit 4) only if sign positive
if((temp & 0x0f) && !(temp & 0x800))
    temp += 0x20;
// 3. drop 4 bits and center to 0x40
temp += 0x400;
temp = (temp >> 4) & 0xff;
// 4. multiply by pitch to get the 20-bit unsigned result
wave_pitch = (pitch * temp) & 0xFFFFF;


Note that despite Volume Gain in only increased if less than 32, it still can be greater than $20 (up to $3F) if set that way through a write to $4080.
=== Wave output unit ===


Also on a clock, the output volume is set to the smaller of Volume Gain or 32 whichever is less (so while Volume Gain may be higher than 32, the actual output volume caps at 32).
When this unit is ticked, it advances to the next position in its wave table
according to the wave position address value in its accumulator.


=== Sweep envelope unit ===
The wave output unit is ticked every 16 CPU cycles.
Sweep Envelope unit behaves just like the Volume Envelope, only it alters Sweep Gain instead of Volume Gain.  The Envelope Unit never pushes Sweep Gain above $20, but it still can get above $20 if set that way via $4084.


Increase/Decrease mode is determined by bit 6 of $4084
== Frequency calculation and timing ==


Sweep Gain is used when calculating the Frequency change in the Modulation Unit:
=== Envelopes ===
 
The volume and modulator envelopes tick once after a specific number of CPU cycles,
calculated in the following way:
 
c = CPU clocks per tick
e = envelope speed ($4080/4084)
m = master envelope speed ($408A)
c =  8 * (e + 1) * (m + 1)
 
To determine the frequency:
 
f = frequency of tick
n = CPU clock rate (≈1789773 Hz)
f = n / c
 
Writing the envelope control registers for these units will reset the timer for the respective unit,
meaning that it will next tick '''c''' cycles after the write.
 
=== Wavetables ===
 
The wave output accumulates a 6-bit wave position address value by adding the
20-bit modulated pitch result to its accumulator every 16 CPU cycles.
 
      ++++++++--------------- 4091 read (bits 12-19)
      ||||||||
[ AAAAAAXXXXXXXXXXXXXXXXXX ]  Wave accumulator
  ||||||||||||||||||||||||
  ||||++++++++++++++++++++--- +wave_pitch (bits 0-19) add
  ||||||
  ++++++--------------------- wave address (bits 23-18)
 
The frequency for wave output (without modulation) can be calculated as follows:
 
f = frequency of wave table
n = CPU clock rate (≈1789773 Hz)
p = current pitch value ($4082/$4083)
f = n * p / 16 / 2^12
 
The pitch value for the wave output is '''not''' simply the 12-bit value from
registers $4082/$4083, but is modified by the modulator counter in an obtuse way.
See [[FDS_audio#Modulation unit|above]] for details.
 
To calculate the momentary final frequency output:
 
f = frequency of wave tick
n = CPU clock rate (≈1789773 Hz)
w = wave_pitch as calculated above
f = n * w / 16 / 2^18
 
Disabling the wave unit via the high bit of $4083 immediately resets its
accumulator, delaying the next tick after they are enabled again until the next
overflow.
 
Consequently, this also resets the wave position to 0 (i.e. the $4040 value).


=== Modulation unit ===
=== Modulation unit ===
The Modulation Unit, when clocked, takes 1 step through the Modulation Table (set by writes to $4088).  The Sweep Bias is adjusted based on the 3-bit value in the list [+0, +1, +2, +4, reset to 0, -4, -2, -1].
The address of the Modulation unit is incremented ''every other clock'' to use the next 3-bit value in the table.  This address wraps at 32 and can be reset to zero by any write to $4085.


After the Sweep Bias is adjusted, it wraps to fit within a signed 7-bit value. That is, if it goes greater than 63, it wraps around to -64, and if it goes below -64, it wraps to 63.
The modulation unit accumulates a 5-bit mod table address value by adding the
12-bit modulation pitch ($4086/$4087) to its accumulator every 16 CPU cycles.


          ++++-+++-------- 4093 read
          |||| |||
[ AAAAA a FFFF ffff ffff ]  Mod accumulator
  ||||| | |||| |||| ||||
  ||||| | |||| ++++-++++-- +freq low (4086[7:0]) add
  ||||| | ++++------------ +freq hi (4087[3:0]) add
  ||||| +----------------- "ghost" modtable address bit(0) makes mod unit step thru each entry twice
  +++++------------------- modtable address


The Modulation Unit works by altering the Frequency of the Main Unit by a value calculated from the Sweep Gain and Sweep Bias values.  The logic for this calculation is complicated, represented by the following pseudocode:
The frequency for the modulation unit can be calculated as follows:


  temp = Sweep_Bias * Sweep_Gain;
f = frequency of modulation tick
  if (temp & 0x0F)
n = CPU clock rate (≈1789773 Hz)
  {
p = current pitch value ($4086/$4087)  
    temp /= 16;
   
    if( Sweep_Bias < 0 )    temp -= 1;
f = n * p / 16 / 2^12
    else                    temp += 2;
  }
  else
    temp /= 16;
 
  if (temp > 193)
    temp -= 258; // not a typo... for some reason the wraps are inconsistent
  if( temp < -64 )
    temp += 256;
 
  Mod = Freq * temp / 64;


To calculate for the frequency of the entire period, divide the result by 64.


In this code, Freq is the 12-bit MAIN UNIT frequency, and Mod is the amount that
Disabling the modulation unit via the high bit of $4087 immediately resets its
frequency is altered (to bend the playback frequency in either direction). This
accumulator, in a similar manner to the wave unit.
generated 'Mod' value is used in the frequency calculation of the main unit (given
earlier):


    Hz = NES * ( (Freq + Mod) / 65536 )
The modulation table position address (bits 13-17) however does not reset.


If at any time the Modulation unit is off, 'Mod' is zero.  Otherwise 'Mod' is the
== Mixing ==
above calculated value.  If Freq + Mod produces a number less than or equal to zero,
the channel is presumably silenced (<0 Hz output isn't really possible/audible).


=== Main unit ===
The current wave output value is attenuated by the current volume gain and master volume. This output signal is affected by a filter that attenuates higher frequencies. This filter can be approximated as a 1-pole lowpass with a cutoff of ~2000Hz.
When the main Unit is clocked, the next entry in the 64 entry waveform table
(accessed via regs $4040-$407F) is output. Once the 64'th sample is played ($407F),
the table restarts ($4040 is played) and it continues to loop.


== Unit activity ==
The maximum volume of the FDS signal on a Famicom is roughly 2.4x the maximum volume of the APU square, and the polarity is the same as the 2A03. On other machines, such as the Twin Famicom, the output may be significantly louder.  
Remember that each unit can be active regardless of the activitiy of other units.
For example, even though the main unit is off and the channel is silent, this does
not mean the Volume Envelope or Modulation units are inactive.


If any of the supplied conditions are false, the unit is inactive and will not
The DAC output of the waveform is 6-bits (0-63), and ideally should be linear, but recent tests have revealed jagged discontinuities at binary nodes. (More works needs to be done to measure this effect.)
be clocked. All conditions must be true for the unit to be active.


*Volume envelope unit
The volume is applied by a pulse width modulation every 32 CPU cycles. The duty
**Volume Envelope must be enabled (bit 7 of $4080 must be off), AND
cycle is controlled by the volume gain (volume gain / 32). Because this
**Envelope Speed must be nonzero (set by $408A), AND
modulation is above the audible threshold (55.930 kHz), it may be emulated
**Envelope must be enabled (bit 6 of $4083 must be off)
simply as a linear volume control.
*Sweep envelope unit
**Sweep Envelope must be enabled (bit 7 of $4084 must be off), AND
**Envelope Speed must be nonzero (set by $408A), AND
**Envelope must be enabled (bit 6 of $4083 must be off)
*Modulation unit
**Modulation must be enabled (bit 7 of $4087 must be off), AND
**Modulation frequency must be non-zero (set by $4086/$4087)
*Main unit:
**Main Unit must be enabled (bit 7 of $4083 must be off), AND
**Main Unit Frequency must be non-zero (set by $4082/$4083), AND
**'Freq + Mod' must be greater than zero (see Frequency Calculation section), AND
**Write Mode must be off (bit 7 of $4089 must be off)


== References ==
== References ==
*[http://nesdev.parodius.com/FDS.txt FDS Sound] by Disch
*[https://forums.nesdev.org/viewtopic.php?p=232662#p232662 Modulator unit notes] by Loopy
*[http://nesdev.parodius.com/FDS%20technical%20reference.txt Famicom Disk System technical reference] by Brad Taylor
*[https://forums.nesdev.org/viewtopic.php?t=25226 FDS decap DAC schematics] from Yuri213212
*[https://forums.nesdev.org/viewtopic.php?f=3&t=10233 FDS output notes] (from forums)
*[https://nesdev.org/FDS.txt FDS Sound] by Disch
*[https://nesdev.org/FDS%20technical%20reference.txt Famicom Disk System technical reference] by Brad Taylor
*[http://patft.uspto.gov/netacgi/nph-Parser?Sect1=PTO1&Sect2=HITOFF&d=PALL&p=1&u=%2Fnetahtml%2FPTO%2Fsrchnum.htm&r=1&f=G&l=50&s1=4,783,812.PN.&OS=PN/4,783,812&RS=PN/4,783,812 US Patent 4783812 on the FDS sound system]
*[http://patft.uspto.gov/netacgi/nph-Parser?Sect1=PTO1&Sect2=HITOFF&d=PALL&p=1&u=%2Fnetahtml%2FPTO%2Fsrchnum.htm&r=1&f=G&l=50&s1=4,783,812.PN.&OS=PN/4,783,812&RS=PN/4,783,812 US Patent 4783812 on the FDS sound system]

Revision as of 11:18, 20 April 2024

The Famicom Disk System audio is an audio channel generated by the 2C33 chip on the Famicom Disk System's RAM card and output through the Famicom cart edge connector's expansion audio pins.

This channel rapidly repeats a wavetable set up by the CPU in a manner similar to channel 3 of the Game Boy but with more sophisticated modulation. By changing the waveform, the program can have it simulate many different instruments, such as in the musical game Otocky.

Registers

Master I/O enable ($4023)

This register must be written to with bit 1 set for the sound registers to function. The FDS bios initializes this by writing $00 followed by $83 to it.

7  bit  0
---------
xxxx xxSD
       ||
       |+- Enable disk I/O registers
       +-- Enable sound I/O registers

Wavetable RAM ($4040-$407F)

The 64-step waveform to be fed to the DAC. Each step consists of an unsigned sample value in the range [0, 63]. However, it cannot be modified unless it is write-enabled, and it cannot be write-enabled while the sound is being played. When writing is disabled ($4089.7), reading anywhere in 4040-407F returns the value at the current wave position. (See also $4089 below.)

7  bit  0  (read/write)
---- ----
OOSS SSSS
|||| ||||
||++-++++- Sample
++-------- Returns 01 on read, likely from open bus

Volume envelope ($4080)

The envelope speed is set by this register whether or not the envelope is enabled by the high bit, but the current volume is set only if the high bit is set.

The volume gain can range from 0 to 63; however, volume values above 32 are clamped to 32 before output.

Changes to the volume envelope only take effect while the wavetable pointer (top 6 bits of wave accumulator) is 0. The volume envelope is basically a PWM unit, so apparently (wave_addr==0) is its comparator latch signal.

Writing to this register immediately resets the clock timer that ticks the volume envelope (delaying the next tick slightly).

7  bit  0  (write; read through $4090)
---- ----
MDVV VVVV
|||| ||||
||++-++++- (M=0) Volume envelope speed
||         (M=1) Volume gain and envelope speed.
|+-------- Volume change direction (0: decrease; 1: increase)
+--------- Volume envelope mode (0: on; 1: off)

Frequency low ($4082)

7  bit  0  (write)
---- ----
FFFF FFFF
|||| ||||
++++-++++- Bits 0-7 of frequency

Frequency high ($4083)

The high bit of this register halts the waveform and resets its phase to 0. Note that if halted it will output the constant value at $4040, and writes to the volume register $4080 or master volume $4089 will affect the output. The envelopes are not ticked while the waveform is halted.

Bit 6 halts just the envelopes without halting the waveform, and also resets both of their timers.

7  bit  0  (write)
---- ----
MExx FFFF
||   ||||
||   ++++- Bits 8-11 of frequency
|+-------- Disable volume and sweep envelopes (but not modulation)
+--------- When enabled, envelopes run 4x faster. Also stops the mod table accumulator.

Mod envelope ($4084)

The envelope speed is set by this register whether or not the envelope is enabled by the high bit, but the current mod gain is set only if the high bit is set.

Writing to this register immediately resets the clock timer that ticks the modulator envelope (delaying the next tick slightly).

7  bit  0  (write; read through $4092)
---- ----
MDSS SSSS
|||| ||||
||++-++++- (M=0) Mod envelope speed
||         (M=1) Mod gain and envelope speed.
|+-------- Mod envelope direction (0: decrease; 1: increase)
+--------- Mod envelope mode (0: on; 1: off)

Mod counter ($4085)

This directly sets the 7-bit signed modulator counter that is otherwise controlled by the mod unit.

Because the current playback position of the modulator unit is generally hard to predict while active, it is bad practice to write $4085 unless the mod unit is disabled via $4087, because it will generally result in a detuned note. Bio Miracle Bokutte Upa does this, and it requires cycle-accurate timing to emulate correctly. Some emulators incorrectly treat $4085 as a phase-reset for the mod table, which will obviate this timing issue.

It is generally good practice to write 0 to $4085 to reset the counter after writing the mod table via $4088.

7  bit  0  (write)
---- ----
xBBB BBBB
 ||| ||||
 +++-++++- Mod counter (7-bit signed; minimum $40; maximum $3F)

Mod frequency low ($4086)

If the 12-bit frequency is set to 0, mod table counter is stopped. The freq mod formula of the modulation unit is always in effect, $4084/$4085 still modify the wave frequency.

7  bit  0  (write)
---- ----
FFFF FFFF
|||| ||||
++++-++++- Bits 0-7 of modulation unit frequency

Mod frequency high ($4087)

Setting the high bit of this register halts the mod unit, and allows the mod table to be written via $4088.

Disabling the resets its timer accumulator, delaying the first tick after re-enabling.

7  bit  0  (write)
---- ----
HFxx FFFF
||   ||||
||   ++++- Bits 8-11 of modulation frequency
|+-------- Force a carry out from bit 11 of mod accumulator. Step every clock.
+--------- Halt mod table counter. (Freq mod table of modulation unit is always in effect.)

On a carry out from bit 11 of the mod, update the mod counter (increment $4085 with mod table).

Mod table write ($4088)

This register has no effect unless the mod unit is disabled via the high bit of $4087.

The mod table is a ring buffer containing 32 entries. Writing to this register replaces an entry at the current mod table playback position with the written value, then advances the playback position to the following entry.

The position of the mod table actually has 64 steps, but the least significant bit is not used to index the 32 entry table. Each entry will get applied twice as the mod table is stepped through.

Writing to this register 32 times will effectively reset the phase of the mod table, having advanced the playback position back to its starting point. You should normally always write all 32 entries at once, since the starting write position is not easily predictable.

Writing $4088 also increments the address (bits 13-17 of wave accumulator) when $4087.7=1.

7  bit  0  (write)
---- ----
xxxx xMMM
      |||
      +++- Modulation input

Wave write / master volume ($4089)

When the high bit is set, the current waveform output is held at its current level until the bit is cleared again. During this time, the wave unit will continue to run, even though the output level is held.

7  bit  0  (write)
---- ----
Wxxx xxVV
|      ||
|      ++- Master volume (0: full; 1: 2/3; 2: 2/4; 3: 2/5)
|          Output volume = current volume (see $4080 above) * master volume
+--------- Wavetable write enable
           (0: write protect RAM; 1: write enable RAM and hold channel)

Envelope speed ($408A)

This sets a clock multiplier for the volume and modulator envelopes. Few FDS NSFs write to this register. The BIOS initializes this to $E8.

7  bit  0  (write)
---- ----
SSSS SSSS
|||| ||||
++++-++++- Sets speed of volume envelope and sweep envelope
           (0: disable them)

Volume gain ($4090)

7  bit  0  (read; write through $4080)
---- ----
OOVV VVVV
|||| ||||
||++-++++- Current volume gain level
++-------- Returns 01 on read, likely from open bus

Wave accumlator ($4091)

7  bit  0  (read)
---- ----
AAAA AAAA
|||| ||||
++++-++++- Bits 12-19 of the wavetable address accumulator

Mod gain ($4092)

7  bit  0  (read; write through $4084)
---- ----
OOVV VVVV
|||| ||||
||++-++++- Current mod gain level
++-------- Returns 01 on read, likely from open bus

Mod table address accumulator ($4093)

7  bit  0  (read)
---- ----
OAAA AAAA
|||| ||||
|+++-++++- Bits 5-11 of the modtable address accumulator
+--------- Returns 0 on read, likely from open bus

Mod counter*gain result ($4094)

The mod unit uses a sequential multiplier. By reading $4094 at different times, you can see the result.

7  bit  0  (read)
---- ----
MMMM MMMM
|||| ||||
++++-++++- Bits 4-11 of mod counter*gain intermediate result.

Mod counter increment ($4095)

This shows the mod table contents at its current position, translated to mod counter increment value (0,1,2,3,4,5,6,7 ==> 0,1,2,4,C,C,E,F). In other words, what will be added to the counter (sign extend to 7 bits) on the next address tick.

7  bit  0  (read)
---- ----
???? MMMM
|||| ||||
|||| ++++- Next mod counter ($4085) increment.
++++------ Unknown counter

Wavetable value ($4096)

7  bit  0  (read)
---- ----
OOVV VVVV
|||| ||||
||++-++++- Value at current wavetable position, masked by PWM from volume envelope. (What's being fed to the DAC, probably.)
++-------- Returns 01 on read, likely from open bus.

Mod counter value ($4097)

7  bit  0  (read)
---- ----
OCCC CCCC
|||| ||||
|+++-++++- Current mod counter ($4085) value
+--------- Returns 0 from read, likely from open bus.

Unit tick

Envelopes

If enabled, when the volume or mod table envelope is ticked by its timer, it will do one of the following based on $4080/$4084 bit 6:

  • Increase: if gain is less than 32, increase it by 1
  • Decrease: if gain is more than 0, decrease it by 1

Note that the gains manually can be set higher than 32, but they can no longer be increased at this point. The gain can still be decreased if above 32.

The volume gain's final output will always be clamped to 32, even though the internal gain can be higher. This is because the pulse width modulation has a period of 32 CPU cycles, and the volume gain is applied as the duty cycle. (32/32 = 100%).

Otherwise, the volume and mod envelopes are functionally identical.

Volume gain changes will only take effect when the current wavetable position is 0.

Modulation unit

When the modulation unit is ticked, it advances to the next position in its modulation table according to the address value in its accumulator.

The modulation unit is ticked every 16 CPU cycles.

The modulation unit applies the modulation value to the counter when bit 11 of its accumulator carries over (i.e. the first 12 bits gets set back to 0 and bit 12 gets incremented).

Note that because the least significant bit is not used to index the 32 entry modulation table, the same value will always be written twice on consecutive ticks.

Each 3-bit value in the mod table corresponds to one of the following adjustment of the mod counter when ticked:

0 = %000 -->  0
1 = %001 --> +1
2 = %010 --> +2
3 = %011 --> +4
4 = %100 --> reset to 0
5 = %101 --> -4
6 = %110 --> -2
7 = %111 --> -1

The mod counter is a signed 7-bit value, and will wrap if overflowed, i.e. 63 + 1 = -64 after wrap, and -64 - 1 = 63.

The value of the mod counter will modify the pitch of the wave output unit in a complicated way, described by the following C-style code:

// pitch   = $4082/4083 (12-bit unsigned pitch value)
// counter = $4085 (7-bit signed mod counter)
// gain    = $4084 (6-bit unsigned mod gain)

// 1. multiply counter by gain
temp = counter * gain;

range: -4032 to 3969
// 2. round up to 6 bits (ignoring bit 4) only if sign positive
if((temp & 0x0f) && !(temp & 0x800))
    temp += 0x20;

// 3. drop 4 bits and center to 0x40
temp += 0x400;
temp = (temp >> 4) & 0xff;

// 4. multiply by pitch to get the 20-bit unsigned result
wave_pitch = (pitch * temp) & 0xFFFFF;

Wave output unit

When this unit is ticked, it advances to the next position in its wave table according to the wave position address value in its accumulator.

The wave output unit is ticked every 16 CPU cycles.

Frequency calculation and timing

Envelopes

The volume and modulator envelopes tick once after a specific number of CPU cycles, calculated in the following way:

c = CPU clocks per tick
e = envelope speed ($4080/4084)
m = master envelope speed ($408A)

c =  8 * (e + 1) * (m + 1)

To determine the frequency:

f = frequency of tick
n = CPU clock rate (≈1789773 Hz)

f = n / c

Writing the envelope control registers for these units will reset the timer for the respective unit, meaning that it will next tick c cycles after the write.

Wavetables

The wave output accumulates a 6-bit wave position address value by adding the 20-bit modulated pitch result to its accumulator every 16 CPU cycles.

      ++++++++--------------- 4091 read (bits 12-19)
      ||||||||
[ AAAAAAXXXXXXXXXXXXXXXXXX ]  Wave accumulator
  ||||||||||||||||||||||||
  ||||++++++++++++++++++++--- +wave_pitch (bits 0-19) add
  ||||||
  ++++++--------------------- wave address (bits 23-18)

The frequency for wave output (without modulation) can be calculated as follows:

f = frequency of wave table
n = CPU clock rate (≈1789773 Hz)
p = current pitch value ($4082/$4083)

f = n * p / 16 / 2^12

The pitch value for the wave output is not simply the 12-bit value from registers $4082/$4083, but is modified by the modulator counter in an obtuse way. See above for details.

To calculate the momentary final frequency output:

f = frequency of wave tick
n = CPU clock rate (≈1789773 Hz)
w = wave_pitch as calculated above

f = n * w / 16 / 2^18

Disabling the wave unit via the high bit of $4083 immediately resets its accumulator, delaying the next tick after they are enabled again until the next overflow.

Consequently, this also resets the wave position to 0 (i.e. the $4040 value).

Modulation unit

The modulation unit accumulates a 5-bit mod table address value by adding the 12-bit modulation pitch ($4086/$4087) to its accumulator every 16 CPU cycles.

          ++++-+++-------- 4093 read
          |||| |||
[ AAAAA a FFFF ffff ffff ]  Mod accumulator
  ||||| | |||| |||| ||||
  ||||| | |||| ++++-++++-- +freq low (4086[7:0]) add
  ||||| | ++++------------ +freq hi (4087[3:0]) add
  ||||| +----------------- "ghost" modtable address bit(0) makes mod unit step thru each entry twice
  +++++------------------- modtable address

The frequency for the modulation unit can be calculated as follows:

f = frequency of modulation tick
n = CPU clock rate (≈1789773 Hz)
p = current pitch value ($4086/$4087) 

f = n * p / 16 / 2^12

To calculate for the frequency of the entire period, divide the result by 64.

Disabling the modulation unit via the high bit of $4087 immediately resets its accumulator, in a similar manner to the wave unit.

The modulation table position address (bits 13-17) however does not reset.

Mixing

The current wave output value is attenuated by the current volume gain and master volume. This output signal is affected by a filter that attenuates higher frequencies. This filter can be approximated as a 1-pole lowpass with a cutoff of ~2000Hz.

The maximum volume of the FDS signal on a Famicom is roughly 2.4x the maximum volume of the APU square, and the polarity is the same as the 2A03. On other machines, such as the Twin Famicom, the output may be significantly louder.

The DAC output of the waveform is 6-bits (0-63), and ideally should be linear, but recent tests have revealed jagged discontinuities at binary nodes. (More works needs to be done to measure this effect.)

The volume is applied by a pulse width modulation every 32 CPU cycles. The duty cycle is controlled by the volume gain (volume gain / 32). Because this modulation is above the audible threshold (55.930 kHz), it may be emulated simply as a linear volume control.

References