Monday, February 20, 2017

Why DOS has sixteen colors

Over at my blog about the usability of open source software, I recently wrote about the readability of DOS applications. This was a followup to an earlier article about how websites are becoming unreadable. I also provided a simple Bash script to compute the contrast ratios of text.

In writing about the readability of DOS applications, I wanted to expand on the color palette for DOS text, but it didn't seem to fit the article. I thought I'd write about it here.

Do you wonder why DOS only supports sixteen colors for text? With DOS, you had a color palette of sixteen colors, enumerated 0 (black) to 15 (white). Backgrounds were limited to the first eight colors:
BackgroundForeground
  1. Black
  2. Blue
  3. Green
  4. Cyan
  5. Red
  6. Magenta
  7. Brown
  8. White ("Light Gray")
  1. Black
  2. Blue
  3. Green
  4. Cyan
  5. Red
  6. Magenta
  7. Brown
  8. White ("Light Gray")
  9. Bright Black ("Gray")
  10. Bright Blue
  11. Bright Green
  12. Bright Cyan
  13. Bright Red
  14. Bright Magenta
  15. Yellow
  16. Bright White
The origins go back to CGA, the Color/Graphics Adapter from the earlier PC-compatible computers. This was a step up from the plain monochrome displays; as the name implies, monochrome could only display black or white. CGA could display a limited range of colors.

CGA supports mixing red (R), green (G) and blue (B) colors. In its simplest form, RGB is either "on" or "off." In this case, you can mix the RGB colors in 2×2×2=8 ways. Let me represent that in a simple table showing the binary and decimal representations of RGB:
000(0)Black
001(1)Blue
010(2)Green
011(3)Cyan
100(4)Red
101(5)Magenta
110(6)Yellow
111(7)White
To double the number of colors, CGA added an extra bit called the "intensifier" bit. With the intensifier bit set, the red, green and blue colors would be set to their maximum values. Without the intensifier bit, each RGB value would be set to a "midrange" intensity. Let's represent that intensifier bit as an extra 1 or 0 in the binary color representation, as iRGB:
0000(0)Black
0001(1)Blue
0010(2)Green
0011(3)Cyan
0100(4)Red
0101(5)Magenta
0110(6)Yellow
0111(7)White
1000(8)Bright Black
1001(9)Bright Blue
1010(10)Bright Green
1011(11)Bright Cyan
1100(12)Bright Red
1101(13)Bright Magenta
1110(14)Bright Yellow
1111(15)Bright White
But there's a problem: 0000 Black and 1000 Black are the same color. There's no red, green or blue color to intensify, so black is black whether or not the "intensifier" bit is set. To get around this limitation, CGA actually implemented a modified iRGB definition, using two intermediate values, at about one-third and two-thirds intensity. Most "normal" mode (0–7) colors used values at the two-thirds intensity, with the exception of yellow which was assigned a one-third green value that turned the color brown. Translating from "normal" mode to "bright" mode, convert zero values to the one-third intensity, and two-thirds values to full intensity.

Here's another iteration of the color table, using 0x0 to 0xF for the color range, with 0x5 and 0xA as the one-third and two-thirds intensities, respectively:
0000(#000)Black
0001(#00A)Blue
0010(#0A0)Green
0011(#0AA)Cyan
0100(#A00)Red
0101(#A0A)Magenta
0110(#A50)Brown
0111(#AAA)White
1000(#555)Bright Black
1001(#55F)Bright Blue
1010(#5F5)Bright Green
1011(#5FF)Bright Cyan
1100(#F55)Bright Red
1101(#F5F)Bright Magenta
1110(#FF5)Bright Yellow
1111(#FFF)Bright White
And that's how DOS got sixteen text colors! That's also why you'll often see "brown" labeled "yellow" in some references, because it started out as plain "yellow" before the intensifier bit. Similarly, you may also see "gray" represented as "Bright Black," because "gray" is really "black" with the intensifier bit set.

You may wonder why only eight background colors? Note that DOS also supported a "Blink" attribute. With this attribute set, your text could blink on and off. The "Blink" bit was encoded at the end of the foreground and background bit-pattern:
Bbbbffff
That's a full byte! Counting from right to left: four bits to represent the text foreground color (0000 Black to 1111 Bright White), three bits to code the background color (000 Black to 111 White) and one bit for the "Blink" attribute.

2 comments:

  1. Is the blink attribute part Of The hardware?

    Also, interesting you could set up vga so that the intensifier bit made it use an alternate don't.

    ReplyDelete
  2. Thanks! I'm glad you enjoyed the article.

    This is actually CGA, which is much older than VGA. First you had CGA (1981) then EGA (1984) then VGA (1987). After that, you technically had XGA, which was an IBM standard - but really SuperVGA displaced that. SVGA was an industry defined superset to VGA that includes the "VESA" high resolution modes.

    Yes, the Blink attribute was defined at the hardware level as part of CGA.

    ReplyDelete