Part #2 of the 8088 MPH writeup series (continued from CGA in 1024 Colors - a New Mode: the Illustrated Guide)
At long last, the final version of 8088 MPH is out. There's a very nice rundown of the fixes and changes in Scali's blog post; much of the visible (i.e., graphical) portion of those changes involved making the demo compatible with all IBM CGA cards.
The party version targeted the earlier, pre-1983 revision of the IBM CGA card (or as we've come to call it, "old" CGA). This was partly because we had better data for this model: reenigne owns such a card, and he had been working on some of these tweaks long before we decided to make a demo about it. Also, when we did our first tests on a "new" CGA card - the post-1983 revision - we discovered that our "secret sauce" extra-color modes weren't yielding very predictable/useful color palettes. Fortunately, this was largely fixed by fine-tuning several CRTC values; this can now be done from the new calibration screen, but more on that later.
Still, ensuring proper colors on both CGA types meant that most of the graphics had to be redone for new-style CGA. The 1024-color palettes proved to be extra sensitive to the old vs. new difference - hopefully this image shows why:
The root of all this trouble lies in how the 16 'direct' colors are generated on the CGA's composite output stage. With an old-style CGA, those colors that carry chrominance information (i.e. all except the grey shades) differ only in phase. With the revised new-style design, IBM added some extra circuitry which feeds the (weighted) R, G, B, and I signals into the mix. This allows for 16 different levels of grey in B&W mode, which is probably why they did it.
To get our 1024 colors, as shown in my previous post, we take bits and pieces of these direct color waveforms and rearrange them into new composite signals of the right frequency. And since each CGA type gives us different "building blocks", the resulting palettes end up very different indeed:
Since I had hand-pixeled those 1K pics using the old-CGA palette (instead of converting them from a higher color depth), the artwork was basically tailor-made for that colorset, and adjusting it for the new one wasn't exactly a trivial step. You might think you could get away with simple nearest-color matching - having 1024 colors gives us enough rope, right? Wrong: I tried that, and the results ranged from "meh" to "vomitous", which would've required lots of manual editing anyway.
In the end, there was no escape other than recoloring things by hand. To get a consistent set of colors for drawing these images (and for converting them to our tweaked CGA format), I created my "work palettes" by splitting them into groups of similar hues and sorting those groups by luma. These palettes tended to change every time we fine-tuned our contrast / brightness / saturation settings, but the ones you see below are representative of what I used while drawing. The pixel artists among you may find it interesting to observe the strengths and weaknesses of both palettes:
As far as the computer can tell, old- and new-style CGA are exactly the same: there's no way to detect the specific revision in software. To enjoy our demo as intended, however, you'll need to know what you've got. Scali's aforementioned blog post shows a comparison photo which outlines the differences on the cards themselves - once you actually look at one, it's easy to spot. But what if you can't be arsed to open up your machine?
Back when we were first toying with those advanced CGA tweaks, I posted a little survey on the Vintage Computer Forums, intended to give us an idea of CGA model distribution "in the wild". To make people's lives easier (as easy as possible when you're badgering them with oddly-specific hardware questions, anyway), I put together an image which can be used to visually determine the CGA card type: it uses text (in plain graphics mode 4), and relies on certain foreground/background patterns being more or less readable, depending on the model. This can be seen/downloaded at the linked post, but it didn't turn out to be as reliable as I'd hoped.
When we had everything ready for the final version - complete with CGA model selection - a better idea occurred to me. I had been thinking about the color setup screen from BurgerTime, which is cleverly designed to show you one thing on RGBI, and another on composite:
Interestingly, we can tell that this screen was composed for old-style CGA - and not just because BurgerTime was a 1982 release: on new-style CGA, the composite display would show a dark brown "R" and beer mug, alongside the "C". My idea was to take that concept and go a little crazier: could we create a screen showing two completely different composite images, depending on the CGA model?
I wanted to use one of our 'extra-color' modes for this; however, they're based on 80-column text mode, and that wouldn't be ideal (there's that nasty CGA hardware bug which truncates the color burst in 80c mode - and that's what the calibration screen is designed to fix in the first place). For a reliable extra-color mode that doesn't require manual calibration, we're pretty much stuck with 40 columns. Now, our extra-color trickery wouldn't work with 40 columns: characters are twice as wide, and none of the 'magic' pixel patterns fit into a single color-cycle period, so they won't get us the solid colors we want.
Fortunately, the CGA framebuffer has enough room for 200 lines of 40 columns each - and using reenigne's CRTC-bending magic, it can actually be forced to display that many lines in text mode! Thus we can get away with considering just one scanline of each ASCII character, rather than two, and indeed one character comes through for us: 0xB1, which has a top scanline of '01010101'. This is effectively the same as using character 0x55 in 80-column mode ('11001100' in half-width pixels), except that the foreground and background colors are flipped; so we have the same 256 colors which that character allows us.
To get the best possible candidate colors for this little trick, I actually did some number-crunching on the RGB values that they map to: for the set of all possible color pairs (256^2), I calculated the distances between them on both old and new CGA, then figured out which pairs had the greatest difference between the old- and new-CGA color distance. If the finished graphic looks distinctly Atari 2600-like, that's because we only have 40 solid color blocks per line, but I think it works well:
The exact same image says either "old" or "new" (complete with silly icons), depending on your CGA model - although if you look closely at each result, a 'ghost' of the other version is actually visible. The rest of the image was just an excuse to throw in some more "ANSI from Hell", using the entire ASCII range without really going for artifact colors; in 80-column mode it'd look like a smeary illegible mess over composite, but 40-column mode makes things much more manageable. And yes, I'm fully aware that the chip count on the CGA is wrong. ;-)
Just to illustrate the technique, here's what the 16-color RGBI image looks like:
The calibration screen uses 80-column text mode (since that's what we're supposed to calibrate), and takes advantage of the 512-color tweak to display those two color ramps. We also wanted to have some text for the instructions, though; and since we can only use the top two scanlines of each ASCII character, those will have to serve as our building blocks. That in itself is hardly a new idea; I've already mentioned Macrocom, who did it in 1984, and I'd done it myself while messing around with the "ANSI from hell" technique. However, here we needed a character set that's nice and legible - and stays that way even at 80 columns on a composite display (which is where the problems usually begin).
After much trial and error (and way too much fussing over tiny details, even for me - I think I revamped and revised this at least five times) something emerged that worked quite nicely, so here's how it works for reference:
The "graffiti" sequence (@2:10 in the final video) obviously starts with the immediately-recognizable, black/cyan/magenta/white palette ("CGA has 4 ugly colors"), then switches to the full 16-color RGBI palette ("with hacks, 16 at best"), before revealing the full "1K colors" image. What's less obvious is that we're not really using those familiar 'direct' colors, even though they're naturally available in our expanded palette.
That's because the 16 direct colors on a composite display don't look that close to their RGBI counterparts - and when most people think "CGA", they think of the latter. Actually, our 1024-color palette has a few artifact colors that look much closer to our recognizable RGBI 16. These are the old-CGA colors; different ones are used for new CGA, but the idea's the same:
Another little bit of fun - which I hoped people would pick up on - was faking the Commodore 64 palette, for the "...until now!" graphic that pops up just before the 1K picture. I wonder if any of the C64 guys in attendance actually noticed ;-) Perhaps it's more obvious when you look at some actual C64 artwork converted for 1K CGA:
And speaking of the C64: the VIC-II palette has several things going that make it very useful for pixel artists - especially how all 16 colors can be arranged to form a nice and smooth brightness gradient all the way from black to white. For our 16-color sections, using "normal" CGA modes 4 and 6, I was curious to see if any of the possible palettes shared that property.
As it turned out, they don't: in the 'classic' mode 6 palette, brightness is determined by how many bits of each nybble are set, since that specifies how many hi-res pixels are lit within a single color cycle. For black and white we get 0 and 4 respectively, but for all other colors (1, 2, or 3 bits set) we're stuck with only three discrete brightness levels. Mode 4 is somewhat more complex, since both the underlying 'direct' colors and their positions along the horizontal pixel grid play a part; still, none of the useful palettes are as well-behaved as the C64 one. In other words, we have another item on the "PC SUXX!" list.
One place where we really wanted such a gradient was the 3D polygon part, for shading the surfaces. Thankfully, Scali was able to add one very useful feature to his code: mapping light levels to both solid colors *and* dither patterns. This single-handedly gave us all the flexibility we needed; when you sneak dither patterns in-between the solid colors, all sorts of nice gradients become possible:
Again, these are old-CGA colors, but new-CGA is equally capable - the gradients are just somewhat different. We only ended up using two of these for the polygons, but Scali's flexible light mapping gave much better results than I had expected. Moral of the story: when your code can harness the machine's strengths, you can often get away with pretending that its weaknesses don't exist, and even the good old IBM PC ends up sucking a lot less vigorously.