TALLER DE DISEÑO EN SISTEMAS DIGITALES I

Introducción a la Programación Gráfica

por Iván Brito Echeverría




El siguiente artículo busca continuar con el trabajo desarrollado el semestre anterior en donde se especificaron las caracteristicas básicas del adaptador de display, en particular del adaptador CGA, sus técnicas de generación de caractéres, el controlador del CRT,etc. En dicho trabajo se menciona el acceso a los distinos adaptadores de display mediante los servicios de atención a interrupciones de la BIOS, en particular la interrupción 10h, cosa que en este artículo se enfatizará y se darán las bases de la programación de gráficos mediante uso de lenguajes de alto nivel ,como ANSI C, y de assembler .


INTRODUCCION

Comenzaremos por nombrar la "jerga gráfica" que nos ayudará a comprender los primeros aspectos de la programación de los modos gráficos. Son conceptos fundamentales resultando por ello muy intuitivos y de fácil comprensión:

Modos alfanuméricos: En los modos alfanuméricos (modos de texto), la pantalla está dividida en una serie de celdillas donde sólo cabe un carácter (tanto un carácter alfabético como un número -de ahí el término alfanumérico-), proporcionándonos resoluciones generalmente reducidas, como por ejemplo 80x25, que quiere decir que tenemos la pantalla limitada a 80 caracteres por línea horizontal y con 25 líneas en el total visualizable. Este tipo de limitaciones es el que nos queremos saltar pasando a programar en los modos gráficos, en contraposición a los de texto. Cualquier forma gráfica en estos modos tiene que ser creada a base de combinaciones de símbolos ASCII.

Pixel: Cuando ejecutamos un programa en modo gráfico, mirando a la pantalla podemos darnos cuenta de que el dibujo ahi representado está formado por una serie de unidades mínimas (unos pequeños puntos cuadrados), cada una de ellas con un color, y con la acumulación de las cuales se forman las figuras gráficas que vemos representadas en el monitor.

La palabra pixel viene de la abreviación del término inglés PEL -picture element- (elemento del dibujo), y representa la unidad mínima capaz de ser representada por el monitor en esa resolución.

Resolución: Acabamos de nombrar la palabra resolución en la definición de pixel, y es que en la pantalla caben un número determinado de pixels (o puntos) a lo ancho y a lo alto de ella. Así, podemos ver que, en un determinado juego, en la pantalla caben 320 pixels a lo ancho y 200 a lo alto mientras en Windows pueden caben por ejemplo 640x480 pixels en la misma pantalla.

La resolución de un modo de vídeo, especificado en el formato (ej: 640x480) nos indica la cantidad de pixels que caben en la pantalla tanto a lo ancho (640 en este caso) como a lo alto (480), lo que quiere decir que en la pantalla hay un total de 640x480 pixels = 307.200 pixels (en nuestro ejemplo). Queda claro pués que a mayor resolución, como el tamaño del monitor es siempre el mismo, los puntos serán más pequeños y conseguiremos una mayor calidad gráfica, mientras que a resoluciones menores (ej: 320x200) es más fácil notar el tamaño de los puntos y la calidad representada será menor. Además mientras mayor resolución queramos mayor memoria de video necesitaremos, por ejemplo:

En modo 320x200 a 256 colores = 320x200 = 64000 direcciones , o sea , 64 KB (justo el tamaño de un segmento!)
ya que 256 colores de codifican en 8 bits=1byte

Coordenadas de pantalla: cuando necesitemos localizar en pantalla una posición específica (por ejemplo: un punto), lo haremos a partir de sus coordenadas, que consisten en dos números que indican una posición respecto a las coordenadas <0,0> (parte superior izquierda de la pantalla).

Supongamos que en el modo 320x200 queremos localizar el centro de la pantalla. Este será, obviamente, el punto (160,100), puesto que ambos números nos indican la posición horizontal primero y luego vertical considerando la pantalla como un eje XY. El último punto de la pantalla tendría en este caso las coordenadas (319,199).

Modos gráficos: Cada modo gráfico se caracteriza por su resolución y su número de colores. Así, tenemos desde el modo gráfico 320x200x256 (256 colores) hasta, por ejemplo, el 1024x768x16M (con 16 Millones de colores). Nuestra tarjeta gráfica es capaz de inicializar todos estos distintos modos, y podremos elegir el más conveniente a nuestros propósitos para usarlo en nuestro programa. Queda claro entonces que, cuanto más nueva y potente sea nuestra tarjeta gráfica (que va instalada dentro del ordenador), de más modos gráficos dispone, de tal manera que sería imposible inicializar el modo 800x600x256 colores en una tarjeta gráfica que no sea SVGA (Super VGA). Cuando nos dispongamos a crear un programa, tendremos que decidir primero en que modo gráfico vamos a realizarlo, debido a que cada modo se programa de una manera y, como es obvio, a menor resolución y menor número de colores, el programa resultante será más rápido. Con respecto al término modo gráfico, que no sea un modo de texto no quiere decir que no podamos representar información textual, pués construiremos los carácteres a base de pixels, ya que cualquier carácter es en sí mismo un símbolo gráfico.


Figura 1: formato típico de lineas en modo texto.

Paleta: Llamaremos paleta (por ahora) al conjunto de colores que posee un modo gráfico (2, 16, 256, 32.000 ó 16 millones, por ejemplo) y ya entraremos más adelante en cómo modificar este juego de colores de que disponemos para adaptar los colores existentes a las tonalidades deseadas.

Dada la inmensidad de estándares gráficos existentes para los ordenadores compatibles, que sucedieron al primer adaptador que sólo soportaba texto (MDA), y que de hecho llenan varias estanterías en las librerías, sólo se tratará de una manera general el tema para dedicarnos a estudiar un en particular, el VGA. Se considerarán los estándares más comunes, con algunos ejemplos de programación de VGA en ensamblador. Las tarjetas gráficas tradicionales administran normalmente entre 16 Kb y 1 Mb de memoria de vídeo, en el segmento 0B800h las CGA/Hércules y en 0A000h las VGA. En los modos de vídeo que precisan más de 64 Kb se recurre a técnicas especiales, tales como planos de bits para los diferentes colores, o bien dividir la pantalla en pequeños fragmentos que se seleccionan en un puerto E/S. Las tarjetas EGA y posteriores vienen acompañadas de una extensión ROM que parchea la BIOS normal del sistema para añadir soporte al nuevo sistema de vídeo.


LA MEMORIA DE VIDEO

Servicios de la BIOS:

La ROM-BIOS del PC es una zona de memoria no modificable (ROM) en la cual disponemos de una serie de funciones y servicios para su uso por el sistema operativo (y utilizables por el programador), que gestionan accesos a disco, control de la tarjeta de video, gestión de impresora, etc. Estas funciones son las llamadas interrupciones software y proporcionan al programador control sobre el hardware del PC.
La manera de llamar a una de estas interrupciones, en lenguaje assembler, consiste en preparar todos los parámetros que ésta requiera (cargar los registros del PC con los valores adecuados) y ejecutar la llamada a la interrupción. Es necesario por tanto saber mover valores a los registros AX, BX, CX, etc... (mov registro, valor) y llamar a la interrupción deseada (int numero_de_int).
Supongamos el ejemplo concreto de inicializar el modo de video 320x200 a 256 colores. Si miramos en la tabla 1 (funciones más importantes de la int 10h) veremos que la interrupción 10h (gestión de la tarjeta gráfica) posee un servicio para inicializar modos de video (servicio 0). Cargamos los registros tal y como los pide la interrupción ( AH = servicio, AL = Modo ), y efectuamos la llamada:
 

  mov ah, 0        /* AH=0: Init VideoMode */
  mov al, 13h      /* modo: 13h */
  int 10h          /* llamada a int 10h */
 

