S-DSP registers: Difference between revisions
(Start S-DSP registers page) |
(Add register sections, documented envelope registers, added errata (most of it was copied from the Errata page).) |
||
Line 37: | Line 37: | ||
* '''RW''' - Readable and Writable | * '''RW''' - Readable and Writable | ||
* '''R''' - Readable (technically writable, but not intended to be written to) | * '''R''' - Readable (technically writable, but not intended to be written to) | ||
== Global registers == | |||
{{Anchor|MVOL|MVOLL|MVOLH}} | |||
=== MVOLL, MVOLR - Main volume ($0C, $1C) === | |||
7 bit 0 | |||
---- ---- | |||
VVVV VVVV | |||
|||| |||| | |||
++++-++++- left/right channel volume (signed) | |||
{{Anchor|EVOL|EVOLL|EVOLR}} | |||
=== EVOLL, EVOLR - Echo volume ($2C, $3C) === | |||
7 bit 0 | |||
---- ---- | |||
VVVV VVVV | |||
|||| |||| | |||
++++-++++- left/right channel volume (signed) | |||
{{Anchor|KON}} | |||
=== KON - Key on ($4C) === | |||
7 bit 0 | |||
---- ---- | |||
7654 3210 | |||
|||| |||| | |||
|||| |||+- key-on voice 0 | |||
|||| ||+-- key-on voice 1 | |||
|||| |+--- key-on voice 2 | |||
|||| +---- key-on voice 3 | |||
|||+------ key-on voice 4 | |||
||+------- key-on voice 5 | |||
|+-------- key-on voice 6 | |||
+--------- key-on voice 7 | |||
Writing a 1 to a <tt>KON</tt> voice bit will: | |||
* Set the voice's envelope to 0 | |||
* Change the voice's ADSR state to Attack | |||
* Reset the voice's BRR decoder to the start of the sample (as determined by <tt>[[#DIR|DIR]]</tt> and <tt>[[#VxSCRN|VxSCRN]]</tt>) | |||
<tt>KON</tt> is polled every second sample. | |||
ERRATA:<ref>Anomie's S-DSP Doc: KON and KOFF registers</ref> | |||
* Clearing <tt>KON</tt> too early can cause the voice to not key-on. | |||
* If a voice's <tt>KON</tt> and <tt>KOFF</tt> bits are both set; the key-on will be followed by a key-off, silencing the channel. | |||
{{Anchor|KOFF|KOF}} | |||
=== KOFF - Key off ($5C) === | |||
7 bit 0 | |||
---- ---- | |||
7654 3210 | |||
|||| |||| | |||
|||| |||+- key-off voice 0 | |||
|||| ||+-- key-off voice 1 | |||
|||| |+--- key-off voice 2 | |||
|||| +---- key-off voice 3 | |||
|||+------ key-off voice 4 | |||
||+------- key-off voice 5 | |||
|+-------- key-off voice 6 | |||
+--------- key-off voice 7 | |||
Setting a voice bit in <tt>KON</tt> will: | |||
* Change the voice's envelope state to Release | |||
<tt>KOFF</tt> is polled every second sample. | |||
ERRATA:<ref>Anomie's S-DSP Doc: KON and KOFF registers</ref> | |||
* Clearing <tt>KOFF</tt> too early can cause the voice to not key-off. | |||
* Clearing the <tt>KOFF</tt> bits after writing to <tt>KON</tt> might key-on, key-off or silence the channel. | |||
* If a voice's <tt>KON</tt> and <tt>KOFF</tt> bits are both set; the key-on will be followed by a key-off, silencing the channel. | |||
{{Anchor|FLG}} | |||
=== FLG - Flags register ($6C) === | |||
7 bit 0 | |||
---- ---- | |||
RMEN NNNN | |||
|||| |||| | |||
|||+-++++- Noise frequency | |||
||+------- Disable echo write | |||
|+-------- Mute all | |||
+--------- Soft reset | |||
{{Anchor|ENDX}} | |||
=== ENDX - End of sample flags ($7C, read-only) === | |||
7 bit 0 | |||
---- ---- | |||
7654 3210 | |||
|||| |||| | |||
|||| |||+- Voice 0 BRR block has end-flag set | |||
|||| ||+-- Voice 1 BRR block has end-flag set | |||
|||| |+--- Voice 2 BRR block has end-flag set | |||
|||| +---- Voice 3 BRR block has end-flag set | |||
|||+------ Voice 4 BRR block has end-flag set | |||
||+------- Voice 5 BRR block has end-flag set | |||
|+-------- Voice 6 BRR block has end-flag set | |||
+--------- Voice 7 BRR block has end-flag set | |||
The voice bits are set when the current BRR block has the end-flag set (not at the end of the BRR sample). | |||
If the voice is recently keyed-on, the <tt>ENDX</tt> bits will be clear (even if the BRR sample is a single BRR block).<ref>ares source code, <tt>ares::SuperFamicom::Dsp::voice5()</tt>, by Near and ares team</ref> | |||
<tt>ENDX</tt> is technically writable and not intended to be written to. The S-DSP updates this register 8 times per sample. | |||
{{Anchor|EFB}} | |||
=== EFB - Echo feedback ($0D) === | |||
7 bit 0 | |||
---- ---- | |||
VVVV VVVV | |||
|||| |||| | |||
++++-++++- echo feedback (signed) | |||
{{Anchor|PMON}} | |||
=== PMON - Pitch modulation enable ($2D) === | |||
7 bit 0 | |||
---- ---- | |||
7654 321- | |||
|||| ||| | |||
|||| ||+-- Enable pitch modulation on voice 1 | |||
|||| |+--- Enable pitch modulation on voice 2 | |||
|||| +---- Enable pitch modulation on voice 3 | |||
|||+------ Enable pitch modulation on voice 4 | |||
||+------- Enable pitch modulation on voice 5 | |||
|+-------- Enable pitch modulation on voice 6 | |||
+--------- Enable pitch modulation on voice 7 | |||
{{Anchor|NON}} | |||
=== NON - Noise enable ($3D) === | |||
7 bit 0 | |||
---- ---- | |||
7654 3210 | |||
|||| |||| | |||
|||| |||+- Replace voice 0 with noise generator | |||
|||| ||+-- Replace voice 1 with noise generator | |||
|||| |+--- Replace voice 2 with noise generator | |||
|||| +---- Replace voice 3 with noise generator | |||
|||+------ Replace voice 4 with noise generator | |||
||+------- Replace voice 5 with noise generator | |||
|+-------- Replace voice 6 with noise generator | |||
+--------- Replace voice 7 with noise generator | |||
{{Anchor|EON}} | |||
=== EON - Echo enable ($3D) === | |||
7 bit 0 | |||
---- ---- | |||
7654 3210 | |||
|||| |||| | |||
|||| |||+- Enable echo on voice 0 | |||
|||| ||+-- Enable echo on voice 1 | |||
|||| |+--- Enable echo on voice 2 | |||
|||| +---- Enable echo on voice 3 | |||
|||+------ Enable echo on voice 4 | |||
||+------- Enable echo on voice 5 | |||
|+-------- Enable echo on voice 6 | |||
+--------- Enable echo on voice 7 | |||
{{Anchor|DIR}} | |||
=== DIR - Sample directory page ($5D) === | |||
7 bit 0 | |||
---- ---- | |||
DDDD DDDD | |||
|||| |||| | |||
++++-++++- Page pointer to the sample source directory | |||
{{Anchor|ESA}} | |||
=== ESA - Echo start address ($6D) === | |||
7 bit 0 | |||
---- ---- | |||
EEEE EEEE | |||
|||| |||| | |||
++++-++++- Page pointer to start of the echo buffer | |||
ERRATA: | |||
* Care must be taken when writing to the <tt>EDL</tt> and <tt>ESA</tt> echo buffer registers. | |||
* The <tt>ESA</tt> (echo buffer address) register is accessed once per sample and <tt>ESA</tt> writes can be delayed by a single sample.<ref>Anomie's S-DSP Doc: ESA register</ref> | |||
* The echo buffer wraps around the 16-bit Audio-RAM address boundary, clobbering zeropage. | |||
* The echo buffer will write a minimum 4 bytes to the start of <tt>ESA</tt>, unless echo writes are disabled in <tt>[[#FLG|FLG]]</tt>. | |||
* See <tt>[[#EDL|EDL]]</tt> errata for more details. | |||
{{Anchor|EDL}} | |||
=== EDL - Echo delay ($7D) === | |||
7 bit 0 | |||
---- ---- | |||
---- DDDD | |||
|||| | |||
++++- Echo delay time (delay = DDDD * 512 samples) | |||
<tt>EDL</tt> controls the size (<tt>DDDD</tt> * 2048 bytes) and length (<tt>DDDD</tt> * 512 samples or 16ms @ 32000Hz) of the echo buffer. | |||
ERRATA: | |||
* Care must be taken when writing to the <tt>EDL</tt> and <tt>ESA</tt> echo buffer registers. | |||
* Setting echo delay (<tt>[[#EDL|EDL]]</tt>, register $7D) to 0 continuously overwrites 4 bytes of ARAM at the start of the echo buffer page (selected by <tt>ESA</tt>, $6D). In particular, <tt>ESA</tt> = $00 and <tt>EDL</tt> = $00 overwrites zero page locations $0000-$0003. If not using echo, remember to set the echo write protect bit of <tt>[[#FLG|FLG]]</tt> ($6C bit 5) to 1. | |||
* <tt>EDL</tt> (echo delay / echo buffer size) register writes take effect when the S-DSP reaches the end of the echo buffer.<ref>Anomie's S-DSP Doc: EDL register</ref> | |||
** Writing to <tt>EDL</tt> can take up to 7680 samples (240ms @ 32000Hz) to take effect. | |||
* The <tt>ESA</tt> (echo buffer address) register is accessed once per sample and <tt>ESA</tt> writes can be delayed by a single sample.<ref>Anomie's S-DSP Doc: ESA register</ref> | |||
* The echo buffer wraps around the 16-bit Audio-RAM address boundary, clobbering zeropage. | |||
* To prevent the echo buffer from clobbering memory when initialing the echo buffer, echo buffer writes should be disabled (<tt>FLG</tt> bit 5 set) before writing to <tt>ESA</tt> and <tt>EDL</tt>. There should be a minimum 7680 sample (240ms @ 32000Hz) delay before echo buffer writes are enabled (<tt>FLG</tt> bit 5 clear). | |||
{{Anchor|FIR}} | |||
=== C0, C1, C2, C3, C4, C5, C6, C7 - Echo FIR filter coefficients ($xF) === | |||
7 bit 0 | |||
---- ---- | |||
VVVV VVVV | |||
|||| |||| | |||
++++-++++- FIR filter tap (signed) | |||
ERRATA: | |||
* The FIR filter uses a clipped-sum for the first 7 tap calculations and a clamped-sum for only the last tap. <ref>[https://sneslab.net/wiki/FIR_Filter#S-DSP_Implementation SnesLab Wiki - FIR Filter - S-DSP Implementation]</ref> | |||
* This can cause audio clicks if the FIR filter gain exceeds 0dB. | |||
* To prevent FIR clicks ensure the absolute sum of the FIR taps (S-DSP registers <tt>C0</tt>-<tt>C7</tt>) is <= 128. | |||
== Voice registers == | |||
{{Anchor|VxVOL|VxVOLL|VxVOLR}} | |||
=== VxVOLL, VxVOLR - Voice volume ($x0, $x1) === | |||
7 bit 0 | |||
---- ---- | |||
VVVV VVVV | |||
|||| |||| | |||
++++-++++- left/right channel volume (signed) | |||
{{Anchor|VxPITCH|VxPITCHL|VxPITCHR}} | |||
=== VxPITCHL, VxPITCHH - Voice pitch ($x2, $x3) === | |||
15 bit 8 7 bit 0 | |||
---- ---- ---- ---- | |||
..PP pppp pppp pppp | |||
|| |||| |||| |||| | |||
++-++++---++++-++++- Voice pitch (2.12 fixed point) | |||
{{Anchor|VxSCRN}} | |||
=== VxSCRN - Voice sample source ($x4) === | |||
7 bit 0 | |||
---- ---- | |||
SSSS SSSS | |||
|||| |||| | |||
++++-++++- DIR sample source table entry | |||
{{Anchor|VxADSR|VxADSR1|VxADSR2}} | |||
=== VxADSR1, VxADSR2 - ADSR enable and settings ($x5, $x6) === | |||
VxADSR1 | |||
7 bit 0 | |||
---- ---- | |||
EDDD AAAA | |||
|||| |||| | |||
|||| ++++- Attack rate (A) | |||
|+++------ Decay rate (D) | |||
+--------- ADSR enable | |||
VxADSR2 | |||
7 bit 0 | |||
---- ---- | |||
LLLR RRRR | |||
|||| |||| | |||
|||+-++++- Sustain rate (SR) | |||
+++------- Sustain level (SL) | |||
* If the ''ADSR enable'' bit is clear, the envelope is controlled by the <tt>[[#VxGAIN|VxGAIN]]</tt> register. | |||
* If the ''ADSR enable'' bit is set, the envelope will be changed depending on the ADSR state | |||
** '''Attack''': | |||
*** If <tt>AAAA == 15</tt>, Linear increase +1024 at a rate of 31 | |||
*** If <tt>AAAA < 15</tt>, Linear increase +32 at a rate of <tt>AAAA1</tt> (equivalent to <tt>VxGAIN == 110_AAAA1</tt>) | |||
** '''Decay''': Exponential decrease at a rate of <tt>1DDD0</tt> (equivalent to <tt>VxGAIN == 101_1DDD0</tt>) | |||
*** ''Sustain level'' controls | |||
** '''Sustain''': Exponential decrease at a rate of <tt>RRRRR</tt> (equivalent to <tt>VxGAIN == 101_RRRRR</tt>) | |||
** '''Release''': Linear decrease at a fixed rate of -8 every sample. | |||
[[File:Adsr_envelope.svg]] | |||
ERRATA: | |||
* There is a race-condition when changing the ADSR/GAIN envelope mode (bit 7 of <tt>ADSR1</tt>) in the middle of a note. If the S-DSP registers are written in the order <tt>ADSR1</tt> followed by <tt>ADSR2</tt>/<tt>GAIN</tt>, the S-DSP might read the old <tt>ADSR2</tt>/<tt>GAIN</tt> value before the <tt>ADSR2</tt>/<tt>GAIN</tt> write, potentially glitching the rest of the envelope (especially if the previous <tt>GAIN</tt> was a fixed envelope).<ref>[https://undisbeliever.net/blog/20231231-terrific-audio-driver.html#i-found-a-race-condition Terrific Audio Driver - I found a race condition]</ref> | |||
** Workaround: Write to the <tt>ADSR2</tt>/<tt>GAIN</tt> register before the <tt>ADSR1</tt> register. | |||
** Workaround: Only change the ADSR/GAIN envelope mode bit when the channel is in the release state. | |||
See: [[DSP envelopes]] | |||
{{Anchor|VxGAIN}} | |||
=== VxGAIN - GAIN envelope settings ($x7) === | |||
7 bit 0 | |||
---- ---- | |||
0VVV VVVV | |||
||| |||| | |||
+++ ++++- Fixed envelope | |||
7 bit 0 | |||
---- ---- | |||
1MMr rrrr | |||
||| |||| | |||
||+ ++++- GAIN rate | |||
++------- GAIN mode | |||
GAIN envelope is used if the <tt>[[#VxADSR1|VxADSR1]]</tt> ''ADSR enable'' bit is clear. | |||
{| class="wikitable" | |||
|+ GAIN modes | |||
! Mode bits || Name || envelope change per rate | |||
|- | |||
| <tt>0??</tt> || Fixed || envelope = <tt>VVVVVVV << 4</tt> every sample | |||
|- | |||
| <tt>100</tt> || Linear decrease || -32 | |||
|- | |||
| <tt>101</tt> || Exponential decrease || -1 - ((envelope - 1) >> 8) | |||
|- | |||
| <tt>110</tt> || Linear increase || +32 | |||
|- | |||
| <tt>111</tt> || Bent increase || if envelope < $600 (75%) { +32 } else { +8 } | |||
|- | |||
|} | |||
If the voice is in the release state, the envelope is linear decrease at a fixed rate of -8 every sample. | |||
See: [[DSP envelopes]] | |||
{{Anchor|VxENVX}} | |||
=== VxENVX - Voice envelope value ($x8, read-only) === | |||
7 bit 0 | |||
---- ---- | |||
0EEE EEEE | |||
||| |||| | |||
+++-++++- Upper 7 bits of envelope | |||
<tt>VxENVX</tt> is technically writable and not intended to be written to. The S-DSP updates this register once per sample. | |||
{{Anchor|VxOUTX}} | |||
=== VxOUTX - Voice sample value ($x9, read-only) === | |||
7 bit 0 | |||
---- ---- | |||
OOOO OOOO | |||
|||| |||| | |||
++++-++++- Upper 8 bits of current sample (signed) | |||
<tt>VxOUTX</tt> contains the upper 8 bits of the current sample after the envelope has been applied to the Gaussian Interpolation (or noise) output and before the <tt>VxVOL</tt> channel volume multiplication. | |||
<tt>VxOUTX</tt> is technically writable and not intended to be written to. The S-DSP updates this register once per sample. | |||
Revision as of 05:47, 2 November 2024
The S-DSP registers are accessed via the DSPADDR and DSPDATA S-SMP registers.
Name | Address | Bits | Type | Notes | |
---|---|---|---|---|---|
MVOLL | MVOL (L) | $0C | VVVV VVVV | RW | Left channel main volume, signed. |
MVOLR | MVOL (R) | $1C | VVVV VVVV | RW | Right channel main volume, signed. |
EVOLL | EVOL (L) | $2C | VVVV VVVV | RW | Left channel echo volume, signed. |
EVOLR | EVOL (R) | $3C | VVVV VVVV | RW | Right channel main volume, signed. |
KON | $4C | 7654 3210 | RW | Key on. Writing this with any bit set will start a new note for the corresponding voice. | |
KOFF | KOF | $5C | 7654 3210 | RW | Key off. Writing this with any bit set will put the corresponding voice into its release state. |
FLG | $6C | RMEN NNNN | RW | Flags: soft reset (R), mute all (M), echo disable (E), noise frequency (N). | |
ENDX | $7C | 7654 3210 | R | Read for end of sample flag for each channel. | |
EFB | $0D | VVVV VVVV | RW | Echo feedback, signed. | |
- | - | $1D | ---- ---- | RW | Unused. |
PMON | $2D | 7654 321- | RW | Enables pitch modulation for each channel, controlled by OUTX of the next lower channel. | |
NON | $3D | 7654 3210 | RW | For each channel, replaces the sample waveform with the noise generator output. | |
EON | $4D | 7654 3210 | RW | For each channel, sends to the echo unit. | |
DIR | $5D | DDDD DDDD | RW | Pointer to the sample source directory page at $DD00. | |
ESA | $6D | EEEE EEEE | RW | Pointer to the start of the echo memory region at $EE00. | |
EDL | $7D | ---- DDDD | RW | Echo delay time (D). | |
FIR0 | C0 | $0F | VVVV VVVV | RW | Echo filter coefficient. |
FIR1 | C1 | $1F | VVVV VVVV | RW | Echo filter coefficient. |
FIR2 | C2 | $2F | VVVV VVVV | RW | Echo filter coefficient. |
FIR3 | C3 | $3F | VVVV VVVV | RW | Echo filter coefficient. |
FIR4 | C4 | $4F | VVVV VVVV | RW | Echo filter coefficient. |
FIR5 | C5 | $5F | VVVV VVVV | RW | Echo filter coefficient. |
FIR6 | C6 | $6F | VVVV VVVV | RW | Echo filter coefficient. |
FIR7 | C7 | $7F | VVVV VVVV | RW | Echo filter coefficient. |
(table source) |
There are 8 voices, numbered 0 to 7. Each voice X has 10 registers in the range $X0-$X9.
Name | Address | Bits | Type | Notes | |
---|---|---|---|---|---|
VxVOLL | VOL (L) | $X0 | SVVV VVVV | RW | Left channel volume, signed. |
VxVOLR | VOL (R) | $X1 | SVVV VVVV | RW | Right channel volume, signed. |
VxPITCHL | P (L) | $X2 | LLLL LLLL | RW | Low 8 bits of sample pitch. |
VxPITCHH | P (H) | $X3 | --HH HHHH | RW | High 6 bits of sample pitch. |
VxSRCN | SRCN | $X4 | SSSS SSSS | RW | Selects a sample source entry from the directory (see DIR below). |
VxADSR1 | ADSR (1) | $X5 | EDDD AAAA | RW | ADSR enable (E), decay rate (D), attack rate (A). |
VxADSR2 | ADSR (2) | $X6 | SSSR RRRR | RW | Sustain level (S), sustain rate (R). |
VxGAIN | GAIN | $X7 | 0VVV VVVV 1MMV VVVV |
RW | Mode (M), value (V). |
VxENVX | ENVX | $X8 | 0VVV VVVV | R | Reads current 7-bit value of ADSR/GAIN envelope. |
VxOUTX | OUTX | $X9 | SVVV VVVV | R | Reads signed 8-bit value of current sample wave multiplied by ENVX, before applying VOL. |
(table source) |
Register types:
- RW - Readable and Writable
- R - Readable (technically writable, but not intended to be written to)
Global registers
MVOLL, MVOLR - Main volume ($0C, $1C)
7 bit 0 ---- ---- VVVV VVVV |||| |||| ++++-++++- left/right channel volume (signed)
EVOLL, EVOLR - Echo volume ($2C, $3C)
7 bit 0 ---- ---- VVVV VVVV |||| |||| ++++-++++- left/right channel volume (signed)
KON - Key on ($4C)
7 bit 0 ---- ---- 7654 3210 |||| |||| |||| |||+- key-on voice 0 |||| ||+-- key-on voice 1 |||| |+--- key-on voice 2 |||| +---- key-on voice 3 |||+------ key-on voice 4 ||+------- key-on voice 5 |+-------- key-on voice 6 +--------- key-on voice 7
Writing a 1 to a KON voice bit will:
- Set the voice's envelope to 0
- Change the voice's ADSR state to Attack
- Reset the voice's BRR decoder to the start of the sample (as determined by DIR and VxSCRN)
KON is polled every second sample.
ERRATA:[1]
- Clearing KON too early can cause the voice to not key-on.
- If a voice's KON and KOFF bits are both set; the key-on will be followed by a key-off, silencing the channel.
KOFF - Key off ($5C)
7 bit 0 ---- ---- 7654 3210 |||| |||| |||| |||+- key-off voice 0 |||| ||+-- key-off voice 1 |||| |+--- key-off voice 2 |||| +---- key-off voice 3 |||+------ key-off voice 4 ||+------- key-off voice 5 |+-------- key-off voice 6 +--------- key-off voice 7
Setting a voice bit in KON will:
- Change the voice's envelope state to Release
KOFF is polled every second sample.
ERRATA:[2]
- Clearing KOFF too early can cause the voice to not key-off.
- Clearing the KOFF bits after writing to KON might key-on, key-off or silence the channel.
- If a voice's KON and KOFF bits are both set; the key-on will be followed by a key-off, silencing the channel.
FLG - Flags register ($6C)
7 bit 0 ---- ---- RMEN NNNN |||| |||| |||+-++++- Noise frequency ||+------- Disable echo write |+-------- Mute all +--------- Soft reset
ENDX - End of sample flags ($7C, read-only)
7 bit 0 ---- ---- 7654 3210 |||| |||| |||| |||+- Voice 0 BRR block has end-flag set |||| ||+-- Voice 1 BRR block has end-flag set |||| |+--- Voice 2 BRR block has end-flag set |||| +---- Voice 3 BRR block has end-flag set |||+------ Voice 4 BRR block has end-flag set ||+------- Voice 5 BRR block has end-flag set |+-------- Voice 6 BRR block has end-flag set +--------- Voice 7 BRR block has end-flag set
The voice bits are set when the current BRR block has the end-flag set (not at the end of the BRR sample).
If the voice is recently keyed-on, the ENDX bits will be clear (even if the BRR sample is a single BRR block).[3]
ENDX is technically writable and not intended to be written to. The S-DSP updates this register 8 times per sample.
EFB - Echo feedback ($0D)
7 bit 0 ---- ---- VVVV VVVV |||| |||| ++++-++++- echo feedback (signed)
PMON - Pitch modulation enable ($2D)
7 bit 0 ---- ---- 7654 321- |||| ||| |||| ||+-- Enable pitch modulation on voice 1 |||| |+--- Enable pitch modulation on voice 2 |||| +---- Enable pitch modulation on voice 3 |||+------ Enable pitch modulation on voice 4 ||+------- Enable pitch modulation on voice 5 |+-------- Enable pitch modulation on voice 6 +--------- Enable pitch modulation on voice 7
NON - Noise enable ($3D)
7 bit 0 ---- ---- 7654 3210 |||| |||| |||| |||+- Replace voice 0 with noise generator |||| ||+-- Replace voice 1 with noise generator |||| |+--- Replace voice 2 with noise generator |||| +---- Replace voice 3 with noise generator |||+------ Replace voice 4 with noise generator ||+------- Replace voice 5 with noise generator |+-------- Replace voice 6 with noise generator +--------- Replace voice 7 with noise generator
EON - Echo enable ($3D)
7 bit 0 ---- ---- 7654 3210 |||| |||| |||| |||+- Enable echo on voice 0 |||| ||+-- Enable echo on voice 1 |||| |+--- Enable echo on voice 2 |||| +---- Enable echo on voice 3 |||+------ Enable echo on voice 4 ||+------- Enable echo on voice 5 |+-------- Enable echo on voice 6 +--------- Enable echo on voice 7
DIR - Sample directory page ($5D)
7 bit 0 ---- ---- DDDD DDDD |||| |||| ++++-++++- Page pointer to the sample source directory
ESA - Echo start address ($6D)
7 bit 0 ---- ---- EEEE EEEE |||| |||| ++++-++++- Page pointer to start of the echo buffer
ERRATA:
- Care must be taken when writing to the EDL and ESA echo buffer registers.
- The ESA (echo buffer address) register is accessed once per sample and ESA writes can be delayed by a single sample.[4]
- The echo buffer wraps around the 16-bit Audio-RAM address boundary, clobbering zeropage.
- The echo buffer will write a minimum 4 bytes to the start of ESA, unless echo writes are disabled in FLG.
- See EDL errata for more details.
EDL - Echo delay ($7D)
7 bit 0 ---- ---- ---- DDDD |||| ++++- Echo delay time (delay = DDDD * 512 samples)
EDL controls the size (DDDD * 2048 bytes) and length (DDDD * 512 samples or 16ms @ 32000Hz) of the echo buffer.
ERRATA:
- Care must be taken when writing to the EDL and ESA echo buffer registers.
- Setting echo delay (EDL, register $7D) to 0 continuously overwrites 4 bytes of ARAM at the start of the echo buffer page (selected by ESA, $6D). In particular, ESA = $00 and EDL = $00 overwrites zero page locations $0000-$0003. If not using echo, remember to set the echo write protect bit of FLG ($6C bit 5) to 1.
- EDL (echo delay / echo buffer size) register writes take effect when the S-DSP reaches the end of the echo buffer.[5]
- Writing to EDL can take up to 7680 samples (240ms @ 32000Hz) to take effect.
- The ESA (echo buffer address) register is accessed once per sample and ESA writes can be delayed by a single sample.[6]
- The echo buffer wraps around the 16-bit Audio-RAM address boundary, clobbering zeropage.
- To prevent the echo buffer from clobbering memory when initialing the echo buffer, echo buffer writes should be disabled (FLG bit 5 set) before writing to ESA and EDL. There should be a minimum 7680 sample (240ms @ 32000Hz) delay before echo buffer writes are enabled (FLG bit 5 clear).
C0, C1, C2, C3, C4, C5, C6, C7 - Echo FIR filter coefficients ($xF)
7 bit 0 ---- ---- VVVV VVVV |||| |||| ++++-++++- FIR filter tap (signed)
ERRATA:
- The FIR filter uses a clipped-sum for the first 7 tap calculations and a clamped-sum for only the last tap. [7]
- This can cause audio clicks if the FIR filter gain exceeds 0dB.
- To prevent FIR clicks ensure the absolute sum of the FIR taps (S-DSP registers C0-C7) is <= 128.
Voice registers
VxVOLL, VxVOLR - Voice volume ($x0, $x1)
7 bit 0 ---- ---- VVVV VVVV |||| |||| ++++-++++- left/right channel volume (signed)
VxPITCHL, VxPITCHH - Voice pitch ($x2, $x3)
15 bit 8 7 bit 0 ---- ---- ---- ---- ..PP pppp pppp pppp || |||| |||| |||| ++-++++---++++-++++- Voice pitch (2.12 fixed point)
VxSCRN - Voice sample source ($x4)
7 bit 0 ---- ---- SSSS SSSS |||| |||| ++++-++++- DIR sample source table entry
VxADSR1, VxADSR2 - ADSR enable and settings ($x5, $x6)
VxADSR1 7 bit 0 ---- ---- EDDD AAAA |||| |||| |||| ++++- Attack rate (A) |+++------ Decay rate (D) +--------- ADSR enable
VxADSR2 7 bit 0 ---- ---- LLLR RRRR |||| |||| |||+-++++- Sustain rate (SR) +++------- Sustain level (SL)
- If the ADSR enable bit is clear, the envelope is controlled by the VxGAIN register.
- If the ADSR enable bit is set, the envelope will be changed depending on the ADSR state
- Attack:
- If AAAA == 15, Linear increase +1024 at a rate of 31
- If AAAA < 15, Linear increase +32 at a rate of AAAA1 (equivalent to VxGAIN == 110_AAAA1)
- Decay: Exponential decrease at a rate of 1DDD0 (equivalent to VxGAIN == 101_1DDD0)
- Sustain level controls
- Sustain: Exponential decrease at a rate of RRRRR (equivalent to VxGAIN == 101_RRRRR)
- Release: Linear decrease at a fixed rate of -8 every sample.
- Attack:
ERRATA:
- There is a race-condition when changing the ADSR/GAIN envelope mode (bit 7 of ADSR1) in the middle of a note. If the S-DSP registers are written in the order ADSR1 followed by ADSR2/GAIN, the S-DSP might read the old ADSR2/GAIN value before the ADSR2/GAIN write, potentially glitching the rest of the envelope (especially if the previous GAIN was a fixed envelope).[8]
- Workaround: Write to the ADSR2/GAIN register before the ADSR1 register.
- Workaround: Only change the ADSR/GAIN envelope mode bit when the channel is in the release state.
See: DSP envelopes
VxGAIN - GAIN envelope settings ($x7)
7 bit 0 ---- ---- 0VVV VVVV ||| |||| +++ ++++- Fixed envelope 7 bit 0 ---- ---- 1MMr rrrr ||| |||| ||+ ++++- GAIN rate ++------- GAIN mode
GAIN envelope is used if the VxADSR1 ADSR enable bit is clear.
Mode bits | Name | envelope change per rate |
---|---|---|
0?? | Fixed | envelope = VVVVVVV << 4 every sample |
100 | Linear decrease | -32 |
101 | Exponential decrease | -1 - ((envelope - 1) >> 8) |
110 | Linear increase | +32 |
111 | Bent increase | if envelope < $600 (75%) { +32 } else { +8 } |
If the voice is in the release state, the envelope is linear decrease at a fixed rate of -8 every sample.
See: DSP envelopes
VxENVX - Voice envelope value ($x8, read-only)
7 bit 0 ---- ---- 0EEE EEEE ||| |||| +++-++++- Upper 7 bits of envelope
VxENVX is technically writable and not intended to be written to. The S-DSP updates this register once per sample.
VxOUTX - Voice sample value ($x9, read-only)
7 bit 0 ---- ---- OOOO OOOO |||| |||| ++++-++++- Upper 8 bits of current sample (signed)
VxOUTX contains the upper 8 bits of the current sample after the envelope has been applied to the Gaussian Interpolation (or noise) output and before the VxVOL channel volume multiplication.
VxOUTX is technically writable and not intended to be written to. The S-DSP updates this register once per sample.
References
- Anomie's S-DSP Doc
- ares source code, ares/sfc/dsp directory, by Near and ares team
- ↑ Anomie's S-DSP Doc: KON and KOFF registers
- ↑ Anomie's S-DSP Doc: KON and KOFF registers
- ↑ ares source code, ares::SuperFamicom::Dsp::voice5(), by Near and ares team
- ↑ Anomie's S-DSP Doc: ESA register
- ↑ Anomie's S-DSP Doc: EDL register
- ↑ Anomie's S-DSP Doc: ESA register
- ↑ SnesLab Wiki - FIR Filter - S-DSP Implementation
- ↑ Terrific Audio Driver - I found a race condition