Pulse width modulation (PWM)

aliases PWM, pulse width, duty cycle, pulse sweep
sources 0 3 7 lab — techniques & theory (summary) · SID-Wizard 1.8 User Manual (summary) · Little Sound Dj v9.2.6 Operating Manual (summary) · GoatTracker v2.72 readme (summary) · Creating Chiptunes with SID-Wizard, Second Edition (summary) · Composing in SID Factory II, Part 4 — Instruments (Chordian) · Newman — Driving the SID chip: Assembly Language, Composition, and Sound Design for the C64 (2017) · Pan Docs — Audio Registers · Pan Docs — Audio Overview · Intense Tech #02 — LSDj Wave Synth Deep Dive, Part 2 (Defense Mechanism) · chipmusic.org — two Johan Kotlinski interviews (2012, 2014) · chipmusic.org — LSDJ Chord Cheat Sheet / Advice on Learning Tables threads (2015) · MOS 6581 Sound Interface Device (SID) datasheet — the text behind Appendix O · C64 Programmer's Reference Guide, ch. 4 — Programming Sound and Music on Your Commodore 64 (1982) · chipmusic.org — C64 Music for Dummies (ant1's SID-Wizard 1.0 tutorial, 2012) · Walleij — A SID player routine (MIDI-driven player design, note tables, hard restart) · Intense Tech #03 — Don't Sleep on Z (Defense Mechanism, after Hypnogram) · Sabrepulse — Getting started with LSDJ tutorial (mirror at Adventure Kid) · Furnace tracker — C64 (SID) system and instrument documentation · chipmusic.org — What are the typical C64/SID sounds/instruments? (2015) · chipmusic.org — sid music hints'n'tips (2013) · The SID Homepage — Interviews with Martin Galway (Commodore Zone 6–7, Happy Computer 11/86) · The SID Homepage — Interview with Bob Yannes (Andreas Varga, August 1996) · 64'er 10/1986 — Musik wie noch nie: the Soundmonitor article and manual · SIDin #2 — Matt Gray's Driller music routine (Stefano Tognon, 2002) · GitHub realdmx/c64_6581_sid_players — Cybernoid II in Deenen's MUSICFILE, the MoN SFX player, the Driller listing · Remix64 — An Interview with Chris Huelsbeck (2001) · Chordian — SID Musicians (JCH's verdicts on the game composers, 2018) · RVG Interviews: Matt Gray (2016) · realdmx: Rob Hubbard's driver — McSweeney's Monty on the Run disassembly and the Commando title · realdmx: Martin Galway's Rambo II loader — the macro-language player, reverse-engineered by dmx87 · realdmx: Jonathan Dunn's Daley Thompson '88 and Ocean Loader 5 in Paul Hughes's Ocean music driver · realdmx: Johannes Bjerregaard's own player — the James Bond 3 demo (a Maniacs of Noise commission) and Myth · realdmx: Reyn Ouwehand's Armada (MUSICFILE, 'Special version for Reyn Ouwehand', 1989) and Dutch Breeze (the 1990 MoN player) · realdmx: three smaller drivers — Fred Gray's Mutants (song as code), Jeroen Kimmel's 'Red's music player' (1989) and David Whittaker's Panther driver · realdmx: two scene players with their own sources — Francois Prijt's Audial Arts 'zong player' v1/v2 (1991) and Adam Bulka's FAME 'Sound Routine f 4.1.0' (1988) · SIDin #4 — Martin Galway's Arkanoid music routine, the explanation (Stefano Tognon, 2003) · SIDin #6 — Asterion Sid Tracker v1.0 reviewed (Stefano Tognon, 2004) · SIDin #7 — Inside Modules: Ivan Del Duca's music engine (Stefano Tognon, 2005) · SIDin #7 — xxlarge: Ninja's 256-byte cover of 'X large' analysed (Stefano Tognon, 2005) · SIDin #13 — JITT64 Tracker: Tognon's Java Ice Team Tracker from the user's side (2010) · SIDin #13 — Inside JITT64: the player routine (Stefano Tognon, 2010) · SIDin #12 — Tiny Sid 2, part 3: Plaster (512 b) and Back To Basics (256 b) (Stefano Tognon, 2008) · SIDin #14 — Inside Matt Gray's Dominator player (Tognon's commented PLAYER V4.2 and the musician's guide to composing in it, 2015) · 64'er 2/88 — Von der Spielidee ins Regal (Rainbow Arts, with Hülsbeck) · defMONing 101: A defMON Operator Field Guide (iLKke, 2013) · defMONing 102: extended field guide (iLKke et al.) · The CheeseCutter Guide — the recovered site manual (Wayback) · DUET research: SID-Wizard player.asm deep dive (summary) · DUET research: SID-Wizard 1.97 formats and player (summary) · roboctopus — PWM, I Choose You! (2014) · Defense Mechanism — LSDj Learning Lab shorts: Limit and Vshift, Long Filter Sweeps, Self-Resonating Bass (videos, 2020) · defMON wiki — features, interface overview, seqED key map · Defense Mechanism — Making every LSDJ kick I can think of (video, 2024) · Happy Computer 3/87 — Der Soundprofessor (Hülsbeck at work) · Remix64 — An Interview with Martin Galway (Neil Carr, 2001) · Remix64 — An Interview with Anders Carlsson aka GOTO80 (Neil Carr, 2002) · realdmx: Martin Galway's Arkanoid — Tognon's SIDin #4 reverse-engineering, with the 'sample' synthesis through $D418 · realdmx: Charles Deenen's two test tunes — an undated MUSICFILE build and the 'Scoop designs' Maniacs of Noise player · Matt Gray's Dominator subtune 6 source (PLAYER V4.2, 1987) and the Reformation driver competition · SIDin #8 — Tiny Sid Compo, the 512-byte entries (Stefano Tognon, 2005)
created 2026-08-30 · updated 2026-08-31

