Skip to content

Latest commit

 

History

History
639 lines (471 loc) · 22.3 KB

File metadata and controls

639 lines (471 loc) · 22.3 KB

Soundprocessingunitspu

Source: https://problemkaputt.de/psx-spx.htm Section: Soundprocessingunitspu

Sound Processing Unit (SPU) SPU Overview

SPU ADPCM Samples

SPU ADPCM Pitch

SPU Volume and ADSR Generator

SPU Voice Flags

SPU Noise Generator

SPU Control and Status Register

SPU Memory Access

SPU Interrupt

SPU Reverb Registers

SPU Reverb Formula

SPU Reverb Examples

SPU Unknown Registers

SPU ADPCM Samples

The SPU supports only ADPCM compressed samples (uncompressed samples seem to be totally unsupported; leaving apart that one can write uncompressed 16bit PCM samples to the Reverb Buffer, which can be then output at 22050Hz, as long as they aren't overwritten by the hardware).

1F801C06h+N*10h - Voice 0..23 ADPCM Start Address (R/W)

This register holds the sample start address (not the current address, ie. the register doesn't increment during playback).

15-0   Startaddress of sound in Sound buffer (in 8-byte units)

Writing to this register has no effect on the currently playing voice.

The start address is copied to the current address upon Key On.

1F801C0Eh+N*10h - Voice 0..23 ADPCM Repeat Address (R/W)

If the hardware finds an ADPCM header with Loop-Start-Bit, then it copies the current address to the repeat addresss register.

If the hardware finds an ADPCM header with Loop-Stop-Bit, then it copies the repeat addresss register setting to the current address; that, playing the current ADPCM block.

15-0  Address sample loops to at end (in 8-byte units)

Normally, repeat works automatically via the above start/stop bits, and software doesn't need to deal with the Repeat Address Register. However, reading from it may be useful to sense if the hardware has reached a start bit, and writing may be also useful in some cases, eg. to redirect a one-shot sample (with stop-bit, but without any start-bits) to a silent-loop located elsewhere in memory.

Sample Data (SPU-ADPCM)

Samples consist of one or more 16-byte blocks:

00h       Shift/Filter (reportedly same as for CDROM XA-ADPCM) (see there)
01h       Flag Bits (see below)
02h       Compressed Data (LSBs=1st Sample, MSBs=2nd Sample)
03h       Compressed Data (LSBs=3rd Sample, MSBs=4th Sample)
04h       Compressed Data (LSBs=5th Sample, MSBs=6th Sample)
...       ...
0Fh       Compressed Data (LSBs=27th Sample, MSBs=28th Sample)

Flag Bits (in 2nd byte of ADPCM Header)

0   Loop End    (0=No change, 1=Set ENDX flag and Jump to [1F801C0Eh+N*10h])
1   Loop Repeat (0=Force Release and set ADSR Level to Zero; only if Bit0=1)
2   Loop Start  (0=No change, 1=Copy current address to [1F801C0Eh+N*10h])
3-7 Unknown    (usually 0)

Possible combinations for Bit0-1 are:

Code 0 = Normal     (continue at next 16-byte block)
Code 1 = End+Mute   (jump to Loop-address, set ENDX flag, Release, Env=0000h)
Code 2 = Ignored    (same as Code 0)
Code 3 = End+Repeat (jump to Loop-address, set ENDX flag)

Looped and One-shot Samples

The Loop Start/End flags in the ADPCM Header allow to play one or more sample block(s) in a loop, that can be either all block(s) endless repeated, or only the last some block(s) of the sample.

There's no way to stop the output, so a one-shot sample must be followed by dummy block (with Loop Start/End flags both set, and all data nibbles set to zero; so that the block gets endless repeated, but doesn't produce any sound).

SPU-ADPCM vs XA-ADPCM

The PSX supports two ADPCM formats: SPU-ADPCM (as described above), and XA-ADPCM. XA-ADPCM is decompressed by the CDROM Controller, and sent directly to the sound mixer, without needing to store the data in SPU RAM, nor needing to use a Voice channel.

The actual decompression algorithm is the same for both formats. However, the XA nibbles are arranged in different order, and XA uses 2x28 nibbles per block (instead of 2x14), XA blocks can contain mono or stereo data, XA supports only two sample rates, and, XA doesn't support looping.

SPU Volume and ADSR Generator

1F801C08h+N*10h - Voice 0..23 Attack/Decay/Sustain/Release (ADSR) (32bit)

____lower 16bit (at 1F801C08h+N*10h)___________________________________
15    Attack Mode       (0=Linear, 1=Exponential)
-     Attack Direction  (Fixed, always Increase) (until Level 7FFFh)
14-10 Attack Shift      (0..1Fh = Fast..Slow)
9-8   Attack Step       (0..3 = "+7,+6,+5,+4")
-     Decay Mode        (Fixed, always Exponential)
-     Decay Direction   (Fixed, always Decrease) (until Sustain Level)
7-4   Decay Shift       (0..0Fh = Fast..Slow)
-     Decay Step        (Fixed, always "-8")
3-0   Sustain Level     (0..0Fh)  ;Level=(N+1)*800h
____upper 16bit (at 1F801C0Ah+N*10h)___________________________________
31    Sustain Mode      (0=Linear, 1=Exponential)
30    Sustain Direction (0=Increase, 1=Decrease) (until Key OFF flag)
29    Not used?         (should be zero)
28-24 Sustain Shift     (0..1Fh = Fast..Slow)
23-22 Sustain Step      (0..3 = "+7,+6,+5,+4" or "-8,-7,-6,-5") (inc/dec)
21    Release Mode      (0=Linear, 1=Exponential)
-     Release Direction (Fixed, always Decrease) (until Level 0000h)
20-16 Release Shift     (0..1Fh = Fast..Slow)
-     Release Step      (Fixed, always "-8")

The Attack phase gets started when the software sets the voice ON flag (see below), the hardware does then automatically go through Attack/Decay/Sustain, and switches from Sustain to Release when the software sets the Key OFF flag.

1F801D80h - Mainvolume left

1F801D82h - Mainvolume right

1F801C00h+N*10h - Voice 0..23 Volume Left

1F801C02h+N*10h - Voice 0..23 Volume Right

Fixed Volume Mode (when Bit15=0):

15    Must be zero      (0=Volume Mode)
0-14  Voice volume/2    (-4000h..+3FFFh = Volume -8000h..+7FFEh)

Sweep Volume Mode (when Bit15=1):

15    Must be set       (1=Sweep Mode)
14    Sweep Mode        (0=Linear, 1=Exponential)
13    Sweep Direction   (0=Increase, 1=Decrease)
12    Sweep Phase       (0=Positive, 1=Negative)
7-11  Not used?         (should be zero)
6-2   Sweep Shift       (0..1Fh = Fast..Slow)
1-0   Sweep Step        (0..3 = "+7,+6,+5,+4" or "-8,-7,-6,-5") (inc/dec)

Sweep is another Volume envelope, additionally to the ADSR volume envelope (unlike ADSR, sweep can be used for stereo effects, such like blending from left to right).

Sweep starts at the current volume (which can be set via Bit15=0, however, caution - the Bit15=0 setting isn't applied until the next 44.1kHz cycle; so setting the initial level with Bit15=0, followed by the sweep parameter with Bit15=1 works only if there's a suitable delay between the two operations). Once when sweep is started, the current volume level increases to +7FFFh, or decreases to 0000h.

Sweep Phase should be equal to the sign of the current volume (not yet tested, in the negative mode it does probably "increase" to -7FFFh?). The Phase bit seems to have no effect in Exponential Decrease mode.

1F801DB0h - CD Audio Input Volume (for normal CD-DA, and compressed XA-ADPCM)

1F801DB4h - External Audio Input Volume

0-15  Volume Left   (-8000h..+7FFFh)
16-31 Volume Right  (-8000h..+7FFFh)

Note: The CDROM controller supports additional CD volume control (including ability to convert stereo CD output to mono, or to swap left/right channels).

Envelope Operation depending on Shift/Step/Mode/Direction

AdsrCycles = 1 SHL Max(0,ShiftValue-11)
AdsrStep = StepValue SHL Max(0,11-ShiftValue)
IF exponential AND increase AND AdsrLevel>6000h THEN AdsrCycles=AdsrCycles*4
IF exponential AND decrease THEN AdsrStep=AdsrStep*AdsrLevel/8000h
Wait(AdsrCycles)              ;cycles counted at 44.1kHz clock
AdsrLevel=AdsrLevel+AdsrStep  ;saturated to 0..+7FFFh

Exponential Increase is a fake (simply changes to a slower linear increase rate at higher volume levels).

1F801C0Ch+N*10h - Voice 0..23 Current ADSR volume (R/W)

15-0  Current ADSR Volume  (0..+7FFFh) (or -8000h..+7FFFh on manual write)

Reportedly Release can go down to -1 (FFFFh), but that isn't true; and release ends at 0... or does THAT depend on an END flag found in the sample-data?

The register is read/writeable, writing allows to let the ADSR generator to "jump" to a specific volume level. But, ACTUALLY, the ADSR generator does overwrite the setting (from another internal register) whenever applying a new Step?!

1F801DB8h - Current Main Volume Left/Right

1F801E00h+voice*04h - Voice 0..23 Current Volume Left/Right

0-15  Current Volume Left  (-8000h..+7FFFh)
16-31 Current Volume Right (-8000h..+7FFFh)

These are internal registers, normally not used by software (the Volume settings are usually set via Ports 1F801D80h and 1F801C00h+N*10h).

Note

Negative volumes are phase inverted, otherwise same as positive.

SPU Noise Generator

1F801D94h - Voice 0..23 Noise mode enable (NON)

0-23  Voice 0..23 Noise (0=ADPCM, 1=Noise)
24-31 Not used

SPU Noise Generator

The signed 16bit output Level is calculated as so (repeated at 44.1kHz clock):

Wait(1 cycle)          ;at 44.1kHz clock
Timer=Timer-NoiseStep  ;subtract Step (4..7)
ParityBit = NoiseLevel.Bit15 xor Bit12 xor Bit11 xor Bit10 xor 1
IF Timer
SPU Memory Access

**1F801DA6h - Sound RAM Data Transfer Address**

15-0 Address in sound buffer divided by eight


Used for manual write and DMA read/write SPU memory. Writing to this registers
stores the written value in 1F801DA6h, and does additional store the value
(multiplied by 8) in another internal "current address" register (that internal
register does increment during transfers, whilst the 1F801DA6h value DOESN'T
increment).

**1F801DA8h - Sound RAM Data Transfer Fifo**

15-0 Data (max 32 halfwords)


Used for manual-write. Not sure if it can be also used for manual read?

**1F801DACh - Sound RAM Data Transfer Control (should be 0004h)**

15-4 Unknown/no effect? (should be zero) 3-1 Sound RAM Data Transfer Type (see below) (should be 2) 0 Unknown/no effect? (should be zero)


The Transfer Type selects how data is forwarded from Fifo to SPU RAM:

Transfer Type___Halfwords in Fifo________Halfwords written to SPU RAM 0,1,6,7 Fill A,B,C,D,E,F,G,H,...,X X,X,X,X,X,X,X,X,... 2 Normal A,B,C,D,E,F,G,H,...,X A,B,C,D,E,F,G,H,... 3 Rep2 A,B,C,D,E,F,G,H,...,X A,A,C,C,E,E,G,G,... 4 Rep4 A,B,C,D,E,F,G,H,...,X A,A,A,A,E,E,E,E,... 5 Rep8 A,B,C,D,E,F,G,H,...,X H,H,H,H,H,H,H,H,...


Rep2 skips the 2nd halfword, Rep4 skips 2nd..4th, Rep8 skips 1st..7th.

Fill uses only the LAST halfword in Fifo, that might be useful for memfill
purposes, although, the length is probably determined by the number of writes
to the Fifo (?) so one must still issue writes for ALL halfwords...?

Note:

The above rather bizarre results apply to WRITE mode. In READ mode, the
register causes the same halfword to be read 2/4/8 times (for rep2/4/8).

**SPU RAM Manual Write**

- Be sure that [1F801DACh] is set to 0004h

- Set SPUCNT to "Stop" (and wait until it is applied in SPUSTAT)

- Set the transfer address

- Write 1..32 halfword(s) to the Fifo

- Set SPUCNT to "Manual Write" (and wait until it is applied in SPUSTAT)

- Wait until Transfer Busy in SPUSTAT goes off (that, AFTER above apply-wait)

For multi-block transfers: Repeat the above last three steps (that is rarely
done by any games, but it is done by the BIOS intro; observe that waiting for
SPUCNT writes being applied in SPUSTAT won't work in that case (since SPUCNT
was already in manual write mode from previous block), so one must instead use
some hardcoded delay of at least 300h cycles; the BIOS is using a much longer
bizarre delay though).