Simplemente conociendo los parámetros que necesita cada interrupción y servicio podemos utilizar cualquiera de las funciones de bajo nivel de que nos provee la BIOS del PC. Esta interrupción en concreto, la interrupción 10h, es la llamada interrupción de video, y nos permite inicializar modos de video (servicio 0), poner pixels en pantalla (servicio 0Ch), leer el valor de un pixel (servicio 0Dh), cambiar la paleta disponible (desde AX=1000h a 101Bh) y otros servicios a disposición del programador. Aparte de la interrupción 10h existen más interrupciones con sus correspondientes servicios (33h=servicios del ratón; 16h=servicios de teclado, etc...), que nos harán el proceso de creación mucho más sencillo.

 TABLA 1: Algunos servicios de la int 10h.

-------------------------------------------------------------------------
|  PARÁMETROS   |    SERVICIO     |       FUNCIÓN QUE REALIZA           |
-------------------------------------------------------------------------
| AH = 00h      | Set Video Mode  | Inicializa el modo de video especi- |
| AL = Modo     |                 | ficado en AL, segun los valores de  |
|               |                 | la tabla 2.                         |
------------------------------------------------------------------------- 
| AH = 0Ch      |   Write Pixel   | Dibuja el pixel (CX,DX) en pantalla |
| CX = Coord. X |                 | con el color Al. El parámetro página|
| DX = Coord. Y |                 | debe ser 0 en 13h ya que sólo hay 1 |
| AL = Color    |                 | página de video y esa es la 0.      |
| BH = Página   |                 |                                     |
-------------------------------------------------------------------------
| AH = 0Dh      |    Read Pixel   | Devuelve en Al el color del pixel   |
| CX = Coord. X |                 | de la posición (CX,DX).             |
| DX = Coord. Y |                 |                                     |
| BH = Página   |                 |                                     |
| Devuelve:     |                 |                                     | 
| AL = Color    |                 |                                     |
-------------------------------------------------------------------------
| AH = 0Fh      | Get Video Mode  | Devuelve en AL el modo de video     |
|  Devuelve:    |                 | actual.                             |
| AL = Modo     |                 |                                     |
-------------------------------------------------------------------------
| AX = 1001h    | Set Border Color| Cambia el color del borde de la pan-|
| BH = Color    |                 | talla al color BH (por defecto es 0,|
|               |                 | equivalente al negro).              |
-------------------------------------------------------------------------

En la tabla 2 disponemos del listado de los modos de video que pueden ser inicializados en una VGA estándar mediante el servicio 0 de la interrupción 10h. Es importante volver del modo gráfico al anterior en el cual se ejecutó nuestro programa,
generalmente el modo texto 03h.
 TABLA 2: Modos de vídeo de la BIOS.

----------------------------------------------------------------------
 MODO     TIPO     RESOLUCIÓN    COLORES    VRAM        SISTEMA
----------------------------------------------------------------------
 00h      Texto      40x25         16       B800h    CGA/MCGA/EGA/VGA
 02h      Texto      80x25      16 grises   B800h    CGA/MCGA/EGA/VGA
 03h      Texto      80x25         16       B800h    CGA/MCGA/EGA/VGA
 04h     Gráfico    320x200         4       B800h    CGA/MCGA/EGA/VGA
 06h     Gráfico    640x200         2       B800h    CGA/MCGA/EGA/VGA
 07h      Texto      80x25        mono      B000h    MDA/Herc/EGA/VGA
 0Dh     Gráfico    320x200         16      A000h         EGA/VGA
 0Eh     Gráfico    640x200         16      A000h         EGA/VGA
 10h     Gráfico    640x350         16      A000h     EGA/VGA (256Kb)
 12h     Gráfico    640x480         16      A000h           VGA
 13h     Gráfico    320x200        256      A000h        MCGA/VGA
------------------------------------------------------------------------

Aún  cuando disponemos de interrupciones estamos limitados a las funcionen que esten disponibles en la BIOS del PC.
Para lograr hacer nuestras propias rutinas de servicios de interrupcion debemos ver como se organiza la memoria para los distintos tipos de modos.
La organización de la memoria (entrelazado, planos de bit o lineal) es la manera en que se direcciona la memoria de vídeo por parte de la CPU. Por ejemplo, en el modo 06h, cada pixel de la pantalla está asociado a un bit (8 pixels por byte) a partir de la dirección B800:0000; sin embargo, cuando se recorren 80 bytes en la memoria (640 bits o pixels,  primera  líneacompleta) no se pasa a la segunda línea de la pantalla sino unas cuantas más abajo, en una arquitectura relativamente compleja debida a las limitaciones del hardware de la CGA. Esto ha sido superado en las siguientes tarjetas, en las que las líneas están consecutivas de manera lógica en una organización lineal.


ORGANIZACIÓN INTERNA DEL MODO 13h

Ya sabemos inicializar el modo de video 320x200x256 (y cualquier otro modo de video) mediante una simple llamada a la interrupción 10h. Ahora hemos de comprender como gestiona el ordenador esos pixels que escribimos en pantalla para empezar a trabajar en 13h. Es algo muy sencillo y visual y requiere tan sólo conocimientos sobre la memoria del PC.
En cierta manera, de toda la memoria del PC los primeros 1024 KiloBytes (1 MegaByte) están divididos en segmentos de 64kb (65.536 bytes) a los que se accede mediante un segmento y un desplazamiento u offset.

Dicho de otro modo, es como si el primer megabyte de memoria RAM estuviera compuesto por bloques de 64Kb cada uno. Cuando queremos escribir en una posición de memoria, le indicamos al ordenador en qué bloque está (segmento), y dentro de ese bloque cual es el byte que queremos modificar (offset o desplazamiento).

Esto es así porque en el 8086 los registros eran de 16 bits (sólo pueden adoptar valores entre 0 y 65.536), por lo que para acceder a la memoria idearon este método para, pudiendo utilizar tan sólo hasta el número 65.535, hacer referencia a posiciones de memoria más elevadas (cuando se creó el PC nadie se podía imaginar que algún día necesitaría más de 64Kb de RAM). Con este sistema de segmentación de memoria, escribir un byte en la posición 65.536 se reduce (de una manera intuitiva a modo de ejemplo) a escribirlo en el bloque 1, offset 0. Realmente en el PC las direcciones absolutas se construyen mediante:p>

     Dir_Física = (Segmento*16)+Offset
 

