.*(b(ra|[bcv][cs]|n[ez]|eq|pl|mi)|jmp|ret).* SPC engine to ROM translation: SNES address = $CF:6C08 + SPC address RAM cleared on engine start: { $00..EE $0391..FF $0440..BE $0500..14FF } $00: Value read from CPU IO 0 (by $1621) $01: Value read from CPU IO 1 (by $1621) $02: Value read from CPU IO 2 (by $1621) $03: Value read from CPU IO 3 (by $1621) $04: Value for CPU IO 0 (by $1621) $05: Value for CPU IO 1 (by $1621) $06: Value for CPU IO 2 (by $1621) $07: Value for CPU IO 3 (by $1621) $08: Previous value read from CPU IO 0 $09: Previous value read from CPU IO 1 $0A: Previous value read from CPU IO 2 $0B: Previous value read from CPU IO 3 $0C: Music track status { 0: Music track playing 1: Music track, ready to play 2: New music track loaded, need to initialise } $0D: Unused $0E: Zero. Used to clear DSP key off flags in DSP update loop. Used in $1B58 as a convenient way to clear YA $10: Note when calling $169B/$16B1/$1D56/$1D74/$1D76. Panning bias when calling $1C4A $12: Usually used as sign bit. DSP voice volume index in $1C4A $13: Note modified flag $14..17: Miscellaneous 16-bit values $18: Random number. Updated in main game loop, otherwise unused $1A: Enabled sound voices. Actually music voices that have been taken over by sounds $1B: Disable note processing { If set, causes main loop to short circuit to music track processing, but notes aren't processed. Tracker and tracks advance as normal, current notes are played until finished, sound effects are not processed at all. Set to 80h by tracker command 80h, cleared by tracker command 81h. Set to 1 by track command FDh, cleared by track command FEh } $1C..1F: Unused $20: Return address in $2875 $22: Sound 1 instruction list pointer set. Set to [$2AED + [current sound 1 index]] when changing sound $24: Pointer to sound 1 channel voice bitset $26: Pointer to sound 1 channel voice mask $28: Pointer to sound 1 channel voice index $2A: Sound 1 channel 0 instruction list pointer $2C: Sound 1 channel 1 instruction list pointer $2E: Sound 1 channel 2 instruction list pointer $30..3F: Track pointers $40: Tracker pointer $42: Tracker timer $43: Sound effects clock. Sound effects are processed when this counter overflows (16 ms (an echo period)) $44: Track index $45: Key on flags (DSP register $4C). Part of DSP update loop { v = hgfedcba a: Key on voice 0 b: Key on voice 1 c: Key on voice 2 d: Key on voice 3 e: Key on voice 4 f: Key on voice 5 g: Key on voice 6 h: Key on voice 7 } $46: Key off flags (DSP register $5C). Part of DSP update loop { v = hgfedcba a: Key off voice 0 b: Key off voice 1 c: Key off voice 2 d: Key off voice 3 e: Key off voice 4 f: Key off voice 5 g: Key off voice 6 h: Key off voice 7 } $47: Current music voice bitset. During music processing, this is the bitset of the voice being processed. During sound processing, this is essentially zero { During sound processing: All voice bitsets are clear before sound processing Voice bitset is set when resetting channel Voice bitset is cleared when initialising channel Voice bitset is cleared when processing next instruction and legato disabled This variable is usually used for the check `[$47] & [$1A] != 0`, which, if true, prevents changes to the voice. During sound processing, this check is always false and so the changes can be done. The way it's done with the above writes is completely redundant } $48: FLG (DSP register $6C). Part of DSP update loop { v = rmefffff f: Noise frequency { 0: 0 Hz 1: 16 Hz 2: 21 Hz 3: 25 Hz 4: 31 Hz 5: 42 Hz 6: 50 Hz 7: 63 Hz 8: 83 Hz 9: 100 Hz Ah: 125 Hz Bh: 167 Hz Ch: 200 Hz Dh: 250 Hz Eh: 333 Hz Fh: 400 Hz 10h: 500 Hz 11h: 667 Hz 12h: 800 Hz 13h: 1.0 KHz 14h: 1.3 KHz 15h: 1.6 KHz 16h: 2.0 KHz 17h: 2.7 KHz 18h: 3.2 KHz 19h: 4.0 KHz 1Ah: 5.3 KHz 1Bh: 6.4 KHz 1Ch: 8.0 KHz 1Dh: 10.7 KHz 1Eh: 16 KHz 1Fh: 32 KHz } e: Disable echo buffer writes m: Mute amplifier r: Soft reset (key off all voices and set envelope 0) } $49: Noise enable flags (DSP register $3D). Part of DSP update loop { v = hgfedcba a: Output noise on voice 0 b: Output noise on voice 1 c: Output noise on voice 2 d: Output noise on voice 3 e: Output noise on voice 4 f: Output noise on voice 5 g: Output noise on voice 6 h: Output noise on voice 7 } $4A: Echo enable flags (DSP register $4D). Part of DSP update loop { v = hgfedcba a: Enable echo on voice 0 b: Enable echo on voice 1 c: Enable echo on voice 2 d: Enable echo on voice 3 e: Enable echo on voice 4 f: Enable echo on voice 5 g: Enable echo on voice 6 h: Enable echo on voice 7 } $4B: Pitch modulation flags (DSP register $2D). Part of DSP update loop { v = gfedcba0 a: Modulate voice 1 pitch by voice 0 amplitude b: Modulate voice 2 pitch by voice 1 amplitude c: Modulate voice 3 pitch by voice 2 amplitude d: Modulate voice 4 pitch by voice 3 amplitude e: Modulate voice 5 pitch by voice 4 amplitude f: Modulate voice 6 pitch by voice 5 amplitude g: Modulate voice 7 pitch by voice 6 amplitude } $4C: Echo timer. Incremented every echo period (16 ms). When negative, echo is completely disabled. When positive, echo output is disabled, but echo buffer writes are enabled. Set to min(0, [$4C]) - 1 - [DSP echo delay] $4D: Echo delay (echo buffer size) (DSP register $7D). Range 0..Fh. 0 = one sample (4 bytes). Otherwise delay = v * 16 ms (v * 2 KB) $4E: Echo feedback volume (DSP register $0D). Signed. Part of DSP update loop $4F: Unused $50: Music transpose. Signed. Units of semitones $51: Music track clock. Music track is processed when this counter overflows. Incremented by [$52] / 100h every 2 ms $52: Music tempo. Music track is pressed every 10000h / [$52] * 2 ms = [$52] times per 131 seconds { Set to 2000h by music track initialisation (62.5 ticks per second) Set to 1000h by boot (31.3 ticks per second) 0..FFh means music track never gets processed again } $54: Dynamic music tempo timer. Cleared by $1758. Timer. If non-zero: $52 += [$56]. Clears $52 once decremented to zero(!) $55: Target music tempo $56: Music tempo delta $58: Music volume multiplier * 10000h. Set to C000h by $1758 $5A: Dynamic music volume timer. Cleared by $1758. Timer. If non-zero: $58 += [$5C]. Clears $58 once decremented to zero $5B: Target music volume multiplier * 100h $5C: Music volume multiplier delta * 10000h $5E: Music voice volume update bitset $5F: Percussion instruments base index $60: Echo volume left (DSP register $2C) * 100h. Signed. Part of DSP update loop $62: Echo volume right (DSP register $2C) * 100h. Signed. Part of DSP update loop $64: Echo volume left delta $66: Echo volume right delta $68: Dynamic echo volume timer. If non-zero: $60 += [$64], $62 += [$66]. Clears $60 and sets $62 = [$6A] * 100h once decremented to zero $69: Target echo volume left $6A: Target echo volume right $6B..6F: Unused $70..7E: Track note timers (decremented on music track processing) $71..7F: Track note ring timers. Set to max(1, track command byte * [track $0201] / 100h) if 80h < track command byte < E0h $80..8E: Track repeated subsection counters. 0 = not repeating subsection. 1 = stop repeating and return. 2+ = repeat subsection $81..8F: Unused $90..9E: Track dynamic volume timers $91..9F: Track dynamic panning timers $A0..AE: Track pitch slide timers $A1..AF: Track pitch slide delay timers $B0..BE: Track vibrato delay timers $B1..BF: Track vibrato extents $C0..CE: Track tremolo delay timers $C1..CF: Track tremolo extents $D0: Sound 1 channel 3 instruction list pointer $D2: Pointer to echo buffer in RAM clear routine on engine start $D4: Sound 2 instruction list pointer set. Set to [$39B3 + [current sound 2 index]] when changing sound $D6: Pointer to sound 2 channel voice bitset $D8: Pointer to sound 2 channel voice mask $DA: Pointer to sound 2 channel voice index $DC: Sound 2 channel 0 instruction list pointer $DE: Sound 2 channel 1 instruction list pointer $E0: Sound 3 instruction list pointer set. Set to [$4E8F + [current sound 3 index]] when changing sound $E2: Pointer to sound 3 channel voice bitset $E4: Pointer to sound 3 channel voice mask $E6: Pointer to sound 3 channel voice index $E8: Sound 3 channel 0 instruction list pointer $EA: Sound 3 channel 1 instruction list pointer $EC: Unused $EE: Address of memory to clear (in $1ED7) $F0..FF: IO ports $0100..0E: Track dynamic vibrato timers $01??..CF: Stack $01D0..FF: Unused $0200..038F: Music RAM { $0200..0E: Track note lengths. Set to track command byte if 0 < track command byte < 80h. Value for track timer after all instructions processed $0201..0F: Track note ring length multipliers * 100h. Set to [$5800 + (track parameter >> 4 & 7)] if 0 <= track parameter < 80h $0210..1E: Track note volume multipliers * 100h. Set to [$5808 + (track parameter & Fh)] if 0 <= track parameter < 80h. Checked against zero when new track data loaded, skips call to track command E0h $0211..1F: Track instrument indices. CAh..DFh are percussion instrument indices taken relative to [$5F] - CAh $0220..2F: Track instrument pitch multipliers / 100h $0230..3F: Track repeated subsection return addresses $0240..4F: Track repeated subsection addresses $0250..7F: Unused $0280..8E: Track slide lengths $0281..8F: Track slide delays $0290..9E: Track slide directions. 0 = slide in, 1 = slide out $0291..9F: Track slide extents. Signed. Semitones $02A0..AE: Track vibrato phases $02A1..AF: Track vibrato rates $02B0..BE: Track vibrato delays $02B1..BF: Track dynamic vibrato lengths $02C0..CE: Track vibrato extent deltas $02C1..CF: Track static vibrato extents. Reset value for vibrato extent after dynamic vibrato finishes $02D0..DE: Track tremolo phases. 0 = max volume, 80h = min volume. If tremolo extent = FFh, all negative values are min volume. Incremented by track tremolo rate $02D1..DF: Track tremolo rates $02E0..EE: Track tremolo delays $02E1..EF: Unused $02F0..FE: Track transposes. Signed. Units of semitones $02F1..FF: Unused $0300..0F: Track volume multipliers * 10000h $0310..1F: Track volume multiplier deltas * 10000h $0320..2E: Track target volume multipliers * 100h $0321..2F: Track output volume multipliers * 100h. Base volume multiplier before apply panning calculations $0330..3F: Track panning biases. Range 0..1400h (0 is fully right, 1400h is fully left). Set to A00h by $1758 $0340..4F: Track panning bias deltas $0350..5E: Track target panning biases $0351..5F: Track phase inversion options. Cleared by $1758 { 80h: Phase invert left volume 40h: Phase invert right volume } $0360..6E: Track subnotes $0361..6F: Track notes. Semitones $0370..7F: Track note deltas * 100h (due to pitch slide) $0380..8E: Track target notes (due to pitch slide). Semitones $0381..8F: Track subtransposes } $0390: Number of bytes to clear (in $1ED7) $0391: Unused $0392..F7: Sound 1 RAM { $0392: Current sound 1 $0393: Current sound 1 index. ([current sound 1] - 1) * 2 $0394..97: Sound 1 channel instruction list indices $0398..9B: Sound 1 channel instruction timers $039C..9F: Sound 1 channel disable bytes. FFh = disabled $03A0: Sound 1 channel index in sound 1 initialisation $03A1: Number of sound 1 voices to set up $03A2: Voice index in sound 1 initialisation. ([voice ID] - 1) * 2 $03A3: Remaining enabled sound voices in sound 1 initialisation $03A4: Sound 1 initialisation flag. FFh = initialised $03A5: Voice ID in sound 1 initialisation. Range 1..8 $03A6..A9: Sound 1 channel voice bitsets $03AA..AD: Sound 1 channel voice masks $03AE: Sound 1 channel index * 2 in sound 1 initialisation $03AF..B2: Sound 1 channel voice indices $03B3: Sound 1 enabled voices $03B4..B7: Sound 1 channel DSP indices. Set to [$03AF..B2] * 8 on sound 1 initialisation $03B8..BE: Sound 1 channel backups of track output volume $03B9..BF: Sound 1 channel backups of track phase inversion options $03C0..C6: Sound 1 channel release flags. FFh = voice keyed off $03C1..C7: Sound 1 channel release timers $03C8..CB: Sound 1 channel repeat counters $03CC..CF: Sound 1 channel repeat points $03D0..D7: Sound 1 channel ADSR settings. Written to DSP voice's ADSR settings when [$03D8..DB] != 0. Set by sound command F9h $03D8..DB: Sound 1 channel update ADSR settings flags $03DC: Sound 1 channel 0 note $03DD: Sound 1 channel 0 subnote $03DE: Sound 1 channel 0 subnote delta (due to pitch slide) $03DF: Sound 1 channel 0 target note (due to pitch slide) $03E0: Sound 1 channel 0 pitch slide flag. FFh = enable pitch slide $03E1: Sound 1 channel 0 legato flag $03E2: Sound 1 channel 0 pitch slide legato flag $03E3: Sound 1 channel 1 note $03E4: Sound 1 channel 1 subnote $03E5: Sound 1 channel 1 subnote delta (due to pitch slide) $03E6: Sound 1 channel 1 target note (due to pitch slide) $03E7: Sound 1 channel 1 pitch slide flag. FFh = enable pitch slide $03E8: Sound 1 channel 1 legato flag $03E9: Sound 1 channel 1 pitch slide legato flag $03EA: Sound 1 channel 2 note $03EB: Sound 1 channel 2 subnote $03EC: Sound 1 channel 2 subnote delta (due to pitch slide) $03ED: Sound 1 channel 2 target note (due to pitch slide) $03EE: Sound 1 channel 2 pitch slide flag. FFh = enable pitch slide $03EF: Sound 1 channel 2 legato flag $03F0: Sound 1 channel 2 pitch slide legato flag $03F1: Sound 1 channel 3 note $03F2: Sound 1 channel 3 subnote $03F3: Sound 1 channel 3 subnote delta (due to pitch slide) $03F4: Sound 1 channel 3 target note (due to pitch slide) $03F5: Sound 1 channel 3 pitch slide flag. FFh = enable pitch slide $03F6: Sound 1 channel 3 legato flag $03F7: Sound 1 channel 3 pitch slide legato flag } $03F8..FF: Sound 2 RAM { $03F8: Current sound 2 $03F9: Current sound 2 index. ([current sound 2] - 1) * 2 $03FA..FB: Sound 2 channel instruction list indices $03FC..FD: Sound 2 channel instruction timers $03FE..FF: Sound 2 channel disable bytes. FFh = disabled } $0400..0E: Track skip new notes flags. Music track advances as normal but new notes aren't played. The current note still plays until its end $0401..0F: Unused $0410..3F: Unused $0440..046F: Sound 2 RAM { $0440: Sound 2 channel index in sound 2 initialisation $0441: Number of sound 2 voices to set up $0442: Voice index in sound 2 initialisation. ([voice ID] - 1) * 2 $0443: Remaining enabled sound voices in sound 2 initialisation $0444: Sound 2 initialisation flag. FFh = initialised $0445: Voice ID in sound 2 initialisation. Range 1..8 $0446..47: Sound 2 channel voice bitsets $0448..49: Sound 2 channel voice masks $044A: Sound 2 channel index * 2 in sound 2 initialisation $044B..4C: Sound 2 channel voice indices $044D: Sound 2 enabled voices $044E..4F: Sound 2 channel DSP indices. Set to [$044B..4C] * 8 on sound 2 initialisation $0450..52: Sound 2 channel backups of track output volume $0451..53: Sound 2 channel backups of track phase inversion options $0454..56: Sound 2 channel release flags. FFh = voice keyed off $0455..57: Sound 2 channel release timers $0458..59: Sound 2 channel repeat counters $045A..5B: Sound 2 channel repeat points $045C..5F: Sound 2 channel ADSR settings. Written to DSP voice's ADSR settings when [$0460..61] != 0. Set by sound command F9h $0460..61: Sound 2 channel update ADSR settings flags $0462: Sound 2 channel 0 note $0463: Sound 2 channel 0 subnote $0464: Sound 2 channel 0 subnote delta (due to pitch slide) $0465: Sound 2 channel 0 target note (due to pitch slide) $0466: Sound 2 channel 0 pitch slide flag. FFh = enable pitch slide $0467: Sound 2 channel 0 legato flag $0468: Sound 2 channel 0 pitch slide legato flag $0469: Sound 2 channel 1 note $046A: Sound 2 channel 1 subnote $046B: Sound 2 channel 1 subnote delta (due to pitch slide) $046C: Sound 2 channel 1 target note (due to pitch slide) $046D: Sound 2 channel 1 pitch slide flag. FFh = enable pitch slide $046E: Sound 2 channel 1 legato flag $046F: Sound 2 channel 1 pitch slide legato flag } $0470..04A7: Sound 3 RAM { $0470: Current sound 3 $0471: Current sound 3 index. ([current sound 3] - 1) * 2 $0472..73: Sound 3 channel instruction list indices $0474..75: Sound 3 channel instruction timers $0476..77: Sound 3 channel disable bytes. FFh = disabled $0478: Sound 3 channel index in sound 3 initialisation $0479: Number of sound 3 voices to set up $047A: Voice index in sound 3 initialisation. ([voice ID] - 1) * 2 $047B: Remaining enabled sound voices in sound 3 initialisation $047C: Sound 3 initialisation flag. FFh = initialised $047D: Voice ID in sound 3 initialisation. Range 1..8 $047E..7F: Sound 3 channel voice bitsets $0480..81: Sound 3 channel voice masks $0482: Sound 3 channel index * 2 in sound 2 initialisation $0483..84: Sound 3 channel voice indices $0485: Sound 3 enabled voices $0486..87: Sound 3 channel DSP indices. Set to [$0483..84] * 8 on sound 3 initialisation $0488..8A: Sound 3 channel backups of track output volume $0489..8B: Sound 3 channel backups of track phase inversion options $048C..8E: Sound 3 channel release flags. FFh = voice keyed off $048D..8F: Sound 3 channel release timers $0490..91: Sound 3 channel repeat counters $0492..93: Sound 3 channel repeat points $0494..97: Sound 3 channel ADSR settings. Written to DSP voice's ADSR settings when [$0498..99] != 0. Set by sound command F9h $0498..99: Sound 3 channel update ADSR settings flags $049A: Sound 3 channel 0 note $049B: Sound 3 channel 0 subnote $049C: Sound 3 channel 0 subnote delta (due to pitch slide) $049D: Sound 3 channel 0 target note (due to pitch slide) $049E: Sound 3 channel 0 pitch slide flag. FFh = enable pitch slide $049F: Sound 3 channel 0 legato flag $04A0: Sound 3 channel 0 pitch slide legato flag $04A1: Sound 3 channel 1 note $04A2: Sound 3 channel 1 subnote $04A3: Sound 3 channel 1 subnote delta (due to pitch slide) $04A4: Sound 3 channel 1 target note (due to pitch slide) $04A5: Sound 3 channel 1 pitch slide flag. FFh = enable pitch slide $04A6: Sound 3 channel 1 legato flag $04A7: Sound 3 channel 1 pitch slide legato flag } $04A8: Unused $04A9: Flag. Disables processing CPU IO 2. Never seems to be written. Checked at start of CPU IO 3 handler, but has no effect $04AA..B0: Unused $04B1: Echo FIR filter set index. Range 0..3. Never read $04B2..B9: Unused $04BA: Sound 3 low health priority. If 2, current sound 3 may not be changed other than to 1 (silence) $04BB: Sound 1 priority. If set, current sound 1 may not be changed other than to 1 (power bomb explosion) or 2 (silence) $04BC: Sound 2 priority. If set, current sound 2 may not be changed other than to 71h (silence) or 7Eh (Mother Brain's cry - high pitch) $04BD: Sound 3 priority. If set, current sound 3 may not be changed other than to 1 (silence) or 2 (low health beep) $04BE..FF: Unused $0500..14FF: Echo buffer. Reduced to $0D00..14FF if [echo delay] = 1 $1500..56E1: SPC engine { $530E..56E1: Shared trackers { $530E..546D: Music track 1 - Samus fanfare $546E..5568: Music track 2 - item fanfare $5569..5696: Music track 3 - elevator $5697..E1: Music track 4 - pre-statue hall } } $56E2..57FF: Unused $5800..17: Note length table (song specific, but every song loads the same data) { $5800..07: Note ring length multiplier * 100h { 32, 65, 7F, 98, B2, CB, E5, FC } $5808..17: Note volume multiplier * 100h { 19, 32, 4C, 65, 72, 7F, 8C, 98, A5, B2, BF, CB, D8, E5, F2, FC } } $5818..1F: Unused $5820..6818: Tracker data { $5820..5827: Shared tracker pointers { $5820: Music track 1 - Samus fanfare $5822: Music track 2 - item fanfare $5824: Music track 3 - elevator $5826: Music track 4 - pre-statue hall } $5828..6818: Song-specific tracker data } $6819..6BFF: Unused $6C00..E9: Instrument table. 6 byte entries. In vanilla, sample table indices are consecutive values and noise is never used { $6C00..8F: Shared instruments (0..17h) $6C90..E9: Song-specific instruments (18h..26h) + 0: Sample table index. If v & 80h: enable noise with noise frequency = v & 1Fh with sample table index = 0 instead + 1: ADSR settings + 3: Gain settings + 4: Instrument pitch multiplier. 16-bit *big* endian } $6CEA..FF: Unused $6D00..9F: Sample table { $6D00..5F: Shared sample table $6D60..9F: Song-specific sample table } $6DA0..FF: Unused $6E00..FFFF: Sample data { $6E00..B515: Shared sample data { $6E00..73C3: Sample 0 $73C4..7987: Sample 1 $7988..7C9F: Sample 2 $7CA0..7DF5: Sample 3 $7DF6..82F3: Sample 4 $82F4..86D1: Sample 5 $86D2..88C0: Sample 6 $88C1..8BCF: Sample 7 $8BD0..91C9: Sample 8 $91CA..92E9: Sample 9 $92EA..92E9: Sample Ah $92EA..A04E: Sample Bh $A04F..A0A8: Sample Ch $A0A9..A15C: Sample Dh $A15D..A2E8: Sample Eh $A2E9..A6BD: Sample Fh $A6BE..A972: Sample 10h $A973..A972: Sample 11h $A973..A9B1: Sample 12h $A9B2..ACB7: Sample 13h $ACB8..AF5A: Sample 14h $AF5B..B20F: Sample 15h $B210..B515: Sample 16h. Unused } $B516..FFFF: Song-specific sample data. Note: sample 17h points here with loop point $CC6E, presumably never used } ; ---------------------------------------------------------------- CPU <-> APU communication: { Upload to APU (load new music data): { CPU sends APU IO 0 = FFh to request upload and waits for ACK APU sends CPU IO 0..1 = AAh BBh to acknowledge upload request and waits for [CPU IO 0] = CCh Kick = CCh For each data block: CPU sends APU IO 2..3 = destination address CPU sends APU IO 1 = 1 (arbitrary non-zero value) CPU sends APU IO 0 = kick and waits for APU IO 0 echo APU echoes CPU IO 0 to acknowledge destination address and waits for [CPU IO 0] = 0 CTR = 0 For each data byte: CPU sends APU IO 1 = data CPU sends APU IO 0 = CTR and waits for echo APU echoes CPU IO 0 to acknowledge data and waits for next CTR Increment CTR Increment CTR, increment again if overflowed to 0 Kick = CTR CPU sends APU IO 2..3 = entry address CPU sends APU IO 1 = 0 to signify end of data transfer CPU sends APU IO 0 = kick APU echoes CPU IO 0 to acknowledge end of data transfer APU jumps to CPU IO 2..3 } Stop music: CPU sends APU IO 0 = 0 Pause (key off music voices): CPU sends APU IO 0 = F0h Resume: CPU sends APU IO 0 = F1h Load new music track: CPU sends APU IO 0 = [music track] (where [music track] is not 0, F0h, F1h nor FFh) Load new sound effect: { State 0: CPU sends APU IO 1/2/3 = [sound effect] State = 1 State 1: If APU hasn't echoed CPU IO 1/2/3: CPU sends APU IO 1/2/3 = [sound effect] (again) Else: Timer = 2 State = 2 State 2: Decrement timer If [timer] = 0: CPU sends APU IO 1/2/3 = 0 State = 3 State 3: If APU hasn't echoed CPU IO 1/2/3: CPU sends APU IO 1/2/3 = 0 (again) Else: State = 0 } } ; ---------------------------------------------------------------- Music trackers format: { ______________ First tracker pointer | _________ Second tracker pointer | | ____ Other tracker pointers | | | aaaa bbbb [...] Variable length with no terminator, indexed by music track. } Tracker format: { A tracker is a list of two byte entries that are either track set pointers or commands. Commands are entries less than 100h: 0000: Terminator 0080: Disable note processing 0081: Enable note processing 00tt pppp: Wait max(1, t) tics, then go to p (where t is signed and p is a pointer to somewhere in the commands list) E.g. the title sequence: $582C: 5838 ; Track set 0 $582E: 5858 ; Track set 1 $5830: 5848 ; Track set 2 $5832: 00FF,5830 ; Go to $5830 after 1 tic $5836: 0000 ; Terminator } Track set format: { ______________________________________ Pointer to track 0 | _________________________________ Pointer to track 1 | | ____________________________ Pointer to track 2 | | | _______________________ Pointer to track 3 | | | | __________________ Pointer to track 4 | | | | | _____________ Pointer to track 5 | | | | | | ________ Pointer to track 6 | | | | | | | ___ Pointer to track 7 | | | | | | | | aaaa bbbb cccc dddd eeee ffff gggg hhhh Where track i is played on voice i. } Track format: { A track is a list of one byte entries that are note lengths, notes, a tie, a rest, or commands. A tie is an entry with value C8h (continues to play the previous note for a note length). A rest is an entry with value C9h (plays nothing for a note length). Note lengths: { ll ; Set note length ll rv ; Set note length, volume and ring length Notes lengths are entries with 1 <= l < 80h and set note length = l tics. If the next byte is less than 80h, then rv is interpreted as a parameter. r is an index into a table of note ring lengths that are fractions out of 100h of the note's length for which the note will ring within v is an index into a table of note volume multipliers that are fractions out of 100h. Ring length table: 32h, 65h, 7Fh, 98h, B2h, CBh, E5h, FCh Volume table: 19h, 32h, 4Ch, 65h, 72h, 7Fh, 8Ch, 98h, A5h, B2h, BFh, CBh, D8h, E5h, F2h, FCh } Notes: { nn Notes are entries with 80h <= n < C8h. The note octave is (n - 80h) / 12 + 1, the note key is given by the table: C, Db, D, Eb, E, F, Gb, G, Ab, A, Bb, B where the table is indexed by (n - 80h) % 12. E.g. A4h is middle C (C_4) } Percussion notes: { nn Percussion notes are entries with CAh <= n < E0h. They play C_4 on percussion instrument (n - CAh) where the percussion instruments base index is given by command FAh. } Commands: { E0 ii ; Select instrument i E1 pp ; Panning bias = (p & 1Fh) / 14h. If p & 80h, left side phase inversion is enabled. If p & 40h, right side phase inversion is enabled E2 tt bb ; Dynamic panning over t tics with target panning bias b / 14h E3 dd rr ee ; Static vibrato after d tics at rate r with extent e E4 ; End vibrato E5 vv ; Music volume multiplier = v / 100h E6 tt vv ; Dynamic music volume over t tics with target volume multiplier v / 100h E7 tt ; Music tempo = t / (0x100 * 0.002) tics per second E8 tt TT ; Dynamic music tempo over t tics with target tempo TT / (0x100 * 0.002) tics per second E9 tt ; Set music transpose of t semitones EA tt ; Set transpose of t semitones EB dd rr ee ; Tremolo after d tics at rate r with extent e EC ; End tremolo ED vv ; Volume multiplier = v / 100h EE tt vv ; Dynamic volume over t tics with target volume multiplier v / 100h EF pppp cc ; Repeat subsection p, c times F0 ll ; Dynamic vibrato over l tics with target extent 0 (unused) F1 dd ll ee ; Slide out after d tics for l tics by e semitones F2 dd ll ee ; Slide in after d tics for l tics by e semitones (unused) F3 ; End slide F4 ss ; Set subtranspose of s / 100h semitones F5 vv ll rr ; Static echo on voices v with echo volume left = l and echo volume right = r F6 ; End echo (unused) F7 dd ff ii ; Set echo parameters: echo delay = d, echo feedback volume = f, echo FIR filter index = i (range 0..3) F8 dd ll rr ; Dynamic echo volume after d tics with target echo volume left = l and target echo volume right = r (unused) F9 dd ll tt ; Pitch slide after d tics over l tics by t semitones FA ii ; Percussion instruments base index = i FB ; Skip next byte (unused) FC ; Skip all new notes (unused) FD ; Stop sound effects and disable music note processing (unused) FE ; Resume sound effects and enable music note processing (unused) } } Sound effect instruction format: { Commands: F5h dd tt - legato pitch slide with subnote delta = d, target note = t F8h dd tt - pitch slide with subnote delta = d, target note = t F9h aaaa - voice's ADSR settings = a FBh - repeat FCh - enable noise FDh - decrement repeat counter and repeat if non-zero FEh cc - set repeat pointer with repeat counter = c FFh - end Otherwise: ii vv pp nn tt i: Instrument index v: Volume p: Panning n: Note. F6h is a tie t: Length }