/************************************************************************** * unchain.c * * written by David Brackeen * * http://www.brackeen.com/home/vga/ * * * * This is a 16-bit program. * * Tab stops are set to 2. * * Remember to compile in the LARGE memory model! * * To compile in Borland C: bcc -ml unchain.c * * * * 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 <stdio.h> #include <stdlib.h> #include <dos.h> #include <mem.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 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 *)0xA0000000L; /* this points to video memory. */ word *my_clock=(word *)0x0000046C; /* 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; 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); } /************************************************************************** * set_unchained_mode * * resets VGA mode 0x13 to unchained mode to access all 256K of memory * **************************************************************************/ void set_unchained_mode(void) { word i; dword *ptr=(dword *)VGA; /* used for faster screen clearing */ outp(SC_INDEX, MEMORY_MODE); /* turn off chain-4 mode */ outp(SC_DATA, 0x06); outpw(SC_INDEX, ALL_PLANES); /* set map mask to all 4 planes */ for(i=0;i<0x4000;i++) /* clear all 256K of memory */ *ptr++ = 0; outp(CRTC_INDEX,UNDERLINE_LOCATION);/* turn off long mode */ outp(CRTC_DATA, 0x00); outp(CRTC_INDEX,MODE_CONTROL); /* turn on byte mode */ outp(CRTC_DATA, 0xe3); } /************************************************************************** * page_flip * * switches the pages at the appropriate time and waits for the * * vertical retrace. * **************************************************************************/ void page_flip(word *page1,word *page2) { word high_address,low_address; word temp; temp=*page1; *page1=*page2; *page2=temp; high_address = HIGH_ADDRESS | (*page1 & 0xff00); low_address = LOW_ADDRESS | (*page1 << 8); #ifdef VERTICAL_RETRACE while ((inp(INPUT_STATUS_1) & DISPLAY_ENABLE)); #endif outpw(CRTC_INDEX, high_address); outpw(CRTC_INDEX, low_address); #ifdef VERTICAL_RETRACE while (!(inp(INPUT_STATUS_1) & VRETRACE)); #endif } /************************************************************************** * show_buffer * * displays a memory buffer on the screen * **************************************************************************/ void show_buffer(byte *buffer) { #ifdef VERTICAL_RETRACE while ((inp(INPUT_STATUS_1) & VRETRACE)); while (!(inp(INPUT_STATUS_1) & VRETRACE)); #endif memcpy(VGA,buffer,SCREEN_SIZE); } /************************************************************************** * 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[(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; /* 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<num_objects;i++) { object[i].width = BITMAP_WIDTH; object[i].height = BITMAP_HEIGHT; object[i].x = rand() % (SCREEN_WIDTH - BITMAP_WIDTH ); object[i].y = rand() % (SCREEN_HEIGHT- BITMAP_HEIGHT); object[i].dx = (rand()%5) - 2; object[i].dy = (rand()%5) - 2; } set_mode(VGA_256_COLOR_MODE); /* set the video mode. */ set_palette(bmp.palette); start=*my_clock; /* record the starting time. */ for(repeat=0;repeat<TOTAL_FRAMES;repeat++) { if ((repeat%ANIMATION_FRAMES)==0) bitmap_offset=0; /* clear background */ memset(double_buffer,0,SCREEN_SIZE); for(i=0;i<num_objects;i++) { screen_offset = (object[i].y<<8) + (object[i].y<<6) + object[i].x; /* draw the object. */ for(y=0;y<BITMAP_HEIGHT*bmp.width;y+=bmp.width) for(x=0;x<BITMAP_WIDTH;x++) if (bmp.data[bitmap_offset+y+x]!=0) double_buffer[screen_offset+y+x]=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); /* 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<TOTAL_FRAMES;repeat++) { if ((repeat%ANIMATION_FRAMES)==0) bitmap_offset=0; /* clear background */ outpw(SC_INDEX,ALL_PLANES); memset(&VGA[active_page],0,SCREEN_SIZE/4); outp(SC_INDEX, MAP_MASK); /* select plane */ for(i=0;i<num_objects;i++) { screen_offset = (object[i].y<<6) + (object[i].y<<4) + (object[i].x>>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<BITMAP_HEIGHT*bmp.width;y+=bmp.width) for(x=plane;x<BITMAP_WIDTH;x+=4) if (bmp.data[bitmap_offset+y+x]!=0) VGA[active_page+screen_offset+(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); return; }