Pascal Vga

Vga

UNIT VGA;

(****************************************************************************)
INTERFACE
(****************************************************************************)

(****************************************************************************)
(* Here we go ... procedure definitions. :)                                 *)
(****************************************************************************)

 (** This one changes the screenmode to N ***********************************)
   PROCEDURE SetMode(N : DWORD);

 (** Set a certain color in the VGA palette. ********************************)
   PROCEDURE SetColor(N, R, G, B : BYTE);

 (** Set a color fade between two given colors. *****************************)
   PROCEDURE SetColorFade(N1,N2,R1,G1,B1,R2,G2,B2 : LONGINT);

 (** Allocate a new fake screen. Returns offset *****************************)
   FUNCTION  NewPage : DWORD;

 (** Disposes a fake screen. ************************************************)
   PROCEDURE DisposePage(PageOFfset : DWORD);

 (** The standard pixel procedures. *****************************************)
   PROCEDURE PutPixel(WHERE : DWORD; X,Y : DWORD; C : BYTE);
   PROCEDURE AddPixel(WHERE : DWORD; X,Y : DWORD; C : BYTE);
   FUNCTION  GetPixel(WHERE : DWORD; X,Y : DWORD) : BYTE;

 (* Line drawing procedures *************************************************)
   PROCEDURE Line(WHERE : DWORD; X1, Y1, X2, Y2 : LONGINT; C : BYTE);
   PROCEDURE AddLine(WHERE : DWORD; X1, Y1, X2, Y2 : LONGINT; C : BYTE);
   PROCEDURE GshadeLine(WHERE : DWORD; X1, Y1, X2, Y2 : LONGINT; C1, C2 : BYTE);

 (* Clear a given fake screen ***********************************************)
   VAR ClearPage : PROCEDURE(WHERE : DWORD);

 (* Fills a screen with a given value. **************************************)
   PROCEDURE FillPage(WHERE : DWORD; Color : BYTE);

 (* Copies one page to another. *********************************************)
   VAR CopyPage  : PROCEDURE(From, Too : DWORD);

 (* Transparant copy (no color 0) *******************************************)
   PROCEDURE CopyPageT(FROM, TOO : DWORD);

 (* Loads a 256*256 RAW image. (as source for rotate) ***********************)
   PROCEDURE LoadRAW(Where : DWORD; Name : STRING);

 (* Plots a pixel on a 256*256 image. ***************************************)
   PROCEDURE PutPixel255(Where : DWORD; X, Y, C : BYTE);

 (* Grabs a pixel from a 256*256 image. *************************************)
   FUNCTION GetPixel255(Where : DWORD; X, Y : BYTE): BYTE;

 (* This one rotates a 256*256 source to a 320*200 screen. ******************)
   PROCEDURE Rotate(From, Too : DWORD; Rot : DWORD; X, Y, SCALE : LONGINT);

 (* Transparant rotate. (No color 0) ****************************************)
   PROCEDURE RotateT(From, Too : DWORD; Rot : DWORD; X, Y, SCALE : LONGINT);

 (* Transparant Rotate Warp. (WarpX and WarpY should be < 8) ****************)
   PROCEDURE RotateTW(From, Too : DWORD; Rot : DWORD; X, Y, SCALE : LONGINT; WARPX, WARPY : WORD);

 (* Same as above but high precision warp. warpx and warpy < 2048 ***********)
   PROCEDURE RotateTW2(From, Too : DWORD; Rot : DWORD; X, Y, SCALE, WARPX, WARPY : LONGINT);

 (* Copying pages (320*200) whilst smudging them ****************************)
   VAR CopyPageSmudge:PROCEDURE(FROM,TOO : DWORD);

 (* Copy smudge, check out to know difference :)  ***************************)
   PROCEDURE CopyPageSmudge2(FROM, TOO : DWORD);

 (* Average two pages. Too := (From1+From2)/2 *******************************)
   VAR MixPages  :PROCEDURE(From1,From2,Too : DWORD);

 (* Saturate two pages. too := (From1+From2)<255 ****************************)
   VAR MixPages2 :PROCEDURE(From1,From2,Too : DWORD);

 (* Alpha blend two pages. amount = 255 -> 100% from1 ***********************)
   VAR MixPages3 :PROCEDURE(From1,From2,Too : DWORD; Amount : BYTE);

 (* Bumpmapping with mutliple lightsources **********************************)
   PROCEDURE BumpMap(FROM, TOO, LIGHT : DWORD; LX, LY : WORD);

 (* Bumpmapping with one lightsource. ***************************************)
   PROCEDURE BumpMap2(FROM, TOO, LIGHT : DWORD; LX, LY : INTEGER);

 (* Bumpmapping with one lightsource. (no zeros) ****************************)
   PROCEDURE BumpMap2t(FROM, TOO, LIGHT : DWORD; LX, LY : INTEGER);

 (* Bumpmapping with one lightsource. height+slope **************************)
   PROCEDURE BumpMap3(FROM, TOO, LIGHT : DWORD; LX, LY : INTEGER);

 (* Bumpmapping with one lightsource. height+slope+transparent **************)
   PROCEDURE BumpMap3t(FROM, TOO, LIGHT : DWORD; LX, LY : INTEGER);

 (* Scale a 320*200 to 256*256 (to use as rotatesource) *********************)
   PROCEDURE Scale320to256(FROM, TOO : DWORD);
   PROCEDURE Scale256to320(FROM, TOO : DWORD);

 (* Apply distort relocation maps to 256*256 source giving 320*200 **********)
   PROCEDURE Relocate(From, Too, Distort1, Distort2, D1Ofs, D2Ofs : LONGINT);

 (* Relocate not doing color 0. *********************************************)
   PROCEDURE RelocateT(From, Too, Distort1, Distort2, D1Ofs, D2Ofs : LONGINT);

 (* This one does a fire effect. ********************************************)
   PROCEDURE DoFire(From, Too, Flame : DWORD);

 (* Use this one to init the standard light for the bumpmap. ****************)
   PROCEDURE InitBump(Light : DWORD);
   PROCEDURE InitBumpSmall(Light : DWORD);

 (* This one does a ripple. *************************************************)
   VAR Ripple : PROCEDURE(VAR Page1, Page2 : DWORD);

 (* This one does the displacement effect. **********************************)
   PROCEDURE DisPlace(From, Too, Disp : DWORD);

 (* Use these to produce some standard distort maps. ************************)
   PROCEDURE InitTunnel(Distort1, Distort2 : DWORD);
   PROCEDURE InitTunnel2(Distort1, Distort2 : DWORD);
   PROCEDURE InitTunnelFast(Distort1, Distort2 : DWORD);
   PROCEDURE InitFlashke(Distort1, Distort2 : DWORD);
   PROCEDURE InitSpiral(Distort1, Distort2 : DWORD);

(****************************************************************************)
IMPLEMENTATION
(****************************************************************************)
(* MMX instructions don't take direct operands ... that would btw mean an   *)
(* opcode with 8 bytes extra :) ... so ... here's some mask I use to mask   *)
(* out the upper bit of every byte since mmx doesn't have PSRLB !! ..       *)
(****************************************************************************)
CONST SomeBitsOutMask : ARRAY[0..7] OF CHAR = ('','','','','','','','');
      Mask512W : ARRAY[0..3] OF WORD = (512,512,512,512);
      Mask128W : ARRAY[0..3] OF WORD = (128,128,128,128);
(****************************************************************************)
(* This procedure sets the screenmode to a desired mode number.             *)
(****************************************************************************)
  PROCEDURE SetMode(N : DWORD); ASSEMBLER; ASM
    MOV  EAX, [N]                          (* Get modenr.                   *)
    INT  $10                               (* Call video interrupt.         *)
  END;


(****************************************************************************)
(* This one plots a pixel on a 320*200 screen.                              *)
(****************************************************************************)
  PROCEDURE PutPixel(WHERE : DWORD;  (* Offset of destination screen.       *)
                     X,Y : DWORD;    (* Positions in destination.           *)
                     C : BYTE);      (* Color of pixel to plot.             *)
  ASSEMBLER; ASM
  (* What we do here : MEM[Where + X*320 + Y] := C. Just a lot faster :)    *)
    MOV  EAX, [X]             (* Get the line.                              *)
    MOV  EDI, [Y]             (* Get the column number.                     *)
    SHL  EAX, 6               (* Line*64                                    *)
    ADD  EDI, [WHERE]         (* Add source pointer to column.              *)
    MOV  BL,  [C]             (* Get Color.                                 *)
    LEA  EAX, [EAX+4*EAX]     (* Eax := (Line*64) + 4*(line*64) =  Line*320 *)
    MOV  [EDI+EAX], BL        (* Put the color in its place.                *)
  END;


(****************************************************************************)
(* This one adds a pixel to the value current on screen                     *)
(****************************************************************************)
  PROCEDURE AddPixel(WHERE : DWORD;  (* Offset of destination.              *)
                     X,Y : DWORD;    (* Positions of destination pixel.     *)
                     C : BYTE);      (* Amount to add to pixel.             *)
  ASSEMBLER; ASM
  (* What we do here : MEM[Where + X*320 + Y] +:= C. Just a lot faster :)   *)
    MOV  EAX, [X]             (* Get the line.                              *)
    MOV  EDI, [Y]             (* Get the column number.                     *)
    SHL  EAX, 6               (* Line*64                                    *)
    ADD  EDI, [WHERE]         (* Add source pointer to column.              *)
    MOV  BL,  [C]             (* Get Color.                                 *)
    LEA  EAX, [EAX+4*EAX]     (* Eax := (Line*64) + 4*(line*64) =  Line*320 *)
    ADD  [EDI+EAX], BL        (* Add the color to the pixel there.          *)
  END;


(****************************************************************************)
(* This one grabs a pixel from a given screen.                              *)
(****************************************************************************)
  FUNCTION GetPixel(WHERE : DWORD;  (* Offset of page to grab pixel from.   *)
                    X, Y : DWORD)   (* Positions of pixel.                  *)
           : BYTE;                  (* The color returned.                  *)
  ASSEMBLER; ASM
    MOV  EDX, [X]             (* Get line                                   *)
    MOV  EDI, [Y]             (* Get column.                                *)
    SHL  EDX, 6               (* EDX = Line*64                              *)
    ADD  EDI, [WHERE]         (* Add offset of source to column.            *)
    LEA  EDX, [EDX+4*EDX]     (* EDX = (Line*64)+4*(Line*64) = Line*320     *)
    MOV  AL,  [EDI+EDX]       (* Grab pixel.                                *)
  END;


