MODO DE VIDEO 13H




    Estos números representan uno de los modos de video más utilizado para la programación de videojuegos. Cada modo de video tiene dos características, primero la resolución o número de pixels horizontales y verticales, 320 y 200 en nuestro caso, y segundo el número de colores que pueden aparecer simultáneamente en pantalla, 256 para nosotros. Por píxel se entiende mínimo elemento (punto) que se puede representar en pantalla.

    En el mercado existen varios tipos de tarjetas de video: EGA, VGA y Super-VGA. La primera ya esta obsoleta y la última es nuestros propósitos inmediatos. Comenzaremos con el modo 13h de la VGA para realizar los programas y no otros modos con más resolución como el 0Eh (640x200), el 10h (640x350) o el 12h (640x480), debido al número de colores. Estos tres modos que hemos nombrado tienen un máximo de 16 colores simultáneos en pantalla y como ya hemos dicho, el modo 13h cuenta con 256 colores, y preferimos tener más colores en pantalla a consta de perder resolución. Además otra ventaja adicional de este modo es la organización de su memoria de video, que es muy sencilla como veremos más adelante.

    Algunos lenguajes como C y Pascal suministran unos controladores e instrucciones especiales para trabajar en modo gráfico, pero nosotros en la medida de lo posible evitaremos el uso de estos servicios, ya que además de tener que cargar con los controladores gráficos ,dígase BGI o como sea, suelen ser bastante lentos derivadas del hecho de ser una "librería gráfica no especializada". Por otra parte, su principal ventaja era que nos permitía trabajar en cualquier modo de video sin apenas trabajo de creación de nuestra parte.

    Si pretendemos usar los distintos modos gráficos de que disponemos, nos vemos en la necesidad de crear nuestro propio set de funciones ( putpixel(),getpixel(),line(),etc... ) para poder trabajar en el modo gráfico que deseemos. El problema es que cada modo de video se programa de una manera. De los (en principio) 19 estándar existentes se gestiona de una forma diferente, por lo que tendremos que crear una librería especializada para cada uno de ellos, aprovechando las ventajas que ese modo nos proporciona para obtener un código mas rápido y optimizado. Por supuesto, pueden agruparse estas librerías en una sola, para crear una librería no especializada, pero mucho más que cualquier BGI, y permitir en nuestros programas que el usuario elija el modo de video en el que desea trabajar, tal como lo hacen Quake, Windows y la mayoría de los juegos profesionales actuales.
 

El modo de video 320X200X256
    Para programar gráficos en este modo de video, principalmente hemos de saber hacer tres cosas:

1)    Inicializar el modo de video 320X200 ó 13h.
2)    Comprender el modo de direccionamiento lineal del 13h y su organización interna
3)    Realizar rutinas gráficas para dicho modo.

    Veamos primero cómo inicializar cualquier modo de vide (entre ellos el 13h), utilizando los servicios disponibles en la ROM-BIOS del PC

