Why FreeDOS has 16 colors

Why does text only come in this limited palette, and why does FreeDOS use those colors and shades, instead of some other colors? The answer, like many things in technology, is because of history.
56 readers like this.
Compute like it's 1989

LSE Library. Modified by Opensource.com. CC BY-SA 4.0

If you've looked carefully at FreeDOS, you've probably noticed that text only comes in a limited range of colors—sixteen text colors, and eight background colors. This is similar to how Linux displays text color—you might be able to change what text colors are used in a Linux terminal, but you're still stuck with just sixteen text colors and eight background colors.

terminal colors

DOS text comes in 16 colors and 8 background colors

(Jim Hall, CC-BY SA 4.0)

Why does text only come in this limited palette, and why does FreeDOS use those colors and shades, instead of some other colors? The answer, like many things in technology, is because of history.

The origins of PC color

To explain why text only comes in sixteen colors, let me tell you a story about the first IBM Personal Computer. Parts of this story may be somewhat apocryphal, but the basics are close enough.

IBM released the Personal Computer 5150 (the "IBM PC") in 1981. The PC used a simple monitor screen that displayed text in green. Because this display only worked with one color, it was dubbed monochrome (the "IBM 5151 monochrome display," with the IBM Monochrome Display Adapter card, or "MDA").

That same year, IBM released an updated version of the PC that sported an amazing technical achievement—color! The new IBM 5153 color display relied on a new IBM Color Graphics Adapter, or "CGA." And it is because of this original CGA that all DOS text inherited their colors.

But before we go there, we first need to understand something about color. When we talk about colors on a computer screen, we're talking about mixing different values of the three primary light colors—red, green, and blue. You can mix together different levels (or "brightnesses") of red, green, and blue light to create almost any color. Mix just red and blue light, and you get magenta. Mix blue and green, and you get cyan or aqua. Mix all colors equally, and you get white. Without any light colors, you see black (an absence of color).

light colors

Mix red, green, and blue light to get different colors

(Jim Hall, CC-BY SA 4.0)

The IBM 5153 color display presented color to the user by lighting up tiny red, green, and blue phosphor dots on a cathode ray tube (a "CRT"). These tiny dots were arranged very close together and in a pattern where a triad of red, green, and blue dots would form a "pixel." By controlling which phosphor dots were lit at one time, the IBM 5153 color display could show different colored pixels.

CRT pixels

Each red, green, and blue triad is a single pixel

(Jim Hall, CC-BY SA 4.0)

By the way, even modern displays use this combination of red, green, and blue dots to represent colors. The difference in modern computers is that instead of tiny phosphor dots, each pixel uses a triad of red, green, and blue LED lights—usually arranged side by side. The computer can turn each LED light on or off to mix the red, green, and blue colors in each pixel.

LCD pixels

Each red, green, and blue triad is a single pixel

(Jim Hall, CC-BY SA 4.0)

Defining CGA colors

The IBM engineers realized they could display several colors by mixing each red, green, and blue pixels. In the simplest case, you could assume each red, green, and blue dot in a single-pixel was either "on" or "off." And as any computer programmer will tell you, you can represent "on" and "off" as binary—ones (1=on) and zeroes (0=off).

Representing red, green, and blue with ones or zeroes means you can combine up to eight colors, from 000 (red, green, and blue are all off) to 111 (red, green, and blue are all on). Note that the bit pattern goes like "RGB," so RGB=001 is blue (only blue is on) and RGB=011 is cyan (both green and blue are on):

  000 Black
  001 Blue
  010 Green
  011 Cyan
  100 Red
  101 Magenta
  110 Yellow
  111 White

But that's just the simplest case. A particularly clever IBM engineer realized you could double the number of colors from eight to sixteen simply by adding another bit. So instead of a bit pattern like RGB, we can use a bit pattern like iRGB. We'll call this extra "i" bit the "intensity" bit because if we set the "intensity" bit to 1 (on), then we'll light up the red, green, and blue phosphor dots at full brightness; if the "intensity" bit is 0 (off) we can use some mid-level brightness.

And with that simple fix, now CGA could display sixteen colors! For the sake of simplicity, the IBM engineers referred to the high-intensity colors as the "bright" versions of the regular color names. So "red" pairs with "bright red," and "magenta" pairs with "bright magenta."

  0000 Black   1000 Bright Black
  0001 Blue   1001 Bright Blue
  0010 Green   1010 Bright Green
  0011 Cyan   1011 Bright Cyan
  0100 Red   1100 Bright Red
  0101 Magenta   1101 Bright Magenta
  0110 Yellow   1110 Bright Yellow
  0111 White   1111 Bright White

Oh no! But wait! This isn't actually sixteen colors. If you notice iRGB=0000 (black) and iRGB=1000 (bright black), they are both the same black. There's no color to make "bright," so they are just both regular black. This means we only have fifteen colors, not the sixteen we were hoping for.

But IBM has clever engineers working for them, and they realized how to fix this to get sixteen colors. Rather than implement a straight RGB to iRGB, IBM actually implemented a modified iRGB scheme. With this change, IBM set four levels of brightness for each phosphor dot: completely off, one-third brightness, two-thirds brightness, and full brightness. If the "intensity" bit was turned off, then each red, green, and blue phosphor dot would light up at two-thirds brightness. If you set the "intensity" bit on, any zeroes in the RGB colors would be lit at one-third brightness, and any ones in the RGB colors would be lit at full brightness.

