-
Notifications
You must be signed in to change notification settings - Fork 0
SongFormatV0
| field | start | length | value |
|---|---|---|---|
| Magic | 0x00 | 0x01 | 'X' |
| Version number | 0x01 | 0x01 | 0,1,... |
| Number of Channels | 0x02 | 0x01 | 0x09 for Adlib |
| Artist Name | 0x03 | 0x10 | padded to fit |
| Song Title | 0x13 | 0x10 | padded to fit |
| Start of Instruments | 0x23 | 0x02 | offset to start of instrument table (LSB first) |
| Start of SpFX | 0x25 | 0x02 | offset to start of SpFx table (LSB first) |
| Start of Orders | 0x27 | 0x02 | offset to start of orders (LSB first) |
| Start of Patterns | 0x29 | 0x02 | offset to start of patterns table (LSB first) |
| Instruments | 0x2B | ??? | Instruments |
| SpFX | ??? | ??? | SpFX |
| Orders | ??? | ??? | Orders |
| Patterns | ??? | ??? | Patterns |
The starting address of a given order is:
[aSong] + [aSong + koStartOfOrders] + [numChannels]*Order#
The internal structure of each order is:
| offset | field |
|---|---|
| 0x00 | pattern # for channel 0 |
| 0x01 | pattern # for channel 1 |
| 0x02 | pattern # for channel 2 |
| ... | |
| [numChannels]-1 | pattern # for channel [numChannels]-1 |
The starting address of a given pattern is:
[aSong] + [aSong + koStartOfPatterns] + 32*4*Pattern#
The internal structure of a pattern line is:
| byte | bitmask | contents | description |
|---|---|---|---|
| 0x00 | 0x80 | ΔN | 1 for new note frequency, 0 for no change |
| 0x00 | 0x7f | Note | frequency of desired note, as # semitones above C-0 |
| 0x01 | 0x80 | ΔV | 1 for new volume, 0 for no change |
| 0x01 | 0x40 | ΔI | 1 for new instrument and Note-On, 0 for no change |
| 0x01 | 0x3f | Vol | carrier volume column |
| 0x02 | 0xF0 | Instr | instrument column |
| 0x02 | 0x0F | Cmd | command column |
| 0x03 | 0xFF | Param | command parameter |
A pattern always contains 32 lines (although the player code breaks this out as a constant).
A 32-line x 4-byte pattern weighs 128 bytes.
The starting address of a given instrument is:
[aSong] + [aSong + koStartOfInstruments] + 16*Instr#
The internal structure of each instrument is:
| operator | offset | value | register |
|---|---|---|---|
| modulator | 0x00 | Amp mod/Vibrato/EG type/Key scaling/Multiple | 0x20+X |
| 0x01 | KSR/Volume | 0x40+X | |
| 0x02 | AD | 0x60+X | |
| 0x03 | SR | 0x80+X | |
| 0x04 | Wave Select | 0xE0+X | |
| carrier | 0x05 | Amp mod/Vibrato/EG type/Key scaling/Multiple | 0x20+X+3 |
| 0x06 | KSR/Volume | 0x40+X+3 | |
| 0x07 | AD | 0x60+X+3 | |
| 0x08 | SR | 0x80+X+3 | |
| 0x09 | Wave Select | 0xE0+X+3 | |
| both | 0x0A | Feedback/Connection | 0xC0+Y |
| 0x0B | -- | -- | |
| 0x0C | -- | -- | |
| 0x0D | -- | -- | |
| 0x0E | -- | -- | |
| 0x0F | -- | -- |
Notice the modulator comes BEFORE the carrier.
TBD
"Position" within a song needs: (order, line, tick).
If you are cranking through channel-by-channel setting registers, you will also need to internally track (channel, pattern). (channel is simply [0..kNumChannels) in order, pattern is lookup'd from order and channel). There is no point in exposing these variables as they only are mutated while the player is crunching on a tick and they do not carry over between ticks.
"Player state" is (speed, playing, vregs). playing is only toggled from outside. speed is only set by the player itself through 'F' commands. vregs is only set by the player itself via pattern lines.
"Song info" is (numChannels, startOfInstruments, startOfSpFX, startOfOrders, startOfPatterns). It only changes on song load.
| Cmd | Purpose |
|---|---|
| 0 | Arp |
| 1 | Slide Up |
| 2 | Slide Down |
| 3 | |
| 4 | |
| 5 | -- |
| 6 | -- |
| 7 | -- |
| 8 | -- |
| 9 | -- |
| A | -- |
| B | |
| C | Fine Note Cut |
| D | |
| E | Modulator Volume |
| F | Speed |