{
    $Id$
    This file is part of the Free Pascal run time library.
    Copyright (c) 2000 by Jonas Maebe and other members of the
    Free Pascal development team

    Implementation of mathamatical Routines (only for real)

    See the file COPYING.FPC, included in this distribution,
    for details about the copyright.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

 **********************************************************************}


{****************************************************************************
                       EXTENDED data type routines
 ****************************************************************************}

    function pi : double;[internconst:in_pi];
      begin
        pi := 3.14159265358979320;
      end;

    function abs(d : extended) : extended;[internproc:in_abs_extended];
    function sqr(d : extended) : extended;[internproc:in_sqr_extended];
    function sqrt(d : extended) : extended;[internproc:in_sqrt_extended];

    function arctan(d : extended) : extended;[internconst:in_arctan_extended];
      begin
        runerror(207);
      end;

    function ln(d : extended) : extended;[internconst:in_ln_extended];
      begin
        runerror(207);
      end;

    function sin(d : extended) : extended;[internconst: in_sin_extended];
      begin
        runerror(207);
      end;

    function cos(d : extended) : extended;[internconst:in_cos_extended];
      begin
        runerror(207);
      end;

    function exp(d : extended) : extended;assembler;[internconst:in_const_exp];
      begin
        runerror(207);
      end;


    function frac(d : extended) : extended;assembler;[internconst:in_const_frac];
      begin
        runerror(207);
      end;


    function int(d : extended) : extended;assembler;[internconst:in_const_int];
      begin
        runerror(207);
      end;


    function trunc(d : extended) : longint;assembler;[internconst:in_const_trunc];
      { input: d in fr1      }
      { output: result in r3 }
      assembler;
      var
        temp:
          record
            case byte of
              0: (l1,l2: longint);
              1: (d: double);
          end;
      asm
        fctiwz   fr1,fr1
        stfd     fr1,temp.d
        lwz      r3,temp.l2
      end ['r3','fr1'];


    function round(d : extended) : longint;assembler;[internconst:in_const_round];
      { input: d in fr1      }
      { output: result in r3 }
      assembler;
      var
        temp:
          record
            case byte of
              0: (l1,l2: longint);
              1: (d: double);
          end;
      asm
        fctiw    fr1,fr1
        stfd     fr1,temp.d
        lwz      r3,temp.l2
      end ['r3','fr1'];


   function power(bas,expo : extended) : extended;
     begin
        if bas=0 then
          begin
            if expo<>0 then
              power:=0.0
            else
              HandleError(207);
          end
        else if expo=0 then
         power:=1
        else
        { bas < 0 is not allowed }
         if bas<0 then
          handleerror(207)
         else
          power:=exp(ln(bas)*expo);
     end;


{****************************************************************************
                       Longint data type routines
 ****************************************************************************}

   function power(bas,expo : longint) : longint;
     begin
        if bas=0 then
          begin
            if expo<>0 then
              power:=0
            else
              HandleError(207);
          end
        else if expo=0 then
         power:=1
        else
         begin
           if bas<0 then
            begin
              if odd(expo) then
               power:=-round(exp(ln(-bas)*expo))
              else
               power:=round(exp(ln(-bas)*expo));
            end
           else
            power:=round(exp(ln(bas)*expo));
         end;
     end;

{****************************************************************************
                    Helper routines to support old TP styled reals
 ****************************************************************************}

    { warning: the following converts a little-endian TP-style real }
    { to a big-endian double. So don't byte-swap the TP real!       }
    function real2double(r : real48) : double;

      var
         res : array[0..7] of byte;
         exponent : word;

      begin
         { copy mantissa }
         res[6]:=0;
         res[5]:=r[1] shl 5;
         res[4]:=(r[1] shr 3) or (r[2] shl 5);
         res[3]:=(r[2] shr 3) or (r[3] shl 5);
         res[2]:=(r[3] shr 3) or (r[4] shl 5);
         res[1]:=(r[4] shr 3) or (r[5] and $7f) shl 5;
         res[0]:=(r[5] and $7f) shr 3;

         { copy exponent }
         { correct exponent: }
         exponent:=(word(r[0])+(1023-129));
         res[1]:=res[1] or ((exponent and $f) shl 4);
         res[0]:=exponent shr 4;

         { set sign }
         res[0]:=res[0] or (r[5] and $80);
         real2double:=double(res);
      end;


{****************************************************************************
                         Int to real helpers
 ****************************************************************************}

function fpc_int64_to_real(i: int64): double; compilerproc;
assembler;
{ input: high(i) in r3, low(i) in r4 }
{ output: double(i) in f0            }
var
  temp:
    record
      case byte of
        0: (l1,l2: cardinal);
        1: (d: double);
    end;
asm
           lis    r0,$4330
           stw    r0,temp.l1
           xoris  r3,r3,$8000
           stw    r3,temp.l2
           lis    r3,longint_to_real_helper@ha
           lfd    fr1,longint_to_real_helper@l(r3)
           lfd    fr0,temp.d
           stw    r4,temp.l2
           fsub   fr0,fr0,fr1
           lis    r4,cardinal_to_real_helper@ha
           lfd    fr1,cardinal_to_real_helper@l(r4)
           lis    r3,int_to_real_factor@ha
           lfd    fr3,temp
           lfd    fr2,int_to_real_factor@l(r3)
           fsub   fr3,fr3,fr1
           fmadd  fr1,fr0,fr2,fr3
end ['r0','r3','r4','fr0','fr1','fr2','fr3'];


function fpc_qword_to_real(q: qword): double; compilerproc;
assembler;
{ input: high(q) in r3, low(q) in r4 }
{ output: double(q) in f0            }
var
  temp:
    record
      case byte of
        0: (l1,l2: cardinal);
        1: (d: double);
    end;
asm
           lis    r0,$4330
           stw    r0,temp.l1
           stw    r3,temp.l2
           lfd    fr0,temp.d
           lis    r3,cardinal_to_real_helper@ha
           lfd    fr1,cardinal_to_real_helper@l(r3)
           stw    r4,temp.l2
           fsub   fr0,fr0,fr1
           lfd    fr3,temp
           lis    r3,int_to_real_factor@ha
           lfd    fr2,int_to_real_factor@l(r3)
           fsub   fr3,fr3,fr1
           fmadd  fr1,fr0,fr2,fr3
end ['r0','r3','fr0','fr1','fr2','fr3'];



{
  $Log$
  Revision 1.3  2001-12-02 16:19:45  jonas
    * fpu results are returned in fr1, not fr0

  Revision 1.2  2001/10/30 17:18:14  jonas
    * fixed fpc_int64_to_double and fpc_int64_to_double (fpc_int64_to_double
      is now mostly tested and should work fine, fpc_qword_to_double should
      work too since it's almost the same)

  Revision 1.1  2001/10/28 14:09:13  jonas
    + initial implementation, lots of things still missing


}
