Monday, July 20, 2020

A nicer 4 bit colour without fancy logic

In the last few years there has been quite a lot of activity in homebrew Retro-computing. I really liked seeing things like the Craft Demo, the UzeBox, The Gigatron and Ben Eater's amazing work in making all of this stuff accessible to mere mortals. There are a lot of toy projects out there generating video signals, sometimes composite, sometimes VGA occasionally something more exotic. This has inspired me to address something that has bugged me for a long long time in the hope that people might want do do some 4 bit digital output to generate a 16 colour palette that isn't completely hideous.

Here's the problem

This is the original CGA palette. Known as RGBI, four digital lines for Red, Green, Blue, and Intensity. it looks like this
Black Red Green Dry Snot
Blue Sad Magenta Sad Cyan Light Grey
Dark Grey Light Red Light Green Yellow
Liht Blue Magenta Cyan White
The Beauty that is RGBI.
This arrangement of colours is somewhat unappealing. To mitigate the worst of this a special case was introduced on the low intensity yellow colour where they halved the green brightness for that one colour. This was a change to the monitors, not the video card, which strictly speaking had no concept of colour and merely output signals on 4 digital wires..
Black Red Green Brown
Blue Sad Magenta Sad Cyan Light Grey
Dark Grey Light Red Light Green Yellow
Liht Blue Magenta Cyan White
Much better, right, let's go to the pub.
This addition of brown helped a great deal, especially if you wanted wood

People managed to do quite a bit with that set but it wasn't an ideal arrangement. When I look at homebrew video generators I can't help but think that there is a tendency to go with six or eight digital lines because four digital lines brings memory of this palette. I wanted to know how much better you could make four digital lines without adding too much complexity.

How did RGBI work anyway

For starters lets look at an RGBI output mechanism.

This circuit is designed to convert four digital 5V lines coming in at the bottom to three analog signals for Red, Green and Blue. This particular one places the Analog signals into the 0.0V - 0.7V range of a 75 Ohm VGA input. The intensity line on the right contributes to each of the red green and blue lines evenly to make all levels brighter. The diodes on the right prevent the original Red, Green, and Blue signals from crossing over into the other channels. Those diodes are not strictly necessary though, we are aiming for a visual representation, not mathematical accuracy.

Let's do the dumbed down version

This arrangement eliminates the diodes. This means that when the Red line is on a small amount of current can travel back through the intensity resistors and onto the Blue and Green lines. Running the numbers shows it's not really a problem though because the voltage is already dropped fairly low and has to go through another two resistors to get out one of the other lanes. When the any one the RGB lines provides 0.3V a sneaky 0.01V slips through to the other two. When two of the RGB lines are high the remaining one receives an extra 0.02V which isn't noticeable.

Now mix things up

To come up with a better arrangement, the plan was to introduce some asymmetry in the resistors to see what colours would come out. I still wanted all lines High to provide white and all lines Low to be Black. The rule of thumb is that if I drop the resistance on one of the colour lines I should correspondingly increase the resistance on the intensity line that contributes to that colour.

In this sense you would represent the traditional RGBI as

Output line Color Resistor Intensity Resistor
Red High Low
Green High Low
Blue High Low

These are not merely boolean options, a range of medium values are available. You have a restriction that no output combination should produce a voltage higher than 0.7V on any line. Additionally if you want to have a white, you need to have a set of resisters that provide 0.7V on all three RGB lines when all of the input lines are high. Initially i tried an arrangement something like this.

Output line Color Resistor Intensity Resistor
Red Low High
Green Medium Medium
Blue High Low

A hint of lane crossing

I did some quick calculations on the colors and, while better, I felt that the problem areas of the original RGBI system remained. A good brown needs more Red than Green So I tried shunting a little green over to red. After more tweaking and fiddling I decided to do the same with blue shunting to green and came up with

What should that look like?

In theory the voltages produced by this arrangement of resistors should produce a palette something like this.

Black Red Green Brown
Dark Blue Purple Sea Green Pale Brown
Steel Blue Blush Leaf Green Yellow
Denim Blue Mauve Cyan White
A theoretically better palette.

To me, that looks like a better set of colors. We now have two tones of brown. The yellow is less intense, and stab-your-eyes magenta has been mellowed to a mauve. Sad magenta has been shifted over slightly to be a purple. Sad cyan is now greener for a nice sea green. The pure greys are gone and replaced with pale colours. I'm rather happy with it.

What does it actually look like?

Of course this doesn't mean anything unless i can actually get it working. I wrote a small Arduino program to throw out some VGA 640x480 timings and a simple test pattern and put the resistor arrangement on a piece of breadboard. I'm not well equipped when it comes to actually making things. So my creation was a bit of a Frankenstein's monster of what resisters I had lying about wired in combinations to make values close to what I needed.

In the end I got the Arduino outputting a signal that resembled VGA enough for a monitor to display it.

I was actually pretty chuffed to get colours that close to what I had calculated. The stripes on the side a a series of 1,2,3 and 4 clock cycle bands. Because the monitor is expecting 640x480 the digital nature of the LCD is quantizing to make the thinner bars uneven. I'm a little torn between the simplicity of getting picture out of a plain Arduino or going for a 25.175MHz clocked chip and seeing what I can get it to do.

Today's objective is however complete. Giving a better set of 4 bit colours hopefully will be of use to someone and inspire them to make some cool things with hardware that was never meant for that purpose. At two pixels per byte there is less data to move around so potentially allowing for new things. I already have a few ideas. A 74LS157 would fairly easily handle turning a byte into two pixels. leaving more time for fetching the pixels in exotic manners. An Arduino would probably use a 74LS158 and a 74LS04 because of the creatively inverted outputs on PORTD.

I'm not exactly Ben Eater level.