(****************************************************************************)
(* This one draws a line on screen in color C                               *)
(****************************************************************************)
  PROCEDURE Line(WHERE : DWORD;     (* Offset of destination.               *)
                 X1, Y1,            (* Positions of begin pixel.            *)
                 X2, Y2 : LONGINT;  (* Positions of ending pixel.           *)
                 C : BYTE);         (* Color to plot line in.               *)
  VAR CX, CY : LONGINT;   (* Current X and Current Y. 16:16 fixed point.    *)
      DX, DY : LONGINT;   (* Delta X and Delta Y.     16:16 fp.             *)
      L1, L2 : LONGINT;   (* Length(L1) and Loop(L2). Temporary variables.  *)
  BEGIN
  (** Step 0 : Figure out length of line. ***********************************)
    IF ABS(X2-X1) > ABS(Y2-Y1) THEN L1 := ABS(X2-X1)
                               ELSE L1 := ABS(Y2-Y1);
  (** Step 1 : Avoid lines that are of 0 length. ****************************)
    IF L1 = 0 THEN INC(L1);
  (** Step 2 : Calculate starting positions. ********************************)
    CX := X1 SHL 16;
    CY := Y1 SHL 16;
  (** Step 3 : Calculate delta's. What do we need to add every pixel ? ******)
    DX := ((X2-X1) SHL 16) DIV L1;
    DY := ((Y2-Y1) SHL 16) DIV L1;
  (** Step 4 : Loop for as many pixels as there are in the line. ************)
    FOR L2 := 0 TO L1 DO BEGIN
    (** Step 4a : Plot the current pixel. ***********************************)
      PutPixel(Where, CX SHR 16, CY SHR 16, C);
    (** Step 4b : Update the positions. *************************************)
      CX +:= DX;
      CY +:= DY;
    END;
  END;


(****************************************************************************)
(* Same thing, but this time adds a line.                                   *)
(****************************************************************************)
  PROCEDURE AddLine(WHERE : DWORD;   (* Offset of destination screen.       *)
                    X1, Y1,          (* Beginpixel positions                *)
                    X2, Y2 : LONGINT;(* Endpixel positions.                 *)
                    C : BYTE);       (* Amount to add.                      *)
  VAR CX, CY : LONGINT;   (* Current X and Current Y. 16:16 fixed point.    *)
      DX, DY : LONGINT;   (* Delta X and Delta Y.     16:16 fp.             *)
      L1, L2 : LONGINT;   (* Length(L1) and Loop(L2). Temporary variables.  *)
  BEGIN
  (** Step 0 : Figure out length of line. ***********************************)
    IF ABS(X2-X1) > ABS(Y2-Y1) THEN L1 := ABS(X2-X1)
                               ELSE L1 := ABS(Y2-Y1);
  (** Step 1 : Avoid lines that are of 0 length. ****************************)
    IF L1 = 0 THEN INC(L1);
  (** Step 2 : Calculate starting positions. ********************************)
    CX := X1 SHL 16;
    CY := Y1 SHL 16;
  (** Step 3 : Calculate delta's. What do we need to add every pixel ? ******)
    DX := ((X2-X1) SHL 16) DIV L1;
    DY := ((Y2-Y1) SHL 16) DIV L1;
  (** Step 4 : Loop for as many pixels as there are in the line. ************)
    FOR L2 := 0 TO L1 DO BEGIN
    (** Step 4a : Add to the current pixel. *********************************)
      AddPixel(Where, CX SHR 16, CY SHR 16, C);
    (** Step 4b : Update the positions. *************************************)
      CX +:= DX;
      CY +:= DY;
    END;
  END;


(****************************************************************************)
(* This procedure draws a shaded line on where.                             *)
(****************************************************************************)
  PROCEDURE GshadeLine(WHERE : DWORD;     (* Offset of destination.         *)
                 X1, Y1,            (* Positions of begin pixel.            *)
                 X2, Y2 : LONGINT;  (* Positions of ending pixel.           *)
                 C1, C2 : BYTE);    (* Color to plot line in.               *)
  VAR CX, CY, CC : LONGINT;
      DX, DY, DC : LONGINT;
      L1, L2 : LONGINT;
  BEGIN
  (** Step 0 : Figure out length of line. ***********************************)
    IF ABS(X2-X1) > ABS(Y2-Y1) THEN L1 := ABS(X2-X1)
                               ELSE L1 := ABS(Y2-Y1);
  (** Step 1 : Avoid lines that are of 0 length. ****************************)
    IF L1 = 0 THEN INC(L1);
  (** Step 2 : Calculate starting positions. ********************************)
    CX := X1 SHL 16;
    CY := Y1 SHL 16;
    CC := C1 SHL 16;
  (** Step 3 : Calculate delta's. What do we need to add every pixel ? ******)
    DX := ((X2-X1) SHL 16) DIV L1;
    DY := ((Y2-Y1) SHL 16) DIV L1;
    DC := ((C2-C1) SHL 16) DIV L1;
  (** Step 4 : Loop for as many pixels as there are in the line. ************)
    FOR L2 := 0 TO L1 DO BEGIN
    (** Step 4a : Plot the current pixel. ***********************************)
      PutPixel(Where, CX SHR 16, CY SHR 16, CC SHR 16);
    (** Step 4b : Update the positions. *************************************)
      CX +:= DX;
      CY +:= DY;
      CC +:= DC;
    END;
  END;



(****************************************************************************)
(* This procedure sets one palette entry of the VGA to specified colors.    *)
(****************************************************************************)
  PROCEDURE SetColor(N,                (* Nr of color to set.               *)
                     R, G, B : BYTE);  (* Red green and blue component.     *)
  (* Color components are expected to be 0-255 !!!!!!!!!!!!!!!!!!!!!!!!!!!  *)
  ASSEMBLER; ASM
    MOV  DX, $3C7       (* Port $3c7 =  Set palette write index.            *)
    MOV  AL, [N]        (* Get the colorNr                                  *)
    OUT  DX, AL         (* Send it to the VGA card.                         *)
    ADD  DX, 2          (* Port $3c9 =  Palette data Write/read             *)
    MOV  AL, [R]        (* Get the red component.                           *)
    SHR  AL, 2          (* Rescale from 0-255 to standard vga 0-63          *)
    OUT  DX, AL         (* Send it to the VGA card.                         *)
    MOV  AL, [G]        (* Get the green component.                         *)
    SHR  AL, 2          (* Rescale it.                                      *)
    OUT  DX, AL         (* Send it.                                         *)
    MOV  AL, [B]        (* Get the blue component.                          *)
    SHR  AL, 2          (* Rescale it.                                      *)
    OUT  DX, AL         (* send it.                                         *)
  END;


(****************************************************************************)
(* This procedure creates a shade of colors between N1 and N2               *)
(****************************************************************************)
  PROCEDURE SetColorFade(N1,                 (* Starting color nr.          *)
                         N2,                 (* Ending color nr. (N1 < N2)  *)
                         R1,G1,B1,           (* Starting color components.  *)
                         R2,G2,B2 : LONGINT);(* Ending color components.    *)
  VAR CR, CG, CB : LONGINT;  (* Current red, green and blue values. 16:16   *)
      DR, DG, DB : LONGINT;  (* Delta red, green and blue.          16:16   *)
      Counter    : BYTE;     (* a counter. To count shit :)                 *)
  BEGIN
  (** Step 1 : Calculate delta values. **************************************)
    DR := ( (R2-R1) SHL 16) DIV (N2-N1);
    DG := ( (G2-G1) SHL 16) DIV (N2-N1);
    DB := ( (B2-B1) SHL 16) DIV (N2-N1);
  (** Step 2 : Get starting values. *****************************************)
    CR := R1 SHL 16;
    CG := G1 SHL 16;
    CB := B1 SHL 16;
  (** Step 3 : Do the shit :) ***********************************************)
    FOR Counter := N1 TO N2-1 DO BEGIN (* Repeat for all colors.            *)
      PORT[$3c7] := Counter;           (* Send color nr.                    *)
      PORT[$3c9] := CR SHR 18; CR+:=DR;(* Send current red and update.      *)
      PORT[$3c9] := CG SHR 18; CG+:=DG;(* Send current green and update.    *)
      PORT[$3c9] := CB SHR 18; CB+:=DB;(* Send current blue and update.     *)
    END;
  END;


(****************************************************************************)
(* This one fills the screen with a certain color.                          *)
(****************************************************************************)
   PROCEDURE FillPage(WHERE : DWORD; Color : BYTE); ASSEMBLER; ASM
      MOV  EDI, [WHERE]
      MOV  AL,  [COLOR]
      MOV  AH,  AL
      MOV  BX,  AX
      SHL  EAX, 16
      MOV  AX,  BX
      MOV  ECX, 16384
      @LOOP:
        MOV [EDI], EAX
        ADD EDI, 4
        DEC ECX
      JNZ @LOOP
   END;


(****************************************************************************)
(* This one fills the screen completely with 0. mmx version.                *)
(****************************************************************************)
  PROCEDURE ClearPage_mmx(Where : DWORD); ASSEMBLER; ASM
    MOV  EDI, [Where]      (* Get destination pointer.                      *)
    PSLLW mm0, 32          (* Clear mm0 register.                           *)
    MOVQ mm1, mm0          (* Clear mm1 too.                                *)
    MOV  ECX, 4096         (* Repeat 4096 times. (4096*16=65536)            *)
    @LOOP:
      MOVQ [EDI], mm0      (* Send mm0.                                     *)
      MOVQ [EDI+8], mm1    (* Send mm1.                                     *)
      ADD EDI, 16          (* Voila, 16 bytes done.                         *)
    DEC ECX                (* Decrement counter.                            *)
    JNZ @LOOP              (* Jump when counter is not zero.                *)
    EMMS                   (* Exit multimediastate.                         *)
  END;


(****************************************************************************)
(* This one fills the screen completely with zeroes, x86 version            *)
(****************************************************************************)
  PROCEDURE ClearPage_x86(Where : DWORD); ASSEMBLER; ASM
    MOV  EDI, [Where]      (* Get destination pointer.                      *)
    XOR  EAX, EAX          (* Clear EAX.                                    *)
    MOV  ECX, 8192         (* Repeat 8192 times. (8192*8=65536)             *)
    @LOOP:
      MOV [EDI], EAX       (* Send EAX.                                     *)
      MOV [EDI+4], EAX     (* Send EAX one dword further.                   *)
      ADD EDI, 8           (* Increase destination pointer with 8.          *)
    DEC ECX                (* Decrease pixelcounter.                        *)
    JNZ @LOOP              (* Jump when counter is not zero.                *)
  END;


(****************************************************************************)
(* This one copies one page to another, mmx version                         *)
(****************************************************************************)
  PROCEDURE CopyPage_mmx(From,         (* Offset of source page.            *)
                         Too : DWORD); (* Offset of destination page.       *)
  ASSEMBLER; ASM
    MOV  EDI, [TOO]        (* Get destination pointer.                      *)
    MOV  ECX, 4096         (* Set counter. (4096*16=65536)                  *)
    MOV  ESI, [FROM]       (* Get source pointer                            *)
    @LOOP:
      MOVQ mm0, [ESI]      (* Read First 8 bytes into mm0                   *)
      MOVQ mm1, [ESI+8]    (* Read next 8 bytes into mm1                    *)
      ADD  ESI, 16         (* Increase sourcepointer with 16                *)
      MOVQ [EDI], mm0      (* Store first 8 bytes.                          *)
      MOVQ [EDI+8], mm1    (* Store next 8 bytes.                           *)
      ADD  EDI, 16         (* Increase destination pointer with 16          *)
    DEC  ECX               (* Decrease loop counter.                        *)
    JNZ  @LOOP             (* Jump unless counter = 0                       *)
    EMMS                   (* Exit multimediastate                          *)
  END;


(****************************************************************************)
(* This one copies one page to another, x86 version                         *)
(****************************************************************************)
  PROCEDURE CopyPage_X86(From,         (* Offset of source page.            *)
                         Too : DWORD); (* Offset of destination page.       *)
  ASSEMBLER; ASM
    MOV  EDI, [TOO]        (* Get destination pointer.                      *)
    MOV  ECX, 8192         (* Set loop count.                               *)
    MOV  ESI, [FROM]       (* Get source pointer.                           *)
    @LOOP:
      MOV EAX, [ESI]       (* Read first four bytes.                        *)
      MOV EBX, [ESI+4]     (* Read next four bytes.                         *)
      ADD ESI, 8           (* Update sourcep pointer.                       *)
      MOV [EDI], EAX       (* Store first four bytes.                       *)
      MOV [EDI+4], EBX     (* Store next four bytes.                        *)
      ADD EDI, 8           (* Update destination pointer.                   *)
    DEC ECX                (* Decrease loop counter.                        *)
    JNZ @LOOP              (* Jump unless counter is zero.                  *)
  END;


(****************************************************************************)
(* This one copies one page to another, not copying color 0, x86 version    *)
(****************************************************************************)
  PROCEDURE CopyPageT(From, Too : DWORD); ASSEMBLER; ASM
    MOV EDI, [Too]    (* Get destination pointer.                           *)
    MOV ECX, 65536    (* Set pixel count.                                   *)
    MOV ESI, [From]   (* Get source pointer.                                *)
    @LOOP:
      MOV AL, [ESI]   (* Get source pixel.                                  *)
      CMP AL, 0       (* Compare to 0                                       *)
      JZ  @SKIP       (* If 0 then skip plotting.                           *)
      MOV [EDI], AL   (* Plot pixel.                                        *)
      @SKIP:
      INC ESI         (* Increase source pointer.                           *)
      INC EDI         (* Increase destination pointer.                      *)
    DEC ECX           (* Decrease loop counter.                             *)
    JNZ @LOOP         (* Jump unless loopcounter = 0                        *)
  END;


(****************************************************************************)
(* This one smudges one page to another. x86 version                        *)
(****************************************************************************)
  PROCEDURE CopyPageSmudge_x86(From, Too : DWORD); ASSEMBLER; ASM
    MOV  EDI, [TOO]                    (* Get destination pointer.          *)
    MOV  ECX, 63360                    (* Set pixel count (skip top/bottom) *)
    MOV  ESI, [FROM]                   (* Get source addres                 *)
    ADD  EDI, 320                      (* Skip first line on destination.   *)
    ADD  ESI, 320                      (* And skip first line on source.    *)
    @LOOP:
      MOVZX EAX, BYTE PTR [ESI-320]    (* Get pixel above.                  *)
      MOVZX EBX, BYTE PTR [ESI-1]      (* Get pixel left.                   *)
      MOVZX EDX, BYTE PTR [ESI+1]      (* Get pixel right.                  *)
      ADD   EAX, EBX
      MOVZX EBX, BYTE PTR [ESI+320]    (* Get pixel below.                  *)
      ADD   EAX, EDX
      ADD   EAX, EBX                   (* Count four pixels together.       *)
      SHR   EAX, 2                     (* Divide by four.                   *)
      MOV   [EDI], AL                  (* And store the result.             *)
      INC   ESI                        (* Increase source pointer.          *)
      INC   EDI                        (* Increase destination pointer.     *)
    DEC  ECX                           (* Decrease loop count.              *)
    JNZ  @LOOP                         (* Loop until 0                      *)
  END;


(****************************************************************************)
(* This one smudges one page to another, mmx version                        *)
(****************************************************************************)
  PROCEDURE CopyPageSmudge_mmx(From, Too : DWORD); ASSEMBLER; ASM
    MOV  EDI, [TOO]                   (* Get destination page.              *)
    MOV  ECX, 7920                    (* Loop count. (7920*8=65536-640)     *)
    MOV  ESI, [FROM]                  (* Get source page.                   *)
    ADD  EDI, 320                     (* Skip first line.                   *)
    ADD  ESI, 320                     (* Also on source.                    *)
    PSLLQ mm7, 64                     (* Clear mm7                          *)
    @LOOP:
      MOVQ  mm0, QWORD PTR [ESI-320]  (* Get 8 pixels above.                *)
      MOVQ  mm1, QWORD PTR [ESI-1]    (* Get 8 pixels left.                 *)
      MOVQ  mm2, mm0                  (* Copy above pixels.                 *)
      MOVQ  mm3, mm1                  (* Copy left pixels.                  *)
      PUNPCKHBW mm0, mm7              (* Unpack 4 above pxls in mm0 to words*)
      PUNPCKHBW mm1, mm7              (* Unpack 4 left pxls in mm1 to words *)
      PUNPCKLBW mm2, mm7              (* Unpack low above pxls to words.    *)
      PUNPCKLBW mm3, mm7              (* Unpack low left pxls to words.     *)
      PADDUSW mm0, mm1                (* Add above and left pixels.         *)
      PADDUSW mm2, mm3                (* Add 4 next above and left pixels.  *)
      MOVQ  mm4, QWORD PTR [ESI+1]    (* Get 8 right pixels.                *)
      MOVQ  mm5, QWORD PTR [ESI+320]  (* Get 8 below pixels.                *)
      MOVQ  mm1, mm4                  (* Copy 8 right pixels.               *)
      MOVQ  mm3, mm5                  (* Copy 8 below pixels.               *)
      PUNPCKHBW mm4, mm7              (* Unpack high right pxls to words.   *)
      PUNPCKHBW mm5, mm7              (* Unpack high below pxls to words.   *)
      PUNPCKLBW mm1, mm7              (* Unpack low right pxls to words.    *)
      PUNPCKLBW mm3, mm7              (* Unpack low below pxls to words.    *)
      PADDUSW mm4, mm5                (* Add right and below pixels.        *)
      PADDUSW mm1, mm3                (* Add other right and below pixels.  *)
      PADDUSW mm0, mm4                (* Now add 4st 4 pixels all together  *)
      PADDUSW mm2, mm1                (* Same for next four.                *)
      PSRLW   mm0, 2                  (* SHR MM0, 2. Divide by 4            *)
      PSRLW   mm2, 2                  (* SHR MM2, 2. Divide by 4            *)
      PACKUSWB mm2, mm0               (* Pack words in mm2 and mm0 to bytes *)
      MOVQ  [EDI], mm2                (* Store 8 pixels on destination.     *)
      ADD  ESI, 8                     (* Increase source pointer.           *)
      ADD  EDI, 8                     (* Increase destination pointer.      *)
    DEC  ECX                          (* Decrease loop counter.             *)
    JNZ  @LOOP                        (* Jump until ECX = 0                 *)
    EMMS                              (* Exit multimedia state.             *)
  END;


(****************************************************************************)
(* This one does a different kind of smudge ... rather fun :)               *)
(****************************************************************************)
  PROCEDURE CopyPageSmudge2(From, Too : DWORD); ASSEMBLER; ASM
    MOV  EDI, [TOO]                  (* Get destination addres              *)
    MOV  ECX, 63360                  (* Set loop count = 64000-2*320        *)
    MOV  ESI, [FROM]                 (* Get source.                         *)
    ADD  EDI, 320                    (* Skip first line on destination.     *)
    ADD  ESI, 320                    (* Skip first line on source.          *)
    @LOOP:
      MOVZX EAX, BYTE PTR [ESI-320]  (* Get pixel above.                    *)
      MOVZX EBX, BYTE PTR [ESI-1]    (* Get pixel left.                     *)
      MOVZX EDX, BYTE PTR [ESI+1]    (* Get pixel right.                    *)
      ADD   EAX, EBX                 (* EAX := Above + left                 *)
      MOVZX EBX, BYTE PTR [ESI+320]  (* Get pixel under.                    *)
      ADD   EAX, EDX                 (* EAX +:= right.                      *)
      MOVZX EDX, BYTE PTR [ESI-321]  (* Get pixel aboveleft.                *)
      ADD   EAX, EBX                 (* EAX +:= under.                      *)
      MOVZX EBX, BYTE PTR [ESI-319]  (* Get pixel aboveright.               *)
      ADD   EDX, EBX                 (* EDX := Aboveleft+Aboveright.        *)
      MOVZX EBX, BYTE PTR [ESI+319]  (* Get pixel belowleft.                *)
      ADD   EDX, EBX                 (* EDX +:= belowleft.                  *)
      MOVZX EBX, BYTE PTR [ESI+321]  (* Get pixel belowright.               *)
      ADD   EDX, EBX                 (* EDX +:= belowright.                 *)
      MOVZX EBX, BYTE PTR [ESI]      (* Get pixel itself.                   *)
      LEA   EBX, [EBX+2*EBX]         (* Multiply pixel with 3               *)
      ADD   EDX, EBX                 (* EDX +:= 3*pixel                     *)
      SHR   EDX, 1                   (* EDX /:= 2;                          *)
      ADD   EAX, EDX                 (* Add all together.                   *)
      SHR   EAX, 3                   (* Divide by 8                         *)
      MOV   [EDI], AL                (* And put on source.                  *)
      INC   ESI                      (* Increase source pointer.            *)
      INC   EDI                      (* Increase destination pointer.       *)
    DEC  ECX                         (* Decrease loop counter.              *)
    JNZ  @LOOP                       (* Jump until 0                        *)
  END;