Esto sólo ocurre en modo real, pues a partir de la aparición de micros de 32 bits (386+), con registros de este tamaño puede accederse a la memoria de manera lineal, en un nuevo modo del micro llamado modo protegido, permitiendo la manipulación de hasta 4 gigabytes.
El objetivo de la anterior introducción a la segmentación de memoria es el segmento 0A000h. Este segmento de memoria (64Kb, 65.536 bytes) es el segmento de VideoRAM, es decir, es donde la VGA guarda los datos de las imágenes gráficas que dibuja en el monitor. Si escribimos algún valor en este segmento, la próxima vez que la tarjeta gráfica redibuje la pantalla (lo hace entre 50 y 70 veces por segundo) el valor que hemos escrito aparecerá en pantalla en forma de punto. Pero veamos que es lo que hace la tarjeta gráfica con esta VideoMemoria.
 



 

EL RETRAZADO DE PANTALLA

Aproximadamente entre 50 y 70 veces por segundo (según el modo de vídeo), la tarjeta gráfica lee de su memoria todos los valores que contiene y transforma esta información digital (unos y ceros, o sea: números) en los puntos que vemos en pantalla. Como sabemos el CTR "bombardea" la pantalla con electrones que producen las diferentes tonalidades. Estas partículas tienden a apagarse, por lo que es necesario redibujar (retrazar) la pantalla el número de veces necesario por segundo para que la imagen no desaparezca.
En este proceso, llamado retrazado de pantalla, el haz de electrones se desplaza hasta la esquina (0,0) del monitor (incluyendo el borde) y comienza a leer bytes de la VideoRAM (segmento 0A000h), transformándolos en pixels y trazándolos en pantalla. Al llegar al final de una línea horizontal, el haz vuelve en a la siguiente línea (retrazado horizontal) y continúa con el proceso hasta llegar a la esquina inferior derecha, donde vuelve en diagonal a (0,0) para repetir el proceso. En la figura 2 puede verse el proceso con más claridad. El refresco de la pantalla consiste en un continúo retrazado actualizando la pantalla para ofrecernos la imagen contenida en la videomemoria (que en realidad constituye RAM de la tarjeta a la que se nos permite acceder tras el proceso de autoarranque del encendido).
 
 


Figura 2: Barrido de la pantalla por el CRTController



 
 


DIRECCIONAMIENTO LINEAL

Visto de esta manera, sabiendo que la tarjeta transforma los bytes del segmento 0a000h (0xA000 en hexadecimal de C) en pixels, si averiguamos qué representa cada byte, cuando tratemos de dibujar un pixel bastaría con escribir en este segmento el color que queremos para que la tarjeta gráfica lo represente durante el próximo retrazado. Nada más sencillo en este modo gráfico, modo de 8 bits por pixel.
 

Esto quiere decir que cada número del 0 al 255 se corresponde con un color. Por defecto, el 0 es el negro, el 1 el azul, y así hasta llegar al 255. Entre el 0 y el 255 disponemos de gamas de azules, verdes, amarillos, etc..., que componen la paleta por defecto de la VGA. Que este modo gráfico sea de un byte por pixel significa que al escribir un byte en este segmento de memoria, su equivalente en pantalla será un pixel, que aparecerá automáticamente en cuanto el haz de electrones pase por esa posición al refrescar la imagen.

En la figura 3 tenemos una representación de cómo está organizada la VideoRAM en el modo 13h.
 
 


Figura 3: Organización lineal del modo 13h.




Como puede verse, al byte 0 le corresponde el pixel (0,0) (el primero de la pantalla); al byte 1 le corresponde el pixel (1,0), al byte número 320 le correspondería el pixel (0,1), (primer pixel de la línea 1, porque hay 320 pixels de resolución horizontal) y así hasta el byte 63.999 del segmento, que corresponde a la posición (319,199). Depende del offset en que coloquemos el byte, el punto aparecerá en distinta posición en el monitor (cada byte es un pixel individual en la pantalla).
El segmento de la VideoRAM se comporta en este modo de video como si fuera una larga línea de pixels de manera que al llegar al final de una línea horizontal de pantalla, el siguiente byte de la VideoMemoria es el que continúa en la siguiente línea de pantalla. De ahí el término direccionamiento lineal: es como si la pantalla fuera un array de C o PASCAL unidimensional desde 0 a 64.000 donde cada 320 bytes estamos situados en una nueva línea de pantalla (el byte 320 es el primer pixel de la segunda línea). Así, durante el retrazado la tarjeta únicamente tiene que dedicarse a leer bytes (todos ellos consecutivos) y representarlos en pantalla.
Tambien podríamos comparar la VideoRam con una gran pantalla de una sóla linea de ancho (de 320x200=64.000 pixels de ancho), y cuando la tarjeta traslada esos colores al monitor, cada 320 bytes salta a una nueva línea. Así obtenemos en pantalla una imagen de 320x200 pixels.
Por otra parte, hay que hacer notar que los 256 colores de que disponemos pueden ser adaptados a nuestras necesidades. La paleta por defecto contiene unos 32 tonos de azul, 32 de rojos, grises, etc... pero si estamos dibujando (por ejemplo) una selva, probablemente necesitaremos más tonos de verdes de los 32 que hay por defecto. La manera de cambiar la paleta (es decir, que cada número 0-255 corresponda a una tonalidad o color) la abordaremos mas adelante, de manera que podríamos (en este ejemplo en concreto) hacer que los primeros 128 colores sean tonos de verdes y los restantes 128 tonos de azules (para el cielo de nuestra selva). Esto quiere decir que el color 0 no tiene porqué ser el negro (como en la paleta por defecto), sino que podemos adaptar cada color (desde el 0 al 255) a la tonalidad (mezcla de rojo, verde y azul) que deseemos.
 

Ejemplo: Graficar los primeros pixeles

El siguiente ejemplo lo ensamblé con éxito (usando el TASM 3.1) graficando 6 pixeles en la esquina superior derecha de color verde:
.MODEL SMALL
.CODE

;programa de prueba grafica ,por Ivan Brito

programa:
    
        call set_video      ;inicializa el modo de video (320x200,256 colores)

;********* seteamos el segmento de memoria de video **********************

        mov ax,9fffh
        add ax,1h       ;segmento de video direccionado en a000h (tama¤o=64kb)
        mov es,ax       ;utilizamos el segmento extra del micro para direccionarlo
        mov al,03h      ;seteamos el color
   

;********** escribimos en los pixeles en la memoria de video *******************************

        mov di,0000h
        mov es:[di],al  ;escribimos el pixel
        mov di,0001h
        mov es:[di],al  ;escribimos el pixel
        mov di,0002h
        mov es:[di],al  ;escribimos el pixel
        mov di,0003h
        mov es:[di],al  ;escribimos el pixel
        mov di,140h
        mov es:[di],al  ;escribimos el pixel
        mov di,280h
        mov es:[di],al  ;escribimos el pixel
        mov di,3c0h
        mov es:[di],al  ;escribimos el pixel

;******* leemos pixeles y desplegamos en pantalla lo que hay ********************************************

;        mov ah,02h
;        mov dl,es:[di]
;        int 21h

        
;******* llamamos a funcion 01 de la int 21h para simular un getch()****

        mov ah,01h
        int 21h

;******* nos vamos del modo de video ***********************************

        call off_video

;******* preparamos la funcion 9h de la int 21h para desplegar strings ***************

        mov ax,@DATA
        mov ds,ax
        mov dx,offset Texto
        mov ah,9h
        int 21h

;******** esta funcion es para terminar "sin problemas " el programa **

        mov ax,4c00h
        int 21h
        
;******* deficicion de procedimientos **********************************