What it is#

A pulse wave's character depends on its duty cycle: 50 % is hollow and round, 25 % brassy, 12.5 % thin and nasal. Sweeping the width while a note plays makes a single channel sound alive — the classic C64/sid shimmer (source: s-037-lab).

How to do it#

SID-Wizard#

The SID pulse width is a 12-bit value (low byte + high nybble), so 50 % = $800, 25 % = $400, 12.5 % = $200. The instrument's pulse-width table (Shift+P) programs it frame by frame:

entrymeaning
$8x..$Fx xxset the pulse width to xxx
$00..$7F xxadd xx to the pulse width, this many times (one step per frame)
$FE xxjump to table position xx

A sweep is therefore: set a start value, add a small positive step for n frames, add a negative step (= negates a value) for n frames, jump back. The third column is a keyboard-tracking curve ($00 off; $01 upward = more pitch dependence, $FF downward = less), so the width can follow the note. C=+P toggles whether the table restarts when the instrument is selected; $0E xx sets the pulse width from the pattern; $0A xx jumps to a table position. The current width is shown as a bar in the top border. With a table speed above $40 the pulse table runs at multispeed rate for smoother sweeps (source: s-sid-wizard-manual).

Details from the SID-Wizard book: $000 and $FFF give "a constant DC output (silence)", $800 a square wave; the first nibble of a set-row must be $8–$F but does not affect the width (true for the immediate output — but the player keeps seven bits of it in its ghost register, and a later sweep runs from the full value; see "What the player actually does" below). Keyboard tracking is "a signed value in exponential scale" — $00–$7F widens the pulse for higher notes, $FF–$80 does the same more weakly. C=+P toggles the header: bright PULSE = the program restarts on every note, dark = only when an instrument is (re)selected. The book's bass recipe: 82 (width $200) then 40 20 (add $20 sixty-four times) with keyboard tracking 10 on both rows — "look at the pulse width bar … while you try the instrument" (instrument-design; source: s-witchmaster-creating-chiptunes-with-sid-wizard).

The beginners' tutorial for SID-Wizard 1.0 puts it in two numbers: 80 10 is "a very thin pulse width", 88 .. the square wave, and past it the pulse gets "thinner again"; a pulse instrument with an empty pulse table is silent ("there is no default pulse width in this tracker"); a sweep row is 01–7F (frames) followed by the speed and direction — FF "a slow downward sweep", 81 "a very fast downward sweep" — and the purple KT column makes "the pulse width follow the pitch of the note being played" (source: s-chipmusic-c64-music-for-dummies).

