We've just updated MediaWiki and its underlying software. If anything doesn't look or work quite right, please mention it to us. --RanAS

David Whittaker Sound Engine: Difference between revisions

From SnesLab
Jump to: navigation, search
(Noted how the note works (and minor correction to note length))
(Adding info for a second build found in World Class Rugby)
 
(39 intermediate revisions by 3 users not shown)
Line 1: Line 1:
__TOC__
__TOC__


David Whittaker Sound Engine is a sound driver for the SPC700 programmed by David Whittaker.
'''David Whittaker Sound Engine''' is a sound driver for the SPC700 programmed by David Whittaker.


Many games have multiple different builds stored in the game at once, sometimes with code differences. Almost every single game has at least one unique build not shared by any of the other games: the exceptions are...
Many games have multiple different builds stored in the game at once, sometimes with code differences. Almost every single game has at least one unique build not shared by any of the other games: the exceptions are...
* The opening logos for Lemmings 2: The Tribes and Apocalypse II
* The opening logos for ''Lemmings 2: The Tribes'' and ''Apocalypse II''
* Kick Off 3 Beta's Title Screen and Lawnmower Man/Virtual Wars' US Beta
* ''Kick Off 3'' Beta's Title Screen and ''Lawnmower Man/Virtual Wars''' US Beta
* ''Lemmings 2: The Tribes'' '  most common build and ''Riddick Bowe Boxing'''s Japanese version
 
The raw build sorting notes can be found [[David Whittaker Sound Engine/Build Sorting|here]].


