#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_info } }; void vga_init( vga_t *vga, interrupt_t *interrupt, void *context ) { memset( vga, 0, sizeof( vga_t ) ); driver_init( (driver_t *)vga, DRIVER_TYPE_VIDEO, 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; } 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_info( void *obj ) { puts( "Generic VGA video driver" ); } 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 ); } vga_mode_t vga_make_mode( const vga_mode_type_t mode_type, const int x, const int y, const int color_depth ) { vga_mode_t mode; mode.mode_type = mode_type; mode.x = x; mode.y = y; mode.color_depth = color_depth; return mode; } // from http://files.osdev.org/mirrors/geezer/osd/graphics/modes.c static vga_mode_t modes[] = { { VGA_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 }, { VGA_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 }, { VGA_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 } }; bool vga_supports_mode( const vga_mode_t mode ) { for( int i = 0; modes[i].x != 0; i++ ) { if( mode.mode_type == modes[i].mode_type && mode.x == modes[i].x && mode.y == modes[i].y && mode.color_depth == modes[i].color_depth ) { return true; } } return false; } static int get_matching_mode_idx( const vga_mode_t mode ) { for( int i = 0; modes[i].x != 0; i++ ) { if( mode.mode_type == modes[i].mode_type && mode.x == modes[i].x && mode.y == modes[i].y && mode.color_depth == modes[i].color_depth ) { return i; } } return -1; } 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_set_mode( vga_t *vga, const vga_mode_t mode ) { // do not allow unknown timings! if( !vga_supports_mode( mode ) ) { return false; } vga_mode_t matching_mode = modes[get_matching_mode_idx( mode )]; write_registers( vga, matching_mode.regs ); vga->mode = mode; vga->mode.segment = get_frame_buffer_segment( vga ); vga->mode.segment_size = get_frame_buffer_segment_size( vga ); vga_use_z_buffer( vga, false ); return true; } vga_color_t vga_make_RGB( int R, int G, int B ) { vga_color_t c; c.R = R; c.G = G; c.B = B; return c; } const vga_color_t VGA_COLOR_BLACK = { 0x00, 0x00, 0x00 }; const vga_color_t VGA_COLOR_BLUE = { 0x00, 0x00, 0xAA }; const vga_color_t VGA_COLOR_GREEN = { 0x00, 0xAA, 0x00 }; const vga_color_t VGA_COLOR_CYAN = { 0x00, 0xAA, 0xAA }; const vga_color_t VGA_COLOR_RED = { 0xAA, 0x00, 0x00 }; const vga_color_t VGA_COLOR_MAGENTA = { 0xAA, 0x00, 0xAA }; const vga_color_t VGA_COLOR_BROWN = { 0xAA, 0x55, 0x00 }; const vga_color_t VGA_COLOR_LIGHT_GREY = { 0xAA, 0xAA, 0xAA }; const vga_color_t VGA_COLOR_WHITE = { 0xFF, 0xFF, 0xFF }; static bool is_same( const vga_color_t c1, const vga_color_t c2 ) { return c1.R == c2.R && c1.G == c2.G && c1.B == c2.B; } static uint8_t get_color_index( const vga_color_t c ) { if( is_same( c, VGA_COLOR_BLACK ) ) return 0x00; if( is_same( c, VGA_COLOR_BLUE ) ) return 0x01; if( is_same( c, VGA_COLOR_GREEN ) ) return 0x02; if( is_same( c, VGA_COLOR_CYAN ) ) return 0x03; if( is_same( c, VGA_COLOR_RED ) ) return 0x04; if( is_same( c, VGA_COLOR_MAGENTA ) ) return 0x05; if( is_same( c, VGA_COLOR_BROWN ) ) return 0x06; if( is_same( c, VGA_COLOR_LIGHT_GREY ) ) return 0x07; if( is_same( c, VGA_COLOR_WHITE ) ) return 0x3F; return 0x00; } static bool params_ok( vga_t *vga, const int x, const int y ) { if( x < 0 || x > vga->mode.x || y < 0 || y > vga->mode.y ) { return false; } return true; } void vga_set_pixel( vga_t *vga, const int x, const int y, const vga_color_t color ) { if( !params_ok( vga, x, y ) ) { kernel_panic( "Pixel coordinates are out of bounds: (%d, %d), resolution is only (%d, %d)", x, y, vga->mode.x, vga->mode.y ); } uint8_t color_idx = get_color_index( color ); uint8_t *addr = vga->base_addr + vga->mode.x * y + x; *addr = color_idx; } void vga_draw_rectangle( vga_t *vga, const int x, const int y, const int w, const int h, const vga_color_t color ) { 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->mode.x, vga->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->mode.x, vga->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->mode.x * yy + x; memset( addr, color_idx, w ); } } void vga_clear_screen( vga_t *vga, const vga_color_t color ) { memset( vga->base_addr, get_color_index( color ), vga->mode.x * vga->mode.y ); } void vga_draw_char( vga_t *vga, const unsigned char c, const int x, const int y, const vga_color_t background, const vga_color_t foreground ) { //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->mode.x * ( y + yy ) + x; memcpy( addr, data, vga_font.Width ); } } void vga_wait_for_retrace( vga_t *vga ) { while( port8_read( &vga->attribute_controller_reset_port ) & 0x08 ); while( !( port8_read( &vga->attribute_controller_reset_port ) & 0x08 ) ); } void vga_use_z_buffer( vga_t *vga, bool use ) { if( vga->use_z_buffer == use ) { return; } vga->use_z_buffer = use; if( vga->use_z_buffer ) { vga->zbuffer = (uint8_t *)malloc( vga->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.segment; } } void vga_refresh( vga_t *vga ) { if( vga->use_z_buffer ) { vga_wait_for_retrace( vga ); memcpy( vga->mode.segment, vga->zbuffer, vga->mode.segment_size ); } }