//------------------------------------------------------------------- // waveview.cpp // // This program utilizes the Radeon graphic engine's CRTC_PITCH // and CRTC_START registers to inplement a horizontal scrolling // capability for viewing the 'pulse-code' data in a .wav file. // // compile using: $ g++ waveview.cpp int86.cpp -o waveview // // programmer: ALLAN CRUSE // written on: 15 OCT 2005 //------------------------------------------------------------------- #include // for printf(), perror() #include // for open() #include // for exit() #include // for lseek() #include // for strncpy(), strtok(), strcat() #include // for tcgetattr(), tcsetattr() #include // for mmap() #include // for inb(), outb(), etc. #include "int86.h" // for init8086(), int86() #define VENDOR_ID 0x1002 // Advanced Technologies, Inc. #define DEVICE_ID 0x5B60 // ATI Radeon X300 //#define DEVICE_ID 0x5159 // ATI Radeon 7000 //#define DEVICE_ID 0x5960 // ATI Radeon 9250 Pro #define CRTC_PITCH 0x022C // Scanline-Width register #define CRTC_START 0x0224 // Start-Address register #define LEFT_ARROW 0x445B1B #define RIGHT_ARROW 0x435B1B typedef unsigned char BAND[ 256 ]; // for an 8-bit pulse-code BAND *vimage; // points to virtual image unsigned char *vram = (unsigned char*)0xA0000000; int vesa_mode = 0x4105, hres = 1024, vres = 768; struct vm86plus_struct vm; int black = 0x00, gray = 0x08, yellow = 0x0E; int iobase, orig_pitch, work_pitch, pitch = hres*2; int ymin = (vres - 256)/2, ymax = (vres + 256)/2; void draw_bands( int src_index, int dst_index, int num_bands ) { unsigned char *band; for (int x = 0; x < num_bands; x++) { int sx = src_index + x; int dx = dst_index + x; band = (unsigned char*)( vimage + sx ); for (int y = ymin; y < ymax; y++) vram[ y * pitch + dx ] = band[ y ]; } } int main( int argc, char **argv ) { // obtain the .wav file's name as a command-line argument if ( argc == 1 ) { fprintf( stderr, "usage: waveview \n" ), exit(1); } char wavename[ 80 ]; strncpy( wavename, argv[1], 75 ); strtok( wavename, ". \n\t" ); strcat( wavename, ".wav" ); // memory-map the .wav input file to userspace int fd = open( wavename, O_RDONLY ); if ( fd < 0 ) { perror( wavename ); exit(1); } int sz = lseek( fd, 0, SEEK_END ); char *wav = (char*)mmap( NULL, sz, PROT_READ, MAP_PRIVATE, fd, 0 ); if ( wav == MAP_FAILED ) { perror( "mmap" ); exit(1); } // verify the .wav file-format if (( strncmp( wav+0, "RIFF", 4 ))||( strncmp( wav+8, "WAVE", 4 ))) { fprintf( stderr, "Invalid .wav file\n" ); exit(1); } if (( strncmp( wav+12, "fmt ", 4 ))||( *(int*)(wav+16) != 0x10 )) { fprintf( stderr, "Invalid format chunk\n" ); exit(1); } // setup a pointer to the .wav file's pulse-code data if ( strncmp( wav+36, "data", 4 ) ) { fprintf( stderr, "data chunk not found\n" ); exit(1); } int datasize = *(int*)(wav+40); unsigned char *pcm = (unsigned char*)(wav+44); // extract the .wav file's playback parameters short formatTag = *(short*)(wav+20); short nChannels = *(short*)(wav+22); int samplesPerSec = *(int*)(wav+24); int avBytesPerSec = *(int*)(wav+28); short blockAlignmnt = *(short*)(wav+32); short bitsPerSample = *(short*)(wav+34); // check for format supported by this viewer: unsigned 8-bit mono if (( formatTag != 1 )||( nChannels != 1 )||( bitsPerSample != 8 )) { fprintf( stderr, "unsupported data format\n" ); exit(1); } // alocate storage for the virtual wave-image int shiftsz = hres >> 7; int margins = 4*shiftsz; int maxband = datasize + margins; if ( maxband < hres ) maxband = hres; maxband = (maxband / shiftsz) * shiftsz; vimage = (BAND*)calloc( maxband, sizeof( BAND ) ); if ( !vimage ) { perror( "malloc" ); exit(1); } // initialize the 'virtual' wave-image's background for (int b = 0; b < maxband; b++) { unsigned char *band = (unsigned char*)&vimage[ b ]; for (int y = 0; y < 256; y++) band[ y ] = gray; } // initialize the 'virtual' wave-image's foreground for (int b = 0; b < datasize; b++) { int index = b + margins/2; unsigned char *band = (unsigned char*)&vimage[ index ]; int pulse = 255 - pcm[ b ]; for (int y = 0; y < 256; y++) { if ((( pulse <= 0x80 )&&( pulse <= y )&&( y <= 0x80 )) ||(( pulse >= 0x80 )&&( 0x80 <= y )&&( y <= pulse ))) band[ y ] = yellow; } } // determine the Radeon engine's port-address init8086(); // allow BIOS and I/O access vm.regs.eax = 0xB102; // PCI Find Device function vm.regs.ecx = DEVICE_ID; // specify the device vm.regs.edx = VENDOR_ID; // specify the vendor vm.regs.esi = 0; // initial search index int86( 0x1A, vm ); // invoke BIOS service if ( vm.regs.eax & 0xFF00 ) // search didn't succeed? { fprintf( stderr, "No Radeon engine\n" ); exit(1); } vm.regs.eax = 0xB10A; // PCI Read Config Dword vm.regs.edi = 0x0014; // Resource 1 base-address int86( 0x1A, vm ); // invoke BIOS service iobase = vm.regs.ecx & ~3; // store I/O Port-Address // memory-map the display-memory into user-space int vd = open( "/dev/vram", O_RDWR ); if ( vd < 0 ) { perror( "/dev/vram" ); exit(1); } int size = lseek( vd, 0, SEEK_END ); int prot = PROT_READ | PROT_WRITE; int flag = MAP_FIXED | MAP_SHARED; if ( mmap( (void*)vram, size, prot, flag, vd, 0 ) == MAP_FAILED ) { perror( "mmap" ); exit(1); } // enable non-canonical keyboard input struct termios orig_tty; tcgetattr( STDIN_FILENO, &orig_tty ); struct termios work_tty = orig_tty; work_tty.c_lflag &= ~( ECHO | ICANON ); work_tty.c_cc[ VTIME ] = 0; work_tty.c_cc[ VMIN ] = 1; tcsetattr( STDIN_FILENO, TCSAFLUSH, &work_tty ); // enter graphics mode vm.regs.eax = 0x4F02; // VESA Set Display Mode vm.regs.ebx = vesa_mode; // specify which mode int86( 0x10, vm ); // invoke BIOS service // clear the two screen-memory areas for (int y = 0; y < vres; y++) for (int x = 0; x < pitch; x++) vram[ y * pitch + x ] = black; // adjust the display pitch outl( CRTC_PITCH, iobase + 0 ); orig_pitch = inl( iobase + 4 ); work_pitch = orig_pitch * 2; outl( work_pitch, iobase + 4 ); // establish the bounds for 'srcnow' and 'dstnow' int srcmin = 0, srcmax = maxband - hres; int dstmin = 0, dstmax = pitch - hres; int srcnow = srcmin, dstnow = dstmax; // draw the initial dual images draw_bands( srcmin, dstmin, hres ); draw_bands( srcmin, dstmax, hres ); // main loop to implement the horizontal panning for(;;) { // await keypress by the user int inch = 0; read( STDIN_FILENO, &inch, sizeof( inch ) ); if ( inch == '\e' ) break; switch ( inch ) { case RIGHT_ARROW: // ignore request to move forward if already at max if ( srcnow >= srcmax ) continue; // make sure the window can be shifted to the right if ( dstnow == dstmax ) // cannot move to the right { dstnow = dstmin; // switch to the left view outl( CRTC_START, iobase + 0 ); outl( dstnow, iobase + 4 ); } // ok, now the window can be shifted to the right // 1. draw new bands at the right (beyond view) draw_bands( srcnow+hres, dstnow+hres, shiftsz ); // 2. shift the window-view to the right outl( CRTC_START, iobase + 0 ); outl( dstnow + shiftsz, iobase + 4 ); // 3. wait until the shift has taken effect while ( ( inb( 0x3DA )&8 ) == 0 ); // 4. draw new bands at the left (beyond view) draw_bands( srcnow+hres, dstnow, shiftsz ); // update the source and destination indexes srcnow += shiftsz; dstnow += shiftsz; break; case LEFT_ARROW: if ( srcnow <= srcmin ) continue; // already at start if ( dstnow == dstmin ) // cannot move to the left { dstnow = dstmax; // switch to the right view outl( CRTC_START, iobase + 0 ); outl( dstnow, iobase + 4 ); } // ok, now the window can be shifted to the left // update the source and destination indexes srcnow -= shiftsz; dstnow -= shiftsz; // 1. draw new bands at the left (beyond view) draw_bands( srcnow, dstnow, shiftsz ); // 2. shift the window-view to the left outl( CRTC_START, iobase + 0 ); outl( dstnow, iobase + 4 ); // 3. wait until the shift has taken effect while ( ( inb( 0x3DA )&8 ) == 0 ); // 4. draw new bands at the right (beyond view) draw_bands( srcnow, dstnow+hres, shiftsz ); break; } } // leave graphics mode vm.regs.eax = 0x0003; // VGA Set Display Mode int86( 0x10, vm ); // invoke BIOS service // restore canonical keyboard-input tcsetattr( STDIN_FILENO, TCSAFLUSH, &orig_tty ); // release the allocated memory free( vimage ); printf( "\n" ); }