ACIDCAT . FILE FORMAT REFERENCE

SNDH Anatomy

Atari STSNDH music file
rev 2026.09
magic SNDH at 12
endian big (68000)
container Pack-Ice
samples none; code

An .sndh is a program: the music's own 68000 player routine, ripped from the game or demo it came from, with a small tag header saying what it is and how to call it. Three branches come first -- init (with the subtune number in d0), exit and play, the last called at the replay rate -- and SNDH at offset 12 opens the tags. To hear one, a 68000 runs init once and play every tick while the YM2149 (and on the STe, the DMA sound) is emulated. Nearly every file is then packed with Pack-Ice.

file regions
▸the Pack-Ice header0x00 . drawn below

Read backwards. The packed stream is consumed from the end of the file toward this header, and the image is written from its end toward its start. Bits come from bytes taken from the end, top bit first; the lowest set bit of each byte is a marker that says it is spent. The stream alternates literal runs (a flag, then a length in fields of 1, 2, 2, 3, 8 and 15 bits, each all-ones field stepping to the next) and strings copied from what has already been written: a length from 2 to 1033 and an offset in one of three widths, or for a two-byte string one of two. One more bit after the image says whether a 32,000-byte picture was also reordered from bitplanes; music never sets it.

▸the entry branchesunpacked, 0x00 . drawn below

Usually BRA.W: 0x6000 and a signed 16-bit displacement from the address after the opcode. An entry with nothing to do can be a bare RTS.

▸the tagsSNDH to HDNS . drawn below

Text tags are four letters and a NUL-terminated string: TITL, COMM (composer), RIPP, CONV, YEAR, and FLAG, a ~ and letters for what the player needs (y for the YM2149, e for the STe, a-d for the timers it takes over). Zero bytes may pad between tags. In this file the composer, ripper, converter and year sit between the title and the part drawn second.

##nnsubtune count, two ASCII digits, often followed by a NUL
!#nndefault subtune; the specification writes #!, files write !#
TAnnn-TDnnnreplay rate in Hz on MFP timer A-D, NUL-terminated; C is the default
!Vnnreplay on the vertical blank at that rate
TIMEa 16-bit length in seconds per subtune
FRMSa 32-bit length in frames per subtune, 0 for endless; replaces TIME
!#SNa 16-bit offset per subtune, from the tag's own first byte, to each subtune's NUL-terminated name
HDNSend of the header, on an even address

Before HDNS. The end tag came with version 2 of the format. An older header just stops: the last tag's terminator, a byte of padding if needed, and the player's code at the next even address.

▸the player68000 code and data

Everything after the header: the routine the three branches reach, its pattern and instrument data, and on STe or Falcon files the DMA samples. It is position independent or relocates itself, since it is loaded wherever the host player puts it.

▸identificationICE! . SNDH

On disk, ICE! at zero (Pack-Ice 2.3 and 2.4; earlier versions write Ice! and are not described here); inside, SNDH at offset 12, after the three entries.