Wednesday, April 05, 2006

ASM/C++: Using inline ASM for Video Palette Manipulation in Turbo C++

Building off of the example of Video Programming with Turbo C++, I am going to use this program as a baseline to demonstrate how to do inline ASM with Turbo C++. For the version available from the Borland Museum, you will additionally need a copy of TASM to compile this program.

#include <dos.h>

int main()
{
     unsigned char red[256], blue[256], green[256], r, g, b;
     unsigned char far *VGA = (unsigned char *)MK_FP(0xA000, 0);
     unsigned int x, y;
     unsigned char z;
     struct time d;

     //set the video mode
     asm{
          mov ax, 13h
          int 10h
     };

     //load the sprawling palette
     for (x = 0; x <= 200; x++)
          for (y = 0; y < 320; y++)
               if (y <= 256)
                    VGA[(x * 320) + y] = y;
               else
                    VGA[(x * 320) + y] = y - 256;

     //Now load the palette into memory
     for (z = 0; z < 255; z++)
     {
          asm{
               //First, trigger the output port
               mov dx, 0x3c7
               mov ax, z
               out dx, al

               //Now read in the R, G, and B values, storing in
               //r, g and b respectively
               mov dx, 0x3c9
               in al, dx
               mov r, al
               in al, dx
               mov g, al
               in al, dx
               mov b, al
          }

          //Assign into the array
          red[z] = r;
          green[z] = g;
          blue[z] = b;
     }

     //What this will do is fade red out first, then blue, then green
     //X controls which color is fading, y controls the intensity, and
     //z is controlling which palette entry we are changing
     for (x = 0; x < 3; x++)
          for (y = 0; y < 64; y++)
               for (z = 1; z < 255; z++)
               {
               //based on the color to fade, subtract that color
               switch (x)
               {
               case 0:
                    if (red[z] > 0)
                         red[z]--;
                    break;
               case 1:
                    if (green[z] > 0)
                         green[z]--;
                    break;
               case 2:
                    if (blue[z] > 0)
                         blue[z]--;
                    break;
               } //end switch

               //set r, g, and b for output in ASM
               r = red[z];
               g = green[z];
               b = blue[z];

               //port 3c8 signals 3c9 to recieve a new palette
               //entry for color z
               asm{
                    //trigger the port for palette output
                    mov dx, 0x3c8
                    mov ax, z
                    out dx, al

                    //output the palette respectively
                    mov dx, 0x3c9
                    mov al, r
                    out dx, al
                    mov al, g
                    out dx, al
                    mov al, b
                    out dx, al
               }
               } //end for

     //set video mode back to 80x25
     asm {
          mov ax, 3h
          int 10h
     };

     return 0;
}

The main changes are the inclusion of inline ASM for manipulating the palette, similar to the method demonstrated with this debug program. The only issue I had with doing this was I could not directly pass the array values into the inline asm, so I had to create the temporary variables r, g, b to store the values to pass. Each time I tried, I got an “invalid index” error. I also used the Borland declaration MK_FP to point to the VGA memory to demonstrate an alternative method of doing so from the previous example. And one very important note, be sure to use unsigned integers rather than integers, as this caused a few undefined results when I initially wrote both programs.

Inline ASM differs greatly between the more inline friendly Borland suites and the GCC family. In GCC, inline asm it basically a function call like so:

asm(“asm code”, output operands, input operands, registers that get “clobbered”);

More information on GCC Inline Assembly can be found here, here, and here. The last link is for DJGPP, a GNU-like compiler for DOS and Windows, but the syntax and information is still valid for GCC.

No comments: