m68k: add a modified version of the generic fpc_round_real, which takes some m68k FPU oddities into account

git-svn-id: trunk@36531 -
This commit is contained in:
Károly Balogh 2017-06-19 01:12:00 +00:00
parent 6bb48bf34f
commit 50812415d8

View File

@ -19,3 +19,73 @@ begin
result:=result - (qword(1) shl 52);
end;
{$endif FPC_INCLUDE_SOFTWARE_LONGWORD_TO_DOUBLE}
{$if defined(FPU68881) or defined(FPUCOLDFIRE)}
{$ifndef FPC_SYSTEM_HAS_ROUND}
{$define FPC_SYSTEM_HAS_ROUND}
function fpc_round_real(d : ValReal) : int64;compilerproc;
type
float64 = record
high,low: longint;
end;
var
tmp: double;
j0: longint;
hx: longword;
sx: longint;
const
H2_52: array[0..1] of double = (
4.50359962737049600000e+15,
-4.50359962737049600000e+15
);
Begin
{ This basically calculates trunc((d+2**52)-2**52) }
hx:=float64(d).high;
j0:=((hx shr 20) and $7ff) - $3ff;
sx:=hx shr 31;
hx:=(hx and $fffff) or $100000;
if j0>=52 then { No fraction bits, already integer }
begin
if j0>=63 then { Overflow, let trunc() raise an exception }
exit(trunc(d)) { and/or return +/-MaxInt64 if it's masked }
else
result:=((int64(hx) shl 32) or float64(d).low) shl (j0-52);
end
else
begin
{ Rounding happens here. It is important that the expression is not
optimized by selecting a larger type to store 'tmp'. }
{ The double cast should enforce a memory store and reload, which is the
fastest way on a 68881/2 to enforce the rounding to double precision.
The internal operation of the '88x is always in extended. If the rounding
of the FPU is set to a different precision in the FPCR, the result is a
a large performance penalty, according to the 68881/68882 Users Manual
Section 2.2.2. So we keep the FPU in extended, but this means the rounding
to double trick might conflict with tmp being a regvar. (KB) }
{$ifdef FPU68881}
tmp:=double(float64(H2_52[sx]+d));
{$else}
{ The above doesn't affect the CF FPU. Its maximum precision is double. }
tmp:=H2_52[sx]+d;
{$endif}
d:=tmp-H2_52[sx];
hx:=float64(d).high;
j0:=((hx shr 20) and $7ff)-$3ff;
hx:=(hx and $fffff) or $100000;
if j0<=20 then
begin
if j0<0 then
exit(0)
else { more than 32 fraction bits, low dword discarded }
result:=hx shr (20-j0);
end
else
result:=(int64(hx) shl (j0-20)) or (float64(d).low shr (52-j0));
end;
if sx<>0 then
result:=-result;
end;
{$endif FPC_SYSTEM_HAS_ROUND}
{$endif defined(FPU68881) or defined(FPUCOLDFIRE)}