unit reg_u_add;

interface

uses service_fpu, fpu_types, fpu_system;

function FPU_u_add (arg1: PFPU_REG; arg2: PFPU_REG; answ: PFPU_REG;
  control_w: u16; sign: u_char; expa: s32; expb: s32): intg;

implementation

uses reg_round;

function FPU_u_add (arg1: PFPU_REG; arg2: PFPU_REG; answ: PFPU_REG;
  control_w: u16; sign: u_char; expa: s32; expb: s32): intg;
  var
    rtmp: PFPU_REG;
    shifted: FPU_REG;
    extent: u32;
    ediff, ed2, eflag, ovfl, carry: intg;
  begin
    extent := 0;
    ediff  := expa - expb;

    if (ediff < 0) then
      begin
      ediff := -ediff;
      rtmp  := arg1;
      arg1  := arg2;
      arg2  := rtmp;
      expa  := expb;
      end;

    (* Now we have exponent of arg1 >= exponent of arg2 *)

    answ^.exp := expa;

{$ifdef PARANOID}
  if ( !(arg1^.sigh  and $80000000) or !(arg2^.sigh  and $80000000) )
    begin
      EXCEPTION(EX_INTERNAL|$201);
      return -1;
    end;
{$endif}

    if (ediff = 0) then
      begin
      extent := 0;
      shifted.sigl := arg2^.sigl;
      shifted.sigh := arg2^.sigh;
      end
    else
      if (ediff < 32) then
        begin
        ed2 := 32 - ediff;
        extent := arg2^.sigl shl ed2;
        shifted.sigl := arg2^.sigl shr ediff;
        shifted.sigl := shifted.sigl or (arg2^.sigh shl ed2);
        shifted.sigh := arg2^.sigh shr ediff;
        end
      else
        if (ediff < 64) then
          begin
          ediff := ediff - 32;
          if (ediff) = 0 then
            begin
            eflag  := 0;
            extent := arg2^.sigl;
            shifted.sigl := arg2^.sigh;
            end
          else
            begin
            ed2 := 32 - ediff;
            eflag := arg2^.sigl;
            if (eflag) <> 0 then
              extent := extent or 1;
            extent := arg2^.sigl shr ediff;
            extent := extent or (arg2^.sigh shl ed2);
            shifted.sigl := arg2^.sigh shr ediff;
            end;
          shifted.sigh := 0;
          end
        else
          begin
          ediff := ediff - 64;
          if (ediff) = 0 then
            begin
            eflag  := arg2^.sigl;
            extent := arg2^.sigh;
            end
          else
            begin
            ed2 := 64 - ediff;
            eflag := arg2^.sigl or arg2^.sigh;
            extent := arg2^.sigh shr ediff;
            end;
          shifted.sigl := 0;
          shifted.sigh := 0;
          if (eflag) <> 0 then
            extent := extent or 1;
          end;

    answ^.sigh := arg1^.sigh + shifted.sigh;
    ovfl := word(shifted.sigh > answ^.sigh);
    answ^.sigl := arg1^.sigl + shifted.sigl;
    if (shifted.sigl > answ^.sigl) then
      begin
      Inc(answ^.sigh);
      if (answ^.sigh = 0) then
        ovfl := 1;
      end;
    if (ovfl) <> 0 then
      begin
      carry  := extent and 1;
      extent := extent shr 1;
      extent := extent or carry;
      if (answ^.sigl and 1) <> 0 then
        extent := extent or $80000000;
      answ^.sigl := answ^.sigl shr 1;
      if (answ^.sigh and 1) <> 0 then
        answ^.sigl := answ^.sigl or $80000000;
      answ^.sigh := answ^.sigh shr 1;
      answ^.sigh := answ^.sigh or $80000000;
      Inc(answ^.exp);
      end;

    Result := FPU_round(answ, extent, 0, control_w, sign);

  end;

end.