set_video proc near
        mov ah,00h
        mov al,13h
        int 10h
        ret
set_video endp

off_video proc near
        mov ah,0
        mov al,3
        int 10h
        ret
off_video endp


.DATA
Texto DB 'Programa OK!, Chester$' 
.STACK
END programa


Aunque este bloque assembler tiene más líneas que un bloque C utlizando los driver BGI y esto induzca a pensar que puede ser más lento que el pokeb() o que una simple llamada al servicio 0Ch de la int 10h, lo cierto es que es, con diferencia, más rápido que la primera acción y muchísimo más que la segunda.

El acceso directo con assembler a la VideoRAM es la manera más directa posible de representar gráficos en pantalla, único camino a seguir al desarrollar aplicaciones y juegos de tiempo crítico (aquellos donde es necesaria mucha velocidad de representación) al igual que hacen los videojuegos de las compañías profesionales, aunque sí es preciso hacer notar que la VideoRAM es bastante más lenta que la RAM normal, pero se puede solucionar con la s llamadas "pantallas virtuales"
 


OFFSET DE UN PIXEL (X,Y)

Resulta bastante intuitivo ver que el offset 0000h corresponde al pixel (0,0), y que el offset 0001h corresponde al pixel (1,0), pero, si queremos modificar o leer el pixel (160, 100), por ejemplo, ¿qué offset tendremos que mover en DI para el acceso a memoria?
De nuevo la solución nos la proporciona la figura 3. Cada 320 bytes de memoria estamos dentro de una nueva línea de pantalla. Esto quiere decir que el byte 320 corresponde al pixel (0,1), el 640 al (0,2), y así hasta el pixel (0,199), que corresponde al offset 199*320 = 63.380. Analicemos este último valor.

Si el pixel (0,199) está situado en el offset 63.380, el pixel (1,199) será el siguiente ( (199*320)+1 = 63.380 + 1= 63.381). De ello se deduce que el offset de cualquier pixel (x,y) será:

  offset = (320*y) + x;

Por lo tanto, el pixel (160,100) estará situado en el segmento 0A000h, en el offset (320*100) + 160 = 32160. Si escribimos el valor 1 (azul en la paleta por defecto) en esta posición de memoria, en el centro de la pantalla aparecerá un pequeño pixel de color azul.
Así de sencillo es el cálculo del offset correspondiente a cualquier pixel (x,y). Únicamente hemos de saltarnos 320 bytes por cada línea desde el principio del segmento (pixel (0,0) = offset 0) hasta llegar al offset deseado.


LA PALETA


Los primeros ordenadores personales tenían un sistema de visualización monocromático, es decir, dos colores en pantalla (fondo y primer plano), que limitaban la vistosidad de los programas. Con la aparición de nuevas tarjetas como la CGA o la EGA se pudo disponer primero de cuatro colores y después de hasta 16 colores diferentes en la pantalla, con la que ya se podían hacer presentaciones más logradas, pero la tarjeta gráfica que ofreció unas prestaciones de calidad fue la tarjeta VGA que conseguía visualizar hasta 256 colores de forma simultánea, aunque en una resolución de 320 pixels de ancho por 200 de alto. También permitía resoluciones altas, de 640 x 480 pixels, aunque a 16 colores.

Para representar los colores el PC se sirve de una paleta que utiliza de forma parecida a la de los pintores. En ella se realizan mezclas para conseguir unas nuevas tonalidades o nuevos colores que se puedan necesitar.

Cada color está constituido por una mezcla tres colores básicos : el rojo, el verde y el azul. A estos se les llama componentes porque la union de estas tres dan como resultado un color. Por lo tanto, una paleta estará constituida por tres componentes para cada color. Para realizar estas mezclas se utiliza un sistema llamado RGB (del inglés red-green-blue) que combina estos tres colores básicos. El porqué de la utilización de este sistema es porque el monitor genera el color centrando los tres haces de luz en un solo punto. Cada haz es de un color básico, es decir, un haz es rojo, otro verde y el otro azul, por lo que al unirlos se verá en la pantalla el color que corresponda a la mezcla de las tres intensidades de cada haz. Basta con acercarse a una televisión para comprobar que cada punto está compuesto por tres pequeños puntos, cada uno activado con una intensidad.

De esta forma, para conseguir el color blanco las tres componentes deberán estar al máximo, y para conseguir el color negro las tres componentes deben estar a cero. Siguiendo la misma tónica, el color rojo más puro la tarjeta lo genera con R=63, G=0 y B=0. Podemos generar así hasta 262.144 colores, como se verá en el apartado “El DAC de la Tarjeta gráfica”.

De esta manera, la paleta de colores tomando como referencia el modo 320x200 a 256 colores, es simplemente un array de 768 elementos (256 colores * 3 componentes cada uno = 768) conteniendo las componentes de estos 256 colores que la VGA guarda internamente para que, al redibujar la pantalla (puede consultarse el proceso de retrazado en la entrega anterior), cuando la tarjeta encuentra en la VideoRAM el numero 0, por ejemplo, va a esta tabla y activa los 3 haces para que generen esa tonalidad RGB.

La paleta gráfica por defecto tiene unas componentes determinadas de manera que siempre el color 0 es el negro, el 1 el azul, etc... Si miramos la paleta por defecto de la VGA, estos 768 elementos que contiene almacenados la tarjeta tendría un aspecto como:

 Paleta_Defecto[768] =
  { 0,0,0, 0,0,17, ... etc ... };
 

El color 0 es negro porque sus componentes son (0,0,0). De la misma manera, el color 1 es azul por defecto porque sus componentes son (0,0,17). El color 15 es blanco porque sus componentes son (63,63,63).
Si queremos que el color 0 en vez de ser negro (0,0,0) sea un amarillo pálido, podemos cambiar dentro de este array interno los valores (0,0,0) por, por ejemplo, (23,44,0).

 Paleta_Defecto[0] = 23;
 Paleta_Defecto[1] = 44;
 Paleta_Defecto[2] = 0;

De esta misma manera, podemos cambiar el color 1, el 15, el 255... en resumen: podemos cambiar cualquier color a la tonalidad que deseemos. De ahí la necesidad de cambiar la paleta: para cada dibujo necesitamos unos colores en concreto (más verdes y menos rojos, todo grises, etc...).

El DAC (digital-analog converter) se utiliza para convertir las señales digitales que genera el ordenador a señales analógicas aplicables al CRT. De esta forma, y si el monitor esta capacitado para ello, con sólo cambiar nuestra tarjeta de vídeo por otra con un DAC mejor se conseguirían más colores representables por la pantalla.

El DAC de una tarjeta VGA es de 6 bits con lo que se consiguen 64 combinaciones diferentes para cada componente de color. Si recordamos, cada color tiene tres componentes diferentes(RGB), por lo que se puede calcular el numero de colores diferentes representable por una tarjeta VGA:
 

   64 Rojos * 64 Verdes * 64 Azules = 64 ^ 3 = 262.144
 

Este número es la cantidad de colores diferentes capaz de generar la VGA, pero no se pueden mostrar todos a la vez por la limitación de la paleta. Dependiendo del modo de vídeo podremos disponer de un número mayor o menor de colores; de esta forma de entre los 262.144 colores la paleta nos fuerza a coger un número determinado de ellos. Por ejemplo, un modo de 256 colores indica que la paleta tendrá 256 colores de los posibles 262.144, y modificando las componentes de cualquiera de estos colores podemos tener acceso a otras tonalidades de entre el total generable.
 



 