What the player actually does (SW 1.97 player.asm)#

From m64's duet research (SETPWID, player.asm 2332–2394; source: s-duet-research-sidwizard-deep):

  • A set row stores seven bits into the pulse-hi ghost (cmd & $7F), not just the nibble: the SID ignores bits 4–7 of $D403, so $9x and $8x sound identical now, but a following sweep runs from the full 7-bit ghost value and wraps at a different point.
  • A sweep row [N, xx] makes N signed 16-bit additions over N+1 ticks (the last tick advances the row without adding). The ghost can run through the whole 16-bit space; the SID sees bits 0–11. The sweep counter is reset on row advance but not at note start — leftover residue can shorten an instrument's first sweep row.
  • Keyboard tracking is applied at the output stage only, every tick: the player adds the first difference EXPTABH[kb+pitch] − EXPTABH[kb+pitch−1] to the written hi byte, never to the ghost. 6502 borrow semantics leak through: a flat region of the curve yields a −1 artifact, an index wrap a +1.
  • The pattern gate does not touch the pulse program — it keeps stepping and writing through the release phase; gate-off merely redirects the position to the instrument's PW gate-off pointer.
  • A $FE jump landing on a set row executes it the same tick (set+jump in one tick); landing on a sweep row starts it the next tick. Big-FX $0A (table jump) also zeroes the sweep counter; big-FX $0E vv writes only PW bits 8–11 (vv & $0F), keeping the low 8 bits.

GoatTracker#

The pulsetable: 01–7F xx = modulate for that many ticks at signed speed xx (right byte), 8X xx = set width $Xxx, FF xx = jump (00 stops). Readme examples: 88 00 / FF 00 sets $800 and stops; 80 10 (very thin), 20 40 (32 ticks up at speed 64), 40 E0 (64 ticks down at −32), 40 20 (64 ticks up at 32), FF 03 (loop from row 3) — "a nice loop". Enter sweeps as limit-based steps (84 00, C0 10 modulate to $C00, 40 10 back to $400) and convert them with SHIFT+L; the player only understands time-based steps. Speeds doubled in v2.4. Pulse execution is skipped on note-init ticks unless -O0; 900 or a 80 00 / FF 00 program stops an unused pulse program under non-pulse instruments (source: s-goattracker-readme).

SID Factory II#

A pulse waveform (41) "doesn't say anything at all" until the pulse table sets a width; the minimal table is 84 00 00 / 7f 00 00 (source: s-chordian-sf2-instruments).

Furnace#

furnace: instrument Duty 0–4095 (2048 = square) and a duty macro per tick, Absolute Duty Macro to write widths directly instead of relative steps, Reset duty on new note to reload the instrument's width; from the pattern 3xxx sets the width 000–FFF, 22xx/23xx slide it up/down at speed xx (00 stops), 1Cxy resets it on the next note / now, and 12xx is a discouraged coarse setting 00–64 (source: s-furnace-c64-docs).

CheeseCutter#

Four-byte pulse-table rows in the JCH mould ("works like byte C/B/A/D in JCH's players"): duration with the sweep direction in bit 7 (00–7F up, 80–FF down; the Guide's cheat sheet tabulates durations in frames and ms — $10 = 16 frames = 320 ms), add value, init value (FF = keep the previous width), jump (00 = next row, 7F = stop — implemented as a jump to row 0's 00 00 FF 7F self-wrap). Change the running note's pulse program with 40–5F in the sequence command column (cheesecutter; source: s-cheesecutter-guide).

defMON#

Two sidTAB columns (defmon): PW sets the width, PS sweeps it — $00–$7F "slide left (to 0%, lowering PW value in time)", $80–$FF "slide right (to 100%)". The two digits XY typed into PW expand to the 12-bit value $YXY (e.g. 83$383), so the closest squares are $08$808 ≈ 50 %, $0C$C0C ≈ 75 %, $04$404 ≈ 25 % (or $F7/$FB/$F3 from the other side); "If pulSEWIDTH is set near to 0% or 100%, the pulse waveform is inaudible" (sources: s-defmon-wiki-defmoning-101, s-defmon-wiki-defmoning-102). The manual states the same landmark more simply — "PW (Pulse Width) — $08 equals the 50% pulse" — and the sweep direction as $00–$7F left, $80–$FF right (source: s-defmon-wiki-manual).

