Filter programming on the SID
What it is#
The sid has one filter shared by its three voices: an 11-bit cutoff, a resonance nybble, per-voice on/off switches ($D417) and a band selection — low-pass, band-pass or high-pass ($D418). Sweeping cutoff and resonance per frame is the C64's second big animation tool after pulse-width-modulation (source: s-sid-wizard-manual).
The registers (datasheet and Programmer's Reference Guide)#
| register | contents |
|---|---|
$D415 (21) | cutoff bits 0–2 (the low 3 bits; "Lcf = 0 through 7") |
$D416 (22) | cutoff bits 3–10 ("Hcf = 0 through 255") |
$D417 (23) | high nybble resonance (16 linear steps, 0 none … F maximum); bit 0 voice 1, bit 1 voice 2, bit 2 voice 3, bit 3 external input routed through the filter |
$D418 (24) | bit 4 low-pass, bit 5 band-pass, bit 6 high-pass, bit 7 3 OFF (voice 3 silent); low nybble = master volume |
The cutoff is an 11-bit value covering "approximately 30 Hz–10 kHz" with the C64's 2200 pF capacitors (FCmax = 2.6E-5 / C; the range extends "approximately 9 octaves below" the maximum). Low-pass passes below the cutoff and rolls off above it at 12 dB/octave ("full-bodied sounds"); band-pass attenuates both sides at 6 dB/octave ("thin, open sounds"); high-pass passes above and rolls off below at 12 dB/octave ("tinny, buzzy sounds"). The modes "ARE additive": low + high = a notch ("band reject"); at least one mode and one routed voice are needed for any effect; "the Filter is, perhaps, the most important element in SID … The best results are achieved by varying the Cutoff Frequency in real-time" (source: s-mos-6581-datasheet). The Programmer's Reference Guide's Example 5 turns Example 1 tinny with POKE S+22,128 : POKE S+21,0 : POKE S+23,1 : POKE S+24,79 (high-pass, volume 15) and adds: "changing the filtering of a sound as it goes through the ADSR phases of its life can produce interesting effects"; its hand clap is noise through the high-pass filter with cutoff high byte 104 (source: s-c64-prg-chapter-4-sound). The datasheet's modulation ideas: ENV 3 added to the cutoff gives "harmonic envelopes, WAH WAH", OSC 3's noise output added to it a "Sample and Hold" effect (source: s-mos-6581-datasheet). Chip variation: the 8580's filter "has totally different characteristics from the MOS6581 analog filter" (source: s-hvsc-sid-file-format).
Why it sounds the way it does: the designer's account#
bob-yannes in 1996 (source: s-sidmusic-yannes-interview for every quotation in this paragraph): the filter is "a classic multi-mode (state variable) VCF design. There was no way to create a variable transconductance amplifier in our NMOS process, so I simply used FETs as voltage-controlled resistors to control the cutoff frequency. An 11-bit D/A converter generates the control voltage for the FETs (it's actually a 12-bit D/A, but the LSB had no audible affect so I disconnected it!)" — hence the 11-bit cutoff above. Resonance is "controlled by a 4-bit weighted resistor ladder. Each bit would turn on one of the weighted resistors and allow a portion of the output to feed back to the input"; the low-, band- and high-pass outputs exist simultaneously and analog switches select the combination — "a notch filter was created by enabling both the high and low-pass outputs simultaneously". His verdict: "The filter is the worst part of SID because I could not create high-gain op-amps in NMOS, which were essential to a resonant filter … different lots of SID chips had different cutoff frequency characteristics. I knew it wouldn't work very well, but it was better than nothing and I didn't have time to make it better"; with more time "a proper MOS op-amp … would also have greatly improved the filter, particularly in achieving high resonance". The root cause of chip-to-chip variation in one sentence: "the resistance of the FETs varied considerably with processing, so different lots of SID chips had different cutoff frequency characteristics." The datasheet's ENV3-readback "filter sweeps" were, in his words, a convenience "for novice programmers".
Christer Rindeblad's account agrees: "The filter was the last feature we added in, and there was no time to make it perfect … Computer simulations told us the filter wouldn't work out very well. And it didn't. I had no chance to correct the errors." (source: s-sidmusic-creation-of-the-sid-chip)
How the working composers lived with it: jeroen-tel — "It was a real bugger when it came down to using the SID-filter so that it would sound good on every C64. There were several versions of the SID-chip around, which varied a lot. But in the end I got hold of all C64 versions and could do some 'average' filter settings. But it remained different.." (source: s-sidmusic-jeroen-tel-interviews); martin-galway — the filter was his favourite feature of the chip and his "worst memory … I wish they were able to fix it" (sources: s-remix64-galway-interview, s-sidmusic-galway-interviews).
How to do it#
SID-Wizard#
The instrument's filter-program table:
| entry | meaning |
|---|---|
$8r..$Fr xx | set resonance r and cutoff xx; the high nybble picks the band: 9 low-pass, A band-pass, C high-pass |
$00..$7F xx | add xx to the cutoff, this many times |
$FE xx | jump to table position xx |
Third column: $00–$7F / $FF–$90 keyboard tracking (cutoff follows the note pitch), $8x filter-switch override. Behaviour: filters are switched on automatically for channels using a filtered instrument; if several tracks try to control the filter, the leftmost channel wins; an instrument with an empty first table row is filtered but does not control the filter. C=+F toggles filter reset on instrument selection.
Pattern effects: $0B xx go to filter table position · $0F xx set cutoff hi-byte · $1C xx shift the cutoff hi-byte permanently · $1F xx write the filter-switch/resonance register · $Bx filter band nybble · $Fx resonance. The Extra player adds a filter-switch/resonance effect and keeps program tables from being skipped; Light lacks keyboard tracking and the 11-bit filter; Bare/Demo lack the filter small effects and filter shift (sid-player-routine). The cutoff bar in the top border shows what the filter does. With a table speed above $80 the filter table runs at multispeed rate (source: s-sid-wizard-manual).
More from the SID-Wizard book: the band nibble also allows combinations — $8 filter off, $9 low-pass, $B low+band, $C high-pass, $D low+high, $E band+high, $F all three (then resonance cannot be $F); the cutoff byte spans roughly 30 Hz–10 kHz; keyboard tracking is a signed exponential strength ($00–$7F brighter for higher notes, $FF–$90 weaker); $80–$8F in the third column is a filter-switch override kept for GoatTracker imports ($81 channel 1, $82 channel 2, $84 channel 3 — printed "channel 4" in the book — $88 external input, combinable). "The latest instrument always takes over the control over the common filter" ("like with polyphonic jamming in JCH's editor"); C=+F toggles whether the program restarts on every note (bright FILT. header) or only on instrument selection. The book's bass filter: 9B 45 (low-pass, resonance B, cutoff $45 — "quite dark") followed by 40 05, which opens the cutoff by $05 sixty-four times (source: s-witchmaster-creating-chiptunes-with-sid-wizard). The 1.6 charts print the same band list but skip $A (band-pass alone), which the beginners' tutorial confirms as "A (band pass)"; the tutorial's first filter row is 9F 30 — low-pass, resonance F, cutoff $30 — and it repeats the rule that an instrument with a blank first row is "filtered, but not to control the filter" (sources: s-sid-wizard-charts-and-tables, s-chipmusic-c64-music-for-dummies).
What the player actually does (SW 1.97 player.asm)#
The mechanism behind "the leftmost channel wins", from m64's duet research (flprog/STRTSND, player.asm 2175–2328, 1931–2009; source: s-duet-research-sidwizard-deep):
- There is one filter program per SID, and only the current controller track executes it. At init the controller is a
$0F"nobody" sentinel — no filter program runs until an instrument takes over. - Arbitration happens at note start, by the FIRST byte of the starting instrument's filter table:
$00→ passive (the channel's routing bit is switched on, controller untouched);$FF→ unfiltered (own routing bit off; if this track was the controller, the program parks on the$FFrow); anything else → take over: this track becomes controller and the table starts from its pointer next tick. Leftmost priority is emergent: channels are processed 3→2→1, so on simultaneous starts channel 1 writes last and wins. An instrument with filter-reset disabled (ctrl bit 7) and no re-typed instrument number skips arbitration entirely — including its routing-bit update. - The table position is per SID but interpreted against whatever instrument currently sits on the controller track — switch instruments there without a reset and the offset points into the wrong table (a real quirk, reproduced by DUET).
- The sweep works on the true 11-bit cutoff ((hi<<3)|low3), the "fine filter sweep" of the Normal/Extra players; the sweep counter is not reset by a take-over.
- Column 3's
$80–$8Foverride replaces the entire$D417routing nibble (all three channel bits plus external-in) and zeroes the keyboard-track value. - Write-out each frame:
$D416= kb-track curve value (indexed by the controller track's pitch) + cutoff-hi + the permanent$1Cshift, carries chained;$D415gets the raw low 3 bits, un-tracked. Big-FX$0B(filter-table jump) skips the controller check — it moves the shared position from any track (quirk).
GoatTracker#
The filtertable: 00 xx set cutoff, 01–7F xx modulate for that many ticks at signed speed, 80–F0 xx set passband (left high nibble 9 low-pass, A band-pass, C high-pass) with the right byte = resonance nibble + channel bitmask (as in command BXY), FF xx jump. A set-parameters row directly followed by a set-cutoff row executes in the same frame. Examples: 90 F1 / 00 40 / FF 00 — low-pass, resonance F, channel 1 only, cutoff $40; A0 87 / 00 00 / 7F 01 / 7F 01 / 7F FF / 7F FF / FF 03 — band-pass on all channels sweeping up 254 ticks and back; C0 F2 / 00 F0 / 90 F2 / 00 50 — high-pass then low-pass on the next frame. From the pattern: AXY sets the filtertable pointer, BXY filter control (X resonance, Y bitmask; B00 = off and stop), CXY cutoff. "In most cases it makes sense to have a filter-controlling instrument only on one channel at a time"; the filtertable runs on every tick, and the test note plays filtered only on a channel selected in the filter parameters (source: s-goattracker-readme).
Furnace#
furnace sets the filter from the pattern with 4xxx (cutoff 000–7FF), 13xx (resonance 00–0F), 14xx (mode: 00 off, 01 low, 02 band, 03 low+band, 04 high, 05 notch, 06 high+band, 07 all), 24xx/25xx (cutoff slide up/down at speed xx, 00 stops) and 1Bxy (reset the cutoff on the next note / now); 11xx is a discouraged coarse cutoff 00–64. Per instrument: Enable filter routes the voice, Initialize filter sets cutoff 0–2047, resonance 0–15 and the mode (low, high, band, ch3off — "selecting both 'low' and 'high' results in a bandstop (notch) filter"), and Absolute Cutoff Macro makes the cutoff macro write 0–2047 directly instead of relative steps. Because the filter is global, the chip configuration decides whose macros win: Left to right (channels 1→3, the last wins) or Last used channel (the latest note-on wins) — a third policy beside SID-Wizard's leftmost-channel rule and its "latest instrument takes over" (source: s-furnace-c64-docs).
CheeseCutter#
"All filter settings can only be controlled from the filter table" — there are no filter commands, only the per-note filter-pointer range 60–7F in the sequence command column (cheesecutter). A table row with byte A ≥ $80 is a filter init: type, resonance + voice bitmask, initial cutoff (90 F1 10 7F = low-pass $10, resonance $F on voice 1, cutoff $10, stop; C0 07 FF 04 = type $40, all voices, keep the cutoff, jump to row 4). Sweep rows are (duration, add) pairs where the add is two's-complement ("add value of FF actually subtracts by one"), and the sweeps have 10-bit accuracy — "4 times as fine as those on JCH players". An instrument's filter byte 00 means the voice keeps playing whatever filter program another instrument set — the shared-filter idiom again (source: s-cheesecutter-guide).
defMON#
Four sidTAB columns instead of a filter table (defmon): RE = resonance nibble + voice-mask nibble, with the defMON-only mask value 8 = "activates the filter for the Voice the Sound Program is called from" — so one filter chunk works from any voice (F8 = resonance $F, filter on, this voice); FV = filter type (first digit, combinable low/band/high); CP = a relative cutoff offset added or subtracted from the base value; ACID = a four-digit absolute cutoff that doubles as a slide command — $0000–$7FFF set, $8000–$BFFF slide up, $C000–$FFFF slide down — with the minimum locked at 200 "Acids" "so you don't blow your speakers/eardrums". Two cutoff controls means a filter envelope can be offset without rewriting it. Two internals worth knowing: defMON never writes $D415, so the two rightmost digits of a set value do nothing (anarkiwi's register dump, 2018-12-22), and on a detected 8580 the player floors the cutoff at $0200 ($0000 on a 6581) to make the chips behave more alike (sources: s-defmon-wiki-manual, s-defmon-wiki-defmoning-101, s-chipmusic-defmon-thread).
In your own player routine#
Walleij's MIDI player writes the instrument's default cutoff ($D415/$D416) and resonance at instrument change, then steps a per-instrument filter macro that writes only the cutoff high byte ("most c64 players use only the high byte") and a resonance byte masked & $F0 | $0F — "it has dangerous effects like switching filter off or on for current channel. Some will mask off the low nybble for this, which is my choice" (all three voices stay routed); a wheel or an LFO ("sinus or sawtooth, or square wave") can drive the cutoff "for a TB303-like-effect" (source: s-walleij-sid-player-routine).
LSDj (the wave synth's filter)#
Not a filter on the output like the sid's, but a filter applied when the synth generates its 16 waves, so what it shapes is the wavetable itself. FILTER is Lowp / Highp / Bandp / Allp, with CUTOFF 00–FF and Q; both interpolate from the synth's first wave to its last (source: s-lsdj-manual).
These are not filters in the sid sense at all — nothing is filtered on the way out: "this is a wavetable synthesizer, it's not a subtractive synthesizer, so the filters that you see here are actually just kind of emulated … it will just generate what you might expect to see from these kinds of filters" (source: s-defensemech-wave-channel-ramble). What they shape is the 16 waves the synth writes.
Two things make it unusually literal. First, CUTOFF is calibrated in harmonics: every multiple of 10 is one partial, so bandpass at 20 sounds the octave, 30 the octave + fifth, 40 two octaves — the filter selects an interval (harmonic-series). Second, lowpass at CUTOFF 10 with Q 3 and VOLUME 08 leaves the fundamental alone, which is how you get a sine out of a channel that has no sine setting (source: s-intense-tech-01-wave-synth-deep-dive-part-1).
Walked up by ear on a bandpass with high Q, the whole series is audible: 10 fundamental, 20 octave, 30 fifth, 40 octave, 50 major third, 60 fifth, 70 an out-of-tune seventh, 80 octave, then 90 a major second, A0 a major third, B0 a sharp fourth, C0 a fifth, D0 a flattened sixth-to-seventh, and nothing much above that — "it probably only generates up to here really". On a square signal the even harmonics are simply absent: CUTOFF 20 changes the volume and nothing else, and the first thing that speaks is the fifth at 30 (source: s-defensemech-wave-channel-ramble). A sweep that must outlast one synth's sixteen waves is built as a chain of synths, one CUTOFF slice each, played in sequence (wavetable-programming; source: s-defensemech-learning-lab-shorts). A sweeping CUTOFF with a high Q is the channel's wah — start high, end low, PLAY ONCE, SPEED 3, LENGTH F.
Q is what makes a sweep audible rather than merely dark: "if you're just going to have a standard lowpass you're just gonna hear more and more bass and less and less treble; with the resonance you're gonna actually have some kind of a sonic cursor" (source: s-painperdu-02-wav-channel-walkthrough).
Because the synth screen is editable while the song runs, Q is also a performance control and not only an instrument setting: "altering Q in wave instruments while a song is playing is a lot of fun for sweepy type stuff" — and Lazerbeat notes it works in song mode, not just live mode (live-tracking; source: s-chipmusic-lsdj-live-performance).
Allpass is the odd one and the useful one for two-note sounds: it passes everything and shifts phase around the cutoff, so a high Q promotes one partial to near-melodic prominence while "no matter how much you boost this Q you really never lose the root note" — defense-mechanism's route to a power chord on one channel, with Q around 8–9 for the fifth. Lowpass gives a cleaner but thinner version of the same, with the harmonics above the fifth rolled away (source: s-defensemech-ghost-channel-allpass; the technique is ghost-channel). Whether the filter overdrives is decided by DIST and LIMIT rather than by the filter itself (wavetable-programming).
Impulse Tracker / OpenMPT (resonant filter)#
The PC-tracker equivalent of the SID filter table is a column of Zxx macros (source: s-openmpt-manual-zxx-macros): by default Z00–Z7F set the cutoff (00 lowest … 7F highest; the top is about 5 kHz, or 10 kHz with Extended Filter Range) and Z80–Z8F the resonance; the underlying messages are F0F000xx cutoff, F0F001xx resonance, F0F00200 lowpass and F0F00210 highpass (mutually exclusive), so a fixed macro can switch filter type. Instruments carry default cutoff, resonance and (MPTM) filter mode; cutoff at maximum with resonance at minimum turns the filter off; the pitch envelope can act as a filter envelope (S7D/S7E force one or the other in MPTM); S9C makes filters stay until switched off (Impulse Tracker behaviour), S9D resets them on every note; MPTM adds cutoff and resonance swing per instrument; the filter never touches plugins (sources: s-openmpt-manual-instruments, s-openmpt-manual-effect-reference). On OPL instruments the cutoff macro scales the modulator — "effectively … a lowpass filter" (opl-fm-instruments; source: s-openmpt-manual-samples). The Amiga's only filter is the LED low-pass toggled by E0x — "connects filter (turns power LED on)" — which OpenMPT calls "muffled" and emulates only with the Amiga resampler (sources: s-protracker-23d-help, s-openmpt-manual-effect-reference).
In the classic drivers (1984–1988)#
- The earliest filter code in the wiki — Datasoft's Dig Dug (1984), disassembled by stefano-tognon. There is no filter table: the filter is a set of step commands inside the same byte-stream that plays the notes —
$28 lo hicutoff,$30 nresonance (n & $F0, written together with the voice-routing bits),$50route this voice through the filter,$60take it out again,$58 nfilter mode (n & $F0). The four settings that have to share two registers are kept in four zero-page shadows —$FArouting,$FCresonance,$FBmode,$FDvolume — and written as$D417 = $FC + $FAand$D418 = $FB + $FD, the shadow-register pattern every later driver repeats (sid-player-routine). Its PSID default subtune is a plain worked example: low-pass (mode nibble$10), cutoff$0087, resonance$20on a triangle bass, with a pulse voice routed through the same filter (source: s-sidin01-dig-dug-rip). - soundmonitor: registers 16–23 of a sound — filter type nibble (
FF= "this sound touches no filter register", which stops the SID clicking when filter bits change; a filtered voice stays filtered until a sound clears 16–23 to00), resonance + voice mask (= SID register 23), cutoff, and a filter EG with up/down times, step (lo nibbles and a hi byte,02–20recommended), restart mode and a trigger-voice mask (source: s-64er-soundmonitor-article). - musicfile: four 10-byte
filterbytesprograms (fb0: $a0,$fe,$02,$ff,$80, fp0,$02,$22,$42,$62withfp0 = $10 + volumeas the$D418byte), plus a "Strangefilter" routine with its ownstrafiltertable (source: s-realdmx-sid-players). - The composers' complaint: chris-huelsbeck — "the SID chip / C64 filter design, which caused each machines filter to sound almost unpredictably different" (source: s-remix64-huelsbeck-interview); VGMPF's VICE advice for his tunes: 6581 with bias 180 for most, but not all (source: s-vgmpf-chris-huelsbeck). matt-gray "had to make a lot of use of the filters" on Vendetta "to keep the mood" (source: s-c64com-matt-gray-interview).
- galway-player (Arkanoid): the filter belongs to one voice and is retriggered with a cutoff envelope on every note of that voice — a 16-byte table of four signed adders with durations:
fil02: $4D,$01 / $D3,$FF / $FB,$FF / $FF,$FF / $03,$14 / $0A,$32 …= up by$014Dfor 3 ticks, then down by$2D× 20,$05× 10,$01× 50 — a filter pluck;LF3sets resonance$f, voice 3 and low-pass in one byte, and the mode nibble is kept in a variable so the sample writes to$D418do not lose it (source: s-realdmx-galway-arkanoid-player). - bjerregaard-player: programs of signed (delta, frames) pairs moving one global cutoff, with the
$D418mode nibble in byte 2 and the resonance hard-wired to$F(FONT !by $F1,$F2,$F4):F1 $01,$80,$10, $F0,$06,$7E, Myth'sf1 ;filterbass $90,$10,$d0,$03,$00,$7f,$ff(source: s-realdmx-bjerregaard-player). - MoN: Deenen's test tune has six 10-byte programs (
fb0 $ff,$e0,$e0,$e0,$f0, fp0,$02,$fd,$fe,$ff— start, three per-frame deltas, end, the$D418byte, three phase starts, an end frame) and a$f1,vstep command that writes$D417directly — routing and resonance as a step event; the 1990 player'sfi1–fi7are a mode nibble, a start cutoff and (frames, delta) pairs,fi4 !by $10,$58 / $01,$50 / $03,$d0 / $08,$ff / $20,$02 / $ff(sources: s-realdmx-deenen-test-tunes, s-realdmx-ouwehand-mon-tunes). - Scene players: Audial Arts keeps the filter in the instrument (on/off + direction, mode + resonance, cutoff, min/max nibbles, add per frame —
$03,$1f,$c0,$4c,$18= LP, resonance$f, from$c0down$18per frame between$40and$c0); FAME's programs are (frames, cutoff add) pairs owned by the voice that last selected a filtered instrument (source: s-realdmx-audial-arts-fame-players); Red's player sweeps one cutoff-high byte from the SR nibble with$f1routing and HP + BP mode (source: s-realdmx-fredgray-kimmel-whittaker). Ocean's driver has emptyFILTER/FILTOFFhandlers, Hubbard's and Whittaker's none.
- The Arkanoid filter table as Tognon lays it out: 16 bytes with the same shape as the pitch program —
$00–$07four signed cutoff adders,$08–$0Btheir cycle counts,$0Can initial delay,$0Dthe flag (bit 7 restart from the stored cutoff, bit 0 restart from the current one, other bits hold),$0Ethe cutoff low byte and$0Fits high 3 bits; loaded byFILTA, withLF3for maximum resonance andEXCTroutines "for better controlling the filter generation" (source: s-sidin04-galway-arkanoid).
From SIDin #6–#7 (2004–05)#
- The modules driver (1988): the filter is the one thing its instrument does carry — three of its nine tables are the cutoff high byte, the resonance/routing byte (
$F0or$00) and the mode ($10low-pass,$30low+band);$86 n flagselects instrumentnand reloads those three registers only whenflag ≠ 0;$9B $02 $00 limit step delaysweeps the cutoff high byte ($9B,$02,$00,$80,$01,$08inpat04); and$95 $1C $16copies voice 3's envelope output ($D41C) into the cutoff high byte every tick — an envelope-shaped filter from the hardware (source: s-sidin07-inside-modules). - asterion-sid-tracker (2004): per instrument
FCTstart cutoff,FLMlower/upper limits,SCP/MCPspeeds,R/Tresonance and type (1 2 4); in the patternCxsets the cutoff (C0= the instrument's LFO; withMCP's high nibble0an absolute valuexinto the MSB) andExthe type (1low,2band,4high,8= do not restart the cutoff on a new note) (source: s-sidin06-asterion-sid-tracker). - digiorganizer: every sample nibble is written as
value | $10, so a tune with a digi track can use the low-pass filter only, and Polonius advises no filter at all (source: s-sidin06-digiorganizer). - xxlarge (under 256 bytes): six-entry
filter_cut($60,$78,$90,$a8,$c0,$DB) andfilter_res($01,$41,$71,$a1,$e1,$1f) tables stepped by an index that a zero nibble in the pattern moves — up on a pattern's first pass, down on its second, theincswapped for arorby rewriting the opcode (source: s-sidin07-xxlarge).
The filter in 256 and 512 bytes — ENV3, a table, and two 2007 filter tables#
- Eric Odland's Resolution (tiny-sid-compo, 2006) is the datasheet's "ENV 3 added to the cutoff" in three instructions: every frame
lda $D41C / lsr / sta $D416— voice 3's envelope output, halved, becomes the cutoff high byte, so the low-pass (resonance 6, all voices,$D417 = $67) opens and closes with the note's ADSR for free. Frantic's BLOCK ACID DUB drives the filter from the song instead: the filter-type bits come from the song position (ASR #%11100000, low-pass forced when none is set), the cutoff from the drum channel's tick counter<< 3(source: s-sidin11-tiny-sid-2-part-2). - The whole filter as a 17-byte table — Tognon's 15BB (tiny-sid-compo 512 bytes, 2005), a cover of the opening of Mike's Bat of Basses.
$1Fgoes to$D418(low-pass, full volume) and$F1to$D417(resonance$F, voice 1 only) once; the cutoff high byte then walks a 17-entry table —$35,$3A,$3F,$44,$3F … $08,$03— one step per tick, rising and then falling to a minimum. That table is the instrument: the original's cutoff ramp was measured with sid2midi ($180 … $220 … $018) and reduced to it. A Life in Hell's Tiny Player, the reusable engine of the same compo, makes the filter a build option —noFilterandfilterSweepsit among its compile-time flags, and with the sweep on it does no more thaninc fltr / sta $d416every frame after$F4→$D417and$40→$D416: a cutoff that rises and wraps, for two bytes of code (source: s-sidin08-tiny-sid-512b). - ENV3 into the cutoff, a year before Resolution — aleksi-eeben's Electronic (256 bytes, 2005) is the same datasheet trick with an offset:
lda $D41C / lsr / adc #$20sets the low-pass cutoff high byte from voice 3's envelope output every frame, with$F4in$D417(resonance$F, voice 3) (source: s-sidin09-tiny-sid-256b). - Two ways to get a sweep for free. Agemixer's Myblock… One Block takes the cutoff from the same free-running counter that plays the song —
and #$07, fourasl,eor #$7Finto$D416— over$C1in$D417(resonance$C, voice 1) and$1Fin$D418. Frantic's New Kid On The Block calls a fragment of BASIC ROM instead:jsr $b5ffrunsinc $23 / ldx $23 / ldy #$00 / rtsandstx $d416takes the result — "hijack some BASIC ROM shit" — which is also what fixes the tune's 399-second length. It rewrites$5Fto$D418(high-pass and low-pass — the datasheet's notch — with volume$F) and$A2to$D417every loop, so the player cannot run twice on one raster line (source: s-sidin09-tiny-sid-256b). - SID Factory (2007), driver v5.0x: filter table rows
0a bb ccadd a 16-bit value (low byte first — "the opposite of the pulse table") forccframes,$8x bb ccset the high byte and wait,$7f ?? ccjump; an instrument's resonance byte, if non-zero, switches the filter on for that channel; v6.0x:aa bb= frames and add value,$80 bbset,$7f bbjump, and the init table's third byte packs resonance and the voice mask ($f1= resonance$f, voice 1). ninjatracker V2:01–7Fmodulate the cutoff for n frames at a signed speed,80–FEset passband (left nybble − 8) and channels (right nybble) with the cutoff in the right byte,FFjump (source: s-sidin11-sid-factory-vs-ninjatracker). - GoatTracker 2 keeps a filter program running across instrument changes: Tognon needed "a special instrument that stop filter execution as GT2 left the filter set up from the previous instrument (the bass)" when a lead took over the filtered voice (source: s-sidin10-goattracker-2).
JITT64 (2010), two tiny players, and an 8580 trick#
- jitt64: three pattern commands —
0Dfilter type (high/band/low pass and the voices to filter),0Eresonance (0–15),0Fcut-off — and three instrument columns: cut-off relative or absolute (values from the instrument's own table), resonance fixed / add / subtract, and a type entry of six checkboxes (HP, BP, LP, voice 1, 2, 3), each with the D/N delay/repeat field; the player keeps them in shadow registers (shadow_FCH,shadow_RES,shadow_TYPE) written after the three voices. The example "filtered bass drum" moves the cut-off per tick (sources: s-sidin13-jitt64-tracker, s-sidin13-inside-jitt64). - 512 and 256 bytes: Plaster sweeps the cut-off high byte between
$3Fand$FFone step per frame with$D417 = $F1and$D418 = $3F; Back To Basics writes the same counter to$D416that it uses for pulse width, with$D417 = $F4and$D418 = $1F(source: s-sidin12-tiny-sid-2-part-3). - freedom's reason to prefer the 8580: "when you change filter type you don't get an unwanted noise as noticeable as the one you hear in 6581. This makes it possible to change filter type on an instrument while a note is being played and you get more sophisticated sounds" — but "way too open filters"; tsm adds that every 8580 sounds the same, unlike the 6581 (source: s-sidin13-tsm-freedom-interview). hermit in 2008: the 6581 "has good bass-filter behavior for older compositions", the 8580 "is more accurate" (source: s-sidin12-hermit-interview).
- Two 1980s filter policies from the SIDin interviews: matt-gray wrote no filter code until after Dominator — "I only started using them post Dominator on Vendetta … I was well aware of the filter inconsistencies and wanted to avoid using them, but eventually I needed the extra palette of sounds" (source: s-sidin15-matt-gray-interview), and elsewhere: "Initially, like others I didn't use the filters because they often sounded very different on another machine, but eventually I simply had to just to move the sound on" (source: s-vandalism63-matt-gray-interview); scarzix's 6581 had "a very light filter", so when the programmer could not hear CombatZone's leads the game got a filter on/off option in its menu (1989/90) (source: s-sidin14-scarzix-interview).
Tips & pitfalls#
- One filter, three voices: decide which channel owns it per section, and give the others unfiltered instruments or "filtered but not controlling" instruments.
- Keyboard tracking keeps a bass or lead equally bright across octaves.
- Hi-hats: "a highpass filter helps with hihats" (breakphase, source: s-chipmusic-sid-music-hints-n-tips) — the PRG's hand clap above is the same idea, noise through the high-pass.
- What outsiders hear: "flabby distorted filter" is one of Jellica's four typical SID sounds, and filter sweeps were on a cover-band keyboardist's list of things to imitate (source: s-chipmusic-typical-sid-sounds).
- Filter response differs strongly between chips: the 8580 has a "more balanced filter-cutoff frequency control curve and less-distorted resonance" and "more effective filters but a thinner sound than the 6581" (source: s-witchmaster-creating-chiptunes-with-sid-wizard); "every SID tends to sound different" — test on real hardware or a HardSID (source: s-goattracker-readme); Ben Daglish: "you never had ANY idea how it was gonna sound on another machine", and Beach-Head let the player adjust the filter (source: s-newman-driving-the-sid-chip). The cause is in the silicon — FET resistance varying with processing (source: s-sidmusic-yannes-interview) — and Tel's answer was to collect every C64 revision and use "average" settings (source: s-sidmusic-jeroen-tel-interviews). Check every tune on both models in vice.
- An extreme 6581 setting can be silent, not merely different, on an 8580. nata names two: Ghouls 'n' Ghosts subtune 6 and The Last Ninja subtune 9 "use some odd filter settings that aren't audible on a new SID". He prefers the newer chip anyway — "It's a heavily improved SID … the 8580 has very nice waveform combinations and filters that are accurate" — and simply re-fits the filter when he covers an old tune for it (source: s-sidin09-nata-interview).
- The filter is not only the chip: the board around it counts. stefano-tognon's checklist for anything that hosts a real SID outside a C64 — written for the catweasel Mk4 — is three items: clock the chip at the right PAL/NTSC speed; "put a sid value in a sid register at the same time the real C64 puts it into the sid"; and have filter capacitors "like in C64, otherwise the filter (of the same chip) will have different behavior". On his card the filter did not merely colour differently, it swallowed voices: in Nulone, voices 1 and 2 sweep a low-pass cutoff
$3A8 … $2E8and are inaudible while the unfiltered voice 3 plays fine, and a second 6581 from a different year behaved the same — "Low filter make mute the voices as the other chip" (source: s-sidin09-catweasel-6581). - How to find out whether the filter is what you cannot hear: dump the tune with siddump or sid2midi, read off which voices are routed and at what cutoff, then patch every
$D417write in the file — Tognon's "Just change all 17 D4 to 17 E4 in the tune and test again" — and play it again. If the missing voices come back, "the problem for this is filter related" (source: s-sidin09-catweasel-6581).
Related#
instrument-tables · pulse-width-modulation · sid · instrument-design · c64-programmers-reference-guide · furnace · defmon · bob-yannes · jeroen-tel · martin-galway · catweasel · siddump · tiny-sid-compo
Sources#
s-sid-wizard-manual · s-witchmaster-creating-chiptunes-with-sid-wizard · s-goattracker-readme · s-newman-driving-the-sid-chip · s-openmpt-manual-zxx-macros · s-openmpt-manual-instruments · s-openmpt-manual-effect-reference · s-openmpt-manual-samples · s-protracker-23d-help · s-mos-6581-datasheet · s-c64-prg-chapter-4-sound · s-hvsc-sid-file-format · s-sid-wizard-charts-and-tables · s-chipmusic-c64-music-for-dummies · s-walleij-sid-player-routine · s-furnace-c64-docs · s-chipmusic-sid-music-hints-n-tips · s-chipmusic-typical-sid-sounds · s-sidmusic-yannes-interview · s-sidmusic-creation-of-the-sid-chip · s-sidmusic-jeroen-tel-interviews · s-remix64-galway-interview · s-sidmusic-galway-interviews · s-64er-soundmonitor-article · s-realdmx-sid-players · s-remix64-huelsbeck-interview · s-vgmpf-chris-huelsbeck · s-c64com-matt-gray-interview · s-realdmx-galway-arkanoid-player · s-realdmx-bjerregaard-player · s-realdmx-ouwehand-mon-tunes · s-realdmx-deenen-test-tunes · s-realdmx-audial-arts-fame-players · s-realdmx-fredgray-kimmel-whittaker · s-sidin04-galway-arkanoid · s-sidin06-asterion-sid-tracker · s-sidin07-inside-modules · s-sidin06-digiorganizer · s-sidin07-xxlarge · s-sidin11-tiny-sid-2-part-2 · s-sidin11-sid-factory-vs-ninjatracker · s-sidin10-goattracker-2 · s-sidin13-jitt64-tracker · s-sidin13-inside-jitt64 · s-sidin13-tsm-freedom-interview · s-sidin12-tiny-sid-2-part-3 · s-sidin12-hermit-interview · s-sidin15-matt-gray-interview · s-sidin14-scarzix-interview · s-defmon-wiki-manual · s-defmon-wiki-defmoning-101 · s-chipmusic-defmon-thread · s-cheesecutter-guide · s-duet-research-sidwizard-deep · s-duet-research-sidwizard · s-defensemech-ghost-channel-allpass · s-intense-tech-01-wave-synth-deep-dive-part-1 · s-lsdj-manual · s-defensemech-wave-channel-ramble · s-painperdu-02-wav-channel-walkthrough · s-defensemech-learning-lab-shorts · s-chipmusic-lsdj-live-performance · s-sidin01-dig-dug-rip · s-sidin08-tiny-sid-512b · s-sidin09-tiny-sid-256b · s-sidin09-catweasel-6581 · s-sidin09-nata-interview · s-vandalism63-matt-gray-interview