Vamos ahora a utilizar la extensión VESA de la BIOS para trabajar en modos SuperVGA con resoluciones como 640x480, 800x600 ó 1024x768, todas ellas a 256 colores. Ahora nos será de utilidad todo lo aprendido: llamada a interrupciones de la BIOS, aprovechamiento de la linealidad de la memoria y creación de nuestras propias rutinas gráficas.
Primero haremos un poco de historia del qué, cómo y porqué de la aparición de la VESA.
El comité VESA (Video Electronics Standards Association) creó una extensión VBE VESA que permitía esta compatibilidad. Los drivers VESA son código ejecutable que bien puede estar incluido en la BIOS de la tarjeta, o bien cargarse desde disco, quedándose residente (tal y como la famosa UNIVESA), de manera que la interrupción 10h (la interrupción de video en el ordenador, que conocimos en las primeras entregas) se amplía y pasa a ofrecernos nuevos servicios para inicializar modos SVGA, realizar cambios de bancos (que a continuación veremos), etc.
En un principio la versión más usada fue la VESA v1.2, aún disponible en muchos ordenadores como los 486, aunque los nuevos ordenadores Pentium ya comienzan a incluir eel driver VESA VBE v2.0, de manera que no haya que cargarlo desde disco.
Así que con nuestra extensión VESA en memoria, tenemos un nuevo set de funciones bajo nuestro control en la int 10h como las que se detallan en la tabla 1. Hay muchas otras funciones aparte de las descritas, pero vamos por ahora con las más importantes.
TABLA 1: Algunas funciones de la int10h para SVGA: ----------------------------------------------------------- INT 10h - SET SuperVGA VIDEO MODE AX = 4F02h BX = modo a inicializar Devuelve: AL = 4Fh función soportada. AH = estado: 00h Correcto. 01h Falló. ----------------------------------------------------------- INT 10h - GET CURRENT VIDEO MODE AX = 4F03h Devuelve: AL = 4Fh función soportada. AH = estado: 00h Correcto. BX = Modo actual. 01h Falló. ----------------------------------------------------------- INT 10h - CPU VIDEO MEMORY CONTROL AX = 4F05h BH = subfunción: 00h Cambiar banco de memoria. DX = Número de banco. 01h Leer banco actual. Devuelve: Banco actual. BL = Ventana hardware a elegir: 00h ventana A. 01h ventana B. Devuelve: AL = 4Fh función soportada. AH = estado: 00h Correcto. 01h Falló.Supongamos que queremos desarrollar una función que inicialice modos de video SVGA para posteriormente trabajar con ellos. Si miramos la tabla anterior observamos la subfunción 02h: Set SuperVGA video mode, que nos va a permitir esto. En AH hay que introducir el valor 4fh (indicador de servicio del driver VESA), en AL la subfunción (02h) y en BX el modo a inicializar. Veamos una posible función SetSVGAVideoMode():
SetSVGAVideoMode( int modo ) { asm { mov ax, 4f02h mov bx, [modo] int 21h } }El valor a introducir en BX puede verse en la tabla 2 y determina el modo SuperVGA que inicializar. Supongamos que deseamos inicializar el modo 800x600x256. Bastaría con ejecutar:
SetSVGAVideoMode( 0x102 );Por supuesto, antes de usar las funciones de la VESA deberemos asegurarnos de que esta está presente en el sistema, sus características (versión, etc.) y si el modo que queremos iniciar está soportado por ese driver VESA. Todo esto podemos hacerlo de igual manera por medio de distintos servicios de la int 10h, como el 00h (Get SVGA information) ó 01h (Get SVGA mode information), permitiendo leer en un buffer o estructura las características del driver VESA instalado y de los modos que soporta esa tarjeta, tal y como muestran los ejemplos del CD de la revista.
TABLA 2: Algunos modos SVGA: MODO - RESOLUCIóN ------------------- 100h - 640x400x256 101h - 640x480x256 102h - 800x600x16 103h - 800x600x256 104h - 1024x768x16 105h - 1024x768x256 106h - 1280x1024x16 107h - 1280x1024x256 108h - 80x60 texto 109h - 132x25 texto 10Ah - 132x43 texto 10Bh - 132x50 texto 10Ch - 132x60 texto 10Dh - 320x200x32K 10Eh - 320x200x64K 10Fh - 320x200x16M 110h - 640x480x32K 111h - 640x480x64K 112h - 640x480x16M 113h - 800x600x32K 114h - 800x600x64K 115h - 800x600x16M 116h - 1024x768x32K 117h - 1024x768x64K 118h - 1024x768x16M 119h - 1280x1024x32K 11Ah - 1280x1024x64K 11Bh - 1280x1024x16M ---------------------
Con un modo SVGA inicializado tenemos 2 soluciones: podemos utilizar la función de la BIOS Write Graphics Pixel (Función 0Ch de la int 10h) para representar pixels individuales (algo tediosamente lento como ya comentamos), o intentar averiguar cómo trabaja la tarjeta en SVGA para tratar de acceder a la VideoRAM de igual manera que hacíamos en modo 13h.
Para empezar hay una buena noticia y otra mala. La buena es que en los modos SVGA de 256 colores (los que nos interesan por ahora), el direccionamiento es lineal igual que en 320x200, de manera que podremos aplicar nuestros conocimientos a estas rutinas gráficas. La mala noticia radica en que nosotros sólo tenemos acceso al segmento 0xA000, que es nuestra ventana a la VideoMemoria de la tarjeta, y recordemos que este segmento tiene sólo 65.536 bytes.
Habrá quien se pregunte: ¿y en qué me afecta esto? Muy sencillo: cuando trabajábamos en 320x200x256 colores, al tener 256 colores utilizábamos 1 byte por pixel (0-255), de manera que 320x200x1 = 64.000 bytes. Con tan sólo los primeros 64.000 bytes del segmento 0xA000 (como en la figura 1) podíamos trabajar en toda la pantalla, o lo que es lo mismo, los modos estándar de la VGA no requerían más de 64KB de VideoRAM. Ahora pongamos el caso de trabajar en 640x480x256. Como es un modo de 1 byte por pixel (256 colores), necesitamos 640x480x1 = 307.200 bytes. Esta es la razón de que las tarjetas SVGA dispongan de 512KB, 1024KB o actualmente, hasta 4MB de VideoRAM. Necesitan ese espacio para almacenar los bytes que representan los pixels que hay en pantalla.
Como ya sabemos, nosotros sólo tenemos acceso a los 64K (65.536 bytes) que hay en el segmento 0xA000, que es nuestra ventana a la VideoRAM, de manera que en el modo 640x480x256 escribiendo bytes en este segmento tan sólo escribimos en la parte superior de la pantalla, tal y como puede verse en la figura 2,
lo que quiere decir que tan sólo podemos trabajar con las 65.536/640 = 102 líneas iniciales de una pantalla en SVGA. Esto puede comprobarse iniciando el modo 640x480x256 (modo 101h) y usando la orden:
pokeb( 0xA000, 65535, 15 );Con esa orden aparecerá un punto blanco aproximadamente en la línea 102 de la pantalla. Si reducimos el offset (ampliarse no se puede, recordemos el límite de la segmentación) obtendremos puntos más cercanos a (0,0), hasta el offset 0 que corresponde a este punto.
Pero está claro que debe haber una solución para este problema: si sólo disponemos de una ventana por la que ver un trocito"de la VideoRAM, bastaría pues con mover la ventana (en el caso de 320x200 esto no era necesario como puede verse en la figura 1). Para solucionar nuestro problema bastaría con decirle al ordenador que la desplace hacia abajo, para que podamos escribir en otra porción de la pantalla SVGA, tal y como muestra la figura 2. Este es el concepto de banco (Bank). Mediante la subfunción 05h de la VESA podemos cambiar el banco desde el 0 (el que está por defecto) a, por ejemplo, el 1, de manera que podríamos dibujar (escribiendo en 0xA000) a las líneas entre la 102 y la 204. A título de ejemplo, es como si dispusiéramos de 5 posiciones distintas donde colocar nuestra ventana para trabajar en las 5 partes que componen la pantalla. Ante el problema de disponer sólo de 64KB a los que acceder, los fabricantes lo solucionaron por medio de bancos (posiciones de la "ventana virtual), conmutando entre los distintos bancos según las coordenadas en que queramos poner el pixel (proceso conocido como Bank Switching, conmutación de bancos). Veamos pues una rutina de selección de bancos.
void SVGASwitchBank( int banco ) { asm { mov ax, 4f05h xor bx, bx mov dx, [banco] int 10h } }El posible valor del parámetro banco es un número entero que indica la "ventana" que queremos seleccionar dentro del total que hay para acceder a la VRAM. El número de ventanas que haya dependerá del modo gráfico en que estemos trabajando. En un modo de m*n pixels tendremos ((m*n)/65536) bancos, de manera que el último de ellos permite leer y escribir en las últimas líneas de la pantalla. En realidad el segmento 0A000h es una ventana móvil por la totalidad de la VRAM disponible en la tarjeta, de manera que al tratar de leer o escribir un pixel individual tendremos que tener en cuenta en qué banco cae (según sus coordenadas), cambiar a este banco y escribirlo en la posición correcta dentro del segmento 0A000h. Esto podemos verlo en el listado 3. El proceso en dicho listado es:
- Calcular el offset absoluto del pixel con (y*AnchoP)+x. - Calcular el banco en que reside el pixel (x,y) mediante (abs_offs/65536) y cambiar a éste por medio del driver VESA. - Calcular, dentro de ese banco, el offset que le corresponde en que escribir dentro de 0xA000 y escribir el pixel en memoria.
LISTADO 3: PutPixel en SVGA. void SVGAPutPixel( int x, int y, char color ) { int vga_offs; char banco; long abs_offs; abs_offs = (y*AnchoP)+x; banco = abs_offs/65536; vga_offs = abs_offs-(banco*65536); SVGASwitchBank( banco ); pokeb( 0xA000, vga_offs, color ); }Otras operaciones, como por ejemplo el borrado de pantalla, derivan de las primitivas anteriores: bastaría con seleccionar todos los bancos, uno por uno, y rellenar con un valor (color deseado) el segmento 0xA000 para cada banco.
Desde Banco=0 a MaxBanco SVGASwitchBank(Banco); Rellenar 0xA000 con <color>; Banco++; Fin DesdePor si aún hay dudas acerca del sistema de bancos, los ejemplos del CD son suficientemente explicativos para resolverlas, estando creados todos ellos para mostrar el proceso de Bank Switching con claridad.
Así pues, ya podemos pues realizar programas en SVGA adaptando las librerías al uso de la nueva función PutPixel() tal y como se ha hecho en los ejemplos.
El sistema operativo Windows 95 proporciona sus propias funciones de acceso a video y MicroSoft ha desarrollado las librerías DirectX para acelerar este acceso. Con la API anteriormente comentada (DirectDraw), podemos aplicar fácilmente cualquier proceso gráfico 2D en pantalla trabajando como lo veníamos haciendo hasta ahora, y dejando a la librería los procesos de representación, volcado de pantallas virtuales, y selección de modos de video, gracias a funciones como IDirectDraw::SetDisplayMode(), por ejemplo.
Bajo Linux es recomendable usar alguna de las librerías gráficas de que dispone, como la SVGALIB, que viene incluida con el sistema y proporciona todas las funciones gráficas necesarias. El sistema operativo Linux es uno de los más completos para los programadores, pues incluye compiladores, librerías, utilidades y código fuente en el mismo CD de instalación, tales como el gcc o g++, las VGA o SVGALIB, etc. El entorno gráfico X-Windows de Linux también incluye utilidades de creación, manuales, debugger y gráficos utilizando llamadas del propio servidor X.
El próximo artículo abordaremos los formatos gráficos más sencillos como el RAW, el SCR y el PCX para conocer su estructura completa y sus métodos de compresión, y es que los bitmaps pueden ser comprimidos ocupando un menor espacio y descomprimidos posteriormente para su utilización. De los principios básicos del almacenamiento de imágenes/bitmaps con y sin compresión nos ocuparemos en el próximo número.
Pulse aquí para bajarse los ejemplos y listados del artículo (17 Kb).
Figura 1: "VideoMemoria para modo 13h."
Figura 2: "VideoMemoria para modos SVGA."
Santiago Romero