Colors, Palette manipulation

The default colors as set by the BIOS after the video mode initalization is this:

The program that draws this picture:

program showpal;

uses gfx256;

var i,j : integer;
begin
 graph320x200;
 for i:=0 to 15 do
  for j:=0 to 15 do
   bar(j*20,i*12,19,11,j+i*16);
 readln;
 text80x25;
end.

The VGA system has a palette for the 256 color modes. This palette stores the color information in the RGB (Red-Green-Blue) format. This means that the palette register contains 3 (1-1-1) components for each base color. You can set the intensity of a base color normally between 00..63 (64 steps), and these define the final color based on the additive color mixing method. Sometimes it's called trichromatic color mixing. Since each component has 6 bits (00..63), the number of the possible colors is 64*64*64 = 262 144. (Note: Modern videocards have 8 bits for each component, but they still have to be compatible with the original VGA standard.)

The videocard has three I/O ports for the palette manipulation:

   Port     Function                     
   $03C7    Palette component read index
   $03C8    Palette component write index
   $03C9    Palette component data
Register $03C7 and $03C8 points to the palette entry you want to read or write. When you write a new value into these registers, an internal pointer moves to the right place. Then you can use the $03C9 data register for reading or writing the selected palette entry. The data can be set or get by three sequential write or read on the port $03C9. For instance a possible solution in pascal (using the built-in port array):

  { write }
  port[$3c8]:=index;
  port[$3c9]:=red;
  port[$3c9]:=green;
  port[$3c9]:=blue;
 
  { read }
  port[$3c7]:=index;
  red:=port[$3c9];
  green:=port[$3c9];
  blue:=port[$3c9];

Now the same proceduers in assembly:

procedure SetRGB(reg,R,G,B:byte);                            assembler;
asm
   mov  dx,03c8h       { RGB write register        }
   mov  al,reg
   out  dx,al          { Set the palette index     }
   inc  dx             { $03c9 RGB data register   }
   mov  al,R           { Red component             }
   out  dx,al
   mov  al,G           { Green component           }
   out  dx,al
   mov  al,B           { Blue component            }
   out  dx,al
end;

procedure GetRGB(reg:byte; var RGB);                         assembler;
asm
   cld
   les  di,RGB
   mov  dx,03c7h       { RGB read register       }
   mov  al,reg
   out  dx,al          { Set the palette index   }
   add  dl,2           { $03c9 RGB data register }
   in   al,dx
   stosb               { Red   }
   in   al,dx
   stosb               { Green }
   in   al,dx
   stosb               { Blue  }
end;

Note that in the second case we pass the parameter RGB by reference and fill up this one (implicitly this is a byte array) with the color components.

Test

This small program sets the last 192 elements of the palette to the base components, while leaves the first 64 entries as they were set by the BIOS.

program showpal;

uses gfx256;

var
   i,j : integer;
begin
 graph320x200;

 for i:=0 to 15 do   { show the orginial palette }
  for j:=0 to 15 do
   bar(j*20,i*12,19,11,j+i*16);

 readln;

 for i:=0 to 63 do   { change the palette        }
  begin
   SetRGB( 64+i,i,0,0);
   SetRGB(128+i,0,i,0);
   SetRGB(192+i,0,0,i);
  end;

 readln;
 text80x25;
end.
The result as we see on the screen:

Downloads: [ Gfx256 unit | Show Palette | Set Palette ]

Back