[Back to GRAPHICS SWAG index] [Back to Main SWAG index] [Original]

    {Written By     : Wesley Burns                                               }
    {Email          : microcon@iafrica.com                                       }

    {Email: me if you have ANY questions about                                   }
    { : 64k DMA Sound Blaster Programming using XMS                              }
    { : Fast Memory Management                                                   }
    { : PCX using XMS                                                            }
    { : XMS Units                                                                }
    { : Pascal in general                                                        }
    { : Or if you have some fast procedures that you don't mind parting with.    }

    PROGRAM FastClipMask;
    {values: . x,y         :coords of picture
             . width,height:width and height of picture
             . maskcolor   :color to leave out
             . Sprite      :Souce of picture(Pointer)
             . Dest        :Pointer of destionation e.g Dest = ptr($a000:0000)}

    var Pic,VScreen,BScreen:Pointer;
        Counter:word;
        time:real;
        color:integer;

    {General Timer}
     Function timer:real;begin  timer:=meml[$0040:$006c]/18.2; end;

    {Wait for vertical retrace, to stop flicker}
     Procedure WaitRetrace; Begin while ((Port[$3DA] AND 8) > 0) do; while
    ((Port[$3DA] AND 8) = 0) do; End;

    {Much Faster that pascal Move Command}
     procedure DMove (var source, dest; count: word); assembler;
      asm
       push ds
       lds  si,source      {ds,si = source}
       les  di,dest        {es,di = dest}
       mov  cx,count       {cx = count}
       mov  ax,cx          {ax = count}
       cld
       shr  cx,2           {cx = count / 4}
       db   66h
       rep  movsw          {copy double words}
       mov  cl,al          {get rest bytes}
       and  cl,3
       rep  movsb          {copy rest}
       pop  ds
     end;

    {Mask Picture}
    Procedure MaskPic(X,Y,Width,Height:integer; Maskcolor:byte; Sprite,
    Dest:Pointer);
     Begin

      If (x <= -width) or (x >= 320) or (y <= -height) or (y >= 200) then exit;
       {The above command checks to seek if the picture TOTAL runs of the scneen.}
       {This is so, because if the below procedure is given a picture that falls}
       {total past >= 320X200 then it screws up.}

     Asm
       PUSH  DS             { save DS. Turbo Pascal automatically }
                            { saves other registers (ES SI DI)    }
       LDS   SI,Sprite      { Make DS:SI point to image data }
       MOV   AX,WIDTH       { store width in variable }
       MOV   DX,AX
       PUSH  DX             { Save width for use with the }
                            { actual screen copying }
       ADD   AX,X           { Add X to width, check for right side cut }
       PUSH  Width          { save origonal width for possible top cutting }
       CMP   AX,320         { Check for right side clipping }
       JG    @RightCut
       SUB   AX,X           { Check for left side clipping }
       JC    @LeftCut
       JMP   @CheckBottom   { no clipping necessary on sides }

     @RightCut:
       SUB   AX,Width
       SUB   AX,320
       NEG   AX             { AX= -(new width), NEG to get + value }
       MOV   Width,AX       { set new width according to rightclip }
       JMP   @CheckBottom

     @LeftCut:
       ADD   AX,X
       MOV   Width,AX       { set width according to left clip }
       SUB   DX,AX
       ADD   SI,DX
       XOR   BX,BX
       MOV   X,BX           { recalculate X parameter }

     @CheckBottom:
       MOV   AX,Height
       ADD   AX,Y
       CMP   AX,200         { Check for bottom cut }
       JG    @BottomCut

       SUB   AX,Y           { Check for top cut }
       JC    @TopCut
       POP   BX             { Saved Width is no longer necessary }
                            { so remove it from the stack }
       JMP   @Display       { no clipping on bottom necessary }

     @BottomCut:
       POP   BX             { remove saved width from stack }
                            { no longer necessary }
       SUB   AX,Height
       SUB   AX,200
       NEG   AX
       MOV   Height,AX       { adjust height according to clip}
       JMP   @Display

     @TopCut:
      ADD   AX,Y
      POP   BX             { retreive saved width value }
      PUSH  AX             { save AX, which is new height value }
      MOV   AX,Y
      NEG   AX
      IMUL  BX
      ADD   SI,AX          { adjust starting offset in sprite data }
                           { according to the top cut }
      POP   AX             { retrieve new height value }

      MOV   Height,AX       { adjust height according to clip}
      MOV   BX,0
      MOV   Y,BX            { Recalculate Y parameter }

     @Display:
     {  les   AX,dest}
     {  MOV   di,AX}
       LES di,dest
     {  XOR   DI,DI           { Make ES:DI point to A000h:0000h}
       MOV   AX,320
       IMUL  [Y]
       MOV   DI,AX
       ADD   DI,X            { Calculate screen offset }
       POP   DX              { Retrieve origional width }

       MOV   BX,Width        { Store values in registers }
       MOV   CX,Height       { for optimal speed }

       { The actual sprite is copied here.
         The Byte Ptr operations are used because they are faster
         than LODSB and STOSB on 386+ CPUs! Change it and see
         for yourself! The same is true with the DEC CX/JNZ
         instead of the LOOP instruction }

      @HeightLoop:               { Loop for height }
       PUSH  SI
       PUSH  DI

       PUSH  CX
       MOV   CX,BX
      @WidthLoop:                { Loop for width }
       MOV   AL,Byte Ptr [DS:SI] { get 1 byte of sprite data }
       CMP   AL,Maskcolor        { check for "transparent" color }
       JZ    @Skipped
       MOV   Byte Ptr [ES:DI],AL { Store sprite data onto screen }

      @Skipped:
       INC   SI
       INC   DI

       DEC   CX
       JNZ  @WidthLoop

       POP   CX
       POP   DI
       POP   SI
       ADD   DI,320  { Increment video memory by 1 line }
       ADD   SI,DX   { Increment Sprite data by 1 line (DX=Width)}

       DEC   CX
       JNZ  @HeightLoop

       POP   DS
       {all done, whew!}
     End;
    End;

    Begin
     asm; mov ax,13h;int 10h;end;
     Getmem(Pic,40000);       {50x50 picture}
     GetMem(VScreen,64000);  {Virtual Screen, Scratch Pad}
     GetMem(BScreen,64000);  {Background, holds "game scenery"}
     Fillchar(VScreen^,64000,0);
     Fillchar(BScreen^,64000,4);
     Color := 0;

     {make picture}
     for counter := 0 to 40000 do
      begin color := not color;
       IF COLOR = -1 THEN mem[seg(Pic^):counter] := 0;
       IF COLOR = 0 THEN mem[seg(Pic^):counter] := random(2)+1;
      end;

     {Animate }
     time:=timer;
     for counter := 0 to 199 do
      begin
       {picture has black(0) and blue(1) stripes, masking only Black(0)}
       {                             | mask color, try changing it.    }
       MaskPic(counter,counter, 200, 200, 0, Pic, VScreen); {Draw Picture To
    VIRTUAL SCREEN}
       DMove(VScreen^, mem[$A000:0000], 64000);        {Move VScreen to
    Screen($a000:0000)}
       DMove(BScreen^, VScreen^, 64000);               {Restore Background screen}
       WaitRetrace;
      end;

     {Shut Down}
     asm; mov ax,03h;int 10h;end;

     writeln;
     writeln('With Background Updating, Copying to screen and Wait Retrace');
     writeln(' Tested on 486-DX4-100MHz                : 2.91 seconds for 100x200x200 frames');
     writeln(' On This machine for 100x 200x200 frames : ', timer-time:3:2, 'seconds');

     Freemem(Pic,40000);
     FreeMem(VScreen,64000);
     FreeMem(BScreen,64000);
    End.

[Back to GRAPHICS SWAG index] [Back to Main SWAG index] [Original]