|
|
|
Introducción La pantalla de nuestro computador puede
funcionar en dos modos, modo texto
Cuando hablamos de modos gráficos, la unidad representable es el pixel. Un pixel (un punto) es la unidad mínima de información que podemos representar en pantalla. El elemento que determina tanto la resolución como la cantidad de colores que podemos representar, es el adaptador. Cada adaptador tiene unas características determinadas, que hace que la forma de programar sea distinta para cada uno. La diferencia fundamental es la manera de gestionar la memoria. La información de un pixel, se almacena en código binario. Esta información hace referencia al color y atributos especiales. Los registros de las tarjetas constituyen el dispositivo de Entrada/Salida (E/S) de más bajo nivel de los adaptadores gráficos. Es en ellos donde se almacena la información relativa a como debe el computador representar los datos en la pantalla. Están conectados a puertos de E/S que los hacen accesibles, permitiendo su modificación y lectura. Una tarjeta de video esta formada por varios controladores hardware integrados en una misma placa. Cada uno de estos controladores tiene a su vez una serie de registros asociados. A la hora de programar las tarjetas gráficas, tendremos que acceder a estos registros, para lo cual se emplea un índice de puerto o index port. Un mismo controlador puede controlar más de un registro, por lo que la finalidad de este índice es señalar cuál de todos los registros es el que se modificará. El modo de acceder a cada controlador es distinto, la forma de leer o escribir en los registros será diferente en cada caso. La aparición de la tarjeta de video VGA (Video Graphic Array) como en el año 1987, hizo que se convirtiera en un estándar de los computadores IBM y compatibles. La VGA tiene una resolución máxima de 640x480 pixeles y puede mostrar simultáneamente un máximo de 256 colores. Esta resolución es muy alta comparada con las antiguas tarjetas CGA y EGA. Lamentablemente para usar los 256 colores solo se puede usar una resolución de 320x200 pixeles. Este modo de vídeo es el conocido Modo 13h, en el cual se han programado la mayoría de los juegos, demos, aplicaciones gráficas, etc. Este modo es uno de los más usados en la programación gráfica, debido a su facilidad de uso y rapidez. Las tarjetas VGA tienen normalmente
un mínimo de 256 Kb, estando la memoria dividida en 4 segmentos
o planos. Existe un bit, que puede estar a 1 o 0. En el primer caso
(bit a 1) hablamos de modos lineales, limitados a un plano. Cuando el bit
está a 0, fuerza a usar los cuatro planos, por lo que estamos en
el caso de los modos denominados planares. Este bit se denomina CHAIN-4,
porque encadena los cuatro planos. La denominación chained (modos
lineales) y unchained (modos planares), hacen referencia al estado de este
bit, indicando que esta a 1 o 0, respectivamente. Los registros de
estas tarjetas pueden ser, de lectura, de escritura, o de lectura/escritura.
Entrando al modo 13h El nombre 13h viene del parámetro que hay que pasarle a la interrupción 10h para iniciar este modo. Esta ultima es un interrupción software que facilita al programador tomar el control del hardware de un PC. La interrupción 10h esta encargado de dar los servicios de video. Para iniciar el modo de video 13h debemos usar la función 00h de la interrupción 10h, y para esto se les pasa unos parámetros.
Qué significa todo esto, lo que sucede es que cada interrupción posee varias funciones y a su vez cada función también puede tener sub-funciones. En este caso nosotros queremos iniciar un nuevo modo de video, para esto utilizamos la función 00h de la interrupción 10h, la cual esta encargada de activar modos de video específicos. En este caso en el registro de 8 bits AL se le pasa el valor 00h y en el otro registro (también de 8 bits) se coloca el modo de video a inicializar. A continuación podrás ver los distintos modos de video, que soporta la tarjeta VGA.
Como podemos ver en la tabla el Modo 13h es uno de los que tiene menor resolución de los modos gráficos, pero es el que tiene mayor número de colores. Nosotros utilizaremos dos modos, el 13h y el 03h, este último los usaremos por la sencilla razón, de que cuando se termina un programa gráfico, se debe regresar nuevamente al modo texto, en este caso al modo 03h que es el que tienen por defecto todos los PC al ser encendidos. Este modo texto es de 80x25 y con 16 colores. Existen modos superiores al 13h, pero estos pertenecen a tarjetas Super VGA o superiores. El modo de video que utilizaremos se compone de 320 pixeles horizontales por 200 pixeles verticales, teniendo un total de 64000 pixeles. (320x200=64000)
Después de haber visto todo la teoria
necesaria, podremos crear nuestra primera rutina en lenguaje C para inicializar
el modo 13h. Al iniciarse el modo 13h, los registros de la VGA se programan
automáticamente con las características propias de este modo.
Cuando se inicia este modo la memoria de video, en este caso los 64 Kb
se limpian, es decir todos tienen el color 0 (negro). Acuérdense
de incluir la cabecera <dos.h> para utilizar la función int86
y la union REGS.
Lo que hemos hecho con esta función es simplemente llamar a la interrupción 10h con los parámetros necesarios para iniciar el modo 13h. Otra rutina que nos será de mucha utilidad será la que regresa al modo texto, esta función es similar al anterior, la única diferencia es que al registro AL se le debe pasar el valor 03h. Podríamos crear una función
general para que realice los cambios de modos de video, esto quedaría
así:
y crear dos constantes con la directiva #define, es decir: #define TEXTO 0x03
de esta manera para iniciar el modo 13h, llamaríamos a la función SetMode de la siguiente forma SetMode(GRAFICO); y para volver al modo texto simplemente: SetMode(TEXTO); Colocando pixeles en el Modo 13h Hasta el momento ya conocemos lo más básico, cambiar modos de video. Pero debemos aprender como se organizan en la memoria los pixeles. Esto es fundamental para la programación gráfica. Como ya hemos comentado, la memoria VGA está compuesta por 4 segmentos o planos, cada uno de 64 Kb. En el modo 13h estos planos se utilizan linealmente, aprovechando solo una mínima parte de cada uno. En cada momento solo se puede acceder a una única página. No podemos, por tanto, ver una página mientras dibujamos en otra. Esta es una de las limitaciones que presenta este modo. En la mayoría de los modos de 16 colores, se puede acceder simultáneamente a varias paginas, lo que permite estar dibujando en una pagina, y volcar los gráficos en otras. Por otra parte, la selección del plano, se realiza de manera automática, con los dos últimos bytes de la coordenada x (vertical), nos es algo que quede libre al programador. De los 256 Kb que tienen normalmente como mínimo las tarjetas VGA, al realizarse la selección del plano automáticamente, se desaprovechan 192 Kb (320x200=64000, solo se aprovechan 64 Kb). Para el modo 13h, el bit CHAIN-4, que como comentábamos antes, indica si el modo es lineal o planar, estará a 1, ya que estamos tratando con un modo lineal. Podríamos hacer que la VGA trabajara en este modo, como un modo de 4 planos, si modificamos este bit (poniéndolo a 0) una vez que hemos inicializado el modo 13h. La ventaja que tienen los modos planares es que se aprovecha toda la memoria, además de tener la opción de trabajar con nuevas resoluciones, que podrían llegar en el caso de 4 planos a una resolución de 640x400 a 256 colores. La dirección de comienzo de la memoria de video se encuentra en A000:0000 y su tamaño como ya vimos es de 64Kb. Aquí se guarda el contenido de la pantalla que se va redibujando constantemente a una velocidad entre 50 y 70 Hz. Si nosotros colocamos un valor de 0 a 255 (un byte) en alguna parte del offset (desplazamiento) de la memoria de video, automáticamente nos aparecerá un color (o pixel) en esa posición determinada. Si se dan cuenta ese valor de 0 a 255, corresponde a uno de los 256 colores posibles que se pueden mostrar simultáneamente en pantalla. Para colocar un pixel, simplemente se usará el segmento A000 y se calculará el desplazamiento dependiendo de las coordenadas del punto, con la siguiente fórmula: OFFSET = Y * 320 + X donde X corresponde a la coordenada vertical (0..199) e Y a la coordenada horizontal (0..319). Lo que hace esta fórmula es simplemente ubicar una línea entre 0 y 199, para eso la multiplicamos por el ancho de la pantalla, en este caso 320, y luego le sumamos la columna correspondiente entre 0 y 319. Esta fórmula se usa mucho en la programación gráfica. Si por ejemplo queremos colocar un punto en la posición (160, 100) de color 15 se hará de la siguiente forma: A000: (160*320)+100=15 así el color 15 estará ubicado en A000:51300, el cual se verá en pantalla en la posición (160,100). Gráficamente esto sería así:
Antes de crear la funcion en C para colocar un pixel, se necesitará un puntero a la memoria de video A000, esto es: unsigned char *DIR_VGA=(unsigned char *) 0xA0000000; Lo anterior también se puede hacer utilizando la funcion MK_FP, el prototipo de esta función se encuentra en dos.h unsigned char *DIR_VGA=(unsigned char *) MK_FP(0xA000, 0); Entonces nuestra función quedaría
así:
Como se dan cuenta la función es muy sencilla, pero es muy lenta ya que existe una multiplicación, y esto conlleva muchos ciclos de reloj. Una forma de acelerar esta función es utilizar ensamblador, pero todas las optimizaciones las encontrarás en la sección Optimizaciones (hacer link a optimizaciones). En vez de hacerlo con ensamblador, tenemos dos soluciones, la primera es crear una tabla con todas las multiplicaciones calculadas y la segunda calcular las multiplicaciones mediante desplazamientos de bits. 1) Tabla precalculada: Hacer esto es muy simple. Se crea un arreglo de tamaño 200 (que es la longitud del alto de la pantalla) y mediante un for se crean todos los valores, así por ejemplo si llamamos al arreglo Linea, tendríamos que Linea[0]=0, Linea[1]=320, Linea[2]=640, etc. De esta forma la fórmula del offset nos quedaría como: OFFSET = Linea[y] + x; Veamos como nos quedaría el código
para generar esta tabla:
Y la rutina para colocar un pixel nos quedaría
así:
En este caso la variable Linea deberá ser global y tendría que definirse así: unsigned int Linea[200];
2) Desplazamientos de bits: La segunda opción es más simple, en el sentido que no necesitamos una variable global para calcular el offset. Simplemente se usarán desplazamientos de bits para realizar las multiplicaciones. Recuerden que un desplazamiento a la izquierda equivale a multiplicar, y a la derecha a dividir. Entonces nuestra nueva fórmula sería: OFFSET = (y<<8) + (y<<6) + x; En la primera operación (y<<8),
hemos multiplicado y por 256 (2^8=256); en la segunda (y<<6) hemos
multiplicado y por 64 (2^6=64), por lo tanto si los sumamos, tenemos que
hemos multiplicado y por 320 (256+64=320) y por ultimo le sumamos x para
completar el offset. De esta forma nos ahorramos estar multiplicando, lo
cual acelera bastante los programas. La función nos quedaría
de la siguiente forma:
Esta función será la que utilizaremos de aquí en adelante. Por último a continuación les muestro algunas otras rutinas que pueden ser útiles: La función GetPixel nos devuelve
el color de un pixel en una posición determinada, es simplemente
la inversa de PutPixel:
Otra función que nos puede resultar
útil, es la que limpia o borra la memoria de video de un color determinado.
Para esto se puede utilizar la función memset (incluir la cabecera
<mem.h>).
Lo que se hace aquí es simplemente llenar los 64000 pixeles de la pantalla de un color determinado. Ejemplos De ahora en adelante iremos creando una “librería” llamada vgalib.h con todas las funciones que vayamos viendo. En cada sección podrás encontrar una nueva librería con el conjunto de todas las funciones vistas hasta el momento. Para compilar los programas que encuentres en este sitio debes tener en cuenta los siguientes puntos: 1) Los ejemplos del sitio y cualquier programa que hagan y que use la librería VGALIB, deben ser compilados usando algunos de estos modelos de memoria: Compact, Large, o Huge. Es necesario usar unos de estos modelos ya que estos usan punteros FAR, los cuales son necesarios para acceder a grandes cantidades de datos. 2) Sería recomendable que
activaran en su compilador la opción Word Alignment, en
3) Además sería bueno que activaran también la optimización de velocidad, esta se encuentra en: Option | Compiler | Optimizations | Optimize for Speed. A continuación podrás ver unos ejemplos utilizando todo lo que vimos. Librería gráfica: vgalib.h
|