Aula Macedonia


Curso de Programación Multimedia Bajo DOS


Artículo realizado por
José Antonio Suárez.





Capítulo 9.
Programación de tarjetas de vídeo (VII).

2.3.- SVGA Estándar. El modo 640x480x256c


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:

  1. 00h = Averiguar las posibilidades de la tarjeta SVGA.
  2. 01h = Averiguar la información completa sobre un modo de vídeo.
  3. 02h = Pasar a un modo en concreto.
  4. 03h = Obtener el modo de vídeo presente o actual.
  5. 04h = Set/Reset de la SVGA.
  6. 05h = Obtener/Situar la ventana de la SVGA.
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.




AULA MACEDONIA
a
MACEDONIA Magazine