LA PANTALLA Y LOS COLORES

Como hemos visto, el ordenador trabaja con una paleta donde almacena los colores y una pantalla donde se indica qué color de la paleta es el que corresponde en esa posición de la pantalla. Por tanto, en la pantalla se pondrá un byte para indicar qué color de la paleta debe colocarse en este lugar, ya que en un byte se puede almacenar 256 números diferentes y la paleta está compuesta por 256 colores. Así, si en una posición de la pantalla se encuentra el valor 15, se buscará el color número 15 de la paleta. Por tanto se puede decir que los valores que se almacenan en la memoria de vídeo son ‘punteros’ que apuntan a un valor de la paleta.
De esta forma, si se cambia la tonalidad de un color, la imagen no se ve afectada, sólamente que los puntos de la pantalla que tengan ese color cambiarán al nuevo tono. Es decir, si en un byte de la VideoMemoria se tiene el número 10, significa que en ese lugar de la pantalla se debe poner el color número 10.

Cuando el DAC retrazando la pantalla lee este valor, lee de la paleta (guardada por la tarjeta) las componentes del color 10 (r=Paleta[10*3], g=Paleta[10*3+1], b=Paleta[10*3+2]) y programa los 3 haces para que dibujen en pantalla esa tonalidad. Cambiando estas 3 componentes de la paleta, cuando el DAC lea otra vez este número, cogerá de este array interno las nuevas componentes y el color resultante en pantalla será el que nosotros deseábamos, sin necesidad de cambiar ningún dato del dibujo original. Por lo tanto la imagen no cambia, sólo cambia la tonalidad de todos los puntos de la pantalla que tengan dicho color.


Figura 3:Organización de la paleta





Después de iniciar cualquier modo gráfico de 256 (o menos) colores, el ordenador nos ofrece una paleta por defecto, que contiene unos colores para uso general. Esto puede ser útil en determinados casos por ser una paleta variada, pero también puede ser que no interese. Supongamos que se quiere hacer el dibujo de una selva. Para este dibujo se necesita una gama de verdes amplia, mientras que probablemente otros colores como rojos y azules no se utilicen, por lo que si se utiliza la paleta por defecto probablemente al dibujo le falten verdes. En cambio, si se cambia la paleta por otra donde el color predominante sea el verde, el dibujo podrá utilizar unos tonos de verdes específicos para las hojas, otros tonos para los matorrales, etc. con lo que se consigue más realismo en la imagen.
Para cambiar un color sólamente hay que cambiar sus tres componentes. Hay varias formas de hacerlo : utilizando las órdenes que el BGI pone a nuestra disposición, por medio de interrupciones o por medio de puertos. Para empezar veremos cómo se realiza en C.

Para poder utilizar la librería gráfica, como veremos más adelante,  hace falta incluir en la cabecera del programa la librería <graphics.h> en la que se incluyen todas las funciones del BGI, pero nos centraremos en la función para cambiar la paleta : ‘setrgbpalette()’. Esta función cambia el color por su RGB, por lo que tendrá como parámetros el número de color a cambiar y las tres componentes del nuevo color :

- setrgbpalette (numcolor, rojo, verde, azul); donde ‘numcolor’ es el número de la paleta que deseamos cambiar, y ‘rojo’, ‘verde’ y ‘azul’ son las tres componentes del color que queremos obtener.

Si se inicia el modo gráfico con un bloque ensamblador, no se podrán utilizar las funciones BGI porque requieren que se inicie el modo con la función initgtraph(), que inicia unas variables como el ancho o el alto de la pantalla para que el BGI pueda trabajar.
 


ACCESO DIRECTO AL DAC

Para cambiar un color de la paleta mediante el acceso al DAC se utilizan unos puertos específicos. En total hay cinco puertos, representados con detalle en la tabla 3.
 
 Tabla 3: Registros del DAC
---------------------------------------------------------------------------
Registro: PEL Address Read Mode Register
Puerto:   03c7h
Función:  Expresa dentro de la tabla interna de 256 colores (PEL) el
          color al que apunta el puntero interno del DAC cuando leemos
          la paleta del puerto 0c39h (PEL DATA REGISTER). Cada 3 lecturas
          del puerto 3c9h este registro se incrementa en 1 (cada 3
          componentes leidas se incrementa el puntero para que se refiera
          al siguiente color).
----------------------------------------------------------------------------
Registro: PEL Address Write Mode Register
Puerto:   03c8h
Función:  Apunta dentro de la tabla interna al color que se modificará
          al realizar 3 escrituras en el PEL DATA REGISTER. Tras estas
          3 escrituras en el puerto 03c9h el puntero se incrementa en 1
          para hacer referencia al siguiente color dentro de la tabla PEL.
----------------------------------------------------------------------------
Registro: PEL Data Register
Puerto:   03c9h
Función:  Mediante este puerto podemos modificar o leer la paleta interna
          de la tarjeta gráfica. Al escribir un valor en este puerto el
          DAC lo almacena dentro de la paleta actual en la dirección
          apuntada por el puntero de escritura, e igual para la lectura.
          Este puerto constituye para el programador la ventana a través
          de la cual modificar la paleta de la tarjeta, conjuntamente con
          los registros PEL Address Read y PEL Address Write, que nos
          permiten seleccionar el color a leer o escribir. Tras esta
          selección los 3 valores leidos/escritos en este puerto
          constituirán las componentes RGB del color.
----------------------------------------------------------------------------
Los puertos 0x3C7 y 0x3C8 actúan como punteros hacia la paleta, es decir, el valor enviado a estos puertos indica el color dentro de la paleta de 256 colores que debe ser leido o escrito mediante el acceso al puerto 0x3C9.
Por ejemplo, en el caso de que se utilice el puerto 0x3C9 para lectura (se lee un valor de él), nos devolverá las componentes correspondientes al color indicado en 0x3C7.

Al utilizar el puerto 0x3C9 automáticamente se incrementa el puntero (al realizar 3 lecturas o 3 escrituras), por lo que al volver a utilizar el puerto nos devolverá las componentes del siguiente color. Así, se pueden leer o escribir más de un color de forma consecutiva sin utilizar cada vez el puerto 0x3C7 ó el 0x3C8, sólamente llamando al puerto 0x3C9 para cada componente que se quiera leer o escribir.

Suponiendo que se quiera modificar las componentes de un color de la paleta por otras diferentes, los pasos a seguir serian los siguientes:
 

 1.- Enviar al puerto 0x3c8 el º de color a cambiar
     (dirección para escritura).
 2.- Enviar al puerto 0x3c9 la primera componente del
     nuevo color (rojo)
 3.- Enviar al puerto 0x3c9 la segunda componente
     del nuevo color (verde)
 4.- Enviar al puerto 0x3c9 la tercera componente del
     nuevo color (azul)
 

De forma práctica, un listado en C sería:
 

 outportb (0x3C8, num_color);
 outportb (0x3C9, rojo);
 outportb (0x3C9, verde);
 outportb (0x3C9, azul);
 

