[index] [home]


RC2018/04 (RetroChallenge, April 2018)

This page describes in some detail my effort for the Retro Challence 2018/04 event.

(TL;DR = make microcontroller-firmware for a Commodore 64 ("C64") cartridge-adapter ("Bait-a-Cart"), and finish/modify/shoehorn a small C64 intro to be uploaded to, and hosted by the Bait-a-Cart.)


Background: C64 & cartridges

The C64 can run ROM-cartridges plugged into its cartridge-slot. This forms a no-fuss way to run games and utilities: simply turn on the C64 to run the program on the ROM-cartridge.

Because this is so easy, ROM-cartridges can be a convenient medium where retro-machines such as the C64 are used for display or hands-on experience, e.g. at a retro-event, party, etc.

What is a "Bait-a-Cart cartridge-adapter"?

The idea of the Bait-a-Cart cartridge-adapter is to be able to show an appropriate logo, intro or text before such a ROM-cartridge starts.

To do this, the Bait-a-Cart is placed physically in between the C64 and an existing ROM-cartridge. The Bait-a-Cart plugs into the C64, and the ROM-cartridge plugs into the Bait-a-Cart:

When the C64 is turned on, the Bait-a-Cart itself mimics a ROM-cartridge containing an intermediate program (logo, intro, etc). This program is then ran until the user presses the space-bar, after which the Bait-a-Cart disables itself, and the actual ROM-cartridge is started.

This is basically the hardware-version of adding a crack-intro to a game.

(The name "Bait-a-Cart" comes from the way "bait" (in the form of an event-/club-specific logo or intro) is added to an existing ROM-cartridge. This makes for a nice attract-mode while the computer is idle.)

What am I planning to do for RC2018/04?

Although the (nearly untested) hardware for this project exists, software mostly does not. Effort during the RetroChallenge will go into making firmware ("microcontroller-software" hereafter) for the Bait-a-Cart, as well as finishing a work-in-progress C64 intro ("C64-software" hereafter), and hosting the latter on the Bait-a-Cart flash to enable the C64 to run it at boot.

So, my plan for RC2018/04 is to...

