{$I386_INTEL}
USES GO32;

TYPE  Sprite = record
               dtx,dty : longint;  { Breite, H”he des Sprites }
               adr     : pointer;  { Speicher-Adresse an der das "Bild" beginnt }
              end;

CONST ManBild : array[0..9,0..9]of byte=  { Probe-Bild (soll ein Maennchen sein ;-) }
                ((0,0,0,0,1,1,0,0,0,0),
                 (0,0,0,1,9,9,1,0,0,0),
                 (8,8,0,0,1,1,0,0,8,8),
                 (0,1,1,0,1,1,0,1,1,0),
                 (0,0,1,1,1,1,1,1,0,0),
                 (0,0,0,0,1,1,0,0,0,0),
                 (0,0,0,0,1,1,0,0,0,0),
                 (0,0,0,1,1,1,1,0,0,0),
                 (0,0,1,1,0,0,1,1,0,0),
                 (0,8,8,0,0,0,0,8,8,0));

      XRes : longint=320;
      YRes : longint=200;


VAR   VideoBuffer : pointer;
      Man : sprite;


 PROCEDURE InitVGAMode(mode : word);
  Var regs : trealregs;
 BEGIN

  regs.ax:=mode;
  realintr($10,regs);

 END;


 PROCEDURE TransSprite(dest : pointer; x,y : longint; spr : sprite);
  Var clipl,clipr,clipo, destskip,sprskip, sprdtx,sprdty : longint; spradr : pointer;
 BEGIN

  with spr do begin

   x-=dtx shr 1;  { Wird ben”tigt, um das Sprite "mittig" zu setzen }
   y-=dty shr 1;  { -> Bsp. x=160/y=100 -> Mittelpunkt des Sprites ist bei 160/100 }

   if (x+dtx<0) or (x>=xres) or (y+dty<0) or (y>=yres) then exit;
   { links auáerhalb, rechts auáerhalb, oben auáerhalb, unten auáerhalb ?? Wenn ja -> raus }

   clipr:=0;  { Alle Variablen 0 = kein Abschneiden (Clipping) }
   clipl:=0;
   clipo:=0;

   if (y<0) then begin clipo:=-y*dtx; dty+=y; y:=0; end;
   if (y+dty>=yres) then dty:=yres-y;
   if (x<0) then begin clipl:=-x; dtx+=x; x:=0; end;
   if (x+dtx>=xres) then begin clipr:=(x+dtx)-xres; dtx:=xres-x; end;
   { Wenn am Rand, dann Spritel„nge/h”he, x/y und Clipping-Variablen ver„ndern }

   if (dtx<=0) or (dty<=0) then exit;
   { Zur Sicherheit berprfen, ob durch Clipping gar nichts mehr darzustellen ist }

   dest+=x+y*xres;   { Im Videospeicher an Anfangs-Zeichen-Position des Sprites rcken }
   adr+=clipo+clipl; { Im "Spritespeicher" an erstes Pixel rcken, das dargestellt wird }

   destskip:=xres-dtx;   { Um diesen Wert mssen wir pro gezeichneter (waagrechter) Linie im Videospeicher weiter }
   sprskip:=clipl+clipr; { Das gleiche fr den Spritespeicher (Bei Clipping !!) }

   spradr:=adr;  { HilfsVariablen (FPC kann leider nicht auf den Sprite-Record ber ASM zugreifen :-( }
   sprdtx:=dtx;
   sprdty:=dty;

   ASM
    cld
    mov edi,dest
    mov esi,spradr

    mov edx,sprdty
   @@yloop:
    mov ecx,sprdtx
   @@xloop:
    lodsb           { Pixel laden }
    or  al,al       { Pixel = 0 ? }
    jz  @@go
     mov [edi],al   { Nein, also setzen }
   @@go:            { Sonst normal im Speicher weiter }
    inc edi

    dec ecx         { Noch mehr Pixel in X-Richtung ? }
    jnz @@xloop

    add esi,sprskip
    add edi,destskip

    dec edx         { Noch mehr Zeilen in Y-Richtung ? }
    jnz @@yloop
   END ['EDI', 'ESI', 'EDX', 'ECX', 'EAX'];

  end;

 END;


 PROCEDURE SolidSprite(dest : pointer; x,y : longint; spr : sprite);
  Var clipl,clipr,clipo, destskip,sprskip, sprdtx,sprdty : longint; spradr : pointer;
 BEGIN

  with spr do begin

   x-=dtx shr 1;  { Wird ben”tigt, um das Sprite "mittig" zu setzen }
   y-=dty shr 1;  { -> Bsp. x=160/y=100 -> Mittelpunkt des Sprites ist bei 160/100 }

   if (x+dtx<0) or (x>=xres) or (y+dty<0) or (y>=yres) then exit;
   { links auáerhalb, rechts auáerhalb, oben auáerhalb, unten auáerhalb ?? Wenn ja -> raus }

   clipr:=0;  { Alle Variablen 0 = kein Abschneiden (Clipping) }
   clipl:=0;
   clipo:=0;

   if (y<0) then begin clipo:=-y*dtx; dty+=y; y:=0; end;
   if (y+dty>=yres) then dty:=yres-y;
   if (x<0) then begin clipl:=-x; dtx+=x; x:=0; end;
   if (x+dtx>=xres) then begin clipr:=(x+dtx)-xres; dtx:=xres-x; end;
   { Wenn am Rand, dann Spritel„nge/h”he, x/y und Clipping-Variablen ver„ndern }

   if (dtx<=0) or (dty<=0) then exit;
   { Zur Sicherheit berprfen, ob durch Clipping gar nichts mehr darzustellen ist }

   dest+=x+y*xres;   { Im Videospeicher an Anfangs-Zeichen-Position des Sprites rcken }
   adr+=clipo+clipl; { Im "Spritespeicher" an erstes Pixel rcken, das dargestellt wird }

   destskip:=xres-dtx;   { Um diesen Wert mssen wir pro gezeichneter (waagrechter) Linie im Videospeicher weiter }
   sprskip:=clipl+clipr; { Das gleiche fr den Spritespeicher (Bei Clipping !!) }

   spradr:=adr;  { HilfsVariablen (FPC kann leider nicht auf den Sprite-Record ber ASM zugreifen :-( }
   sprdtx:=dtx;
   sprdty:=dty;

   ASM
    cld
    mov edi,dest
    mov esi,spradr

    mov edx,sprdty
   @@yloop:
    mov ecx,sprdtx
    rep movsb       { Alle Pixel in X-Richtung am Stck setzen }

    add esi,sprskip
    add edi,destskip

    dec edx         { Noch mehr Zeilen in Y-Richtung ? }
    jnz @@yloop
   END ['EDI', 'ESI', 'EDX', 'ECX'];

  end;

 END;


BEGIN

  getmem(videobuffer,64000);
  initvgamode($13);                         { 320x200,256 }

  man.adr:=@manbild;                        { Adresse des Sprites gleich Adresse unseres "Bildes" }
  man.dtx:=10;                              { Breite=10 }
  man.dty:=10;                              { H”he=10 }

  fillchar(videobuffer^,64000,10);          { "Hintergrund" = Farbe 10 }

  transsprite(videobuffer,80,100,man);      { Transparentes Sprite }
  solidsprite(videobuffer,240,100,man);     { Undurchsichtiges Sprite }

  dosmemput($a000,0,videobuffer^,64000);    { BUFFER AN $a000:0 KOPIEREN }

  readln;
  initvgamode($3);                          { TEXTMODUS }
  freemem(videobuffer,64000);

END.

{ P.S. Natrlich k”nnen die Routinen noch etwas ;-) optimiert werden... }
{ P.P.S. Alle bisherigen Lektionen arbeiten auch mit dem PMODE/DJ-Extender
         und selbstverst„ndlich auch Win9x zusammen... }