También se puede hacer con un bloque en ensamblador dentro del C para mayor rapidez, usando las instrucciones assembler ‘IN’ y ‘OUT’. La instrucción ‘IN AL, DX’ lee del puerto DX el valor que contiene y lo almacena en AL. De la misma manera, ‘OUT DX, AL’ escribe en el puerto DX el valor contenido en el registro AL. Tan sólo hemos de cargar los registros con los valores correctos para evitar las llamadas a funciones C (como outportb() e inportb(), que además de ser más lentas que el bloque assembler, requieren la inclusión del fichero DOS.H).
Como puede verse en la tabla 2, al realizar en el ejemplo anterior las 3 lecturas, el puntero interno del DAC se incrementa automáticamente, por lo que podemos leer las componentes del siguiente color sin especificarlo en el puerto 3c7h (igual ocurre en la escritura).



 

LOS REGISTROS INTERNOS DE CONFIGURACION DE LA VGA


Los registros de las tarjetas son el dispositivo E/S de más bajo nivel de nuestros adaptadores graficos, y en este número vamos a aprender a controlarlos para aprovechar nuestra tarjeta al 100% en nuestros programas.
Los registros de las tarjetas gráficas representan acceder al nivel más bajo (más bajo significa más directo a hardware) en cuanto a programación gráfica se refiere. El objetivo es proporcionar unos conocimientos generales en lo que son los registros de las tarjetas gráficas, su uso y más tarde particularizar en algunos de ellos para conseguir una solución hardware a un problema en un programa gráfico.

Como  es de suponer cada tarjeta SVGA tiene su set de registros y por cuestiones de compatibilidad no convenía programar estas tarjetas a nivel de circuitería, de manera que solucionaremos esto usando el set de registros de las tarjetas VGA, set estándar para todos los adaptadores de este tipo y con el que las SVGA son totalmente compatibles, funcionando los programas que desarrollemos en cualquier tarjeta gráfica VGA o superior (por compatibilidad descendente).



 

LOS REGISTROS

Un registro, en general, constituye una variable interna implementada en circuitería, es decir, una variable del hardware. De esta manera, las CPU’s tienen sus registros (EAX/EBX/ECX/D0/R0, registros intermedios, etc) con los que operan y donde almacenan datos necesarios para el proceso y ejecución de instrucciones. Las tarjetas gráficas también poseen un set de registros donde almacenan datos y parámetros importantes para el correcto funcionamiento del sistema completo que engloba la tarjeta, con el objetivo de que en el monitor aparezcan correctamente los gráficos construidos en la VideoRAM.
Los registros de las tarjetas gráficas contienen pues toda la información que define cómo debe el ordenador representar en el monitor los datos que hay en la VRAM, es decir, un registro almacena la resolución horizontal de pantalla, otro la vertical, otro la velocidad de refresco del monitor, los pixels por scanline, y así con todos los parámetros necesarios para esta representación.



 

REGISTROS VGA

Las tarjetas VGA poseen un set restringido y estándar de registros, el significado de algunos de los cuales puede resultar muy útil al programador. Lo importante entonces es la determinación de cuántos registros estándar VGA hay, que hace cada uno de ellos y cómo se modifica o lee el valor de estos registros.
Como veremos en el presente artículo, los registros pueden ser accedidos mediante los puertos E/S a los que están conectados, que van desde el 3b0h al 3dfh, permitiéndonos su modificación y lectura.
 



 

LOS CONTROLADORES ASOCIADOS A LA VGA

Una tarjeta gráfica consiste en varios controladores hardware (integrados en la misma placa de la tarjeta) cuya asociación produce el resultado esperado en pantalla. Podemos ver en la figura 4 una simple representación de estos controladores dentro de una tarjeta gráfica. Estos 5 controladores son los siguientes:
 
 


Figura 4: Registros internos de la VGA.






-Attribute Controller (puerto 3c0h): es el controlador de atributos, relacionado con todos los temas de colores, atributos, etc, accediéndose por el puerto 3c0h.

-VGA Sequencer (puerto 3c4h): es el secuenciador de la VGA y gestiona sus funciones más críticas e importantes (reseteo de la tarjeta, generador de carácteres, etc).

-Graphics Controller (puerto 3c3h): es el controlador gráfico de la VGA, dando acceso a la lectura de planos, reseteo de registros, etc.

- Cathode Ray Tube Controller (puerto 3d4h): maneja todos los datos referentes al tubo de rayos catódicos que refresca o redibuja la pantalla, en un sistema asociado al monitor.

-El SVGA Controller (puerto ???h) podemos considerar que estará presente en las tarjetas SuperVGA, conteniendo los registros extendidos para este tipo de tarjetas. Su puerto de acceso varía según el fabricante de la tarjeta, de ahí los problemas de compatibilidad en la programación de estas tarjetas.
 



 

PROGRAMACIÓN DE LOS REGISTROS VGA

Programar correctamente los registros de las tarjetas gráficas es un tema muy extenso, pero vamos a tratar de proporcionar unas reglas generales.
En principio sabemos que tenemos 4 controladores en la tarjeta gráfica (ATTRC, VGASEQ, GRAPHC y CRTC, descritos arriba) cada uno de ellos con una dirección E/S (un puerto) asignado para su acceso. Estos 4 controladores tienen cada uno una serie de registros y el objetivo es saber cómo se accede a un registro determinado y un controlador determinado.

Supongamos que queremos cambiar el color del borde de la pantalla (normalmente negro) a azul. Miramos en cualquier manual de referencia que contenga los registros de la VGA y vemos que existe un registro llamado <<Border Color Register>> al que se accede mediante el Attribute Controller. En el manual pondrá algo así como:
 

  Attribute Controller (3c0h):
  Border Color Register, index 11h, r/w.
  Bits (0-5): VGA screen border color.
 

El significado de esto es que el Border Color Register es el registro número 11h (index 11h) del ATTRC, que es de lectura y escritura (r/w), es decir, que podemos leer su valor y modificarlo, y que si los bits del 0 al 5 de este byte indican el color del borde (0=negro, 1=azul, etc).
Simplificando conceptos: los registros VGA pueden ser de escritura, de lectura, o de escritura y lectura (w, r, r/w), y cada registro de un controlador tiene un número asociado (index port o índice de puerto) que indica dentro del total de registros de un controlador a cuál nos referimos. Ahora tan sólo habría que acceder al registro nº 11h del ATTRC (puerto 3c0h), escribiendo allí el color que queremos que tome el borde, en nuestro caso azul (1).

La función que tiene el index port al acceder a un registro se puede comparar con el que tiene el registro AH (subfunción) al llamar a una interrupción, porque al igual que la interrupción 10h tiene diversas funciones, un controlador VGA puede controlar más de un registro, y necesitamos indicarle cuál de ellos queremos leer o modificar. Así pues, el índice de puerto le indica al puerto al que se escribe cuál es el registro que queremos modificar de todos los que ese puerto controla. Únicamente hemos de enviar los parámetros tal y como los requiere cada controlador, y ese es el principal problema de la programación a bajo nivel de la VGA.
 

SET DE REGISTROS VGA