Where relevant, software-progress will be split up into "microcontroller-software" (running on the Bait-a-Cart microcontroller), and "C64-software" (only hosted on the Bait-a-Cart's flash).

Progress & status (in reverse chronological order)

2018-04-21: made serial monitor for uploading and switching between ROM-images

Nice! In order to debug this thing, made a serial monitor for issuing commands and rudimentary uploading/verifying ROM-images. This basically shows the Bait-a-Cart works as intended, albeit still through a serial command-link only instead of having a pushbutton/LED interface.


Serial input coming in on the USB-connector is parsed into commands. One such command is "help", which displays an overview of all available commands:

    Build: Apr 21 2018 01:15:14
      s : (e)rase<sec8>, (c)sum<sec8> ("sector")
      d : (w)rite<ofs20><num4><u8>*, (r)ead<ofs20> ("data")
      m : <region4><col4> ("minicart")
      b : o(n)<region4><carttype4>/of(f)/(r)eset/(t)ellmode ("Bait_a_Cart")
      x : erase whole flash ("xtra")
      h : print this help-page ("help")

This interface allows for manual testing and controlling of the Bait-a-Cart. All available features on a row:


The last option (write mini-cartridge image, or "minicart") writes a ROM-image into the flash at a specified location. This ROM-image, when ran, toggles the C64's border-colour between black and a specified alternate colour. By using different alternate colours for different ROM-images, these can be visually identified to verify that ROM-image selection works.

The source-code for this mini-cartridge looks similar to aforementioned minimal ROM-image:

    * = $8000

            .word Start                     ; cold-start vector
            .word Start                     ; warm-start vector

            .byte $c3, $c2, $cd, $38, $30   ; "CBM80"

            lda #4             ; <---
            sta $d020
            lda #0
            sta $d020
            jmp Start

    * = $9fff                               ; fill
            .byte $0

(Using a serial command-argument, the colour-code "4" indicated by the arrow can be changed into any other colour - for example, "1" for white.)

An example of such a mini-cartridge image sitting at the 1st 16k-slot in the flash (0x00000 - 0x03fff):

      09 80 09 80 C3 C2 CD 38 30 A9 01 8D 20 D0 A9 00 
      8D 20 D0 4C 09 80 FF FF FF FF FF FF FF FF FF FF 

As mentioned elsewhere in this text, I miswired ROML and ROMH somewhere, causing the need to swap lower and upper 8k-blocks in each ROM-image. Without this mistake, the 1st byte of the 1st ROM-image would be at flash-address 0x00000 instead of 0x02000.

2018-04-18/-20: bughunt: stray pulses on /IO1 or /IO2

Apparently, short stray pulses on /IO1 and /IO2 are generated at least by my C64. Although these are harmless for normal operation, they were mistakenly picked out by the Bait-a-Cart's microcontroller. A low-pass filter on the Bait-a-Cart side solves it.

(I only did measurements and tested a quickfix, because right now I don't actually have SMD-parts here to fix this problem on the Bait-a-Cart itself.)

How the C64 generates /IO1 and /IO2

For the previous progress-update (2018-04-15), I used a program to rotate a LED-pattern clockwise / counter-clockwise once when a pulse on /IO1 respectively /IO2 was detected. After running for a while, I noticed the LED-pattern moving without explicitly using PEEKs or POKEs. Changes were quite random. This probably indicated unexpected activity on /IO1 and /IO2.

As can be seen in the (partial) C64 schematic, a 74LS139 dual 1:4 demux/decoder (U15) generates both /IO1 and /IO2:

The pins circled in green are interesting: U15's pin 15 (input /EN2) enables its low-active outputs, of which 2 lines (pins 10 and 9, or /2Y2 and /2Y3) are directly connected to the cartridge-port's /IO1 and /IO2. These signals can only be low if U15's /EN2 (called "Enable" or "G" in this figure) is low:

(Table from ON Semiconductor 74LS139 datasheet, although my C64 used a Fairchild 74LS139PC.)

The select-inputs of this half of U15 are formed by A8 and A9 of the C64's address-bus. When /EN2 is low (active), /IO1 will be activated when only A9 is high, while /IO2 will be activated when both A8 and A9 are high.

Viewing /EN2, A8, A9 and either /IO1 or /IO2 on the scope, occasional short pulses on /IO1 and /IO2 can indeed be seen:

Propagation-delay of U15's select- en enable-inputs is in the order of 20-30 ns. As can be seen, the time between A9 rising and /EN2 being released is also around 20-30 ns, which will occasionally cause stray activity on either /IO2 or /IO1, depending on timing of A8 in those situations.


The Bait-a-Cart should ignore these short pulses, and should only be triggered by "real" I/O-area access-signals, which are in the order of 500 ns. Either the software or hardware could be altered to make this happen - I think a hardware-fix makes more sense.

Using a sensible low-pass filter between the C64's /IO1 and /IO2 signals and the Bait-a-Cart's corresponding microcontroller-inputs will filter out short pulses, while keeping /IO1 and /IO2 themselfes pretty much as-is, in order not to mess with ROM-cartridges plugged into the Bait-a-Cart.

Using an RC-filter of 4k7 Ohm and 22 pF (cutoff around 1.5 MHz), the Bait-a-Cart's microcontroller-input becomes quiet like the Danish countryside railroad-track at night:

The blue signal is raised a bit. This is caused by a 47k Ohm pull-up resistor, which I used to mimic the internal pull-up of the Bait-a-Cart's microcontroller-pin. (Have to think whether it makes sense in any situation to use pull-ups on these pins at all. :-)

Same setup, but with "real" I/O-area access-signals (500 ns) on /IO1 and /IO2:

(The rightmost picture is similar to the leftmost one, but zoomed out a bit.)

As can be seen, pulses still look good. The Bait-a-Cart's microcontroller will see the yellow trace as input, which is enough to trigger on.

2018-04-15: small test: verified that access to I/O regions can be captured

The Bait-a-Cart can reliably capture reads/writes by the C64 to I/O regions $DE00-$DEFF (IO1) and $DF00-DFFF (IO2). This mechanism will be used to allow C64-software to disable the Bait-a-Cart's flash in favour of an existing ROM-cartridge (or the C64's own BASIC/KERNAL).

To test this, I used a small program reading/clearing the AVR's EIFR (External Interrupt Flag Register). On the C64's side, I used simple PEEKs and POKEs.

2018-04-14(c): first succesful test with hosted ROM-cartridge

And later still...

I had a "Magic Desk I" 8 kb ROM-cartridge laying around, especially for testing-purposes.

Using a button on the Bait-a-Cart to toggle between "on"- and "off/programming" modes works just fine. In short, this hack demonstrates most of what the Bait-a-Cart was intended for. (It's all about self-confidence, I guess. :-)

This picture shows a typical setup with Bait-a-Cart and existing ROM-cartridge, inserted vertically.

2018-04-14(b): first succesful test in an actual C64

Later that day, ...

...I just had to see an actual C64 run a minimal ROM-image hosted on the Bait-a-Cart.

That's not just gibberish on the screen, it's all intended. Yay! :-)


I used the following program as minimal ROM-image:

    * = $8000

            .word Start                     ; cold-start vector
            .word Start                     ; warm-start vector

            .byte $c3, $c2, $cd, $38, $30   ; "CBM80": cart-identifier

            inc $d020                       ; increment border-colour
            jmp Start

    * = $9fff                               ; pad until 8 kb
            .byte $0

...assembled it with 64tass (or tass64):

    tass64 -q -B -b -L lst.lst --tab-size=1 -o crt.crt asm.asm

...and first tested it in VICE:

    x64 -autostart-warp -cart8 crt.crt

...which seemed to work:

(TBH, I can't remember now why the middle of the screen remains blank - either it is a VICE quirk, missing command-line option, video-emulation thing, KERNAL-code thing, or something else. IIRC, the video should not have been initialised when cartridge-code is found and executed. Oh well, worked as expected on real hardware.)


To test this image using actual hardware, I wrote the verbatim hex-code to the Bait-a-Cart's flash from software:

    static const uint8_t a[] = { 
        0x09, 0x80,
        0x09, 0x80,
        0xc3, 0xc2, 0xcd, 0x38, 0x30,
        0xee, 0x20, 0xd0,
        0x4c, 0x09, 0x80


    uint8_t i;

    for ( i = 0; i < sizeof( a ); i++ )
        flash_prog_byte( 0x2000 + i,  a[ i ] );

Note the "0x2000" offset (8 kb) near the bottom - this is because the ROML signal was erroneously used instead of ROMH as 14th address-line for the flash, so we will have to swap the lower and upper 8 kb in each image from now on...

After programming, the Bait-a-Cart basically asserts /EXROM and releases /GAME towards the C64, to indicate it is pretending to be an 8 kb cartridge. The Bait-a-Cart then resets the C64, and goes to sleep while the C64 validates and runs the cartridge-image.

2018-04-14: nicified software and tested on-board flash

Instead of using magic constants in I/O-related software, cleaned it up to give way for final software. Tested the Bait-a-Cart's flash, and simplified some intended modes of operation.

Modes of operation

Let's distinguish just 2 modes of operation:

Switching between these 2 modes effectively makes the C64 run either from the Bait-a-Cart's flash or from the existing ROM-cartridge.

Simplified pictures help to illustrate the connections between different components in each mode ("MCU" = microcontroller):


In the "on"-mode, the Bait-a-Cart's flash is used instead of the existing ROM-cartridge:

In this mode, the microcontroller is effectively decoupled from the Bait-a-Cart's flash (by tri-stating its connections to the flash's address- and data-signals). The ROM-cartridge is disabled, by making sure it can never be selected (by de-asserting the ROMDRV signal in the schematic).


In the "off/programming" mode, the ROM-cartridge is enabled instead of the Bait-a-Cart's flash:

In this mode, the Bait-a-Cart's flash is coupled to the microcontroller in order to allow for programming through serial commands, and the ROM-cartridge is connected to the C64's data-bus. (Or rather, it is allowed to be selected by the C64 by asserting the ROMDRV signal).

If interested, see the schematic for details.


The Bait-a-Cart has a 256 kb flash. The C64 cartridge-size is limited to 16 kb, disregarding bankswitch-magic. Therefore, the Bait-a-Cart's flash can hold a maximum of 16 cartridge-images of 16 kb each.

The 18-bit address-bus of the Bait-a-Cart's flash (for 256 kb in total) can be divided into 2 parts:

I made code to properly configure all "switches" shown in the above pictures in both "on"- and "off/programming"-modes, and tested the flash w.r.t. chip-ID read, byte-read, byte-write, sector-erase and chip-erase, and all worked well.

2018-04-07: related work: made a small digital probe

Made a small digital probe to quickly distinguish high / low / floating pins.


Toyed with this idea for a while already, but this project finally made me get off my ass and make it.

Using a dual comparator, this probe can quickly show whether a pin is floating, grounded or connected to Vcc. Perhaps it'll come in handy when checking correct working of the latches.

2018-04-04: made an overview of relevant cartridge-adapter signals

Just made some friendly documentation, avoiding the need for myself to constantly switch between various on-line references.

Signals of the C64's cartridge-adapter

The C64's cartridge-adapter (or "expansion port") has 44 signals in total.

Apart from 16 address- and 8 data-lines, there are a number of control-signals. Some of these signals (e.g. /DMA, /IRQ or DOT_CLK) are not relevant for the Bait-a-Cart functionality, and are simply passed from the C64's cartridge port to an existing ROM-cartridge plugged into the Bait-a-Cart.

All of the C64's control-signals used by the Bait-a-Cart are listed below, along with their direction (from C64 towards ROM-cartridge or vice versa):

    pin     name    C64 <--> cart   descr

     8      /GAME       <--         for memory-reconfig; see GAME and EXROM table below
     9      /EXROM      <--         for memory-reconfig; see GAME and EXROM table below
    11      /ROML        -->        access in range $8000-$9FFF, and RAM disabled (GAME and EXROM)
     B      /ROMH        -->        access within $A000-$BFFF or $E000-FFFF (GAME and EXROM), and RAM disabled
     7      /IO1         -->        access within $DE00-$DEFF
    10      /IO2         -->        access within $DF00-$DFFF
     C      /RST        <--         (PU) pulled low: reset CPU

What does the Bait-a-Cart do with the C64's cartridge-port signals?

This section goes over each of the C64's cartridge-port signals, and describes the way in which the Bait-a-Cart uses that signal. (See the schematic for details.)

Focus is on the C64's cartridge-port signals and those of the Bait-a-Cart's own cartridge-slot. The fact that the Bait-a-Cart's microcontroller is able to program the Bait-a-Cart flash is ignored here for sake of simplicity.


Both of these signals are available on the Bait-a-Cart's own cartridge-port (as AUX_/ROML and AUX_/ROMH), albeit gated using a ROMDRV signal from the Bait-a-Cart's microcontroller. The microcontroller can thus prevent the existing ROM-cartridge plugged into the Bait-a-Cart from ever being selected. This is exactly what happens whenever the Bait-a-Cart's flash is enabled in favour of the existing ROM-cartridge (see "Data-bus" below for more info).

Furthermore, whenever the Bait-a-Cart's flash is enabled, ...


A ROM-cartridge can pull /GAME, /EXROM or both to ground, to tell the C64 which memory-regions the cartridge implements, and where those regions should be mapped in the C64's address-space.

(Apart from the /GAME and /EXROM lines in the C64's cartridge-port, the 6510 CPU itself provides 3 pins - CHAREN, HIRAM and LORAM - to further configure the memory-configuration during run-time. These 3 pins are not discussed in this project. If interested, look at details about each available memory-configuration.)

In short, the /GAME and /EXROM pins allow for 3 cartridge-flavours (see here for details):

    /GAME   /EXROM  descr

      1       1     (no cartridge present)
      1       0     8k cartridge. /ROML: $8000-$9FFF
      0       0     16k cartridge. /ROML: $8000-$9FFF; /ROMH: $A000-$BFFF
      0       1     "Ultimax"-style 16k cartridge. /ROML: $8000-$9FFF; /ROMH: $E000-$FFFF

Thus, disrding runtime memory-configuration using the CHAREN / HIRAM / LORAM lines, part of a normal 16k cartridge occupies the BASIC ROM region ($A000-$BFFF), while part of an Ultimax-style cartridge occupies the KERNAL ROM ($E000-$FFFF). An 8k cartridge doesn't occupy either BASIC or KERNAL ROM regions.

In the Bait-a-Cart setup, the Bait-a-Cart's microcontroller reads the ROM-cartridge's intended memory-configuration through the AUX_/GAME and AUX_/EXROM pins on its own cartridge-slot, and simply copies this configuration to /GAME and /EXROM on the C64-side whenever the Bait-a-Cart's flash is disabled.

Whenever the Bait-a-Cart's flash is enabled, the size (8k or 16k) of a hosted ROM-image is used to determine the value of /GAME and /EXROM to communicate to the C64.


The data-bus signals of the C64's cartridge-port are directly connected to corresponding data-bus pins of the Bait-a-Cart's own cartridge slot.

Furthermore, the data-signals of the Bait-a-Cart's own on-board flash are connected to this data-bus as well, via a tri-state buffer (U106 in the schematic).

Whenever the Bait-a-Cart's flash is enabled, the Bait-a-Cart...

Whenever the Bait-a-Cart's flash is disabled, the Bait-a-Cart...


Like the data-bus, the address-bus signals of the C64's cartridge-port are directly connected to corresponding address-bus pins of the Bait-a-Cart's own cartridge-slot.

The address-bus is also connected to the address-signals of the flash via a tri-state buffer (U103 and U105 in the schematic). The only function of this tri-state buffer is to allow the Bait-a-Cart's microcontroller instead of the C64 to address the Bait-a-Cart flash, in order to program or verify it. (Programming of the flash is ignored in this section.)

/IO1 and /IO2

The C64's /IO1 and /IO2 signals are activated when the C64 addresses memory in the range $DE00-$DEFF respectively $DF00-$DFFF. These regions are often used as means to communicate with intelligent cartridges or peripherals.

Both of these signals are directly connected to corresponding pins of the Bait-a-Cart's own cartridge-slot, and to 2 inputs on the microcontroller. By simply accessing aforementioned special regions, a C64-program running off the Bait-a-Cart's flash can tell the Bait-a-Cart to disable its flash, and enable the existing ROM-cartridge instead.

This mechanism can e.g. be used to make a short advertisement-intro or info-screen which, when a key is pressed, will jump to the existing ROM-cartridge's code.


The reset-signal is pulled high by the C64, and is directly connected to the Bait-a-Cart's cartridge-slot. Either the existing ROM-cartridge or the Bait-a-Cart can pull this signal low, to force a system-reset. One of the buttons on the Bait-a-Cart is indeed intended as a reset-button, although actual behaviour is software-dependent.

2018-04-03: hack/fix: connected C64's /IO1 and /IO2 to microcontroller's pins with latch-function

Fixed a design-bug, allowing the Bait-a-Cart microcontroller to read very short memory-access pulses generated by the C64, by routing them to I/O-pins with latch-functionality.


The C64 activates cartridge-port pins /IO1 and /IO2 whenever it accesses certain I/O memory-regions under certain circumstances.

The Bait-a-Cart will interpret accesses to such a "magic" region as a request to disable itself, connect the existing ROM-cartridge to the C64, reset the C64, and thus effectively run the existing ROM-cartridge plugged into the Bait-a-Cart.

Although the C64 runs at approximately 1 MHz, a single read/write access doesn't take very long. To monitor even these short access-periods, the Bait-a-Cart microcontroller could poll the /IO1 and /IO2 lines continuously, or use an interrupt-/latch-approach.

Since the Bait-a-Cart microcontroller has other stuff to do besides monitoring these 2 pins, polling is not a good idea.

The Atmel AVR microcontrollers implement hardware-support for external interrupts on certain pins, and not on other pins. Whenever a preconfigured event (rising/falling edge or level) occurs on the pin, it can either execute an appropriate interrupt-handler, or raise a register-flag to indicate the event has occurred (i.e. "latch" the event).

In the context of the Bait-a-Cart software, it doesn't matter whether interrupt-handlers or register-flags are used - bottom line is that short events on the C64's /IO1 and /IO2 pins must not be lost.

In the schematic, the C64's /IO1 and /IO2 pins were routed to microcontroller-pins without this interrupt-capability. That would mean that the Bait-a-Cart software would have to revert to polling, or the PCB would have to be hacked to route /IO1 and /IO2 to appropriate microcontroller-pins - which is what I did.

Detail of the schematic showing the mistake:

(Instead of microcontroller pins 90 and 91, for example 8 and 9 should be used. "INTx" means that a pin has interrupt-capability.)

I used 2 wires to simply connect pin 90 to 9, and 91 to 8:

(Pins 90 and 91 can then simply be configured as floating inputs, to be ignored in software. The other 2 green wires that can be seen running off the picture's edge fix an aforementioned problem where USB/serial signals were accidentally connected to non-UART pins on the microcontroller.)

To test latch-/interrupt-functionality of C64's /IO1 and /IO2 outputs, I made a small hack to mimic the C64's /IO1 and /IO2 outputs:

(/IO1 and /IO2 are simply pulled up, and can be grounded by using the blue GND-wire.)


Nothing to see here, really...

ATmega640's EIFR (External Interrupt Flag Register) shows the latched events, should they occur. Writing bitwise "1" to bits in EIFR clears the event.

Since /IO1 and /IO2 are normally pulled up in this test-setup, I configured the microcontroller to listen for falling-edge events using EICRA and EICRB (External Interrupt Control Register A and B).

2018-04-02: figured out what I was actually doing, circuit-wise

So... this is the first official effort :-)

Most time was spent reading the schematic and drawing connections on paper. Next thing I have to do is probably make simplified drawings showing the essence of the Bait-a-Cart circuit in terms of connections with the C64's cartridge-port and ROM-cartridge.

(Stuff you see here already existed before the start, including the soldered wires, ribbon-cable, tape and black cartridge-slot.)


Spent about 1.5 hours verifying the Bait-a-Cart microcontroller could still be programmed from the PC, but mainly tracing the schematic to see how C64, Bait-a-Cart and existing ROM-cartridge were connected again.

To get an idea what a typical debug-setup looks like:

That's the front-side, facing the user.

The black cartridge-slot had already been mounted earlier. (An existing ROM-cartridge can be plugged into this slot.) The small PCB in the upper right corner is a programming dongle, allowing for a host-PC to program the Bait-a-Cart microcontroller during development and debugging. (That dongle itself is a homebrew design as well, BTW. :-)

There are 4 reverse-mounted LEDs to show status and selected image. Right under the texts "SEL" (select) and "RST" (reset) there are 2 side-viewing buttons to control the Bait-a-Cart. These buttons have no function yet, except to rotate the LED-pattern to show that LEDs and buttons actually work.

Both the blue and green cables go to respectively the "programming" and "serial terminal" USB-ports on my laptop. (I use AVRDUDE as programmer, and either Minicom or Picocom as serial terminal-emulator.) At this point, there is no actual communication-protocol between host-PC and Bait-a-Cart yet, but I use the serial terminal for ad-hoc debugging and feedback.


I chose to put all electrical components at the back of the PCB to make it look better, and to shield them a bit from poking fingers.

The piece of ribbon-cable soldered to the PCB's programming-pads quickfixes a flaw: the programming-pads are mechanically inaccessible when the Bait-a-Cart is plugged into the C64... I should have put them a bit more towards the right-most edge of the PCB as shown here. The tape adds a bit of strain-relief.

The 2 thin green wires quickfix another aforementioned problem: I shuffled the microcontroller's pin-assignments around a bit too much, and forgot to leave the UART-pins in place... The green wires connect the FTDI bridge-chip (lower right) to the correct UART-pins on the microcontroller (big chip, lower left).

The USB-socket into which the green USB-cable is plugged at the moment is accessible when the Bait-a-Cart is plugged into the C64, as well as the 2 buttons (nearly visible, at the top edge of the PCB, near the blue USB-connector).

2018-03-14: status quo before RC2018/04 starts

Please read this first before accusing me of a "false start". :-)


At this moment, the Bait-a-Cart hardware exists. It basically consists of...

It looks like this:

(Fish-logo is visible from the top when the Bait-a-Cart is plugged into a C64, and an existing ROM-cartridge is plugged into the Bait-a-Cart vertically, from the top as well, into a card-edge connector (not yet mounted when the picture was taken). Shown in the front is the actual bottom-side of the Bait-a-Cart, containing the electrical components such as the microcontroller and the parallel flash.)

If interested, take a look at the schematic. One known mistake is that the microcontroller's Tx/Rx are not connected to the FT232RL USB-to-serial bridge. This has already been fixed by 2 bodge-wires (not shown on the picture).


At this moment, only the LEDs and pushbuttons have been tested: push the button to change the LED-pattern. This implicitly also tested the microcontroller on the Bait-a-Cart.


At this moment, a small intro for the C64 has already been made - it looks like this:

It's a colour-animation of letters, with a black "pupil" nervously moving behind the letters. It doesn't feature any sound or music. See this thing in action (YouTube), or an earlier snapshot of work-in-progress.

Delivered to you by Vim, GNU Make, MultiMarkdown, bozohttpd, NetBSD, and 1 human.