(****************************************************************************)
(* This one does a putpixel on a 256*256 screen ... (src for rotate).       *)
(****************************************************************************)
  PROCEDURE PutPixel255(Where : DWORD; X, Y, C : BYTE); ASSEMBLER; ASM
    XOR  EAX, EAX             (* Empty EAX                                  *)
    MOV  EDI, [WHERE]         (* Get source addres                          *)
    MOV  AL,  [Y]             (* Get column number.                         *)
    MOV  BL,  [C]             (* Get color.                                 *)
    MOV  AH,  [X]             (* Get line number.                           *)
    MOV  [EDI+EAX], BL        (* Store color at correct place.              *)
  END;


(****************************************************************************)
(* This one grabs a pixel from a 256*256 screen.                            *)
(****************************************************************************)
  FUNCTION GetPixel255(Where : DWORD; X, Y : BYTE) : BYTE; ASSEMBLER; ASM
    XOR  EBX,  EBX           (* Empty EBX.                                  *)
    MOV  EDI,  [WHERE]       (* Get source addres                           *)
    MOV  BH,   [X]           (* Get line.                                   *)
    MOV  BL,   [Y]           (* Get column.                                 *)
    MOV  AL,   [EDI+EBX]     (* Get pixel and return it.                    *)
  END;


(****************************************************************************)
(* This one rotates a 256*256 source image to the screen, tiling it ...     *)
(****************************************************************************)
  PROCEDURE Rotate(From, Too : DWORD; (* Source(256) and Destination.       *)
                   Rot : DWORD;       (* 0 < ROT < 4096                     *)
                   X, Y,              (* pixelmappedtocenter/256*65535      *)
                   SCALE : LONGINT);  (* Sort of scale.                     *)
  VAR ANGLE            : REAL;        (* Temporary real variable. Yuck      *)
      Ddx,Ddy,D2x,D2y  : INTEGER;     (* Delta's to move over source.       *)
      i,j              : INTEGER;     (* Starting coordinates in the bitmap.*)
  BEGIN
   (* Calculate angle. ******************************************************)
     angle := rot * 2 * 3.14159236/ 2048;

   (* Moving delta's. 8:8 fixed point ***************************************)
     Ddx:=trunc(((-Cos(angle)*256)) * SCALE) SHR 8;
     Ddy:=trunc(((Sin(Angle)*256)) * Scale) SHR 8;
     D2x:=trunc(((-Cos(angle+3.14159265/2) * 341))*SCALE) SHR 8;
     D2y:=trunc(((Sin(angle+3.14159265/2) * 341))*SCALE) SHR 8;

   (* Starting coordinates in the source bitmap. *)
     i:=X-Ddx*(160)-D2x*(100);
     j:=y-Ddy*(160)-D2y*(100);

   (* Do the shit :) *)
     ASM
       MOV   ESI, [FROM]      (* Get source addres.                         *)
       MOV   EDI, [TOO]       (* Get destination addres.                    *)
       MOV   ECX, 200         (* The line count.                            *)
       @LineLoop:
         PUSH ECX             (* Store the linecount on the stack.          *)
         MOV   BX, [I]        (* Get starting coordinates for this line.    *)
         MOV   DX, [J]        (* Store them in the BX and DX registers.     *)
         MOV  ECX, 320        (* Set the pixel count.                       *)
         XOR  EAX, EAX        (* Clear the EAX register.                    *)
         @PixelLoop:
           PUSH ECX           (* Store the pixelcount on the stack.         *)
           MOV AH, BH         (* Get current linenr.                        *)
           MOV AL, DH         (* Get current column.                        *)
           MOV CL, [ESI+EAX]  (* Get source pixel.                          *)
           MOV [EDI], CL      (* Store pixel on destination.                *)
           INC EDI            (* Increase destination pointer.              *)
           ADD BX, [DDX]      (* Update source positions for next pixel.    *)
           ADD DX, [DDY]      (* Update source positions for next pixel.    *)
           POP ECX            (* Get pixelcount from stack.                 *)
         DEC  ECX             (* Decrease pixelcount.                       *)
         JNZ  @PixelLoop      (* Jump until pixelcount is 0                 *)
         MOV  AX, [D2X]       (* Get line update vars.                      *)
         MOV  CX, [D2Y]       (* idem dito.                                 *)
         ADD  [I], AX         (* Add these to the starting coordinates of   *)
         ADD  [J], CX         (* the beginning of the line = move to next   *)
         POP  ECX             (* Get linecount.                             *)
       DEC  ECX               (* Decrease linecount.                        *)
       JNZ  @LineLoop         (* Jump until linecount = 0                   *)
     END; (**)
  END;


(****************************************************************************)
(* This one rotates a 256*256 source image to the screen and skips color 0  *)
(****************************************************************************)
  PROCEDURE RotateT(From, Too : DWORD; (* Source(256) and Destination.      *)
                    Rot : DWORD;       (* 0 < ROT < 4096                    *)
                    X, Y,              (* pixelmappedtocenter/256*65535     *)
                    SCALE : LONGINT);  (* Sort of scale.                    *)
  VAR ANGLE            : REAL;        (* Temporary real variable. Yuck      *)
      Ddx,Ddy,D2x,D2y  : INTEGER;     (* Delta's to move over source.       *)
      i,j              : INTEGER;     (* Starting coordinates in the bitmap.*)
  BEGIN
   (* Calculate angle. ******************************************************)
     angle := rot * 2 * 3.14159236/ 2048;

   (* Moving delta's. 8:8 fixed point ***************************************)
     Ddx:=trunc(((-Cos(angle)*256)) * SCALE) SHR 8;
     Ddy:=trunc(((Sin(Angle)*256)) * Scale) SHR 8;
     D2x:=trunc(((-Cos(angle+3.14159265/2) * 341))*SCALE) SHR 8;
     D2y:=trunc(((Sin(angle+3.14159265/2) * 341))*SCALE) SHR 8;

   (* Starting coordinates in the source bitmap. *)
     i:=X-Ddx*(160)-D2x*(100);
     j:=y-Ddy*(160)-D2y*(100);

   (* Do the shit :) *)
     ASM
       MOV   ESI, [FROM]      (* Get source addres.                         *)
       MOV   EDI, [TOO]       (* Get destination addres.                    *)
       MOV   ECX, 200         (* The line count.                            *)
       @LineLoop:
         PUSH ECX             (* Store the linecount on the stack.          *)
         MOV   BX, [I]        (* Get starting coordinates for this line.    *)
         MOV   DX, [J]        (* Store them in the BX and DX registers.     *)
         MOV  ECX, 320        (* Set the pixel count.                       *)
         XOR  EAX, EAX        (* Clear the EAX register.                    *)
         @PixelLoop:
           PUSH ECX           (* Store the pixelcount on the stack.         *)
           MOV AH, BH         (* Get current linenr.                        *)
           MOV AL, DH         (* Get current column.                        *)
           MOV CL, [ESI+EAX]  (* Get source pixel.                          *)
           CMP CL, 0          (* Check if source pixel is 0                 *)
           JZ  @Skip          (* If it is 0 then skip the plotting.         *)
           MOV [EDI], CL      (* Store pixel on destination.                *)
           @Skip:
           INC EDI            (* Increase destination pointer.              *)
           ADD BX, [DDX]      (* Update source positions for next pixel.    *)
           ADD DX, [DDY]      (* Update source positions for next pixel.    *)
           POP ECX            (* Get pixelcount from stack.                 *)
         DEC  ECX             (* Decrease pixelcount.                       *)
         JNZ  @PixelLoop      (* Jump until pixelcount is 0                 *)
         MOV  AX, [D2X]       (* Get line update vars.                      *)
         MOV  CX, [D2Y]       (* idem dito.                                 *)
         ADD  [I], AX         (* Add these to the starting coordinates of   *)
         ADD  [J], CX         (* the beginning of the line = move to next   *)
         POP  ECX             (* Get linecount.                             *)
       DEC  ECX               (* Decrease linecount.                        *)
       JNZ  @LineLoop         (* Jump until linecount = 0                   *)
     END; (**)
  END;


(****************************************************************************)
(* This one rotates a 256*256 source image to the screen and skips color 0  *)
(* and warps the image.                                                     *)
(****************************************************************************)
  PROCEDURE RotateTW(From, Too : DWORD; Rot : DWORD; X, Y, SCALE : LONGINT; WARPX, WARPY : WORD);
  VAR ANGLE            : REAL;            (* Yuck :) *)
      Ddx,Ddy,D2x,D2y  : INTEGER;
      i,j              : INTEGER;
      WARPDDY          : INTEGER;
  BEGIN
   (* Sin and cos only 4 for every screen ... aint gonna waist a look- *)
   (* up table on that ! *)
     angle := rot * 2*3.14159236 / 2048;

   (* Fixed Point Verplaatsingsvectoren geroteerd en gewogen(scale) *)
     Ddx:=trunc(((-Cos(angle)*256)) * SCALE) SHR 8;
     Ddy:=trunc(((Sin(Angle)*256)) * Scale) SHR 8;
     D2x:=trunc(((-Cos(angle+3.14159265/2) * 341))*SCALE) SHR 8;
     D2y:=trunc(((Sin(angle+3.14159265/2) * 341))*SCALE) SHR 8;

   (* Starting coordinates in the source bitmap. *)
     i:=X-Ddx*(160)-D2x*(100);
     j:=y-Ddy*(160)-D2y*(100);

   (* Do the shit :) *)
     ASM
       MOV   ESI, [FROM]
       MOV   EDI, [TOO]
       MOV   ECX, 200
       @LineLoop:
         PUSH ECX
         MOV   BX, [I]
         MOV   DX, [J]
         MOV  ECX, 320
         MOV  AX,  [DDY]
         MOV  [WarpDDY], AX
         XOR  EAX, EAX
         @PixelLoop:
           MOV AH, BH
           MOV AL, DH
           SHL EBX, 8
           MOV BL, [ESI+EAX]
           CMP BL, 0
           JZ  @SKIP
           MOV [EDI], BL
           @SKIP:
           INC EDI
           SHR EBX, 8
           MOV AX, [WarpDDY]
           ADD DX, AX
           ADD AX, [WarpY]
           MOV [WarpDDY], AX
           ADD BX, [DDX]
         DEC  ECX
         JNZ  @PixelLoop
         MOV  AX, [D2X]
         MOV  CX, [D2Y]
         ADD  [I], AX
         ADD  [J], CX
         POP  ECX
         ADD  AX, [WARPX]
         MOV  [D2X], AX
       DEC  ECX
       JNZ  @LineLoop
     END; (**)
  END;