Con todo lo visto hasta ahora se han proporcionado los métodos de acceso a los distintos controladores para modificar/leer el valor sus registros. Ahora lo importante radica en conocer qué significa el valor de cada registro, y las consecuencias que tendría su modificación (que al fin y al cabo es lo que nos interesa). En la tabla 4 podemos ver los registros más interesantes en un aspecto básico (no están todos comentados), pudiendo encontrar la lista completa en el CD que acompaña a la revista.
 
     Tabla 4 : Algunos registros VGA

----------------------------------------------------------------------
Port-Index: 02h              Port: 03c4h
Registro: Color plane write enable register
Descrip.: Controla la escritura en los diferentes planos de la VRAM.
            Bit 7, 6  Reservados
            Bit 3     Permitir escritura en plano 3.
            Bit 2     Permitir escritura en plano 2.
            Bit 1     Permitir escritura en plano 1.
            Bit 0     Permitir escritura en plano 0.
----------------------------------------------------------------------
Port-Index: 04h              Port: 03c4h
Registro: Memory mode register
Descrip.:
            Bit 4-7  Reservados
            Bit 3    Bit Chain 4. (b3=1 -> modos de 256 colores).
            Bit 2    Direccionamiento Odd/even.
            Bit 1    Activado si hay más de 64 de VRAM.
            Bit 0    b0=1: modo alfanumérico. b0=0: modo gráfico.

----------------------------------------------------------------------
Port-Index: 01h              Port: 3d4h
Registro: Horizontal display enable register
Descrip.: Nº total de carácteres visualizados (horiz.) menos 1.
----------------------------------------------------------------------
Port-Index: 02h           Port: 3d4h
Registro: Start Horizontal blanking register
Descrip.: Carácter en el que empieza el blanqueo.
----------------------------------------------------------------------
Port-Index: 04h               Port: 3d4h, 3b4h
Registro: Start Horizontal retraze register
Descrip.: Carácter en que empieza el retrazo horizontal.
-----------------------------------------------------------------------
Port-Index: 0ah              Port: 3d4h
Registro: Cursor Start Register
Descrip.: Indica línea inicial (0-16) donde empieza el cursor hardware.
            Bit 7, 6 : reservados.
            Bit 5  : Cursor off
            Bit 4-0  : Cursor Start
-----------------------------------------------------------------------
Port-Index: 0bh              Port: 3d4h
Registro: Cursor End Register
Descrip.: Indica línea final (0-16) donde empieza el cursor hardware.
            Bit 7 : reservado.
            Bit 6, 5  : Cursor skew.
            Bit 4-0  : Cursor end.
-----------------------------------------------------------------------
Port-Index: 13h              Port: 3d4h, 3b4h
Registro: Offset/Logical Screen Width register
Descrip.: Anchura lógica entre 2 scanlines sucesivos.
Port-Index:               Port: 3dah
Registro: CTRC Status register
Descrip.: Indica el estado del retrazo vert. Y horiz.
        Bit 3: Retrazo vertical:
                 0 = refrescando.
                 1 = volviendo.
        Bit 0: Retrazo horizontal:
                 0 = refrescando.
                 1 = volviendo.
----------------------------------------------------------------------
Port-Index: 05h              Port: 03ceh
Registro: Mode register
Descrip.: Especifica el modo de lectura y escritura de los datos.
            Bit 7     Reservado (0).
            Bit 6     (b6=1) modo de 256 colores.
            Bit 5     Modo de rotación de registros.
            Bit 4     Modo odd/even de direccionamiento.
            Bit 3     Lectura de datos (b3=0 leer, b3=1 comparar).
            Bit 2     Reservado (0).
            Bit 1, 0  Modo de escritura de datos.
                      0 = escritura directa,
                      1 = transferencia VRAM a VRAM,
                      2 = Uso de color o patrón.
----------------------------------------------------------------------
Port-Index: 06h              Port: 03ceh
Registro: Miscellaneous register
Descrip.: Define datos de distintos tipos (mapeado, modo, etc).
            Bit 7-4  Reservados
            Bit 3-2  Segmento de memoria (g_window) y su tamaño.
                  00 = A000h -> 128k
                  01 = A000h -> 64k
                  10 = B000h -> 32k
                  11 = B800h -> 32k
            Bit 1    Odd/even enable (used in text modes)
            Bit 0    Graphics mode enable
----------------------------------------------------------------------
Port-Index: 10h              Port: 03c0h
Registro: Mode control register
Descrip.: Datos referentes a los modos de vídeo.
            Bit 6  (VGA) Si b6=1, la anchura del pixel = 8 (256 colores).
            Bit 5  (VGA) Si 0, se ignora la comparación de línea.
            Bit 4  Reservado
            Bit 3  Si b3=1, el bit 7 del byte de atributo significa
                   parpadeo. Si es 0, ese bit significa intensidad.
            Bit 2  Si b2=1: caracteres de 9 bits.
            Bit 1  Modo monocromo si es 1. Color si es 0.
            Bit 0  b0=1->modo gráfico. B0=0->modo de texto.
----------------------------------------------------------------------
Port-Index: 11h              Port: 03c0h
Registro: Screen border color register
Descrip.: Indica el color del overscan (borde).
----------------------------------------------------------------------
Port-Index: -              Port: 03c7h
Registro: Lookup table read index register.
Descrip.: Indica el color a leer en el registro 03c9h (cambia el puntero
           dentro de la paleta interna de la tarjeta de vídeo).
----------------------------------------------------------------------
Port-Index: -              Port: 03c8h
Registro: Lookup table write index register.
Descrip.: Indica el color a escribir en el registro 03c9h (cambia el puntero
           dentro de la paleta interna de la tarjeta de vídeo).
----------------------------------------------------------------------
Port-Index: -              Port: 03c9h
Registro: Lookup table data register.
Descrip.: Apunta a la paleta interna (actual), dentro del color especificado
           con los registros 03c8h o 03c7h, y en este puerto podemos modificar
           esta tabla (cambio/lectura de la paleta).
           dentro de la paleta interna de la tarjeta de vídeo).
----------------------------------------------------------------------

Ejemplo: Estado de un registro


Para comprender la importancia de estos registros vamos a comentar el Registro de estado del CRTC (STATUS_P, 3dah), cuya descripción puede verse en la tabla 3. El significado de sus bits es sencillo de interpretar y proporciona información sobre los retrazos verticales y horizontales.
Como ya sabemos, en el monitor hay un haz de electrones (tubo de rayos catódicos) que bombardea el fósforo del monitor componiendo pixel a pixel la imagen final entre 60 y 70 veces por segundo, tomando de la VRAM los bytes que representan cada color. Al final de cada línea horizontal se produce un retorno a la siguiente línea (Retrazo Horizontal), y al finalizar el retrazado completo el haz vuelve a (0,0) (incluido el borde) desde el final de la pantalla (como vimos en el nº 2). A este tiempo de retorno se le llama, pues, Retrazo Vertical. En este período de tiempo el haz está volviendo y no redibujando, de manera que podemos aprovechar este tiempo muerto para escribir en VRAM sabiendo que hasta el próximo refresco no aparecerán los cambios que estamos realizando (deben ser cambios muy rápidos). De esta manera nos aseguramos que no habrá parpadeo ni nieve debido a estas escrituras. Como se puede ver en la descripción, el bit 3 de este registro indica:
 

  Bit 3 = 0 -> Haz redibujando la pantalla.
  Bit 3 = 1 -> Haz volviendo a (0,0).

