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

{$A+,B-,D+,E+,F-,G+,I+,L+,N-,O-,P-,Q-,R-,S+,T-,V-,X+,Y+}
{$M 16384,0,32786}
Program BitMap;       { rotates/pans/scales a 256x256 bitmap }
USES CRT;                 { by Paul H. Kahler   Jan 1994 }

Var   SinTable,CosTable: Array[0..255] of integer;
      Sin2Table,Cos2Table: Array[0..255] of integer;
      Map:word; {used as a pointer to the bitmap}

Procedure MakeTables;                   {Creates sin/cos tables}
Var direction:integer;
    angle:real;
begin
     For Direction:=0 to 255 do begin   {use 256 degrees in circle}
         angle:=Direction;
         angle:=angle*3.14159265/128;
         SinTable[Direction]:=round(Sin(angle)*256);
         CosTable[Direction]:=round(Cos(angle)*256);
         Sin2Table[Direction]:=round(Sin(angle+3.14159265/2)*256*1.2);
         Cos2Table[Direction]:=round(Cos(angle+3.14159265/2)*256*1.2);
     end;                 { the 1.2 accounts for pixel aspect ratio }
end;

Procedure DrawScreen(x,y,scale:word; rot:byte);
var Temp:Longint;            {used for intermediate large values}
    ddx,ddy,d2x,d2y:integer;
    i,j:word;
    label hloop,vloop,nodraw;

begin
{ the following 8 lines of code calculate a 'right' and 'down' vector used
  for scanning the source bitmap. I use quotes because these directions
  depend on the rotation. For example, with a rotation, 'right' could mean
  up and to the left while 'down' means up and to the right. Since the
  destination image (screen) is scanned left-right/top-bottom, the bitmap
  needs to be scanned in arbitrary directions to get a rotation. }

     Temp:=(CosTable[rot]);Temp:=(Temp*Scale) div 256;
     ddx:=Temp;
     Temp:=(SinTable[rot]);Temp:=(Temp*Scale) div 256;
     ddy:=Temp;

{ Different tables are used for the 'down' vector to account for the non-
  square pixels in mode 13h (320x200). The 90 degree difference is built
  into the tables. If you don't like that, then use (rot+64)and255 here
  and take the pi/2 out of CreateTables. To each his own I guess. }

     Temp:=(Cos2Table[rot]);Temp:=(Temp*SCALE) div 256;
     d2x:=Temp;
     Temp:=(Sin2Table[rot]);Temp:=(Temp*SCALE) div 256;
     d2y:=Temp;

{ Since we want to rotate around the CENTER of the screen and not the upper
  left corner, we need to move 160 pixels 'left' and 100 'up' in the bitmap.}

     i:=x-ddx*160-d2x*100; j:=y-ddy*160-d2y*100;

{ The following chunk of assembly does the good stuff. It redraws the entire
  screen by scanning left-right/top-bottom on screen while also scanning the
  bitmap in the arbitrary directions determined above. }

         ASM
                 push ds
                 mov  ax,[Map]      {get segment of bitmap}
                 mov  ds,ax
                 mov  ax,$a000      {set es: to video memory}
                 mov  es,ax
                 mov  ax,0          {set ds: to upper left corner of}
                 mov  di,ax         {the video memory}
                 mov  ax,[ddx]      {this is just to speed things up later}
                 mov  si,ax         {add ax,si  faster than  add ax,[ddx] }
                 mov  cx,200        {Number of rows on Screen}
         vloop:
                 push cx
                 mov  ax,[i]        {start scanning the source bitmap}
                 mov  dx,[j]        {at i,j which were calculated above.}
                 mov  cx,320        {Number of coulumns on screen}
         hloop:
                 add  ax,si        {add the 'right' vector to the current}
                 add  dx,[ddy]     {bitmap coordinates.  8.8 fixed point}
                 mov  bl,ah        {  bx = 256*int(y)+int(x)  }
                 mov  bh,dh
                 mov  bl,[ds:bx]   { load a pixel from source }
                 mov  [es:di],bl   { copy it to destination }
                 inc  di           { advance to next destination pixel }

         {*** by repeating the above 7 instructions 5 times, and reducing
              the loop count to 64, I have hit 37fps on a 486-33 with a
              fast video card. ***}

                 loop hloop         {End of horizontal loop}

                 mov  ax,d2x        { get the 'down' vector }
                 mov  dx,d2y

              { add  si,2 }    {** uncomment this instr. for extra fun **}

                 add  i,ax          { i,j is the starting coords for a line }
                 add  j,dx          { so this moves down one line }
                 pop  cx            { get the row count back and loop }
                 loop vloop         { End of verticle loop }
                 pop  ds            { Restore the ds }
         end;
end;

Procedure GraphMode;      {start 320x200x256 mode}
begin
     Asm
        Mov     AH,00
        Mov     AL,13h
        Int     10h
     end;
end;

Procedure AllocateMem;  {returns a segment pointer for a 64K bitmap}
label noerror;
begin
     asm
              mov   ah,$48
              mov   bx,$1000     { request 64K }
              int   $21
              jnc   noerror
              mov   ax,0000
     noerror: mov   Map,ax       { The segment pointer goes in Map }
              end;
     If Map=0 then begin
        Writeln('Could not allocate enough memory');
        Writeln('Program ending...');
        Halt;end;
end;

Procedure GiveBackMem; {returns the memory used for the map to the system}
begin
     asm
        mov  ah,$49
        mov  dx,Map
        mov  es,dx
        int  $21
     end;
end;

Procedure DrawImage;  {draws a test image which shows some limitations.}

{ If anyone stuffs in code to load a picture in a standard format
  (ie .gif .bmp etc..) I'd like if you send me a copy. Preferably
  something simple. This will have to do for now. }

Var x,y:integer;
Begin
     for x:=-32768 to 32767 do mem[Map:x]:=0;
     for y:=0 to 15 do          {this just frames the area}
        for x:=y to 255 do begin
           mem[Map:Y*256+x]:=1;
           mem[Map:X*256+y]:=2;
           end;
     for y:=16 to 47 do         { this part show Aliasing effects }
        for x:=16 to 255 do mem[Map:Y*256+x]:=2+(x and 1)+(y and 1);

     for y:= -50 to 50 do       { this draw the circles }
        for x:= round(-sqrt(2500 - y*y)) to round(sqrt(2500 - y*y)) do
          mem[Map:(y+100)*256+x+100]:=5+(X*X+Y*Y) div 100;

     for x:=0 to 100 do         { These lines also show sampling effects }
        for y:=0 to 8 do
           mem[Map:(Y*2560)+x+41100]:=5;
end;

Var    rot,dr:word;
       x,y,dist,dd:word;

Begin
     AllocateMem;
     DrawImage;
     MakeTables;
     GraphMode;
     x:=32768; y:=0;         {this corresponds to (128,0) in fixed point}
     rot:=0; dr:=1;          {rotation angle and it's delta}
     dist:=1200; dd:=65534;  {distance to bitmap (sort of) and its delta}
     repeat
        DrawScreen(x,y,dist,lo(rot));
        rot:=rot+dr;
        y:=y+128;      {slow panning. 1/2 pixel per frame}
        dist:=dist+dd;
        if (dist=2000) or (dist=2) then dd:=-dd;
        if random(150)=1 then dr:=random(7)-3;
     until keypressed;
     GiveBackMem;
     ASM {back to 80x25}
      MOV AX,3
      INT 10h
     END;
end.

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