Programming tutorial: Part 6–Colors

Atari Lynx programming tutorial series:

In the previous parts we looked at a lot of things that are required to get started. We created an empty project and did some simple text graphics. Now we will have a look at color management.

Color me bad

The Atari Lynx has a 160×102 LCD display that is capable of showing 4096 possible colors. Each LCD pixel consists of a triad of cells that can display red, green and blue respectively. Each cell can have 16 different intensities, so there can be 16*16*16 = 4096 different colors.

At most it can show 16 out of these 4096 colors at one particular time. It is possible to attain a higher number of simultaneous colors, but it means you need to use smart tricks. We’ll stick to 16 for the moment.

The 16 different colors that the LCD will show are stored inside a palette. The colors in the palette are indicated by their index from 0 to 15. With only 16 index values from the palette the Lynx can efficiently store the color of a particular pixel on the LCD display. Just 4 bits are enough, so it is possible to store the colors of two pixels inside of a single byte that represents the video memory.

Let’s do some math to calculate the total amount of memory that a full video buffer requires to store the indexes for the colors of the pixels:

160 pixels* 102 pixels * 4 bits = 65280 bits = 8160 bytes = 0x1FE0 bytes

Remember this number, because you will run into it later on when looking at video buffers and the corresponding memory allocation.

Color addresses

The color palette requires 16 triplets of nibbles (4 bits) to specify the 3 times 16 intensities of red, green and blue for the colors. These triplets of RGB values are stored in two sets of 16 bytes.

The ranges for the palette colors are FDA0 to FDAF for green and FDB0 to FDBF for blue and red.

Address Alias bits 3-0 Example
FDA0 GREEN0 %0000 GGGG 0x0F
FDA1 GREEN1 %0000 GGGG 0x0A
FDAF GREENF %0000 GGGG 0x03
Address Alias bits 7-0 Example
FDB0 BLUERED0 %BBBB RRRR 0xDF
FDB1 BLUERED1 %BBBB RRRR 0x1A
FDBF BLUEREDF %BBBB RRRR 0x73

It makes more sense to put each of these next to one another. Below is a table that shows the palette index versus the color addresses.

Palette index Green Blue+Red Default value Name
0 GREEN0 BLUERED0 0x000 COLOR_BLACK
1 GREEN1 BLUERED1 0x007 COLOR_RED
2 GREEN2 BLUERED2 0x070 COLOR_BLUE
3 GREEN3 BLUERED3 0x700 COLOR_GREEN
4 GREEN4 BLUERED4 0x077 COLOR_VIOLET
5 GREEN5 BLUERED5 0x770 COLOR_CYAN
6 GREEN6 BLUERED6 0x707 COLOR_PEAGREEN
7 GREEN7 BLUERED7 0x777 COLOR_GREY
8 GREEN8 BLUERED8 0x333 COLOR_NAVYBLUE
9 GREEN9 BLUERED9 0x00F COLOR_LIGHTRED
A GREENA BLUEREDA 0x0F0 COLOR_LIGHTBLUE
B GREENB BLUEREDB 0xF00 COLOR_LIGHTGREEN
C GREENC BLUEREDC 0x0FF COLOR_LIGHTPURPLE
D GREEND BLUEREDD 0xFF0 COLOR_LIGHTCYAN
E GREENE BLUEREDE 0xF0F COLOR_YELLOW
F GREENF BLUEREDF 0xFFF COLOR_WHITE
Palette index Green Blue+Red Default Name

The table also shows the default values for each of the indexes after loading the TGI driver. The symbolic names are an easier reference for the particular index inside the palette. These names are defined in the lynx.h include file.

To show the default palette, here is a piece of code that will show the hex value of the palette index in the corresponding color.

void show_palette(const char* header)
{
  char index = 0;
  char text[5];
 
  tgi_clear();
	
  tgi_setbgcolor(COLOR_BLACK);
  tgi_setcolor(COLOR_WHITE);
  tgi_outtextxy(10, 0, header);
 
  tgi_gotoxy(10, 10);
  for (index = 0; index < 16; index++)
  {
    itoa(index, text, 16);
    tgi_setcolor(index);
    tgi_outtext(text);
  }
 
  tgi_setbgcolor(COLOR_WHITE);
  tgi_gotoxy(10, 20);
  for (index = 0; index < 16; index++)
  {
    itoa(index, text, 16);
    tgi_setcolor(index);
    tgi_outtext(text);
  }
 
  tgi_updatedisplay();
  while (tgi_busy());
}

The code will first clear the screen, display a header, then print the hex values twice: once against a black background and another time against a white background.

The call to show_palette is made from the main loop.

void main(void)  {	
  initialize();
 
  while (1)
  {
    tgi_setpalette(tgi_getdefpalette());
    show_palette("Default palette");
  };
}

The result is like this.

image

Choosing your colors

For your game you need to pick your 16 colors that you will use. Once you have these just a little code is required to set the palette. First, declare an array of all values for the 16 colors in the format 0x0GBR (e.g. 0xF0F for yellow).

static int palette[] =  {
  0x0000, 0x011a, 0x0692, 0x0946, 0x0404, 0x0818,
0x0666, 0x0888, 0x0616, 0x0b7f, 0x066e, 0x0b3b,
0x0513, 0x0b55, 0x0f5f, 0x0eee };

Next, you need to transfer these integer values to the color addresses: The high bytes go to FDA0-FDAF and the low bytes go to FDB0-FDBF. A little piece of code will help transfer this.

