Skip to content
Techniques & Technology

Hard restart

Working around the SID's ADSR bug

Every C64 music driver silences and resets a SID voice two frames before each note, because the chip's envelope generator can otherwise stall for 1.7 frames. The workaround is older than the explanation.

commodore-64audiosidmusichardware-quirkprogramming1985–present

Every Commodore 64 music driver silences and resets a SID voice a couple of frames before it plays the next note. That is a hard restart, and it exists to dodge a bug in the chip’s envelope generator.

The bug

SID’s ADSR is driven by a 15-bit LFSR acting as a prescaler. Each A, D or R register value maps to a comparison value in a table inside the chip; when the LFSR matches it, the envelope counter advances.

ADSR bug occurs, when A, D or R values are changed when the LFSR has already passed the corresponding new comparison value. The LFSR must then run a full cycle before the new comparison value can be reached. Full cycle takes 32768 cycles. That is 1.7 frames in terms of 1/50 fps frames.

A note that should attack instantly instead sits silent for most of two frames. On a tune driven once per frame, that is audible and irregular.

The rule for avoiding it is one line: “Equal or larger values for A, D or R are safe.” Any write that lowers one can trigger the stall — including gate on and gate off, which move between envelope states and can therefore step downward without the driver writing a lower number itself.

The classic workaround

Rather than avoid the condition, drivers provoke it early where it does no harm:

The HR is started 2 full frames before next note to make sure that the 1.7 frames long bug-condition takes place before the next note and to make sure that the bug has been passed before next note starts.

The routine stops the oscillator so nothing is heard, sets ADSR to a chosen value, and gates off. Two frames of budget for 1.7 frames of stall, with the difference as margin.

The choice of ADSR value for those two frames is not arbitrary — it determines what the LFSR comparison value will be when the note starts, so it decides whether the note begins cleanly.

Two ways to do it

Cadaver’s guide to writing a music routine sorts the players into two camps. The old method, “what old players such as Hubbard’s use”, clears the gate bit and zeroes the ADSR registers a couple of frames before the note ends, then writes the new note as waveform, attack/decay, sustain/release in that order. It “works well on both PAL & NTSC machines and isn’t sensitive to timing”. The “testbit” method of newer players such as JCH’s and DMC sets ADSR to a preset two or more frames early, writes the instrument’s ADSR first on the note’s first frame followed by $09 (test bit plus gate) to the waveform register, and only loads the real waveform on the second frame. It “gives a nice sharp sound” but “works reliably on PAL machines only”.

Linus Wallej (King Fisher/Triad) singles out the JCH player for the same reason — “what makes this and some other c64 players better than others is that it utilizes something called ‘hard restart’” — and adds the limit: it “only fixes problems with attack, not the ever-present problem with release”.

The practice came first

The article documenting this is candid that the technique was in use long before anyone could say precisely why it worked:

This is a bit vague, but without hard knowledge this is the level at which hard restart has been previously described.

It presents itself as notes on a live argument — “few notes about what has been discussed about SID envelopes lately at CSDB and at IRC. Errors are all mine, and this being a Wiki, you can fix them. :)” — and carries a correction in its own body where a reader disputed an analogy.

Making it cycle-exact

The classic hard restart gets the envelope quiet. It does not get the chip into a known state, which is what you need in order to measure the envelope’s behaviour at all.

shrydar set out to do that in 2011 and got stuck: “the best routine I could manage took well over three frames, as I had to twice recapture a potential rate-counter escape.”

lft solved it in early April 2015 by using a transition the chip handles safely — a slow attack into a fast decay — to do the recapture near the top of the envelope’s range:

if this is all done up around env=$fe, it can be performed in just a couple of hundred cycles. The envelope overflow bug (where an attack triggered when env=$ff causes env to wrap back to $00) can then be used to bring the envelope back down.

One bug is used to escape another. The result takes ten raster lines of CPU against the original’s three-plus frames — with the condition that “all DMA and interrupts must be disabled for the last 600 cycles.”

See also

Not yet fact-checked. This entry was drafted by an AI and nobody has verified it. The dates, figures and technical details may be wrong. Use it to find your bearings, then confirm anything that matters against a primary source.