Hi there. In this new series of articles, I'll be writing about color palettes of VGA. Since this is a relatively deep topic, I planned to cover it over several posts. In this post, I'll first focus on VGA 640 x 480 (aka screen 12) and text mode to explain what the color palette is, how to access and change it. I'll mention mode 13h in a separate post later and discuss some visual effects, based on VGA palette.
VGA introduced two important graphics modes. First one is mode 12h with 640 x 480 resolution and 16 colors, and the other one is well-known mode 13h, with 320 x 200 resolution and 256 colors. Other modes were retained for backwards compatibility with older graphics hardware. The VGA card's digital-analog converter (DAC) can display colors from 18-bit RGB color gamut (218 = 256 K = 262 144 colors) on the screen [1], but under mode 13h, only 256 of these at the same time, and under mode 12h only 16. This subset of colors from this color gamut is called a palette.
Color information consists of a palette index and an 18-bit RGB color value (6+6+6). For example, if the index is 1 and the RGB code is 0,0,63, the first color is blue; if the index is 2 and the RGB code is 24,24,24, the second color is gray, and so on. The index value starts at zero, and the zeroth color is the background color, which is therefore usually black (RGB 0,0,0). If the indices from 0 to 255 are filled with linearly increasing t values in RGB t,t,t form, a grayscale palette is obtained. Or if they are filled in the form RGB 0,r,0, a purely green toned palette is obtained.
Colors are processed through the DAC of graphics card. Therefore, palette operations are carried out using three* DAC registers of VGA card [3]:
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| DAC Read Address | |||||||
Actually, 3C7h has two functions. If this register is read, the two least significant bits indicate, whether the DAC is in read or write mode. However, if I've already executed an out instruction, I've written, or if I've executed an in instruction, I've read. This is why, this function of the register isn't used any often. To read the palette, the index value is written to this register. Then, the DAC data register on port 3C9h is read three times each in byte-length:
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| DAC Data | |||||||
When writing to the palette, the color index is written to port 3C8h, and three byte-length values are sent to the DAC data register.
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| DAC Write Address | |||||||
*There are actually four DAC registers. The DAC Mask register, which I did not mention above, is accessed via port 3C6h and always contains 0FFh value. Writing any other value to this register disables access to the DAC [2].
The aforementioned byte-length read and write operations refer to in al,dx and out dx,al instructions. However, only the lowest 6 bits of the read and written values are significant. Remember that the color gamut is 6+6+6=18 bits.
Let's focus on the text mode and mode 12h, first. In these two modes, a maximum of 16 colors can be displayed on the screen. In this respect, they are similar. One detail, which goes often unnoticed is that the text mode palette can also be modified. In my first VGA post [5], I explained, how to change text and background colors of a text directly. Now, let's take a look at the text mode palette using a simple BASIC code:
OUT &H3C7, I%
PRINT "("; I%; "="; INP(&H3C9); INP(&H3C9); INP(&H3C9); ")";
NEXT I%
DEF SEG = &HB800
FOR J% = 1 TO 15
FOR I% = 1 TO 159 STEP 2
POKE (I% + J% * 160), J%
NEXT I%
NEXT J%
In first part, I send the color index value from 3C7h, then read the color codes from 3C9h, and printed them to the screen. In second part, I accessed the text mode video memory and colored the first line with the first palette color, the second line with the second palette color, and so on:
I mentioned that 16 colors can be used in text mode, but I printed 64 color codes to the screen, and interestingly, it appears that non-zero codes have been assigned to the indices between [16, 63]. The codes for indices between [64, 255] are zero, therefore not printed, but colors can be assigned to them as well if needed. So, what's the meaning of this? Normally, a character on the screen consists of 2 bytes: one byte is its ASCII code, and the next one holds its color information. The lower 4 bits of the color byte represent the character's color, while the upper 4 bits represent the character's background color. Since just 4 bits are allocated for colors, assigning a color code to the indices 16 and above might seem pointless at first glance, but there is a trick: The VGA Attribute Register (3C0h) [4] can be used to change a color's palette index. Without getting into too much detail, here is a simple code snippet:
OUT &H3C0, 5
OUT &H3C0, 60
OUT &H3C0, &H20
where I assigned the 60th color to the fifth one, by writing the value 60, into the fifth attribute register.
Getting back to the screenshot, the eighth palette entry (0, 0, 21) should be navy blue or dark blueish, and the ninth palette entry (0, 0, 63) should be pure blue. However, assuming that we're counting from zero, the eighth row is actually dark grey instead of navy blue, and the ninth row, which supposed to be vivid blue, is just a pale blue (neon blue). If these color codes printed on the screen were represented the actual colors of the rows, the screen would actually look like the right side of the image below. The left side shows, what actually visible is. For an easy comparison, I've put two images side by side:
The conclusion is, that the text mode is actually using attribute registers for the colors [8, 15]. To render the left side of the above image, I manually assigned the first fifteen colors to the first fifteen palette indices manually.
Everything about text mode palette also applies to mode 12h palette. Even though 16 colors can be shown at any time, the default palette contains 64 colors. Hint: If you switch from mode 13h to mode 12h or to text mode, mode 13h palette will also stay in DAC for other modes. In other words, when the computer (or DosBox) starts up, all colors in the range [64, 255] are set to (0, 0, 0). If you just enter mode 13h and switch back to text mode (or mode 12h), these indices won't be containing zeros anymore.
Similar to text mode, the colors on the screen are selected from the palette, but the attribute register provides a second conversion layer. Here is another BASIC code snippet to demonstrate all these:
CONST K = 20
FOR I% = 0 TO 63
OUT &H3C7, I%
PRINT "("; I%; "="; INP(&H3C9); INP(&H3C9); INP(&H3C9); ")";
NEXT I%
SLEEP
FOR I% = 0 TO 15
' PRINTING RECTANGLES
LINE (0, I% * K)-(640, (I% + 1) * K), I%, BF
NEXT I%
FOR I% = 0 TO 15
' SETTING PALETTE
OUT &H3C8, I%
OUT &H3C9, I% * 4
OUT &H3C9, I% * 0
OUT &H3C9, 63 - I% * 4
NEXT I%
SLEEP
' Change fifth color
A% = INP(&H3DA)
OUT &H3C0, 5
OUT &H3C0, 60
OUT &H3C0, &H20
In the first part, color codes of indices [0, 63] are printed to the screen. In the next part, 16 rectangles, whose size set by the const K, are drawn on the screen using first 16 colors and then 16 colors ranging from blue to red are assigned to the palette. As demonstrated, colors are written to the palette by writing their palette index to the port 3C8h and sending their color codes via port 3C9h afterwards.
Since blue is assigned to the zeroth color here, the background becomes blue. In the final part, the 60th palette entry is assigned to the fifth one, clearly highlighting the color difference.
[1]: https://en.wikipedia.org/wiki/Video_Graphics_Array
[2]: https://wiki.osdev.org/VGA_Hardware#Port_0x3C6
[3]: http://www.osdever.net/FreeVGA/vga/colorreg.htm
[4]: http://www.osdever.net/FreeVGA/vga/attrreg.htm
[5]: https://trapgate.blogspot.com/2025/11/programming-vga-smooth-scrolling-in.html