void setpalette(const int* palette)
{
  char index;
  for (index = 0; index < 0x10; index++)
  {
    POKE(0xFDA0 + index, palette[index] >> 8);
    POKE(0xFDB0 + index, palette[index] & 0xFF);
  }
}

The POKE method will stick a byte value into an address. It is available if you include peekpoke.h in your code.

#include <peekpoke.h>

Finally, a call to setpalette is needed, passing in the array of palette color values.

setpalette(palette);	

After that all is done. The palette has changed.

Let’s prove that by running the show_palette again.

void main(void)  {	
  initialize();
 
  while (1)
  {
    setpalette(rawpalette);
    show_palette("Custom palette");
  };
}

And in a picture:

image

Another approach for setting palette

Since we are using the TGI library anyway, there is another option available to set the palette. TGI includes a function called tgi_setpalette which takes an array of 32 bytes. These represent the green and bluered values that go into the addresses FDA0 to FDBF sequentially. In other words, by simply copying the values across the color address range, all is good.

Using tgi_setpalette requires a different palette array. Taking the custom values we used earlier, here is the new layout.

static char palette[] =  {
  // Green values
  0x0000 >> 8,
  0x011a >> 8,
  0x0692 >> 8,
  0x0946 >> 8,
  0x0404 >> 8,
  0x0818 >> 8,
  0x0666 >> 8,
  0x0888 >> 8,
  0x0616 >> 8,
  0x0b7f >> 8,
  0x066e >> 8,
  0x0b3b >> 8,
  0x0513 >> 8,
  0x0b55 >> 8,
  0x0f5f >> 8,
  0x0eee >> 8,
 
  // Blue and red values
  0x0000 & 0xff
  0x011a & 0xff,
  0x0692 & 0xff,	
  0x0946 & 0xff,	
  0x0404 & 0xff,
  0x0818 & 0xff,
  0x0666 & 0xff,	
  0x0888 & 0xff,	
  0x0616 & 0xff,	
  0x0b7f & 0xff,	
  0x066e & 0xff,	
  0x0b3b & 0xff,	
  0x0513 & 0xff,	
  0x0b55 & 0xff,	
  0x0f5f & 0xff,	
  0x0eee & 0xff	
};

In this case we have split the integer (2 byte) GBR values of the colors into the upper green and lower bluered bytes and stored them after each other. It means you have duplicate values for the integers, with additional administration should you decide to change the colors. On the other hand, you will be using a library function instead of your own setpalette implementation.

Finishing touch

It is important to realize that changing the palette renders the symbolic color names virtually useless. The names refer to an index, not the actual color. For example, assume you changed the color for index 0 to yellow. The COLOR_BLACK still refers to the color at index 0, which is now yellow. This means when you change the palette you need to define your own symbolic names for these.

//#define COLOR_BLACK		0x00
#define COLOR_DARK_RED	0x01
//#define COLOR_BLUE		0x02
//#define COLOR_GREEN		0x03
#define COLOR_DARK_BROWN	0x04
#define COLOR_LIGHT_BROWN	0x05
#define COLOR_DARK_GREY	0x06
//#define COLOR_GREY		0x07
#define COLOR_BROWN		0x08
#define COLOR_ORANGE		0x09
#define COLOR_PINK		0x0a
#define COLOR_DARK_YELLOW	0x0b
#define COLOR_DARK_GREEN	0x0c
#define COLOR_YELLOW_GREEN	0x0d
//#define COLOR_YELLOW	0x0e
//#define COLOR_WHITE		0x0f

I think it is wise to keep the original symbolic names as much as possible. You will have to carefully compose your palette and align the colors at the correct index. In the example above I have commented out the color names that are already defined. They are still correct (like blue, green and grey), but have slightly different GBR values when compared to the default palette. That should not matter and the symbolic name can stay the same.

Even if you should change nearly everything, try to avoid changing black and white. If you need those colors anyway, it is very convenient to have these at their normal index. Moreover, some TGI functions operate on fixed index values, such as tgi_clear that will clear the screen using the color at index 0.

Next time

In the next episodes we will continue our journey on the Lynx programming road. The TGI graphics library needs some further investigation and I know people are anxious to read about sprites on the Lynx. Stay tuned for more.

Advertisements
This entry was posted in Tutorial. Bookmark the permalink.

7 Responses to Programming tutorial: Part 6–Colors

  1. Pingback: Programming tutorial: Part 6–The basics of sprites | Diary of an Atari Lynx developer

  2. Pingback: Programming tutorial: Part 7–The basics of sprites | Diary of an Atari Lynx developer

  3. Pingback: Programming tutorial: Part 8–Changing appearances | Diary of an Atari Lynx developer

  4. Pingback: Programming tutorial: Part 9–Advanced sprites | Diary of an Atari Lynx developer

  5. Pingback: Programming tutorial: Part 10–Collisions | Diary of an Atari Lynx developer

  6. Pingback: Programming tutorial: Part 11–Pens and more collisions | Diary of an Atari Lynx developer

  7. Johney says:

    This is an interresting since the lynx was the first portible color handheld, 16 colors onscreen doesn’t sound that much BUT thanks(during developing stage)color dithering or color blending you can create more colors, another mothode is to just change the colors per scanline to create the ellussion of more colors onscreen but it invulves way more proccesing power then nessecarry.
    Also since the lynx has an massive colorpallet of 4096 colors, these colors look more warmer then the nes colors, the lemmings on both systems is a great example of this.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s