(****************************************************************************)
(* This one rotates a 256*256 source image to the screen and skips color 0  *)
(* and warps the image. (Slower but high precision warping.)                *)
(****************************************************************************)
  PROCEDURE RotateTW2(From, Too : DWORD; Rot : DWORD; X, Y, SCALE, WARPX, WARPY : LONGINT);
  VAR ANGLE            : REAL;            (* Yuck :) *)
      Ddx,Ddy,D2x,D2y  : LONGINT;
      i,j              : LONGINT;
      WARPDDY          : LONGINT;
  BEGIN
   (* Sin and cos only 4 for every screen ... aint gonna waist a look- *)
   (* up table on that ! *)
     angle := rot * 2*3.14159236 / 2048;

   (* Fixed Point Verplaatsingsvectoren geroteerd en gewogen(scale) *)
     Ddx:=trunc(((-Cos(angle)*65536)) * SCALE) SHR 8;
     Ddy:=trunc(((Sin(Angle)*65536)) * Scale) SHR 8;
     D2x:=trunc(((-Cos(angle+3.14159265/2) * 87296))*SCALE) SHR 8;
     D2y:=trunc(((Sin(angle+3.14159265/2) * 87296))*SCALE) SHR 8;

   (* Starting coordinates in the source bitmap. *)
     i:=(X SHL 8)-(Ddx)*(160)-(D2x + 100 * WarpX)*(100);
     j:=(y SHL 8)-(Ddy + 160*WarpY)*(160)-(D2y)*(100);

   (* Do the shit :) *)
     ASM
       MOV   ESI, [FROM]
       MOV   EDI, [TOO]
       MOV   ECX, 200
       @LineLoop:
         PUSH ECX
         MOV  EBX, [I]
         MOV  EDX, [J]
         MOV  ECX, 320
         MOV  EAX, [DDY]
         MOV  [WarpDDY], EAX
         XOR  EAX, EAX
         @PixelLoop:
           PUSH ECX
           XOR EAX, EAX
           MOV ECX, EBX
           SHR ECX, 8
           MOV AH, CH
           MOV ECX, EDX
           SHR ECX, 8
           MOV AL, CH
           MOV CL, [ESI+EAX]
           CMP CL, 0
           JZ  @SKIP
           MOV [EDI], CL
           @SKIP:
           INC EDI
           MOV EAX, [WarpDDY]
           ADD EDX, EAX
           ADD EAX, [WarpY]
           MOV [WarpDDY], EAX
           ADD EBX, [DDX]
           POP ECX
         DEC  ECX
         JNZ  @PixelLoop
         MOV  EAX, [D2X]
         MOV  ECX, [D2Y]
         ADD  [I], EAX
         ADD  [J], ECX
         POP  ECX
         ADD  EAX, [WARPX]
         MOV  [D2X], EAX
       DEC  ECX
       JNZ  @LineLoop
     END; (**)
  END;


