Song format

This describes the format that songs are stored in fortISSimO at the binary level. The format of files exported by teNOR is kind of irrelevant, and the format of hUGETracker’s .uge files is documented elsewhere.

Last updated as of commit 7fb8329. This is susceptible to having changed since then; see the changes since then, particularly files teNOR/export.rs and include/fortISSimO.inc.

ℹ️ For forward compatibility’s sake, it is unwise to assume that components will always be in a certain order unless otherwise specified.

For example, currently, teNOR emits duty instruments immediately after the “row pool”, and right before the wave instruments; this should be considered an unstable implementation detail. However, all fields of the song header will remain in the specified order (until the next major release of fO, anyway).

Unless specified:

  • there is no padding between any of the structures’ fields,
  • all multi-byte values are stored in little-endian format (low byte first).
  1. BYTE — How many ticks each row lasts for.
  2. BYTE — The maximum value wOrderIdx can take; inclusive. Also specifies the size of the pattern pointer arrays, below.
  3. POINTER — To the array of duty instruments.
  4. POINTER — To the array of wave instruments.
  5. POINTER — To the array of noise instruments.
  6. POINTER — To the song’s routine.
  7. POINTER — To the array of waves.
  8. For each channel, from 1 to 4: its column of the order matrix:
    1. As many as specified above:
      1. POINTER — To a pattern.

Patterns

A pattern is simply a collection of rows; teNOR attempts to find overlap between the patterns to minimise how much space they take; thus, all patterns are coalesced into sort of a “pool of rows”.

There is no definitive end to the row pool—simply, only as many rows are emitted as are necessary.

Rows

Rows are constitued differently depending on whether they belong to a “main” pattern, or a subpattern.

Pattern rows

  1. BYTE — The effect’s parameter.
  2. BYTE — Split as follows:
    • UPPER NIBBLE — The instrument ID, or 0 for “none”.
    • LOWER NIBBLE — The effect ID.
  3. BYTE — The note’s ID, or 90 for “no note”.

Subpattern rows

  1. BYTE — The effect’s parameter.
  2. BYTE — Split as follows:
    • UPPER NIBBLE — Bits 0–3 of the next row’s ID.
    • LOWER NIBBLE — The effect ID.
  3. BYTE — Split as follows:
    • BIT 7 — Bit 4 of the next row’s ID.
    • BITS 0–6 — The offset from the base note, plus 36; or 90 for “no offset”.

In order to allow looping back on the same row as an effect, subpatterns rows all have a built-in jump target. Since there are 32 possible rows to jump to, 5 bits are needed—the unused 7th bit of the note byte is used to store that 5th bit.

Effects

Effect IDs are unchanged from hUGETracker. The effect parameter is, however, sometimes different.

EffectID (hex)Stored parameter
Arpeggio0Unchanged.
Porta up1Unchanged.
Porta down2Unchanged.
Tone porta3Unchanged.
Vibrato4Unchanged.
Set master vol5Unchanged.
Call routine6Unchanged.
Note delay7Unchanged.
Set panning8Unchanged.
Change timbre9Unchanged.
Vol slideAUnchanged.
Pos jumpBThe pattern ID is stored in wOrderIdx format.
Set volCNibbles are swapped from hUGETracker.
Pattern breakDThe row ID is stored in wForceRow’s format.
Note cutEUnchanged.
Set tempoFUnchanged.

Instruments

Instruments are grouped in “banks” by their type. Each bank is an array of up to 15 instruments, with no padding in-between.

Duty

  1. BYTE — Frequency sweep, in NR10 format.
  2. BYTE — Duty & length, in NR11/NR12 format.
  3. BYTE — Volume & envelope, in NR12/NR22 format.
  4. POINTER — Pointer to the subpattern, or 0 if not enabled.
  5. BYTE — Control bits:
    • BIT 7 — Always set.
    • BIT 6 — Whether the “length” is enabled.

Wave

  1. BYTE — Length, in NR31 format.
  2. BYTE — Volume, in NR32 format.
  3. POINTER — Pointer to the subpattern, or 0 if not enabled.
  4. BYTE — Control bits:
    • BIT 7 — Always set.
    • BIT 6 — Whether the “length” is enabled.
  5. BYTE — ID of the wave to load.

Noise

  1. BYTE — Volume, in NR42 format.
  2. POINTER — Pointer to the subpattern, or 0 if not enabled.
  3. BYTE — Control bits:
    • BIT 7 — 0 if the LFSR should be in “long” (15-bit) mode, 1 if the LFSR should be in “short” (7-bit) mode.
    • BIT 6 — Whether the “length” is enabled.
    • BITS 0–5 — Length, in NR41 format.

Waves

Each wave is an array of 16 bytes, stored directly in wave RAM format.

There are up to 16 waves; wave IDs start at 0.

Routine

See the dedicated chapter.