**SPU RAM DMA-Write**

- Be sure that [1F801DACh] is set to 0004h

- Set SPUCNT to "Stop" (and wait until it is applied in SPUSTAT)

- Set the transfer address

- Set SPUCNT to "DMA Write" (and wait until it is applied in SPUSTAT)

- Start DMA4 at CPU Side (blocksize=10h, control=01000201h)

- Wait until DMA4 finishes (at CPU side)

**SPU RAM Manual-Read**

As by now, there's no known method for reading SPU RAM without using DMA.

**SPU RAM DMA-Read (stable reading, with [1F801014h].bit24-27 = nonzero)**

- Be sure that [1F801014h] is set to 220931E1h (bit24-27 MUST be nonzero)

- Be sure that [1F801DACh] is set to 0004h

- Set SPUCNT to "Stop" (and wait until it is applied in SPUSTAT)

- Set the transfer address

- Set SPUCNT to "DMA Read" (and wait until it is applied in SPUSTAT)

- Start DMA4 at CPU Side (blocksize=10h, control=01000200h)

- Wait until DMA4 finishes (at CPU side)

**SPU RAM DMA-Read (unstable reading, with [1F801014h].bit24-27 = zero)**

Below describes some dirt effects and some trickery to get around those dirt
effects.

Below problems (and workarounds) apply ONLY if [1F801014h].bit24-27 = zero. Ie. below info describes what happens when [1F801014h] is mis-initialized. Normally one should set [1F801014h]=220931E1h (and can ignore below info).


