#include "vga.h" #include "stdio.h" #include "stdlib.h" #include "kernel.h" #include "vga_font.h" #undef DEBUG static vga_vtable_t const vga_vtable = { { { vga_activate, vga_deactivate, vga_deinit, vga_print_name, vga_print_info }, video_register_mode, video_supports_mode, video_set_mode, vga_switch_mode, vga_set_pixel, vga_draw_rectangle, vga_clear_screen, vga_draw_char, vga_wait_for_retrace, vga_use_z_buffer, vga_refresh } }; // from http://files.osdev.org/mirrors/geezer/osd/graphics/modes.c static vga_mode_t modes[] = { { { VIDEO_MODE_TYPE_TEXT, 640, 480, 4 }, { /* MISC */ 0x67, /* SEQ */ 0x03, 0x00, 0x03, 0x00, 0x02, /* CRTC */ 0x5F, 0x4F, 0x50, 0x82, 0x55, 0x81, 0xBF, 0x1F, 0x00, 0x4F, 0x0D, 0x0E, 0x00, 0x00, 0x00, 0x50, 0x9C, 0x0E, 0x8F, 0x28, 0x1F, 0x96, 0xB9, 0xA3, 0xFF, /* GC */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0E, 0x00, 0xFF, /* AC */ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x0C, 0x00, 0x0F, 0x08, 0x00 }, NULL }, { { VIDEO_MODE_TYPE_GRAPHICS, 320, 200, 8 }, { /* MISC */ 0x63, /* SEQ */ 0x03, 0x01, 0x0F, 0x00, 0x0E, /* CRTC */ 0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0xBF, 0x1F, 0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9C, 0x0E, 0x8F, 0x28, 0x40, 0x96, 0xB9, 0xA3, 0xFF, /* GC */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F, 0xFF, /* AC */ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x41, 0x00, 0x0F, 0x00, 0x00 }, NULL }, { { VIDEO_MODE_TYPE_GRAPHICS, 640, 480, 4 }, { /* MISC */ 0xE3, /* SEQ */ 0x03, 0x01, 0x08, 0x00, 0x06, /* CRTC */ 0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0x0B, 0x3E, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEA, 0x0C, 0xDF, 0x28, 0x00, 0xE7, 0x04, 0xE3, 0xFF, /* GC */ 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x05, 0x0F, 0xFF, /* AC */ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x01, 0x00, 0x0F, 0x00, 0x00 }, NULL } }; void vga_init( vga_t *vga, interrupt_t *interrupt, void *context ) { memset( vga, 0, sizeof( vga_t ) ); puts( "Initializing driver for generic VGA video card.." ); video_init( (video_t *)vga, interrupt, context ); port8_init( &vga->misc_port, 0x3C2 ); port8_init( &vga->crtc_index_port, 0x3D4 ); port8_init( &vga->crtc_data_port, 0x3D5 ); port8_init( &vga->sequencer_index_port, 0x3C4 ); port8_init( &vga->sequencer_data_port, 0x3C5 ); port8_init( &vga->graphics_controller_index_port, 0x3CE ); port8_init( &vga->graphics_controller_data_port, 0x3CF ); port8_init( &vga->attribute_controller_index_port, 0x3C0 ); port8_init( &vga->attribute_controller_read_port, 0x3C1 ); port8_init( &vga->attribute_controller_write_port, 0x3C0 ); port8_init( &vga->attribute_controller_reset_port, 0x3DA ); ((driver_t *)vga)->vtable = (driver_vtable_t *)&vga_vtable; // register modes for( int i = 0; modes[i].base.x != 0; i++ ) { ((video_vtable_t *)(vga->base.base.vtable))->register_mode( vga, (const video_mode_t *)&modes[i] ); } } void vga_activate( void *obj ) { puts( "Activating driver for generic VGA video card.." ); } void vga_deactivate( void *obj ) { puts( "Dectivating driver for generic VGA video card.." ); } void vga_deinit( void *obj ) { vga_t *vga = (vga_t *)obj; if( vga->zbuffer != NULL ) { free( vga->zbuffer ); vga->zbuffer = NULL; } } void vga_print_name( void *obj ) { puts( "Generic VGA video driver" ); } void vga_print_info( void *obj ) { // TODO: print avaiable modes, colors, resolutions } static void write_registers( vga_t *vga, uint8_t *regs ) { // misc port8_write( &vga->misc_port, *( regs++ ) ); // seq (5) for( uint8_t i = 0; i < 5; i++ ) { port8_write( &vga->sequencer_index_port, i ); port8_write( &vga->sequencer_data_port, *( regs++ ) ); } // unlock CRTC register (and make sure mode data doesn't lock it again by accident) port8_write( &vga->crtc_index_port, 0x03 ); port8_write( &vga->crtc_data_port, port8_read( &vga->crtc_data_port ) | 0x80 ); port8_write( &vga->crtc_index_port, 0x11 ); port8_write( &vga->crtc_data_port, port8_read( &vga->crtc_data_port ) & ~0x80 ); regs[0x3] |= 0x80; regs[0x11] &= ~0x80; // crtc (25) for( uint8_t i = 0; i < 25; i++ ) { port8_write( &vga->crtc_index_port, i ); port8_write( &vga->crtc_data_port, *( regs++ ) ); } // gc (9) for( uint8_t i = 0; i < 9; i++ ) { port8_write( &vga->graphics_controller_index_port, i ); port8_write( &vga->graphics_controller_data_port, *( regs++ ) ); } // ac (21) for( uint8_t i = 0; i < 21; i++ ) { (void)port8_read( &vga->attribute_controller_reset_port ); port8_write( &vga->attribute_controller_index_port, i ); port8_write( &vga->attribute_controller_write_port, *( regs++ ) ); } // lock 16-color palette and unblank display (void)port8_read( &vga->attribute_controller_reset_port ); port8_write( &vga->attribute_controller_index_port, 0x20 ); } static uint8_t *get_frame_buffer_segment( vga_t *vga ) { port8_write( &vga->graphics_controller_index_port, 0x06 ); uint8_t segment_no = ( port8_read( &vga->graphics_controller_data_port ) >> 2 ) & 0x03; uint8_t *segment = 0x0; switch( segment_no ) { case 0: // A0000h - BFFFFh segment = (uint8_t *)0xA0000; break; case 1: // A0000h - AFFFFh segment = (uint8_t *)0xA0000; break; case 2: // B0000h - B7FFFh segment = (uint8_t *)0xB0000; break; case 3: // B8000h - BFFFFh segment = (uint8_t *)0xB8000; break; } #ifdef DEBUG printf( "segment: %d 0x%X\n", segment_no, segment ); #endif return segment; } static size_t get_frame_buffer_segment_size( vga_t *vga ) { port8_write( &vga->graphics_controller_index_port, 0x06 ); uint8_t segment_no = ( port8_read( &vga->graphics_controller_data_port ) >> 2 ) & 0x03; size_t segment_size = 0; switch( segment_no ) { case 0: // 128k segment_size = 128 * 1024; break; case 1: // 64k segment_size = 64 * 1024; break; case 2: // 32k case 3: segment_size = 32 * 1024; break; } #ifdef DEBUG printf( "segment size: %d 0x%X\n", segment_no, segment_size ); #endif return segment_size; } bool vga_switch_mode( void *obj, const video_mode_t *mode ) { vga_t *vga = (vga_t *)obj; vga_mode_t *vga_mode = (vga_mode_t *)mode; write_registers( vga, vga_mode->regs ); vga_mode->segment = get_frame_buffer_segment( vga ); vga_mode->segment_size = get_frame_buffer_segment_size( vga ); vga->base.mode = mode; vga_use_z_buffer( vga, false ); return true; } static bool is_same( const video_rgb_color_t c1, const video_rgb_color_t c2 ) { return c1.R == c2.R && c1.G == c2.G && c1.B == c2.B; } static uint8_t get_color_index( const video_rgb_color_t c ) { if( is_same( c, VIDEO_RGB_COLOR_BLACK ) ) return 0x00; if( is_same( c, VIDEO_RGB_COLOR_BLUE ) ) return 0x01; if( is_same( c, VIDEO_RGB_COLOR_GREEN ) ) return 0x02; if( is_same( c, VIDEO_RGB_COLOR_CYAN ) ) return 0x03; if( is_same( c, VIDEO_RGB_COLOR_RED ) ) return 0x04; if( is_same( c, VIDEO_RGB_COLOR_MAGENTA ) ) return 0x05; if( is_same( c, VIDEO_RGB_COLOR_BROWN ) ) return 0x06; if( is_same( c, VIDEO_RGB_COLOR_GRAY ) ) return 0x07; if( is_same( c, VIDEO_RGB_COLOR_WHITE ) ) return 0x3F; return 0x00; /* 0x8 0x38 21,21,21 85,85,85 #555555 dark gray 0x9 0x39 21,21,63 85,85,255 #5555ff bright blue 0xA 0x3A 21,63,21 85,255,85 #55ff55 bright green 0xB 0x3B 21,63,63 85,255,255 #55ffff bright cyan 0xC 0x3C 63,21,21 255,85,85 #ff5555 bright red 0xD 0X3D 63,21,63 255,85,255 #ff55ff bright magenta 0xE 0x3E 63,63,21 255,255,85 #ffff55 yellow */ } static bool params_ok( vga_t *vga, const int x, const int y ) { if( x < 0 || x > vga->base.mode->x || y < 0 || y > vga->base.mode->y ) { return false; } return true; } void vga_set_pixel( void *obj, const int x, const int y, const video_rgb_color_t color ) { vga_t *vga = (vga_t *)obj; if( !params_ok( vga, x, y ) ) { kernel_panic( "Pixel coordinates are out of bounds: (%d, %d), resolution is only (%d, %d)", x, y, vga->base.mode->x, vga->base.mode->y ); } uint8_t color_idx = get_color_index( color ); uint8_t *addr = vga->base_addr + vga->base.mode->x * y + x; *addr = color_idx; } void vga_draw_rectangle( void *obj, const int x, const int y, const int w, const int h, const video_rgb_color_t color ) { vga_t *vga = (vga_t *)obj; if( !params_ok( vga, x, y ) ) { kernel_panic( "Rectangle start coordinates are out of bounds: (%d, %d), resolution is only (%d, %d)", x, y, vga->base.mode->x, vga->base.mode->y ); } if( !params_ok( vga, x + w, y + h ) ) { kernel_panic( "Rectangle end coordinates are out of bounds: (%d, %d), resolution is only (%d, %d)", x + w, y + h, vga->base.mode->x, vga->base.mode->y ); } uint8_t color_idx = get_color_index( color ); for( int yy = y; yy < y + h; yy++ ) { uint8_t *addr = vga->base_addr + vga->base.mode->x * yy + x; memset( addr, color_idx, w ); } } void vga_clear_screen( void *obj, const video_rgb_color_t color ) { vga_t *vga = (vga_t *)obj; memset( vga->base_addr, get_color_index( color ), vga->base.mode->x * vga->base.mode->y ); } void vga_draw_char( void *obj, const unsigned char c, const int x, const int y, const video_rgb_color_t background, const video_rgb_color_t foreground ) { vga_t *vga = (vga_t *)obj; //const struct bitmap_font font = { /* .Width = 8, .Height = 16, .Chars = 2899, .Widths = __font_widths__, .Index = __font_index__, .Bitmap = __vga_font_bitmap__, */ // assuming font width is 8 int mask[8] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }; // for now, the 34 entry, character height is 16, assuming A-Z const unsigned char *bmap = &vga_font.Bitmap[(c-32)*16]; uint8_t fg_color_idx = get_color_index( foreground ); uint8_t bg_color_idx = get_color_index( background ); // should be dynamic and vga_font.Width uint8_t data[8]; for( int yy = 0; yy < vga_font.Height; yy++ ) { memset( data, bg_color_idx, sizeof( data ) ); for( int xx = 0; xx < vga_font.Width; xx++ ) { if( bmap[yy] & mask[xx] ) { data[vga_font.Width-1-xx] = fg_color_idx; } } uint8_t *addr = vga->base_addr + vga->base.mode->x * ( y + yy ) + x; memcpy( addr, data, vga_font.Width ); } } void vga_wait_for_retrace( void *obj ) { vga_t *vga = (vga_t *)obj; while( port8_read( &vga->attribute_controller_reset_port ) & 0x08 ); while( !( port8_read( &vga->attribute_controller_reset_port ) & 0x08 ) ); } void vga_use_z_buffer( void *obj, bool use ) { vga_t *vga = (vga_t *)obj; vga->use_z_buffer = use; if( vga->use_z_buffer ) { vga->zbuffer = (uint8_t *)malloc( ((vga_mode_t *)(vga->base.mode))->segment_size ); vga->base_addr = &vga->zbuffer[0]; } else { if( vga->zbuffer != NULL ) { free( vga->zbuffer ); vga->zbuffer = NULL; } vga->base_addr = ((vga_mode_t *)(vga->base.mode))->segment; } } void vga_refresh( void *obj ) { vga_t *vga = (vga_t *)obj; if( vga->use_z_buffer ) { vga_wait_for_retrace( vga ); memcpy( ((vga_mode_t *)(vga->base.mode))->segment, vga->zbuffer, ((vga_mode_t *)(vga->base.mode))->segment_size ); } }