|
|
Artículo realizado por
Como ya se comentó anteriormente, las bajas capacidades gráficas
del ordenador PC han sido progresivamente aumentadas y mejoradas a lo largo
de los años. De las tarjetas monocromas iniciales hemos ido pasando
por una serie de siglas que a todos se nos antojan de los más familiares.
De MDA a CGA, de éstas a la EGA, de la EGA a VGA y finalmente a
las superpoderosas SVGA. Cada una de ellas iba añadiendo colorido
y resolución a nuestros pobres gráficos.
Las tarjetas SVGA han llegado para romper con todo eso. Ahora, es posible
tener 640x480 píxels con 16 millones de colores simultáneos.
Pero en detrimento de esto, las SVGA son lentas, muy lentas, ¡exageradamente
lentas! como para que se pueda programar algo con mediana acción
y rapidez visual. Aunque aún así, ganamos mucho en calidad
visual.
2.3.1.- El Estándar VESA
El principal problema de la SVGA aparte de su lentitud, viene cuando
se la intenta programar. Esto es así porque las SVGA son muy graciosas,
es decir: son incompatibles entre ellas. Cada una requiere una programación
diferente y acomodada a cada caso, dando lugar a un verdadero caos informático.
Nombres como TRIDENT, Cirrus, Ahead, ATI, Wonder, Number Nine, etc., se
han sumado a la larga lista de pseudoestándares del grafismo en
alta resolución.
Para solucionar esto, se inventó el estándar VESA, el
cual viene a dictar unas normas comunes que se han de seguir en todas las
tarjetas. Ha costado su tiempo, pero la gran mayoría de las tarjetas
ya traen el driver VESA en su VÍDEO ROM BIOS, es decir, grabada
en el firmware de la propia tarjeta. La que no tenga esta capacidad ha
de valerse de un driver software para que se acople al estándar.
Aquí se incluye un programa freeware para hacer que nuestra SVGA
cumpla el estándar VESA.
La incompatibilidad viene dada en tres aspectos fundamentales: la organización
de la VRAM (RAM de Vídeo), la numeración de los modos de
vídeo y el direccionamiento a la memoria de vídeo. El resultado
de esto es que un programa para una tarjeta no funcionará nunca
en otra.
El driver VESA es accesible como si de una subfunción de la interrupción
10h se tratase. Es la subfunción 4Fh y permite a su vez 6 posibles
tareas:
José Antonio Suárez.
Capítulo 9.
Programación de tarjetas de vídeo (VII).
2.3.- SVGA Estándar. El modo 640x480x256c
Los modos VESA estandarizados son 9:
Numeración | 100h | 101h | 102h | 103h | 104h | 105h | 106h | 107h | 6Ah |
Resolución
X / Y |
640
400 |
640
480 |
800
600 |
800
600 |
1024
768 |
1024
768 |
1280
1024 |
1280
1024 |
800
600 |
Colores | 256 | 256 | 16 | 256 | 16 | 256 | 16 | 256 | 16 |
Y el procedimiento que se ocupa de la selección del modo de vídeo VESA 640x480 con 256 colores dado aquí, es el siguiente:
void ModoVESA64x48(void)
{
asm{
mov ah,0x4f
mov al,0x2
mov bx,0x101
int
0x10
}
}
Simplísimo. ¡Gracias, VESA!
2.3.2.- La Memoria de Vídeo de la SVGA
Hasta ahora, hemos visto cómo sobrepasar el límite de los 64Kb de la VGA para llegar a los 256, pero todos sabemos que las tarjetas actuales disponen como mínimo de 1 Mb de VRAM. Las SVGA y sus modos extendidos necesitan más de 256Kb de memoria, pero se encuentran con el problema del acceso estándar del espacio de direcciones, por razones de compatibilidad.
Las ventanas de vídeo proveen una forma de acceder a la memoria "extra" de la tarjeta SVGA que de otra forma sería imposible alcanzar dentro de las direcciones de memoria de la CPU.
La primera consideración que hay que tener cuando se implementa este modo es permitir el acceso a la memoria de vídeo a los procedimientos.
El espacio de direcciones de la VGA para el modo 13h comentado anteriormente, comienza en el segmento A000h (de la RAM del ordenador) y tiene una longitud de 64Kb. Esto permite acceder a los 256Kb de la RAM de la tarjeta, esto es, 64Kb por plano. Para acceder a la completitud de los 1024Kb de la SVGA se mapean porciones de memoria de vídeo a partir de A000h.
Vamos a intentar aclarar esto.
Una pantalla en el modo 640x480x256c ocupa 307200 bytes. La zona de memoria en que escribimos los bytes para que la circuitería de vídeo lea de ahí y los sitúe en su propia memoria comienza en A000h, tal y como se explicó anteriormente. Cuando estábamos en el modo 320x200x256c necesitábamos 64000 bytes para una pantalla, que cabían perfectamente en el segmento que comienza a partir de A000h. Pero ahora ese rango de 64Kb disponible se nos queda corto para almacenar los 307200 bytes que hay que pasarle a la tarjeta de vídeo para que ésta los ponga en su memoria y posteriormente se vea en la pantalla.
La forma de conseguir esto es mediante el mapeado de memoria, o lo que es lo mismo, la división del bloque de 307200 bytes en 4 bloques de 64Kbytes y un bloque más de 45060 bytes. De esta forma vamos poniendo los bloques en A000h y la tarjeta los va leyendo secuencialmente. Pero aún nos falta algo, ya que si fuéramos dándole los 65536 bytes primeros, luego los segundos, los terceros y los cuartos, y finalmente los 45060 finales, la tarjeta los iría poniendo siempre a partir de la dirección 0 de su propia memoria, pisándose unos a otros. Para decirle que cuando le hayamos dado los primeros 64Kb los ponga en el principio (Banco 0), los siguientes 64Kb a continuación del último (Banco 1), y así hasta llegar al Banco 4 donde almacenará los 45060 bytes finales, hace falta usar el servicio 5h de la subfunción 4Fh de VESA.
El procedimiento que hará esto es:
void CambiaBancoVESA(BYTE banco)
{
asm{
mov ah,0x4f
mov al,0x5
xor bx,bx
xor dh,dh
mov dl,banco
int 0x10
}
}
Así, vamos poniendo las porciones de la pantalla en la memoria de vídeo. Es mucho más costoso y tedioso que el "simple" modo 13h estándar, pero la calidad de las imágenes es muy superior.
Como es lógico, aquí implementamos el procedimiento para leer una pantalla gráfica que almacene los 307200 bytes que la conforman. El formato también es el PCX utilizado para las pantallas en MCGA, pero con la salvedad de que para paliar en parte la lentitud de la enorme cantidad de bytes que ahora hay que descomprimir, se usan cinco zonas de memoria que almacenarán los cinco bloques que luego se volcarán a partir de A000h como se ha expuesto antes.
El procedimiento que lo hace es:
void LeePcx64x48(char [12],BYTE *,BYTE *,BYTE *,BYTE *,BYTE *);
Y usa el mismo método de descompresión PCX pero ahora tendrá que descomprimir más bytes y almacenarlos en distintas zonas de memoria.
Ahora bien, una vez que tenemos la pantalla en memoria RAM y la queremos pasar a memoria VRAM, usamos el procedimiento siguiente:
void VuelcaPantalla64x48(BYTE *banco0,BYTE
*banco1,BYTE *banco2, BYTE banco3,BYTE banco4)
{
register int segmento0,segmento1,segmento2,segmento3,segmento4;
register int desplaza0,desplaza1,desplaza2,desplaza3,desplaza4;
segmento0=FP_SEG(banco0); desplaza0=FP_OFF(banco0);
segmento1=FP_SEG(banco1); desplaza1=FP_OFF(banco1);
segmento2=FP_SEG(banco2); desplaza2=FP_OFF(banco2);
segmento3=FP_SEG(banco3); desplaza3=FP_OFF(banco3);
segmento4=FP_SEG(banco4); desplaza4=FP_OFF(banco4);
asm{
push ds
mov ds,segmento0
mov si,desplaza0
// Cambio de banco=0
mov ah,0x4f
mov al,0x5
xor bx,bx
xor dh,dh
mov dl,0
int 0x10
// Destino: la memoria buffer a la que accede la tarjeta:A000h
mov ax,0xA000
mov es,ax
xor di,di
// 65534 veces [ds:si]->[es:di]. 65534 bytes = 32767 words
mov cx,32767
rep movsw
// El byte que falta de los 1ºs 65535. 1 vez [ds:si]->[es:di]
movsb
// Primer byte de la segunda zona de memoria y que completan
// los 65536 bytes -> 64 kbytes asignables al primer banco de la Svga
mov ds,segmento1
mov si,desplaza1
movsb
// Cambio al banco 1
mov ah,0x4f
mov al,0x5
xor bx,bx
xor dh,dh
mov dl,1
int 0x10
// Destino, buffer de vídeo
mov ax,0xA000
mov es,ax
xor di,di
// Forzar posición par en memoria
movsb
// 65532 veces lo mismo que antes
mov cx,32766
rep movsw
movsb
// Ahora el desfase es de 2 bytes y se va a ir incrementando en 1
mov ds,segmento2
mov si,desplaza2
movsw
// Cambio al banco 2
mov ah,0x4f
mov al,0x5
xor bx,bx
xor dh,dh
mov dl,2
int 0x10
// Y vuelta a empezar con la siguiente zona de memoria
mov ax,0xA000
mov es,ax
xor di,di
// 65532 veces lo mismo que antes
mov cx,32766
rep movsw
// Byte que falta: 32766 words + 1 byte = 65533
movsb
// Desfase= 3 bytes
mov ds,segmento3
mov si,desplaza3
movsw
movsb
// Cambio al banco 3
mov ah,0x4f
mov al,0x5
xor bx,bx
xor dh,dh
mov dl,3
int 0x10
// Siguiente zona de memoria
mov ax,0xA000
mov es,ax
xor di,di
// Forzar posición par en memoria
movsb
// 65530 veces lo mismo que antes
mov cx,32765
rep movsw
movsb
// Desfase= 4 bytes
mov ds,segmento4
mov si,desplaza4
movsw
movsw
// Cambio al banco 4
mov ah,0x4f
mov al,0x5
xor bx,bx
xor dh,dh
mov dl,4
int 0x10
// Siguiente zona de memoria y última
mov ax,0xA000
mov es,ax
xor di,di
// 45056 veces lo mismo que antes: 22528*2
mov cx,22528
rep movsw
pop ds
}
}
¿Dónde estaba aquél simple VuelcaPantallaMcga?
Como se ve, es infinitamente más complejo que el procedimiento de volcado de pantalla en MCGA (muuuucho más complejo). Esto es debido a la necesidad de los cambios de bancos, los desfases existentes entre cada asignación de porciones de memoria, la cantidad de información... También se puede apreciar la inclusión del procedimiento CambiaBancoVESA que en lugar de llamarse, se incluye varias veces. Esto como comentamos ya, ha sido realizado así para evitar la pérdida de tiempo en las llamadas a procedimientos por los cambios de contexto. Esta pérdida de velocidad es bastante notable si en lugar de hacerlo así, se hace con las respectivas llamadas al procedimiento del cambio de banco.
Aparte de esto, los procedimientos de paleta pueden aplicarse tal cuales
sobre este modo, ya que todos los utilizados aquí son de 256 colores,
con lo que son compatibles con los tres modos gráficos analizados.
|
|