These are the games where the sound engine was used:
These are the games where the sound engine was used:
{| class="wikitable sortable"
{| class="wikitable sortable"
|-
|-
! Game Name !! Music Version !! VCMD Code Location !! ROM Offset
! Game Name !! Version !! VCMD Code Location !! ROM Offset
|-
| ''Dream TV'' || 0.0 (Beta 2)<br>2.1 (all other versions) || <tt>0x098A</tt> (Beta 2)<br><tt>0x0614</tt> (all other versions) || <tt>0x0196A2</tt> (Beta 2, uncompressed)<br><tt>0x039B16</tt> (all other versions, ''RNC compressed'')
|-
| ''Krusty's Super Fun House'' || 1.0 || <tt>0x04F1</tt> || <tt>0x0E8000</tt> (all versions)
|-
| ''Kick Off/Super Kick Off'' || 1.0 || <tt>0x04E6</tt> || <tt>0x098000</tt> (all versions)
|-
| ''Batman: Revenge of the Joker'' || 2.0 || <tt>0x0610</tt> || <tt>0x068000</tt>
|-
| ''Super SWIV/Firepower 2000'' || 2.2 || <tt>0x06AD</tt> || <tt>0x158000</tt>
|-
| ''Gods'' || 2.3 || <tt>0x0500</tt>/<tt>0x0600</tt>/<tt>0x0612</tt> || <tt>0x980000</tt>/<tt>0x98AFAC</tt>/<tt>0x99D448</tt>/<tt>0x9A8000</tt> (all versions, three unique build variants, ''RNC compressed'')
|-
| ''World Class Rugby'' || 2.3 || <tt>0x0608</tt>/<tt>N/A</tt> || <tt>0x021D64</tt>/<tt>0x0B8000</tt> (all versions)
|-
| ''Battle Cars'' || 3.0 || <tt>0x0717</tt> || <tt>0x0A8000</tt>
|-
| ''Chavez/Riddick Bowe Boxing'' || 3.0 (US version)<br>3.1 (all other versions) || <tt>0x0621</tt> (Japanese version)<br><tt>0x067C</tt> (US version)<br><tt>0x067D</tt> (Chavez) || <tt>0x0D8000</tt> (all versions)/<tt>0x0E8000</tt> (all versions except ''Chavez'')<br>(three unique build variants, one per version between ''Chavez'', US & Japanese versions)
|-
| ''Lawnmower Man/Virtual Wars'' || 3.1 || <tt>0x06BD</tt> (US beta)<br><tt>0x06A4</tt> (all other versions) || <tt>0x9C8000</tt> (all versions except US beta)/<tt>0x9E8000</tt> (all versions)
|-
| ''Apocalypse II'' || 3.1 || <tt>0x05AF</tt>/<tt>0x0624</tt> || <tt>0x118000</tt>/<tt>0x168000</tt>
|-
| ''Elite Soccer/World Cup Striker'' || 3.1 || <tt>0x0601</tt> (US and European version In-Game)<br><tt>0x060B</tt> (US and Japanese version Title Screen)<br><tt>0x0704</tt> (Japanese version In-Game)<br><tt>0x070B</tt> (European version Title Screen) || <tt>0x1B8000</tt> (all versions)/<tt>0x08D500</tt> (US & Japanese version)/<tt>0x08D550</tt> (European beta version)/<tt>0x08D640</tt> (European version)<br>(Four unique build variants: two Title Screen (US/JP compared to EU) and two In-Game (US/EU compare to JP))
|-
| ''Kick Off 3'' (Beta version only) || 3.1 || <tt>0x0658</tt>/<tt>0x06BD</tt> || <tt>0x1A8000</tt>/<tt>0x1D8000</tt>
|-
| ''Lemmings 2: The Tribes'' || 3.1 || <tt>0x05AF</tt>/<tt>0x060F</tt>/<tt>0x0614</tt>/<tt>0x0621</tt>/<tt>0x0697</tt> || ''(omitted for now: there are 16 copies of the code in the ROM, with five different build variants being present: four of them are used in one instance each, that being the opening logo, the Title Screen, the Beach Tribe and the Cavelem Tribe, and the others use a single common copy of the code with modifications of constants and non-code pointers)''
|-
| ''Porky Pig's Haunted Holiday'' (Beta version only) || 3.1 || <tt>0x06CA</tt> || ''(omitted for now: there are 36 copies of the code in the ROM, none of which are unique except for the sound data and modifications of constants and non-code pointers)''
|-
| ''Shaq Fu'' || 4.0a || <tt>0x0539</tt> || <tt>0xD96408</tt> (all versions)
|-
| ''Michael Jordan: Chaos in the Windy City'' || 4.0b || <tt>0x0539</tt> || <tt>0xD2B18F</tt> (all versions)
|}
 
==Communication with the SNES==
Each command is triggered by sending the parameters to $2141-$2143 first, then the command ID to $2140. The latches are cleared afterwards on all registers, which means the same command IDs can be sent again, provided the parameters are present.
 
===Output to the SNES===
====V0.0====
There is no acknowledgement to the SNES beyond the latches being cleared.<br>
====V1.0====
On initialization, initially $2140-$2141 are sent a zero while the sound driver sets itself up. When initialization finishes, a <tt>$55</tt> is sent to $2140-$2141.<br>
After the first command ID is sent, $2140-$2142 are updated every time a command ID is sent, and $2143 is updated every timer 0 tick.<br>
When a command ID is sent, the latches are cleared, allowing the SNES to send the same command ID again. However, this also means that the parameters have to be resent if the same parameters are to be used again.
<pre>xx yy zz %00000abc</pre>
* <tt>xx</tt> is the command ID that was just sent.
* <tt>yy</tt> is a parameter that was sent to $2141 to execute the command.
* <tt>zz</tt> is the command ID that was just sent.
* <tt>%a</tt> indicates that a piece of SFX is playing on channel 7.
* <tt>%b</tt> indicates that a piece of SFX is playing on channel 8.
* <tt>%c</tt> indicates that music is playing.
 
====V2.0-V4.0====
On initialization, initially $2140-$2141 are sent a zero while the sound driver sets itself up. When initialization finishes, a <tt>$55</tt> is sent to $2140-$2141, and the internal command counter is initialized to the same value.<br>
After the first command ID is sent, $2140 is updated every time a command ID is sent, and $2141-$2143 are updated every timer 0 tick.<br>
When a command ID is sent, the latches are cleared, allowing the SNES to send the same command ID again. However, this also means that the parameters have to be resent if the same parameters are to be used again.
 
=====V2.0-V4.0a=====
<pre>xx %yyyyyyyy zz %aaaaaaaa</pre>
* <tt>xx</tt> is a counter that is incremented every time a command is received.
* <tt>%yyyyyyyy</tt> is a set of bits for each channel that is playing a note for music. ''Not used in V4.0a.''
* <tt>zz</tt> indicates whether or not music is playing. It is <tt>$FF</tt> when music is playing, and zero otherwise.
* <tt>%aaaaaaaa</tt> is a set of bits for each channel that is playing SFX. These bits are cleared only when the SFX has elapsed all of its ticks, not when the sample is done playing if non-looping. ''Not used in V4.0a.''
 
=====V4.0b=====
<pre>xx yy ?? ??</pre>
* <tt>xx</tt> is a counter that is incremented every time a command is received.
* <tt>yy</tt> indicates whether or not music is playing. It is <tt>$FF</tt> when music is playing, and zero otherwise.
 
===Command IDs (V0.0-V1.0)===
{| class="wikitable sortable"
|-
! Command ID !! Description !! Register Values & Arguments !! Minimum Version
|-
|<tt>$00</tt> || NOP || <tt>$00 ?? ?? ??</tt> || 0.0
|-
|<tt>$01</tt> || Play Music || <tt>$01 xx ?? ??</tt> || 0.0
|-
|<tt>$02</tt> || Pause Music || <tt>$02 ?? ?? ??</tt> || 0.0
|-
|<tt>$03</tt> || Continue Music || <tt>$03 ?? ?? ??</tt> || 0.0
|-
|<tt>$04</tt> || Play SFX (Channel 7) || <tt>$04 xx yz ??</tt> || 0.0
|-
|<tt>$05</tt> || Play SFX (Channel 8) || <tt>$05 xx yz ??</tt> || 0.0
|-
|<tt>$06</tt> || Fast Forward On || <tt>$06 ?? ?? ??</tt> || 0.0
|-
|<tt>$07</tt> || Fast Forward Off || <tt>$07 ?? ?? ??</tt> || 0.0
|-
|<tt>$08</tt> || Main Volume || <tt>$08 xx ?? ??</tt> || 1.0
|-
|<tt>$09-$FF</tt> || Latch Clear || <tt>$09-$FF ?? ?? ??</tt> || 0.0
|}
 
===Command IDs (V2.0-V4.0a)===
{| class="wikitable sortable"
|-
! Command ID !! Description !! Register Values & Arguments !! Minimum Version
|-
|<tt>$00</tt> || NOP || <tt>$00 ?? ?? ??</tt> || 0.0
|-
|<tt>$01</tt> || Play Music || <tt>$01 xx ?? ??</tt> || 0.0
|-
|<tt>$02</tt> || Pause Music || <tt>$02 ?? ?? ??</tt> || 0.0
|-
|<tt>$03</tt> || Continue Music || <tt>$03 ?? ?? ??</tt> || 0.0
|-
|<tt>$04</tt> || Play SFX (Channel 1) || <tt>$04 xx yz ??</tt> || 2.0
|-
|<tt>$05</tt> || Play SFX (Channel 2) || <tt>$05 xx yz ??</tt> || 2.0
|-
|<tt>$06</tt> || Play SFX (Channel 3) || <tt>$06 xx yz ??</tt> || 2.0
|-
|<tt>$07</tt> || Play SFX (Channel 4) || <tt>$07 xx yz ??</tt> || 2.0
|-
|<tt>$08</tt> || Play SFX (Channel 5) || <tt>$08 xx yz ??</tt> || 2.0
|-
|<tt>$09</tt> || Play SFX (Channel 6) || <tt>$09 xx yz ??</tt> || 2.0
|-
|<tt>$0A</tt> || Play SFX (Channel 7) || <tt>$0A xx yz ??</tt> || 2.0
|-
|-
| Dream TV || 0.0 (Beta 2)<br>2.0 (all other versions) || <tt>0x098A</tt> (Beta 2)<br><tt>0x0614</tt> (all other versions) || <tt>0x0196A2</tt> (Beta 2, uncompressed)<br><tt>0x039B16</tt> (all other versions, ''RNC compressed'')
|<tt>$0B</tt> || Play SFX (Channel 8) || <tt>$0B xx yz ??</tt> || 2.0
|-
|-
| Krusty's Super Fun House || 1.0 || <tt>0x04F1</tt> || <tt>0x0E8000</tt> (all versions)
|<tt>$0C</tt> || Fast Forward On || <tt>$0C ?? ?? ??</tt> || 2.0
|-
|-
| Kick Off/Super Kick Off || 1.0 || <tt>0x04E6</tt> || <tt>0x098000</tt> (all versions)
|<tt>$0D</tt> || Fast Forward Off || <tt>$0D ?? ?? ??</tt> || 2.0
|-
|-
| Batman: Revenge of the Joker || 2.0 || <tt>0x0610</tt> || <tt>0x068000</tt>
|<tt>$0E</tt> || Main & Echo Volume || <tt>$0E xx ?? ??</tt> || 2.0
|-
|-
| Super SWIV/Firepower 2000 || 2.1 || <tt>0x06AD</tt> || <tt>0x158000</tt>
|<tt>$0F</tt> || Echo Feedback || <tt>$0F xx ?? ??</tt> || 2.0
|-
|-
| Gods || 2.2 || <tt>0x0500</tt>/<tt>0x0600</tt>/<tt>0x0612</tt> || <tt>0x980000</tt>/<tt>0x98AFAC</tt>/<tt>0x99D448</tt>/<tt>0x9A8000</tt> (all versions, three unique build variants, ''RNC compressed'')
|<tt>$10</tt> || Go to IPL Boot Program || <tt>$10 ?? ?? ??</tt> || 2.0
|-
|-
| World Class Rugby || 2.2 || <tt>0x0608</tt> || <tt>0x0B8000</tt> (all versions)
|<tt>$11</tt> || Set Tempo || <tt>$11 xx ?? ??</tt> || 2.1
|-
|-
| Battle Cars || 3.0 || <tt>0x0717</tt> || <tt>0x0A8000</tt>
|<tt>$12</tt> || Set SFX Volume (Channel 1) || <tt>$12 ?? xy ??</tt> || 3.0
|-
|-
| Chavez/Riddick Bowe Boxing || 3.0 (all other versions)<br>3.1 (Japanese version) || <tt>0x0621</tt> (Japanese version)<br><tt>0x067C</tt> (US version)<br><tt>0x067D</tt> (Chavez) || <tt>0x0D8000</tt> (all versions)/<tt>0x0E8000</tt> (all versions except Chavez)<br>(three unique build variants, one per version between Chavez, US & Japanese versions)
|<tt>$13</tt> || Set SFX Volume (Channel 2) || <tt>$13 ?? xy ??</tt> || 3.0
|-
|-
| Lawnmower Man/Virtual Wars || 3.1 || <tt>0x06BD</tt> (US beta)<br><tt>0x06A4</tt> (all other versions) || <tt>0x9C8000</tt> (all versions except US beta)/0x9E8000 (all versions)
|<tt>$14</tt> || Set SFX Volume (Channel 3) || <tt>$14 ?? xy ??</tt> || 3.0
|-
|-
| Apocalypse II || 3.1 || <tt>0x05AF</tt>/<tt>0x0624</tt> || <tt>0x118000</tt>/<tt>0x168000</tt>
|<tt>$15</tt> || Set SFX Volume (Channel 4) || <tt>$15 ?? xy ??</tt> || 3.0
|-
|-
| Elite Soccer/World Cup Striker || 3.1 || <tt>0x0601</tt> (US and European version In-Game)<br><tt>0x060B</tt> (US and Japanese version Title Screen)<br><tt>0x0704</tt> (Japanese version In-Game)<br><tt>0x070B</tt> (European version Title Screen) || <tt>0x1B8000</tt> (all versions)/<tt>0x08D500</tt> (US & Japanese version)/<tt>0x08D550</tt> (European beta version)/<tt>0x08D640</tt> (European version)<br>(Four unique build variants: two Title Screen (US/JP compared to EU) and two In-Game (US/EU compare to JP))
|<tt>$16</tt> || Set SFX Volume (Channel 5) || <tt>$16 ?? xy ??</tt> || 3.0
|-
|-
| Kick Off 3 (Beta version only) || 3.1 || <tt>0x0658</tt>/<tt>0x06BD</tt> || 0x1A8000/0x1D8000
|<tt>$17</tt> || Set SFX Volume (Channel 6) || <tt>$17 ?? xy ??</tt> || 3.0
|-
|-
| Lemmings 2: The Tribes || 3.1 || <tt>0x05AF</tt>/<tt>0x060F</tt>/<tt>0x0614</tt>/<tt>0x0621</tt>/<tt>0x0697</tt> || ''(omitted for now: there are 16 copies of the code in the ROM, with five different build variants being present: four of them are used in one instance each, that being the opening logo, the Title Screen, the Beach Tribe and the Cavelem Tribe, and the others use a single common copy of the code with modifications of constants and non-code pointers)''
|<tt>$18</tt> || Set SFX Volume (Channel 7) || <tt>$18 ?? xy ??</tt> || 3.0
|-
|-
| Porky Pig's Haunted Holiday (Beta version only) || 3.1 || <tt>0x06CA</tt> || ''(omitted for now: there are 36 copies of the code in the ROM, none of which are unique except for the sound data and modifications of constants and non-code pointers)''
|<tt>$19</tt> || Set SFX Volume (Channel 8) || <tt>$19 ?? xy ??</tt> || 3.0
|-
|-
| Shaq Fu || 4.0a || <tt>0x0539</tt> || 0xD96408 (all versions)
|<tt>$1A</tt> || Set SFX Pitch (Channel 1) || <tt>$1A xx yy ??</tt> || 3.0
|-
|-
| Michael Jordan: Chaos in the Windy City || 4.0b || <tt>0x0539</tt> || 0xD2B18F (all versions)
|<tt>$1B</tt> || Set SFX Pitch (Channel 2) || <tt>$1B xx yy ??</tt> || 3.0
|-
|<tt>$1C</tt> || Set SFX Pitch (Channel 3) || <tt>$1C xx yy ??</tt> || 3.0
|-
|<tt>$1D</tt> || Set SFX Pitch (Channel 4) || <tt>$1D xx yy ??</tt> || 3.0
|-
|<tt>$1E</tt> || Set SFX Pitch (Channel 5) || <tt>$1E xx yy ??</tt> || 3.0
|-
|<tt>$1F</tt> || Set SFX Pitch (Channel 6) || <tt>$1F xx yy ??</tt> || 3.0
|-
|<tt>$20</tt> || Set SFX Pitch (Channel 7) || <tt>$20 xx yy ??</tt> || 3.0
|-
|<tt>$21</tt> || Set SFX Pitch (Channel 8) || <tt>$21 xx yy ??</tt> || 3.0
|-
|<tt>$22-$23</tt> || Load New Data || <tt>$22-$23 xx yy ??</tt> || 4.0a
|-
|<tt>$24-$FF</tt> || Latch Clear/Increment Counter || <tt>$24-$FF ?? ?? ??</tt> || 0.0
|}
|}
===Command IDs (V4.0b)===
{| class="wikitable sortable"
|-
! Command ID !! Description !! Register Values & Arguments
|-
|<tt>$00</tt> || NOP || <tt>$00 ?? ?? ??</tt>
|-
|<tt>$01</tt> || Play Music || <tt>$01 xx ?? ??</tt>
|-
|<tt>$02</tt> || Pause Music || <tt>$02 ?? ?? ??</tt>
|-
|<tt>$03</tt> || Continue Music || <tt>$03 ?? ?? ??</tt>
|-
|<tt>$04</tt> || Play SFX || <tt>$04 xx yz ??</tt>
|-
|<tt>$05</tt> || Stop All SFX || <tt>$05 ?? ?? ??</tt>
|-
|<tt>$06</tt> || Main Volume || <tt>$06 xx ?? ??</tt>
|-
|<tt>$07</tt> || Set Tempo || <tt>$07 xx ?? ??</tt>
|-
|<tt>$08</tt> || Fast Forward On || <tt>$08 xx ?? ??</tt>
|-
|<tt>$09</tt> || Fast Forward Off || <tt>$09 xx ?? ??</tt>
|-
|<tt>$0A</tt> || Stop Music & Load New Data || <tt>$0A ?? ?? ??</tt>
|-
|<tt>$0B</tt> || Load New Data || <tt>$0B ?? ?? ??</tt>
|-
|<tt>$0C</tt> || Panning Separation || <tt>$0C xx ?? ??</tt>
|-
|<tt>$0D-$FF</tt> || Latch Clear/Increment Counter || <tt>$0D-$FF ?? ?? ??</tt>
|}
===Latch Clear/Increment Counter===
Clears the latches on the CPUIO registers and does nothing else except increment the counter on V2.0 and up, thus making this effectively a NOP. This occurs on almost all invalid command IDs: there is exactly one exception, noted under Invalid.
===Invalid (Command <tt>$22</tt> - Porky Pig's Haunted Holiday (beta) only)===
Crashes the sound driver. The only game that can do this is Porky Pig's Haunted Holiday (beta), and it is because it jumps directly to a RET opcode on an empty stack, thus causing a stack underflow and a jump to a memory location not used for code.
===NOP (Command <tt>$00</tt>)===
Does absolutely nothing.
===Play Music (Command <tt>$01</tt>)===
<pre>$01 xx ?? ??</pre>
Stops a previous piece of music, then plays a piece of music.
* <tt>xx</tt> is the music ID.
** V1.0 and up have boundary checks that prevent invalid music IDs from being played.
The following builds don't support this command because it effectively turns it into a NOP:
* ''Apocalypse II'' (Opening logo)/''Lemmings 2: The Tribes'' (Opening logo)
* ''Kick Off 3'' (Beta) (In-Game)
* ''World Class Rugby'' (During a game)
===Pause Music (Command <tt>$02</tt>)===
Stops music, allowing it to be resumed later.
===Continue Music (Command <tt>$03</tt>)===
Resumes a paused song.
===Play SFX (Command <tt>$04-$05</tt> (V0.0-V1.0), <tt>$04-$0B</tt> (V2.0-V4.0a), <tt>$04</tt> (V4.0b))===
<pre>ii xx yz ??</pre>
Plays a piece of SFX.
* <tt>ii</tt>, the command ID used for the SFX, defines the channel used.<br>
** <u>V0.0-V1.0:</u>
*** Channels 1-6: Not supported
*** Channel 7: <tt>$04</tt>
*** Channel 8: <tt>$05</tt>
** <u>V2.0-V4.0a:</u>
*** Channel 1: <tt>$04</tt>
*** Channel 2: <tt>$05</tt>
*** Channel 3: <tt>$06</tt>
*** Channel 4: <tt>$07</tt>
*** Channel 5: <tt>$08</tt>
*** Channel 6: <tt>$09</tt>
*** Channel 7: <tt>$0A</tt>
*** Channel 8: <tt>$0B</tt>
** V4.0b instead dynamically allocates the channel that the SFX is played on across channels 5-8, and has a fixed ID of <tt>$04</tt>.
* <tt>xx</tt> defines the SFX ID. Setting the highest bit (covering IDs <tt>$80-$FF</tt>) keys off the SFX instead of playing it.
** V1.0 and up have boundary checks that prevent invalid SFX IDs from being played, and only accept <tt>$FF</tt> as an ID to key off SFX on that channel. V4.0b doesn't support <tt>$80-$FF</tt> at all.
* <tt>y</tt> defines the left volume. This is not signed.
* <tt>z</tt> defines the right volume. This is not signed.
** For both volume values, the 4-bit values are translated into the raw values for the VxVOLL/VxVOLR DSP registers via the following multipliers (in actuality, using XCNs and shift opcodes) on a per-game basis...
*** ''Dream TV'' (beta and final): '''2X'''
*** All other games: '''4X'''
*** ''Batman: Revenge of the Joker'', ''Krusty's Super Fun House'', ''Michael Jordan Chaos in the Windy City'' ''(SFX IDs <tt>$00</tt> and <tt>$4B</tt> only)'', ''Shaq-Fu'', ''Super SWIV/Firepower 2000'': '''8X'''
The following builds don't support this command:
* ''Gods'' (Ending)
====SFX Data Format====
Each SFX entry is a pointer to a 14-byte entry containing a series of parameters to utilize on a per-SFX basis. Only one channel's worth is defined here.
<pre>xx yy zz aa bb cc dd ee ff gg hh ii jj kk</pre>
* <tt>xx</tt> is the number of timer 0 ticks (times 10) to play the SFX for.
* <tt>yy</tt> is the number of timer 0 ticks (times 10) to slide pitches before resetting and doing the slide again.
* <tt>zz</tt> is a starting value for either the PITCHL DSP register or a noise frequency.
* <tt>aa</tt> is a starting value for the PITCHH DSP register.
* <tt>bb</tt> is the offset to apply to either the PITCHL DSP register or noise frequency per step.
* <tt>cc</tt> is a offset to apply to the PITCHH DSP register per step.
* <tt>dd</tt> is a direct write to the SRCN DSP register. <tt>$80</tt> and up instead cause this to use noise.
* <tt>ee</tt> modifies the pitch using RNG using a non-zero value (except for V3.0 and up, this value is any non-zero value). How it is modified depends on the version.
** V0.0-V2.0 overwrites the pitch settings and instead uses two bytes of RNG values for the pitch on a per-step basis.
** V2.1 adds a random offset of the RNG value ANDed by <tt>$3F</tt> to the high byte of the final pitch output on a per-step basis.
** V2.2 adds a random offset of the RNG value ANDed by <tt>$3F</tt> to the low byte of the starting pitch for the first step only.
** V2.3 does nothing.
** V3.0 and up apply a random offset of the RNG value ANDed by <tt>ee</tt> to the low byte of the starting pitch for the first step only.
* <tt>ff</tt> defines the sign for the pitch offset.
** <tt>$00</tt> turns off pitch slides for the SFX.
** <tt>$01-$7F</tt> apply a positive offset.
** <tt>$80-$FF</tt> apply a negative offset.
* <tt>gg</tt> is the number of timer 0 ticks (times 10) to perform the pitch slide for.
* <tt>hh</tt> is a direct write to the ADSR1 DSP register.
* <tt>ii</tt> is a direct write to the ADSR2 DSP register.
* <tt>jj</tt> is generally an endless flag, but its operation differs between versions.
** For V0.0-V4.0a, when this value is non-zero, it causes the SFX to be played endlessly until keyed off or interrupted. It also causes pitch slides to run forever.
** For V4.0b, this acts as SFX priority. If all four channels are being used for SFX and the priority of the incoming SFX is greater than the SFX currently played, then the SFX is overwritten. The SFX plays forever if this value is greater than <tt>$7F</tt>, like what is done in V0.0-V4.0a.
* <tt>kk</tt> is the number of timer 0 ticks (times 10) per step.
===Stop All SFX (Command <tt>$05</tt> (V4.0b))===
Stops all SFX from playing in channels 5-8.
===Fast Forward On (Command <tt>$06</tt> (V0.0-V1.0), <tt>$0C</tt> (V2.0-V4.0a), <tt>$08</tt> (V4.0b))===
Causes the music to play at maximum tempo... which means every ten timer 0 ticks. This is equivalent to a tempo of 256.
===Fast Forward Off (Command <tt>$07</tt> (V0.0-V1.0), <tt>$0D</tt> (V2.0-V4.0a), <tt>$09</tt> (V4.0b))===
Causes the music to play at normal tempo.
===Main & Echo Volume (Command <tt>$08</tt> (V1.0), <tt>$0E</tt> (V2.0-V4.0a), <tt>$06</tt> (V4.0b))===
<pre>ii xx ?? ??</pre>
* <tt>xx</tt> is a direct write to the MVOLL and MVOLR DSP registers.
** V2.0 and up may also set the EVOLL and EVOLR DSP registers, but only if echo is supported in that build. It is always scaled by the main volume. The scaler multiplier (actually done with shifts) is as following, sorted by build...
*** ''Batman: Revenge of the Joker'': '''1/4'''
*** ''Dream TV'': '''1/4'''
*** ''Gods'' (all builds): '''3/16'''
*** ''World Class Rugby'' (Outside of a game): '''3/16'''
*** ''Lawnmower Man/Virtual Wars'' (Driving): '''3/16'''
*** ''Lemmings 2: The Tribes'' (Cavelem Tribe): '''3/16'''
===Echo Feedback (Command <tt>$0F</tt> (V2.0-V4.0a))===
<pre>$0F xx ?? ??</pre>
* <tt>xx</tt> is a direct write to the EFB DSP register.
This is only ever supported if echo is supported in the ROM. That means only these builds support this command:
* ''Batman: Revenge of the Joker''
* ''Dream TV''
* ''Gods'' (all builds)
* ''World Class Rugby'' (Outside of a game)
* ''Lawnmower Man/Virtual Wars'' (Driving)
* ''Lemmings 2: The Tribes'' (Cavelem Tribe)
These other games have a reference for the ID, but effectively make it a NOP:
* ''Battle Cars''
* ''Riddick Bowe Boxing/Chavez'' (all versions except Japanese)
* ''Shaq-Fu''
===Go to IPL Boot Program (Command <tt>$10</tt> (V2.0-V4.0a))===
Jumps directly to the [[SPC700/IPL ROM|IPL Boot Program]].
===Set Tempo (Command <tt>$11</tt> (V2.1-V2.2, V3.0-V4.0a))===
<pre>$11 xx ?? ??</pre>
* <tt>xx</tt> is the tempo to set the song at. This overwrites the song's tempo settings.
The following builds don't support this command:
* ''World Class Rugby'' (During a game)
===Set SFX Volume (Command <tt>$12-$19</tt> (V3.0-V4.0a))===
<pre>ii ?? xy ??</pre>
* <tt>ii</tt>, the command ID used for the SFX, defines the channel used.<br>
** Channel 1: <tt>$12</tt>
** Channel 2: <tt>$13</tt>
** Channel 3: <tt>$14</tt>
** Channel 4: <tt>$15</tt>
** Channel 5: <tt>$16</tt>
** Channel 6: <tt>$17</tt>
** Channel 7: <tt>$18</tt>
** Channel 8: <tt>$19</tt>
* <tt>x</tt> defines the left volume. This is not signed.
* <tt>y</tt> defines the right volume. This is not signed.
** For both volume values, the 4-bit values are translated into the raw values for the VxVOLL/VxVOLR DSP registers via the following multipliers (in actuality, using XCNs and shift opcodes) on a per-game basis...
*** All other games: '''4X'''
*** ''Michael Jordan Chaos in the Windy City'' ''(SFX IDs <tt>$00</tt> and <tt>$4B</tt> only)'', Shaq-Fu: '''8X'''
===Set SFX Pitch (Command <tt>$1A-$21</tt> (V3.0-V4.0a))===
<pre>ii xx yy ??</pre>
* <tt>ii</tt>, the command ID used for the SFX, defines the channel used.<br>
** Channel 1: <tt>$1A</tt>
** Channel 2: <tt>$1B</tt>
** Channel 3: <tt>$1C</tt>
** Channel 4: <tt>$1D</tt>
** Channel 5: <tt>$1E</tt>
** Channel 6: <tt>$1F</tt>
** Channel 7: <tt>$20</tt>
** Channel 8: <tt>$21</tt>
* <tt>xx</tt> overwrites the PITCHL value used in the SFX data.
* <tt>yy</tt> overwrites the PITCHH value used in the SFX data.
This command is only supported in the following builds...
* ''Battle Cars''
* ''World Cup Striker'' (European version) (Title Screen)
* ''Elite Soccer/World Cup Striker'' (European version) (In-Game)
* ''Kick Off 3'' (beta) (In-Game)
* ''Kick Off 3'' (beta) (Title Screen)
* ''Porky Pig's Haunted Holiday'' (beta)
* ''Shaq-Fu''
Not all IDs are supported in these builds...
* ''Lawnmower Man/Virtual Wars'' (In-Game) ''(does not support $1A-$1D, not even in the code)''
* ''World Cup Striker (Japanese)'' (In-Game) ''(skips over $1A-$1D via a longer branding distance, though the code still exists)''
===Load New Data (Command <tt>$22-$23</tt> (V4.0a), <tt>$0B</tt> (V4.0b))===
Loads new data from the SNES using an IPL Boot ROM variant.<br>
''TODO Loading New Data (the loading protocol should be nearly identical to IPL Boot ROM with a few differences or so, since the program seems like it's using a similar variant)''
===Stop Music & Load New Data (Command <tt>$0A</tt> (V4.0b))===
Stops the music, then loads new data from the SNES using an IPL Boot ROM variant.
===Panning Separation (Command <tt>$0C</tt> (V4.0b))===
<pre>$0C xx ?? ??</pre>
* <tt>xx</tt> sets the amount that the panning will influence the left and right volumes. The maximum is zero, while the minimum is <tt>$FF</tt>, making it effectively mono.


==Song Entry==
==Song Entry==
For each song entry in an array of song definitions, one byte is defined as a starting tempo, followed by a series of pointers defined little endian style for each channel. For Dream TV Beta and Kick Off, only five pointers are defined. For Krusty's Super Fun House, only six pointers are defined. For all other games, eight pointers are defined.
For each song entry in an array of song definitions, one byte is defined as a starting tempo, followed by a series of pointers defined [[little endian]] style for each channel. For ''Dream TV'' Beta and ''Kick Off'', only five pointers are defined. For ''Krusty's Super Fun House'', only six pointers are defined. For all other games, eight pointers are defined.


<u>Pre-V2.0 (''Dream TV Beta'' and ''Kick Off'')</u>
<pre>xx yy yy zz zz aa aa bb bb cc cc</pre>
<u>Pre-V2.0 (''Krusty's Super Fun House'')</u>
<pre>xx yy yy zz zz aa aa bb bb cc cc dd dd</pre>
<u>V2.0 and up</u>
<pre>xx yy yy zz zz aa aa bb bb cc cc dd dd ee ee ff ff</pre>
<pre>xx yy yy zz zz aa aa bb bb cc cc dd dd ee ee ff ff</pre>
* <tt>xx</tt> is the starting tempo.
* <tt>xx</tt> is the starting tempo.
* <tt>yy yy</tt> is a little endian pointer to a pattern order list for channel 1.
* <tt>yy yy</tt> is a [[little endian]] pointer to a pattern order list for channel 1.
* <tt>zz zz</tt> is a little endian pointer to a pattern order list for channel 2.
* <tt>zz zz</tt> is a [[little endian]] pointer to a pattern order list for channel 2.
* <tt>aa aa</tt> is a little endian pointer to a pattern order list for channel 3.
* <tt>aa aa</tt> is a [[little endian]] pointer to a pattern order list for channel 3.
* <tt>bb bb</tt> is a little endian pointer to a pattern order list for channel 4.
* <tt>bb bb</tt> is a [[little endian]] pointer to a pattern order list for channel 4.
* <tt>cc cc</tt> is a little endian pointer to a pattern order list for channel 5.
* <tt>cc cc</tt> is a [[little endian]] pointer to a pattern order list for channel 5.
* <tt>dd dd</tt> is a little endian pointer to a pattern order list for channel 6. ''(Krusty's Super Fun House only pre-V2.0)''
* <tt>dd dd</tt> is a [[little endian]] pointer to a pattern order list for channel 6. ''(''Krusty's Super Fun House'' only pre-V2.0)''
* <tt>ee ee</tt> is a little endian pointer to a pattern order list for channel 7. ''(not supported pre-V2.0)''
* <tt>ee ee</tt> is a [[little endian]] pointer to a pattern order list for channel 7. ''(not supported pre-V2.0)''
* <tt>ff ff</tt> is a little endian pointer to a pattern order list for channel 8. ''(not supported pre-V2.0)''
* <tt>ff ff</tt> is a [[little endian]] pointer to a pattern order list for channel 8. ''(not supported pre-V2.0)''


Two builds don't support song entries at all:
Three builds don't support song entries at all:
* The opening logos for Lemmings 2: The Tribes and Apocalypse II
* The opening logos for ''Lemmings 2: The Tribes'' and ''Apocalypse II''
* Kick Off 3 Beta (In-Game)
* ''Kick Off 3'' Beta (In-Game)
* ''World Class Rugby'' (During a game)
 
''World Class Rugby'' (During a game) also doesn't support the music format at all since the code was removed.


==Pattern Order List==
==Pattern Order List==
Each channel has a list of little endian pointers on a per-channel basis. If the pointer is zero, then you jump back to the beginning of the pattern order list for that channel.
Each channel has a list of [[little endian]] pointers on a per-channel basis. If the pointer is zero, then you jump back to either the beginning of the pattern order list for that channel, or to the loop marker that was set by the $F5 VCMD.


==Instrument Format==
==Instrument Format==
The instrument format at V0.0 is N-SPC/Kankichi-kun compatible minus noise support for SRCN values above 127. Post V1.0, one of the bytes loses its usage.
The instrument format at V0.0 is N-SPC/Kankichi-kun compatible minus noise support for SRCN values above 127. Post V1.0, one of the bytes loses its usage.


The instrument format is defined as direct writes to DSP registers for the first four bytes, followed by two pitch-related bytes. They are defined in this order, from top to bottom...
The instrument format is defined as direct writes to DSP registers for the first four bytes, followed by two pitch-related bytes. They are defined like this...<br>
* SRCN
<u>V0.0-V1.0</u>
* ADSR1
<pre>xx yy zz aa bb cc</pre>
* ADSR2
<u>V2.0 and up</u>
* GAIN ''(unused byte post-V1.0)''
<pre>xx yy zz ?? bb cc</pre>
* Pitch Base Multiplier
* <tt>xx</tt> is a direct write to the VxSRCN DSP register.
* Pitch Base Fractional Multiplier (in 256ths)
* <tt>yy</tt> is a direct write to the VxADSR1 DSP register.
* <tt>zz</tt> is a direct write to the VxADSR2 DSP register.
* <tt>aa</tt> is a direct write to the VxGAIN DSP register... but only for V1.0 and older. Otherwise, it's an unused byte.
* <tt>bb</tt> is a pitch base multiplier.
* <tt>cc</tt> is a fractional pitch base multiplier, defined in 256ths.


==Voice Command Format==
==Voice Command Format==
Line 86: Line 471:
| <tt>$00-$5F</tt> || Note || || 0.0
| <tt>$00-$5F</tt> || Note || || 0.0
|-
|-
| <tt>$60-$7F</tt> || Note Length || || 0.0
| <tt>%011xxxxx</tt><br><tt>$60-$7F</tt> || Note Length || || 0.0
|-
|-
| <tt>$80-$EC</tt> || Invalid || || 0.0
| <tt>$80-$EC</tt> || Invalid || || 0.0
Line 92: Line 477:
| <tt>$ED</tt> || Instant Pitch Change to Note || <tt>xx</tt> || 4.0a only
| <tt>$ED</tt> || Instant Pitch Change to Note || <tt>xx</tt> || 4.0a only
|-
|-
| <tt>$ED</tt> || Set Master Volume || <tt>xx</tt> || 4.0b only
| <tt>$ED</tt> || Set Main Volume || <tt>xx</tt> || 4.0b only
|-
|-
| <tt>$EE</tt> || Volume Scaler by Fraction ||<tt>xx yy</tt> || 3.0
| <tt>$EE</tt> || Volume Scaler by Fraction ||<tt>xx yy</tt> || 3.0
Line 108: Line 493:
| <tt>$F4</tt> || Tempo ||<tt>xx</tt> || 0.0
| <tt>$F4</tt> || Tempo ||<tt>xx</tt> || 0.0
|-
|-
| <tt>$F5</tt> || Jump to Pattern in Order List ||<tt>xx xx</tt> || 0.0
| <tt>$F5</tt> || Jump to Pattern in Order List + Mark Loop Point ||<tt>xx xx</tt> || 0.0
|-
|-
| <tt>$F6-$F7</tt> || Invalid || || 0.0
| <tt>$F6-$F7</tt> || Invalid || || 0.0
Line 139: Line 524:
===Note Length (VCMDs <tt>$60-$7F</tt>)===
===Note Length (VCMDs <tt>$60-$7F</tt>)===
<pre>%011xxxxx</pre>
<pre>%011xxxxx</pre>
* <tt>xxxxx</tt> represents your note length (as five bits).
* <tt>%xxxxx</tt> represents your note length (as five bits).
Note lengths are defined as the number of tempo ticks plus one. Notes are keyed off either on another note or on the key off VCMD (<tt>$F9</tt>).
Note lengths are defined as the number of tempo ticks plus one. Notes are keyed off either on another note or on the key off VCMD (<tt>$F9</tt>).


===Instant Pitch Change to Note (VCMD <tt>$ED</tt>)===
===Instant Pitch Change to Note (VCMD <tt>$ED</tt> (V4.0a))===
Requires V4.0a to use. Lower versions freeze the sound driver instead.
Requires V4.0a to use. Lower versions freeze the sound driver instead.


Line 149: Line 534:
* <tt>xx</tt> defines the note to change the pitch to.
* <tt>xx</tt> defines the note to change the pitch to.


===Set Master Volume (VCMD <tt>$ED</tt>)===
===Set Main Volume (VCMD <tt>$ED</tt> (V4.0b))===
Requires V4.0b to use. Lower versions freeze the sound driver instead.
Requires V4.0b to use. Lower versions freeze the sound driver instead.


Line 156: Line 541:
* <tt>xx</tt> defines the value to directly write to the MVOL DSP registers.
* <tt>xx</tt> defines the value to directly write to the MVOL DSP registers.


<pre>$EE xx yy</pre>
===Volume Scaler by Fraction (VCMD <tt>$EE</tt>)===
===Volume Scaler by Fraction (VCMD <tt>$EE</tt>)===
Requires V3.0 or later to use. Lower versions freeze the sound driver instead.
Requires V3.0 or later to use. Lower versions freeze the sound driver instead.
Line 164: Line 548:
* <tt>xx</tt> is the numerator, and is the value to multiply the volume by. The scaling process is skipped if this is zero.
* <tt>xx</tt> is the numerator, and is the value to multiply the volume by. The scaling process is skipped if this is zero.
* <tt>yy</tt> is the denominator, and is the value to divide the multiplied volume by.
* <tt>yy</tt> is the denominator, and is the value to divide the multiplied volume by.
Put the two together, and you get the current volume multiplied by <math>x/y</math>.
Put the two together, and you get the current volume multiplied by <math>\frac{x}{y}</math>.


===Set ADSR (VCMD <tt>$EF</tt>)===
===Set ADSR (VCMD <tt>$EF</tt>)===
Line 192: Line 576:


====Pitch Envelope Format====
====Pitch Envelope Format====
Pitch envelopes are defined using a signed value. These are in units used directly for the VxPITCH DSP registers.
=====Pitch Offset Tick (CMD <tt>$00-$7F</tt>, <tt>$81-$FF</tt>)=====
If <tt>$80</tt> is defined, the envelope loops back to the beginning.
<pre>xx</pre>
* <tt>xx</tt> is a signed offset in units used directly for the VxPITCH DSP registers.
 
=====Restart Pitch Envelope (CMD <tt>$80</tt>)=====
<pre>$80</pre>
Jumps back to the beginning of the pitch envelope.


===L/R Voice Volume (VCMD <tt>$F3</tt>)===
===L/R Voice Volume (VCMD <tt>$F3</tt>)===
<pre>$F3 xx yy</pre>
<pre>$F3 xx yy</pre>
* This is a direct DSP register write to the VxVOL registers, with <tt>xx</tt> being the left volume and <tt>yy</tt> being the right volume.
* This is a direct DSP register write to the VxVOL registers, with <tt>xx</tt> being the left volume and <tt>yy</tt> being the right volume.
This VCMD's operation changes depending on the version:
* V0.0-V2.2: Just a straight write with no modifications made to the input.
* V2.3: The input values are shifted left once.
* V3.0-V4.0b: The input values are scaled by VCMD <tt>$EE</tt>, then shifted left once.
* V4.0a: The input values are scaled by the values used for VCMD <tt>$EE</tt>. The shifting operation is not done here.


===Tempo (VCMD <tt>$F4</tt>)===
===Tempo (VCMD <tt>$F4</tt>)===
<pre>$F4 xx</pre>
<pre>$F4 xx</pre>
* <tt>xx</tt> defines one tempo tick as <math>10*(256/x)</math> timer 0 ticks. Zero freezes the song except for pitch bends and envelopes, which are not affected by the tempo ticker.
* <tt>xx</tt> defines one tempo tick as <math>10*\frac{256}{x}</math> timer 0 ticks. Zero freezes the song except for pitch bends and envelopes, which are not affected by the tempo ticker.


===Jump to Pattern in Order List (VCMD <tt>$F5</tt>)===
===Jump to Pattern in Order List + Mark Loop Point (VCMD <tt>$F5</tt>)===
<pre>$F5 xx xx</pre>
<pre>$F5 xx xx</pre>
* <tt>xx xx</tt> is a little endian pointer to a pattern pointer in the order list.
* <tt>xx xx</tt> is a [[little endian]] pointer to a pattern pointer in the order list. The loop point is marked here.


===Key Off (VCMD <tt>$F8</tt>)===
===Key Off (VCMD <tt>$F8</tt>)===
Line 220: Line 614:
<pre>$FB xx</pre>
<pre>$FB xx</pre>
* <tt>xx</tt> is a signed note offset to apply for all channels for the music.
* <tt>xx</tt> is a signed note offset to apply for all channels for the music.
Global transpsition is not applied to channel 4 for V0.0-2.1.


===Absolute Transposition (VCMD <tt>$FC</tt>)===
===Absolute Transposition (VCMD <tt>$FC</tt>)===

Latest revision as of 22:05, 23 September 2023

David Whittaker Sound Engine is a sound driver for the SPC700 programmed by David Whittaker.

Many games have multiple different builds stored in the game at once, sometimes with code differences. Almost every single game has at least one unique build not shared by any of the other games: the exceptions are...

  • The opening logos for Lemmings 2: The Tribes and Apocalypse II
  • Kick Off 3 Beta's Title Screen and Lawnmower Man/Virtual Wars' US Beta
  • Lemmings 2: The Tribes ' most common build and Riddick Bowe Boxing's Japanese version

The raw build sorting notes can be found here.

These are the games where the sound engine was used:

Game Name Version VCMD Code Location ROM Offset
Dream TV 0.0 (Beta 2)
2.1 (all other versions)
0x098A (Beta 2)
0x0614 (all other versions)
0x0196A2 (Beta 2, uncompressed)
0x039B16 (all other versions, RNC compressed)
Krusty's Super Fun House 1.0 0x04F1 0x0E8000 (all versions)
Kick Off/Super Kick Off 1.0 0x04E6 0x098000 (all versions)
Batman: Revenge of the Joker 2.0 0x0610 0x068000
Super SWIV/Firepower 2000 2.2 0x06AD 0x158000
Gods 2.3 0x0500/0x0600/0x0612 0x980000/0x98AFAC/0x99D448/0x9A8000 (all versions, three unique build variants, RNC compressed)
World Class Rugby 2.3 0x0608/N/A 0x021D64/0x0B8000 (all versions)
Battle Cars 3.0 0x0717 0x0A8000
Chavez/Riddick Bowe Boxing 3.0 (US version)
3.1 (all other versions)
0x0621 (Japanese version)
0x067C (US version)
0x067D (Chavez)
0x0D8000 (all versions)/0x0E8000 (all versions except Chavez)
(three unique build variants, one per version between Chavez, US & Japanese versions)
Lawnmower Man/Virtual Wars 3.1 0x06BD (US beta)
0x06A4 (all other versions)
0x9C8000 (all versions except US beta)/0x9E8000 (all versions)
Apocalypse II 3.1 0x05AF/0x0624 0x118000/0x168000
Elite Soccer/World Cup Striker 3.1 0x0601 (US and European version In-Game)
0x060B (US and Japanese version Title Screen)
0x0704 (Japanese version In-Game)
0x070B (European version Title Screen)
0x1B8000 (all versions)/0x08D500 (US & Japanese version)/0x08D550 (European beta version)/0x08D640 (European version)
(Four unique build variants: two Title Screen (US/JP compared to EU) and two In-Game (US/EU compare to JP))
Kick Off 3 (Beta version only) 3.1 0x0658/0x06BD 0x1A8000/0x1D8000
Lemmings 2: The Tribes 3.1 0x05AF/0x060F/0x0614/0x0621/0x0697 (omitted for now: there are 16 copies of the code in the ROM, with five different build variants being present: four of them are used in one instance each, that being the opening logo, the Title Screen, the Beach Tribe and the Cavelem Tribe, and the others use a single common copy of the code with modifications of constants and non-code pointers)
Porky Pig's Haunted Holiday (Beta version only) 3.1 0x06CA (omitted for now: there are 36 copies of the code in the ROM, none of which are unique except for the sound data and modifications of constants and non-code pointers)
Shaq Fu 4.0a 0x0539 0xD96408 (all versions)
Michael Jordan: Chaos in the Windy City 4.0b 0x0539 0xD2B18F (all versions)

Communication with the SNES

Each command is triggered by sending the parameters to $2141-$2143 first, then the command ID to $2140. The latches are cleared afterwards on all registers, which means the same command IDs can be sent again, provided the parameters are present.

Output to the SNES

V0.0

There is no acknowledgement to the SNES beyond the latches being cleared.

V1.0

On initialization, initially $2140-$2141 are sent a zero while the sound driver sets itself up. When initialization finishes, a $55 is sent to $2140-$2141.
After the first command ID is sent, $2140-$2142 are updated every time a command ID is sent, and $2143 is updated every timer 0 tick.
When a command ID is sent, the latches are cleared, allowing the SNES to send the same command ID again. However, this also means that the parameters have to be resent if the same parameters are to be used again.

xx yy zz %00000abc
  • xx is the command ID that was just sent.
  • yy is a parameter that was sent to $2141 to execute the command.
  • zz is the command ID that was just sent.
  • %a indicates that a piece of SFX is playing on channel 7.
  • %b indicates that a piece of SFX is playing on channel 8.
  • %c indicates that music is playing.

V2.0-V4.0

On initialization, initially $2140-$2141 are sent a zero while the sound driver sets itself up. When initialization finishes, a $55 is sent to $2140-$2141, and the internal command counter is initialized to the same value.
After the first command ID is sent, $2140 is updated every time a command ID is sent, and $2141-$2143 are updated every timer 0 tick.
When a command ID is sent, the latches are cleared, allowing the SNES to send the same command ID again. However, this also means that the parameters have to be resent if the same parameters are to be used again.

V2.0-V4.0a
xx %yyyyyyyy zz %aaaaaaaa
  • xx is a counter that is incremented every time a command is received.
  • %yyyyyyyy is a set of bits for each channel that is playing a note for music. Not used in V4.0a.
  • zz indicates whether or not music is playing. It is $FF when music is playing, and zero otherwise.
  • %aaaaaaaa is a set of bits for each channel that is playing SFX. These bits are cleared only when the SFX has elapsed all of its ticks, not when the sample is done playing if non-looping. Not used in V4.0a.
V4.0b
xx yy ?? ??
  • xx is a counter that is incremented every time a command is received.
  • yy indicates whether or not music is playing. It is $FF when music is playing, and zero otherwise.

Command IDs (V0.0-V1.0)

Command ID Description Register Values & Arguments Minimum Version
$00 NOP $00 ?? ?? ?? 0.0
$01 Play Music $01 xx ?? ?? 0.0
$02 Pause Music $02 ?? ?? ?? 0.0
$03 Continue Music $03 ?? ?? ?? 0.0
$04 Play SFX (Channel 7) $04 xx yz ?? 0.0
$05 Play SFX (Channel 8) $05 xx yz ?? 0.0
$06 Fast Forward On $06 ?? ?? ?? 0.0
$07 Fast Forward Off $07 ?? ?? ?? 0.0
$08 Main Volume $08 xx ?? ?? 1.0
$09-$FF Latch Clear $09-$FF ?? ?? ?? 0.0

Command IDs (V2.0-V4.0a)

Command ID Description Register Values & Arguments Minimum Version
$00 NOP $00 ?? ?? ?? 0.0
$01 Play Music $01 xx ?? ?? 0.0
$02 Pause Music $02 ?? ?? ?? 0.0
$03 Continue Music $03 ?? ?? ?? 0.0
$04 Play SFX (Channel 1) $04 xx yz ?? 2.0
$05 Play SFX (Channel 2) $05 xx yz ?? 2.0
$06 Play SFX (Channel 3) $06 xx yz ?? 2.0
$07 Play SFX (Channel 4) $07 xx yz ?? 2.0
$08 Play SFX (Channel 5) $08 xx yz ?? 2.0
$09 Play SFX (Channel 6) $09 xx yz ?? 2.0
$0A Play SFX (Channel 7) $0A xx yz ?? 2.0
$0B Play SFX (Channel 8) $0B xx yz ?? 2.0
$0C Fast Forward On $0C ?? ?? ?? 2.0
$0D Fast Forward Off $0D ?? ?? ?? 2.0
$0E Main & Echo Volume $0E xx ?? ?? 2.0
$0F Echo Feedback $0F xx ?? ?? 2.0
$10 Go to IPL Boot Program $10 ?? ?? ?? 2.0
$11 Set Tempo $11 xx ?? ?? 2.1
$12 Set SFX Volume (Channel 1) $12 ?? xy ?? 3.0
$13 Set SFX Volume (Channel 2) $13 ?? xy ?? 3.0
$14 Set SFX Volume (Channel 3) $14 ?? xy ?? 3.0
$15 Set SFX Volume (Channel 4) $15 ?? xy ?? 3.0
$16 Set SFX Volume (Channel 5) $16 ?? xy ?? 3.0
$17 Set SFX Volume (Channel 6) $17 ?? xy ?? 3.0
$18 Set SFX Volume (Channel 7) $18 ?? xy ?? 3.0
$19 Set SFX Volume (Channel 8) $19 ?? xy ?? 3.0
$1A Set SFX Pitch (Channel 1) $1A xx yy ?? 3.0
$1B Set SFX Pitch (Channel 2) $1B xx yy ?? 3.0
$1C Set SFX Pitch (Channel 3) $1C xx yy ?? 3.0
$1D Set SFX Pitch (Channel 4) $1D xx yy ?? 3.0
$1E Set SFX Pitch (Channel 5) $1E xx yy ?? 3.0
$1F Set SFX Pitch (Channel 6) $1F xx yy ?? 3.0
$20 Set SFX Pitch (Channel 7) $20 xx yy ?? 3.0
$21 Set SFX Pitch (Channel 8) $21 xx yy ?? 3.0
$22-$23 Load New Data $22-$23 xx yy ?? 4.0a
$24-$FF Latch Clear/Increment Counter $24-$FF ?? ?? ?? 0.0

Command IDs (V4.0b)

Command ID Description Register Values & Arguments
$00 NOP $00 ?? ?? ??
$01 Play Music $01 xx ?? ??
$02 Pause Music $02 ?? ?? ??
$03 Continue Music $03 ?? ?? ??
$04 Play SFX $04 xx yz ??
$05 Stop All SFX $05 ?? ?? ??
$06 Main Volume $06 xx ?? ??
$07 Set Tempo $07 xx ?? ??
$08 Fast Forward On $08 xx ?? ??
$09 Fast Forward Off $09 xx ?? ??
$0A Stop Music & Load New Data $0A ?? ?? ??
$0B Load New Data $0B ?? ?? ??
$0C Panning Separation $0C xx ?? ??
$0D-$FF Latch Clear/Increment Counter $0D-$FF ?? ?? ??

Latch Clear/Increment Counter

Clears the latches on the CPUIO registers and does nothing else except increment the counter on V2.0 and up, thus making this effectively a NOP. This occurs on almost all invalid command IDs: there is exactly one exception, noted under Invalid.

Invalid (Command $22 - Porky Pig's Haunted Holiday (beta) only)

Crashes the sound driver. The only game that can do this is Porky Pig's Haunted Holiday (beta), and it is because it jumps directly to a RET opcode on an empty stack, thus causing a stack underflow and a jump to a memory location not used for code.

NOP (Command $00)

Does absolutely nothing.

Play Music (Command $01)

$01 xx ?? ??

Stops a previous piece of music, then plays a piece of music.

  • xx is the music ID.
    • V1.0 and up have boundary checks that prevent invalid music IDs from being played.

The following builds don't support this command because it effectively turns it into a NOP:

  • Apocalypse II (Opening logo)/Lemmings 2: The Tribes (Opening logo)
  • Kick Off 3 (Beta) (In-Game)
  • World Class Rugby (During a game)

Pause Music (Command $02)

Stops music, allowing it to be resumed later.

Continue Music (Command $03)

Resumes a paused song.

Play SFX (Command $04-$05 (V0.0-V1.0), $04-$0B (V2.0-V4.0a), $04 (V4.0b))

ii xx yz ??

Plays a piece of SFX.

  • ii, the command ID used for the SFX, defines the channel used.
    • V0.0-V1.0:
      • Channels 1-6: Not supported
      • Channel 7: $04
      • Channel 8: $05
    • V2.0-V4.0a:
      • Channel 1: $04
      • Channel 2: $05
      • Channel 3: $06
      • Channel 4: $07
      • Channel 5: $08
      • Channel 6: $09
      • Channel 7: $0A
      • Channel 8: $0B
    • V4.0b instead dynamically allocates the channel that the SFX is played on across channels 5-8, and has a fixed ID of $04.
  • xx defines the SFX ID. Setting the highest bit (covering IDs $80-$FF) keys off the SFX instead of playing it.
    • V1.0 and up have boundary checks that prevent invalid SFX IDs from being played, and only accept $FF as an ID to key off SFX on that channel. V4.0b doesn't support $80-$FF at all.
  • y defines the left volume. This is not signed.
  • z defines the right volume. This is not signed.
    • For both volume values, the 4-bit values are translated into the raw values for the VxVOLL/VxVOLR DSP registers via the following multipliers (in actuality, using XCNs and shift opcodes) on a per-game basis...
      • Dream TV (beta and final): 2X
      • All other games: 4X
      • Batman: Revenge of the Joker, Krusty's Super Fun House, Michael Jordan Chaos in the Windy City (SFX IDs $00 and $4B only), Shaq-Fu, Super SWIV/Firepower 2000: 8X

The following builds don't support this command:

  • Gods (Ending)

SFX Data Format

Each SFX entry is a pointer to a 14-byte entry containing a series of parameters to utilize on a per-SFX basis. Only one channel's worth is defined here.

xx yy zz aa bb cc dd ee ff gg hh ii jj kk
  • xx is the number of timer 0 ticks (times 10) to play the SFX for.
  • yy is the number of timer 0 ticks (times 10) to slide pitches before resetting and doing the slide again.
  • zz is a starting value for either the PITCHL DSP register or a noise frequency.
  • aa is a starting value for the PITCHH DSP register.
  • bb is the offset to apply to either the PITCHL DSP register or noise frequency per step.
  • cc is a offset to apply to the PITCHH DSP register per step.
  • dd is a direct write to the SRCN DSP register. $80 and up instead cause this to use noise.
  • ee modifies the pitch using RNG using a non-zero value (except for V3.0 and up, this value is any non-zero value). How it is modified depends on the version.
    • V0.0-V2.0 overwrites the pitch settings and instead uses two bytes of RNG values for the pitch on a per-step basis.
    • V2.1 adds a random offset of the RNG value ANDed by $3F to the high byte of the final pitch output on a per-step basis.
    • V2.2 adds a random offset of the RNG value ANDed by $3F to the low byte of the starting pitch for the first step only.
    • V2.3 does nothing.
    • V3.0 and up apply a random offset of the RNG value ANDed by ee to the low byte of the starting pitch for the first step only.
  • ff defines the sign for the pitch offset.
    • $00 turns off pitch slides for the SFX.
    • $01-$7F apply a positive offset.
    • $80-$FF apply a negative offset.
  • gg is the number of timer 0 ticks (times 10) to perform the pitch slide for.
  • hh is a direct write to the ADSR1 DSP register.
  • ii is a direct write to the ADSR2 DSP register.
  • jj is generally an endless flag, but its operation differs between versions.
    • For V0.0-V4.0a, when this value is non-zero, it causes the SFX to be played endlessly until keyed off or interrupted. It also causes pitch slides to run forever.
    • For V4.0b, this acts as SFX priority. If all four channels are being used for SFX and the priority of the incoming SFX is greater than the SFX currently played, then the SFX is overwritten. The SFX plays forever if this value is greater than $7F, like what is done in V0.0-V4.0a.
  • kk is the number of timer 0 ticks (times 10) per step.

Stop All SFX (Command $05 (V4.0b))

Stops all SFX from playing in channels 5-8.

Fast Forward On (Command $06 (V0.0-V1.0), $0C (V2.0-V4.0a), $08 (V4.0b))

Causes the music to play at maximum tempo... which means every ten timer 0 ticks. This is equivalent to a tempo of 256.

Fast Forward Off (Command $07 (V0.0-V1.0), $0D (V2.0-V4.0a), $09 (V4.0b))

Causes the music to play at normal tempo.

Main & Echo Volume (Command $08 (V1.0), $0E (V2.0-V4.0a), $06 (V4.0b))

ii xx ?? ??
  • xx is a direct write to the MVOLL and MVOLR DSP registers.
    • V2.0 and up may also set the EVOLL and EVOLR DSP registers, but only if echo is supported in that build. It is always scaled by the main volume. The scaler multiplier (actually done with shifts) is as following, sorted by build...
      • Batman: Revenge of the Joker: 1/4
      • Dream TV: 1/4
      • Gods (all builds): 3/16
      • World Class Rugby (Outside of a game): 3/16
      • Lawnmower Man/Virtual Wars (Driving): 3/16
      • Lemmings 2: The Tribes (Cavelem Tribe): 3/16

Echo Feedback (Command $0F (V2.0-V4.0a))

$0F xx ?? ??
  • xx is a direct write to the EFB DSP register.

This is only ever supported if echo is supported in the ROM. That means only these builds support this command:

  • Batman: Revenge of the Joker
  • Dream TV
  • Gods (all builds)
  • World Class Rugby (Outside of a game)
  • Lawnmower Man/Virtual Wars (Driving)
  • Lemmings 2: The Tribes (Cavelem Tribe)

These other games have a reference for the ID, but effectively make it a NOP:

  • Battle Cars
  • Riddick Bowe Boxing/Chavez (all versions except Japanese)
  • Shaq-Fu

Go to IPL Boot Program (Command $10 (V2.0-V4.0a))

Jumps directly to the IPL Boot Program.

Set Tempo (Command $11 (V2.1-V2.2, V3.0-V4.0a))

$11 xx ?? ??
  • xx is the tempo to set the song at. This overwrites the song's tempo settings.

The following builds don't support this command:

  • World Class Rugby (During a game)

Set SFX Volume (Command $12-$19 (V3.0-V4.0a))

ii ?? xy ??
  • ii, the command ID used for the SFX, defines the channel used.
    • Channel 1: $12
    • Channel 2: $13
    • Channel 3: $14
    • Channel 4: $15
    • Channel 5: $16
    • Channel 6: $17
    • Channel 7: $18
    • Channel 8: $19
  • x defines the left volume. This is not signed.
  • y defines the right volume. This is not signed.
    • For both volume values, the 4-bit values are translated into the raw values for the VxVOLL/VxVOLR DSP registers via the following multipliers (in actuality, using XCNs and shift opcodes) on a per-game basis...
      • All other games: 4X
      • Michael Jordan Chaos in the Windy City (SFX IDs $00 and $4B only), Shaq-Fu: 8X

Set SFX Pitch (Command $1A-$21 (V3.0-V4.0a))

ii xx yy ??
  • ii, the command ID used for the SFX, defines the channel used.
    • Channel 1: $1A
    • Channel 2: $1B
    • Channel 3: $1C
    • Channel 4: $1D
    • Channel 5: $1E
    • Channel 6: $1F
    • Channel 7: $20
    • Channel 8: $21
  • xx overwrites the PITCHL value used in the SFX data.
  • yy overwrites the PITCHH value used in the SFX data.

This command is only supported in the following builds...

  • Battle Cars
  • World Cup Striker (European version) (Title Screen)
  • Elite Soccer/World Cup Striker (European version) (In-Game)
  • Kick Off 3 (beta) (In-Game)
  • Kick Off 3 (beta) (Title Screen)
  • Porky Pig's Haunted Holiday (beta)
  • Shaq-Fu

Not all IDs are supported in these builds...

  • Lawnmower Man/Virtual Wars (In-Game) (does not support $1A-$1D, not even in the code)
  • World Cup Striker (Japanese) (In-Game) (skips over $1A-$1D via a longer branding distance, though the code still exists)

Load New Data (Command $22-$23 (V4.0a), $0B (V4.0b))

Loads new data from the SNES using an IPL Boot ROM variant.
TODO Loading New Data (the loading protocol should be nearly identical to IPL Boot ROM with a few differences or so, since the program seems like it's using a similar variant)

Stop Music & Load New Data (Command $0A (V4.0b))

Stops the music, then loads new data from the SNES using an IPL Boot ROM variant.

Panning Separation (Command $0C (V4.0b))

$0C xx ?? ??
  • xx sets the amount that the panning will influence the left and right volumes. The maximum is zero, while the minimum is $FF, making it effectively mono.

Song Entry

For each song entry in an array of song definitions, one byte is defined as a starting tempo, followed by a series of pointers defined little endian style for each channel. For Dream TV Beta and Kick Off, only five pointers are defined. For Krusty's Super Fun House, only six pointers are defined. For all other games, eight pointers are defined.

Pre-V2.0 (Dream TV Beta and Kick Off)

xx yy yy zz zz aa aa bb bb cc cc

Pre-V2.0 (Krusty's Super Fun House)

xx yy yy zz zz aa aa bb bb cc cc dd dd

V2.0 and up

xx yy yy zz zz aa aa bb bb cc cc dd dd ee ee ff ff
  • xx is the starting tempo.
  • yy yy is a little endian pointer to a pattern order list for channel 1.
  • zz zz is a little endian pointer to a pattern order list for channel 2.
  • aa aa is a little endian pointer to a pattern order list for channel 3.
  • bb bb is a little endian pointer to a pattern order list for channel 4.
  • cc cc is a little endian pointer to a pattern order list for channel 5.
  • dd dd is a little endian pointer to a pattern order list for channel 6. (Krusty's Super Fun House only pre-V2.0)
  • ee ee is a little endian pointer to a pattern order list for channel 7. (not supported pre-V2.0)
  • ff ff is a little endian pointer to a pattern order list for channel 8. (not supported pre-V2.0)

Three builds don't support song entries at all:

  • The opening logos for Lemmings 2: The Tribes and Apocalypse II
  • Kick Off 3 Beta (In-Game)
  • World Class Rugby (During a game)

World Class Rugby (During a game) also doesn't support the music format at all since the code was removed.

Pattern Order List

Each channel has a list of little endian pointers on a per-channel basis. If the pointer is zero, then you jump back to either the beginning of the pattern order list for that channel, or to the loop marker that was set by the $F5 VCMD.

Instrument Format

The instrument format at V0.0 is N-SPC/Kankichi-kun compatible minus noise support for SRCN values above 127. Post V1.0, one of the bytes loses its usage.

The instrument format is defined as direct writes to DSP registers for the first four bytes, followed by two pitch-related bytes. They are defined like this...
V0.0-V1.0

xx yy zz aa bb cc

V2.0 and up

xx yy zz ?? bb cc
  • xx is a direct write to the VxSRCN DSP register.
  • yy is a direct write to the VxADSR1 DSP register.
  • zz is a direct write to the VxADSR2 DSP register.
  • aa is a direct write to the VxGAIN DSP register... but only for V1.0 and older. Otherwise, it's an unused byte.
  • bb is a pitch base multiplier.
  • cc is a fractional pitch base multiplier, defined in 256ths.

Voice Command Format

VCMD ID Description Arguments Minimum Version
$00-$5F Note 0.0
%011xxxxx
$60-$7F
Note Length 0.0
$80-$EC Invalid 0.0
$ED Instant Pitch Change to Note xx 4.0a only
$ED Set Main Volume xx 4.0b only
$EE Volume Scaler by Fraction xx yy 3.0
$EF Set ADSR xx yy 2.0
$F0 Fine Tune xx 1.0
$F1 Pitch Bend xx 1.0
$F2 Pitch Envelope ID xx 0.0
$F3 L/R Voice Volume xx yy 0.0
$F4 Tempo xx 0.0
$F5 Jump to Pattern in Order List + Mark Loop Point xx xx 0.0
$F6-$F7 Invalid 0.0
$F8 Key Off 0.0
$F9 One Note Delay 0.0
$FA Instrument xx 0.0
$FB Absolute Global Transposition xx 0.0
$FC Absolute Transposition xx 0.0
$FD Invalid 0.0
$FE Song End 0.0
$FF Pattern End 0.0

Invalid (VCMDs $80-$EC, $F6-$F7, $FD, $ED (pre-V4.0), $EE (pre-V3.0), $EF (pre-V2.0), $F0-$F1 (pre-V1.0))

The sound driver deliberately freezes itself via an infinite branch always loop rather than crashing or skipping the byte. This also takes up VCMD slots that are not filled in earlier versions.

Note (VCMDs $00-$5F)

Plays a note and delays the channel for one note length.

The initial pitch of the note is calculated using a pitch table that contains one octave (and a note)'s worth of pitch values. This value is shifted according to the octave used, and multiplied that by the instrument's pitch base to get the pitch for the note.

Note Length (VCMDs $60-$7F)

%011xxxxx
  • %xxxxx represents your note length (as five bits).

Note lengths are defined as the number of tempo ticks plus one. Notes are keyed off either on another note or on the key off VCMD ($F9).

Instant Pitch Change to Note (VCMD $ED (V4.0a))

Requires V4.0a to use. Lower versions freeze the sound driver instead.

$ED xx
  • xx defines the note to change the pitch to.

Set Main Volume (VCMD $ED (V4.0b))

Requires V4.0b to use. Lower versions freeze the sound driver instead.

$ED xx
  • xx defines the value to directly write to the MVOL DSP registers.

Volume Scaler by Fraction (VCMD $EE)

Requires V3.0 or later to use. Lower versions freeze the sound driver instead.

$EE xx yy
  • xx is the numerator, and is the value to multiply the volume by. The scaling process is skipped if this is zero.
  • yy is the denominator, and is the value to divide the multiplied volume by.

Put the two together, and you get the current volume multiplied by .

Set ADSR (VCMD $EF)

Requires V2.0 or later to use. Lower versions freeze the sound driver instead.

$EF xx yy
  • xx is a direct write to the VxADSR1 DSP register.
  • yy is a direct write to the VxADSR2 DSP register.

Fine Tune (VCMD $F0)

Requires V1.0 or later to use. Lower versions freeze the sound driver instead.

$F0 xx
  • xx is a signed delta value for the pitch. These are in units used directly for the VxPITCH DSP registers.

Pitch Bend (VCMD $F1)

Requires V1.0 or later to use. Lower versions freeze the sound driver instead.

$F1 xx
  • xx is a signed delta value for the pitch. These are in units used directly for the VxPITCH DSP registers.

Pitch bends are performed every 10 timer 0 ticks independently of the tempo. They are automatically terminated after one note.

Pitch Envelope ID (VCMD $F2)

$F2 xx
  • xx is an index to an array to a pointer of pitch envelopes. The pitch envelopes always loop and are clocked at a rate of 10 timer 0 ticks independently of the tempo ticker.

Pitch Envelope Format

Pitch Offset Tick (CMD $00-$7F, $81-$FF)
xx
  • xx is a signed offset in units used directly for the VxPITCH DSP registers.
Restart Pitch Envelope (CMD $80)
$80

Jumps back to the beginning of the pitch envelope.

L/R Voice Volume (VCMD $F3)

$F3 xx yy
  • This is a direct DSP register write to the VxVOL registers, with xx being the left volume and yy being the right volume.

This VCMD's operation changes depending on the version:

  • V0.0-V2.2: Just a straight write with no modifications made to the input.
  • V2.3: The input values are shifted left once.
  • V3.0-V4.0b: The input values are scaled by VCMD $EE, then shifted left once.
  • V4.0a: The input values are scaled by the values used for VCMD $EE. The shifting operation is not done here.

Tempo (VCMD $F4)

$F4 xx
  • xx defines one tempo tick as timer 0 ticks. Zero freezes the song except for pitch bends and envelopes, which are not affected by the tempo ticker.

Jump to Pattern in Order List + Mark Loop Point (VCMD $F5)

$F5 xx xx
  • xx xx is a little endian pointer to a pattern pointer in the order list. The loop point is marked here.

Key Off (VCMD $F8)

Keys off the note and delays the channel for one note length.

One Note Delay (VCMD $F9)

Delays the channel for one note length.

Instrument (VCMD $FA)

$FA xx
  • xx is an instrument ID to an array of instruments. See Instrument Format above for the format.

Absolute Global Transposition (VCMD $FB)

$FB xx
  • xx is a signed note offset to apply for all channels for the music.

Global transpsition is not applied to channel 4 for V0.0-2.1.

Absolute Transposition (VCMD $FC)

$FC xx
  • xx is a signed note offset to apply for the channel.

Song End (VCMD $FE)

Terminates the song for all of the channels.

Pattern End (VCMD $FF)

Ends the pattern and goes to the pattern order list to fetch another pattern.