With [1F801014h].bit24-27=zero, reading SPU RAM via DMA works glitchy:

The first received halfword within each block is FFFFh. So with a DMA blocksize
of 10h words (=20h halfwords), the following is received:

1st block: FFFFh, halfwords[00h..1Eh] 2nd block: FFFFh, halfwords[20h..3Eh] etc.


that'd theoretically match the SPU Fifo Size, but, because of the inserted
FFFFh value, the last Fifo entry isn't received, ie. halfword[1Fh,3Fh] are
lost. As a workaround, one can increase the DMA blocksize to 11h words, and
then the following is received:

1st block: FFFFh, halfwords[00h..1Eh], twice halfword[1Fh] 2nd block: FFFFh, halfwords[20h..3Eh], twice halfword[3Fh] etc.


this time, all data is received, but after the transfer one must still remove
the FFFFh values, and the duplicated halfwords by software. Aside from the
<inserted> FFFFh values there are occassionaly some unstable halfwords
ORed by FFFFh (or ORed by other garbage values), this can be fixed by using
"rep2" mode, which does then receive:

1st block: FFFFh, halfwords[00h,00h,..0Eh,0Eh], triple halfword[0Fh] 2nd block: FFFFh, halfwords[10h,10h,..1Eh,1Eh], triple halfword[1Fh] etc.


