/************************************************************************** * unchain.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 unchain.c -o unchain.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 demonstrates VGA's unchained mode * **************************************************************************/ #include #include #include #include #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 SC_INDEX 0x03c4 /* VGA sequence controller */ #define SC_DATA 0x03c5 #define PALETTE_INDEX 0x03c8 /* VGA digital-to-analog converter */ #define PALETTE_DATA 0x03c9 #define GC_INDEX 0x03ce /* VGA graphics controller */ #define GC_DATA 0x03cf #define CRTC_INDEX 0x03d4 /* VGA CRT controller */ #define CRTC_DATA 0x03d5 #define INPUT_STATUS_1 0x03da #define MAP_MASK 0x02 /* Sequence controller registers */ #define ALL_PLANES 0xff02 #define MEMORY_MODE 0x04 #define LATCHES_ON 0x0008 /* Graphics controller registers */ #define LATCHES_OFF 0xff08 #define HIGH_ADDRESS 0x0C /* CRT controller registers */ #define LOW_ADDRESS 0x0D #define UNDERLINE_LOCATION 0x14 #define MODE_CONTROL 0x17 #define DISPLAY_ENABLE 0x01 /* VGA input status bits */ #define VRETRACE 0x08 #define SCREEN_WIDTH 320 /* width in pixels of mode 0x13 */ #define SCREEN_HEIGHT 200 /* height in pixels of mode 0x13 */ #define SCREEN_SIZE (word)(SCREEN_WIDTH*SCREEN_HEIGHT) #define NUM_COLORS 256 /* number of colors in mode 0x13 */ #define BITMAP_WIDTH 32 #define BITMAP_HEIGHT 25 #define ANIMATION_FRAMES 24 #define TOTAL_FRAMES 140 #define VERTICAL_RETRACE /* comment out this line for more accurate timing */ 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; typedef struct tagOBJECT /* the structure for a moving object in 2d space; used for animation */ { int x,y; int dx,dy; byte width,height; } OBJECT; /************************************************************************** * fskip * * Skips bytes in a file. * **************************************************************************/ void fskip(FILE *fp, int num_bytes) { int i; for (i=0; iwidth, 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;indexpalette[(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[(int)(index+x)]=(byte)fgetc(fp); fclose(fp); } /************************************************************************** * 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 */ } /************************************************************************** * plot_pixel * * Plots a pixel in unchained mode * **************************************************************************/ void plot_pixel(int x,int y,byte color) { outp(SC_INDEX, MAP_MASK); /* select plane */ outp(SC_DATA, 1 << (x&3) ); VGA[(y<<6)+(y<<4)+(x>>2)]=color; } /************************************************************************** * Main * **************************************************************************/ void main(int argc, char *argv[]) { word bitmap_offset,screen_offset; word visual_page = 0; word active_page = SCREEN_SIZE/4; word start; float t1,t2; int i,repeat,plane,num_objects=0; word x,y; byte *double_buffer; BITMAP bmp; OBJECT *object; 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; /* get command-line options */ if (argc>0) num_objects=atoi(argv[1]); if (num_objects<=0) num_objects=8; /* allocate memory for double buffer and background image */ if ((double_buffer = (byte *) malloc(SCREEN_SIZE)) == NULL) { printf("Not enough memory for double buffer.\n"); exit(1); } /* allocate memory for objects */ if ((object = (OBJECT *) malloc(sizeof(OBJECT)*num_objects)) == NULL) { printf("Not enough memory for objects.\n"); free(double_buffer); exit(1); } /* load the images */ load_bmp("balls.bmp",&bmp); /* set the object positions */ srand(*my_clock); for(i=0;i SCREEN_WIDTH-object[i].width-1) object[i].dx=-object[i].dx; if (object[i].y + object[i].dy < 0 || object[i].y + object[i].dy > SCREEN_HEIGHT-object[i].height-1) object[i].dy=-object[i].dy; /* move the object */ object[i].x+=object[i].dx; object[i].y+=object[i].dy; } /* point to the next image in the animation */ bitmap_offset+=BITMAP_WIDTH; if ((bitmap_offset%bmp.width)==0) bitmap_offset+=bmp.width*(BITMAP_HEIGHT-1); /* show the buffer */ show_buffer(double_buffer); } t1=(*my_clock-start)/18.2; /* calculate how long it took. */ free(double_buffer); /* free up memory used */ /************************************************************************/ set_unchained_mode(); /* set unchained mode */ start=*my_clock; /* record the starting time. */ for(repeat=0;repeat>2); /* draw the object. */ for(plane=0;plane<4;plane++) { /* select plane */ outp(SC_DATA, 1 << ((plane+object[i].x)&3) ); for(y=0;y>2)+((x+(object[i].x&3)) >> 2)]= bmp.data[bitmap_offset+y+x]; } /* check to see if the object is within boundries */ if (object[i].x + object[i].dx < 0 || object[i].x + object[i].dx > SCREEN_WIDTH-object[i].width-1) object[i].dx=-object[i].dx; if (object[i].y + object[i].dy < 0 || object[i].y + object[i].dy > SCREEN_HEIGHT-object[i].height-1) object[i].dy=-object[i].dy; /* move the object */ object[i].x+=object[i].dx; object[i].y+=object[i].dy; } /* point to the next image in the animation */ bitmap_offset+=BITMAP_WIDTH; if ((bitmap_offset%bmp.width)==0) bitmap_offset+=bmp.width*(BITMAP_HEIGHT-1); /* flip the pages */ page_flip(&visual_page,&active_page); } t2=(*my_clock-start)/18.2; /* calculate how long it took. */ free(bmp.data); free(object); set_mode(TEXT_MODE); /* set the video mode back to text mode. */ /* output the results... */ printf("Results with %i objects",num_objects); #ifdef VERTICAL_RETRACE printf(":\n"); #else printf(" (vertical retrace *ignored*):\n"); #endif printf(" Mode 0x13 with double buffering:\n"); printf(" %f seconds,\n",t1); printf(" %f frames per second.\n",(float)TOTAL_FRAMES/t1); printf(" Unchained mode with page flipping:\n"); printf(" %f seconds,\n",t2); printf(" %f frames per second.\n",(float)TOTAL_FRAMES/t2); if (t2 != 0) printf(" Unchained mode with page flipping was %f times faster.\n",t1/t2); __djgpp_nearptr_disable(); printf("%p\n",my_clock); return; }