Rendering animated GIFs on a Commodore64
If you’ve always thought the one thing the world needed was rendering animated GIFs on a Commodore64, then you’ve come to the right place! I’ve written a tool called gif-to-c64-sprites which takes an animated GIF file as input, and outputs a stream of Commodore64 hardware sprite format data.
TLDR; show me the final result
How does that work?
First, a refresher on C64 hardware sprites… Hardware sprites are 24x21 pixels in size, and can be moved freely around the screen, either above or below the character or pixel-based screen memory. They are commonly used for small moving items because they do not require memory to be copied around to change their position on the screen. The VIC-II graphics hardware is responsible for rendering the sprites, freeing the CPU to do other things. Up to 8 sprites can be displayed at once, each one enabled by setting the corresponding bit in $d015
( sprite-enable register). Other registers specify the pointer to sprite data, position of the sprite on the screen and color, horizontal and vertical scaling, and collision detection with another sprite.
This example image shows how the data for a default single-color sprite is laid out. A sprite only takes up 63 bytes. In single-color mode, those 63 bytes are used to represent 24x21 pixels, where the pixel can be either set or not set and is represented with a single bit.
24 bits (3 bytes) across * 21 rows = 63 bytes. In the image, the first row is the byte count, the second row is the bit count.
The sprite data pointer register (like all registers) is only 8 bits, but the sprite data can live anywhere in the active 16kb memory bank - how can that work!? To make the whole 16kb addressable, the value in the register is multiplied by 64 internally by the VIC-II before fetching from memory. In effect, the register selects which of the possible 256 locations to read sprite data from, rather than the raw memory address. The constraint this implies is that we must place sprite data on a 64 byte boundary. Heres some example code to enable sprite #0:
To animate a C64 sprite, first you need to store multiple 63-byte chunks of sprite data, one after the other, on 64-byte boundaries. (eg at 64, 128, 192…). Then on each screen refresh you change the data pointer to point to the next chunk of sprite data, by doing inc $07f8
or similar. This command adds 1 to the specified memory address. Because the value of the register is multiplied internally by 64, this has the effect of moving the pointer to the start of the next 64 byte chunk of sprite data. Once you reach the last chunk of sprite data, you reset the pointer back to the initial value, and you have an animation loop!
Thats the C64 side of things, for more details, there are plenty of better descriptions than mine!
gif-to-c64-sprites
Now that we know how to animate hardware sprites on the C64 the only trick left is how to generate the sprite data. SpritePad is a great tool but unfortunately I am too lazy to create my own assets. :(
Instead I wrote gif-to-c64-sprites - a little node script which uses gifsicle and get-pixels to pick out pixel data from each frame in an animated gif. Each frame is converted into a C64 sprite chunk by bitshifting pixels into byte buffers. As it only currently outputs single-color sprites, it works best for cartoon-like source images.
Below are some examples I generated using the tool and the C64 sprite animation code above.
Double-size mode
When you use the --doubleSize
option, 4 sprites are generated per frame, by splitting the source image into 4 quadrants. This gives better image quality, with the expense of using 4x the amount of data. On the C64 we display 4 sprites in a square shape next to each other to create the image. In the screenshot below, I’ve set each sprite to be a different color to illustrate how the final image is combined. There is an example C64 app showing how to do this in the gif-to-c64-sprites repo.
https://github.com/jeff-1amstudios/gif-to-c64-sprites
Conversation on reddit -
Rendering animated GIFs on a Commodore64 from gamedev