AnteriorPosterior

Ejemplo con ensamblador: sprites

  Curso: Curso de Pascal, por Nacho Cabanes

Finalmente, otro ejemplo más elaborado.  Se trata de "sprites", figuras transparentes que se mueven por la pantalla.  Con "transparentes" me refiero a que si hay algún hueco, debe verse el fondo a través suyo. Esto es totalmente imprescindible en los videojuegos: por ejemplo, mientras que anda nuestro personaje, tiene que verse el fondo entre sus piernas o junto a su cabeza en vez de un fondo negro.

Está tomado de una práctica que hice para una asignatura de la Universidad, en la que teníamos que manejar la pantalla VGA en modo gráfico 320x200x256.  Como el lenguaje era libre, empleé Pascal, que es mi favorito, y como había que conseguir el menor tamaño posible (en el ejecutable) y una cierta rapidez, incluí bastantes cosas en ensamblador. Este es el resultado...

 {--------------------------}
 {  Ejemplo en Pascal:      }
 {                          }
 {    Dibujo de "sprites"   }
 {    (imágenes transpa-    }
 {    rentes) en pantalla   }
 {    NSPRITE.PAS           }
 {                          }
 {  Este fuente procede de  }
 {  CUPAS, curso de Pascal  }
 {  por Nacho Cabanes       }
 {                          }
 {  Comprobado con:         }
 {    - Turbo Pascal 7.0    }
 {--------------------------}
 program nSprite;
 {$G+ Dibuja Sprites en Pantalla }
 const
   segVideo: word = $a000;
   NumSprites = 10;           { Número de sprites }
   xSize = 30; ySize = 30;    { Tamaño de cada uno }
 type
   tipoSprite = array[1..xSize, 1..ySize] of byte;  { El sprite en sí }
 const
   sprite : tipoSprite =
    ((0,0,0,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0),
     (0,0,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,0,0),
     (0,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,0),
     (2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3),
     (2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3),
     (2,1,1,1,1,2,2,2,2,2,2,2,2,1,1,1,1,2,2,2,2,2,2,2,2,1,1,1,1,3),
     (2,1,1,1,3,0,0,0,0,0,0,0,0,2,1,1,3,0,0,0,0,0,0,0,0,2,1,1,1,3),
     (2,1,1,1,3,0,0,0,0,0,0,0,0,2,1,1,3,0,0,0,0,0,0,0,0,2,1,1,1,3),
     (2,1,1,1,3,0,0,0,0,0,0,0,0,2,1,1,3,0,0,0,0,0,0,0,0,2,1,1,1,3),
     (2,1,1,1,3,0,0,0,0,0,2,3,0,2,1,1,3,0,0,0,0,0,2,3,0,2,1,1,1,3),
     (2,1,1,1,3,0,0,0,0,0,3,3,0,2,1,1,3,0,0,0,0,0,3,3,0,2,1,1,1,3),
     (2,1,1,1,3,0,0,0,0,0,0,0,0,2,1,1,3,0,0,0,0,0,0,0,0,2,1,1,1,3),
     (2,1,1,1,1,3,3,3,3,3,3,3,3,1,1,1,1,3,3,3,3,3,3,3,3,1,1,1,1,3),
     (2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3),
     (2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3),
     (2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3),
     (2,1,1,1,1,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,1,1,1,3),
     (2,1,1,1,3,0,0,0,2,1,1,1,1,1,1,1,1,1,1,1,1,2,0,0,0,0,1,1,1,3),
     (2,1,1,1,3,0,0,0,0,0,2,1,1,1,1,1,1,1,1,2,0,0,0,0,0,0,1,1,1,3),
     (2,1,1,1,3,0,0,0,0,0,0,0,2,1,1,1,1,2,0,0,0,0,0,0,0,0,1,1,1,3),
     (2,1,1,1,3,0,0,0,0,0,0,0,0,2,2,2,2,0,0,0,0,0,0,0,0,0,1,1,1,3),
     (2,1,1,1,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,1,1,1,3),
     (2,1,1,1,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,1,1,1,1,3),
     (2,1,1,1,1,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,1,1,1,1,1,3),
     (2,1,1,1,1,1,1,3,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,1,1,1,1,1,1,3),
     (2,1,1,1,1,1,1,1,1,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,2),
     (0,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,0),
     (0,0,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,0,0),
     (0,0,0,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,0,0,0),
     (0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0)
    );
 type SprDat= record
   x, y: word;              { Coordenadas de cada sprite }
   vx, vy: shortint;        { Velocidad (incremento) según cada eje }
 end;
 
 { --- Componentes R,G,B para un color --- }
 procedure setpal(col,r,g,b:byte); assembler;
 asm
   mov dx,03c8h;
   mov al,col;
   out dx,al;
   inc dx;
   mov al,r; out dx,al;
   mov al,g; out dx,al;
   mov al,b; out dx,al;
 end;
 
 { --- Copia 64000 bytes de una pantalla virutal a otra (física) --- }
 procedure copia(org,dst:word); assembler;
 asm
   push ds;
   mov ds,[org];
   xor si,si;
   mov es,[dst]
   xor di,di;
   mov cx,320*200/2;
   rep movsw;
   pop ds;
 end;
 
 { --- Borra 64000 bytes en una zona de memoria --- }
 procedure cls(dst:word); assembler;
 asm
   mov es,[dst];
   xor di,di;
   xor ax,ax;
   mov cx,320*200/2;
   rep stosw;
 end;
 
 { --- Sincroniza con el barrido de la pantalla --- }
 procedure retrace; assembler; asm
   mov dx,03dah;
 @vert1: in al,dx;
   test al,8;
   jnz @vert1
 @vert2: in al,dx;
   test al,8;
   jz @vert2;
   end;
 
 { --- Escribe un sprite en la pantalla --- }
 procedure putsprite(x,y,sprseg, sprofs,virseg:word); assembler;
 asm
   push ds
   mov ds,sprseg;
   mov si,sprofs            { Segmento y desplaz. del sprite }
   mov es,virseg; xor di,di { Segmento y desp. de la pantalla virtual }
   mov ax,[y];              { Numero de fila }
   shl ax,6;
   mov di,ax;
   shl ax,2;
   add di,ax                { Fila * 320 }
   add di,[x]               { Fila * 320 + columna }
   mov dx,320-xsize         { Pixels restantes en la línea }
   mov bx,ysize             { Altura del dibujo }
  @l1:
   mov cx,xsize             { Anchura de cada fila }
  @l0:
   lodsb;                   { Leo un byte del sibujo }
   or al,al;
   jz @noDib                { Si es 0 (transp.), lo salto }
   mov [es:di],al           { Si no, lo dibujo }
  @noDib:
   inc di;                  { Siguiente pixel }
   dec cx;                  { Queda uno menos por dibujar en la fila }
   jnz @l0;                 { Si aun quedan, repito }
   add di,dx;               { Si no quedan, voy al principio de la sgte fila }
   dec bx;                  { Queda una fila menos }
   jnz @l1;                 { Repito hasta que se acaben las filas }
   pop ds
 end;
 
 { --- Variables que usaré en el cuerpo --- }
 var
   PantVirt: pointer;  { La pantalla virtual }
   SegVirt: word;      { Segmento donde se encuentra }
   Fondo: pointer;     { La pantalla de fondo }
   SegFon: word;       { y del fondo }
   D:                  { Datos de cada sprite }
     array[1..numSprites] of SprDat;
   i,j: integer;       { Bucles }
   label bucle;
 
 {---------------------------- }
 { --- Cuerpo del programa --- }
 {---------------------------- }
 begin
   asm mov ax,13h; int 10h; end;  { Cambio a modo 320x200x256 }
   randomize;                     { Números aleatorios }
   getmem(PantVirt,320*200);      { Reservo y vacío pantalla virtual }
     SegVirt := seg(PantVirt^);
     cls(SegVirt);
   getmem(Fondo,320*200);         { Y la de fondo }
     SegFon:=seg(Fondo^);
     cls(SegFon);
   for i := 1 to NumSprites do    { Datos aleatorios de los Sprites }
     with d[i] do
       begin
       x := random (219-xSize)+50;
       y := random (99-ySize)+50;
       repeat
         vx := random(6) - 3;
       until vx<>0;
       repeat
         vy := random(6) - 3;
       until vy<>0;
       end;
   for i:=1 to 128 do             { Paleta de colores del fondo }
     setpal(127+i,20+i div 5,i div 3,20+i div 7);
   SetPal(1,10,10,45);
   SetPal(2,0,0,25);
   SetPal(3,20,20,60);
 
   for i:=0 to 319 do             { Dibujo el patrón de fondo }
     for j:=0 to 199 do
       mem[SegFon:j*320+i]:=128+abs(i*i-j*j) and 127;
 bucle:
     copia(SegFon,SegVirt);       { Copio el fondo en la pantalla virtual }
     for i := 1 to numSprites do  { Dibujo los sprites }
       begin
       PutSprite( d[i].x, d[i].y, seg(Sprite), ofs(Sprite), SegVirt);
       inc(d[i].x,d[i].vx);           { Actualizo las coordenadas }
       if (d[i].x < 5) or (d[i].x > (315-xSize)) then
         d[i].vx := -d[i].vx;
       inc(d[i].y,d[i].vy);
       if (d[i].y < 5) or (d[i].y > (195-ySize)) then
         d[i].vy := -d[i].vy;
       end;
     retrace;                     { Sincronizo con el barrido }
     copia(SegVirt,SegVideo);     { Y copio la pantalla virtual en la visible }
   { Repito hasta que se pulse una tecla }
   asm  mov ah, 1;  int 16h;  jz bucle; end;
   { Absorbo esa pulsación de tecla }
   asm  mov ah, 0;  int 16h;  end;
   freemem(PantVirt,320*200);     { Libero la memoria reservada }
   freemem(Fondo,320*200);
   asm mov ax,3; int 10h; end;    { Vuelvo a modo texto }
 end. 

Este programa está comprobado con Turbo Pascal 7.0. Eso sí, como reserva dos pantallas virtuales puede que no quede memoria suficiente para ejecutarlo desde el IDE normal (TURBO.EXE). Entonces habría que usar el compilador de línea de comandos (TPC.EXE) o el de modo protegido (TPX.EXE).

Actualizado el: 29-12-2011 11:40

AnteriorPosterior