Bitmap Bonanza!

MEGA65 Projects

Bitmap Bonanza! Dan’s MEGA65 Digest for May 2023.

Bitmap Bonanza!
A photo of Beaker of the Muppet Show, as displayed on the MEGA65
Beaker of the Muppet Show, as displayed on the MEGA65.

The MEGA65’s VIC-IV video chip has multiple graphics modes. Each mode and feature pulls ideas from some point in vintage computing history: terminal-style text output, character graphics, hardware sprites, palette banks, full-screen scrolling, and even Amiga-style blitter objects and graphics-optimized DMA operations. Several VIC-IV modes are fully backwards compatible with the VIC-II of the Commodore 64 and 128, and the VIC-III of the Commodore 65.

In today’s Digest, we’ll be looking at the VIC-III bitplane graphics mode. While this mode doesn’t show off all of the MEGA65’s capabilities, it’s one of the more fun modes to use with BASIC 65 thanks to its library of 31 drawing commands and functions. In Featured Files, we’ll see a new game that takes advantage of BASIC 65’s graphics system to draw vector art. We’ll also try using a built-in feature to display full-screen high color photographs and illustrations.

This month’s Features Files are all brand new, hot off the presses!

Onion Cake and the Hungry Dinosaurs, by Gurce
Onion Cake and the Hungry Dinosaurs, by Gurce.

Onion Cake and the Hungry Dinosaurs, by Gurce. This short comedy adventure game combines a text command interface with gorgeous full-screen vector art by Gurce’s sister Ayca and original music. You get to watch the BASIC code fill in the image as it loads each scene, and the code cleverly uses the MEGA65’s memory and DMA features to cache the render so they display instantly on subsequent visits.

Gurce wrote a MEGA65 vector art editing tool, called VART, to develop the image format used by the game. Both the game and the editor are written in the Eleven programming environment.

Onion Cake and the Hungry Dinosaurs is inspired by another MEGA65 game, Escape from Onion Cake by MrZaadii, from his Mega65 Games Pack from 2018. MrZaadii’s game also uses bitplane graphics with pixel art, pre-dating some of the newer graphics features that were added by the MEGA65 team.

Don’t miss Gurce’s behind-the-scenes presentation of the making of Onion Cake.

Old Mine Hoist, by GierS
Old Mine Hoist, by GierS.

Old Mine Hoist by GeirS. This addictive one button game has you lowering a box of supplies down a perilous mine shaft. Struts on the mineshaft walls and barrels of dynamite risk damaging your payload. How far can you get?

Rescue Inc., by SirLazarus
Rescue Inc., by SirLazarus.

Rescue Inc. by SirLazarus. An original solitaire strategy game written in BASIC 65. Launch probes to locate missing battleships in the dark ocean waters. Try to find all the ships with as few probes as possible.

C64 for MEGA65 V5 alpha releases

Commodore 64 games running on the C64-for-MEGA65 core: Sam's Journey (left), Rogue 64 (top right), Choplifter (bottom right)
Commodore 64 games running on the C64-for-MEGA65 core: Sam's Journey (left), Rogue 64 (top right), Choplifter (bottom right)

In last November’s Digest we mentioned the Commodore 64 core for the MEGA65 by MJoergen and sy2002, the best way to run Commodore 64 software on MEGA65 hardware. The core completely reconfigures the FPGA for a precise recreation of the Commodore 64 chipset, capable of running software with a very high degree of accuracy and compatibility. The latest stable release is version 4, with support for D64 disk images on the SD card, joystick port peripherals, built-in RAM expansion, and multiple display modes for modern displays.

The C64 for MEGA65 project is ramping up for a major upgrade with release 5. sy2002 has been posting alpha releases to the Discord, and you’re invited to help test! As of V5 alpha 22, the core supports:

  • Running virtual cartridges as CRT files from the SD card
  • Running Commodore 64 PRG files directly from the SD card
  • Connecting IEC devices to the MEGA65’s IEC port, including external disk drives and printers
  • Connecting C64 hardware cartridges to the MEGA65’s expansion port, with support for EasyFlash 1 cartridges
  • Installing a licensed JiffyDOS ROM for use with IEC disk drives

As this is an alpha testing release, it comes with a few caveats and has known issues. Be sure to read through the list before installing an alpha release.

For simpler C64 hardware cartridges, you can turn off the MEGA65, connect the cartridge, hold the No Scroll key while turning it on, then select the C64 core from the core selection menu. Remember that if you allow it to boot with the MEGA65 core, it’ll just try to run the cartridge software in its C64 mode, which is not the same as the C64 core and is less likely to be successful. lydon is working on improving the MEGA65 core such that the boot-up process can be configured to automatically select different cores for different types of hardware cartridges.

