//------------------------------------------------------------------- // drawline.cpp // // This program shows how the ATI Radeon 2D graphics engine is // programmed to draw a straight line in video display memory. // (Other vendors will implement this capability differently.) // We studied source-code for Linux's ATI Radeon device-driver // to learn the details about this device's various registers. // // compile using: $ g++ drawline.cpp int86.cpp -o drawline // // Linux-references: /usr/src/linux/include/video/radeon.h // /usr/src/linux/drivers/video/aty/radeon_accel.h // /usr/src/linux/drivers/video/aty/radeon_accel.c // // programmer: ALLAN CRUSE // written on: 04 JUL 2005 // revised on: 06 OCT 2005 //------------------------------------------------------------------- #include // for printf(), perror() #include // for exit() #include // for inl(), outl() #include // for nanosleep() #include "int86.h" // for init8086(), int86() #define VENDOR_ID 0x1002 // Advanced Technologies, Inc. #define DEVICE_ID 0x5B60 // ATI Radeon X300 #define MM_INDEX 0x0000 // Graphics Engine Addr-Port #define MM_DATA 0x0004 // Graphics Engine Data-Port #define RBBM_STATUS 0x0E40 #define DP_GUI_MASTER_CNTL 0x146C #define DP_BRUSH_FRGD_CLR 0x147C #define DP_BRUSH_BKGD_CLR 0x1478 #define DP_WRITE_MSK 0x16CC #define DP_DATATYPE 0x16C4 #define DST_32BPP 0x00000006 #define BRUSH_SOLIDCOLOR 0x00000D00 #define GMC_DST_32BPP 0x00000600 #define GMC_SRC_DATATYPE_COLOR 0x00003000 #define GMC_BRUSH_SOLID_COLOR 0x000000D0 #define ROP3_PATCOPY 0x00F00000 #define DST_LINE_START 0x1600 #define DST_LINE_END 0x1604 #define RB2D_DSTCACHE_MODE 0x3428 #define GMC_CLR_CMP_CNTL_DIS (1<<28) #define RB2D_DSTCACHE_CTLSTAT 0x342C #define RB3D_CNTL 0x1C3C #define RB2D_DC_FLUSH_ALL 0xF #define RB2D_DC_BUSY (1<<31) #define MC_FB_LOCATION 0x0148 #define DST_PITCH_OFFSET 0x142C #define SRC_PITCH_OFFSET 0x1428 #define DEFAULT_PITCH_OFFSET 0x16E0 #define DEFAULT_SC_BOTTOM_RIGHT 0x16E8 #define DEFAULT_SC_TOP_LEFT 0x16EC int vesa_mode = 0x121, hres = 640, vres = 480, iobase; struct vm86plus_struct vm; struct timespec us = { 0, 1000 }; int return_radeon_port_address( void ) { unsigned int radeonX300 = (DEVICE_ID<<16)|(VENDOR_ID<<0); // we use BIOS services to discover the Radeon's I/O port vm.regs.eax = 0xB101; // PCI Detect BIOS presence int86( 0x1A, vm ); // invoke ROM-BIOS service if ( ( vm.regs.eax & 0xFF00 ) != 0x0000 ) return 0; for (int i = 0; i < 0x10000; i++) { vm.regs.eax = 0xB103; // PCI Find Device Class vm.regs.ecx = 0x030000; // SVGA Device-Type Code vm.regs.esi = i; // device index-number int86( 0x1A, vm ); // invoke BIOS service if ( ( vm.regs.eax & 0xFF00 ) == 0x8600 ) break; vm.regs.eax = 0xB10A; // PCI Read Config Dword vm.regs.edi = 0; // Config Registers 0-3 int86( 0x1A, vm ); // invoke BIOS service if ( ( vm.regs.eax & 0xFF00 ) == 0x8600 ) break; if ( vm.regs.ecx != radeonX300 ) continue; vm.regs.eax = 0xB10A; // PCI Read Config Dword vm.regs.edi = 20; // Config Registers 20..23 int86( 0x1A, vm ); // invoke BIOS service if ( ( vm.regs.eax & 0xFF00 ) == 0x8600 ) break; if ( vm.regs.ecx & 1 ) return vm.regs.ecx & 0xFFFC; } return 0; } void radeon_fifo_wait( int entries ) { for (int i = 0; i < 2000000; i++) { outl( RBBM_STATUS, iobase + MM_INDEX ); int rbbm_status = inl( iobase + MM_DATA ); if ( (rbbm_status & 0x7F) >= entries ) return; nanosleep( &us, NULL ); } vm.regs.eax = 0x0003; int86( 0x10, vm ); fprintf( stderr, "\nradeon FIFO timeout!!\n\n" ); exit(1); } void draw_solid_line( int x0, int y0, int x1, int y1, int color ) { // setup the drawing processor's parameters int line_color = color; int sx = x0, sy = y0; // starting point int dx = x1, dy = y1; // ending point int deltax = dx - sx; int deltay = dy - sy; //------------------------------------------------------ // now we draw a straight line with the graphics engine //------------------------------------------------------ // wait for 4 FIFO entries radeon_fifo_wait( 4 ); // program the DP_GUI_MASTER_CNTL register int dp_gui_master_cntl; outl( DP_GUI_MASTER_CNTL, iobase + MM_INDEX ); dp_gui_master_cntl = inl( iobase + MM_DATA ); dp_gui_master_cntl |= GMC_CLR_CMP_CNTL_DIS; dp_gui_master_cntl |= GMC_DST_32BPP; dp_gui_master_cntl |= GMC_SRC_DATATYPE_COLOR; dp_gui_master_cntl |= GMC_BRUSH_SOLID_COLOR; dp_gui_master_cntl |= ROP3_PATCOPY; outl( DP_GUI_MASTER_CNTL, iobase + MM_INDEX ); outl( dp_gui_master_cntl, iobase + MM_DATA ); // set the foreground brush-color registers outl( DP_BRUSH_FRGD_CLR, iobase + MM_INDEX ); outl( line_color, iobase + MM_DATA ); // Not effective // set the background brush-color registers outl( DP_BRUSH_BKGD_CLR, iobase + MM_INDEX ); outl( 0, iobase + MM_DATA ); // set the default write mask register outl( DP_WRITE_MSK, iobase + MM_INDEX ); outl( ~0, iobase + MM_DATA ); // wait for 2 FIFO entries radeon_fifo_wait( 2 ); // setup the line's start-location register int start_locn = (sy<<16)|sx; outl( DST_LINE_START, iobase + MM_INDEX ); outl( start_locn, iobase + MM_DATA ); // setup the line's end-location register int end_locn = (dy<<16)|dx; outl( DST_LINE_END, iobase + MM_INDEX ); outl( end_locn, iobase + MM_DATA ); } void radeon_engine_flush( void ) { // initiate flush outl( RB2D_DSTCACHE_CTLSTAT, MM_INDEX ); outl( RB2D_DC_FLUSH_ALL, MM_DATA ); // wait until status is idle for (int i = 0; i < 2000000; i++) { outl( RB2D_DSTCACHE_CTLSTAT, MM_INDEX ); if ( !( inl( MM_DATA )&RB2D_DC_BUSY ) ) return; nanosleep( &us, NULL ); } vm.regs.eax = 0x0003; int86( 0x10, vm ); fprintf( stderr, "\nradeon FLUSH timeout!!\n\n" ); exit(1); } void radeon_engine_idle( void ) { // ensure FIFO is empty before waiting for idle radeon_fifo_wait( 64 ); for (int i = 0; i < 2000000; i++) { outl( RBBM_STATUS, iobase + MM_INDEX ); int rbbm_status = inl( iobase + MM_DATA ); if ( ( rbbm_status & RB2D_DC_BUSY ) == 0 ) { radeon_engine_flush(); return; } nanosleep( &us, NULL ); } vm.regs.eax = 0x0003; int86( 0x10, vm ); fprintf( stderr, "\nradeon IDLE timeout!!\n\n" ); exit(1); } void initialize_drawing_engine( void ) { // here we follow the Linux driver's steps // 1. disable the 3D engine outl( RB3D_CNTL, iobase + MM_INDEX ); outl( 0, iobase + MM_DATA ); // 2. reset the radeon frame-buffer engine // 2a. initiate flush radeon_engine_flush(); // 2b. setup RB2D_DSTCACHE_MODE (for certain chips) radeon_fifo_wait( 1 ); outl( RB2D_DSTCACHE_MODE, iobase + MM_INDEX ); outl( 0, iobase + MM_DATA ); // 2c. we reread MC_FB_LOCATION from card as it may have // been modified by XFree drivers radeon_fifo_wait( 4 ); outl( MC_FB_LOCATION, iobase + MM_INDEX ); int fb_local_base = inl( iobase + MM_DATA )<<16; int pitch = hres / 16; int pitch_offset = (fb_local_base >> 10); pitch_offset &= 0x003FFFFF; pitch_offset |= (pitch << 22); outl( DEFAULT_PITCH_OFFSET, iobase + MM_INDEX ); outl( pitch_offset, iobase + MM_DATA ); outl( DST_PITCH_OFFSET, iobase + MM_INDEX ); outl( pitch_offset, iobase + MM_DATA ); outl( SRC_PITCH_OFFSET, iobase + MM_INDEX ); outl( pitch_offset, iobase + MM_DATA ); // 2d. set the DP_DATATYPE register radeon_fifo_wait( 1 ); int dp_datatype = DST_32BPP | BRUSH_SOLIDCOLOR; outl( DP_DATATYPE, iobase + MM_INDEX ); outl( dp_datatype, iobase + MM_DATA ); // 2f. We setup the scissors registers radeon_fifo_wait( 2 ); int sc_top_left = (0x0000 << 16)|(0x0000<<0); int sc_bottom_right = (0x1FFF << 16)|(0x1FFF<<0); outl( DEFAULT_SC_TOP_LEFT, iobase + MM_INDEX ); outl( sc_top_left, iobase + MM_DATA ); outl( DEFAULT_SC_BOTTOM_RIGHT, iobase + MM_INDEX ); outl( sc_bottom_right, iobase + MM_DATA ); } int main( int argc, char **argv ) { // we use ROM-BIOS services to discover the Radeon's I/O port init8086(); // setup access to BIOS and I/O ports iobase = return_radeon_port_address(); if ( iobase ) printf( "\e[H\e[JRadeon I/O port: %04X \n", iobase ); else { fprintf( stderr, "Radeon not found\n" ); exit(1); } getchar(); // enter graphics mode vm.regs.eax = 0x4F02; vm.regs.ebx = vesa_mode; int86( 0x10, vm ); // initialize the 2D graphics engine initialize_drawing_engine(); // draw a window border (red) draw_solid_line( 0, 0, 0, vres-1, 0x00FF0000 ); draw_solid_line( 0, 0, hres-1, 0, 0x00FF0000 ); draw_solid_line( hres-1, vres-1, 0, vres-1, 0x00FF0000 ); draw_solid_line( hres-1, vres-1, hres-1, 0, 0x00FF0000 ); getchar(); // draw a window border (yellow) draw_solid_line( 0, 0, 0, vres-1, 0x00FFFF00 ); draw_solid_line( 0, 0, hres-1, 0, 0x00FFFF00 ); draw_solid_line( hres-1, vres-1, 0, vres-1, 0x00FFFF00 ); draw_solid_line( hres-1, vres-1, hres-1, 0, 0x00FFFF00 ); getchar(); // draw a slanted line (purple) draw_solid_line( 120, 90, 520, 390, 0x00FF00FF ); getchar(); // draw some random lines in random colors for (int i = 0; i < 100; i++) { int color = rand() % 0x1000000; int x0 = rand() % hres; int y0 = rand() % vres; int x1 = rand() % hres; int y1 = rand() % vres; draw_solid_line( x0, y0, x1, y1, color ); } getchar(); // leave graphics mode vm.regs.eax = 0x4F02; vm.regs.ebx = 0x0003; int86( 0x10, vm ); printf( "\n" ); }