(****************************************************************************)
(* This one mixes two pages and stores the result. x86 version              *)
(****************************************************************************)
  PROCEDURE MixPages_x86(From1, From2, Too : DWORD); ASSEMBLER; ASM
    MOV  ESI, [FROM1]
    MOV  EBX, [FROM2]
    MOV  EDI, [TOO]
    MOV  ECX, 64000
    @PixelLoop:
      MOVZX  EAX, BYTE PTR [ESI]
      INC    ESI
      MOVZX  EDX, BYTE PTR [EBX]
      INC    EBX
      ADD    EAX, EDX
      SHR    EAX, 1
      MOV    [EDI], AL
      INC    EDI
    DEC  ECX
    JNZ  @PixelLoop
  END;


(****************************************************************************)
(* This one mixes two pages and stores. mmx version                         *)
(****************************************************************************)
  PROCEDURE MixPages_MMX(From1, From2, Too : DWORD); ASSEMBLER; ASM
    MOV  ESI, [FROM1]
    MOV  EBX, [FROM2]
    MOV  EDI, [TOO]
    MOVQ  mm2, QWORD PTR [SomeBitsOutMask]
    MOV  ECX, 8192
    @PixelLoop:
      MOVQ   mm0, [ESI]
      ADD    ESI, 8
      MOVQ   mm1, [EBX]
      PSRLQ  mm0, 1
      PSRLQ  mm1, 1
      PAND   mm0, mm2
      PAND   mm1, mm2
      ADD    EBX, 8
      PADDUSB mm0, mm1
      MOVQ   [EDI], mm0
      ADD    EDI, 8
    DEC  ECX
    JNZ  @PixelLoop
    EMMS
  END;


(****************************************************************************)
(* This one mixes pages with saturation, just (A+B) and trim on 255         *)
(****************************************************************************)
  PROCEDURE MixPages2_x86(From1, From2, Too : DWORD); ASSEMBLER; ASM
    MOV  ESI, [FROM1]
    MOV  EBX, [FROM2]
    MOV  EDI, [TOO]
    MOV  ECX, 64000
    @PixelLoop:
      MOVZX  EAX, BYTE PTR [ESI]
      INC    ESI
      MOVZX  EDX, BYTE PTR [EBX]
      INC    EBX
      ADD    EAX, EDX
      CMP    EAX, 255
      JB     @OK
      MOV    AL, 255
      @OK:
      MOV    [EDI], AL
      INC    EDI
    DEC  ECX
    JNZ  @PixelLoop
  END;


(****************************************************************************)
(* This one does a saturated add of two pages, mmx version                  *)
(****************************************************************************)
  PROCEDURE MixPages2_MMX(From1, From2, Too : DWORD); ASSEMBLER; ASM
    MOV  ESI, [FROM1]
    MOV  EBX, [FROM2]
    MOV  EDI, [TOO]
    MOV  ECX, 8000
    @PixelLoop:
      MOVQ   mm0, [ESI]
      ADD    ESI, 8
      MOVQ   mm1, [EBX]
      ADD    EBX, 8
      PADDUSB mm0, mm1
      MOVQ   [EDI], mm0
      ADD    EDI, 8
    DEC  ECX
    JNZ  @PixelLoop
    EMMS
  END;


(****************************************************************************)
(* This one does an alphablended mix of two pages.                          *)
(****************************************************************************)
  PROCEDURE MixPages3_x86(From1, From2, Too : DWORD; Amount : BYTE);ASSEMBLER; ASM
    MOV ESI, [FROM1]
    MOV EBX, [FROM2]
    MOV ECX, 64000
    MOV EDI, [TOO]
    @LOOP:
       MOV AL, [ESI]
       MUL [amount]
       MOV DH, AH
       MOV AL, [EBX]
       MOV DL, [Amount]
       NOT DL
       MUL DL
       SHR AX, 8
       ADD AL, DH
       MOV [EDI], AL
       INC ESI
       INC EBX
       INC EDI
    DEC ECX
    JNZ @LOOP
  END;


(****************************************************************************)
(* This one does an alphablended mix of two pages. mmx version.             *)
(****************************************************************************)
  PROCEDURE MixPages3_mmx(FROM1, FROM2, TOO : DWORD; Amount : BYTE); ASSEMBLER; ASM
    MOV ESI, [FROM1]
    MOVZX DX,  [Amount]
    MOV EBX, [FROM2]
    SHL EDX, 16
    MOV EDI, [TOO]
    MOV ECX, 8000
    MOVZX DX,  [Amount]
    MOV AX, DX
    NOT AL
    SHL EAX, 16
    MOV AX, DX
    NOT AL
    MOVD mm0, EDX
    MOVD mm1, EDX
    PSLLQ mm1, 32
    POR  mm0, mm1
    MOVD mm1, EAX
    MOVD mm2, EAX
    PSLLQ mm2, 32
    POR  mm1, mm2
    PSLLQ mm7, 64
    @QuadPixelLoop:
      MOVQ mm2, [ESI]
      MOVQ mm3, [EBX]
      MOVQ mm4, mm2
      MOVQ mm5, mm3
      PUNPCKHBW mm2, mm7
      PUNPCKLBW mm4, mm7
      PMULLW mm2, mm0
      PUNPCKHBW mm3, mm7
      PMULLW mm4, mm0
      PUNPCKLBW mm5, mm7
      PMULLW mm3, mm1
      ADD ESI, 8
      PMULLW mm5, mm1
      PADDUSW mm2, mm3
      ADD EBX, 8
      PADDUSW mm4, mm5
      PSRLW mm2, 8
      PSRLW mm4, 8
      PACKUSWB mm4, mm2
      MOVQ [EDI], mm4
      ADD EDI, 8
    DEC ECX
    JNZ @QuadPixelLoop
    EMMS
  END;

(****************************************************************************)
(* This one does a bump-map.                                                *)
(****************************************************************************)
  PROCEDURE BumpMap(FROM, TOO, LIGHT : DWORD; LX, LY : WORD); ASSEMBLER;
  VAR X, Y : WORD;
  ASM
    MOV   ESI,   [FROM]
    MOV   EDI,   [TOO]
    MOV   EDX,   [LIGHT]
    MOV   ECX,   63360
    ADD   ESI,   320
    ADD   EDI,   320
    MOV   [X],   0
    MOV   [Y],   0
    @PixelLoop:
      MOVZX EAX, BYTE [ESI-320]
      MOV   AH,  BYTE [ESI+320]
      SUB   AL,  AH
      MOV   BH,  BYTE [ESI-1]
      MOV   BL,  BYTE [ESI+1]
      SUB   BL,  BH
      MOV   AH,  0
      MOV   BH,  0
      ADD   AX,  [LX]
      ADD   BX,  [LY]
      ADD   AX,  [X]
      ADD   BX,  [Y]
      MOV   AH,  BL
      INC   [X]
      CMP   [X], 320
      JNZ   @NO
      INC   [Y]
      MOV   [X], 0;
      @NO:
      MOV   BL,  [EDX+EAX]
      MOV   [EDI], BL
      INC   ESI
      INC   EDI
    DEC   ECX
    JNZ   @PixelLoop
  END;

