//------------------------------------------------------------------- // horizpan.cpp // // This program illustrates some effects of reprogramming the // display pitch, by writing a new value into the CRTC Offset // Register (port 0x3D4, index 0x13). (The CRT Start Address // is then incremented, or decremented, to perform horizontal // panning over an enlarged logical screensize which is twice // as wide as the actual physical screensize.) // // programmer: ALLAN CRUSE // written on: 26 NOV 2003 //------------------------------------------------------------------- #include // for printf(), perror() #include // for open() #include // for exit() #include // for read(), write(), close() #include // for tcgetattr(), tcsetattr() #include // for mmap() #include // for iopl() #define VRAM_BASE_ADDRESS 0xA0000000 #define VGA_MEMORY_LENGTH ( 4 << 20) unsigned char *vram = (unsigned char*)VRAM_BASE_ADDRESS; const int display_mode = 0x4101, hres = 640, vres = 480; int pitch = 640; 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 { vram[ y*pitch + x ] = color; ++x; } while ( x < maxx ); do { vram[ y*pitch + x ] = color; ++y; } while ( y < maxy ); do { vram[ y*pitch + x ] = color; --x; } while ( x > minx ); do { vram[ y*pitch + x ] = color; --y; } while ( y > miny ); } int map_system_memory( int base, int size ) { int fd = open( "/dev/vram", O_RDWR ); if ( fd < 0 ) { perror( "/dev/vram" ); return -1; } int prot = PROT_READ | PROT_WRITE; int flag = MAP_FIXED | MAP_SHARED; void *mm = (void*)base; if ( mmap( mm, size, prot, flag, fd, 0 ) == MAP_FAILED ) { perror( "mmap" ); return -1; } close( fd ); } int main( int argc, char **argv ) { // setup i/o permissions and map video frame-buffer if ( iopl( 3 ) ) { perror( "iopl" ); exit(1); } system( "/sbin/insmod vram.o" ); map_system_memory( VRAM_BASE_ADDRESS, VGA_MEMORY_LENGTH ); // reprogram the terminal for non-canonical input struct termios otty; tcgetattr( 0, &otty ); struct termios tty = otty; tty.c_lflag &= ~( ICANON | ECHO | ISIG ); tty.c_cc[ VMIN ] = 1; tty.c_cc[ VTIME ] = 0; tcsetattr( 0, TCSAFLUSH, &tty ); // enter graphics mode char command[ 40 ]; sprintf( command, " clear ; mode3 %d ", display_mode ); system( command ); // draw a white border (640x480) int color = 15; draw_rectangle( 0, 0, hres, vres, color ); getchar(); // color horizontal scanlines alternately green and red for (int v = 1; v < vres-1; v++) { color = ( v % 2 ) ? 2 : 4; for (int h = 1; h < hres-1; h++) vram[ v*hres + h ] = color; } getchar(); // reprogram the CRTC Offset Register (for 1280x480) outb( 0x13, 0x3D4 ); int orig_offset = inw( 0x3D4 ); int work_offset = orig_offset; work_offset += (orig_offset & ~0xFF); outw( work_offset, 0x3D4 ); getchar(); // perform horizontal panning by incrementing CRTC Start Address int start = 0; for (int i = 0; i < 160; i++) { ++start; outw( ((start << 8)&0xFF00)|0x0D, 0x3D4 ); outw( ((start << 0)&0xFF00)|0x0C, 0x3D4 ); getchar(); } getchar(); // modify our software 'display_pitch' variable pitch *= 2; // draw a yellow border for our new 'logical' screensize int wide = hres *2; int high = vres; color = 14; draw_rectangle( 0, 0, wide, high, color ); getchar(); // draw concentric rectangles to fill the new screensize color = 32; for (int i = 1; i < vres/2; i++) { wide -= 2; high -= 2; draw_rectangle( i, i, wide, high, color ); ++color; } getchar(); // perform horizontal panning by decrementing CRTC Start Address for (int i = 0; i < 160; i++) { --start; outw( ((start << 8)&0xFF00)|0x0D, 0x3D4 ); outw( ((start << 0)&0xFF00)|0x0C, 0x3D4 ); getchar(); } getchar(); // leave graphics mode system( " mode3 3 " ); tcsetattr( 0, TCSAFLUSH, &otty ); }