The registers (datasheet and Programmer's Reference Guide)#

Two registers per voice, 12 bits: PW Lo and the low nybble of PW Hi ("bits 4–7 of PW HI are not used"). PWn = Hpw × 256 + Lpw; the duty cycle is PWout = PWn / 40.95 %, so $800 (2048; Hpw = 8, Lpw = 0) is a square wave and 0 or $FFF "a constant DC output"; the resolution "allows the width to be smoothly swept with no discernable stepping"; "rapidly jumping between different pulse widths can produce interesting harmonic sequences"; the pulse waveform must be selected for the registers to have any effect (source: s-mos-6581-datasheet). The PRG's demonstration: POKE S+3,8 : POKE S+2,0 for a square, then change register 3 from 8 to 1 — "notice how dramatic the difference in sound quality is?"; its harmonic summary: a square wave holds odd harmonics at 1/n, other widths "have varying harmonic content" (source: s-c64-prg-chapter-4-sound). In a player routine the pulse macro usually writes only the high byte — "of course you could add a byte for $d402 also, but most c64 players don't do this" — and a pulse-width vibrato is an LFO with a delay, amplitude and period added over $D402/$D403, "similar to usual vibrato, but larger amplitude can be used" (source: s-walleij-sid-player-routine). Inside the chip the pulse is "a 12-bit digital comparator" on the upper 12 bits of the phase accumulator, whose single output drives all 12 D/A bits — the width register is compared against the oscillator's phase directly (source: s-sidmusic-yannes-interview).

Sample-based trackers#

Fake it by switching between pulse samples of different widths every row or two (source: s-037-lab); (unverified) or use single-cycle pulse samples with an instrument that cycles them.

LSDj#

The pulse instrument's WAVE parameter selects the duty ("typically 50-50 high/low", other waves for a more distorted sound); the Wxy command changes it from a phrase or table — with the hardware oddity that the instrument's LENGTH timer is reset, possibly extending the sound. A duty sweep is therefore a table with W on successive rows, looped with H (source for the commands: s-lsdj-manual).

What the hardware allows (sources: s-pandocs-audio-registers, s-pandocs-audio-overview): bits 7–6 of NR11/NR21 select one of exactly four duties — 00 12.5 %, 01 25 %, 10 50 %, 11 75 % — and Pan Docs notes that "there is no audible difference between the 25 % and 75 % duty cycle settings", so a W sweep has three timbres to step through; it is a stepped effect, never the continuous sweep of the sid. The W-resets-LENGTH oddity follows from the duty and the initial length timer sharing that register (inference). A pulse channel's phase is "only ever reset by turning the APU off" and the duty setting shifts the phase, which is "usually not noticeable unless changing the duty cycle mid-note" — a duty step can land anywhere in the cycle, so listen to the transition rows.

The W values count 00 = 12.5 %, 01 = 25 %, 02 = 50 %, 03 = 75 %, which is what makes a random duty possible: W on the note and Z03 on its repeats picks any of the four, and Z02 (12.5 / 25 / 50 %) is the better choice because 25 % and 75 % "effectively sound identical"; an added value past the range wraps to the remainder after dividing by 4 (randomization-and-probability; source: s-intense-tech-03-dont-sleep-on-z). A beginner's defaults from sabrepulse's tutorial: 50 % ("a good low bassy sound") for the bass, 25 % for the lead (source: s-sabrepulse-getting-started-with-lsdj).

Three more sources: the wave channel does the sweep the pulse channels cannot — Square with PHASE Normal from 00 to 1F is "a smoothly modulated pulse lead (commonly associated with the C64 SID)" (source: s-intense-tech-02-wave-synth-deep-dive-part-2); johan-kotlinski's own bass duty "depends on the octave. For the base, I go for 15 or 25 first tick, then switch to 50" — a W on the first tick of a table (source: s-chipmusic-kotlinski-interviews); and Dire Hit's reading of Trey Frey's "Dreaming": at the fade-out an H loop over two table steps flips the pulse width while an E release runs (source: s-chipmusic-tables-and-chords-threads).

On the pulse channels themselves the duty is also a drum parameter: for a pulse kick, 50 % is "the beefiest", and the channel's floor of C2 is what makes the wave channel the better home for a kick with real low end (source: s-defensemech-every-lsdj-kick).

A cheaper approximation of the same motion: VSHIFT combined with FOLD or WRAP, where the shifted part of the wave folds or wraps back in — "that's almost like a pulse-width modulation thing happening" (source: s-defensemech-learning-lab-shorts; wavetable-programming).

Hand-drawn PWM on the wave channel#

Before the synth screen could sweep PHASE, the wave channel got its PWM by hand, one wave at a time (source: s-lsdj-and-you-pwm): draw a square in wave 0 at VOLUME 10, then narrow the pulse a little more in each of the synth's remaining fifteen waves — copy a whole wave with SELECT+B twice and paste it into the next frame with SELECT+A, then edit, rather than redrawing. Play it with PLAY PINGPONG, REPEAT F, LENGTH F and a slow SPEED, and the pulse narrows and widens continuously: "PWM will allow your game boy to sound a bit more like a C64, though nowhere near as smooth and classy." The steps are audible — "you can hear the synth stepping through each frame" — but "less noticeable on leads/basses in a full mix … and barely discernible when used in a fast arpeggio". roboctopus's verdict: "It's great for giving a lead line a more organic feel, and it makes a pretty massive bass."

The article predates LSDj 7's silky wave, which removed the click from frame changes (wavetable-programming), so on a current version the stepping should be gentler than he describes (inference).

In the classic drivers (1986–1988)#

  • soundmonitor: register 4 sets the pulse width, 5/6/7 a pulse EG — how long register 7 is added, how long it is subtracted, and the step ("above 30 the tone gets dirty"); register 9's left digit 1 restarts the EG constantly (source: s-64er-soundmonitor-article).
  • matt-gray-player: instrument byte 4 is a per-frame step and the high byte bounces between $08 and $0E — "pulse from 2048 to 3584"; effect bit 1 decides whether the pulse restarts at each note or carries on from the last one (source: s-sidin02-matt-gray-driller).
  • musicfile: a pulsetabel of 8-byte programs ($04,$0c,$02,$30, $04,$30,$06,$30), a tune-wide pulserunspeed = $63 for the "Pulserun" routine and a pulsearp beside the wave arpeggio (source: s-realdmx-sid-players).
  • chris-huelsbeck: "the pulse-with modulation is the holy grail of its power" (source: s-remix64-huelsbeck-interview).
  • hubbard-player: instrument byte 6 — bits 0–4 a delay in frames between steps, bits 5–7 the step ($20, $40 …); the width bounces between high bytes $08 and $0e, and the routine rewrites the instrument's own PW bytes in place; Commando adds flag $08, "pulse width lo modulation", which adds the byte to the low byte every frame (source: s-realdmx-hubbard-player).
  • galway-player: a 10-byte pulse program — p1time, p2time, delay, flags, pm1add, pm2add, initial width — two signed adders with durations, then reset ($80) or continue ($01): $14,$14,$00,$05, $14,$00,$ec,$ff, $00,$08 = 20 frames +20, 20 frames −20 around $0800, the Rambo pulsating JCH points at (source: s-realdmx-galway-rambo-player).
  • ocean-music-driver: PWM n selects a 5-byte entry rate, frames up, frames down, initial width ($80,$00,$02 / $0F50; $40,48,42 / $0000), one rate both ways (source: s-realdmx-ocean-dunn-hughes-driver).
  • bjerregaard-player: pulse programs of (delta, frames) pairs after an initial width, bit 7 of the delta = add: P2 ;GUITAR $01,$E0, $C0,$01, $B0,$01, $A0,$02, $10,$03, $08,$04,$FE — unequal segments, i.e. a shaped sweep rather than a triangle (source: s-realdmx-bjerregaard-player). The 1990 MoN player's p1 !by $8b,$01 / $06,$30 / $01,$50 / $ff bounces between $08xx and $0bxx with per-segment speeds (source: s-realdmx-ouwehand-mon-tunes).
  • whittaker-player: $90 min,max,speed — bounce between two high bytes, re-issued per note; Fred Gray's Mutants keeps a free-running 8-bit accumulator (PW += add each frame, $d0 = −48 sweeps the whole range in five frames) reset to the instrument's value at every note — JCH's "jumping pulsating settings for subsequent note triggers" (source: s-realdmx-fredgray-kimmel-whittaker).
  • Audial Arts (1991): a sweep add plus an acceleration byte added to it every frame, with a turnaround length — an opening sweep instead of a linear one; FAME (1988): one 8-bit value written to both $d402 and $d403, stepped by (frames, add) pairs (source: s-realdmx-audial-arts-fame-players).
  • Arkanoid's pulse program in Tognon's reading: two phases only — $32,$32 / $14,$05 / $0A,$00 / $F6,$FF = after a 20-cycle delay, 50 cycles of +10 then 50 of −10, flag $05 restarting from the current width, "a classical up/down pulse amplitude effect"; flag bit 7 would instead snap back to the stored width ($16–$17) each round, and a flag with neither bit 7 nor bit 0 stops after the two phases and holds (source: s-sidin04-galway-arkanoid).

Heard in / history#

The datasheet already promised that "sweeping the pulse width in real-time produces a dynamic 'phasing' effect which adds a sense of motion to the sound" (source: s-mos-6581-datasheet; quoted in s-witchmaster-creating-chiptunes-with-sid-wizard). martin-galway "didn't develop pulse-width modulation until … Cyclone. It later came out as Helikopter Jagd"; Chris Huelsbeck called PWM "the holy grail" of the SID's power (source: s-newman-driving-the-sid-chip). Galway's own account: helikopter-jagd was worked on in 1984 as "Cyclone" — "It was the first tune I did pulse-width sweeps on, but didn't get released 'til after a bunch of other games had come out I think. The sound blew everyone away." (source: s-sidmusic-galway-interviews)

Hülsbeck's 1988 description#

His tip to 64'er's readers is the effect described from the composer's chair: SID registers "können … schnell rauf und runter gezählt werden. Das ergibt sehr interessante Effekte. Es können zum Beispiel mit einem Ton Schwingungen erzeugt werden, die normalerweise nur mit zwei Tönen möglich wären" — one voice, swept fast, beating as if it were two (detune) (source: s-64er-rainbow-arts-feature).

Hülsbeck's 1987 checklist#

A year earlier, in Happy Computer, PWM appears in his advice on writing a music routine at all — as one of the two extensions to plan for from the start: think about "wie man zum Beispiel die Pulsweite moduliert, oder wie man Filter manipuliert. Dadurch entstehen sehr schöne Effekte" (source: s-happy-computer-soundprofessor; sid-player-routine, filter-programming).

Pioneers, per JCH#

"The clean pulsating with the instantaneously triggered notes (a trademark of his)" — martin-galway was "a pioneer of programmed pulsating, years before anyone else. Want evidence? Load up the old game Rambo and play for a while until you get to the hiscore table. Listen to the tune there and pay attention to the pulsating. Note how it changes several times on the long notes." Fred Gray "had a thing with jumping pulsating settings for subsequent note triggers"; Mark Cooksey's "edge-breaking pulsating and an exaggerated vibrato" put him off (source: s-chordian-sid-musicians). matt-gray's use: a pulse-width sweep on a chord plex so "the bubbling chord took on a life of it's own" (source: s-rvg-matt-gray-interview).

One feature per project#

martin-galway's working method makes PWM a case study in something more general: exploit one chip feature per project and let it carry the tune — pulse-width sweeps on Cyclone/helikopter-jagd, then ring modulation, then samples (source: s-remix64-galway-interview). The aesthetic survived into the 2000s scene: goto80 names "pulsemodulating bass sounds and the freedom of the SID" as what drew him, alongside ring modulation and "setting bits" (source: s-remix64-goto80-interview).

The sweep engines, read from source (1986–1991)#

Three drivers whose PWM code has been recovered, in increasing order of how much the composer controls:

  • galway-player has no dedicated PWM: pulse width runs on the same generic adder engine as vibrato and filter — two 16-bit adders ($12–$15) with two stage lengths ($0E/$0F), a delay ($10), flags ($11) and an initial width ($16/$17). Arkanoid's lead instrument ins01 is a pulse sweep of ±$0A for 50 ticks each around a start width of $0800, under a vibrato of +20 × 3 / −20 × 6 / +20 × 3 ticks. Because the song can rewrite an instrument's bytes while it sounds, a "patch change" costs one byte (source: s-realdmx-galway-arkanoid-player).
  • musicfile (Maniacs of Noise) makes it a named sub-program: ;pulse: slidespeed, bottom, top, speed, loop+$02,$10,$30,$04,$f4 is a bounded bounce written as five bytes — selected per instrument by fx2 bits 0–2, with bits 4–7 setting the pulse speed after the program ends, and separate fx3 flags for pulserun ($02) and pulse arpeggio ($08) (source: s-realdmx-deenen-test-tunes).
  • matt-gray-player keeps the state in the player rather than in a table: PMODDIR for the direction and PUCH/PUCL for the limits, with the bounce written into the instrument's bytes (source: s-matt-gray-dominator-source).

From SIDin #6–#7 (2004–05)#

  • asterion-sid-tracker: the instrument has a pulse start speed (PWM), lower/upper limits (PLM) and a shared cutoff/pulse speed (SCP) — a bounded sweep as in the 1980s drivers; the pattern's Bx sets the pulse (B0 = the instrument's LFO; with MCP's low nibble 0 an absolute width x into the MSB, otherwise x becomes a second pulse value), and 2x bit 8 stops the pulse from restarting on a new note (source: s-sidin06-asterion-sid-tracker).
  • The modules driver (1988): the instrument's width is fixed (tbWLo/tbWHi, e.g. $077A, $0850); a sweep is the pattern command $9B $02 $01 limit step delay on the width's high byte — $9B,$02,$01,$20,$01,$0A in pat06, $9B,$02,$01,$10,$01,$05 on the bass-like instrument 3 (source: s-sidin07-inside-modules).
  • xxlarge (Ninja/theDream, under 256 bytes): the same byte goes to $D402 and $D403 from a 20-entry table ($07,$47,$87,$B7,$08,$38,$88,$B8, $09,$39,$89,$49 …) indexed by the KERNAL's cursor-blink counter at $CD — a free pulse LFO whose entries' top bit (after asl) also decides whether the rest of the player runs that frame (source: s-sidin07-xxlarge).

JITT64 (2010) and the 512/256-byte players#

  • jitt64: pattern commands 0A xx / 0B xx add or subtract xx per tick to the 12-bit pulse value (carry into the high byte, no bounds) until 00 with bits 5–6 stops them; in the instrument table the Pulse column takes fixed values (0–2048, from the instrument's own value table) or relative steps, the D/N field setting how many ticks each step holds and repeats — that is how the example instruments do "a 'vibrato' to the pulse" (Emil's string, a Dane lead, Matt Gray's bass) and "a 'portamento' onto the pulse" (the filtered bass drum, the other Dane lead) (sources: s-sidin13-jitt64-tracker, s-sidin13-inside-jitt64).
  • The 2005 512-byte entries are three variations on a free-running counter (source: s-sidin08-tiny-sid-512b): Crue Gurl steps the pulse high byte for voices 1 and 3 from pwmMin = 3 to pwmMax = 6 every four frames, wrapping — with the source's own warning that the step counter pwmSpeed = 3 ; must be (n^2)-1!!!! because the code masks rather than compares; laxity's Twone Five gives each voice a start high byte (pulsehi $08,$01,$04) and adds a per-voice constant (pulseadd $00,$10,$3d) to the low byte at every SID write, so voice 3 sweeps fast and voice 1 not at all; 4mat's Empty makes it an instrument parameter — one instadd byte per instrument ($1c,$0c,$00,$15,$00,$74,$a5) added to the pulse low byte each frame, with the pulse high byte packed into the low nibble of instpuls.
  • In GRG's 512-byte Plaster the pulse low byte of each voice is simply incremented by $18 every frame from high bytes $08, $08, $04 — a free-running sweep with no bounds; in Jaymz Julian's 256-byte Back To Basics one counter, incremented every four loops, is written as the pulse high byte of all three voices with the voice offset added, and as the filter cut-off at the same time (source: s-sidin12-tiny-sid-2-part-3).
  • matt-gray-player V4.2: the bounce limits became part of the instrument — byte 14 holds them as nibbles $LH, i.e. between $0Lxx and $0Hxx ($8E = the Driller $08$0E; $33 keeps the width "around" $03xx; $35 = $0300$0500); byte 0 is the start width as swapped nibbles ($23$0320), byte 4 the per-frame step. Tognon's worked example: start $0320, step $A0, limits $18 → 800, 960, 1120 … 2080, then down to $01xx and back (source: s-sidin14-dominator-player).

Tips & pitfalls#

  • Slow, wide sweeps for pads and leads; narrow, fast for bass "growl".
  • The width also changes perceived volume and pitch centre slightly; keyboard tracking keeps a sound consistent across octaves.
  • The classic lead, from a 2015 cover-band thread: "Start with a pulse oscillator at about 10% width, and increase the width up to a 50/50 square wave and add vibrato to it all" — on the SID roughly $19A$800 (derived from the percentages); "PWM is a huge part of the C64 sound and you won't be pulling off realistic SID leads without it" (source: s-chipmusic-typical-sid-sounds).
  • Multispeed PWM: 4mat's reading of a 5× tune (Gallefoss, Electronic Transfer) — "The rapid pulsewidth modulation on there is down to the multispeed. He's probably got it adding about $40 on each player tick" (multispeed; source: s-chipmusic-sid-music-hints-n-tips).

instrument-tables · wavetable-programming · filter-programming · multispeed · instrument-design · vibrato · furnace · defmon · helikopter-jagd · martin-galway

Sources#

s-037-lab · s-sid-wizard-manual · s-goattracker-readme · s-witchmaster-creating-chiptunes-with-sid-wizard · s-chordian-sf2-instruments · s-newman-driving-the-sid-chip · s-pandocs-audio-registers · s-pandocs-audio-overview · s-intense-tech-02-wave-synth-deep-dive-part-2 · s-chipmusic-kotlinski-interviews · s-chipmusic-tables-and-chords-threads · s-mos-6581-datasheet · s-c64-prg-chapter-4-sound · s-chipmusic-c64-music-for-dummies · s-walleij-sid-player-routine · s-intense-tech-03-dont-sleep-on-z · s-sabrepulse-getting-started-with-lsdj · s-furnace-c64-docs · s-chipmusic-typical-sid-sounds · s-chipmusic-sid-music-hints-n-tips · s-sidmusic-galway-interviews · s-sidmusic-yannes-interview · s-64er-soundmonitor-article · s-sidin02-matt-gray-driller · s-realdmx-sid-players · s-remix64-huelsbeck-interview · s-chordian-sid-musicians · s-rvg-matt-gray-interview · s-realdmx-hubbard-player · s-realdmx-galway-rambo-player · s-realdmx-ocean-dunn-hughes-driver · s-realdmx-bjerregaard-player · s-realdmx-ouwehand-mon-tunes · s-realdmx-fredgray-kimmel-whittaker · s-realdmx-audial-arts-fame-players · s-sidin04-galway-arkanoid · s-sidin06-asterion-sid-tracker · s-sidin07-inside-modules · s-sidin07-xxlarge · s-sidin13-jitt64-tracker · s-sidin13-inside-jitt64 · s-sidin12-tiny-sid-2-part-3 · s-sidin14-dominator-player · s-64er-rainbow-arts-feature · s-defmon-wiki-defmoning-101 · s-defmon-wiki-defmoning-102 · s-cheesecutter-guide · s-duet-research-sidwizard-deep · s-duet-research-sidwizard · s-defmon-wiki-manual · s-defensemech-every-lsdj-kick · s-happy-computer-soundprofessor · s-remix64-galway-interview · s-remix64-goto80-interview · s-realdmx-galway-arkanoid-player · s-realdmx-deenen-test-tunes · s-matt-gray-dominator-source · s-sidin08-tiny-sid-512b · s-lsdj-manual · s-lsdj-and-you-pwm · s-defensemech-learning-lab-shorts

source file wiki/techniques/pulse-width-modulation.md · 1 unverified · graph