again, remove the first halfword (FFFFh) and the last halfword, and, take the
duplicated halfwords ANDed together. Unstable values occur only every 32
halfwords or so (probably when the SPU is simultaneously reading ADPCM data),
but do never occur on two continous halfwords, so, even if one halfword was
ORed by garbage, the other halfword is always correct, and the result of the
ANDed halfwords is 100% stable.

Note: The unstable reading does NOT occur always, when resetting the PSX a
couple of times it does occassionally boot-up with totally stable reading,
since there is no known way to activate the stable "mode" via I/O ports, the
stable/unstable behaviour does eventually depend on internal clock
dividers/multipliers, and whether they are starting in sync with the CPU or
not.

Caution: The "rep2" trick cannot be used in combination with reverb (reverb
seems to be using the Port 1F801DACh Sound RAM Data Transfer Control, too).

SPU Reverb Registers

**Reverb Volume and Address Registers (R/W)**

Port Reg Name Type Expl. 1F801D84h spu vLOUT volume Reverb Output Volume Left 1F801D86h spu vROUT volume Reverb Output Volume Right 1F801DA2h spu mBASE base Reverb Work Area Start Address in Sound RAM 1F801DC0h rev00 dAPF1 disp Reverb APF Offset 1 1F801DC2h rev01 dAPF2 disp Reverb APF Offset 2 1F801DC4h rev02 vIIR volume Reverb Reflection Volume 1 1F801DC6h rev03 vCOMB1 volume Reverb Comb Volume 1 1F801DC8h rev04 vCOMB2 volume Reverb Comb Volume 2 1F801DCAh rev05 vCOMB3 volume Reverb Comb Volume 3 1F801DCCh rev06 vCOMB4 volume Reverb Comb Volume 4 1F801DCEh rev07 vWALL volume Reverb Reflection Volume 2 1F801DD0h rev08 vAPF1 volume Reverb APF Volume 1 1F801DD2h rev09 vAPF2 volume Reverb APF Volume 2 1F801DD4h rev0A mLSAME src/dst Reverb Same Side Reflection Address 1 Left 1F801DD6h rev0B mRSAME src/dst Reverb Same Side Reflection Address 1 Right 1F801DD8h rev0C mLCOMB1 src Reverb Comb Address 1 Left 1F801DDAh rev0D mRCOMB1 src Reverb Comb Address 1 Right 1F801DDCh rev0E mLCOMB2 src Reverb Comb Address 2 Left 1F801DDEh rev0F mRCOMB2 src Reverb Comb Address 2 Right 1F801DE0h rev10 dLSAME src Reverb Same Side Reflection Address 2 Left 1F801DE2h rev11 dRSAME src Reverb Same Side Reflection Address 2 Right 1F801DE4h rev12 mLDIFF src/dst Reverb Different Side Reflect Address 1 Left 1F801DE6h rev13 mRDIFF src/dst Reverb Different Side Reflect Address 1 Right 1F801DE8h rev14 mLCOMB3 src Reverb Comb Address 3 Left 1F801DEAh rev15 mRCOMB3 src Reverb Comb Address 3 Right 1F801DECh rev16 mLCOMB4 src Reverb Comb Address 4 Left 1F801DEEh rev17 mRCOMB4 src Reverb Comb Address 4 Right 1F801DF0h rev18 dLDIFF src Reverb Different Side Reflect Address 2 Left 1F801DF2h rev19 dRDIFF src Reverb Different Side Reflect Address 2 Right 1F801DF4h rev1A mLAPF1 src/dst Reverb APF Address 1 Left 1F801DF6h rev1B mRAPF1 src/dst Reverb APF Address 1 Right 1F801DF8h rev1C mLAPF2 src/dst Reverb APF Address 2 Left 1F801DFAh rev1D mRAPF2 src/dst Reverb APF Address 2 Right 1F801DFCh rev1E vLIN volume Reverb Input Volume Left 1F801DFEh rev1F vRIN volume Reverb Input Volume Right