For EasyFlash and some fancier cartridges, the core selection menu won’t work at all with the cartridge connected. To get this to work, you have to do something we would have considered unthinkable in the days of static-sensitive PLA chips: start with the cartridge not connected, boot with No Scroll, select the C64 core, then connect the cartridge and press the reset button on the side of the machine.

EasyFlash 1 or 1CR works with the latest alpha release. EasyFlash 3 does not yet work. You can buy an EasyFlash 1CR from Daniel Mantione (dmantione in the MEGA65 Discord). Be sure to also order a Protoparts enclosure from Daniel (or from Protoparts directly) to ensure the best fit in the MEGA65 cartridge port.

When using the IEC port with the C64 core to connect disk drives, keep in mind that the C64 core always uses device number 8 for the virtual drive for D64 disk images. Set your external drive to any other device number for use with the C64 core. You can use this set-up to copy D64 disk images to physical floppy disks and vice versa.

Whether you’re installing the C64 core’s latest stable release V4 or trying out the V5 alpha release, the installation process is the same:

  1. Download the C64-for-MEGA65 release that you want to install, and unpack the archive.
  2. Copy the .cor file to the root of your MEGA65 SD card.
  3. Make a folder in the root of the SD card named c64.
  4. Copy the file named c64mega65 to the c64 folder. Also take this opportunity to copy any D64, CRT, and C64 PRG files you want to try to the c64 folder as well.
  5. Reinstall the SD card in your MEGA65.
  6. With the MEGA65 turned off, hold the No Scroll key, then turn it on. This opens the core selection menu.
  7. Select a core slot to overwrite with the core, then hold Ctrl and press the number for that slot. Follow the prompts to install the C64M65~1.cor file.

If you’re juggling multiple versions of the C64 core, be sure to use the c64mega65 file that comes with that version. This file saves your menu preferences, and the file format has changed between V4 and V5.

Visit the #c64-core channel in the MEGA65 Discord for alpha release downloads, release notes, and troubleshooting issues.

Saying hello to BASIC 65 bitmap graphics

Gurce’s Onion Cake and the Hungry Dinosaurs and MrZaadii’s original Escape from Onion Cake both use the bitmap graphics system built in to BASIC 65. This subsystem allows for rendering high resolution pixel perfect images using BASIC commands. Bitmap graphics are implemented as a separate graphics mode from the text mode you see when writing programs, so you can only see either the text screen or a graphics screen on the display at one time. There are 31 commands and functions you can use to manipulate screens and draw figures in your BASIC programs.

Because the graphics screen hides the text screen, this isn’t easy to experiment with from the READY. prompt. Instead, you can use a short program like this one:

10 SCREEN 320,200,2
20 PEN 1
30 LINE 25,25,295,175
40 GETKEY A$
50 SCREEN CLOSE
The result of a program that draws a white diagonal line on a black graphics screen
The result of the program: a white line on a black background.

In this example, line 10 opens a new screen that is 320 pixels wide and 200 pixels tall, with a palette of four colors (a “bit depth” of 2). Line 20 sets the color used by subsequent drawing commands (the “pen” color) to palette entry 1 (the second of four colors), in this case using the default palette’s color of white. (See the PALETTE command for a way to change the palette colors.) Line 30 draws a line using the pen color from screen coordinate (25,25) to (295,175), a diagonal stripe.

Line 40 pauses until the user presses a key, and line 50 closes the screen and returns to text mode. This is a useful recipe when you’re experimenting with graphics commands. If your program exits without closing the screen, the MEGA65 will stay in graphics mode and you won’t be able to see the READY. prompt. This can happen if there is an error in your code that stops the program before it closes the screen. If your program appears to get stuck showing a graphics screen, hold Run/Stop and press Restore to reset the display to text mode and a fresh READY. prompt.

The graphics system supports widths of either 320 or 640 pixels, heights of either 200 or 400 pixels, and color bit depths from 1 (two colors on screen at a time) to 8 (256 colors). You can have up to 16 colors (bit depth 4) with a 640x400 screen, and up to 256 colors (bit depth 8) for the other possible resolutions. It is possible to allocate multiple screens and switch between them, though this limits the possible sizes and bit depths due to available memory. Each color palette entry can be any of 4,096 possible colors, with 16 possible values of each of the red, green, and blue components.

It’s worth noting that the MEGA65 video hardware is capable of 24-bit color. The BASIC 65 graphics system only uses a subset of the MEGA65’s graphics capability.