(****************************************************************************)
(* This one does a bump-map. (no tiled lights)                              *)
(****************************************************************************)
  PROCEDURE BumpMap2(FROM, TOO, LIGHT : DWORD; LX, LY : INTEGER); ASSEMBLER;
  VAR X, Y : WORD;
  ASM
    MOV   ESI,   [FROM]
    MOV   EDI,   [TOO]
    MOV   ECX,   63360
    ADD   ESI,   320
    ADD   EDI,   320
    MOV   [X],   0
    MOV   [Y],   0
    @PixelLoop:
      MOVZX EAX, BYTE [ESI-320]
      MOVZX DX,  BYTE [ESI+320]
      SUB   AX,  DX
      MOVZX BX,  BYTE [ESI-1]
      MOVZX DX,  BYTE [ESI+1]
      SUB   BX,  DX
      ADD   AX,  [LX]
      ADD   BX,  [LY]
      ADD   AX,  [X]
      ADD   BX,  [Y]
      CMP   AH,  0
      JZ    @XinRange
      MOV   AL,  0
      MOV   BL,  0
      @XinRange:
      CMP   BH,  0
      JZ    @YinRange
      MOV   AL,  0
      MOV   BL,  0
      @YinRange:
      MOV   AH,  BL
      INC   [X]
      MOV   EDX,   [LIGHT]
      CMP   [X], 320
      JNZ   @NO
      INC   [Y]
      MOV   [X], 0;
      @NO:
      MOV   BL,  [EDX+EAX]
      MOV   [EDI], BL
      INC   ESI
      INC   EDI
    DEC   ECX
    JNZ   @PixelLoop
  END;


(****************************************************************************)
(* This one does a bump-map. (no tiled lights, no color 0)                  *)
(****************************************************************************)
  PROCEDURE BumpMap2T(FROM, TOO, LIGHT : DWORD; LX, LY : INTEGER); ASSEMBLER;
  VAR X, Y : WORD;
  ASM
    MOV   ESI,   [FROM]
    MOV   EDI,   [TOO]
    MOV   ECX,   63360
    ADD   ESI,   320
    ADD   EDI,   320
    MOV   [X],   0
    MOV   [Y],   0
    @PixelLoop:
      MOV   BL,  BYTE PTR [ESI]
      CMP   BL,  0
      JZ    @Skip
      MOVZX EAX, BYTE [ESI-320]
      MOVZX DX,  BYTE [ESI+320]
      SUB   AX,  DX
      MOVZX BX,  BYTE [ESI-1]
      MOVZX DX,  BYTE [ESI+1]
      SUB   BX,  DX
      ADD   AX,  [LX]
      ADD   BX,  [LY]
      ADD   AX,  [X]
      ADD   BX,  [Y]
      CMP   AH,  0
      JZ    @XinRange
      MOV   AL,  0
      MOV   BL,  0
      @XinRange:
      CMP   BH,  0
      JZ    @YinRange
      MOV   AL,  0
      MOV   BL,  0
      @YinRange:
      MOV   AH,  BL
      MOV   EDX,   [LIGHT]
      MOV   BL,  [EDX+EAX]
      @Skip:
      INC   [X]
      CMP   [X], 320
      JNZ   @NO
      INC   [Y]
      MOV   [X], 0;
      @NO:
      MOV   [EDI], BL
      INC   ESI
      INC   EDI
    DEC   ECX
    JNZ   @PixelLoop
  END;


(****************************************************************************)
(* This one does a bump-map. (no tiled lights)                              *)
(****************************************************************************)
  PROCEDURE BumpMap3(FROM, TOO, LIGHT : DWORD; LX, LY : INTEGER); ASSEMBLER;
  VAR X, Y : WORD;
  ASM
    MOV   ESI,   [FROM]
    MOV   EDI,   [TOO]
    MOV   ECX,   63360
    ADD   ESI,   320
    ADD   EDI,   320
    MOV   [X],   0
    MOV   [Y],   0
    @PixelLoop:
      MOVZX EAX, BYTE [ESI-320]
      MOVZX DX,  BYTE [ESI+320]
      SUB   AX,  DX
      MOVZX EBX, BYTE [ESI-1]
      MOVZX DX,  BYTE [ESI+1]
      SUB   BX,  DX
      ADD   AX,  [LX]
      ADD   BX,  [LY]
      ADD   AX,  [X]
      ADD   BX,  [Y]
      MOVZX DX, BYTE [ESI]
      NOT   DL
      SHR   DL, 1
      ADD   DL, 128
      MUL   DX
      SHR   EAX,  8
      MOV   AH, DL
      XCHG  EAX,  EBX
      MOVZX DX, BYTE [ESI]
      NOT   DL
      SHR   DL, 1
      ADD   DL, 128
      MUL   DX
      SHR   EAX,  8
      MOV   AH, DL
      XCHG  EAX, EBX
      CMP   AH,  0
      JZ    @XinRange
      MOV   AL,  0
      MOV   BL,  0
      @XinRange:
      CMP   BH,  0
      JZ    @YinRange
      MOV   AL,  0
      MOV   BL,  0
      @YinRange:
      MOV   AH,  BL
      INC   [X]
      MOV   EDX,   [LIGHT]
      CMP   [X], 320
      JNZ   @NO
      INC   [Y]
      MOV   [X], 0;
      @NO:
      MOV   BL,  [EDX+EAX]
      MOV   [EDI], BL
      INC   ESI
      INC   EDI
    DEC   ECX
    JNZ   @PixelLoop
  END;



(****************************************************************************)
(* This one does a bump-map. (slope+heights+transparent)                    *)
(****************************************************************************)
  PROCEDURE BumpMap3t(FROM, TOO, LIGHT : DWORD; LX, LY : INTEGER); ASSEMBLER;
  VAR X, Y : WORD;
  ASM
    MOV   ESI,   [FROM]
    MOV   EDI,   [TOO]
    MOV   ECX,   63360
    ADD   ESI,   320
    ADD   EDI,   320
    MOV   [X],   0
    MOV   [Y],   0
    @PixelLoop:
      MOV   BL,  [ESI]
      CMP   BL,  0
      JZ    @Skip
      MOVZX EAX, BYTE [ESI-320]
      MOVZX DX,  BYTE [ESI+320]
      SUB   AX,  DX
      MOVZX EBX, BYTE [ESI-1]
      MOVZX DX,  BYTE [ESI+1]
      SUB   BX,  DX
      ADD   AX,  [LX]
      ADD   BX,  [LY]
      ADD   AX,  [X]
      ADD   BX,  [Y]
      MOVZX DX, BYTE [ESI]
      NOT   DL
      SHR   DL, 1
      ADD   DL, 128
      MUL   DX
      SHR   EAX,  8
      MOV   AH, DL
      XCHG  EAX,  EBX
      MOVZX DX, BYTE [ESI]
      NOT   DL
      SHR   DL, 1
      ADD   DL, 128
      MUL   DX
      SHR   EAX,  8
      MOV   AH, DL
      XCHG  EAX, EBX
      CMP   AH,  0
      JZ    @XinRange
      MOV   AL,  0
      MOV   BL,  0
      @XinRange:
      CMP   BH,  0
      JZ    @YinRange
      MOV   AL,  0
      MOV   BL,  0
      @YinRange:
      MOV   AH,  BL
      MOV   EDX,   [LIGHT]
      MOV   BL,  [EDX+EAX]
      @Skip:
      INC   [X]
      CMP   [X], 320
      JNZ   @NO
      INC   [Y]
      MOV   [X], 0;
      @NO:
      MOV   [EDI], BL
      INC   ESI
      INC   EDI
    DEC   ECX
    JNZ   @PixelLoop
  END;



(****************************************************************************)
(* This one scales a 320*200 to 256*256 to use as source for rotate :)      *)
(****************************************************************************)
  PROCEDURE Scale320to256(FROM, TOO : DWORD);
  ASSEMBLER; VAR EEX, EFX : LONGINT; ASM
      MOV  ESI, [FROM]
      MOV  EDI, [TOO]
      MOV  ECX,  256
      MOV  [EFX], 0
      @LineLoop:
        PUSH ECX
        MOV  [EEX], 0
        MOV  ECX, 256
        @PixelLoop:
          MOV  EAX, [EFX]
          MOV  EBX, [EEX]
          SHR  EAX, 8
          SHR  EBX, 8
          MOV  EDX, 320
          mul  EDX
          ADD  EAX, EBX
          MOV  DL,  [ESI+EAX]
          MOV  [EDI], DL
          INC  EDI
          ADD  [EEX], 320
        DEC  CL
        JNZ  @PixelLoop
      ADD  [EFX], 200
      POP  ECX
      DEC  ECX
      JNZ  @LineLoop
  END;


(****************************************************************************)
(* This one scales a 256*256 to 320*200.                                    *)
(****************************************************************************)
  PROCEDURE Scale256to320(FROM, TOO : DWORD);
  VAR CX, CY : LONGINT;
      DX, DY : LONGINT;
      W1, W2 : LONGINT;
  BEGIN
     DX := (255 SHL 8) DIV 199;
     DY := (255 SHL 8) DIV 319;
     CX := 0;
     CY := 0;
     FOR W1 := 0 TO 199 DO BEGIN
      CY := 0;
      FOR W2 := 0 TO 319 DO BEGIN
       PutPixel(Too, W1, W2, GetPixel255(From, CX SHR 8, CY SHR 8));
       CY +:= DY;
      END;
      CX +:= DX;
     END;
  END;


(****************************************************************************)
(* This one applys two relocation maps to from. (from is 255*255 !)         *)
(****************************************************************************)
  PROCEDURE Relocate(From, Too, Distort1, Distort2, D1Ofs, D2Ofs : LONGINT); ASSEMBLER; ASM
      MOV   ESI, [FROM]
      MOV   EDI, [TOO]
      MOV   EDX, [Distort1]
      MOV   EBX, [Distort2]
      MOV   ECX, 64000
      @LOOP:
        PUSH   ECX
        MOVZX  EAX, BYTE PTR [EDX]
        ADD    EAX, [D1Ofs]
        SHL    EAX, 8
        MOVZX  ECX, BYTE PTR [EBX]
        SHL    ECX, 1
        ADD    ECX, [D2Ofs]
        ADD    EAX, ECX
        AND    EAX, $FFFF
        MOV    CL,  [ESI+EAX]
        MOV    [EDI], CL
        INC    EDX
        INC    EBX
        INC    EDI
        POP    ECX
      DEC   ECX
      JNZ   @Loop
  END;