La manera de esperar este tiempo muerto para redibujado de pantallas consiste en esperar a que el haz se encuentre redibujando (=refrescando) la pantalla (bit3=0), y luego esperar a que acabe de hacerlo (bit3=1), volviendo. Si sólo se comprobara esta última condición podríamos encontrarnos con el haz a punto de llegar a (0,0) con el consiguiente parpadeo en las operaciones gráficas. Veamos como sería el código de espera al retrazo vertical (WaitVRetrace()):
 

 mov dx, 3dah
 vert1:
       in al,dx
       test al,8
       jnz vert1
 vert2:
        in al,dx
        test al,8
        jz vert2
 

El código está implementado en assembler porque tras su ejecución tenemos un tiempo mínimo (el que tarde el haz en volver a (0,0)) para realizar cambios en VRAM, de manera que la sincronización ha de ser lo más rápida posible. Con funciones C/PASCAL (outportb(),port[]) habría saltos, accesos a pila, retornos, etc, malgastando un tiempo demasiado importante.
Con este mismo registro también podemos sincronizarnos con el retrazo horizontal mediante el bit 1 (como se hace en las barras de copper, por ejemplo). Otra posible aplicación es generar (para cualquier tipo de juego, animación o efecto) los fotogramas en una pantalla virtual, esperar el fin de retrazado y volcar esta pantalla virtual sobre la VRAM, repitiendo continuamente el proceso.


USO DE LAS LIBRERIAS BGI


Todo lo anterior se puede realizar de manera trasparente (computinezca!) si trabajamos con las librería apropiadas: hablamos del sistema BGI -Borland Graphics Interface- (Interface gráfico que usan tanto C como Pascal), que permite, mediante órdenes concretas del lenguaje que estemos utilizando, acceder a las funciones de dibujo más corrientes a partir de las cuales podemos empezar nuestro periplo por los modos gráficos.
Para empezar, hay que tener claro que los ficheros .BGI incluidos con los compiladores de Borland (en el subdirectorio \BGI) son ficheros de código ejecutable con funciones como putpixel(), circle(), line(), etc... que el programador puede usar para crear cualquier forma gráfica.

Para poder usar estas funciones en nuestros programas, y tomando como ejemplo el compilador Borland C (o Turbo C), para ello tan sólo hemos de cumplir 3 requisitos:
 

 1. Incluir el fichero de cabecera graphics.h
 2. Incluir la librería graphics.lib en el momento de la compilación.
    Esto es muy sencillo de hacer desde dentro del entorno C (abriendo
    un fichero proyecto desde las opciones del compilador) o desde
    fuera, mediante:

  bcc fichero.c graphics.lib

 3. Que cuando ejecutemos nuestro programa, esté presente en el directorio
    actual ( u otro definido ) el fichero BGI correspondiente al modo
    gráfico que estemos inicializando (ej: CGA = CGA.BGI, VGA = EGAVGA.BGI,
    etc...).

Si queremos que el ordenador use el modo gráfico de mayor resolución de que disponga nuestro PC, bastará con seleccionar gdriver=DETECT y no dar ningún valor a gmode. La figura 5 muestra los distintos driver BGI.


Figura 5: Driver BGI para trabajar en modos graficos.



Ejemplo: Circulos de colores

El siguiente programa muestra como utilizar las funciones de la libreria.
#include <stdio.h>
#include <graphics.h>

int main(void){
int gdriver,gmode,color;
int x;

/* Permito cambiar los driver en linea */

printf("ingrese el driver , el modo de video y el color\n");
printf("driver: ");
scanf("%d",&gdriver);
printf("modo: ");
scanf("%d",&gmode);
printf("color: ");
scanf("%d",&color);



initgraph(&gdriver,&gmode,"c:\\tc"); /* inicializo el modo gráfico */

printf("Modo de video: %d,%d",getmaxx()+1,getmaxy()+1);
getch();

setcolor(color);

line(0,0,getmaxx(),getmaxy()); /*sin comentarios*/
getch();
 
/*Ahora lo bonito*/
for(x=1;x<=getmaxy();x++){
circle((getmaxx()/2),(getmaxy()/2),x);
}

for (y=1;y<=17;y++){
setcolor(y);
for(x=1;x<=y*10;x++){
circle((getmaxx()/2),(getmaxy()/2),x);
}

}
getch();

closegraph(); /*ojo, se debe volver al modo texto */
return(0);
}

La función initgraph( driver, modo, directorio ) inicializa el modo
gráfico usando el driver driver, situado en el path directorio,
y entrando en el modo gráfico especificado en modo. En este caso,
buscará en el directorio C:\TC cuando se ejecute nuestro programa.
Si sabemos que el driver va a estar en el directorio actual cuando se ejecute
nuestro programa, bastará con poner comillas vacías ("")

 

DIBUJANDO EN UN MODO GRÁFICO YA INICIALIZADO

Una vez inicializado el modo gráfico deseado, podemos usar cualquiera de las funciones C para dibujar en la pantalla, como puede verse en los ejemplos proporcionados en el CD que acompaña a la revista. A partir de las primitivas que suponen el punto, la línea, el rellenado, etc... podemos crear buenas composiciones gráficas para ir adentrándonos en la programación gráfica.
Podemos ver en la tabla 5 algunas de las funciones gráficas de C y que podemos usar tras entrar en alguno de sus modos gráficos. En ella están todos los parámetros que requieren, y aunque no están todas las funciones BGI, en cualquier manual de referencia C o PASCAL encontraremos la lista completa.

Tabla 5: Funciones típicas de la librería graphic.h




Son muchas y graves las desventajas de estos ficheros. Para empezar, al incluir la librería graphics.lib, el tamaño del programa ejecutable aumenta sobre los 18.000 bytes (en el caso de Borland C/C++ v3.1) debido a la inclusión de todas las funciones, aunque sólo usemos algunas de ellas.
Además, el programa al ser ejecutado deberá tener disponible en el directorio actual (u otro establecido mediante initgraph) el fichero BGI correspondiente al modo que se inicialice, y aunque se puede linkar junto al ejecutable usando la utilidad BGIOBJ.EXE, el sistema BGI sigue pecando en el sentido que más puede quejarse un programa: su velocidad.

Si un programador desarrolla una librería para, por ejemplo, 320x200x256 colores, creará las funciones específicas para ese modo, aprovechando las características que la tarjeta nos brinde para él, y obteniendo una "librería especializada" que funcionará exclusivamente en dicho modo. Por contra, los BGI tienen que funcionar para distintos modos de video, y todo eso dentro de la misma función detectando en cada momento la resolución que se está usando y de una manera "estándar" para todos ellos, por lo que el código ejecutable resultante sea demasiado lento para intentar hacer con él un programa de carácter profesional.
En particular en el caso del proyecto osciloscopio dada la velocidad del XT resultará ( en teoría) bastante lenta la ejecución de nuestro programa considerando que el adaptador CGA utiliza sólo 1 bits por pixel, de manera que si programamos nuestras rutinas gráficas en ensamblador resultarán , aunque costoso en términos de horas hombre, bastante más rápidas.
 
 
 



 

REFERENCIAS

 El universo digital de IBM PC, AT y PS/2

 The PC Hardware Page

 Ensamblador

 VGA de 256 colores

 MonitorWorld.com