(**********************************************************)
(*                                                        *)
(* The following routines are inline assembly, they are   *)
(* thus processor and bitness specific. Replace them      *)
(* with your own if you want to port the TrueType Engine  *)

 (*****************************************************************)
 (*                                                               *)
 (*  MulDiv : computes A*B/C with an intermediate 64 bits         *)
 (*           precision.                                          *)
 (*                                                               *)
 (*****************************************************************)

 function MulDiv( a, b, c : Int32 ) : Int32;
 var
   s    : Int32;
   temp : Int64;
 begin
   s := 0;
   if a < 0 then begin s := s xor 1; a := -a; end;
   if b < 0 then begin s := s xor 1; b := -b; end;
   if c < 0 then begin s := s xor 1; c := -c; end;

   MulTo64( a, b, temp );
   c := Div64by32( temp, c );

   if s <> 0 then c := -c;

   MulDiv := c;
 end;

 (*****************************************************************)
 (*                                                               *)
 (*  MulDiv : computes A*B/C with an intermediate 64 bits         *)
 (*  _Round   precision and rounding.                             *)
 (*                                                               *)
 (*****************************************************************)

 function MulDiv_Round( a, b, c : Int32 ) : Int32;
 var
   s : Int32;

   temp, temp2 : Int64;
 begin
   s := 0;
   if a < 0 then begin s := s xor 1; a := -a; end;
   if b < 0 then begin s := s xor 1; b := -b; end;
   if c < 0 then begin s := s xor 1; c := -c; end;

   MulTo64( a, b, temp );

   temp2.hi := 0;
   temp2.lo := c div 2;

   Add64( temp, temp2, temp );

   c := Div64by32( temp, c );

   if s <> 0 then c := -c;

   MulDiv_Round := c;
 end;

 procedure Neg64( var x : Int64 );
 begin
   (* Remember that -(0x80000000) == 0x80000000 with 2-complement! *)
   (* We take care of that here.                                   *)

   x.hi := x.hi xor $FFFFFFFF;
   x.lo := x.lo xor $FFFFFFFF;
   inc( x.lo );

   if x.lo = 0 then
   begin
     inc( x.hi );
     if x.hi = $80000000 then  (* check -MaxInt32-1 *)
     begin
       dec( x.lo );  (* we return $7FFFFFFF *)
       dec( x.hi );
     end;
   end;
 end;

(* We need unsigned longints to perform correctly our additions *)
(* we include inline assembly to get them, baaahhh ..           *)

{$IFDEF OS2}

(**********************************************************)
(* 64 Bit Addition                                        *)

procedure Add64( var X, Y, Z : Int64 ); assembler; {&USES ebx, edx}
asm
  mov ebx,[X].dword
  mov eax,[ebx]
  mov edx,[ebx+4]

  mov ebx,[Y].dword
  add eax,[ebx]
  adc edx,[ebx+4]

  mov ebx,[Z].dword
  mov [ebx],eax
  mov [ebx+4],edx
end;


(**********************************************************)
(* 64 Bit Substraction                                    *)

procedure Sub64( var X, Y, Z : Int64 ); assembler; {&USES eax, ebx, edx}
asm
  mov ebx,[X].dword
  mov eax,[ebx]
  mov edx,[ebx+4]

  mov ebx,[Y].dword
  sub eax,[ebx]
  sbb edx,[ebx+4]

  mov ebx,[Z].dword
  mov [ebx],eax
  mov [ebx+4],edx
end;


(**********************************************************)
(* Multiply two Int32 to an Int64                         *)

procedure MulTo64( X, Y : Int32; var Z : Int64 ); assembler; {&USES ebx, edx }
asm
  mov ebx,[Z].dword
  mov eax,[X]
  imul [Y]
  mov [ebx],eax
  mov [ebx+4],edx
end;


(**********************************************************)
(* Divide an Int64 by an Int32                            *)

function Div64by32( var X : Int64; Y : Int32 ) : Int32; assembler;
         {&USES ebx, edx}
asm
  mov ebx, [X].dword
  mov eax, [ebx]
  mov edx, [ebx+4]
  idiv [Y]
end;

procedure DivMod64by32( var X : Int64; Y : Int32; var Q, R : Int32 );
          assembler; {&USES ebx, edx}
asm
  mov ebx, [X].dword
  mov eax, [ebx]
  mov edx, [ebx+4]
  idiv [Y]
  mov ebx, [Q].dword
  mov [ebx], eax
  mov ebx, [R].dword
  mov [ebx], edx
end;


{$ELSE}

{**********************************************************}
{* 64 Bit Addition                                        *}

procedure Add64( var X, Y, Z : Int64 ); assembler;
asm
  les si,[X]

  mov ax,es:[ si ].word
  mov dx,es:[si+2].word
  mov bx,es:[si+4].word
  mov cx,es:[si+6].word

  les si,[Y]
  add ax,es:[ si ].word
  adc dx,es:[si+2].word
  adc bx,es:[si+4].word
  adc cx,es:[si+6].word

  les si,[Z]
  mov es:[ si ].word,ax
  mov es:[si+2].word,dx
  mov es:[si+4].word,bx
  mov es:[si+6].word,cx
end;


{**********************************************************}
{* 64 Bit Substraction                                    *}

procedure Sub64( var X, Y, Z : Int64 ); assembler;
asm
  les si,[X]

  mov ax,es:[ si ].word
  mov dx,es:[si+2].word
  mov bx,es:[si+4].word
  mov cx,es:[si+6].word

  les si,[Y]
  sub ax,es:[ si ].word
  sbb dx,es:[si+2].word
  sbb bx,es:[si+4].word
  sbb cx,es:[si+6].word

  les si,[Z]
  mov es:[ si ].word,ax
  mov es:[si+2].word,dx
  mov es:[si+4].word,bx
  mov es:[si+6].word,cx
end;


{**********************************************************}
{* Multiply two Int32 to an Int64                         *}

procedure MulTo64( X, Y : Int32; var Z : Int64 ); assembler;
asm
  les si,[Z]
  db $66; mov ax,[X].word
  db $66; imul [Y].word
  db $66; mov es:[si],ax
  db $66; mov es:[si+4],dx
end;


{**********************************************************}
{* Divide an Int64 by an Int32                            *}

function Div64by32( var X : Int64; Y : Int32 ) : Int32; assembler;
asm
  les si,[X]

  db $66; mov ax,es:[si]
  db $66; mov dx,es:[si+4]
  db $66; idiv [Y].word

  db $66; mov dx, ax
  db $66; sar dx, 16
end;

procedure DivMod64by32( var X : Int64; Y : Int32; var Q, R : Int32 ); assembler;
asm
  les si,[X]

  db $66; mov ax,es:[si]
  db $66; mov dx,es:[si+4]
  db $66; idiv [Y].word

  les si, [Q]
  db $66; mov es:[si], ax

  les si, [R]
  db $66; mov es:[si], dx
end;


{$ENDIF}


(**********************************************************)
(* MSB index ( return -1 for 0 )                          *)

function Order64( var Z : Int64 ) : integer;
begin
  if Z.Hi <> 0 then Order64 := 32 + Order32( Z.Hi )
               else Order64 := Order32( Z.Lo );
end;


(**********************************************************)
(* MSB index ( return -1 for 0 )                          *)

function Order32( Z : Int32 ) : integer;
var b : integer;
begin
  b := 0;
  while Z <> 0 do begin Z := Z shr 1; inc( b ); end;
  Order32 := b-1;
end;

