Hold-And-Modify Graphics Mode

-

After the recent MODE rearrangement, MODE6 was left unused, and I was looking for something to fill its place. Amiga’s HAM (Hold-And-Modify) mode seemed like a great candidate. Implementing HAM on an 8-bit machine looked like a challenge, so I gave it a shot.

There are major differences between Amiga and X65 graphics capabilities. The Amiga uses 12-bit RGB graphics with 4 bits per channel, yielding 4096 possible colors from a programmable palette. In contrast, the X65 generates a full 24-bit RGB8 DVI data stream with a fixed 256-color palette. We don’t have enough plane registers to store 16 base HAM colors (as the Amiga does), so we had to settle for just 8 base HAM colors.

This results in 8 base colors pointing to one of the 256 palette entries. Three bits are used to encode a base color. HAM mode requires two additional bits to encode commands:

Three bits for the base color plus two bits for commands make five bits per pixel. This would work well in the Amiga’s bitplane paradigm, where pixel entries can have any number of bits. However, the X65 doesn’t use planar graphics, so encoding five bits per pixel in its memory layout would be cumbersome and likely inefficient.

So, what if we used a full 8 bits per pixel? At a full-screen resolution of 384x240 pixels, that would require 92,160 bytes - or 90 kB. With a memory bank of only 64 kB, this is unworkable.

Next, let’s see how much memory a 6-bit-per-pixel screen would need. At 384x240 pixels, it would require 552,960 bits, or 69,120 bytes - around 67.5 kB. Still too much. 😞

But… Let’s check 320x240 resolution, which was standard for graphics of the era. We can stand 4 columns of border. So… a 320x240 screen at 6 bits per pixel requires 460,800 bits, or 57,600 bytes - around 56.25 kB. That fits within a 64 kB memory bank! 🎉

Let’s go for 6 bit per pixel. Now we need to efficiently encode 6 bits per pixel in 8 bit bytes. Fortunately if we look at factor pairs for 6 and 8, we see that 4x6 = 3x8 = 24, so we can neatly pack 4 6-bits pixels in 3 8-bit bytes. So we have our nice bitpacked encoding.

Now, back to what the bits mean. Three bits index a base color, and the remaining three bits encode a command:

Then I realized: If I shift the command bits one position to the left, I could use 4 bits for a (signed) delta instead of just 3.

This gives us:

That leaves the 001xxx command unused. While researching ideas for HAM, I came across a cool concept: a BLEND command. Instead of setting the current raster color to a palette entry, the BLEND command combines the current color with xxx palette entry at a 50%/50% ratio. So, I implemented that! 💪

One more difference to Amiga’s HAM needed to be addressed. The Amiga uses 16 color levels per RGB channel, so a 6-bit (signed) delta results in a significant change. In contrast, we use 8 bits, allowing for 256 levels of channel brightness. This makes a 4-bit delta 16 times less significant. To compensate, the delta is applied with greater quanta.

After experimenting, I found that a quantum value of 1/32 works really well. It produces even smoother gradients than the Amiga’s 1/16, with bearable color fringing - especially when combined with the BLEND command.

I have to admit, I was skeptical about implementing HAM. I worried it might end up feeling too much like the Amiga, losing its 8-bit charm. But with the unique constraints of the X65, it still looks distinct. And oh boy, it looks amazing!

MODE6 HAM

This image was converted from the original 24bit PNG using converter.ts, which I extended to generate HAM pictures.