SMPS/Song data
From Sega Retro
- Back to: SMPS.
Regardless of what variant of SMPS is used, song data itself is almost nearly identical. The elementary unit of the song data is the byte; its value determines what the byte is:
- value in range [$00, $7F]: Duration value
- value in range [$80]: Rest (counts as a note value)
- value in range [$81, $DF]: Note value
- value in range [$E0, $FF]: Coordination flag
Duration values, rests, and note values are described in the Note Editing section; coordination flags in the Coordination Flags section.
Contents
Note Editing
Notation is SMPS usually follows the same general formula:
dc.b Note Byte, Duration Byte
However, if you already have a note byte, you can string multiple durations together to repeat notes:
dc.b Note Byte, Duration 1, Duration 2, ...
Every time a duration byte is read, the note is retriggered — that is, each duration value represents a new note play — unless the duration is prefixed by the $E7 (no attack) coordination flag. Do not put $E7 between a note byte and a duration byte! Likewise, if you already have a duration defined, you can string multiple notes together to make them use the same duration:
dc.b Note Byte, Duration Byte. Note 2, Note 3, ...
The rules for $E7 apply here too.
Here are some examples of valid note definitions:
dc.b $81, $01, $04, $03, $02
This particular combination of values will play note $81 at multiple durations--first 01, then 04, then 03, and then 02. As long as a note is defined, its value will stay in memory and be repeated on any subsequent duration parsed in the channel.
dc.b $82, $02, $81, $82, $83, $84, $86, $8F
This combination of values will play all of the defined notes with a duration of 02--first 82, then 81, then 82, then 83... so on and so forth. As previously stated, the first definition in a string of values must always be a note. When repeating notes instead of definitions to omit duplicate data, the string will be ended by placing a new duration value.
Note Equivalents
For tone channels (synthesis channels and the Sega CD PCM), notes represent actual music notes. In these cases, getting the note equivalent is simple: subtract $81 and divide by 12. The quotient gives you the octave number and the remainder gives you the note (0 = C, 1 = C#, 2 = D, etc.). For your convenience, however, here is a table.
Value | Note | Value | Note | Value | Note | Value | Note |
---|---|---|---|---|---|---|---|
$81 | C0 | $99 | C2 | $B1 | C4 | $C9 | C6 |
$82 | C#0 | $9A | C#2 | $B2 | C#4 | $CA | C#6 |
$83 | D0 | $9B | D2 | $B3 | D4 | $CB | D6 |
$84 | D#0 | $9C | D#2 | $B4 | D#4 | $CC | D#6 |
$85 | E0 | $9D | E2 | $B5 | E4 | $CD | E6 |
$86 | F0 | $9E | F2 | $B6 | F4 | $CE | F6 |
$87 | F#0 | $9F | F#2 | $B7 | F#4 | $CF | F#6 |
$88 | G0 | $A0 | G2 | $B8 | G4 | $D0 | G6 |
$89 | G#0 | $A1 | G#2 | $B9 | G#4 | $D1 | G#6 |
$8A | A0 | $A2 | A2 | $BA | A4 | $D2 | A6 |
$8B | A#0 | $A3 | A#2 | $BB | A#4 | $D3 | A#6 |
$8C | B0 | $A4 | B2 | $BC | B4 | $D4 | B6 |
$8D | C1 | $A5 | C3 | $BD | C5 | $D5 | C7 |
$8E | C#1 | $A6 | C#3 | $BE | C#5 | $D6 | C#7 |
$8F | D1 | $A7 | D3 | $BF | D5 | $D7 | D7 |
$90 | D#1 | $A8 | D#3 | $C0 | D#5 | $D8 | D#7 |
$91 | E1 | $A9 | E3 | $C1 | E5 | $D9 | E7 |
$92 | F1 | $AA | F3 | $C2 | F5 | $DA | F7 |
$93 | F#1 | $AB | F#3 | $C3 | F#5 | $DB | F#7 |
$94 | G1 | $AC | G3 | $C4 | G5 | $DC | G7 |
$95 | G#1 | $AD | G#3 | $C5 | G#5 | $DD | G#7 |
$96 | A1 | $AE | A3 | $C6 | A5 | $DE | A7 |
$97 | A#1 | $AF | A#3 | $C7 | A#5 | $DF | A#7 |
$98 | B1 | $B0 | B3 | $C8 | B5 |
This base value is then added to a per-channel counter, the channel key displacement, which is then turned into a frequency value for the respective sound chip. Another special value, the channel frequency displacement, is added to this frequency value to define the final frequency of output sound.
For pure sample channels (the YM2612 and 32X), notes represent indexes into the DAC array; simply subtract $81 to get the (offset 0) index.
Coordination flags
Coordination flags take the form
dc.b Coordination Flag Byte dc.b Coordination Flag Byte, Parameter Byte(s)
Mappings and definitions have changed between different SMPS versions; the differences are noted in the table:
Flag | SMPS-68000 (and some SMPS-Z80s like Sonic 2) | SMPS-Z80/SMPS-32X/SMS(??)/GG(??) | SMPS-PCM |
---|---|---|---|
$E0xx | Panning, AMS, FMS. xx takes the binary form lraaa0ff:
| ||
$E1xx | Set channel frequency displacement to signed xx | ||
$E2xx | Unknown | Unknown, though $E2FF is fade-in to previous song (needed on DAC channel) | Unknown |
$E3 | Return from subroutine | Unknown | |
$E4 | Fade-in to previous song (needed on DAC channel) | Unknown | No operation |
$E5xx | Change tempo divider to xx | No operation; there is no parameter byte in this case | |
$E6xx | Change channel volume by xx; xx is signed | ||
$E7 | Prevent next note from attacking | Unknown, possibly also no attack | |
$E8xx | Set note fill amount (??) to xx | Unknown, possibly the same | |
$E9xx | Add xx to channel key displacement; this value accumulates (that is, if the accumulated value was 4 and xx is 3, the new accumulated value is 7) | Unknown | No operation; there is no parameter byte in this case |
$EAxx | Set music tempo to xx | Unknown, possibly the same | |
$EBxx | Change Tempo Modifier to xx for ALL channels | Unknown, possibly the same | |
$ECxx | Change channel volume to xx; xx is signed(??) | No operation; there is no parameter byte in this case | |
$ED | Unknown | No operation | |
$EE | Something with Voice Selection | No operation | |
$EFxx | Change current voice (YM2612/SN76489)/sample (RF5C164) to the one at table index xx
| ||
$F0wwxxyyzz | Modulation
|
Unknown; there are no parameter bytes in this case | |
$F1 | Turn on modulation | Unknown; same as $F0 | |
$F2 | Stop the track | Unknown; same as $F1 | |
$F3xx | Change current PSG noise to xx (For noise channel)
|
No operation; there is no parameter byte in this case | |
$F4xxxx | Turn off modulation; there are no parameter bytes in this case | Same as $F6xxxx | |
$F5xx | Change current PSG tone to xx(??) | No operation; there is no parameter byte in this case | |
$F6xxxx | Jump to address xxxx | ||
$F7xxyyzzzz | Repeat section of music
| ||
$F8yyyy | Call subroutine at address yyyy | ||
$F9 | Unknown | Return from subroutine | |
$FAxx | Unknown, it is unknown whether or not there are parameter bytes in this case | Unknown | |
$FBxx | Unknown | Add xx to channel key displacement; this value accumulates (that is, if the accumulated value was 4 and xx is 3, the new accumulated value is 7) | |
$FCyyyy | Unknown, it is unknown whether or not there are parameter bytes in this case | Loop Sound Effect from pointer yyyy. | Unknown, possibly loop SFX |
$FD | Unknown | No operation | |
$FE | Unknown | Invalid | |
$FF | Unknown | Invalid |
Notes on $E0
On the Mega Drive, only YM2612 channels (both FM synthesis and DAC) can be panned. On the Master System, SN76489 channels cannot be panned; on the Game Gear, they can. Both 32X PWM and RF5C164 PCM channels can be panned.
OutRun on Mega Drive stores DAC panning setup in its DAC table, therefore its DAC channel cannot be panned and you will need to reconfigure the panning when importing from OutRun.
Note on $F3
On the Mega Drive and 32X, the $F3 flag determines whether the composer wishes to use up to three tones or up to two tones and one noise on the SN76489. You may freely use a channel's tones until $F3 is issued, at which point the channel is changed to a noise channel — this cannot be undone unless the song is started over or a new song is played. On the Master System and Game Gear, the third channel is always tone and the fourth channel is always noise.