Cycling the DAC Color Palette

Methodology

There’s a couple of ways of accomplishing colors that rotate. The basic idea is to read the palette, and write its contents one index over. We can use the Load_DAC_Palette(..) and Write_DAC_Palette(..) functions to do this very very easily. The only problem is that our background color gets written over with this method. If you have a full screen picture, this may not be TOO bad, but personally it bugs the crap out of me! I decided to do the function in inline assembly so that the background always remains the same. Another point to mention is that with most video cards, if you don’t wait for a vertical retrace and just decide to start rotating the colors, it will result in MASSIVE flickering. If we do wait we get a flawless looking picture with little or no performance loss. You decide! If this seems a little confusing, look below. Each accomplishes color palette cycling. You should be able to find the example that fits your needs, and your programming style.

BIOS Functions

Good old BIOS provides several functions that allow the reading and writing of DAC registers. These should be used as a starting point only. They are VERY slow. Then again, compared to the time it takes the inline assembly code to do a rotate (1/70 of a second for 1 color shift) anything else seems slow :) BIOS doesn’t provide function that actually DO the rotating, we’ll just be using the functions we created earlier that use the BIOS, to do it.

  1. void Video::Rotate()
  2. {
  3.     uchar Palette[256];
  4.     Load_DAC_Palette(0,256,Palette);
  5.     Write_DAC_Palette(1,256,Palette);
  6. }

Right now you should be asking yourself, if we are starting at color one when we are writing, how can we write 256 colors? Shouldn’t that be 255 colors? Well let me answer…..NO! The BIOS is smart enough that if we specify more colors than what is actually available, it will wrap around and start setting the colors from 0! Neeto huh :) With this example color 0 does get shifted so the background color shifts as well, for better or worse :) The next function should take care of that problem as i view it.

  1. void Video::RotateNoBlack()
  2. {
  3.     uchar R,G,B,*Palette;
  4.     Get_DAC(255,R,G,B);
  5.     Load_DAC_Palette(1,255,Palette);
  6.     Write_DAC_Palette(2,254,Palette);
  7.     Set_DAC(1,R,G,B);
  8. }

Here we are just reading in the last color of the palette, the one that was destined to go into color 0. We set the rest of the palette, starting with 1 and only 254 colors so black isn’t touched. Then set the 1st color (besides black) to our saved color, and voila! I’m writing this from memory, but i think it will work ok!

C++ Functions

This is the same structure as the above functions that use the BIOS functions. The only difference is that these will be a little faster because we are communicating right with the metal baby!

  1. void Video::Rotate()
  2. {
  3.     uchar Palette[256];
  4.     Load_DAC_C_Palette(0,256,Palette);
  5.     Write_DAC_C_Palette(1,256,Palette);
  6. }
  7.  
  8. void Video::RotateNoBlack()
  9. {
  10.     uchar R,G,B,*Palette;
  11.     Get_DAC_C(255,R,G,B);
  12.     Load_DAC_C_Palette(1,255,Palette);
  13.     Write_DAC_C_Palette(2,254,Palette);
  14.     Set_DAC_C(1,R,G,B);
  15. }

These functions do the exact same thing as the BIOS versions above, except like i mentioned before they will be faster since they are communicating directly with the hardware!

Inline Assembly Functions

  1. void Video::FastRotate()
  2. {
  3.     Load_DAC_Palette(0,256,Palette); // use inline asm version
  4.     address=(long)&Palette;
  5.     while(inp(0x3DA) & 0×08);
  6.     while(!(inp(0x3Da)&0×08));
  7.     __asm__("
  8.         movw $762,%%cx           //counter
  9.         movw $0x03c6,%%dx       // dx is our port
  10.         movw $0xff,%%ax         // ax is the input number to go to port
  11.         outb %%ax,%%dx
  12.  
  13.         movl $2,%%eax           //Set Starting color
  14.         movw $0x03c8,%%dx
  15.         outb %%ax,%%dx
  16.  
  17.         movl _address,%%ebx
  18.         add  $3,%%ebx           //skip over color 0!
  19.  
  20.         LoopStart:
  21.         movl (%%ebx),%%eax
  22.         movw $0x03c9,%%dx
  23.         outb %%ax,%%dx
  24.         add  $1,%%bx
  25.         Loop LoopStart
  26.  
  27.         mov  $0,%%eax          //set black
  28.         outb %%ax,%%dx
  29.         outb %%ax,%%dx
  30.         outb %%ax,%%dx
  31.  
  32.         movl (%%ebx),%%eax     //set wrap around color
  33.         outb %%ax,%%dx
  34.         add  $1,%%bx
  35.         movl (%%ebx),%%eax
  36.         outb %%ax,%%dx
  37.         add  $1,%%bx
  38.         movl (%%ebx),%%eax
  39.         outb %%ax,%%dx
  40.         "
  41.         :
  42.         :
  43.         :"%cx","%eax","%dx","%ebx");
  44. }

Lets go through this in Chunks if you will :) First off, for this to work, address must be defined globally since its inline assembly. We load up the color palette and assign address to the address of our buffer in memory. The next two while’s wait for a vertical retrace to start before proceeding. That gives us a 0 flicker screen! Try it without and then write me saying how grateful you are :) yeah right!

In the first chunk of the assembly we set cx to be the number of loop iterations we have to do. Loop uses cx for this. Next, we set the DAC mask register to 0xff so that we have access to all colors.

The next chunk tells the DAC that we will start with color number 2. Next, we move the address of address into bx so we can use it as a pointer, then we add 3 onto it because we know the first 3 values are going to be 0′s, because who in their right mind is going to set color 0 to a REAL color!? I think i may have created some enemies from that statement, but moving right along..!

Inside the Loop (LoopStart) we create a pointer (ax) and set the port to which we will be writing this information (dx). We exit out of the loop with all colors set except 0 (which we want to leave anyway) and 1. At this very point, the DAC will reset to start setting data for color 0, so lets just set it to 0′s. That is what the chunk after the loop does. The final chunk sets color 1 manually.

Hope this helps you out some ;)

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>