Listen closely to a careless C64 tune and some notes start a fraction late. Not every note and not by the same amount: one attacks crisply, the next stumbles. The cause is a timing fault in the SID’s envelope generator, and the standard cure is the hard restart: a couple of frames before each new note, the music player cuts the voice’s gate and zeroes its envelope settings, so that the fault happens in the gap, where nobody can hear it.
Rob Hubbard’s 1985 music player already did this. The explanation of why it works came much later, from people measuring the chip cycle by cycle.
The envelope generator
Each SID voice has an envelope: a level from 0 to 255 that rises during the attack, falls to the sustain level during the decay, and falls to zero during the release. The attack, decay and release settings, 0 to 15 each, set how fast it moves.
The speed comes from a counter that runs once every clock cycle. Each setting selects a comparison value, and every time the counter reaches it, the counter starts again from zero and the envelope takes one step. In Dag Lem’s reSID emulator, which models the chip from measurements, setting 0 steps the envelope about every 9 cycles and setting 15 about every 31,251. (The Codebase64 notes on the subject call the counter a 15-bit LFSR, a linear-feedback shift register; the effect is the same.)
The delay bug
The fault appears when the comparison value drops while the counter is already past it. Suppose a slow release has the counter at 20,000 and the program now writes a fast attack, whose comparison value is 9. The counter does not notice that it has overshot. It keeps counting until it wraps round at 32,768, which reSID’s source describes as “2^15 = 0x8000”, and only then counts up to 9 and steps the envelope. For all that time the envelope stands still.
32,768 cycles is about 33 milliseconds on a PAL C64, a little over one and a half 50 Hz frames; the Codebase64 notes round it to “1.7 frames”. How long a given note waits depends on where the counter happened to be, anything from nothing to the full 33 ms, which is why the result sounds uneven rather than simply late.
The notes give the rule: “Equal or larger values for A, D or R are safe.” Any write that lowers one of them can trigger the stall, and so can the gate bit. Setting the gate moves the envelope from release to attack, clearing it moves it to release, and if the new phase has a faster rate than the old one, the comparison value drops just as if the program had written a smaller number.
The classic hard restart
The cure is to provoke the stall on purpose, early. Hubbard’s player, which Anthony McSweeney disassembled from Monty on the Run for C=Hacking in 1993 and found in “Rob’s first 20 or 30 musics”, does it when a note’s length runs out:
lda voicectrl,x ;length exceeded so
and #$fe ;start the release
sta $d404,y ;and kill adsr
lda #$00
sta $d405,y
sta $d406,yClearing bit 0 of the control register ends the note, and writing zero to attack/decay ($D405) and sustain/release ($D406) makes the release as fast as the chip allows. When the next note starts, the player writes the control register (waveform and gate) first, then attack/decay, then sustain/release, the order that Lasse Öörni (Cadaver) recommends in his guide to writing a music routine because it “ensures the sharpest attack possible”.
Setting everything to zero works for two reasons. The write of zero may well trigger the stall, but the stall now happens during the silence before the next note. Once it has passed, the counter is running against the smallest comparison value of all, so it never climbs above about 9. Every instrument’s attack setting has a comparison value at least that large, so the next note’s writes can only raise it: in the Codebase64 notes’ words, “afterwards one can write any value safely in the beginning of the next note”. Meanwhile the fastest release brings the envelope down to zero, so the new note’s attack starts from silence.
The gap has to be long enough to contain the worst case. A music player usually runs once per frame, and one PAL frame, 20 ms, is shorter than the 33 ms stall, so players start the hard restart two frames before the note: “to make sure that the 1.7 frames long bug-condition takes place before the next note”. Linus Walleij (King Fisher of Triad) gives the same figure, “2/50 second (2 frames) before next attack”, and quotes Dag Lem for the true minimum, “33 ms = 2^15 cycles”.
The values matter. The Codebase64 notes show the opposite case: a hard restart that leaves a slow setting such as $FF in attack/decay, followed by a note with attack 0, can trigger the stall all over again on the note itself.
The test-bit method
Cadaver sorts players into two families. The old method, “what old players such as Hubbard’s use”, is the one above. It “works well on both PAL & NTSC machines and isn’t sensitive to timing”. Newer players such as JCH’s and DMC use the test bit, bit 3 of the control register, which holds the oscillator in reset. Two or more frames before the note they set the envelope settings to a preset such as $0000 and clear the gate. On the note’s first frame they write the instrument’s attack/decay and sustain/release, then $09 (test bit and gate) to the control register; only on the second frame do they write the real waveform, and the note is heard. Cadaver says it “gives a nice sharp sound” but “works reliably on PAL machines only”.
Neither method touches the release. Wallej puts it plainly: “Hard restart only fixes problems with attack, not the ever-present problem with release.”
A second envelope trap
The SID has another envelope fault that bit sound-effect programmers. Martin Walker described it in his ZZAP!64 development diary in September 1988: the envelope “seems not to like being re-triggered if the sustain level is still maximum”, so “a sound effect would go completely silent when triggered twice in quick succession”. It took him hours to find. His fix was never to use sustain level 15, “always set it to 14 or lower”.
reSID’s source records the same behaviour from measurements: with the envelope at its maximum, 255, a quick release followed by a new attack can flip the level from 255 to 0, where it stays “frozen at zero” until the voice is released and attacked again. A sustain of 15 holds the envelope at exactly 255 (reSID’s sustain levels run in steps of 17, from 0 to 255), which would explain why Walker’s limit of 14 made the problem go away.
Making it cycle-exact
A classic hard restart makes the envelope quiet, but it does not put the chip into a known state, accurate to the cycle. That matters to anyone who wants to measure the envelope’s timing exactly. In 2011 the coder shrydar tried, and “the best routine I could manage took well over three frames, as I had to twice recapture a potential rate-counter escape”. In early April 2015 lft found a shortcut using a transition the chip handles safely, a slow attack into a fast decay, 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 overflow from 255 to 0 then brings the envelope back down. One bug undoes another, and the processor-heavy part takes “a mere ten raster lines”, provided that “all DMA and interrupts” are disabled for the last 600 cycles.
The practice came first
Composers used hard restarts for years without a precise account of the counter. The Codebase64 notes, written after discussions on CSDb and IRC, describe the classic recipe as “a bit vague, but without hard knowledge this is the level at which hard restart has been previously described”. The comments in reSID, where Dag Lem writes that the wrap-round “has been verified by sampling ENV3”, the readable output of voice 3’s envelope, supplied the hard knowledge.
Sources
- Anthony McSweeney, “Rob Hubbard’s Music: Disassembled, Commented and Explained”, C=Hacking, issue 5, 7 March 1993: the player from Monty on the Run and the games using it; the release routine and the order of the register writes for a new note.
- mixer and others, “ADSR Discussion Notes”, Codebase64 wiki (web, secondary): the 32,768-cycle stall, the safe and unsafe writes, the two-frame hard restart and its example settings. https://codebase64.net/doku.php?id=base:classic_hard-restart_and_about_adsr_in_generally
- Dag Lem, reSID,
envelope.ccandenvelope.h(as included in the VICE 3.10 source code): the rate counter and its comparison values, the “ADSR delay bug” wrap at0x8000, and the envelope frozen at zero. - Lasse Öörni (Cadaver), “Building a musicroutine”, Codebase64 wiki (web, secondary): the old and test-bit methods and the register write order. https://codebase64.net/doku.php?id=base:building_a_music_routine
- Linus Walleij (King Fisher/Triad), “A SID player routine”, Codebase64 wiki (web, secondary): two frames before the attack, Dag Lem’s 33 ms minimum, and release. https://codebase64.net/doku.php?id=base:a_sid_player_routine
- ZZAP!64, issue 41, September 1988, p. 101: Martin Walker’s diary.
- shrydar, with contributions from lft, “A new kind of hard-restart”, Codebase64 wiki (web, secondary). https://codebase64.net/doku.php?id=base:a_new_kind_of_hard-restart