|
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.
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
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) ).
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 :-).