/************************************************************************** * palette.c * * written by David Brackeen * * http://www.brackeen.com/home/vga/ * * * * Tab stops are set to 2. * * This program compiles with DJGPP! (www.delorie.com) * * To compile in DJGPP: gcc palette.c -o palette.exe * * * * This program will only work on DOS- or Windows-based systems with a * * VGA, SuperVGA, or compatible video adapter. * * * * Please feel free to copy this source code. * * * * DESCRIPTION: This program demostrates palette manipulation and * * vertical retrace sychronization. * **************************************************************************/ #include <stdio.h> #include <stdlib.h> #include <dos.h> #include <sys/nearptr.h> #define VIDEO_INT 0x10 /* the BIOS video interrupt. */ #define SET_MODE 0x00 /* BIOS func to set the video mode. */ #define VGA_256_COLOR_MODE 0x13 /* use to set 256-color mode. */ #define TEXT_MODE 0x03 /* use to set 80x25 text mode. */ #define PALETTE_INDEX 0x03c8 #define PALETTE_DATA 0x03c9 #define INPUT_STATUS 0x03da #define VRETRACE 0x08 #define SCREEN_WIDTH 320 /* width in pixels of mode 0x13 */ #define SCREEN_HEIGHT 200 /* height in pixels of mode 0x13 */ #define NUM_COLORS 256 /* number of colors in mode 0x13 */ typedef unsigned char byte; typedef unsigned short word; typedef unsigned long dword; byte *VGA = (byte *)0xA0000; /* this points to video memory. */ word *my_clock = (word *)0x046C; /* this points to the 18.2hz system clock. */ typedef struct tagBITMAP /* the structure for a bitmap. */ { word width; word height; byte palette[256*3]; byte *data; } BITMAP; /************************************************************************** * fskip * * Skips bytes in a file. * **************************************************************************/ void fskip(FILE *fp, int num_bytes) { int i; for (i=0; i<num_bytes; i++) fgetc(fp); } /************************************************************************** * set_mode * * Sets the video mode. * **************************************************************************/ void set_mode(byte mode) { union REGS regs; regs.h.ah = SET_MODE; regs.h.al = mode; int86(VIDEO_INT, ®s, ®s); } /************************************************************************** * load_bmp * * Loads a bitmap file into memory. * **************************************************************************/ void load_bmp(char *file,BITMAP *b) { FILE *fp; long index; word num_colors; int x; /* open the file */ if ((fp = fopen(file,"rb")) == NULL) { printf("Error opening file %s.\n",file); exit(1); } /* check to see if it is a valid bitmap file */ if (fgetc(fp)!='B' || fgetc(fp)!='M') { fclose(fp); printf("%s is not a bitmap file.\n",file); exit(1); } /* read in the width and height of the image, and the number of colors used; ignore the rest */ fskip(fp,16); fread(&b->width, sizeof(word), 1, fp); fskip(fp,2); fread(&b->height,sizeof(word), 1, fp); fskip(fp,22); fread(&num_colors,sizeof(word), 1, fp); fskip(fp,6); /* assume we are working with an 8-bit file */ if (num_colors==0) num_colors=256; /* try to allocate memory */ if ((b->data = (byte *) malloc((word)(b->width*b->height))) == NULL) { fclose(fp); printf("Error allocating memory for file %s.\n",file); exit(1); } /* read the palette information */ for(index=0;index<num_colors;index++) { b->palette[(int)(index*3+2)] = fgetc(fp) >> 2; b->palette[(int)(index*3+1)] = fgetc(fp) >> 2; b->palette[(int)(index*3+0)] = fgetc(fp) >> 2; x=fgetc(fp); } /* read the bitmap */ for(index=(b->height-1)*b->width;index>=0;index-=b->width) for(x=0;x<b->width;x++) b->data[(word)(index+x)]=(byte)fgetc(fp); fclose(fp); } /************************************************************************** * draw_bitmap * * Draws a bitmap. * **************************************************************************/ void draw_bitmap(BITMAP *bmp,int x,int y) { int j; word screen_offset = (y<<8)+(y<<6)+x; word bitmap_offset = 0; for(j=0;j<bmp->height;j++) { memcpy(&VGA[screen_offset],&bmp->data[bitmap_offset],bmp->width); bitmap_offset+=bmp->width; screen_offset+=SCREEN_WIDTH; } } /************************************************************************** * set_palette * * Sets all 256 colors of the palette. * **************************************************************************/ void set_palette(byte *palette) { int i; outp(PALETTE_INDEX,0); /* tell the VGA that palette data is coming. */ for(i=0;i<256*3;i++) outp(PALETTE_DATA,palette[i]); /* write the data */ } /************************************************************************** * rotate_palette * * Rotates the colors of the palette. * **************************************************************************/ void rotate_palette(byte *palette) { int i,red,green,blue; red = palette[3]; green= palette[4]; blue = palette[5]; for(i=3;i<256*3-3;i++) palette[i]=palette[i+3]; palette[256*3-3]=red; palette[256*3-2]=green; palette[256*3-1]=blue; set_palette(palette); } /************************************************************************** * wait_for_retrace * * Wait until the *beginning* of a vertical retrace cycle (60hz). * **************************************************************************/ void wait_for_retrace(void) { /* wait until done with vertical retrace */ while ((inp(INPUT_STATUS) & VRETRACE)) {}; /* wait until done refreshing */ while (!(inp(INPUT_STATUS) & VRETRACE)) {}; } /************************************************************************** * wait * * Wait for a specified number of clock ticks (18hz). * **************************************************************************/ void wait(int ticks) { word start; start=*my_clock; while (*my_clock-start<ticks) { *my_clock=*my_clock; /* this line is for some compilers that would otherwise ignore this loop */ } } /************************************************************************** * Main * * Draws a bitmap and then rotates the palette. * **************************************************************************/ void main() { BITMAP bmp; int i; if (__djgpp_nearptr_enable() == 0) { printf("Could get access to first 640K of memory.\n"); exit(-1); } VGA+=__djgpp_conventional_base; my_clock = (void *)my_clock + __djgpp_conventional_base; load_bmp("mset.bmp",&bmp); /* open the file */ set_mode(VGA_256_COLOR_MODE); /* set the video mode. */ set_palette(bmp.palette); /* set the palette */ draw_bitmap(&bmp, /* draw the bitmap centered */ (SCREEN_WIDTH-bmp.width) >>1, (SCREEN_HEIGHT-bmp.height) >>1); wait(25); for(i=0;i<510;i++) /* rotate the palette at 30hz */ { wait_for_retrace(); wait_for_retrace(); rotate_palette(bmp.palette); } wait(25); free(bmp.data); /* free up memory used */ set_mode(TEXT_MODE); /* set the video mode back to text mode. */ __djgpp_nearptr_disable(); return; }