(****************************************************************************)
(* Same, but doesn't draw color 0                                           *)
(****************************************************************************)
  PROCEDURE RelocateT(From, Too, Distort1, Distort2, D1Ofs, D2Ofs : LONGINT); ASSEMBLER; ASM
      MOV   ESI, [FROM]
      MOV   EDI, [TOO]
      MOV   EDX, [Distort1]
      MOV   EBX, [Distort2]
      MOV   ECX, 64000
      @LOOP:
        PUSH   ECX
        MOVZX  EAX, BYTE PTR [EDX]
        ADD    EAX, [D1Ofs]
        SHL    EAX, 8
        MOVZX  ECX, BYTE PTR [EBX]
        SHL    ECX, 1
        ADD    ECX, [D2Ofs]
        ADD    EAX, ECX
        AND    EAX, $FFFF
        MOV    CL,  [ESI+EAX]
        CMP    CL,  0
        JZ     @Skip
        MOV    [EDI], CL
        @Skip:
        INC    EDX
        INC    EBX
        INC    EDI
        POP    ECX
      DEC   ECX
      JNZ   @Loop
  END;



(****************************************************************************)
(* This one does a ripple. (X86 version)                                    *)
(****************************************************************************)
  PROCEDURE Ripple_x86(VAR Page1, Page2 : DWORD); ASSEMBLER; ASM
    MOV  ECX, [Page1]
    MOV  EDX, [Page2]
    MOV  ESI, [ECX]
    MOV  EDI, [EDX]
    MOV  [ECX], EDI
    MOV  [EDX], ESI
    ADD  ESI, 320
    ADD  EDI, 320
    MOV  ECX, 63360
    @LOOP:
      MOVZX EAX, BYTE [ESI-320]
      MOVZX EBX, BYTE [ESI-1]
      MOVZX EDX, BYTE [ESI+1]
      ADD   EAX, EBX
      MOVZX EBX, BYTE [ESI+320]
      ADD   EAX, EDX
      MOVZX EDX, BYTE [EDI]
      ADD   EAX, EBX
      SUB   EAX, 512
      SUB   EDX, 128
      INC   ESI
      SAR   EAX, 1
      SUB   EAX, EDX
      MOV   EBX, EAX
      SAR   EAX, 5
      SUB   EBX, EAX
      MOV   EAX, EBX
      ADD   EAX, 128
      CMP   EAX, 0
      JA    @OK1
      MOV   EAX, 0
      @OK1:
      CMP   EAX, 255
      JB    @OK2
      MOV   EAX, 255
      @OK2:
      MOV   [EDI], AL
      INC   EDI
    DEC  ECX
    JNZ  @Loop
  END;



(****************************************************************************)
(* This one does a ripple. (mmx version)                                    *)
(****************************************************************************)
  PROCEDURE Ripple_mmx(VAR Page1, Page2 : DWORD); ASSEMBLER; ASM
    MOV  ECX, [Page1]
    MOV  EDX, [Page2]
    MOV  ESI, [ECX]
    MOV  EDI, [EDX]
    MOV  [ECX], EDI
    MOV  [EDX], ESI
    MOV  ECX, 7920                    (* Loop count. (7920*8=65536-640)     *)
    ADD  EDI, 320                     (* Skip first line.                   *)
    ADD  ESI, 320                     (* Also on source.                    *)
    PSLLQ mm7, 64                     (* Clear mm7                          *)
    @LOOP:
      MOVQ  mm0, QWORD PTR [ESI-320]  (* Get 8 pixels above.                *)
      MOVQ  mm1, QWORD PTR [ESI-1]    (* Get 8 pixels left.                 *)
      MOVQ  mm2, mm0                  (* Copy above pixels.                 *)
      MOVQ  mm3, mm1                  (* Copy left pixels.                  *)
      PUNPCKHBW mm0, mm7              (* Unpack 4 above pxls in mm0 to words*)
      PUNPCKHBW mm1, mm7              (* Unpack 4 left pxls in mm1 to words *)
      PUNPCKLBW mm2, mm7              (* Unpack low above pxls to words.    *)
      PUNPCKLBW mm3, mm7              (* Unpack low left pxls to words.     *)
      PADDUSW mm0, mm1                (* Add above and left pixels.         *)
      PADDUSW mm2, mm3                (* Add 4 next above and left pixels.  *)
      MOVQ  mm4, QWORD PTR [ESI+1]    (* Get 8 right pixels.                *)
      MOVQ  mm5, QWORD PTR [ESI+320]  (* Get 8 below pixels.                *)
      MOVQ  mm1, mm4                  (* Copy 8 right pixels.               *)
      MOVQ  mm3, mm5                  (* Copy 8 below pixels.               *)
      PUNPCKHBW mm4, mm7              (* Unpack high right pxls to words.   *)
      PUNPCKHBW mm5, mm7              (* Unpack high below pxls to words.   *)
      PUNPCKLBW mm1, mm7              (* Unpack low right pxls to words.    *)
      PUNPCKLBW mm3, mm7              (* Unpack low below pxls to words.    *)
      PADDUSW mm4, mm5                (* Add right and below pixels.        *)
      PADDUSW mm1, mm3                (* Add other right and below pixels.  *)
      PADDUSW mm0, mm4                (* Now add 4st 4 pixels all together  *)
      PADDUSW mm2, mm1                (* Same for next four.                *)
      PSUBW   mm0, [MASK512W]
      PSUBW   mm2, [MASK512W]
      MOVQ    mm4, QWORD PTR [EDI]
      MOVQ    mm1, mm4
      PUNPCKHBW mm4, mm7
      PUNPCKLBW mm1, mm7
      PSUBW   mm4, [MASK128W]
      PSUBW   mm1, [MASK128W]
      PSRAW   mm0, 1
      PSRAW   mm2, 1
      PSUBW   mm0, mm4
      PSUBW   mm2, mm1
      MOVQ    mm3, mm0
      MOVQ    mm4, mm2
      PSRAW   mm3, 5
      PSRAW   mm4, 5
      PSUBW   mm0, mm3
      PSUBW   mm2, mm4
      PADDW   mm0, [MASK128W]
      PADDW   mm2, [MASK128W]
      PACKUSWB mm2, mm0               (* Pack words in mm2 and mm0 to bytes *)
      MOVQ  [EDI], mm2                (* Store 8 pixels on destination.     *)
      ADD  ESI, 8                     (* Increase source pointer.           *)
      ADD  EDI, 8                     (* Increase destination pointer.      *)
    DEC  ECX                          (* Decrease loop counter.             *)
    JNZ  @LOOP                        (* Jump until ECX = 0                 *)
    EMMS                              (* Exit multimedia state.             *)
  END;



(****************************************************************************)
(* Displacement effect.                                                     *)
(****************************************************************************)
  PROCEDURE DisPlace(From, Too, Disp : DWORD); ASSEMBLER; ASM
    MOV   EDI, [Too]
    MOV   ESI, [Disp]
    ADD   EDI, 320
    MOV   EDX, 1
    @LineLoop:
      XOR   ECX, ECX
      @PixelLoop:
        PUSH   EDX
        PUSH   ECX
        MOVZX  EBX, BYTE [ESI-320]
        MOVZX  EAX, BYTE [ESI+320]
        SUB    EBX, EAX
        ADD    EDX, EBX
        MOVZX  EBX, BYTE [ESI-1]
        MOVZX  EAX, BYTE [ESI+1]
        SUB    EBX, EAX
        ADD    ECX, EBX
        CMP    ECX, 0
        JA     @OK1
        MOV    ECX, 0
        @OK1:
        CMP    ECX, 319
        JB     @OK2
        MOV    ECX, 319
        @OK2:
        CMP    EDX, 0
        JA     @OK3
        MOV    EDX, 0
        @OK3:
        CMP    EDX, 199
        JB     @OK4
        MOV    EDX, 199
        @OK4:
          LEA    EDX, [EDX+4*EDX]
          MOV    EBX, 0
          SHL    EDX, 6
          ADD    EDX, ECX
          MOV    EBX, [FROM]
          MOV    AL, [EBX+EDX]
          MOV    BYTE [EDI], AL
          INC    EDI
          INC    ESI
          POP    ECX
          POP    EDX
      INC   ECX
      CMP   ECX, 320
      JNZ   @PixelLoop
    INC   EDX
    CMP   EDX, 195
    JNZ   @LineLoop
  END;

(****************************************************************************)
(* This one does a fire effect.                                             *)
(****************************************************************************)
  PROCEDURE DoFire(From, Too, Flame : DWORD); ASSEMBLER; ASM
    MOV  EDI, [TOO]
    MOV  ECX, 63360
    MOV  ESI, [FROM]
    MOV  EDX, [Flame]
    @LOOP:
      MOVZX EAX, BYTE PTR [ESI]
      MOVZX EBX, BYTE PTR [ESI+319]
      ADD   EAX, EBX
      MOVZX EBX, BYTE PTR [ESI+320]
      SHL   EBX, 2
      ADD   EAX, EBX
      MOVZX EBX, BYTE PTR [ESI+321]
      ADD   EAX, EBX
      MOVZX EBX, BYTE PTR [ESI+640]
      ADD   EAX, EBX
      SHR   EAX, 3
      MOVZX EBX, BYTE PTR [EDX]
      SHR   EBX, 3
      SUB   EAX,  EBX
      JNS   @Skip
      MOV   AL,  0
      @Skip:
      MOV   [EDI], AL
      INC   ESI
      INC   EDI
      INC   EDX
    DEC  ECX
    JNZ  @LOOP
  END;


(****************************************************************************)
(* Initialise two distortion maps for tunnel effect.                        *)
(****************************************************************************)
  PROCEDURE InitTunnel(Distort1, Distort2 : DWORD);
  VAR W1, W2, W3, W4, W5 : LONGINT;
      R1, R2, R3 : REAL;
  BEGIN
  FOR W1:=-100 TO 99 DO
    FOR W2:=-160 TO 159 DO
      BEGIN
        IF W1<>0 THEN W3:=W1 ELSE W3:=1;
        IF W2<>0 THEN W4:=W2 ELSE W4:=1;
        R1:=Trunc(Exp(Ln(262144*(SQRT(W1*W1+W2*W2)+0.001))*0.1)*256);
        W5:=trunc(R1+(32*Sin(R1*1.284/64)));
        PutPixel(Distort1, W1+100, W2+160, W5 AND $FF);
      END;
  FOR W1:=-100 TO 99 DO
    FOR W2:=-160 TO 159 DO
      BEGIN
        IF W1<>0 THEN R2:=W1 ELSE R2:=0.0001;
        IF W2<>0 THEN R3:=W2 ELSE R3:=0.0001;
        R1:=ArcTan(R3/R2)*(256/6.284);
        W3:=Trunc(R1);
        PutPixel(Distort2, W1+100, W2+160, W3 AND $FF);
      END;
  END;


(****************************************************************************)
(* Initialise two distortion maps for tunnel effect.                        *)
(****************************************************************************)
  PROCEDURE InitTunnel2(Distort1, Distort2 : DWORD);
  VAR W1, W2, W3, W4, W5 : LONGINT;
      R1, R2, R3 : REAL;
  BEGIN
  FOR W1:=-100 TO 99 DO
    FOR W2:=-160 TO 159 DO
      BEGIN
        IF W1<>0 THEN W3:=W1 ELSE W3:=1;
        IF W2<>0 THEN W4:=W2 ELSE W4:=1;
        R1:=Trunc(Exp(Ln(262144*(SQRT(W1*W1+W2*W2)+0.001))*0.1)*256);
        W5:=trunc(R1+(32*Sin(R1*1.284/64)));
        W5:=TRUNC(R1);
        PutPixel(Distort1, W1+100, W2+160, W5 AND $FF);
      END;
  FOR W1:=-100 TO 99 DO
    FOR W2:=-160 TO 159 DO
      BEGIN
        IF W1<>0 THEN R2:=W1 ELSE R2:=0.0001;
        IF W2<>0 THEN R3:=W2 ELSE R3:=0.0001;
        R1:=ArcTan(R3/R2)*(256/6.284) + ArcTan(SQRT(W1*W1+W2*W2))*2000;
        W3:=Trunc(R1);
        PutPixel(Distort2, W1+100, W2+160, W3 AND $FF);
      END;
  END;


(****************************************************************************)
(* Initialise two distortion maps for tunnel effect.                        *)
(****************************************************************************)
  PROCEDURE InitTunnelFast(Distort1, Distort2 : DWORD);
  VAR W1, W2, W3, W4, W5 : LONGINT;
      R1, R2, R3 : REAL;
  BEGIN
  FOR W1:=-100 TO 99 DO
    FOR W2:=-160 TO 159 DO
      BEGIN
        IF W1<>0 THEN W3:=W1 ELSE W3:=1;
        IF W2<>0 THEN W4:=W2 ELSE W4:=1;
      	R1:=TRUNC(SQRT(((W3*W3)+(W4*W4))));
        R1:=Trunc(Exp((Ln(Ln(256*R1))*0.41))*256);
        W5:=Trunc(r1+(64*Cos(R1*1.284/64)));
        PutPixel(Distort1, W1+100, W2+160, W5 AND $FF);
      END;
  FOR W1:=-100 TO 99 DO
    FOR W2:=-160 TO 159 DO
      BEGIN
        IF W1<>0 THEN R2:=W1 ELSE R2:=0.0001;
        IF W2<>0 THEN R3:=W2 ELSE R3:=0.0001;
        R1:=ArcTan(R3/R2)*(256/6.284);
        W3:=Trunc(R1);
        PutPixel(Distort2, W1+100, W2+160, W3 AND $FF);
      END;
  END;



(****************************************************************************)
(* Initialise two distortion maps for flashke effect.                       *)
(****************************************************************************)
  PROCEDURE InitFlashke(Distort1, Distort2 : DWORD);
  VAR W1, W2, W3, W4, W5 : LONGINT;
      R1, R2, R3 : REAL;
  BEGIN
  FOR W1:=-100 TO 99 DO
    FOR W2:=-160 TO 159 DO
      BEGIN
        IF W1<>0 THEN W3:=W1 ELSE W3:=1;
        IF W2<>0 THEN W4:=W2 ELSE W4:=1;
      	R1:=TRUNC(SQRT(((W3*W3)+(W4*W4))));
        R1:=Trunc(Exp(Ln(256*R1)*0.21)*256);
        W5:=Trunc(R1+(64*Cos(R1*1.284/32)));
        PutPixel(Distort1, W1+100, W2+160, W5 AND $FF);
      END;
  FOR W1:=-100 TO 99 DO
    FOR W2:=-160 TO 159 DO
      BEGIN
        IF W1<>0 THEN R2:=W1 ELSE R2:=0.0001;
        IF W2<>0 THEN R3:=W2 ELSE R3:=0.0001;
        R1:=ArcTan(R3/R2)*(256/6.284) + SQRT(R2*R2 + R3*R3);
        W3:=Trunc(R1);
        PutPixel(Distort2, W1+100, W2+160, W3 AND $FF);
      END;
  END;


(****************************************************************************)
(* Initialise two distortion maps for flashke effect.                       *)
(****************************************************************************)
  PROCEDURE InitSpiral(Distort1, Distort2 : DWORD);
  VAR W1, W2, W3, W4, W5 : LONGINT;
      R1, R2, R3 : REAL;
  BEGIN
  FOR W1:=-100 TO 99 DO
    FOR W2:=-160 TO 159 DO
      BEGIN
        IF W1<>0 THEN W3:=W1 ELSE W3:=1;
        IF W2<>0 THEN W4:=W2 ELSE W4:=1;
       	R1:=TRUNC(SQRT(((W3*W3)+(W4*W4))));
        R1:=Trunc((exp(ln( ABS(Sin(R1/256))*128 )*0.2))*256);
        W5:=TRUNC(r1/(sin(R1/256)/cos(r1/256)));
        PutPixel(Distort1, W1+100, W2+160, W5 AND $FF);
      END;
  FOR W1:=-100 TO 99 DO
    FOR W2:=-160 TO 159 DO
      BEGIN
        IF W1<>0 THEN R2:=W1 ELSE R2:=0.0001;
        IF W2<>0 THEN R3:=W2 ELSE R3:=0.0001;
        R1:=(ArcTan(R3/R2)*(256/6.284)*4) + SIN(SQRT((SQR(R2)+SQR(R3))/256))*64 + COS(SQRT((SQR(R2)+SQR(R3))/256))*64;
        W3:=Trunc(R1);
        PutPixel(Distort2, W1+100, W2+160, W3 AND $FF);
      END;
  END;

(****************************************************************************)
(* Init's the standard light for the bumpmapper.                            *)
(****************************************************************************)
  PROCEDURE InitBump(Light : DWORD);
  VAR W1, W2 : LONGINT;
  FUNCTION CL(X:LONGINT):LONGINT;BEGIN IF X < 0 THEN CL:=0 ELSE CL := X END;
  BEGIN
   FOR W1 := 0 TO 255 DO
    FOR W2 := 0 TO 255 DO
     PutPixel255(Light, W1, W2, CL(255-TRUNC(SQRT(SQR(W1-128)+SQR(W2-128))/191*370)));
  END;
(****************************************************************************)
(* This one init's a lightmap with a smaller light.                         *)
(****************************************************************************)
  PROCEDURE InitBumpSmall(Light : DWORD);
  VAR W1, W2 : LONGINT;
  FUNCTION CL(X:LONGINT):LONGINT;BEGIN IF X < 0 THEN CL:=0 ELSE CL := X END;
  BEGIN
   FOR W1 := 0 TO 255 DO
    FOR W2 := 0 TO 255 DO
     PutPixel255(Light, W1, W2, CL(255-TRUNC(SQRT(SQR(W1-128)+SQR(W2-128))/191*740)));
  END;


(****************************************************************************)
(* This one loads a raw image from disk onto a fake screen. Only 256*256*8b *)
(****************************************************************************)
  PROCEDURE LoadRAW(Where : DWORD; Name : STRING);
  TYPE Square255 = ARRAY[0..255,0..255] OF BYTE;
  VAR F : FILE;
      P : ^Square255;
  BEGIN
    ASSIGN(F, Name);
    {$i-}
    RESET(F, 1);
    {$i+}
    P := PTR(Where);
    IF IORESULT = 0 THEN BEGIN
      BlockRead(F, P^, 65536);
    END;
    CLOSE(F);
  END;


(****************************************************************************)
(* This one allocate memory for a new fake screen. Returns offset           *)
(****************************************************************************)
  FUNCTION NewPage : DWORD;
  VAR Temp : POINTER;
  BEGIN
    getmem(temp, 65536);
    NewPage := OFS(Temp^);  (* Returns new page offset *)
  END;


(****************************************************************************)
(* This one allocates the memory allocated to the given offset.             *)
(****************************************************************************)
  PROCEDURE DisposePage(PageOffset : DWORD);
  VAR  Temp : Pointer;
  BEGIN
    Temp := PTR(PAgeOffset);
    Freemem(Temp, 65536);
  END;


(****************************************************************************)
(* This one detects if mmx is available.                                    *)
(****************************************************************************)
  FUNCTION MMXsupported : BOOLEAN;ASSEMBLER; ASM
    (* First check if the CPUID instruction is supported. *******************)
      PUSHFD                    (* PUSH Extended flags                      *)
      POP      EAX              (* Pop'em into EAX                          *)
      MOV      EBX, EAX         (* Copy into EBX                            *)
      XOR      EAX, $200000     (* Change the 21st byte                     *)
      PUSH     EAX              (* Push this new altered value              *)
      POPFD                     (* Pop'em into the EFLAGS register.         *)
      PUSHFD                    (* Push the flags again.                    *)
      POP      EAX              (* Pop'em into EAX again.                   *)
      CMP      EAX, EBX         (* See if we could change the 21st bit.     *)
      JZ       @NoMMX           (* nopez we could'nt .. CPUID NOT SUPPORTED *)
    (* Now check for mmx. ***************************************************)
      MOV      EAX, 1           (* Call subfunction 1.                      *)
      CPUID                     (* Execute CPUID                            *)
      AND      ECX, $00800000   (* Mask out mmx bit.                        *)
      JZ       @NOMMX           (* It's a zero .. no mmx.                   *)
      MOV      AL, 1            (* Yups ... we got mmx.                     *)
      @NOMMX:
  END;
(****************************************************************************)

BEGIN
  Write ('þ Mute''s educational VGA engine : ');
  IF (MMXSupported = TRUE) OR NOT(ParamSTR(1) = 'NOMMX') THEN BEGIN
    WriteLn ('using MMX technology !');
    ClearPage      := ClearPage_mmx;
    CopyPage       := CopyPage_mmx;
    CopyPageSmudge := CopyPageSmudge_mmx;
    MixPages       := MixPages_mmx;
    MixPages2      := MixPages2_mmx;
    MixPages3      := MixPages3_mmx;
    Ripple         := Ripple_mmx;
  END ELSE BEGIN
    WriteLn ('using X86 technology !');
    ClearPage      := ClearPage_x86;
    CopyPage       := CopyPage_x86;
    CopyPageSmudge := CopyPageSmudge_x86;
    MixPages       := MixPages_x86;
    MixPages2      := MixPages2_x86;
    MixPages3      := MixPages3_x86;
    Ripple         := Ripple_x86;
  END;
END.