As tempting as it is to tour all 31 commands of the graphics system in this Digest, for now I will leave it to you to discover them in your User’s Guide. Look for SCREEN, BOX, CHAR, CIRCLE, DOT, ELLIPSE, LINE, PAINT, PALETTE, PEN, POLYGON, and SCNCLR, for starters.

Saving the graphics screen to disk

BASIC 65 includes a command for saving the currently active bitmap graphics screen to disk. The SAVEIFF command takes a filename, and optional drive and unit numbers. It only works with a graphics screen active, so it’s best to make it part of the program.

35 SAVEIFF "LINE-EXAMPLE"

The image is saved to disk in the IFF-ILBM file format, a bitmap graphics file format common to Amiga computers. The file appears on the disk as a PRG file, but it’s actually just the IFF-ILBM data, without the two-byte address header of an actual PRG file.

IFF-ILBM files use a simple form of data compression to reduce file sizes. The iamge produced by the program above of a single line on a blank screen at 320x200x2 occupies six blocks on disk to store 1,502 bytes. More complex images, or higher resolutions or color bit depths, will require more space.

Loading the graphics screen from disk

A file saved with SAVEIFF can be loaded back to the active graphics screen with another command: LOADIFF. As before, the graphics screen must be active.

10 SCREEN 320,200,2
20 LOADIFF "LINE-EXAMPLE"
30 GETKEY A$
40 SCREEN CLOSE

Due to how multi-color bitmap graphics are stored, the color bit depth of the active screen must match the file. Loading an image that was saved from a smaller resolution than the active screen (for example, loading a 320x200 image onto a 640x400 screen) will simply load the image into the upper-left corner of the screen, leaving the rest of the screen intact.

SAVEIFF includes the current palette settings with the image data in the IFF-ILBM file. You do not need to restore the palette separately in your program. Notice that the current palette will be overwritten by LOADIFF.

Exploring the IFF-ILBM file format

The IFF-ILBM file format is a standard format developed by Electronic Arts in cooperation with Commodore in 1985. IFF, or “Interchange File Format,” is what is known as a container (or “envelope”) format, capable of representing multiple chunks of data of many kinds. Amiga fans mostly know IFF files as still images of the inner format ILBM, or “Interleaved Bitmap.” The IFF-ANIM format was a variant for storing multiple ILBM images as an animation in a single IFF envelope, also popular on the Amiga. As a kid, I only knew that IFF files contained either stills or animations, and you could make them with Deluxe Paint.

Here is the beginning of the IFF-ILBM file created by the BASIC program above, as hexadecimal values:

00000000  46 4f 52 4d 00 00 05 de  49 4c 42 4d 42 4d 48 44  |FORM....ILBMBMHD|
00000010  00 00 00 14 01 40 00 c8  00 00 00 00 02 00 01 00  |.....@..........|
00000020  00 00 00 00 01 40 00 c8  43 4d 41 50 00 00 00 0c  |.....@..CMAP....|
00000030  00 00 00 ff ff ff ff 00  00 00 ff ff 42 4f 44 59  |............BODY|
00000040  00 00 05 9a d9 00 d9 00  d9 00 d9 00 d9 00 d9 00  |................|

An IFF file contains one or more chunks. Each chunk has a four-byte type ID and a four-byte size, followed by that many bytes of data. The SAVEIFF command creates a single outer chunk of type FORM, so it begins with those uppercase letters encoded as ASCII (46 4f 52 4d). The size is a signed 32-bit integer in Big Endian format (largest digits first), so 00 00 05 de represents a size of hexadecimal $05de, or 1,502 bytes. It’s not obvious from looking at it, but this is actually a bug in SAVEIFF: the actual data size is 1,494 bytes, but it is erroneously including the eight bytes of the FORM header in this size. Because of this bug, some tools will complain about an “unexpected end of input file” when reading this.

According to the IFF specification, a FORM chunk is a record with an inner type and one or more inner chunks. In this case, the inner type is ILBM, so those are the next four bytes (49 4c 42 4d). The inner chunks have types BMHD, CMAP, and BODY, which are part of the ILBM specification for still images. You can see these identifiers in the hex dump, along with their four-byte sizes and data.

Reading along with the ILBM specification for the $00000014 (20) bytes of the BMHD chunk, we can see the following properties of the image:

  • 01 40 00 c8: The image is $0140 = 320 pixels wide, and $00c8 = 200 pixels tall.
  • 00 00 00 00: The image starts at coordinate (0,0) on the screen.
  • 02: It has a color bit depth of 2.
  • 00: It does not use the mask feature of the ILBM format.
  • 01: The data uses Run-Length Encoding for compression (type 1).
  • 00: This is unused.
  • 00 00: This is related to the mask feature, not used here.
  • 00 00: This file describes its aspect ratio as 0:0 ($00 $00). This is not actually a valid aspect ratio. Some tools will complain, and just assume a ratio of 1:1.
  • 01 40 00 c8: The intended screen size for the image is 320x200, the same size as the image.