La BIOS del PC

    La ROM-BIOS del PC es una zona de memoria no modificable, en la cual disponemos de una serie de funciones y servicios para su uso por el sistema operativo ( utilizables por el usuario ), que gestionan accesos a disco, control de la tarjeta de video, gestión de impresora, etc. Estas funciones son las llamadas "interrupciones de software" y proporcionan al programador todo el control sobre el hardware del PC.
    La manera de llamar a una de esas interrupciones, en lenguaje assembler, consiste en preparar todos los parámetros que ésta requiera (cargar los registros del PC, como AX, BX, CX, con los valores adecuados) y ejecutar la llamada a la interrupción. Supongamos que queremos iniciar el 320x200. Cargamos los registros como se solicita y efectuamos la llamada:

                                                    mov ah,0                    /* AH=0 :  Init VideoMode */
                                                    mov al,13h                /* modo : 13h */
                                                    int 10h                     /* llamada a int 10h ( o interrupcion de video)  */

    Podemos observar que conociendo los parámetros que requiere cada interrupción y servicio podemos utilizar cualquiera de las funciones de bajo nivel que nos provee la BIOS.
    Dentro de una fución en C sería, por ejemplo:

                            void SetVideoMode (char modo)
                            {
                                    asm{
                                                mov ah,0
                                                mov al,[modo]
                                                int 10h
                                          }
                            }

    La misma puede ser convocada para iniciar el modo gráfico ( SetVideomode(0x13) y también volver al modo texto que es el modo 03h ( SetVideomode(3) ).
 

Organización interna del modo 13h

        Las tarjetas EGA/VGA disponían de un segmento de 64 kb para representar su imagen (A000), esta zona forma parte de la VRAM (Video RAM) y es la zona de memoria de donde la tarjeta gráfica lee los datos para convertirlos en imágenes, es decir, cuando nosotros queremos visualizar algo en la pantalla no tratamos directamente con el monitor, si no que trabajamos con una zona de memoria y a partir de ahí, la VGA se encarga automáticamente de 'dibujar' las imágenes.

    Hemos dicho que esa zona de memoria es de 64 Kb entonces, ¨Cómo se explica que algunas tarjetas VGA consten de una memoria de 1, 2 ó más megas?, pues bien, ante la imposibilidad de trabajar con ese estrecho margen de 64 kb, los fabricantes de tarjetas se decidieron por utilizar los que se llaman Bit-planes o páginas de 64 kb cada uno, que se 'esconden' detrás de esos 64 kb visibles y direccionables, aumentando la memoria disponible pero complicando mucho la electrónica del sistema de vídeo, en algunos modos de video (los de 16 colores) se hace imprescindible trabajar directamente con las páginas, pero en este modo no es así. Como ya dijimos al principio, una de las ventajas de trabajar en el modo 13h es la organización de la memoria de video. En este modo cada punto en pantalla se representa por un byte que indica el color de ese punto, por lo tanto con 8 bits se pueden codificar 256 valores diferentes, de ahí viene el número de colores de este modo de video. La resolución gráfica de este modo es de 320x200, cada línea de pantalla se compone de 320 puntos o bytes y en total habrá 200 líneas, todas estas líneas se suceden unas a otras de modo lineal a través de ese segmento de 64 kb situado en la dirección A000, ocupando un total de 64000 bytes.

    Aproximadamente entre 50 y 70 veces por segundo la tarjeta gráfica lee de su memoria todos los valores que contiene y transforma esa información digital en los puntos que vemos en la pantalla. Tras nuestro monitor hay un haz de electrones que bombardea la pantalla con electrones que producen las diferentes tonalidades. Estas particulas tienden a apgarse lo que es necesario redibujar la pantalla, este proceso se denomina "retrazado o refresco de pantalla"

    Volviendo al direccionamiento si nosotros nos definimos una tabla de 64000 bytes y la situamos en esa zona de memoria con la claúsula Pascal absolute...:

    Pantalla:Array[1..64000] of bytes Absolute $A000:0000;

tendremos una variable definida justo 'encima' de la memoria de video y cualquier cambio que hagamos sobre dicha variable se reflejar inmediatamente en pantalla. Para escribir un punto en pantalla de color C, en las coordenadas X,Y únicamente tendremos que calcular la posición en dicha tabla donde escribir el color. Debido a la disposición lineal que dijimos antes, esta será y*320+x, es decir, el número de línea por 320 más el número de columna, en Pascal nos quedaría:

    Pantalla[Y*320+X]:=C           /*Visualiza un punto de color C en coordenadas X,Y*/

    En C no podemos poner cualquier valor, en la tarjeta VGA todos los colores (256) que se pueden representar en pantalla en un momento determinado,se guardan en una estructura conocida como tabla de colores o paleta.

    El método anterior para representar un punto utiliza la técnica de acceso directo a memoria (no confundir con la DMA), para lograr sus propósitos podríamos haber tenido el mismo resultado si hubiésemos utilizado la instrucción MEM, que se utiliza para referenciar una posición de memoria.

    MEM[$A000:Y*320+X]:=C       /*Visualiza un punto de color C en coordenadas X,Y*/

    Existen otros métodos para representar puntos en pantalla, en el sentido de optimizar los anteriores.
    Optimizar una rutina significa eliminar operaciones innecesarias y sustituir las operaciones lentas por otras más rápidas logrando así velocidad. Una de los puntos que podemos mejorar es en las multiplicaciones. Cualquier programador de ensamblador sabe que la multiplicación es una de las operaciones más lentas para el microprocesador (entre 139 y 13 ciclos de reloj0 y una rutina tan crítica como el calculo del desplazamiento (offset) de un punto que es repetida varios cientos de miles de veces por segundo, es importantísimo su optimización.
    Recordemos que los bits de cualquier byte pueden ser desplazados a izquierda y a derecha mediante las instrucciones asm shl y shr o usando los operadores de C  <<  y  >>  . Desplazar un número binario de izquierda es equivalente a multiplicarlo por 2, veamos:

                                                00010110 binario =  22 decimal
                                                00101100 binario =  44 decimal
                                                01011000 binario =  88 decimal
Con el desplazamiento de los bits a derecha ocurriría lo contrario se dividiría por 2, 4, 16 segun la cantidad de bits a mover. Logramos asi que el producto o la división se reduzcan a a simples operaciones logicas a nivel de bits (en solo 1 ó 2 ciclos de reloj).
    Pero por desgracia 320 no es potencia de 2, por lo que no podemos multiplicar directamente usando desplazamientos...excepto aprovechándonos de la propiedad distributiva del producto segun la cual una multiplicación puede dividirse en suma de multiplicaciones:

                                                                num*320 = (num*256) + (num*64)

    Ahora sí, 256 y 64 son potencias de 2 y podemos usar desplazamientos para multiplicar:

                                                                offset = (y<<8) + (y<<6) + x

    Esta rutina es mucho más rápida que la primera que desarrollamos y por tanto podremos poner más píxels en pantalla en menos tiempo. La optimización de código es otro punto importante de la programación gráica, ya que arañar algun ciclo de reloj puede parecer poco y nada, pero si es una rutina que se ejecuta miles de veces por segundo, el incremento de velocidad puede ser notable.

    Podemos encontrar valioso verlo ahora en un fuente real en   Pascal  y en C (Turbo C++ 3.0)  que colocan un pixel en la posición (100,50) de la pantalla (observar detenidamente :-).

Pixel (100,50,2)