Let me describe this to you another way, using web color code representation. If you are familiar with the HTML colorspace, you probably know that you can represent colors using #RGB, where RGB represents a combination of red, green, and blue values, each between the hexadecimal values 0 through F. So using IBM's modified iRGB definition, iRGB=0001 is #00a (blue) and iRGB=1001 is #55f (bright blue) because with high-intensity colors, all zeroes in RGB=001 are lit at one-third brightness (around "5" on the 0 to F scale) and all ones in RGB=001 are lit at two-third brightness (about "A" on the 0 to F scale).

  0000 Black   1000 Bright Black
  0001 Blue   1001 Bright Blue
  0010 Green   1010 Bright Green
  0011 Cyan   1011 Bright Cyan
  0100 Red   1100 Bright Red
  0101 Magenta   1101 Bright Magenta
  0110 Yellow   1110 Bright Yellow
  0111 White   1111 Bright White

And with those colors, we are finally done! We have a full spectrum of colors from iRGB=0000 (black) to iRGB=1111 (bright white) and every color in between. Like a rainbow of colors, this is beautiful.

Except, no. Wait. Something's wrong here. We can't actually replicate all of the colors of the rainbow yet. The handy mnemonic we learned in grade school was ROYGBIV, to help us remember that a rainbow has colors from red, orange, yellow, green, blue, indigo, and violet. Our modified iRGB color scheme includes red, yellow, green, and blue—and we can "fake" it for indigo and "violet." But we're missing orange. Oh no!

rainbow

A beautiful rainbow - which unfortunately contains orange 

(Paweł Fijałkowski, public domain)

To fix this, the smart IBM engineers made one final fix for RGB=110. The high-intensity color (iRGB=1110) lit up the red and green phosphor dots at full brightness to make yellow. But at the low-intensity color (iRGB=0110), they lit the red at two-thirds brightness and the green at one-third brightness. This turned iRGB=0110 into an orange color—although it was later dubbed "brown" because IBM had to mess up the standard names somewhere.

  0000 Black   1000 Bright Black
  0001 Blue   1001 Bright Blue
  0010 Green   1010 Bright Green
  0011 Cyan   1011 Bright Cyan
  0100 Red   1100 Bright Red
  0101 Magenta   1101 Bright Magenta
  0110 Brown   1110 Yellow
  0111 White   1111 Bright White

And that's how CGA—and by extension, DOS—got the sixteen colors! And in case you're curious, that's also why there's a "bright black" color, even though it's just a shade of gray.

Representing colors (bits and bytes)

But you may wonder: why can DOS only display eight background colors if it can display sixteen text colors? For that, we need to take a quick diversion into how computers passed color information to the CGA card.

In brief, the CGA card expected each character's text color and background color to be encoded in a single byte packet. That's eight bits. So where do the eight bits come from?

We just learned how iRGB (four bits) generates the sixteen colors. Text color uses iRGB, or four bits. The background color is limited to the eight low-intensity colors (RGB, or three bits). Together, that makes only seven bits. Where is the missing eighth bit?

The final bit was reserved for perhaps the DOS era's most important user interface element—blinking text. While the blinking text might be annoying today, throughout the early 1980s, blinking text was the friendly way to represent critical information such as error messages.

Adding this "blink" bit to the three background color bits (RGB) and the four text color bits (iRGB) makes eight bits or a byte! Computers like to count in full bytes, making this a convenient way to package color (and blink) information to the computer.

Thus, the full byte to represent color (and blink) was Bbbbffff, where ffff is the iRGB bit pattern for the text color (from 0 to 15), bbb is the RGB bit pattern for the low-intensity background color (from 0 to 7), and B is the "blink" bit.

The limit of sixteen text colors and eight background colors continues to this day. Certainly, DOS is stuck with this color palette, but even Linux terminal emulators like GNOME Terminal remain constrained to sixteen text colors and eight background colors. Sure, a Linux terminal might let you change the specific colors used, but you're still limited to sixteen text colors and eight background colors. And for that, you can thank DOS and the original IBM PC. You're welcome!

What to read next

How FreeDOS boots

An overview of how your computer boots up and starts a simple operating system like FreeDOS.

Tags
photo of Jim Hall
Jim Hall is an open source software advocate and developer, best known for usability testing in GNOME and as the founder + project coordinator of FreeDOS.

2 Comments

Great article. Some interesting additional bits.

IBM's original monochrome display adapter also uses an 8-bit attribute value per character. The blink bit still makes text blink. The least-significant foreground bit (CGA's blue bit) makes text underlined.

Most VGA adapters (unclear if this is from IBM or later add-ons) will let you configure the screen to support MDA-style underlining. If you do this without switching to a full MDA-style display (which IBM's cards never supported, but lots of third-party cards do), then blue text ends up underlined. Not terribly useful, but that's what happens.

Additionally, it is possible to tweak a register in the video card (definitely for VGA. I think also for EGA and CGA) to disable blinking, which lets you use all 16 colors for background.

Finally, it should be noted that orange and brown are really the same color. It's just a matter of brightness and viewing context as to how you perceive it. This video demonstrates the idea better than any text could: https://youtu.be/wh4aWZRtTwU

Thanks for the great comment, David! I'd completely forgotten about underlined text.

Creative Commons LicenseThis work is licensed under a Creative Commons Attribution-Share Alike 4.0 International License.