All volume registers are signed 16bit (range -8000h..+7FFFh).

All src/dst/disp/base registers are addresses in SPU memory (divided by 8),
src/dst are relative to the current buffer address, the disp registers are
relative to src registers, the base register defines the start address of the
reverb buffer (the end address is fixed, at 7FFFEh). Writing a value to mBASE
does additionally set the current buffer address to that value.

**1F801D98h - Voice 0..23 Reverb mode aka Echo On (EON) (R/W)**

0-23 Voice 0..23 Destination (0=To Mixer, 1=To Mixer and to Reverb) 24-31 Not used


Sets reverb for the channel. As soon as the sample ends, the reverb for that
channel is turned off... that's fine, but WHEN does it end?

In Reverb mode, the voice seems to output BOTH normal (immediately) AND via
Reverb (delayed).

**Reverb Bits in SPUCNT Register (R/W)**

The SPUCNT register contains a Reverb Master Enable flag, and Reverb Enable
flags for External Audio input and CD Audio input.

When the Reverb Master Enable flag is cleared, the SPU stops to write any data
to the Reverb buffer (that is useful when zero-filling the reverb buffer;
ensuring that already-zero values aren't overwritten by still-nonzero values).

However, the Reverb Master Enable flag does not disable output from Reverb
buffer to the speakers (that might be useful to output uncompressed 22050Hz
samples) (otherwise, to disable the buffer output, set the Reverb Output volume
to zero and/or zerofill the reverb buffer).

SPU Reverb Examples

**Reverb Examples**

Below are some Reverb examples, showing the required memory size (ie. set Port
1F801DA2h to "(80000h-size)/8"), and the Reverb register settings for Port
1F801DC0h..1F801DFFh, ie. arranged like so:

dAPF1 dAPF2 vIIR vCOMB1 vCOMB2 vCOMB3 vCOMB4 vWALL ;1F801DC0h..CEh vAPF1 vAPF2 mLSAME mRSAME mLCOMB1 mRCOMB1 mLCOMB2 mRCOMB2 ;1F801DD0h..DEh dLSAME dRSAME mLDIFF mRDIFF mLCOMB3 mRCOMB3 mLCOMB4 mRCOMB4 ;1F801DE0h..EEh dLDIFF dRDIFF mLAPF1 mRAPF1 mLAPF2 mRAPF2 vLIN vRIN ;1F801DF0h..FEh


Also, don't forget to initialize Port 1F801D84h, 1F801D86h, 1F801D98h, and
SPUCNT, and to zerofill the Reverb Buffer (so that no garbage values are output
when activating reverb). For whatever reason, one MUST also initialize Port
1F801DACh (otherwise reverb stays off).

**Room (size=26C0h bytes)**

007Dh,005Bh,6D80h,54B8h,BED0h,0000h,0000h,BA80h 5800h,5300h,04D6h,0333h,03F0h,0227h,0374h,01EFh 0334h,01B5h,0000h,0000h,0000h,0000h,0000h,0000h 0000h,0000h,01B4h,0136h,00B8h,005Ch,8000h,8000h


**Studio Small (size=1F40h bytes)**

0033h,0025h,70F0h,4FA8h,BCE0h,4410h,C0F0h,9C00h 5280h,4EC0h,03E4h,031Bh,03A4h,02AFh,0372h,0266h 031Ch,025Dh,025Ch,018Eh,022Fh,0135h,01D2h,00B7h 018Fh,00B5h,00B4h,0080h,004Ch,0026h,8000h,8000h


**Studio Medium (size=4840h bytes)**

00B1h,007Fh,70F0h,4FA8h,BCE0h,4510h,BEF0h,B4C0h 5280h,4EC0h,0904h,076Bh,0824h,065Fh,07A2h,0616h 076Ch,05EDh,05ECh,042Eh,050Fh,0305h,0462h,02B7h 042Fh,0265h,0264h,01B2h,0100h,0080h,8000h,8000h


**Studio Large (size=6FE0h bytes)**

00E3h,00A9h,6F60h,4FA8h,BCE0h,4510h,BEF0h,A680h 5680h,52C0h,0DFBh,0B58h,0D09h,0A3Ch,0BD9h,0973h 0B59h,08DAh,08D9h,05E9h,07ECh,04B0h,06EFh,03D2h 05EAh,031Dh,031Ch,0238h,0154h,00AAh,8000h,8000h


**Hall (size=ADE0h bytes)**

01A5h,0139h,6000h,5000h,4C00h,B800h,BC00h,C000h 6000h,5C00h,15BAh,11BBh,14C2h,10BDh,11BCh,0DC1h 11C0h,0DC3h,0DC0h,09C1h,0BC4h,07C1h,0A00h,06CDh 09C2h,05C1h,05C0h,041Ah,0274h,013Ah,8000h,8000h


**Half Echo (size=3C00h bytes)**

0017h,0013h,70F0h,4FA8h,BCE0h,4510h,BEF0h,8500h 5F80h,54C0h,0371h,02AFh,02E5h,01DFh,02B0h,01D7h 0358h,026Ah,01D6h,011Eh,012Dh,00B1h,011Fh,0059h 01A0h,00E3h,0058h,0040h,0028h,0014h,8000h,8000h


**Space Echo (size=F6C0h bytes)**

033Dh,0231h,7E00h,5000h,B400h,B000h,4C00h,B000h 6000h,5400h,1ED6h,1A31h,1D14h,183Bh,1BC2h,16B2h 1A32h,15EFh,15EEh,1055h,1334h,0F2Dh,11F6h,0C5Dh 1056h,0AE1h,0AE0h,07A2h,0464h,0232h,8000h,8000h


**Chaos Echo (almost infinite) (size=18040h bytes)**

0001h,0001h,7FFFh,7FFFh,0000h,0000h,0000h,8100h 0000h,0000h,1FFFh,0FFFh,1005h,0005h,0000h,0000h 1005h,0005h,0000h,0000h,0000h,0000h,0000h,0000h 0000h,0000h,1004h,1002h,0004h,0002h,8000h,8000h


**Delay (one-shot echo) (size=18040h bytes)**

0001h,0001h,7FFFh,7FFFh,0000h,0000h,0000h,0000h 0000h,0000h,1FFFh,0FFFh,1005h,0005h,0000h,0000h 1005h,0005h,0000h,0000h,0000h,0000h,0000h,0000h 0000h,0000h,1004h,1002h,0004h,0002h,8000h,8000h


**Reverb off (size=10h dummy bytes)**

0000h,0000h,0000h,0000h,0000h,0000h,0000h,0000h 0000h,0000h,0001h,0001h,0001h,0001h,0001h,0001h 0000h,0000h,0001h,0001h,0001h,0001h,0001h,0001h 0000h,0000h,0001h,0001h,0001h,0001h,0000h,0000h


Note that the memory offsets should be 0001h here (not 0000h), otherwise
zerofilling the reverb buffer seems to fail (maybe because zero memory offsets
somehow cause the fill-value to mixed with the old value or so; that appears
even when reverb master enable is zero). Also, when not using reverb, Port
1F801D84h, 1F801D86h, 1F801D98h, and the SPUCNT reverb bits should be set to
zero.