============================================================================= Anomie's S-DSP Doc $Revision: 1157 $ $Date: 2007-07-12 16:39:41 -0400 (Thu, 12 Jul 2007) $ ============================================================================= The S-DSP is the actual sound generator for the SNES. It shares 64K of RAM with the SPC700, and can be poked at via SPC700 registers $00F2 and $00F3. It has an input clock running nominally at 24576000 Hz, and supplies the SPC700 1024000 Hz clock and the 8192000(?) Hz clock to the expansion port. Note that this clock has been indirectly observed to vary, with rates of anywhere from 24592800 Hz to 24645600 Hz. All SPC700 RAM access goes through the S-DSP, and the registers and IPL ROM may well be located there as well. The S-DSP takes two memory accesses in between each SPC700 memory access; this means none of the S-DSP external operations described below can occur at the same time as a SPC700 operation. Credit to libopenspc, via SNEeSe and snes9x, for much of the below information. I've since re-verified and re-interpreted much of it. Also, many thanks to blargg for most of the timing data and several more algorithms, and just incredible amounts of research. A note on terminology: "Clip" in this document refers to bit truncation, while "clamp" refers to range restriction. In C, these could be done as: v = v & 0x7fff; /* clip to 15 bits unsigned */ v = (v<0) ? 0 : ((v>0x7fff) ? 0x7fff : v); /* clamp to 15 bits unsigned */ SOUND GENERATION ================ The S-DSP can mix and output up to 8 voices to product stereo sound (later on, sound generated by a device on the cart or the expansion port device may also be mixed in, but the S-DSP has no knowledge of or control over this). Output is nominally at 32000 Hz, realistically once every 768 clock cycles (32 SPC700 cycles). At a high level, each voice generates a stereo sample: * BRR data is decoded (15-bit mono sample). * Interpolation is performed over 4 BRR samples to determine the output sample, or the noise sample is selected (15-bit mono). * Apply the volume envelope (15-bit mono sample). * Apply the VxVOL registers (16-bit stereo sample). The echo buffer also generates a sample as output from the FIR filter (15-bit stereo sample). These 9 samples are used in two ways: 1. * Mix all voices in order, clamping to 16 bits after each addition (16-bit stereo sample). * Adjust by the MVOL registers to get the main sample (16-bit stereo). * Adjust the FIR sample by the EVOL registers (16-bit stereo sample). * Mix the adjusted FIR into the total, and clamp to 16 bits (16-bit stereo sample). * Output to the DAC (16-bit stereo sample) unless FLG bit 6 applies. 2. * Mix all voices selected in EON in order, clamping to 16 bits after each addition (16-bit stereo sample). * Adjust the FIR sample by EFB (16-bit stereo sample). * Mix the adjusted FIR into the total, and clamp to 16 bits (16-bit stereo sample). * Write to the echo buffer (15-bit stereo sample, left-aligned in 16 bits) unless FLG bit 5 applies. In all cases, convert from 15- to 16-bits by adding a 0 bit on the low end, and from 16- to 15-bits by dropping the low bit. More specifically, the registers and memory are accessed as follows. Note that most register values are read once per sample output and cached internally for use as needed. Note also that the S-DSP may perform some of the "if necessary" operations unconditionally but only make use of the result "if necessary". For example, in voice processing step V2 it may load the sample pointer unconditionally, but this has no effect unless there was a loop or KON. Each voice carries out the following operations: V1. Load the current value of the VxSRCN register, if necessary. V2. Load the sample pointer (using previously loaded DIR and VxSRCN) if necessary. Load the current value of the VxPITCHL register. Load the current value of the VxADSR1 register. V3. a. Load the current value of the VxPITCHH register. Apply pitch modulation if applicable. b. Load the BRR header byte (every time), and the first of the two BRR bytes that will be decoded. c. If applicable, replace the current sample with the noise sample. Apply the volume envelope. - This is the value used for modulating the next voice's pitch, if applicable. Calculate the values for VxOUTX and VxENVX, for steps V8 and V9. Check FLG bit 7 (NOT previously loaded). Check BRR header 'e' and 'l' bits to determine if the voice ends. Handle KOFF and KON using previously loaded values. If KON, ENDX will be updated in step V7. Load VxGAIN or VxADSR2. Update the volume envelope, using previously loaded values. V4. Load and apply VxVOLL. If a new group of BRR samples is required, load the second BRR byte and decode the group of 4 BRR samples. This is definately not done when not necessary. If necessary, adjust the BRR pointer to the next block, or flag the loop address for loading next step V2 and set ENDX in step V7. Note that this setting of ENDX will not override the clearing due to KON in step V3c, if both occur during the same sample. Increment interpolation sample position as specified by pitch values. At any point from now until we next get to V3c, the next sample may be calculated using the interpolation position and BRR buffer contents. V5. Load and apply VxVOLR. The new ENDX value is prepared, and can be overwritten. Reads will not see it yet. V6. The new VxOUTX value is prepared, and can be overwritten. Reads will not see it yet. V7. The new ENDX value may now be read. The new VxENVX value is prepared, and can be overwritten. Reads will not see it yet. V8. The new VxOUTX value may now be read. V9. The new VxENVX value may now be read. The full sample generation loop is as follows. Note how the above voice process is interleaved for the 8 voices. The choice of which cycle to call "cycle 0" is semi-arbitrary. I've included the standard timing of the SPC700 timer ticks, but note that frobbing the SPC700 TEST register can change this syncronization. 0. Voice steps: 0:V5 1:V2 Tick the SPC700 Stage 1 timers, always for T2 and every 4 samples for T0 and T1. 1. Voice steps: 0:V6 1:V3 2. Voice steps: 0:V7 1:V4 3: V1 3. Voice steps: 0:V8 1:V5 2:V2 4. Voice steps: 0:V9 1:V6 2:V3 5. Voice steps: 1:V7 2:V4 4: V1 6. Voice steps: 1:V8 2:V5 3:V2 7. Voice steps: 1:V9 2:V6 3:V3 8. Voice steps: 2:V7 3:V4 5: V1 9. Voice steps: 2:V8 3:V5 4:V2 10. Voice steps: 2:V9 3:V6 4:V3 11. Voice steps: 3:V7 4:V4 6: V1 12. Voice steps: 3:V8 4:V5 5:V2 13. Voice steps: 3:V9 4:V6 5:V3 14. Voice steps: 4:V7 5:V4 7: V1 15. Voice steps: 4:V8 5:V5 6:V2 16. Voice steps: 4:V9 5:V6 6:V3 Tick the SPC700 Stage 1 timer for T2. 17. Voice steps: 0:V1 5:V7 6:V4 18. Voice steps: 5:V8 6:V5 7:V2 19. Voice steps: 5:V9 6:V6 7:V3 20. Voice steps: 1:V1 6:V7 7:V4 21. Voice steps: 0:V2 6:V8 7:V5 22. Voice steps: 0:V3a 6:V9 7:V6 Load left channel sample from the echo buffer. Load FIR0. 23. Voice steps: 7:V7 Load right channel sample from the echo buffer. Load FIR1 and FIR2. 24. Voice steps: 7:V8 Load FIR3, FIR4, and FIR5. 25. Voice steps: 0:V3b 7:V9 Load FIR6 and FIR7. 26. Load and apply MVOLL. Load and apply EVOLL. Output the left sample to the DAC. Load and apply EFB. 27. Load PMON Load and apply MVOLR. Load and apply EVOLR. Output the right sample to the DAC. 28. Load NON, EON, and DIR. Load FLG bit 5 for application to the left channel. 29. Write left channel sample to the echo buffer, if allowed. Load EDL and ESA for future use. Load FLG bit 5 again for application to the right channel. ** Clear internal KON bits for any channels keyed on in the previous 2 cycles. 30. Voice steps: 0:V3c Write right channel sample to the echo buffer, if allowed. Apply ESA using the previously loaded value. If the current echo index is 0, apply EDL using the previously loaded value. Increment the echo index, and set to 0 if it exceeds the buffer length. Update global counter. Load FLG bits 0-4 and update noise sample. ** Load KOFF and internal KON. 31. Voice steps: 0:V4 2:V1 ** These two steps (KON and KOFF related) are performed every other sample. Note that the internal KON bits are not cleared until 63 cycles after they are loaded. You could also consider the above loop to run from 0-63, with everything except these two steps repeated at T+32. Unless the SPC700 TEST register is frobbed, it is always the case that the KON/KOFF poll happens either 30 & 94 or 62 & 126 cycles after the SPC700 timer T0 and T1 tick. On power on, 62 & 126 seems to be chosen more frequently but 30 & 94 can still be chosen sometimes. On reset, either can be chosen. COUNTERS ======== The S-DSP has a global counter, which is examined by the noise sample generator and the volume envelope adjustments. The global counter counts from 0x77ff to zero, decrementing by one each sample. The noise and envelope adjustments use the following tables to determine when to perform their actions: Modulus[] = { Inf, 2048, 1536, 1280, 1024, 768, 640, 512, 384, 320, 256, 192, 160, 128, 96, 80, 64, 48, 40, 32, 24, 20, 16, 12, 10, 8, 6, 5, 4, 3, 2, 1 } Offset[] = { n/a, 0, 1040, 536, 0, 1040, 536, 0, 1040, 536, 0, 1040, 536, 0, 1040, 536, 0, 1040, 536, 0, 1040, 536, 0, 1040, 536, 0, 1040, 536, 0, 1040, 0, 0 } When (Counter + Offset[R]) % Modulus[R] is zero, the action is performed. VOLUME CONTROL & ECHO ===================== In all cases, volume samples are adjusted in a simple linear fashion: Sout = (Sin * vol) >> vol_shift. "vol_shift" is chosen to give vol an effective range of -1<=vol<1. Thus, if vol is unsigned then vol_shift is the number of bits in vol, while if vol is signed then vol_shift is one less (e.g. 8-bit signed has a vol_shift of 7). In all cases, mixed values are clamped to 16 bits. There are several layers to S-DSP volume control. First, the sample is adjusted by the volume envelope (11 bits unsigned). Then each sample is adjusted by the per-voice volume (8-bit two's complement) separately for the left and right channels (which may invert the phase of the signal). After all voices are mixed the volume is adjusted by the master volume (8-bit two's complement) separately for the left and right channels. And finally, the whole thing can be muted by the FLG register. Echo splits off the main audio path after the per-voice volume, before all enabled voices are mixed together. The final sample from the echo ring buffer (specified by ESA and EDL) is fed into the FIR filter, and that output is adjusted by the echo volume (8-bit two's complement) and mixed back into the main output (after master volume adjustment). Then (if echo write is enabled in FLG) the FIR output is adjusted by the echo feedback volume (8-bit two's complement) and mixed with all voices enabled in EON, and output into the end of the echo ring buffer. So note that if echo write is disabled, the "echo ring buffer" becomes a static sample buffer up to 0.96 seconds long. BRR DECODING ============ The input samples to the S-DSP are compressed via a method known as "bit rate reduction", compressing 16 16-bit samples into 9 byte blocks. The block format is: ssssffle 00001111 22223333 44445555 .... EEEEFFFF ssss = shift ff = filter l = loop (really "don't end") e = end (really "loop") 0000 = (D) data for sample #0 in this block, signed 2's complement ... FFFF = (D) data for sample #15 in this block, signed 2's complement While the pre-BRR samples were supposedly 16-bit, the BRR decoder seems to lose the low bit. This can be seen below, in that the input D loses a bit at the low end. The bit is 'recovered' after the VxVOLL/VxVOLR volume adjustment. The 'shift' value scales the sample data D. Values 0-12 work normally, 16-bit D=(D<>1. Values 13-15 force D to either 0x0000 or 0xF800 depending on the sign of the input D (i.e. they give the same values as 0 or F do with shift=12). Each voice has a 12-sample ring buffer for decoding BRR data, divided into 3 groups of 4 samples. BRR data is always decoded in a group of 4 samples. There are two 'active' groups, and one reserve group. When the interpolation index passes 0x4000, the ring is turned and a new group of BRR data is decoded into the new reserve group. There are 4 possible 'filters' to use in decoding the blocks. Some filters use previous samples in decoding, this does carry over between groups and blocks and is separate for each voice. Filter 0 (Direct): S(x) = D Filter 1 (15/16): S(x) = D + S(x-1) + ((-S(x-1))>>4) Filter 2 (61/32-15/16): S(x) = D + (S(x-1)<<1) + ((-((S(x-1)<<1)+S(x-1)))>>5) - S(x-2) + (S(x-2)>>4) Filter 3 (115/64-13/16): S(x) = D + (S(x-1)<<1) + ((-(S(x-1)+(S(x-1)<<2)+(S(x-1)<<3)))>>6) - S(x-2) + (((S(x-2)<<1) + S(x-2))>>4) The calculations above are preformed in some higher number of bits, clamped to 16 bits at the end and then clipped to 15 bits. This 15-bit value is the value output and the value used as S(x-1) or S(x-2) as needed for future filter iterations. Certain games do seem to depend on these exact formulas, trying to simplify will break some sound effects. If the very first block in a sample uses a filter other than Direct, the previous samples are taken from the *physical* end of the BRR ring buffer since the buffer index is reset to 0 on KON. Note that BRR decoding never stops for a voice: KOFF and FLG bit 7 don't affect it at all (they just set the envelope), and it always loops after reaching a block with 'e' set ('l' clear again just sets the envelope). KON is the only thing that actually affects a BRR decode in progress, and that simply restarts it from the beginning. Now, as for the remaining two bits. If 'e' is set for the block, the bit in ENDX is set when the block is complete and the next block will be that pointed to by the loop pointer for this sample (see DIR and VxSRCN). Also, as soon as a header is loaded with 'e' set and 'l' clear, the voice goes into the Release state and the envelope goes to 0 immediately. Due to the 12-sample buffer, the 'e' and 'l' bits of the final block can be seen before the final few samples of the penultimate block are output if the pitch rate is slow enough. The samples in the final block will never be output. When a voice is keyed on, there are 5 '0000' samples output before the first sample encoded by the BRR data. These are used to preload the BRR buffer: #0 = After the final pre-KON sample is prepared, the envelope is set to 0 and enters the Attack state, and is not updated for the next several samples. The interpolation index is reset to 0, and is not updated for the next several samples. The final pre-KON BRR decode also occurs here (which can matter if the first block of the new BRR data uses a non-Direct filter). #1 = The first '0000' sample. At step V2, the start address is read. No BRR decoding or header checks, envelope updating, or interpolation index updating is performed. #2 = At step V4, first BRR group is decoded. No envelope or interpolation index updating. #3 = At step V4, second BRR group is decoded. No envelope or interpolation index updating. #4 = At step V4, third BRR group is decoded. No envelope or interpolation index updating. #5 = Envelope updating begins. The sample output is still '0000', because of the order in which voice operations are performed. The interpolation position is still 0. #6 = Finally, we see the first data sample. The first interpolation position update is done during step V4. PITCH ADJUSTMENTS ================= The S-DSP has two methods to adjust the 'pitch' of the input sound. Each voice has a 14-bit pitch control, and for voices 1-7 this can be further tweaked by the output sample of the previous voice. The pitch adjustment is fairly simple: pitch = voice[x].PITCH; if(PMON&~NON&~1&(1<> 5) * voice[x].PITCH) >> 10; voice[x].interpolation_index += pitch; if(voice[x].interpolation_index>0x7fff) voice[x].interpolation_index = 0x7fff; In the above, remember that voice[x].PITCH is only 14 bits while the 'pitch' variable is large enough to never wrap. When determining whether a new BRR group is needed: if(voice[x].interpolation_index>=0x4000){ NextBRRGroup(x); voice[x].interpolation_index -= 0x4000; } The samples in the BRR buffer are then interpolated using a 4-point gaussian interpolation. Note that pitch adjustment does not function on noise voices (see NON) or on voice 0. The exact interpolation table from libopenspc is: // gaussian table by libopenspc static const int32 gauss[512] = { 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x003, 0x003, 0x003, 0x003, 0x003, 0x004, 0x004, 0x004, 0x004, 0x004, 0x005, 0x005, 0x005, 0x005, 0x006, 0x006, 0x006, 0x006, 0x007, 0x007, 0x007, 0x008, 0x008, 0x008, 0x009, 0x009, 0x009, 0x00A, 0x00A, 0x00A, 0x00B, 0x00B, 0x00B, 0x00C, 0x00C, 0x00D, 0x00D, 0x00E, 0x00E, 0x00F, 0x00F, 0x00F, 0x010, 0x010, 0x011, 0x011, 0x012, 0x013, 0x013, 0x014, 0x014, 0x015, 0x015, 0x016, 0x017, 0x017, 0x018, 0x018, 0x019, 0x01A, 0x01B, 0x01B, 0x01C, 0x01D, 0x01D, 0x01E, 0x01F, 0x020, 0x020, 0x021, 0x022, 0x023, 0x024, 0x024, 0x025, 0x026, 0x027, 0x028, 0x029, 0x02A, 0x02B, 0x02C, 0x02D, 0x02E, 0x02F, 0x030, 0x031, 0x032, 0x033, 0x034, 0x035, 0x036, 0x037, 0x038, 0x03A, 0x03B, 0x03C, 0x03D, 0x03E, 0x040, 0x041, 0x042, 0x043, 0x045, 0x046, 0x047, 0x049, 0x04A, 0x04C, 0x04D, 0x04E, 0x050, 0x051, 0x053, 0x054, 0x056, 0x057, 0x059, 0x05A, 0x05C, 0x05E, 0x05F, 0x061, 0x063, 0x064, 0x066, 0x068, 0x06A, 0x06B, 0x06D, 0x06F, 0x071, 0x073, 0x075, 0x076, 0x078, 0x07A, 0x07C, 0x07E, 0x080, 0x082, 0x084, 0x086, 0x089, 0x08B, 0x08D, 0x08F, 0x091, 0x093, 0x096, 0x098, 0x09A, 0x09C, 0x09F, 0x0A1, 0x0A3, 0x0A6, 0x0A8, 0x0AB, 0x0AD, 0x0AF, 0x0B2, 0x0B4, 0x0B7, 0x0BA, 0x0BC, 0x0BF, 0x0C1, 0x0C4, 0x0C7, 0x0C9, 0x0CC, 0x0CF, 0x0D2, 0x0D4, 0x0D7, 0x0DA, 0x0DD, 0x0E0, 0x0E3, 0x0E6, 0x0E9, 0x0EC, 0x0EF, 0x0F2, 0x0F5, 0x0F8, 0x0FB, 0x0FE, 0x101, 0x104, 0x107, 0x10B, 0x10E, 0x111, 0x114, 0x118, 0x11B, 0x11E, 0x122, 0x125, 0x129, 0x12C, 0x130, 0x133, 0x137, 0x13A, 0x13E, 0x141, 0x145, 0x148, 0x14C, 0x150, 0x153, 0x157, 0x15B, 0x15F, 0x162, 0x166, 0x16A, 0x16E, 0x172, 0x176, 0x17A, 0x17D, 0x181, 0x185, 0x189, 0x18D, 0x191, 0x195, 0x19A, 0x19E, 0x1A2, 0x1A6, 0x1AA, 0x1AE, 0x1B2, 0x1B7, 0x1BB, 0x1BF, 0x1C3, 0x1C8, 0x1CC, 0x1D0, 0x1D5, 0x1D9, 0x1DD, 0x1E2, 0x1E6, 0x1EB, 0x1EF, 0x1F3, 0x1F8, 0x1FC, 0x201, 0x205, 0x20A, 0x20F, 0x213, 0x218, 0x21C, 0x221, 0x226, 0x22A, 0x22F, 0x233, 0x238, 0x23D, 0x241, 0x246, 0x24B, 0x250, 0x254, 0x259, 0x25E, 0x263, 0x267, 0x26C, 0x271, 0x276, 0x27B, 0x280, 0x284, 0x289, 0x28E, 0x293, 0x298, 0x29D, 0x2A2, 0x2A6, 0x2AB, 0x2B0, 0x2B5, 0x2BA, 0x2BF, 0x2C4, 0x2C9, 0x2CE, 0x2D3, 0x2D8, 0x2DC, 0x2E1, 0x2E6, 0x2EB, 0x2F0, 0x2F5, 0x2FA, 0x2FF, 0x304, 0x309, 0x30E, 0x313, 0x318, 0x31D, 0x322, 0x326, 0x32B, 0x330, 0x335, 0x33A, 0x33F, 0x344, 0x349, 0x34E, 0x353, 0x357, 0x35C, 0x361, 0x366, 0x36B, 0x370, 0x374, 0x379, 0x37E, 0x383, 0x388, 0x38C, 0x391, 0x396, 0x39B, 0x39F, 0x3A4, 0x3A9, 0x3AD, 0x3B2, 0x3B7, 0x3BB, 0x3C0, 0x3C5, 0x3C9, 0x3CE, 0x3D2, 0x3D7, 0x3DC, 0x3E0, 0x3E5, 0x3E9, 0x3ED, 0x3F2, 0x3F6, 0x3FB, 0x3FF, 0x403, 0x408, 0x40C, 0x410, 0x415, 0x419, 0x41D, 0x421, 0x425, 0x42A, 0x42E, 0x432, 0x436, 0x43A, 0x43E, 0x442, 0x446, 0x44A, 0x44E, 0x452, 0x455, 0x459, 0x45D, 0x461, 0x465, 0x468, 0x46C, 0x470, 0x473, 0x477, 0x47A, 0x47E, 0x481, 0x485, 0x488, 0x48C, 0x48F, 0x492, 0x496, 0x499, 0x49C, 0x49F, 0x4A2, 0x4A6, 0x4A9, 0x4AC, 0x4AF, 0x4B2, 0x4B5, 0x4B7, 0x4BA, 0x4BD, 0x4C0, 0x4C3, 0x4C5, 0x4C8, 0x4CB, 0x4CD, 0x4D0, 0x4D2, 0x4D5, 0x4D7, 0x4D9, 0x4DC, 0x4DE, 0x4E0, 0x4E3, 0x4E5, 0x4E7, 0x4E9, 0x4EB, 0x4ED, 0x4EF, 0x4F1, 0x4F3, 0x4F5, 0x4F6, 0x4F8, 0x4FA, 0x4FB, 0x4FD, 0x4FF, 0x500, 0x502, 0x503, 0x504, 0x506, 0x507, 0x508, 0x50A, 0x50B, 0x50C, 0x50D, 0x50E, 0x50F, 0x510, 0x511, 0x511, 0x512, 0x513, 0x514, 0x514, 0x515, 0x516, 0x516, 0x517, 0x517, 0x517, 0x518, 0x518, 0x518, 0x518, 0x518, 0x519, 0x519 }; // 4-point gaussian interpolation i = voice[x].interpolation_index >> 12; // 0 <= i <= 4 d = (voice[x].interpolation_index >> 4) & 0xff; // 0 <= d <= 255 outx = ((gauss[255-d] * voice[x].BRRdata[i+0]) >> 11); outx += ((gauss[511-d] * voice[x].BRRdata[i+1]) >> 11); outx += ((gauss[256+d] * voice[x].BRRdata[i+2]) >> 11); // The above 3 wrap at 15 bits signed. The last is added to that, and is // clipped rather than wrapped. outx = ((outx & 0x7FFF) ^ 0x4000) - 0x4000; outx += ((gauss[ 0+d] * voice[x].BRRdata[i+3]) >> 11); CLIP15(outx); S-DSP REGISTERS =============== The S-DSP contains a number of registers, which are internally polled at various points during the 32-cycle sample generation loop and often stored internally for later use. Thus, most writes do not take effect immediately. All registers are accessed by the SPC700 setting the address in $00F2, then reading/writing $00F3. Note that the register addresses use only 7 bits: $80-$ff are read-only mirrors of $00-$7f. Any unspecified registers/bits are read/write with no known effect. On power on, most registers are uninitialized. There does seem to be something of a pattern, but it's nothing specific and seems to differ based between chips. On reset, most registers retain their previous values. Some notable exceptions: FLG will always act as if set to 0xE0 after power on or reset, even if the value read back indicates otherwise. VxENVX and VxOUTX are of course 0, since all channels are in the Release state due to FLG. And ENDX will be 0 on power on or reset, but recall that the voices are still running even when keyed off so the various bits may have been set by BRR decoding by the time you get to read it. First, the 10 per-voice registers. These occupy $00-$09, $10-$19, and so on up to $70-$79. $x0 rw VxVOLL - Left volume for Voice x $x1 rw VxVOLR - Right volume for Voice x vvvvvvvv These are the volumes of the voice in the left/right stereo channel. The value is 2's-complement, negative values invert the phase of the signal in the channel. Volume adjustment is SL = (S * VL)>>7 and SR = (S * VR)>>7. VxVOLL is accessed during voice processing step V4, cycles: 0:31 1:2 2:5 3:8 4:11 5:14 6:17 7:20 VxVOLR is accessed during voice processing step V5, cycles: 0:0 1:3 2:6 3:9 4:12 5:15 6:18 7:21 $x2 rw VxPITCHL - Pitch scaler for Voice x low byte $x3 rw VxPITCHH - Pitch scaler for Voice x high byte --pppppp pppppppp This 14-bit number adjusts the pitch of the sounds output for this voice, as the function: Fout = Fin * P / 0x1000 Considering things on the normal 12-note scale, P=0x2000 will increase the pitch by one octave, P=0x3FFF will increase by (just about) two octaves, P=0x0800 will reduce by one octave, P=0x0400 will reduce by two octaves, and so on. Note that even though the high bits of $x3 are not significant, they are still read back as written. VxPITCHL is accessed during voice processing step V2, cycles: 0:21 1:0 2:3 3:6 4:9 5:12 6:15 7:18 VxPITCHH is accessed during voice processing step V3a, cycles: 0:22 1:1 2:4 3:7 4:10 5:13 6:16 7:19 $x4 rw VxSRCN - Source number for Voice x nnnnnnnn This selects the "instrument" this voice is to play. The number set here is used as an offset into the table pointed to by DIR. Changing this while the voice is playing will have no immediate effect, but when the voice afterwards loops or is keyed on it will use the new value. VxSRCN is accessed during voice processing step V1, cycles: 0:17 1:20 2:31 3:2 4:5 5:8 6:11 7:14 $x5 rw VxADSR1 - Attack-Decay-Sustain-Release settings for Voice x (part 1) edddaaaa $x6 rw VxADSR2 - Attack-Decay-Sustain-Release settings for Voice x (part 2) lllrrrrr $x7 rw VxGAIN - Gain settings for Voice x EGGGGGGG or Emmggggg e/E = Envelope adjustment method bits. ddd = Decay rate: R=16+d*2 aaaa = Attack rate: R=a*2+1 lll = Sustain level (see note) rrrrr = Sustain rate: R=r mm = Gain mode ggggg = Gain rate: R=g GGGGGGG = Direct Gain mode gain setting: E=g*16 Note: the "lll" bits are the Sustain Level only when bit 'e' is set. If 'e' is clear, the top 3 bits of VxGAIN are used instead. These three registers give control over the volume envelope. The volume envelope is 11 bits unsigned: volume adjustment is S = (S * E)>>11. Various settings of these registers will automatically adjust the envelope after a certain number of samples, based on a counter as described above. The volume envelope adjustment has 4 states: Attack, Decay, Sustain, and Release. When the voice is keyed off or a BRR end-without-loop block is reached, the state is set to Release. When the voice is keyed on, the state is set to Attack. When the envelope is in the Release state, this overrides all settings of these registers. In this case, the counter rate R=31 (i.e. adjust every sample), and the adjustment is E-=8. The simplest method of envelope control ("Direct Gain") is available when VxADSR1 bit 7 and VxGAIN bit 7 are both clear. In this case, the volume envelope is simply E=%GGGGGGG0000, and R does not matter. The second method ("Gain", usually with one of the 4 names below) is available when VxADSR1 bit 7 is clear, but VxGAIN bit 7 is set. In this case, we have 4 options, chosen based on the 'm' bits. 00 = Linear Decrease. R=g, E-=32 01 = Exp Decrease. R=g, E-=((E-1)>>8)+1 10 = Linear Increase. R=g, E+=32 11 = Bent Increase. R=g, E+=(E<0x600)?32:8 In all cases, clip E to 0 or 0x7ff rather than wrapping. The most complex method ("ADSR") is used when VxADSR1 bit 7 is 1. You can think of this method as loading VxGAIN with different values at different times based on the value of the volume envelope. VxGAIN is not actually altered, however. Attack: If aaaa == %1111, R=31 and E+=1024. Otherwise, pretend VxGAIN = %110aaaa1. In either case, when E exceeds 0x7ff (before clamping) enter the Decay state. Decay: Pretend VxGAIN = %1011ddd0. When the upper 3 bits of E equal the Sustain Level (see above), enter the Sustain state. Sustain: Pretend VxGAIN = %101rrrrr. These updates happen even when ADSR mode is not selected. These registers are actually used to update the envelope every sample. The calculated value is used as follows: 1. If the counter specifies the envelope is to be updated, the envelope is set to the new value, clamped to 11 bits. 2. If the mode is Decay and the Sustain Level is matched, change to the Sustain state. 3. If the mode is Attack and the new value is greater than 0x7ff, change to the Decay state. 4. Save the new value, *clipped* to 11 bits, to determine the increment for GAIN Bent Increase mode next sample. Note that a negative value for the new value will result in the clipped version being greater than 0x600. VxADSR1 is accessed during voice processing step V2, cycles: 0:21 1:0 2:3 3:6 4:9 5:12 6:15 7:18 VxADSR2 and VxGAIN are accessed during voice processing step V3c, cycles: 0:30 1:1 2:4 3:7 4:10 5:13 6:16 7:19 $x8 r- VxENVX - Current envelope value for Voice X 0eeeeeee This returns the high 7 bits of the current volume envelope value (IOW, E>>4) for this voice. Note that the high bit will always be 0. Also note that (obviously) there is no way to directly determine the low 4 bits unless you're using Direct Gain. Technically, this register IS writable. But whatever value you write will be overwritten at 32000 Hz. VxENVX is updated during voice processing step V9, cycles: 0:4 1:7 2:10 3:13 4:16 5:19 6:22 7:25 However, a write up to 2 cycles earlier will overwrite the new value. $x9 r- VxOUTX - Current sample value for Voice X oooooooo This returns the high byte of the current sample for this voice, after envelope volume adjustment but before VxVOL[LR] is applied. Technically, this register IS writable. But whatever value you write will be overwritten at 32000 Hz. VxOUTX is updated during voice processing step V8, cycles: 0:3 1:6 2:9 3:12 4:15 5:18 6:21 7:24 However, a write up to 2 cycles earlier will overwrite the new value. Now, the general-purpose registers: $0c rw MVOLL - Left channel master volume $1c rw MVOLR - Right channel master volume vvvvvvvv These are the master volumes of the left/right stereo channel. The value is 2's-complement, negative values invert the phase of the channel. Volume adjustment is ML = (SL * VL)>>7 and MR = (SR * VR)>>7. MVOLL is accessed during cycle 26. MVOLR is accessed during cycle 27. $2c rw EVOLL - Left channel echo volume $3c rw EVOLR - Right channel echo volume vvvvvvvv These are the echo volumes of the left/right stereo channel. The value is 2's-complement, negative values invert the phase of the channel. This is the adjustment applied to the FIR filter outputs before mixing with the main signal (after master volume adjustment). Volume adjustment is EL = (SL * VL)>>7 and ER = (SR * VR)>>7. EVOLL is accessed during cycle 26. EVOLR is accessed during cycle 27. $4c rw KON - Key on for all voices $5c rw KOFF - Key off for all voices 76543210 Each bit of KON/KOFF corresponds to one voice. Setting 1 to the KOFF bit will transition the voice to the Release state. Thus, the envelope will decrease by 8 every sample (regardless of the VxADSR and VxGAIN settings) until it reaches 0, where it will stay until the next KON. Writing 1 to the KON bit will set the envelope to 0, the state to Attack, and will start the channel from the beginning (see DIR and VxSRCN). Note that this happens even if the channel is already playing (which may cause a click/pop), and that there are 5 'empty' samples before envelope updates and BRR decoding actually begin. These registers seem to be polled only at 16000 Hz, when every other sample is due to be output. Thus, if you write two values in close succession, usually but not always only the second value will have an effect: ; assume KOFF = 0, but no voices playing mov $f2, #$4c ; KON = 1 then KON = 2 mov $f3, #$01 ; -> *usually* only voice 2 is keyed on. If both are, mov $f3, #$02 ; voice 1 will be *2* samples ahead rather than one. and ; assume various voices playing mov $f2, #$5c ; KOFF = $ff then KOFF = 0 mov $f3, #$ff mov $f3, #$00 ; -> *usually* all voices remain playing FLG bit 7, however, is polled every sample and polled for each voice. These registers and FLG bit 7 interact as follows: 1. If FLG bit 7 or the KOFF bit for the channel is set, transition to the Release state. If FLG bit 7 is set, also set the envelope to 0. 2. If the 'internal' value of KON has the channel's bit set, perform the KON actions described above. 3. Set the 'internal' value of KON to 0. This has a number of consequences: * KON effectively takes effect 'on write', even though a non-zero value can be read back much later. KOFF and FLG.7, on the other hand, exert their influence constantly until a new value is written. * Writing KON while KOFF or FLG.7 will not result in any samples being output by the channel. The channel is keyed on, but it is turned off again 2 samples later. Since there is a 5 sample delay after KON before the channel actually beings processing, the net effect is no output. * However, if KOFF is cleared within 63 SPC700 cycles of the KON write above, the channel WILL be keyed on as normal. If KOFF is cleared betwen 64 and 127 SPC700 cycles later, the channel MIGHT be keyed on with decreasing probability depending on how many cycles before the KON/KOFF poll the KON write occurred. * Setting both KOFF and KON for a channel will turn the channel off much faster than just KOFF alone, since the KON will set the envelope to 0. This can cause a click/pop, though. KOFF and internal KON are accessed during cycle 30 every other sample. Internal KON bits are cleared during cycle 29, just before KON is accessed. $6c rw FLG - Reset, Mute, Echo-Write flags and Noise Clock rmennnnn r = When set, the S-DSP "soft-resets" itself. Mostly, this seems to mean the S-DSP acts as if KOFF=$ff and forces all envelopes to 0; echo proccessing still continues, and any remaining echo data will continue to echo and generate samples. You must clear the bit to resume normal operation. See KON/KOFF for some details. Note though that this bit is checked much more frequently than KOFF. m = When set, no sound will be output. Samples will still be decoded, echos processed, and such; just no sounds will be output. e = When set, the echo ring buffer (see ESA and EDL) will not be written. Echo processing on the buffer will continue as normal, just the buffer itself will not be updated and so the echo samples will loop forever. nnnnn = Noise frequency. This is used with the global counter to determine when to generate a new noise sample. Note that there is ony one noise source shared by all voices for which noise is enabled (see NON). On reset, this register seems to have a value resembling $E0, even though this may not be read back. At least, 'r' is 'set' so we can't key on any samples, 'e' is 'set' so the echo buffer is not being updated, and 'm' is 'set' because even whatever static data is in the echo buffer gives no sound. 'n' is '0', since the noise sample is constant until this is set non-zero. FLG bit 'r' is accessed during voice processing step V3c, cycles: 0:30 1:1 2:4 3:7 4:10 5:13 6:16 7:19 FLG bit 'e' is accessed during cycles 28 and 29. FLG bits 'n' are accessed during cycle 30. $7c r* ENDX - Voice end flags 76543210 When the BRR block including the end flag is decoded in a voice, the corresponding bit is set in this register. When the voice is keyed on (successfully or not), the corresponding bit is cleared. Any write to this register will clear ALL bits, no matter what value is written. Note that the bit is set at the START of decoding the BRR block, not at the end. Recall that BRR processing, and therefore the setting of bits in this register, continues even for voices in the Release state. On reset, all bits are cleared. ENDX is updated during voice processing step V7, cycles: 0:2 1:5 2:8 3:11 4:14 5:17 6:20 7:23 However, a write up to 2 cycles earlier will overwrite the new value. $0d rw EFB - Echo feedback volume vvvvvvvv When echo buffer write is enabled, the FIR output will be adjusted by this volume and mixed into the buffer. The value is 2's-complement, negative values invert the phase of the signal. Volume adjustment is E = (E * V)>>7. EFB is accessed during cycle 26. $2d rw PMON - Pitch modulation enable 7654321- Each bit corresponds to the corresponding voice. When the bit is set, the VxPITCH value will be adjusted by the output of the voice x-1. The exact formula seems to be: P = VxPITCH + (((OutX[x-1] >> 5) * VxPITCH) >> 10) For the purposes of pitch adjustment, a voice not playing is all zeros and thus has no effect on the pitch. PMON is accessed during cycle 27. $3d rw NON - Noise enable 76543210 Each bit corresponds to the corresponding voice. When the bit is set, the samples produced by BRR decoding will not be used. Instead, the output sample will be the current value of the noise generator (see FLG). The noise generator seems to function something like this: On reset, N=0x4000. Each update (see FLG), N=(N>>1)|(((N<<14)^(N<<13))&0x4000). And the output noise sample at any point is N (after which is volume adjustment then the left-shift to 'restore' the low bit). Note that the noise sample is not affected by VxPITCH or PMON, but VxPITCH and PMON still control the speed of BRR decoding and the end-without-loop of BRR decoding will still transition to Release (and update ENDX). NON is accessed during cycle 28. $4d rw EON - Echo enable 76543210 Each bit corresponds to the corresponding voice. When the bit is set and echo buffer write is enabled, this voice will be mixed into the sample to be written to the echo buffer for later echo processing. EON is accessed during cycle 28. $5d rw DIR - Sample table address aaaaaaaa This forms the high byte of the start address of the sample pointer table (the low byte is always 0). The sample pointer table is indexed for each voice by VxSRCN to determine which BRR data to decode and play. Each entry is 4 bytes. The first word points to the start of the BRR data, and the second word points to the 'restart' point for when the BRR end block is reached. Changing this while voices are playing will have no immediate effect, but when any voice afterwards loops or is keyed on it will use the new table. DIR is accessed during cycle 28. $6d rw ESA - Echo ring buffer address aaaaaaaa This forms the high byte of the start address of the echo ring buffer (the low byte is always 0). When echo buffer write is enabled in FLG, all voices marked in EON will be mixed together, mixed with the FIR output (adjusted by the echo feedback volume), and output into the ring buffer (4 bytes, 2 per stereo channel). And every sample, one entry (4 bytes) will be removed from the ring buffer and passed into the FIR filter. The size of the buffer is controlled by EDL. The echo buffer will wrap within 16 bits, if the ESA and EDL values combine to specify a buffer that would go beyond address $FFFF. Note that the register is accessed 32 cycles before the value is used for a write; at a sample level, this causes writes to appear to be delayed by at least a full sample before taking effect. ESA is accessed during cycle 29. $7d rw EDL - Echo delay (ring buffer size) ----dddd This controls the size of the echo ring buffer, and therefore the delay between when a sample is first output and when it enters the echo FIR filter. The size of the buffer is simply D<<11 bytes (D<<9 16-bit stereo samples), however when D=0 the buffer is 4 bytes (1 sample) rather than 0. Note that only the low 4 bits are used to determine the buffer length. The register value is only used under certain conditions: * Write the echo buffer at sample 'idx' (cycles 29 and 30) * If idx==0, set idx_max = EDL<<9 (cycle 30-ish) * Increment idx. If idx>=idx_max, idx=0 (cycle 30-ish) This means that it can take up to .25s for a newly written value to actually take effect, if the old value was 0x0f and the new value is written just after the cycle 30 in which buffer index 0 was written. EDL is accessed during cycle 29. $xf rw FIRx - Echo FIR filter coefficient X cccccccc These 8 registers specify the 8 coefficients of the 8-tap FIR filter used to calculate the echo signal. Each time a sample is generated by the voices, one sample is taken from the echo ring buffer and input to the FIR filter (this is S(x)). The FIR filter output is then mixed with the outputs of the voices to generate the output sound, and mixed with the sample being input into the echo buffer for echo feedback. Note that the echo buffer contains 15-bit samples, so the 16-bit value read must be right-shifted by one bit to get S(x). The internal calculations, however, are done in 16 bits before being converted to 15-bit again in the output. The FIR formula is: // The value is clipped when mixing samples x-1 to x-7: FIR = (int16)(S(x-1) * FIR6 >> 6 + S(x-2) * FIR5 >> 6 + S(x-3) * FIR4 >> 6 + S(x-4) * FIR3 >> 6 + S(x-5) * FIR2 >> 6 + S(x-6) * FIR1 >> 6 + S(x-7) * FIR0 >> 6); // We have overflow detection when adding the most recent sample // only: FIR = clamp16(FIR + S(x) * FIR7 >> 6); // Finally, convert back to 15-bits left-justified in the 16-bit // word: FIR = FIR & ~1; Note that the left and right stereo channels are filtered separately (no crosstalk), but with identical coefficients. FIR0 is accessed during cycle 22. FIR1 and FIR2 are accessed during cycle 23. FIR3, FIR4, and FIR5 are accessed during cycle 24. FIR6 and FIR7 are accessed during cycle 25. The echo buffer left channel is read during cycle 22, and written during cycle 29. The echo buffer right channel is read during cycle 23, and written during cycle 30.