Signal Carnival

At Revision 2025, we released "Signal Carnival" (csdb, YouTube). This demo asks you to switch the audio and video cable of your C64:

Diagram of audio and video crossed (Diagram by Felidae)

Technically, "misplugging" those cables is not a completely new idea. In the 90s, it was a common thing to connect audio to both speaker and video, to get screen flickering timed with the music beats.

However, "Signal Carnival" is the first production to switch both these cables, while still being able to play meaningful audio and video.

How to drive audio using a video signal

The C64's VIC chip operates at a frequency of 7.9Mhz, and new values can be written to it by the 6502 at a rate of up to 246kHz. Since even high quality audio rarely exceeds 44kHz, this is easily a high enough frequency to generate music.

Inspired by the music routine from freespin, we combined two timers to get an interesting waveform, which is adjusted once per frame. This is the code that drives audio:

lda $dc06
ora $dd06
eor $02
sta $d020

Here, $dc06 and $dd06 are the lower bytes of the B timers of CIA #1 and #2, respectively. They're in turn increased every time their A timers overflow. And those are set to a new value for every new note.

Eor-ing with the value at zeropage $02 allows to slightly tweak the waveform (and volume!), and then the result is written to $d020, which is the screen color. Only the brightness of said color matters (chrominance is encoded at the PAL color carrier frequency, which is outside of the human hearing range). There's no obvious relationship between the brightness of the C64 color and its number, but that's fine - it actually makes the waveforms more interesting.

Of course, the above uses up all four timers the C64 CIAs have, which means there are no timers left for e.g. video stabilization, which is instead done through the lightpen circuitry. (Finally! A use case for $d013. :))

Music is just one voice, but the song switches between voices (and waveforms) often enough to sound mildly polyphonic.

How to drive video using an audio signal

The SID chip of the C64 internally operates (for PAL) at 9.85Mhz, and, like the VIC, can be fed with new values at a rate of up to 246kHz.

The SID has a number of audio features. It can generate three voices using multiple waveforms, it has an ADSR envelope generator, can sync between voices, and apply filters. Unfortunately, all this circuitry works at a (for video) glacial pace: The highest frequency the chip can output is 3906Hz, which is the equivalent of changing the video output every four rasterlines. Similar with ADSR, for which the smallest possible configurable attack time is 2 ms, so about 32 rasterlines.

But thankfully, there's also circuitry that is wired more directly and and gives instant output: the volume register ($d418)! This has been used in the past to generate samples at high frequencies (16+ khz), and is a good choice for generating video, as well.

However, the C64 mainboard also, vexingly, passes the SID signal through a analogue bandpass filter. And this always happens, regardless of whether filtering is on or not.

Subsection of C64 mainboard circuit diagram

This means that the pixels we work with are very wide, and also horizontally blurred. Here's one "pixel", displayed on my 1084 monitor:

Photo of 1084 display, with a blurry horizontal "pixel"

This blurryness poses an interesting constraint on effects. While technically, you do have some degree of horizontal resolution, the blurriness makes it unenticing to use it for scrollers. Here are a few letters, for illustration:

Photo of three letters, heavily blurred

Signal Carnival makes heavy use of both axis, but to cope with the bluriyness, the x axis is used for graphics elements that are fine to blur, like e.g. textures.

Other than that, driving the video is just your normal vanilla video bitbanging. Due to the bandpass filter, horizontal syncs (i.e., pulling the signal line to ground) need to be precisely timed, so they get recognized, but not mistaken for vertical syncs. (which differ from horizontal syncs only in that the signal is pulled to ground a little bit longer)

Video sync generation is happening at the same refresh frequency that the VIC would have produced (so we can use raster interrupts and query the lightpen), but we use a different horizontal frequency (64 cycles per line instead of 63), since having that extra cycle proved useful in a lot of effects. And besides, if you're generating your own video signal, you might as well make your own rules!

Loading

I've written a new loader for probably every demo I've ever released on the C64, and this one is no exception. Since a lot of the effects are quite memory-hungry (we even play a sample at the end), loading breaks are long enough that we can't just pause audio while they happen.

Hence, we have to bitbang audio the entire time. Also while transferring data from the drive to the C64. Also during decrunching. (The decruncher actually unrolls all copy loops, also for that reason)

The loader has a few gimmicks, like doing on-the-fly GCR decoding that allows to write to any page (not just stack), and it buffers (stashes) a full kilobyte. But it's also a one-off, so it's missing tons of features seen in fully-fledged C64 loaders. It doesn't even support loading from tracks 18 and above yet (the demo uses 1-10.) Also no checksumming. (So remember, kids, dust off your 1541 drives, or Signal Carnival might not work!)

Talking about GCR decoding, I found it tedious to handcode the decoding tables, so we instead wrote a solver that generates them given the code that uses the tables, and the desired input/output. Very useful tool, hopefully to be released soon.