The CMAP inner chunk describes the color palette. With a bit depth of 2, there are four possible colors in the image, each described as one byte for each of the red, green, and blue components, $0000000c (12) bytes in total.

  • Color 0: red = $00, green = $00, blue = $00, aka black
  • Color 1: red = $ff, green = $ff, blue = $ff, aka white
  • Color 2: red = $ff, green = $00, blue = $00, aka bright red
  • Color 3: red = $00, green = $ff, blue = $ff, aka cyan

The IFF-ILBM format supports RGB component values from 0 to 255 (8 bits). The MEGA65 only supports component values from 0 to 15 (4 bits). LOADIFF uses the most significant four bits of each RGB component value when setting the palette, effectively rounding each number down to the nearest 16.

The spec describes the RLE algorithm that is used for the BODY chunk. For example, $d9 $00 represents forty zeroes, and you can see this repeated in the excerpt above. The decoding algorithm will build out 320x200 bits for each of the four bitplanes. There’s a bit in each bitplane for each pixel of the image. Take the pixel’s bit from each of the bitplanes, starting with the lowest bit in the first bitplane, to get the pixel’s color number.

ILBM is useful for Amiga and MEGA65 BASIC bitmap graphics because these systems represent the screen as bitplanes in memory, so it’s easy to save the image just by compressing memory, and load it again just by decompressing it. The MEGA65 has other graphics modes that don’t use bitplanes.

Do you believe in ImageMagick?

Most modern graphics software does not support the vintage IFF-ILBM image format. To use an image saved with SAVEIFF, it must be converted to a modern format, such as PNG.

ImageMagick is the ultimate software suite for converting and manipulating image data in a wide variety of formats. It’s free, packed with features, and runs on most operating systems. You typically use ImageMagick from the command line, which makes it great for automation scripts. See the ImageMagick Downloads page for installation instructions for your operating system. On a Mac using Homebrew, install this with: brew install imagemagick

In most cases, you can convert images between formats using the magick convert command:

magick convert line-example.iff line-example.png

There’s a slight problem when trying this with files made with the MEGA65’s SAVEIFF command. Those SAVEIFF bugs about the FORM chunk length and 0:0 aspect ratio cause the inner workings of ImageMagick to believe that the conversion failed. When I run the magick convert command on a SAVEIFF file, I get these messages, and no PNG file:

