//------------------------------------------------------------------- // animate2.cpp // // This is an enhancement of our earlier 'animate1.cpp' demo. // It incorporates (1) the Michener circle-drawing algorithm; // (2) asynchronous and noncanonical keyboard-input, allowing // users to control program-termination (via the ESCAPE-key); // and (3) movement of the 'pong' ball is controlled by alarm // signals from an interval timer, thereby decoupling drawing // from ball-movement. The drawing operations continue to be // synchronized with the CRT's Vertical Blanking interval, to // provide viewers with the impression of "smooth" animation. // // programmer: ALLAN CRUSE // date begun: 21 SEP 2003 // completion: 24 SEP 2003 //------------------------------------------------------------------- #include // for printf(), perror() #include // for open() #include // for exit() #include // for close() #include // for mman() #include // for iopl(), inb() #include // for signal() #include // for tcgetattr(), tcsetattr() #include // for setitimer() #include // for fcntl() #define VRAM_BASE 0xB0000000 #define VRAM_SIZE (1 << 20) #define ESCAPE_KEY 0x1B unsigned char *fb = (unsigned char*)VRAM_BASE; int display_mode = 0x4101, hres = 640, vres = 480; struct termios save_termios; struct itimerval save_itimer; char devname[] = "/dev/vram"; // global variables used for game control int xpos = 200, ypos = 100, radius = 10, xinc = 1, yinc = -1; int xmin = 70, ymin = 60, xmax = hres-1-70, ymax = vres-1-60; int done = 0, speed = 4; void on_alarm( int signum ) { for (int i = 0; i < speed; i++) { xpos += xinc; // move the ball horizontally if (( xpos <= xmin )||( xpos >= xmax )) xinc = -xinc; ypos += yinc; // move the ball vertically if (( ypos <= ymin )||( ypos >= ymax )) yinc = -yinc; } } void on_input( int signum ) { int inch = 0; read( STDIN_FILENO, &inch, 4 ); if ( inch == ESCAPE_KEY ) done = 1; } void at_exit( void ) { setitimer( ITIMER_REAL, &save_itimer, 0 ); tcsetattr( STDIN_FILENO, TCSAFLUSH, &save_termios ); system( " mode3 3 " ); printf( "\n" ); } void draw_segments( int x, int y, int xcent, int ycent, int color ) { int minx = 0, miny = 0, maxx = hres-1, maxy = vres-1; int xlo, xhi, v, h; xlo = xcent-x, xhi = xcent+x; if ( xlo < minx ) xlo = minx; if ( xhi > maxx ) xhi = maxx; if ( ( v = ycent-y ) >= miny ) for (h = xlo; h <= xhi; h++) fb[ v*hres + h ] = color; if ( ( v = ycent+y ) <= maxy ) for (h = xlo; h <= xhi; h++) fb[ v*hres + h ] = color; xlo = xcent-y, xhi = xcent+y; if ( xlo < minx ) xlo = minx; if ( xhi > maxx ) xhi = maxx; if ( ( v = ycent-x ) >= miny ) for (h = xlo; h <= xhi; h++) fb[ v*hres + h ] = color; if ( ( v = ycent+x ) <= maxy ) for (h = xlo; h <= xhi; h++) fb[ v*hres + h ] = color; } void draw_ball( int x, int y, int radius, int color ) { int xcent = x, ycent = y, disp = 3 - 2*radius; y = radius, x = 0; while ( x <= y ) { draw_segments( x, y, xcent, ycent, color ); if ( disp < 0 ) { disp += 4*x + 6; } else { disp += 4*(x - y) + 10; --y; } ++x; } } void draw_rectangle( int x, int y, int h, int v, int color ) { int minx = x, miny = y, maxx = x+h-1, maxy = y+v-1; do { fb[ y*hres + x ] = color; ++x; } while ( x < maxx ); do { fb[ y*hres + x ] = color; ++y; } while ( y < maxy ); do { fb[ y*hres + x ] = color; --x; } while ( x > minx ); do { fb[ y*hres + x ] = color; --y; } while ( y > miny ); } void fill_rectangle( int x, int y, int h, int v, int color ) { int minx = x, miny = y, maxx = x+h-1, maxy = y+v-1; for (y = miny; y <= maxy; y++) for (x = minx; x <= maxx; x++) fb[ y*hres + x ] = color; } void vsync( void ) { // wait for current retrace to finish while ( ( inb( 0x3DA ) & 8 ) != 8 ); // then wait till next retrace begins while ( ( inb( 0x3DA ) & 8 ) == 8 ); } void map_system_memory( int base, int size ) { int prot = PROT_READ | PROT_WRITE; int flag = MAP_FIXED | MAP_SHARED; void *mm = (void*)base; int fd = open( devname, O_RDWR ); if ( fd < 0 ) { perror( devname ); exit(1); } if ( mmap( mm, size, prot, flag, fd, 0 ) == MAP_FAILED ) { perror( "mmap" ); exit(1); } close( fd ); } int main( int argc, char **argv ) { // install SVGA device-driver and map in the VRAM system( "/sbin/insmod vram.o" ); map_system_memory( VRAM_BASE, VRAM_SIZE ); // allow i/o (for detecting CRT vertical retrace) if ( iopl( 3 ) ) { perror( "iopl" ); exit(1); } // enable noncanonical keyboard input tcgetattr( STDIN_FILENO, &save_termios ); struct termios work_termios = save_termios; work_termios.c_lflag &= ~(ICANON|ECHO|ISIG); work_termios.c_cc[ VMIN ] = 1; work_termios.c_cc[ VTIME ] = 0; tcsetattr( STDIN_FILENO, TCSAFLUSH, &work_termios ); // activate asynchronous input notification signal( SIGIO, on_input ); fcntl( STDIN_FILENO, F_SETOWN, getpid() ); int val = fcntl( STDIN_FILENO, F_GETFL, 0 ); fcntl( STDIN_FILENO, F_SETFL, val | O_ASYNC ); // enter graphics mode char command[ 40 ]; sprintf( command, "clear ; mode3 %d ", 0x4101 ); system( command ); // 640x480, 256-color, w/LFB atexit( at_exit ); // insure return to textmode // draw screen border int black = 0, white = 15, blue = 9, green = 10, yellow = 14; int x = 0, y = 0, wide = hres, high = vres; draw_rectangle( x, y, wide, high, white ); // draw game wall x += 40, y += 30, wide -= 80, high -= 60; fill_rectangle( x, y, wide, high, blue ); x += 20, y += 20, wide -= 40, high -= 40; fill_rectangle( x, y, wide, high, black ); // draw game ball draw_ball( xpos, ypos, radius, yellow ); // draw game paddle fill_rectangle( 80, 454, 100, 8, green ); // enable the interval timer signal( SIGALRM, on_alarm ); struct itimerval itval = {0}; itval.it_value.tv_sec = 2; itval.it_value.tv_usec = 0; itval.it_interval.tv_sec = 0; itval.it_interval.tv_usec = 10000; setitimer( ITIMER_REAL, &itval, &save_itimer ); // animate the game ball int xold, yold; done = 0; do { xold = xpos, yold = ypos; draw_ball( xpos, ypos, radius, yellow ); vsync(); draw_ball( xold, yold, radius, black ); } while ( !done ); }