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.