El segundo "pequeño" problema es que incluso para la VGA, el acceso a la memoria no siempre es igual: tenemos modos como el 13h (320x200, 256 colores) en lo que cada byte corresponde a un pixel. Otros modos en los que un byte corresponde a 8 pixels (640x480 mono: 11h). En otros, tenemos 4 planos: un byte en cada uno afectará al color de ocho pixels...
Hay montones de libros que os pueden ayudar a programar en esos modos "complicados", en los que hay que fijar sólo algunos de los bits de cada byte para dibujar un pixel, o hacer cambios de plano.
Mi intención no es esa, sino daros la base para que cada uno
pueda experimentar por donde quiera, así que me voy a centrar en
el modo más fácil de programar, y que además es el
más vistoso de la VGA estándar: el modo 13h: 320x200,
256 colores.
En ese modo tenemos que cada punto de la pantalla corresponde a un byte y viceversa. Así, si el primer byte de la memoria de pantalla es un 3, quiere decir que en las coordenadas (0,0) (esquina superior izquierda) hay un punto de color 3.
Esta memoria de pantalla, en un PC con tarjeta gráfica VGA funcionado con sistema operativo MsDOS, está en el segmento A000h, luego la dirección de dicho primer punto será A000:0000. La cantidad de memoria que ocuparemos será 320x200 = 64.000 bytes.
Entonces, cambiaremos al modo gráfico como antes:
procedure Modo320x200x256; { Cambia a modo gr谩fico }
begin
regs.ah := 0; { Funci贸n 0 }
regs.al := $13; { El modo es el 13h = 19 }
intr($10,regs); { Interrupci贸n de video }
end;
y para dibujar un punto podemos acceder directamente a la posición de memoria que nos interesa:
procedure PonPixel(x,y: word; color: byte); { Dibuja Pixel }
begin
Mem[$A000 : y * 320 + x] := color;
end;
Con lo que la adaptación del ejemplo anterior es inmediata.
También podemos leer fácilmente el color de un cierto punto de la pantalla:
function LeePixel(x, y : Word) : Byte; { Color de un pixel }
begin
LeePixel := Mem[$A000 : y * 320 + x];
end;
Otra forma muy sencilla de dibujar puntos en pantalla y leerlos es
creando un array en la posición concreta de la memoria
que ya conocemos:
var
pantalla: array [ 0..319, 0..199 ] of byte absolute $A000:0000;
Dibujaríamos un punto con
pantalla [x,y] := color;
y lo leeríamos con
color := pantalla [x,y];
Así, si usamos Turbo Pascal 7, el ejemplo anterior podría quedar simplemente
{--------------------------}
{ Ejemplo en Pascal: }
{ }
{ Gr谩ficos accediendo a }
{ memoria de pantalla: }
{ 320x200, 256 colores }
{ Versi贸n para TP7 }
{ GRB2.PAS }
{ }
{ Este fuente procede de }
{ CUPAS, curso de Pascal }
{ por Nacho Cabanes }
{ }
{ Comprobado con: }
{ - Turbo Pascal 7.0 }
{--------------------------}
program GrB2;
uses dos; { Usaremos interrupciones }
const NumPuntos = 10000; { N煤mero de puntos que dibujaremos }
var
regs: registers; { Para acceder a los registros, claro }
bucle: word; { Para bucles, claro }
pantalla: array [0..319,0..199] of byte
absolute $A000:0; { Pues la pantalla ;-) }
procedure ModoPantalla( modo: byte );
{ Cambia a un modo dado }
begin
regs.ah := 0; { Funci贸n 0 }
regs.al := modo; { El modo indicado }
intr($10,regs); { Interrupci贸n de video }
end;
begin
ModoPantalla($13);
for bucle := 1 to NumPuntos do
Pantalla[ random(319), random(199)] := random(255) ;
readln;
ModoPantalla(3);
end.
Si compilamos con TMT, debemos indicarle la dirección
de memoria de la pantalla de una forma ligeramente distinta:
{--------------------------}
{ Ejemplo en Pascal: }
{ }
{ Gr谩ficos accediendo a }
{ memoria de pantalla: }
{ 320x200, 256 colores }
{ Versi贸n para TMT }
{ GRB2T.PAS }
{ }
{ Este fuente procede de }
{ CUPAS, curso de Pascal }
{ por Nacho Cabanes }
{ }
{ Comprobado con: }
{ - Tmt Pascal Lt 1.00 }
{--------------------------}
program GrB2T;
uses dos; { Usaremos interrupciones }
const NumPuntos = 10000; { N煤mero de puntos que dibujaremos }
var
regs: registers; { Para acceder a los registros, claro }
bucle: word; { Para bucles, claro }
pantalla: array [0..319,0..199] of byte
absolute $A0000; { Pues la pantalla ;-) }
procedure ModoPantalla( modo: byte );
{ Cambia a un modo dado }
begin
regs.ah := 0; { Funci贸n 0 }
regs.al := modo; { El modo indicado }
intr($10,regs); { Interrupci贸n de video }
end;
begin
ModoPantalla($13);
for bucle := 1 to NumPuntos do
Pantalla[ random(319), random(199)] := random(255) ;
readln;
ModoPantalla(3);
end.
Si empleamos Free Pascal, en general no podremos usar este método, porque existe para distintos tipos de ordenadores, con distintos sistemas operativos y diferentes subsistemas gráficos, y nada nos garantiza que la memoria de video se encuentre en esa posición de memoria.