ilbmtoppm: warning - illegal aspect ratio 0:0, using 1:1
ilbmtoppm: input is a 2-plane ILBM
ilbmtoppm: Unexpected end of input file
convert: delegate failed `'ilbmtoppm' '%i' > '%o'' @ error/delegate.c/InvokeDelegate/1924.
convert: no decode delegate for this image format `ILBM' @ error/constitute.c/ReadImage/781.
convert: no images defined `line-example-test.png' @ error/convert.c/ConvertImageCommand/3342.

As shown in these messages, ImageMagick delegates IFF-ILBM conversion to a tool called ilbmtoppm, part of another software suite called NetPBM that’s included with ImageMagick. When I run the ilbmtoppm tool directly, it complains about the issues with the file, but successfully converts the IFF-ILBM file to the PPM format:

ilbmtoppm line-example.iff >line-example.ppm

The PPM format is supported by some modern software, and can be further converted to other formats by ImageMagick.

magick convert line-example.ppm line-example.png

The PPM file can also be converted back to IFF-ILBM format with the SAVEIFF errors corrected:

magick convert line-example.ppm line-example-fixed.iff
The example program's output converted to PNG format, a white line on a black background
The example program's output converted to PNG format

Converting images to be displayed on a MEGA65

You can reverse the conversion process to prepare any image, including high color photographs, as an IFF-ILBM file, so it can be displayed on your MEGA65 with the LOADIFF command in a BASIC program.

For this to work, it needs to not only be in IFF-ILBM format, but also be the right size and color depth for a BASIC 65 screen. You can try adjusting your image to 320x200 and 256 colors (8-bit color) using image editing software, or you can let ImageMagick do it for you.

magick convert photo.jpg -resize 320x200\! +dither -colors 256 -depth 8 photo.iff

This command converts photo.jpg to photo.iff with the following options:

-resize 320x200\! : Resize the image to 320 pixels wide and 200 pixels high. The \! tells ImageMagick to stretch the original image to fit this aspect ratio, which may or may not be what you want. ImageMagick has other options for resizing that can crop or extend the image instead. See any ImageMagick tutorial for examples.

+dither : Disable dithering. Dithering is a technique for representing color and value gradations in digital images, using noise patterns and just a few colors. In this case, we’re converting a JPEG, which may introduce compression artifacts. Disabling dithering prevents these artifacts from being preserved as dithered patterns in the final image. If you’re converting from another format, you may be able to leave dithering enabled.

-colors 256 -depth 8 : Set the color depth to 8, with the maximum of 256 colors.

Photo of Huxley the cat, as a 256-color 320x200 IFF-ILBM file, displayed on a MEGA65
Photo of Huxley the cat, as a 256-color 320x200 IFF-ILBM file.

I had some issues with ImageMagick and NetPBM that were resolved by updating to the latest versions. If you already have these tools installed from a while ago, consider updating. With Homebrew: brew update && brew upgrade

Four-Byte Burger

Four-Byte Burger (1985) by Jack Haeger, recreated by Stuart Brown aka Ahoy, displayed on a MEGA65 and rotated 90 degrees
Four-Byte Burger (1985) by Jack Haeger, recreated by Stuart Brown aka Ahoy, displayed on a MEGA65 and rotated 90 degrees

In 1985, Jack Haeger created an illustration of a hamburger falling through the air, with a 3-1/2" floppy disk for the hamburger patty. Jack painted the image using an early version of the Graphicraft art software for the Commodore Amiga, one of the first works of art created on the machine. At the time, Graphicraft lacked any way to save an image to disk or export it to print, so Jack photographed the screen with a 35mm film camera. Presumably, Jack eventually turned off the machine, and the digital record of the work was lost forever. The photograph appeared in the Graphicraft manual and the first issue of Amiga World magazine.

In 2023, YouTuber Stuart Brown aka Ahoy recreated “Four-Byte Burger” as digital art. Ahoy described his research and his process in a 30-minute video, including the realization that Jack must have drawn the image sideways then rotated the photograph to give it more height, as shown by the CRT scan lines running up and down the photo. Ahoy concluded that Jack used just fewer than 32 colors, and the original image was 320x200 in size.

Ahoy published a PNG file of his recreation, and by popular demand also produced an IFF-ILBM file of the same image for potential display on an Amiga. Naturally, I wanted to display it on my MEGA65, so I loaded it onto a D81 disk image and wrote an appropriate LOADIFF program, with a screen 320x200x5.

I was a bit surprised that it rendered as mustard and ketchup colored garbage instead of the actual image. So I peeked into the hex dump as we did above. Everything looked as I expected it to, until I got to the compression type field of the BMHD chunk, which was $00 instead of $01. According to the ILBM spec, this indicates that the BODY data uses no compression at all, and not Run-Length Encoding. So I looked at the ROM code for LOADIFF, and sure enough, LOADIFF always assumes the BODY region uses RLE, and ignores the compression type field. (That should be another easy fix in a future version of the ROM.)

There’s an easy workaround: use ilbmtoppm to convert the IFF-ILBM file to PPM, then use ppmtoilbm to convert it right back.

ilbmtoppm four-byte-burger.iff >four-byte-burger.ppm
ppmtoilbm four-byte-burger.ppm >four-byte-burger-fixed.iff

With “Four-Byte Burger,” the resulting file was naturally smaller, and had the compression field set to $01. It loaded fine on the MEGA65. If you have an IFF image file of a compatible size and color depth and it still isn’t loading correctly with LOADIFF, this is an easy workaround to try.

One more tip: If you find an IFF-ILBM file and want to know its dimensions and color depth, use the magick identify command:

magick identify four-byte-burger.iff

IFF MegaShow65

IFF MegaShow 65, by Nobato
IFF MegaShow 65, by Nobato.

Want to display a set of IFF-ILBM images as a slideshow? Check out IFF MegaShow65 by Nobato. This clever program can browse and display multiple IFF image files on a disk. It uses BASIC 65’s graphics system and LOADIFF command behind the scenes, reading the BMHD section of the file to determine the resolution and color depth.


The BASIC graphics system originated at Commodore, and retains a vintage feel. Even though it doesn’t use the MEGA65’s capabilities to their fullest, it’s a ton of fun to play with and use from programs, much easier than using bitmap graphics on a Commodore 64.

Screenshot of text from Escape from Onion Cake, by MrZaadii.

Dirty old Onion Cake has kidnapped you. You know that it is your own fault since you have not written enough MEGA65 software. Better get started!

— Dan