Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

120
arch/arm/nwfpe/ARM-gcc.h Normal file
View file

@ -0,0 +1,120 @@
/*
-------------------------------------------------------------------------------
The macro `BITS64' can be defined to indicate that 64-bit integer types are
supported by the compiler.
-------------------------------------------------------------------------------
*/
#define BITS64
/*
-------------------------------------------------------------------------------
Each of the following `typedef's defines the most convenient type that holds
integers of at least as many bits as specified. For example, `uint8' should
be the most convenient type that can hold unsigned integers of as many as
8 bits. The `flag' type must be able to hold either a 0 or 1. For most
implementations of C, `flag', `uint8', and `int8' should all be `typedef'ed
to the same as `int'.
-------------------------------------------------------------------------------
*/
typedef char flag;
typedef unsigned char uint8;
typedef signed char int8;
typedef int uint16;
typedef int int16;
typedef unsigned int uint32;
typedef signed int int32;
#ifdef BITS64
typedef unsigned long long int bits64;
typedef signed long long int sbits64;
#endif
/*
-------------------------------------------------------------------------------
Each of the following `typedef's defines a type that holds integers
of _exactly_ the number of bits specified. For instance, for most
implementation of C, `bits16' and `sbits16' should be `typedef'ed to
`unsigned short int' and `signed short int' (or `short int'), respectively.
-------------------------------------------------------------------------------
*/
typedef unsigned char bits8;
typedef signed char sbits8;
typedef unsigned short int bits16;
typedef signed short int sbits16;
typedef unsigned int bits32;
typedef signed int sbits32;
#ifdef BITS64
typedef unsigned long long int uint64;
typedef signed long long int int64;
#endif
#ifdef BITS64
/*
-------------------------------------------------------------------------------
The `LIT64' macro takes as its argument a textual integer literal and if
necessary ``marks'' the literal as having a 64-bit integer type. For
example, the Gnu C Compiler (`gcc') requires that 64-bit literals be
appended with the letters `LL' standing for `long long', which is `gcc's
name for the 64-bit integer type. Some compilers may allow `LIT64' to be
defined as the identity macro: `#define LIT64( a ) a'.
-------------------------------------------------------------------------------
*/
#define LIT64( a ) a##LL
#endif
/*
-------------------------------------------------------------------------------
The macro `INLINE' can be used before functions that should be inlined. If
a compiler does not support explicit inlining, this macro should be defined
to be `static'.
-------------------------------------------------------------------------------
*/
#define INLINE static inline
/* For use as a GCC soft-float library we need some special function names. */
#ifdef __LIBFLOAT__
/* Some 32-bit ops can be mapped straight across by just changing the name. */
#define float32_add __addsf3
#define float32_sub __subsf3
#define float32_mul __mulsf3
#define float32_div __divsf3
#define int32_to_float32 __floatsisf
#define float32_to_int32_round_to_zero __fixsfsi
#define float32_to_uint32_round_to_zero __fixunssfsi
/* These ones go through the glue code. To avoid namespace pollution
we rename the internal functions too. */
#define float32_eq ___float32_eq
#define float32_le ___float32_le
#define float32_lt ___float32_lt
/* All the 64-bit ops have to go through the glue, so we pull the same
trick. */
#define float64_add ___float64_add
#define float64_sub ___float64_sub
#define float64_mul ___float64_mul
#define float64_div ___float64_div
#define int32_to_float64 ___int32_to_float64
#define float64_to_int32_round_to_zero ___float64_to_int32_round_to_zero
#define float64_to_uint32_round_to_zero ___float64_to_uint32_round_to_zero
#define float64_to_float32 ___float64_to_float32
#define float32_to_float64 ___float32_to_float64
#define float64_eq ___float64_eq
#define float64_le ___float64_le
#define float64_lt ___float64_lt
#if 0
#define float64_add __adddf3
#define float64_sub __subdf3
#define float64_mul __muldf3
#define float64_div __divdf3
#define int32_to_float64 __floatsidf
#define float64_to_int32_round_to_zero __fixdfsi
#define float64_to_uint32_round_to_zero __fixunsdfsi
#define float64_to_float32 __truncdfsf2
#define float32_to_float64 __extendsfdf2
#endif
#endif

91
arch/arm/nwfpe/ChangeLog Normal file
View file

@ -0,0 +1,91 @@
2003-03-22 Ralph Siemsen <ralphs@netwinder.org>
* Reformat all but softfloat files to get a consistent coding style.
Used "indent -kr -i8 -ts8 -sob -l132 -ss" and a few manual fixups.
* Removed dead code and fixed function protypes to match definitions.
* Consolidated use of (opcode && MASK_ARITHMETIC_OPCODE) >> 20.
* Make 80-bit precision a compile-time option. (1%)
* Only initialize FPE state once in repeat-FP situations. (6%)
2002-01-19 Russell King <rmk@arm.linux.org.uk>
* fpa11.h - Add documentation
- remove userRegisters pointer from this structure.
- add new method to obtain integer register values.
* softfloat.c - Remove float128
* softfloat.h - Remove float128
* softfloat-specialize - Remove float128
* The FPA11 structure is not a kernel-specific data structure.
It is used by users of ptrace to examine the values of the
floating point registers. Therefore, any changes to the
FPA11 structure (size or position of elements contained
within) have to be well thought out.
* Since 128-bit float requires the FPA11 structure to change
size, it has been removed. 128-bit float is currently unused,
and needs various things to be re-worked so that we won't
overflow the available space in the task structure.
* The changes are designed to break any patch that goes on top
of this code, so that the authors properly review their changes.
1999-08-19 Scott Bambrough <scottb@netwinder.org>
* fpmodule.c - Changed version number to 0.95
* fpa11.h - modified FPA11, FPREG structures
* fpa11.c - Changes due to FPA11, FPREG structure alterations.
* fpa11_cpdo.c - Changes due to FPA11, FPREG structure alterations.
* fpa11_cpdt.c - Changes due to FPA11, FPREG structure alterations.
* fpa11_cprt.c - Changes due to FPA11, FPREG structure alterations.
* single_cpdo.c - Changes due to FPA11, FPREG structure alterations.
* double_cpdo.c - Changes due to FPA11, FPREG structure alterations.
* extended_cpdo.c - Changes due to FPA11, FPREG structure alterations.
* I discovered several bugs. First and worst is that the kernel
passes in a pointer to the FPE's state area. This is defined
as a struct user_fp (see user.h). This pointer was cast to a
FPA11*. Unfortunately FPA11 and user_fp are of different sizes;
user_fp is smaller. This meant that the FPE scribbled on things
below its area, which is bad, as the area is in the thread_struct
embedded in the process task structure. Thus we were scribbling
over one of the most important structures in the entire OS.
* user_fp and FPA11 have now been harmonized. Most of the changes
in the above code were dereferencing problems due to moving the
register type out of FPREG, and getting rid of the union variable
fpvalue.
* Second I noticed resetFPA11 was not always being called for a
task. This should happen on the first floating point exception
that occurs. It is controlled by init_flag in FPA11. The
comment in the code beside init_flag state the kernel guarantees
this to be zero. Not so. I found that the kernel recycles task
structures, and that recycled ones may not have init_flag zeroed.
I couldn't even find anything that guarantees it is zeroed when
when the task structure is initially allocated. In any case
I now initialize the entire FPE state in the thread structure to
zero when allocated and recycled. See alloc_task_struct() and
flush_thread() in arch/arm/process.c. The change to
alloc_task_struct() may not be necessary, but I left it in for
completeness (better safe than sorry).
1998-11-23 Scott Bambrough <scottb@netwinder.org>
* README.FPE - fix typo in description of lfm/sfm instructions
* NOTES - Added file to describe known bugs/problems
* fpmodule.c - Changed version number to 0.94
1998-11-20 Scott Bambrough <scottb@netwinder.org>
* README.FPE - fix description of URD, NRM instructions
* TODO - remove URD, NRM instructions from TODO list
* single_cpdo.c - implement URD, NRM
* double_cpdo.c - implement URD, NRM
* extended_cpdo.c - implement URD, NRM
1998-11-19 Scott Bambrough <scottb@netwinder.org>
* ChangeLog - Added this file to track changes made.
* fpa11.c - added code to initialize register types to typeNone
* fpa11_cpdt.c - fixed bug in storeExtended (typeExtended changed to
typeDouble in switch statement)

12
arch/arm/nwfpe/Makefile Normal file
View file

@ -0,0 +1,12 @@
#
# Copyright (C) 1998, 1999, 2001 Philip Blundell
#
obj-$(CONFIG_FPE_NWFPE) += nwfpe.o
nwfpe-y += fpa11.o fpa11_cpdo.o fpa11_cpdt.o \
fpa11_cprt.o fpmodule.o fpopcode.o \
softfloat.o single_cpdo.o double_cpdo.o \
entry.o
nwfpe-$(CONFIG_FPE_NWFPE_XP) += extended_cpdo.o

View file

@ -0,0 +1,167 @@
/*
NetWinder Floating Point Emulator
(c) Rebel.COM, 1998,1999
Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
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. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "fpa11.h"
#include "softfloat.h"
#include "fpopcode.h"
union float64_components {
float64 f64;
unsigned int i[2];
};
float64 float64_exp(float64 Fm);
float64 float64_ln(float64 Fm);
float64 float64_sin(float64 rFm);
float64 float64_cos(float64 rFm);
float64 float64_arcsin(float64 rFm);
float64 float64_arctan(float64 rFm);
float64 float64_log(float64 rFm);
float64 float64_tan(float64 rFm);
float64 float64_arccos(float64 rFm);
float64 float64_pow(float64 rFn, float64 rFm);
float64 float64_pol(float64 rFn, float64 rFm);
static float64 float64_rsf(struct roundingData *roundData, float64 rFn, float64 rFm)
{
return float64_sub(roundData, rFm, rFn);
}
static float64 float64_rdv(struct roundingData *roundData, float64 rFn, float64 rFm)
{
return float64_div(roundData, rFm, rFn);
}
static float64 (*const dyadic_double[16])(struct roundingData*, float64 rFn, float64 rFm) = {
[ADF_CODE >> 20] = float64_add,
[MUF_CODE >> 20] = float64_mul,
[SUF_CODE >> 20] = float64_sub,
[RSF_CODE >> 20] = float64_rsf,
[DVF_CODE >> 20] = float64_div,
[RDF_CODE >> 20] = float64_rdv,
[RMF_CODE >> 20] = float64_rem,
/* strictly, these opcodes should not be implemented */
[FML_CODE >> 20] = float64_mul,
[FDV_CODE >> 20] = float64_div,
[FRD_CODE >> 20] = float64_rdv,
};
static float64 float64_mvf(struct roundingData *roundData,float64 rFm)
{
return rFm;
}
static float64 float64_mnf(struct roundingData *roundData,float64 rFm)
{
union float64_components u;
u.f64 = rFm;
#ifdef __ARMEB__
u.i[0] ^= 0x80000000;
#else
u.i[1] ^= 0x80000000;
#endif
return u.f64;
}
static float64 float64_abs(struct roundingData *roundData,float64 rFm)
{
union float64_components u;
u.f64 = rFm;
#ifdef __ARMEB__
u.i[0] &= 0x7fffffff;
#else
u.i[1] &= 0x7fffffff;
#endif
return u.f64;
}
static float64 (*const monadic_double[16])(struct roundingData *, float64 rFm) = {
[MVF_CODE >> 20] = float64_mvf,
[MNF_CODE >> 20] = float64_mnf,
[ABS_CODE >> 20] = float64_abs,
[RND_CODE >> 20] = float64_round_to_int,
[URD_CODE >> 20] = float64_round_to_int,
[SQT_CODE >> 20] = float64_sqrt,
[NRM_CODE >> 20] = float64_mvf,
};
unsigned int DoubleCPDO(struct roundingData *roundData, const unsigned int opcode, FPREG * rFd)
{
FPA11 *fpa11 = GET_FPA11();
float64 rFm;
unsigned int Fm, opc_mask_shift;
Fm = getFm(opcode);
if (CONSTANT_FM(opcode)) {
rFm = getDoubleConstant(Fm);
} else {
switch (fpa11->fType[Fm]) {
case typeSingle:
rFm = float32_to_float64(fpa11->fpreg[Fm].fSingle);
break;
case typeDouble:
rFm = fpa11->fpreg[Fm].fDouble;
break;
default:
return 0;
}
}
opc_mask_shift = (opcode & MASK_ARITHMETIC_OPCODE) >> 20;
if (!MONADIC_INSTRUCTION(opcode)) {
unsigned int Fn = getFn(opcode);
float64 rFn;
switch (fpa11->fType[Fn]) {
case typeSingle:
rFn = float32_to_float64(fpa11->fpreg[Fn].fSingle);
break;
case typeDouble:
rFn = fpa11->fpreg[Fn].fDouble;
break;
default:
return 0;
}
if (dyadic_double[opc_mask_shift]) {
rFd->fDouble = dyadic_double[opc_mask_shift](roundData, rFn, rFm);
} else {
return 0;
}
} else {
if (monadic_double[opc_mask_shift]) {
rFd->fDouble = monadic_double[opc_mask_shift](roundData, rFm);
} else {
return 0;
}
}
return 1;
}

124
arch/arm/nwfpe/entry.S Normal file
View file

@ -0,0 +1,124 @@
/*
NetWinder Floating Point Emulator
(c) Rebel.COM, 1998
(c) 1998, 1999 Philip Blundell
Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
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. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <asm/assembler.h>
#include <asm/opcodes.h>
/* This is the kernel's entry point into the floating point emulator.
It is called from the kernel with code similar to this:
sub r4, r5, #4
ldrt r0, [r4] @ r0 = instruction
adrsvc al, r9, ret_from_exception @ r9 = normal FP return
adrsvc al, lr, fpundefinstr @ lr = undefined instr return
get_current_task r10
mov r8, #1
strb r8, [r10, #TSK_USED_MATH] @ set current->used_math
add r10, r10, #TSS_FPESAVE @ r10 = workspace
ldr r4, .LC2
ldr pc, [r4] @ Call FP emulator entry point
The kernel expects the emulator to return via one of two possible
points of return it passes to the emulator. The emulator, if
successful in its emulation, jumps to ret_from_exception (passed in
r9) and the kernel takes care of returning control from the trap to
the user code. If the emulator is unable to emulate the instruction,
it returns via _fpundefinstr (passed via lr) and the kernel halts the
user program with a core dump.
On entry to the emulator r10 points to an area of private FP workspace
reserved in the thread structure for this process. This is where the
emulator saves its registers across calls. The first word of this area
is used as a flag to detect the first time a process uses floating point,
so that the emulator startup cost can be avoided for tasks that don't
want it.
This routine does three things:
1) The kernel has created a struct pt_regs on the stack and saved the
user registers into it. See /usr/include/asm/proc/ptrace.h for details.
2) It calls EmulateAll to emulate a floating point instruction.
EmulateAll returns 1 if the emulation was successful, or 0 if not.
3) If an instruction has been emulated successfully, it looks ahead at
the next instruction. If it is a floating point instruction, it
executes the instruction, without returning to user space. In this
way it repeatedly looks ahead and executes floating point instructions
until it encounters a non floating point instruction, at which time it
returns via _fpreturn.
This is done to reduce the effect of the trap overhead on each
floating point instructions. GCC attempts to group floating point
instructions to allow the emulator to spread the cost of the trap over
several floating point instructions. */
#include <asm/asm-offsets.h>
.globl nwfpe_enter
nwfpe_enter:
mov r4, lr @ save the failure-return addresses
mov sl, sp @ we access the registers via 'sl'
ldr r5, [sp, #S_PC] @ get contents of PC;
mov r6, r0 @ save the opcode
emulate:
ldr r1, [sp, #S_PSR] @ fetch the PSR
bl arm_check_condition @ check the condition
cmp r0, #ARM_OPCODE_CONDTEST_PASS @ condition passed?
@ if condition code failed to match, next insn
bne next @ get the next instruction;
mov r0, r6 @ prepare for EmulateAll()
bl EmulateAll @ emulate the instruction
cmp r0, #0 @ was emulation successful
reteq r4 @ no, return failure
next:
.Lx1: ldrt r6, [r5], #4 @ get the next instruction and
@ increment PC
and r2, r6, #0x0F000000 @ test for FP insns
teq r2, #0x0C000000
teqne r2, #0x0D000000
teqne r2, #0x0E000000
retne r9 @ return ok if not a fp insn
str r5, [sp, #S_PC] @ update PC copy in regs
mov r0, r6 @ save a copy
b emulate @ check condition and emulate
@ We need to be prepared for the instructions at .Lx1 and .Lx2
@ to fault. Emit the appropriate exception gunk to fix things up.
@ ??? For some reason, faults can happen at .Lx2 even with a
@ plain LDR instruction. Weird, but it seems harmless.
.pushsection .fixup,"ax"
.align 2
.Lfix: ret r9 @ let the user eat segfaults
.popsection
.pushsection __ex_table,"a"
.align 3
.long .Lx1, .Lfix
.popsection

View file

@ -0,0 +1,154 @@
/*
NetWinder Floating Point Emulator
(c) Rebel.COM, 1998,1999
Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
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. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "fpa11.h"
#include "softfloat.h"
#include "fpopcode.h"
floatx80 floatx80_exp(floatx80 Fm);
floatx80 floatx80_ln(floatx80 Fm);
floatx80 floatx80_sin(floatx80 rFm);
floatx80 floatx80_cos(floatx80 rFm);
floatx80 floatx80_arcsin(floatx80 rFm);
floatx80 floatx80_arctan(floatx80 rFm);
floatx80 floatx80_log(floatx80 rFm);
floatx80 floatx80_tan(floatx80 rFm);
floatx80 floatx80_arccos(floatx80 rFm);
floatx80 floatx80_pow(floatx80 rFn, floatx80 rFm);
floatx80 floatx80_pol(floatx80 rFn, floatx80 rFm);
static floatx80 floatx80_rsf(struct roundingData *roundData, floatx80 rFn, floatx80 rFm)
{
return floatx80_sub(roundData, rFm, rFn);
}
static floatx80 floatx80_rdv(struct roundingData *roundData, floatx80 rFn, floatx80 rFm)
{
return floatx80_div(roundData, rFm, rFn);
}
static floatx80 (*const dyadic_extended[16])(struct roundingData*, floatx80 rFn, floatx80 rFm) = {
[ADF_CODE >> 20] = floatx80_add,
[MUF_CODE >> 20] = floatx80_mul,
[SUF_CODE >> 20] = floatx80_sub,
[RSF_CODE >> 20] = floatx80_rsf,
[DVF_CODE >> 20] = floatx80_div,
[RDF_CODE >> 20] = floatx80_rdv,
[RMF_CODE >> 20] = floatx80_rem,
/* strictly, these opcodes should not be implemented */
[FML_CODE >> 20] = floatx80_mul,
[FDV_CODE >> 20] = floatx80_div,
[FRD_CODE >> 20] = floatx80_rdv,
};
static floatx80 floatx80_mvf(struct roundingData *roundData, floatx80 rFm)
{
return rFm;
}
static floatx80 floatx80_mnf(struct roundingData *roundData, floatx80 rFm)
{
rFm.high ^= 0x8000;
return rFm;
}
static floatx80 floatx80_abs(struct roundingData *roundData, floatx80 rFm)
{
rFm.high &= 0x7fff;
return rFm;
}
static floatx80 (*const monadic_extended[16])(struct roundingData*, floatx80 rFm) = {
[MVF_CODE >> 20] = floatx80_mvf,
[MNF_CODE >> 20] = floatx80_mnf,
[ABS_CODE >> 20] = floatx80_abs,
[RND_CODE >> 20] = floatx80_round_to_int,
[URD_CODE >> 20] = floatx80_round_to_int,
[SQT_CODE >> 20] = floatx80_sqrt,
[NRM_CODE >> 20] = floatx80_mvf,
};
unsigned int ExtendedCPDO(struct roundingData *roundData, const unsigned int opcode, FPREG * rFd)
{
FPA11 *fpa11 = GET_FPA11();
floatx80 rFm;
unsigned int Fm, opc_mask_shift;
Fm = getFm(opcode);
if (CONSTANT_FM(opcode)) {
rFm = getExtendedConstant(Fm);
} else {
switch (fpa11->fType[Fm]) {
case typeSingle:
rFm = float32_to_floatx80(fpa11->fpreg[Fm].fSingle);
break;
case typeDouble:
rFm = float64_to_floatx80(fpa11->fpreg[Fm].fDouble);
break;
case typeExtended:
rFm = fpa11->fpreg[Fm].fExtended;
break;
default:
return 0;
}
}
opc_mask_shift = (opcode & MASK_ARITHMETIC_OPCODE) >> 20;
if (!MONADIC_INSTRUCTION(opcode)) {
unsigned int Fn = getFn(opcode);
floatx80 rFn;
switch (fpa11->fType[Fn]) {
case typeSingle:
rFn = float32_to_floatx80(fpa11->fpreg[Fn].fSingle);
break;
case typeDouble:
rFn = float64_to_floatx80(fpa11->fpreg[Fn].fDouble);
break;
case typeExtended:
rFn = fpa11->fpreg[Fn].fExtended;
break;
default:
return 0;
}
if (dyadic_extended[opc_mask_shift]) {
rFd->fExtended = dyadic_extended[opc_mask_shift](roundData, rFn, rFm);
} else {
return 0;
}
} else {
if (monadic_extended[opc_mask_shift]) {
rFd->fExtended = monadic_extended[opc_mask_shift](roundData, rFm);
} else {
return 0;
}
}
return 1;
}

128
arch/arm/nwfpe/fpa11.c Normal file
View file

@ -0,0 +1,128 @@
/*
NetWinder Floating Point Emulator
(c) Rebel.COM, 1998,1999
(c) Philip Blundell, 2001
Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
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. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "fpa11.h"
#include "fpopcode.h"
#include "fpmodule.h"
#include "fpmodule.inl"
#include <linux/compiler.h>
#include <linux/string.h>
/* Reset the FPA11 chip. Called to initialize and reset the emulator. */
static void resetFPA11(void)
{
int i;
FPA11 *fpa11 = GET_FPA11();
/* initialize the register type array */
for (i = 0; i <= 7; i++) {
fpa11->fType[i] = typeNone;
}
/* FPSR: set system id to FP_EMULATOR, set AC, clear all other bits */
fpa11->fpsr = FP_EMULATOR | BIT_AC;
}
int8 SetRoundingMode(const unsigned int opcode)
{
switch (opcode & MASK_ROUNDING_MODE) {
default:
case ROUND_TO_NEAREST:
return float_round_nearest_even;
case ROUND_TO_PLUS_INFINITY:
return float_round_up;
case ROUND_TO_MINUS_INFINITY:
return float_round_down;
case ROUND_TO_ZERO:
return float_round_to_zero;
}
}
int8 SetRoundingPrecision(const unsigned int opcode)
{
#ifdef CONFIG_FPE_NWFPE_XP
switch (opcode & MASK_ROUNDING_PRECISION) {
case ROUND_SINGLE:
return 32;
case ROUND_DOUBLE:
return 64;
case ROUND_EXTENDED:
return 80;
default:
return 80;
}
#endif
return 80;
}
void nwfpe_init_fpa(union fp_state *fp)
{
FPA11 *fpa11 = (FPA11 *)fp;
#ifdef NWFPE_DEBUG
printk("NWFPE: setting up state.\n");
#endif
memset(fpa11, 0, sizeof(FPA11));
resetFPA11();
fpa11->initflag = 1;
}
/* Emulate the instruction in the opcode. */
unsigned int EmulateAll(unsigned int opcode)
{
unsigned int code;
#ifdef NWFPE_DEBUG
printk("NWFPE: emulating opcode %08x\n", opcode);
#endif
code = opcode & 0x00000f00;
if (code == 0x00000100 || code == 0x00000200) {
/* For coprocessor 1 or 2 (FPA11) */
code = opcode & 0x0e000000;
if (code == 0x0e000000) {
if (opcode & 0x00000010) {
/* Emulate conversion opcodes. */
/* Emulate register transfer opcodes. */
/* Emulate comparison opcodes. */
return EmulateCPRT(opcode);
} else {
/* Emulate monadic arithmetic opcodes. */
/* Emulate dyadic arithmetic opcodes. */
return EmulateCPDO(opcode);
}
} else if (code == 0x0c000000) {
/* Emulate load/store opcodes. */
/* Emulate load/store multiple opcodes. */
return EmulateCPDT(opcode);
}
}
/* Invalid instruction detected. Return FALSE. */
return 0;
}

121
arch/arm/nwfpe/fpa11.h Normal file
View file

@ -0,0 +1,121 @@
/*
NetWinder Floating Point Emulator
(c) Rebel.com, 1998-1999
Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
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. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __FPA11_H__
#define __FPA11_H__
#define GET_FPA11() ((FPA11 *)(&current_thread_info()->fpstate))
/*
* The processes registers are always at the very top of the 8K
* stack+task struct. Use the same method as 'current' uses to
* reach them.
*/
#define GET_USERREG() ((struct pt_regs *)(THREAD_START_SP + (unsigned long)current_thread_info()) - 1)
#include <linux/thread_info.h>
/* includes */
#include "fpsr.h" /* FP control and status register definitions */
#include "milieu.h"
struct roundingData {
int8 mode;
int8 precision;
signed char exception;
};
#include "softfloat.h"
#define typeNone 0x00
#define typeSingle 0x01
#define typeDouble 0x02
#define typeExtended 0x03
/*
* This must be no more and no less than 12 bytes.
*/
typedef union tagFPREG {
float32 fSingle;
float64 fDouble;
#ifdef CONFIG_FPE_NWFPE_XP
floatx80 fExtended;
#else
u32 padding[3];
#endif
} __attribute__ ((packed,aligned(4))) FPREG;
/*
* FPA11 device model.
*
* This structure is exported to user space. Do not re-order.
* Only add new stuff to the end, and do not change the size of
* any element. Elements of this structure are used by user
* space, and must match struct user_fp in <asm/user.h>.
* We include the byte offsets below for documentation purposes.
*
* The size of this structure and FPREG are checked by fpmodule.c
* on initialisation. If the rules have been broken, NWFPE will
* not initialise.
*/
typedef struct tagFPA11 {
/* 0 */ FPREG fpreg[8]; /* 8 floating point registers */
/* 96 */ FPSR fpsr; /* floating point status register */
/* 100 */ FPCR fpcr; /* floating point control register */
/* 104 */ unsigned char fType[8]; /* type of floating point value held in
floating point registers. One of
none, single, double or extended. */
/* 112 */ int initflag; /* this is special. The kernel guarantees
to set it to 0 when a thread is launched,
so we can use it to detect whether this
instance of the emulator needs to be
initialised. */
} __attribute__ ((packed,aligned(4))) FPA11;
extern int8 SetRoundingMode(const unsigned int);
extern int8 SetRoundingPrecision(const unsigned int);
extern void nwfpe_init_fpa(union fp_state *fp);
extern unsigned int EmulateAll(unsigned int opcode);
extern unsigned int EmulateCPDT(const unsigned int opcode);
extern unsigned int EmulateCPDO(const unsigned int opcode);
extern unsigned int EmulateCPRT(const unsigned int opcode);
/* fpa11_cpdt.c */
extern unsigned int PerformLDF(const unsigned int opcode);
extern unsigned int PerformSTF(const unsigned int opcode);
extern unsigned int PerformLFM(const unsigned int opcode);
extern unsigned int PerformSFM(const unsigned int opcode);
/* single_cpdo.c */
extern unsigned int SingleCPDO(struct roundingData *roundData,
const unsigned int opcode, FPREG * rFd);
/* double_cpdo.c */
extern unsigned int DoubleCPDO(struct roundingData *roundData,
const unsigned int opcode, FPREG * rFd);
/* extneded_cpdo.c */
extern unsigned int ExtendedCPDO(struct roundingData *roundData,
const unsigned int opcode, FPREG * rFd);
#endif

51
arch/arm/nwfpe/fpa11.inl Normal file
View file

@ -0,0 +1,51 @@
/*
NetWinder Floating Point Emulator
(c) Rebel.COM, 1998,1999
Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
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. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "fpa11.h"
/* Read and write floating point status register */
static inline unsigned int readFPSR(void)
{
FPA11 *fpa11 = GET_FPA11();
return (fpa11->fpsr);
}
static inline void writeFPSR(FPSR reg)
{
FPA11 *fpa11 = GET_FPA11();
/* the sysid byte in the status register is readonly */
fpa11->fpsr = (fpa11->fpsr & MASK_SYSID) | (reg & ~MASK_SYSID);
}
/* Read and write floating point control register */
static inline FPCR readFPCR(void)
{
FPA11 *fpa11 = GET_FPA11();
/* clear SB, AB and DA bits before returning FPCR */
return (fpa11->fpcr & ~MASK_RFC);
}
static inline void writeFPCR(FPCR reg)
{
FPA11 *fpa11 = GET_FPA11();
fpa11->fpcr &= ~MASK_WFC; /* clear SB, AB and DA bits */
fpa11->fpcr |= (reg & MASK_WFC); /* write SB, AB and DA bits */
}

137
arch/arm/nwfpe/fpa11_cpdo.c Normal file
View file

@ -0,0 +1,137 @@
/*
NetWinder Floating Point Emulator
(c) Rebel.COM, 1998,1999
(c) Philip Blundell, 2001
Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
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. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "fpa11.h"
#include "fpopcode.h"
unsigned int SingleCPDO(struct roundingData *roundData, const unsigned int opcode, FPREG * rFd);
unsigned int DoubleCPDO(struct roundingData *roundData, const unsigned int opcode, FPREG * rFd);
unsigned int ExtendedCPDO(struct roundingData *roundData, const unsigned int opcode, FPREG * rFd);
unsigned int EmulateCPDO(const unsigned int opcode)
{
FPA11 *fpa11 = GET_FPA11();
FPREG *rFd;
unsigned int nType, nDest, nRc;
struct roundingData roundData;
/* Get the destination size. If not valid let Linux perform
an invalid instruction trap. */
nDest = getDestinationSize(opcode);
if (typeNone == nDest)
return 0;
roundData.mode = SetRoundingMode(opcode);
roundData.precision = SetRoundingPrecision(opcode);
roundData.exception = 0;
/* Compare the size of the operands in Fn and Fm.
Choose the largest size and perform operations in that size,
in order to make use of all the precision of the operands.
If Fm is a constant, we just grab a constant of a size
matching the size of the operand in Fn. */
if (MONADIC_INSTRUCTION(opcode))
nType = nDest;
else
nType = fpa11->fType[getFn(opcode)];
if (!CONSTANT_FM(opcode)) {
register unsigned int Fm = getFm(opcode);
if (nType < fpa11->fType[Fm]) {
nType = fpa11->fType[Fm];
}
}
rFd = &fpa11->fpreg[getFd(opcode)];
switch (nType) {
case typeSingle:
nRc = SingleCPDO(&roundData, opcode, rFd);
break;
case typeDouble:
nRc = DoubleCPDO(&roundData, opcode, rFd);
break;
#ifdef CONFIG_FPE_NWFPE_XP
case typeExtended:
nRc = ExtendedCPDO(&roundData, opcode, rFd);
break;
#endif
default:
nRc = 0;
}
/* The CPDO functions used to always set the destination type
to be the same as their working size. */
if (nRc != 0) {
/* If the operation succeeded, check to see if the result in the
destination register is the correct size. If not force it
to be. */
fpa11->fType[getFd(opcode)] = nDest;
#ifdef CONFIG_FPE_NWFPE_XP
if (nDest != nType) {
switch (nDest) {
case typeSingle:
{
if (typeDouble == nType)
rFd->fSingle = float64_to_float32(&roundData, rFd->fDouble);
else
rFd->fSingle = floatx80_to_float32(&roundData, rFd->fExtended);
}
break;
case typeDouble:
{
if (typeSingle == nType)
rFd->fDouble = float32_to_float64(rFd->fSingle);
else
rFd->fDouble = floatx80_to_float64(&roundData, rFd->fExtended);
}
break;
case typeExtended:
{
if (typeSingle == nType)
rFd->fExtended = float32_to_floatx80(rFd->fSingle);
else
rFd->fExtended = float64_to_floatx80(rFd->fDouble);
}
break;
}
}
#else
if (nDest != nType) {
if (nDest == typeSingle)
rFd->fSingle = float64_to_float32(&roundData, rFd->fDouble);
else
rFd->fDouble = float32_to_float64(rFd->fSingle);
}
#endif
}
if (roundData.exception)
float_raise(roundData.exception);
return nRc;
}

407
arch/arm/nwfpe/fpa11_cpdt.c Normal file
View file

@ -0,0 +1,407 @@
/*
NetWinder Floating Point Emulator
(c) Rebel.com, 1998-1999
(c) Philip Blundell, 1998, 2001
Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
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. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "fpa11.h"
#include "softfloat.h"
#include "fpopcode.h"
#include "fpmodule.h"
#include "fpmodule.inl"
#include <linux/uaccess.h>
static inline void loadSingle(const unsigned int Fn, const unsigned int __user *pMem)
{
FPA11 *fpa11 = GET_FPA11();
fpa11->fType[Fn] = typeSingle;
get_user(fpa11->fpreg[Fn].fSingle, pMem);
}
static inline void loadDouble(const unsigned int Fn, const unsigned int __user *pMem)
{
FPA11 *fpa11 = GET_FPA11();
unsigned int *p;
p = (unsigned int *) &fpa11->fpreg[Fn].fDouble;
fpa11->fType[Fn] = typeDouble;
#ifdef __ARMEB__
get_user(p[0], &pMem[0]); /* sign & exponent */
get_user(p[1], &pMem[1]);
#else
get_user(p[0], &pMem[1]);
get_user(p[1], &pMem[0]); /* sign & exponent */
#endif
}
#ifdef CONFIG_FPE_NWFPE_XP
static inline void loadExtended(const unsigned int Fn, const unsigned int __user *pMem)
{
FPA11 *fpa11 = GET_FPA11();
unsigned int *p;
p = (unsigned int *) &fpa11->fpreg[Fn].fExtended;
fpa11->fType[Fn] = typeExtended;
get_user(p[0], &pMem[0]); /* sign & exponent */
#ifdef __ARMEB__
get_user(p[1], &pMem[1]); /* ms bits */
get_user(p[2], &pMem[2]); /* ls bits */
#else
get_user(p[1], &pMem[2]); /* ls bits */
get_user(p[2], &pMem[1]); /* ms bits */
#endif
}
#endif
static inline void loadMultiple(const unsigned int Fn, const unsigned int __user *pMem)
{
FPA11 *fpa11 = GET_FPA11();
register unsigned int *p;
unsigned long x;
p = (unsigned int *) &(fpa11->fpreg[Fn]);
get_user(x, &pMem[0]);
fpa11->fType[Fn] = (x >> 14) & 0x00000003;
switch (fpa11->fType[Fn]) {
case typeSingle:
case typeDouble:
{
get_user(p[0], &pMem[2]); /* Single */
get_user(p[1], &pMem[1]); /* double msw */
p[2] = 0; /* empty */
}
break;
#ifdef CONFIG_FPE_NWFPE_XP
case typeExtended:
{
get_user(p[1], &pMem[2]);
get_user(p[2], &pMem[1]); /* msw */
p[0] = (x & 0x80003fff);
}
break;
#endif
}
}
static inline void storeSingle(struct roundingData *roundData, const unsigned int Fn, unsigned int __user *pMem)
{
FPA11 *fpa11 = GET_FPA11();
union {
float32 f;
unsigned int i[1];
} val;
switch (fpa11->fType[Fn]) {
case typeDouble:
val.f = float64_to_float32(roundData, fpa11->fpreg[Fn].fDouble);
break;
#ifdef CONFIG_FPE_NWFPE_XP
case typeExtended:
val.f = floatx80_to_float32(roundData, fpa11->fpreg[Fn].fExtended);
break;
#endif
default:
val.f = fpa11->fpreg[Fn].fSingle;
}
put_user(val.i[0], pMem);
}
static inline void storeDouble(struct roundingData *roundData, const unsigned int Fn, unsigned int __user *pMem)
{
FPA11 *fpa11 = GET_FPA11();
union {
float64 f;
unsigned int i[2];
} val;
switch (fpa11->fType[Fn]) {
case typeSingle:
val.f = float32_to_float64(fpa11->fpreg[Fn].fSingle);
break;
#ifdef CONFIG_FPE_NWFPE_XP
case typeExtended:
val.f = floatx80_to_float64(roundData, fpa11->fpreg[Fn].fExtended);
break;
#endif
default:
val.f = fpa11->fpreg[Fn].fDouble;
}
#ifdef __ARMEB__
put_user(val.i[0], &pMem[0]); /* msw */
put_user(val.i[1], &pMem[1]); /* lsw */
#else
put_user(val.i[1], &pMem[0]); /* msw */
put_user(val.i[0], &pMem[1]); /* lsw */
#endif
}
#ifdef CONFIG_FPE_NWFPE_XP
static inline void storeExtended(const unsigned int Fn, unsigned int __user *pMem)
{
FPA11 *fpa11 = GET_FPA11();
union {
floatx80 f;
unsigned int i[3];
} val;
switch (fpa11->fType[Fn]) {
case typeSingle:
val.f = float32_to_floatx80(fpa11->fpreg[Fn].fSingle);
break;
case typeDouble:
val.f = float64_to_floatx80(fpa11->fpreg[Fn].fDouble);
break;
default:
val.f = fpa11->fpreg[Fn].fExtended;
}
put_user(val.i[0], &pMem[0]); /* sign & exp */
#ifdef __ARMEB__
put_user(val.i[1], &pMem[1]); /* msw */
put_user(val.i[2], &pMem[2]);
#else
put_user(val.i[1], &pMem[2]);
put_user(val.i[2], &pMem[1]); /* msw */
#endif
}
#endif
static inline void storeMultiple(const unsigned int Fn, unsigned int __user *pMem)
{
FPA11 *fpa11 = GET_FPA11();
register unsigned int nType, *p;
p = (unsigned int *) &(fpa11->fpreg[Fn]);
nType = fpa11->fType[Fn];
switch (nType) {
case typeSingle:
case typeDouble:
{
put_user(p[0], &pMem[2]); /* single */
put_user(p[1], &pMem[1]); /* double msw */
put_user(nType << 14, &pMem[0]);
}
break;
#ifdef CONFIG_FPE_NWFPE_XP
case typeExtended:
{
put_user(p[2], &pMem[1]); /* msw */
put_user(p[1], &pMem[2]);
put_user((p[0] & 0x80003fff) | (nType << 14), &pMem[0]);
}
break;
#endif
}
}
unsigned int PerformLDF(const unsigned int opcode)
{
unsigned int __user *pBase, *pAddress, *pFinal;
unsigned int nRc = 1, write_back = WRITE_BACK(opcode);
pBase = (unsigned int __user *) readRegister(getRn(opcode));
if (REG_PC == getRn(opcode)) {
pBase += 2;
write_back = 0;
}
pFinal = pBase;
if (BIT_UP_SET(opcode))
pFinal += getOffset(opcode);
else
pFinal -= getOffset(opcode);
if (PREINDEXED(opcode))
pAddress = pFinal;
else
pAddress = pBase;
switch (opcode & MASK_TRANSFER_LENGTH) {
case TRANSFER_SINGLE:
loadSingle(getFd(opcode), pAddress);
break;
case TRANSFER_DOUBLE:
loadDouble(getFd(opcode), pAddress);
break;
#ifdef CONFIG_FPE_NWFPE_XP
case TRANSFER_EXTENDED:
loadExtended(getFd(opcode), pAddress);
break;
#endif
default:
nRc = 0;
}
if (write_back)
writeRegister(getRn(opcode), (unsigned long) pFinal);
return nRc;
}
unsigned int PerformSTF(const unsigned int opcode)
{
unsigned int __user *pBase, *pAddress, *pFinal;
unsigned int nRc = 1, write_back = WRITE_BACK(opcode);
struct roundingData roundData;
roundData.mode = SetRoundingMode(opcode);
roundData.precision = SetRoundingPrecision(opcode);
roundData.exception = 0;
pBase = (unsigned int __user *) readRegister(getRn(opcode));
if (REG_PC == getRn(opcode)) {
pBase += 2;
write_back = 0;
}
pFinal = pBase;
if (BIT_UP_SET(opcode))
pFinal += getOffset(opcode);
else
pFinal -= getOffset(opcode);
if (PREINDEXED(opcode))
pAddress = pFinal;
else
pAddress = pBase;
switch (opcode & MASK_TRANSFER_LENGTH) {
case TRANSFER_SINGLE:
storeSingle(&roundData, getFd(opcode), pAddress);
break;
case TRANSFER_DOUBLE:
storeDouble(&roundData, getFd(opcode), pAddress);
break;
#ifdef CONFIG_FPE_NWFPE_XP
case TRANSFER_EXTENDED:
storeExtended(getFd(opcode), pAddress);
break;
#endif
default:
nRc = 0;
}
if (roundData.exception)
float_raise(roundData.exception);
if (write_back)
writeRegister(getRn(opcode), (unsigned long) pFinal);
return nRc;
}
unsigned int PerformLFM(const unsigned int opcode)
{
unsigned int __user *pBase, *pAddress, *pFinal;
unsigned int i, Fd, write_back = WRITE_BACK(opcode);
pBase = (unsigned int __user *) readRegister(getRn(opcode));
if (REG_PC == getRn(opcode)) {
pBase += 2;
write_back = 0;
}
pFinal = pBase;
if (BIT_UP_SET(opcode))
pFinal += getOffset(opcode);
else
pFinal -= getOffset(opcode);
if (PREINDEXED(opcode))
pAddress = pFinal;
else
pAddress = pBase;
Fd = getFd(opcode);
for (i = getRegisterCount(opcode); i > 0; i--) {
loadMultiple(Fd, pAddress);
pAddress += 3;
Fd++;
if (Fd == 8)
Fd = 0;
}
if (write_back)
writeRegister(getRn(opcode), (unsigned long) pFinal);
return 1;
}
unsigned int PerformSFM(const unsigned int opcode)
{
unsigned int __user *pBase, *pAddress, *pFinal;
unsigned int i, Fd, write_back = WRITE_BACK(opcode);
pBase = (unsigned int __user *) readRegister(getRn(opcode));
if (REG_PC == getRn(opcode)) {
pBase += 2;
write_back = 0;
}
pFinal = pBase;
if (BIT_UP_SET(opcode))
pFinal += getOffset(opcode);
else
pFinal -= getOffset(opcode);
if (PREINDEXED(opcode))
pAddress = pFinal;
else
pAddress = pBase;
Fd = getFd(opcode);
for (i = getRegisterCount(opcode); i > 0; i--) {
storeMultiple(Fd, pAddress);
pAddress += 3;
Fd++;
if (Fd == 8)
Fd = 0;
}
if (write_back)
writeRegister(getRn(opcode), (unsigned long) pFinal);
return 1;
}
unsigned int EmulateCPDT(const unsigned int opcode)
{
unsigned int nRc = 0;
if (LDF_OP(opcode)) {
nRc = PerformLDF(opcode);
} else if (LFM_OP(opcode)) {
nRc = PerformLFM(opcode);
} else if (STF_OP(opcode)) {
nRc = PerformSTF(opcode);
} else if (SFM_OP(opcode)) {
nRc = PerformSFM(opcode);
} else {
nRc = 0;
}
return nRc;
}

373
arch/arm/nwfpe/fpa11_cprt.c Normal file
View file

@ -0,0 +1,373 @@
/*
NetWinder Floating Point Emulator
(c) Rebel.COM, 1998,1999
(c) Philip Blundell, 1999, 2001
Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
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. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "fpa11.h"
#include "fpopcode.h"
#include "fpa11.inl"
#include "fpmodule.h"
#include "fpmodule.inl"
#include "softfloat.h"
unsigned int PerformFLT(const unsigned int opcode);
unsigned int PerformFIX(const unsigned int opcode);
static unsigned int PerformComparison(const unsigned int opcode);
unsigned int EmulateCPRT(const unsigned int opcode)
{
if (opcode & 0x800000) {
/* This is some variant of a comparison (PerformComparison
will sort out which one). Since most of the other CPRT
instructions are oddball cases of some sort or other it
makes sense to pull this out into a fast path. */
return PerformComparison(opcode);
}
/* Hint to GCC that we'd like a jump table rather than a load of CMPs */
switch ((opcode & 0x700000) >> 20) {
case FLT_CODE >> 20:
return PerformFLT(opcode);
break;
case FIX_CODE >> 20:
return PerformFIX(opcode);
break;
case WFS_CODE >> 20:
writeFPSR(readRegister(getRd(opcode)));
break;
case RFS_CODE >> 20:
writeRegister(getRd(opcode), readFPSR());
break;
default:
return 0;
}
return 1;
}
unsigned int PerformFLT(const unsigned int opcode)
{
FPA11 *fpa11 = GET_FPA11();
struct roundingData roundData;
roundData.mode = SetRoundingMode(opcode);
roundData.precision = SetRoundingPrecision(opcode);
roundData.exception = 0;
switch (opcode & MASK_ROUNDING_PRECISION) {
case ROUND_SINGLE:
{
fpa11->fType[getFn(opcode)] = typeSingle;
fpa11->fpreg[getFn(opcode)].fSingle = int32_to_float32(&roundData, readRegister(getRd(opcode)));
}
break;
case ROUND_DOUBLE:
{
fpa11->fType[getFn(opcode)] = typeDouble;
fpa11->fpreg[getFn(opcode)].fDouble = int32_to_float64(readRegister(getRd(opcode)));
}
break;
#ifdef CONFIG_FPE_NWFPE_XP
case ROUND_EXTENDED:
{
fpa11->fType[getFn(opcode)] = typeExtended;
fpa11->fpreg[getFn(opcode)].fExtended = int32_to_floatx80(readRegister(getRd(opcode)));
}
break;
#endif
default:
return 0;
}
if (roundData.exception)
float_raise(roundData.exception);
return 1;
}
unsigned int PerformFIX(const unsigned int opcode)
{
FPA11 *fpa11 = GET_FPA11();
unsigned int Fn = getFm(opcode);
struct roundingData roundData;
roundData.mode = SetRoundingMode(opcode);
roundData.precision = SetRoundingPrecision(opcode);
roundData.exception = 0;
switch (fpa11->fType[Fn]) {
case typeSingle:
{
writeRegister(getRd(opcode), float32_to_int32(&roundData, fpa11->fpreg[Fn].fSingle));
}
break;
case typeDouble:
{
writeRegister(getRd(opcode), float64_to_int32(&roundData, fpa11->fpreg[Fn].fDouble));
}
break;
#ifdef CONFIG_FPE_NWFPE_XP
case typeExtended:
{
writeRegister(getRd(opcode), floatx80_to_int32(&roundData, fpa11->fpreg[Fn].fExtended));
}
break;
#endif
default:
return 0;
}
if (roundData.exception)
float_raise(roundData.exception);
return 1;
}
/* This instruction sets the flags N, Z, C, V in the FPSR. */
static unsigned int PerformComparison(const unsigned int opcode)
{
FPA11 *fpa11 = GET_FPA11();
unsigned int Fn = getFn(opcode), Fm = getFm(opcode);
int e_flag = opcode & 0x400000; /* 1 if CxFE */
int n_flag = opcode & 0x200000; /* 1 if CNxx */
unsigned int flags = 0;
#ifdef CONFIG_FPE_NWFPE_XP
floatx80 rFn, rFm;
/* Check for unordered condition and convert all operands to 80-bit
format.
?? Might be some mileage in avoiding this conversion if possible.
Eg, if both operands are 32-bit, detect this and do a 32-bit
comparison (cheaper than an 80-bit one). */
switch (fpa11->fType[Fn]) {
case typeSingle:
//printk("single.\n");
if (float32_is_nan(fpa11->fpreg[Fn].fSingle))
goto unordered;
rFn = float32_to_floatx80(fpa11->fpreg[Fn].fSingle);
break;
case typeDouble:
//printk("double.\n");
if (float64_is_nan(fpa11->fpreg[Fn].fDouble))
goto unordered;
rFn = float64_to_floatx80(fpa11->fpreg[Fn].fDouble);
break;
case typeExtended:
//printk("extended.\n");
if (floatx80_is_nan(fpa11->fpreg[Fn].fExtended))
goto unordered;
rFn = fpa11->fpreg[Fn].fExtended;
break;
default:
return 0;
}
if (CONSTANT_FM(opcode)) {
//printk("Fm is a constant: #%d.\n",Fm);
rFm = getExtendedConstant(Fm);
if (floatx80_is_nan(rFm))
goto unordered;
} else {
//printk("Fm = r%d which contains a ",Fm);
switch (fpa11->fType[Fm]) {
case typeSingle:
//printk("single.\n");
if (float32_is_nan(fpa11->fpreg[Fm].fSingle))
goto unordered;
rFm = float32_to_floatx80(fpa11->fpreg[Fm].fSingle);
break;
case typeDouble:
//printk("double.\n");
if (float64_is_nan(fpa11->fpreg[Fm].fDouble))
goto unordered;
rFm = float64_to_floatx80(fpa11->fpreg[Fm].fDouble);
break;
case typeExtended:
//printk("extended.\n");
if (floatx80_is_nan(fpa11->fpreg[Fm].fExtended))
goto unordered;
rFm = fpa11->fpreg[Fm].fExtended;
break;
default:
return 0;
}
}
if (n_flag)
rFm.high ^= 0x8000;
/* test for less than condition */
if (floatx80_lt(rFn, rFm))
flags |= CC_NEGATIVE;
/* test for equal condition */
if (floatx80_eq(rFn, rFm))
flags |= CC_ZERO;
/* test for greater than or equal condition */
if (floatx80_lt(rFm, rFn))
flags |= CC_CARRY;
#else
if (CONSTANT_FM(opcode)) {
/* Fm is a constant. Do the comparison in whatever precision
Fn happens to be stored in. */
if (fpa11->fType[Fn] == typeSingle) {
float32 rFm = getSingleConstant(Fm);
float32 rFn = fpa11->fpreg[Fn].fSingle;
if (float32_is_nan(rFn))
goto unordered;
if (n_flag)
rFm ^= 0x80000000;
/* test for less than condition */
if (float32_lt_nocheck(rFn, rFm))
flags |= CC_NEGATIVE;
/* test for equal condition */
if (float32_eq_nocheck(rFn, rFm))
flags |= CC_ZERO;
/* test for greater than or equal condition */
if (float32_lt_nocheck(rFm, rFn))
flags |= CC_CARRY;
} else {
float64 rFm = getDoubleConstant(Fm);
float64 rFn = fpa11->fpreg[Fn].fDouble;
if (float64_is_nan(rFn))
goto unordered;
if (n_flag)
rFm ^= 0x8000000000000000ULL;
/* test for less than condition */
if (float64_lt_nocheck(rFn, rFm))
flags |= CC_NEGATIVE;
/* test for equal condition */
if (float64_eq_nocheck(rFn, rFm))
flags |= CC_ZERO;
/* test for greater than or equal condition */
if (float64_lt_nocheck(rFm, rFn))
flags |= CC_CARRY;
}
} else {
/* Both operands are in registers. */
if (fpa11->fType[Fn] == typeSingle
&& fpa11->fType[Fm] == typeSingle) {
float32 rFm = fpa11->fpreg[Fm].fSingle;
float32 rFn = fpa11->fpreg[Fn].fSingle;
if (float32_is_nan(rFn)
|| float32_is_nan(rFm))
goto unordered;
if (n_flag)
rFm ^= 0x80000000;
/* test for less than condition */
if (float32_lt_nocheck(rFn, rFm))
flags |= CC_NEGATIVE;
/* test for equal condition */
if (float32_eq_nocheck(rFn, rFm))
flags |= CC_ZERO;
/* test for greater than or equal condition */
if (float32_lt_nocheck(rFm, rFn))
flags |= CC_CARRY;
} else {
/* Promote 32-bit operand to 64 bits. */
float64 rFm, rFn;
rFm = (fpa11->fType[Fm] == typeSingle) ?
float32_to_float64(fpa11->fpreg[Fm].fSingle)
: fpa11->fpreg[Fm].fDouble;
rFn = (fpa11->fType[Fn] == typeSingle) ?
float32_to_float64(fpa11->fpreg[Fn].fSingle)
: fpa11->fpreg[Fn].fDouble;
if (float64_is_nan(rFn)
|| float64_is_nan(rFm))
goto unordered;
if (n_flag)
rFm ^= 0x8000000000000000ULL;
/* test for less than condition */
if (float64_lt_nocheck(rFn, rFm))
flags |= CC_NEGATIVE;
/* test for equal condition */
if (float64_eq_nocheck(rFn, rFm))
flags |= CC_ZERO;
/* test for greater than or equal condition */
if (float64_lt_nocheck(rFm, rFn))
flags |= CC_CARRY;
}
}
#endif
writeConditionCodes(flags);
return 1;
unordered:
/* ?? The FPA data sheet is pretty vague about this, in particular
about whether the non-E comparisons can ever raise exceptions.
This implementation is based on a combination of what it says in
the data sheet, observation of how the Acorn emulator actually
behaves (and how programs expect it to) and guesswork. */
flags |= CC_OVERFLOW;
flags &= ~(CC_ZERO | CC_NEGATIVE);
if (BIT_AC & readFPSR())
flags |= CC_CARRY;
if (e_flag)
float_raise(float_flag_invalid);
writeConditionCodes(flags);
return 1;
}

190
arch/arm/nwfpe/fpmodule.c Normal file
View file

@ -0,0 +1,190 @@
/*
NetWinder Floating Point Emulator
(c) Rebel.com, 1998-1999
(c) Philip Blundell, 1998-1999
Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
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. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "fpa11.h"
#include <linux/module.h>
#include <linux/moduleparam.h>
/* XXX */
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <asm/thread_notify.h>
#include "softfloat.h"
#include "fpopcode.h"
#include "fpmodule.h"
#include "fpa11.inl"
/* kernel symbols required for signal handling */
#ifdef CONFIG_FPE_NWFPE_XP
#define NWFPE_BITS "extended"
#else
#define NWFPE_BITS "double"
#endif
#ifdef MODULE
void fp_send_sig(unsigned long sig, struct task_struct *p, int priv);
#else
#define fp_send_sig send_sig
#define kern_fp_enter fp_enter
extern char fpe_type[];
#endif
static int nwfpe_notify(struct notifier_block *self, unsigned long cmd, void *v)
{
struct thread_info *thread = v;
if (cmd == THREAD_NOTIFY_FLUSH)
nwfpe_init_fpa(&thread->fpstate);
return NOTIFY_DONE;
}
static struct notifier_block nwfpe_notifier_block = {
.notifier_call = nwfpe_notify,
};
/* kernel function prototypes required */
void fp_setup(void);
/* external declarations for saved kernel symbols */
extern void (*kern_fp_enter)(void);
/* Original value of fp_enter from kernel before patched by fpe_init. */
static void (*orig_fp_enter)(void);
/* forward declarations */
extern void nwfpe_enter(void);
static int __init fpe_init(void)
{
if (sizeof(FPA11) > sizeof(union fp_state)) {
printk(KERN_ERR "nwfpe: bad structure size\n");
return -EINVAL;
}
if (sizeof(FPREG) != 12) {
printk(KERN_ERR "nwfpe: bad register size\n");
return -EINVAL;
}
if (fpe_type[0] && strcmp(fpe_type, "nwfpe"))
return 0;
/* Display title, version and copyright information. */
printk(KERN_WARNING "NetWinder Floating Point Emulator V0.97 ("
NWFPE_BITS " precision)\n");
thread_register_notifier(&nwfpe_notifier_block);
/* Save pointer to the old FP handler and then patch ourselves in */
orig_fp_enter = kern_fp_enter;
kern_fp_enter = nwfpe_enter;
return 0;
}
static void __exit fpe_exit(void)
{
thread_unregister_notifier(&nwfpe_notifier_block);
/* Restore the values we saved earlier. */
kern_fp_enter = orig_fp_enter;
}
/*
ScottB: November 4, 1998
Moved this function out of softfloat-specialize into fpmodule.c.
This effectively isolates all the changes required for integrating with the
Linux kernel into fpmodule.c. Porting to NetBSD should only require modifying
fpmodule.c to integrate with the NetBSD kernel (I hope!).
[1/1/99: Not quite true any more unfortunately. There is Linux-specific
code to access data in user space in some other source files at the
moment (grep for get_user / put_user calls). --philb]
This function is called by the SoftFloat routines to raise a floating
point exception. We check the trap enable byte in the FPSR, and raise
a SIGFPE exception if necessary. If not the relevant bits in the
cumulative exceptions flag byte are set and we return.
*/
#ifdef CONFIG_DEBUG_USER
/* By default, ignore inexact errors as there are far too many of them to log */
static int debug = ~BIT_IXC;
#endif
void float_raise(signed char flags)
{
register unsigned int fpsr, cumulativeTraps;
#ifdef CONFIG_DEBUG_USER
if (flags & debug)
printk(KERN_DEBUG
"NWFPE: %s[%d] takes exception %08x at %pf from %08lx\n",
current->comm, current->pid, flags,
__builtin_return_address(0), GET_USERREG()->ARM_pc);
#endif
/* Read fpsr and initialize the cumulativeTraps. */
fpsr = readFPSR();
cumulativeTraps = 0;
/* For each type of exception, the cumulative trap exception bit is only
set if the corresponding trap enable bit is not set. */
if ((!(fpsr & BIT_IXE)) && (flags & BIT_IXC))
cumulativeTraps |= BIT_IXC;
if ((!(fpsr & BIT_UFE)) && (flags & BIT_UFC))
cumulativeTraps |= BIT_UFC;
if ((!(fpsr & BIT_OFE)) && (flags & BIT_OFC))
cumulativeTraps |= BIT_OFC;
if ((!(fpsr & BIT_DZE)) && (flags & BIT_DZC))
cumulativeTraps |= BIT_DZC;
if ((!(fpsr & BIT_IOE)) && (flags & BIT_IOC))
cumulativeTraps |= BIT_IOC;
/* Set the cumulative exceptions flags. */
if (cumulativeTraps)
writeFPSR(fpsr | cumulativeTraps);
/* Raise an exception if necessary. */
if (fpsr & (flags << 16))
fp_send_sig(SIGFPE, current, 1);
}
module_init(fpe_init);
module_exit(fpe_exit);
MODULE_AUTHOR("Scott Bambrough <scottb@rebel.com>");
MODULE_DESCRIPTION("NWFPE floating point emulator (" NWFPE_BITS " precision)");
MODULE_LICENSE("GPL");
#ifdef CONFIG_DEBUG_USER
module_param(debug, int, 0644);
#endif

47
arch/arm/nwfpe/fpmodule.h Normal file
View file

@ -0,0 +1,47 @@
/*
NetWinder Floating Point Emulator
(c) Rebel.com, 1998-1999
Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
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. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27/03/03 Ian Molton Clean up CONFIG_CPU
*/
#ifndef __FPMODULE_H__
#define __FPMODULE_H__
#define REG_ORIG_R0 17
#define REG_CPSR 16
#define REG_PC 15
#define REG_LR 14
#define REG_SP 13
#define REG_IP 12
#define REG_FP 11
#define REG_R10 10
#define REG_R9 9
#define REG_R9 9
#define REG_R8 8
#define REG_R7 7
#define REG_R6 6
#define REG_R5 5
#define REG_R4 4
#define REG_R3 3
#define REG_R2 2
#define REG_R1 1
#define REG_R0 0
#endif

View file

@ -0,0 +1,74 @@
/*
NetWinder Floating Point Emulator
(c) Rebel.COM, 1998,1999
Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
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. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
static inline unsigned long readRegister(const unsigned int nReg)
{
/* Note: The CPU thinks it has dealt with the current instruction.
As a result the program counter has been advanced to the next
instruction, and points 4 bytes beyond the actual instruction
that caused the invalid instruction trap to occur. We adjust
for this in this routine. LDF/STF instructions with Rn = PC
depend on the PC being correct, as they use PC+8 in their
address calculations. */
struct pt_regs *regs = GET_USERREG();
unsigned int val = regs->uregs[nReg];
if (REG_PC == nReg)
val -= 4;
return val;
}
static inline void
writeRegister(const unsigned int nReg, const unsigned long val)
{
struct pt_regs *regs = GET_USERREG();
regs->uregs[nReg] = val;
}
static inline unsigned long readCPSR(void)
{
return (readRegister(REG_CPSR));
}
static inline void writeCPSR(const unsigned long val)
{
writeRegister(REG_CPSR, val);
}
static inline unsigned long readConditionCodes(void)
{
#ifdef __FPEM_TEST__
return (0);
#else
return (readCPSR() & CC_MASK);
#endif
}
static inline void writeConditionCodes(const unsigned long val)
{
struct pt_regs *regs = GET_USERREG();
unsigned long rval;
/*
* Operate directly on userRegisters since
* the CPSR may be the PC register itself.
*/
rval = regs->ARM_cpsr & ~CC_MASK;
regs->ARM_cpsr = rval | (val & CC_MASK);
}

63
arch/arm/nwfpe/fpopcode.c Normal file
View file

@ -0,0 +1,63 @@
/*
NetWinder Floating Point Emulator
(c) Rebel.COM, 1998,1999
Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
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. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "fpa11.h"
#include "softfloat.h"
#include "fpopcode.h"
#include "fpsr.h"
#include "fpmodule.h"
#include "fpmodule.inl"
#ifdef CONFIG_FPE_NWFPE_XP
const floatx80 floatx80Constant[] = {
{ .high = 0x0000, .low = 0x0000000000000000ULL},/* extended 0.0 */
{ .high = 0x3fff, .low = 0x8000000000000000ULL},/* extended 1.0 */
{ .high = 0x4000, .low = 0x8000000000000000ULL},/* extended 2.0 */
{ .high = 0x4000, .low = 0xc000000000000000ULL},/* extended 3.0 */
{ .high = 0x4001, .low = 0x8000000000000000ULL},/* extended 4.0 */
{ .high = 0x4001, .low = 0xa000000000000000ULL},/* extended 5.0 */
{ .high = 0x3ffe, .low = 0x8000000000000000ULL},/* extended 0.5 */
{ .high = 0x4002, .low = 0xa000000000000000ULL},/* extended 10.0 */
};
#endif
const float64 float64Constant[] = {
0x0000000000000000ULL, /* double 0.0 */
0x3ff0000000000000ULL, /* double 1.0 */
0x4000000000000000ULL, /* double 2.0 */
0x4008000000000000ULL, /* double 3.0 */
0x4010000000000000ULL, /* double 4.0 */
0x4014000000000000ULL, /* double 5.0 */
0x3fe0000000000000ULL, /* double 0.5 */
0x4024000000000000ULL /* double 10.0 */
};
const float32 float32Constant[] = {
0x00000000, /* single 0.0 */
0x3f800000, /* single 1.0 */
0x40000000, /* single 2.0 */
0x40400000, /* single 3.0 */
0x40800000, /* single 4.0 */
0x40a00000, /* single 5.0 */
0x3f000000, /* single 0.5 */
0x41200000 /* single 10.0 */
};

481
arch/arm/nwfpe/fpopcode.h Normal file
View file

@ -0,0 +1,481 @@
/*
NetWinder Floating Point Emulator
(c) Rebel.COM, 1998,1999
(c) Philip Blundell, 2001
Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
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. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __FPOPCODE_H__
#define __FPOPCODE_H__
/*
ARM Floating Point Instruction Classes
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
|c o n d|1 1 0 P|U|u|W|L| Rn |v| Fd |0|0|0|1| o f f s e t | CPDT
|c o n d|1 1 0 P|U|w|W|L| Rn |x| Fd |0|0|1|0| o f f s e t | CPDT (copro 2)
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
|c o n d|1 1 1 0|a|b|c|d|e| Fn |j| Fd |0|0|0|1|f|g|h|0|i| Fm | CPDO
|c o n d|1 1 1 0|a|b|c|L|e| Fn | Rd |0|0|0|1|f|g|h|1|i| Fm | CPRT
|c o n d|1 1 1 0|a|b|c|1|e| Fn |1|1|1|1|0|0|0|1|f|g|h|1|i| Fm | comparisons
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
CPDT data transfer instructions
LDF, STF, LFM (copro 2), SFM (copro 2)
CPDO dyadic arithmetic instructions
ADF, MUF, SUF, RSF, DVF, RDF,
POW, RPW, RMF, FML, FDV, FRD, POL
CPDO monadic arithmetic instructions
MVF, MNF, ABS, RND, SQT, LOG, LGN, EXP,
SIN, COS, TAN, ASN, ACS, ATN, URD, NRM
CPRT joint arithmetic/data transfer instructions
FIX (arithmetic followed by load/store)
FLT (load/store followed by arithmetic)
CMF, CNF CMFE, CNFE (comparisons)
WFS, RFS (write/read floating point status register)
WFC, RFC (write/read floating point control register)
cond condition codes
P pre/post index bit: 0 = postindex, 1 = preindex
U up/down bit: 0 = stack grows down, 1 = stack grows up
W write back bit: 1 = update base register (Rn)
L load/store bit: 0 = store, 1 = load
Rn base register
Rd destination/source register
Fd floating point destination register
Fn floating point source register
Fm floating point source register or floating point constant
uv transfer length (TABLE 1)
wx register count (TABLE 2)
abcd arithmetic opcode (TABLES 3 & 4)
ef destination size (rounding precision) (TABLE 5)
gh rounding mode (TABLE 6)
j dyadic/monadic bit: 0 = dyadic, 1 = monadic
i constant bit: 1 = constant (TABLE 6)
*/
/*
TABLE 1
+-------------------------+---+---+---------+---------+
| Precision | u | v | FPSR.EP | length |
+-------------------------+---+---+---------+---------+
| Single | 0 | 0 | x | 1 words |
| Double | 1 | 1 | x | 2 words |
| Extended | 1 | 1 | x | 3 words |
| Packed decimal | 1 | 1 | 0 | 3 words |
| Expanded packed decimal | 1 | 1 | 1 | 4 words |
+-------------------------+---+---+---------+---------+
Note: x = don't care
*/
/*
TABLE 2
+---+---+---------------------------------+
| w | x | Number of registers to transfer |
+---+---+---------------------------------+
| 0 | 1 | 1 |
| 1 | 0 | 2 |
| 1 | 1 | 3 |
| 0 | 0 | 4 |
+---+---+---------------------------------+
*/
/*
TABLE 3: Dyadic Floating Point Opcodes
+---+---+---+---+----------+-----------------------+-----------------------+
| a | b | c | d | Mnemonic | Description | Operation |
+---+---+---+---+----------+-----------------------+-----------------------+
| 0 | 0 | 0 | 0 | ADF | Add | Fd := Fn + Fm |
| 0 | 0 | 0 | 1 | MUF | Multiply | Fd := Fn * Fm |
| 0 | 0 | 1 | 0 | SUF | Subtract | Fd := Fn - Fm |
| 0 | 0 | 1 | 1 | RSF | Reverse subtract | Fd := Fm - Fn |
| 0 | 1 | 0 | 0 | DVF | Divide | Fd := Fn / Fm |
| 0 | 1 | 0 | 1 | RDF | Reverse divide | Fd := Fm / Fn |
| 0 | 1 | 1 | 0 | POW | Power | Fd := Fn ^ Fm |
| 0 | 1 | 1 | 1 | RPW | Reverse power | Fd := Fm ^ Fn |
| 1 | 0 | 0 | 0 | RMF | Remainder | Fd := IEEE rem(Fn/Fm) |
| 1 | 0 | 0 | 1 | FML | Fast Multiply | Fd := Fn * Fm |
| 1 | 0 | 1 | 0 | FDV | Fast Divide | Fd := Fn / Fm |
| 1 | 0 | 1 | 1 | FRD | Fast reverse divide | Fd := Fm / Fn |
| 1 | 1 | 0 | 0 | POL | Polar angle (ArcTan2) | Fd := arctan2(Fn,Fm) |
| 1 | 1 | 0 | 1 | | undefined instruction | trap |
| 1 | 1 | 1 | 0 | | undefined instruction | trap |
| 1 | 1 | 1 | 1 | | undefined instruction | trap |
+---+---+---+---+----------+-----------------------+-----------------------+
Note: POW, RPW, POL are deprecated, and are available for backwards
compatibility only.
*/
/*
TABLE 4: Monadic Floating Point Opcodes
+---+---+---+---+----------+-----------------------+-----------------------+
| a | b | c | d | Mnemonic | Description | Operation |
+---+---+---+---+----------+-----------------------+-----------------------+
| 0 | 0 | 0 | 0 | MVF | Move | Fd := Fm |
| 0 | 0 | 0 | 1 | MNF | Move negated | Fd := - Fm |
| 0 | 0 | 1 | 0 | ABS | Absolute value | Fd := abs(Fm) |
| 0 | 0 | 1 | 1 | RND | Round to integer | Fd := int(Fm) |
| 0 | 1 | 0 | 0 | SQT | Square root | Fd := sqrt(Fm) |
| 0 | 1 | 0 | 1 | LOG | Log base 10 | Fd := log10(Fm) |
| 0 | 1 | 1 | 0 | LGN | Log base e | Fd := ln(Fm) |
| 0 | 1 | 1 | 1 | EXP | Exponent | Fd := e ^ Fm |
| 1 | 0 | 0 | 0 | SIN | Sine | Fd := sin(Fm) |
| 1 | 0 | 0 | 1 | COS | Cosine | Fd := cos(Fm) |
| 1 | 0 | 1 | 0 | TAN | Tangent | Fd := tan(Fm) |
| 1 | 0 | 1 | 1 | ASN | Arc Sine | Fd := arcsin(Fm) |
| 1 | 1 | 0 | 0 | ACS | Arc Cosine | Fd := arccos(Fm) |
| 1 | 1 | 0 | 1 | ATN | Arc Tangent | Fd := arctan(Fm) |
| 1 | 1 | 1 | 0 | URD | Unnormalized round | Fd := int(Fm) |
| 1 | 1 | 1 | 1 | NRM | Normalize | Fd := norm(Fm) |
+---+---+---+---+----------+-----------------------+-----------------------+
Note: LOG, LGN, EXP, SIN, COS, TAN, ASN, ACS, ATN are deprecated, and are
available for backwards compatibility only.
*/
/*
TABLE 5
+-------------------------+---+---+
| Rounding Precision | e | f |
+-------------------------+---+---+
| IEEE Single precision | 0 | 0 |
| IEEE Double precision | 0 | 1 |
| IEEE Extended precision | 1 | 0 |
| undefined (trap) | 1 | 1 |
+-------------------------+---+---+
*/
/*
TABLE 5
+---------------------------------+---+---+
| Rounding Mode | g | h |
+---------------------------------+---+---+
| Round to nearest (default) | 0 | 0 |
| Round toward plus infinity | 0 | 1 |
| Round toward negative infinity | 1 | 0 |
| Round toward zero | 1 | 1 |
+---------------------------------+---+---+
*/
/*
===
=== Definitions for load and store instructions
===
*/
/* bit masks */
#define BIT_PREINDEX 0x01000000
#define BIT_UP 0x00800000
#define BIT_WRITE_BACK 0x00200000
#define BIT_LOAD 0x00100000
/* masks for load/store */
#define MASK_CPDT 0x0c000000 /* data processing opcode */
#define MASK_OFFSET 0x000000ff
#define MASK_TRANSFER_LENGTH 0x00408000
#define MASK_REGISTER_COUNT MASK_TRANSFER_LENGTH
#define MASK_COPROCESSOR 0x00000f00
/* Tests for transfer length */
#define TRANSFER_SINGLE 0x00000000
#define TRANSFER_DOUBLE 0x00008000
#define TRANSFER_EXTENDED 0x00400000
#define TRANSFER_PACKED MASK_TRANSFER_LENGTH
/* Get the coprocessor number from the opcode. */
#define getCoprocessorNumber(opcode) ((opcode & MASK_COPROCESSOR) >> 8)
/* Get the offset from the opcode. */
#define getOffset(opcode) (opcode & MASK_OFFSET)
/* Tests for specific data transfer load/store opcodes. */
#define TEST_OPCODE(opcode,mask) (((opcode) & (mask)) == (mask))
#define LOAD_OP(opcode) TEST_OPCODE((opcode),MASK_CPDT | BIT_LOAD)
#define STORE_OP(opcode) ((opcode & (MASK_CPDT | BIT_LOAD)) == MASK_CPDT)
#define LDF_OP(opcode) (LOAD_OP(opcode) && (getCoprocessorNumber(opcode) == 1))
#define LFM_OP(opcode) (LOAD_OP(opcode) && (getCoprocessorNumber(opcode) == 2))
#define STF_OP(opcode) (STORE_OP(opcode) && (getCoprocessorNumber(opcode) == 1))
#define SFM_OP(opcode) (STORE_OP(opcode) && (getCoprocessorNumber(opcode) == 2))
#define PREINDEXED(opcode) ((opcode & BIT_PREINDEX) != 0)
#define POSTINDEXED(opcode) ((opcode & BIT_PREINDEX) == 0)
#define BIT_UP_SET(opcode) ((opcode & BIT_UP) != 0)
#define BIT_UP_CLEAR(opcode) ((opcode & BIT_DOWN) == 0)
#define WRITE_BACK(opcode) ((opcode & BIT_WRITE_BACK) != 0)
#define LOAD(opcode) ((opcode & BIT_LOAD) != 0)
#define STORE(opcode) ((opcode & BIT_LOAD) == 0)
/*
===
=== Definitions for arithmetic instructions
===
*/
/* bit masks */
#define BIT_MONADIC 0x00008000
#define BIT_CONSTANT 0x00000008
#define CONSTANT_FM(opcode) ((opcode & BIT_CONSTANT) != 0)
#define MONADIC_INSTRUCTION(opcode) ((opcode & BIT_MONADIC) != 0)
/* instruction identification masks */
#define MASK_CPDO 0x0e000000 /* arithmetic opcode */
#define MASK_ARITHMETIC_OPCODE 0x00f08000
#define MASK_DESTINATION_SIZE 0x00080080
/* dyadic arithmetic opcodes. */
#define ADF_CODE 0x00000000
#define MUF_CODE 0x00100000
#define SUF_CODE 0x00200000
#define RSF_CODE 0x00300000
#define DVF_CODE 0x00400000
#define RDF_CODE 0x00500000
#define POW_CODE 0x00600000
#define RPW_CODE 0x00700000
#define RMF_CODE 0x00800000
#define FML_CODE 0x00900000
#define FDV_CODE 0x00a00000
#define FRD_CODE 0x00b00000
#define POL_CODE 0x00c00000
/* 0x00d00000 is an invalid dyadic arithmetic opcode */
/* 0x00e00000 is an invalid dyadic arithmetic opcode */
/* 0x00f00000 is an invalid dyadic arithmetic opcode */
/* monadic arithmetic opcodes. */
#define MVF_CODE 0x00008000
#define MNF_CODE 0x00108000
#define ABS_CODE 0x00208000
#define RND_CODE 0x00308000
#define SQT_CODE 0x00408000
#define LOG_CODE 0x00508000
#define LGN_CODE 0x00608000
#define EXP_CODE 0x00708000
#define SIN_CODE 0x00808000
#define COS_CODE 0x00908000
#define TAN_CODE 0x00a08000
#define ASN_CODE 0x00b08000
#define ACS_CODE 0x00c08000
#define ATN_CODE 0x00d08000
#define URD_CODE 0x00e08000
#define NRM_CODE 0x00f08000
/*
===
=== Definitions for register transfer and comparison instructions
===
*/
#define MASK_CPRT 0x0e000010 /* register transfer opcode */
#define MASK_CPRT_CODE 0x00f00000
#define FLT_CODE 0x00000000
#define FIX_CODE 0x00100000
#define WFS_CODE 0x00200000
#define RFS_CODE 0x00300000
#define WFC_CODE 0x00400000
#define RFC_CODE 0x00500000
#define CMF_CODE 0x00900000
#define CNF_CODE 0x00b00000
#define CMFE_CODE 0x00d00000
#define CNFE_CODE 0x00f00000
/*
===
=== Common definitions
===
*/
/* register masks */
#define MASK_Rd 0x0000f000
#define MASK_Rn 0x000f0000
#define MASK_Fd 0x00007000
#define MASK_Fm 0x00000007
#define MASK_Fn 0x00070000
/* condition code masks */
#define CC_MASK 0xf0000000
#define CC_NEGATIVE 0x80000000
#define CC_ZERO 0x40000000
#define CC_CARRY 0x20000000
#define CC_OVERFLOW 0x10000000
#define CC_EQ 0x00000000
#define CC_NE 0x10000000
#define CC_CS 0x20000000
#define CC_HS CC_CS
#define CC_CC 0x30000000
#define CC_LO CC_CC
#define CC_MI 0x40000000
#define CC_PL 0x50000000
#define CC_VS 0x60000000
#define CC_VC 0x70000000
#define CC_HI 0x80000000
#define CC_LS 0x90000000
#define CC_GE 0xa0000000
#define CC_LT 0xb0000000
#define CC_GT 0xc0000000
#define CC_LE 0xd0000000
#define CC_AL 0xe0000000
#define CC_NV 0xf0000000
/* rounding masks/values */
#define MASK_ROUNDING_MODE 0x00000060
#define ROUND_TO_NEAREST 0x00000000
#define ROUND_TO_PLUS_INFINITY 0x00000020
#define ROUND_TO_MINUS_INFINITY 0x00000040
#define ROUND_TO_ZERO 0x00000060
#define MASK_ROUNDING_PRECISION 0x00080080
#define ROUND_SINGLE 0x00000000
#define ROUND_DOUBLE 0x00000080
#define ROUND_EXTENDED 0x00080000
/* Get the condition code from the opcode. */
#define getCondition(opcode) (opcode >> 28)
/* Get the source register from the opcode. */
#define getRn(opcode) ((opcode & MASK_Rn) >> 16)
/* Get the destination floating point register from the opcode. */
#define getFd(opcode) ((opcode & MASK_Fd) >> 12)
/* Get the first source floating point register from the opcode. */
#define getFn(opcode) ((opcode & MASK_Fn) >> 16)
/* Get the second source floating point register from the opcode. */
#define getFm(opcode) (opcode & MASK_Fm)
/* Get the destination register from the opcode. */
#define getRd(opcode) ((opcode & MASK_Rd) >> 12)
/* Get the rounding mode from the opcode. */
#define getRoundingMode(opcode) ((opcode & MASK_ROUNDING_MODE) >> 5)
#ifdef CONFIG_FPE_NWFPE_XP
static inline floatx80 __pure getExtendedConstant(const unsigned int nIndex)
{
extern const floatx80 floatx80Constant[];
return floatx80Constant[nIndex];
}
#endif
static inline float64 __pure getDoubleConstant(const unsigned int nIndex)
{
extern const float64 float64Constant[];
return float64Constant[nIndex];
}
static inline float32 __pure getSingleConstant(const unsigned int nIndex)
{
extern const float32 float32Constant[];
return float32Constant[nIndex];
}
static inline unsigned int getTransferLength(const unsigned int opcode)
{
unsigned int nRc;
switch (opcode & MASK_TRANSFER_LENGTH) {
case 0x00000000:
nRc = 1;
break; /* single precision */
case 0x00008000:
nRc = 2;
break; /* double precision */
case 0x00400000:
nRc = 3;
break; /* extended precision */
default:
nRc = 0;
}
return (nRc);
}
static inline unsigned int getRegisterCount(const unsigned int opcode)
{
unsigned int nRc;
switch (opcode & MASK_REGISTER_COUNT) {
case 0x00000000:
nRc = 4;
break;
case 0x00008000:
nRc = 1;
break;
case 0x00400000:
nRc = 2;
break;
case 0x00408000:
nRc = 3;
break;
default:
nRc = 0;
}
return (nRc);
}
static inline unsigned int getRoundingPrecision(const unsigned int opcode)
{
unsigned int nRc;
switch (opcode & MASK_ROUNDING_PRECISION) {
case 0x00000000:
nRc = 1;
break;
case 0x00000080:
nRc = 2;
break;
case 0x00080000:
nRc = 3;
break;
default:
nRc = 0;
}
return (nRc);
}
static inline unsigned int getDestinationSize(const unsigned int opcode)
{
unsigned int nRc;
switch (opcode & MASK_DESTINATION_SIZE) {
case 0x00000000:
nRc = typeSingle;
break;
case 0x00000080:
nRc = typeDouble;
break;
case 0x00080000:
nRc = typeExtended;
break;
default:
nRc = typeNone;
}
return (nRc);
}
extern const float64 float64Constant[];
extern const float32 float32Constant[];
#endif

108
arch/arm/nwfpe/fpsr.h Normal file
View file

@ -0,0 +1,108 @@
/*
NetWinder Floating Point Emulator
(c) Rebel.com, 1998-1999
Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
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. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __FPSR_H__
#define __FPSR_H__
/*
The FPSR is a 32 bit register consisting of 4 parts, each exactly
one byte.
SYSTEM ID
EXCEPTION TRAP ENABLE BYTE
SYSTEM CONTROL BYTE
CUMULATIVE EXCEPTION FLAGS BYTE
The FPCR is a 32 bit register consisting of bit flags.
*/
/* SYSTEM ID
------------
Note: the system id byte is read only */
typedef unsigned int FPSR; /* type for floating point status register */
typedef unsigned int FPCR; /* type for floating point control register */
#define MASK_SYSID 0xff000000
#define BIT_HARDWARE 0x80000000
#define FP_EMULATOR 0x01000000 /* System ID for emulator */
#define FP_ACCELERATOR 0x81000000 /* System ID for FPA11 */
/* EXCEPTION TRAP ENABLE BYTE
----------------------------- */
#define MASK_TRAP_ENABLE 0x00ff0000
#define MASK_TRAP_ENABLE_STRICT 0x001f0000
#define BIT_IXE 0x00100000 /* inexact exception enable */
#define BIT_UFE 0x00080000 /* underflow exception enable */
#define BIT_OFE 0x00040000 /* overflow exception enable */
#define BIT_DZE 0x00020000 /* divide by zero exception enable */
#define BIT_IOE 0x00010000 /* invalid operation exception enable */
/* SYSTEM CONTROL BYTE
---------------------- */
#define MASK_SYSTEM_CONTROL 0x0000ff00
#define MASK_TRAP_STRICT 0x00001f00
#define BIT_AC 0x00001000 /* use alternative C-flag definition
for compares */
#define BIT_EP 0x00000800 /* use expanded packed decimal format */
#define BIT_SO 0x00000400 /* select synchronous operation of FPA */
#define BIT_NE 0x00000200 /* NaN exception bit */
#define BIT_ND 0x00000100 /* no denormalized numbers bit */
/* CUMULATIVE EXCEPTION FLAGS BYTE
---------------------------------- */
#define MASK_EXCEPTION_FLAGS 0x000000ff
#define MASK_EXCEPTION_FLAGS_STRICT 0x0000001f
#define BIT_IXC 0x00000010 /* inexact exception flag */
#define BIT_UFC 0x00000008 /* underflow exception flag */
#define BIT_OFC 0x00000004 /* overfloat exception flag */
#define BIT_DZC 0x00000002 /* divide by zero exception flag */
#define BIT_IOC 0x00000001 /* invalid operation exception flag */
/* Floating Point Control Register
----------------------------------*/
#define BIT_RU 0x80000000 /* rounded up bit */
#define BIT_IE 0x10000000 /* inexact bit */
#define BIT_MO 0x08000000 /* mantissa overflow bit */
#define BIT_EO 0x04000000 /* exponent overflow bit */
#define BIT_SB 0x00000800 /* store bounce */
#define BIT_AB 0x00000400 /* arithmetic bounce */
#define BIT_RE 0x00000200 /* rounding exception */
#define BIT_DA 0x00000100 /* disable FPA */
#define MASK_OP 0x00f08010 /* AU operation code */
#define MASK_PR 0x00080080 /* AU precision */
#define MASK_S1 0x00070000 /* AU source register 1 */
#define MASK_S2 0x00000007 /* AU source register 2 */
#define MASK_DS 0x00007000 /* AU destination register */
#define MASK_RM 0x00000060 /* AU rounding mode */
#define MASK_ALU 0x9cfff2ff /* only ALU can write these bits */
#define MASK_RESET 0x00000d00 /* bits set on reset, all others cleared */
#define MASK_WFC MASK_RESET
#define MASK_RFC ~MASK_RESET
#endif

48
arch/arm/nwfpe/milieu.h Normal file
View file

@ -0,0 +1,48 @@
/*
===============================================================================
This C header file is part of the SoftFloat IEC/IEEE Floating-point
Arithmetic Package, Release 2.
Written by John R. Hauser. This work was made possible in part by the
International Computer Science Institute, located at Suite 600, 1947 Center
Street, Berkeley, California 94704. Funding was partially provided by the
National Science Foundation under grant MIP-9311980. The original version
of this code was written as part of a project to build a fixed-point vector
processor in collaboration with the University of California at Berkeley,
overseen by Profs. Nelson Morgan and John Wawrzynek. More information
is available through the Web page
http://www.jhauser.us/arithmetic/SoftFloat-2b/SoftFloat-source.txt
THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort
has been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT
TIMES RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO
PERSONS AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ANY
AND ALL LOSSES, COSTS, OR OTHER PROBLEMS ARISING FROM ITS USE.
Derivative works are acceptable, even for commercial purposes, so long as
(1) they include prominent notice that the work is derivative, and (2) they
include prominent notice akin to these three paragraphs for those parts of
this code that are retained.
===============================================================================
*/
/*
-------------------------------------------------------------------------------
Include common integer types and flags.
-------------------------------------------------------------------------------
*/
#include "ARM-gcc.h"
/*
-------------------------------------------------------------------------------
Symbolic Boolean literals.
-------------------------------------------------------------------------------
*/
enum {
FALSE = 0,
TRUE = 1
};

View file

@ -0,0 +1,124 @@
/*
NetWinder Floating Point Emulator
(c) Rebel.COM, 1998,1999
(c) Philip Blundell, 2001
Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
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. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "fpa11.h"
#include "softfloat.h"
#include "fpopcode.h"
float32 float32_exp(float32 Fm);
float32 float32_ln(float32 Fm);
float32 float32_sin(float32 rFm);
float32 float32_cos(float32 rFm);
float32 float32_arcsin(float32 rFm);
float32 float32_arctan(float32 rFm);
float32 float32_log(float32 rFm);
float32 float32_tan(float32 rFm);
float32 float32_arccos(float32 rFm);
float32 float32_pow(float32 rFn, float32 rFm);
float32 float32_pol(float32 rFn, float32 rFm);
static float32 float32_rsf(struct roundingData *roundData, float32 rFn, float32 rFm)
{
return float32_sub(roundData, rFm, rFn);
}
static float32 float32_rdv(struct roundingData *roundData, float32 rFn, float32 rFm)
{
return float32_div(roundData, rFm, rFn);
}
static float32 (*const dyadic_single[16])(struct roundingData *, float32 rFn, float32 rFm) = {
[ADF_CODE >> 20] = float32_add,
[MUF_CODE >> 20] = float32_mul,
[SUF_CODE >> 20] = float32_sub,
[RSF_CODE >> 20] = float32_rsf,
[DVF_CODE >> 20] = float32_div,
[RDF_CODE >> 20] = float32_rdv,
[RMF_CODE >> 20] = float32_rem,
[FML_CODE >> 20] = float32_mul,
[FDV_CODE >> 20] = float32_div,
[FRD_CODE >> 20] = float32_rdv,
};
static float32 float32_mvf(struct roundingData *roundData, float32 rFm)
{
return rFm;
}
static float32 float32_mnf(struct roundingData *roundData, float32 rFm)
{
return rFm ^ 0x80000000;
}
static float32 float32_abs(struct roundingData *roundData, float32 rFm)
{
return rFm & 0x7fffffff;
}
static float32 (*const monadic_single[16])(struct roundingData*, float32 rFm) = {
[MVF_CODE >> 20] = float32_mvf,
[MNF_CODE >> 20] = float32_mnf,
[ABS_CODE >> 20] = float32_abs,
[RND_CODE >> 20] = float32_round_to_int,
[URD_CODE >> 20] = float32_round_to_int,
[SQT_CODE >> 20] = float32_sqrt,
[NRM_CODE >> 20] = float32_mvf,
};
unsigned int SingleCPDO(struct roundingData *roundData, const unsigned int opcode, FPREG * rFd)
{
FPA11 *fpa11 = GET_FPA11();
float32 rFm;
unsigned int Fm, opc_mask_shift;
Fm = getFm(opcode);
if (CONSTANT_FM(opcode)) {
rFm = getSingleConstant(Fm);
} else if (fpa11->fType[Fm] == typeSingle) {
rFm = fpa11->fpreg[Fm].fSingle;
} else {
return 0;
}
opc_mask_shift = (opcode & MASK_ARITHMETIC_OPCODE) >> 20;
if (!MONADIC_INSTRUCTION(opcode)) {
unsigned int Fn = getFn(opcode);
float32 rFn;
if (fpa11->fType[Fn] == typeSingle &&
dyadic_single[opc_mask_shift]) {
rFn = fpa11->fpreg[Fn].fSingle;
rFd->fSingle = dyadic_single[opc_mask_shift](roundData, rFn, rFm);
} else {
return 0;
}
} else {
if (monadic_single[opc_mask_shift]) {
rFd->fSingle = monadic_single[opc_mask_shift](roundData, rFm);
} else {
return 0;
}
}
return 1;
}

View file

@ -0,0 +1,754 @@
/*
===============================================================================
This C source fragment is part of the SoftFloat IEC/IEEE Floating-point
Arithmetic Package, Release 2.
Written by John R. Hauser. This work was made possible in part by the
International Computer Science Institute, located at Suite 600, 1947 Center
Street, Berkeley, California 94704. Funding was partially provided by the
National Science Foundation under grant MIP-9311980. The original version
of this code was written as part of a project to build a fixed-point vector
processor in collaboration with the University of California at Berkeley,
overseen by Profs. Nelson Morgan and John Wawrzynek. More information
is available through the web page
http://www.jhauser.us/arithmetic/SoftFloat-2b/SoftFloat-source.txt
THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort
has been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT
TIMES RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO
PERSONS AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ANY
AND ALL LOSSES, COSTS, OR OTHER PROBLEMS ARISING FROM ITS USE.
Derivative works are acceptable, even for commercial purposes, so long as
(1) they include prominent notice that the work is derivative, and (2) they
include prominent notice akin to these three paragraphs for those parts of
this code that are retained.
===============================================================================
*/
/*
-------------------------------------------------------------------------------
Shifts `a' right by the number of bits given in `count'. If any nonzero
bits are shifted off, they are ``jammed'' into the least significant bit of
the result by setting the least significant bit to 1. The value of `count'
can be arbitrarily large; in particular, if `count' is greater than 32, the
result will be either 0 or 1, depending on whether `a' is zero or nonzero.
The result is stored in the location pointed to by `zPtr'.
-------------------------------------------------------------------------------
*/
INLINE void shift32RightJamming( bits32 a, int16 count, bits32 *zPtr )
{
bits32 z;
if ( count == 0 ) {
z = a;
}
else if ( count < 32 ) {
z = ( a>>count ) | ( ( a<<( ( - count ) & 31 ) ) != 0 );
}
else {
z = ( a != 0 );
}
*zPtr = z;
}
/*
-------------------------------------------------------------------------------
Shifts `a' right by the number of bits given in `count'. If any nonzero
bits are shifted off, they are ``jammed'' into the least significant bit of
the result by setting the least significant bit to 1. The value of `count'
can be arbitrarily large; in particular, if `count' is greater than 64, the
result will be either 0 or 1, depending on whether `a' is zero or nonzero.
The result is stored in the location pointed to by `zPtr'.
-------------------------------------------------------------------------------
*/
INLINE void shift64RightJamming( bits64 a, int16 count, bits64 *zPtr )
{
bits64 z;
__asm__("@shift64RightJamming -- start");
if ( count == 0 ) {
z = a;
}
else if ( count < 64 ) {
z = ( a>>count ) | ( ( a<<( ( - count ) & 63 ) ) != 0 );
}
else {
z = ( a != 0 );
}
__asm__("@shift64RightJamming -- end");
*zPtr = z;
}
/*
-------------------------------------------------------------------------------
Shifts the 128-bit value formed by concatenating `a0' and `a1' right by 64
_plus_ the number of bits given in `count'. The shifted result is at most
64 nonzero bits; this is stored at the location pointed to by `z0Ptr'. The
bits shifted off form a second 64-bit result as follows: The _last_ bit
shifted off is the most-significant bit of the extra result, and the other
63 bits of the extra result are all zero if and only if _all_but_the_last_
bits shifted off were all zero. This extra result is stored in the location
pointed to by `z1Ptr'. The value of `count' can be arbitrarily large.
(This routine makes more sense if `a0' and `a1' are considered to form a
fixed-point value with binary point between `a0' and `a1'. This fixed-point
value is shifted right by the number of bits given in `count', and the
integer part of the result is returned at the location pointed to by
`z0Ptr'. The fractional part of the result may be slightly corrupted as
described above, and is returned at the location pointed to by `z1Ptr'.)
-------------------------------------------------------------------------------
*/
INLINE void
shift64ExtraRightJamming(
bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr )
{
bits64 z0, z1;
int8 negCount = ( - count ) & 63;
if ( count == 0 ) {
z1 = a1;
z0 = a0;
}
else if ( count < 64 ) {
z1 = ( a0<<negCount ) | ( a1 != 0 );
z0 = a0>>count;
}
else {
if ( count == 64 ) {
z1 = a0 | ( a1 != 0 );
}
else {
z1 = ( ( a0 | a1 ) != 0 );
}
z0 = 0;
}
*z1Ptr = z1;
*z0Ptr = z0;
}
/*
-------------------------------------------------------------------------------
Shifts the 128-bit value formed by concatenating `a0' and `a1' right by the
number of bits given in `count'. Any bits shifted off are lost. The value
of `count' can be arbitrarily large; in particular, if `count' is greater
than 128, the result will be 0. The result is broken into two 64-bit pieces
which are stored at the locations pointed to by `z0Ptr' and `z1Ptr'.
-------------------------------------------------------------------------------
*/
INLINE void
shift128Right(
bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr )
{
bits64 z0, z1;
int8 negCount = ( - count ) & 63;
if ( count == 0 ) {
z1 = a1;
z0 = a0;
}
else if ( count < 64 ) {
z1 = ( a0<<negCount ) | ( a1>>count );
z0 = a0>>count;
}
else {
z1 = ( count < 64 ) ? ( a0>>( count & 63 ) ) : 0;
z0 = 0;
}
*z1Ptr = z1;
*z0Ptr = z0;
}
/*
-------------------------------------------------------------------------------
Shifts the 128-bit value formed by concatenating `a0' and `a1' right by the
number of bits given in `count'. If any nonzero bits are shifted off, they
are ``jammed'' into the least significant bit of the result by setting the
least significant bit to 1. The value of `count' can be arbitrarily large;
in particular, if `count' is greater than 128, the result will be either 0
or 1, depending on whether the concatenation of `a0' and `a1' is zero or
nonzero. The result is broken into two 64-bit pieces which are stored at
the locations pointed to by `z0Ptr' and `z1Ptr'.
-------------------------------------------------------------------------------
*/
INLINE void
shift128RightJamming(
bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr )
{
bits64 z0, z1;
int8 negCount = ( - count ) & 63;
if ( count == 0 ) {
z1 = a1;
z0 = a0;
}
else if ( count < 64 ) {
z1 = ( a0<<negCount ) | ( a1>>count ) | ( ( a1<<negCount ) != 0 );
z0 = a0>>count;
}
else {
if ( count == 64 ) {
z1 = a0 | ( a1 != 0 );
}
else if ( count < 128 ) {
z1 = ( a0>>( count & 63 ) ) | ( ( ( a0<<negCount ) | a1 ) != 0 );
}
else {
z1 = ( ( a0 | a1 ) != 0 );
}
z0 = 0;
}
*z1Ptr = z1;
*z0Ptr = z0;
}
/*
-------------------------------------------------------------------------------
Shifts the 192-bit value formed by concatenating `a0', `a1', and `a2' right
by 64 _plus_ the number of bits given in `count'. The shifted result is
at most 128 nonzero bits; these are broken into two 64-bit pieces which are
stored at the locations pointed to by `z0Ptr' and `z1Ptr'. The bits shifted
off form a third 64-bit result as follows: The _last_ bit shifted off is
the most-significant bit of the extra result, and the other 63 bits of the
extra result are all zero if and only if _all_but_the_last_ bits shifted off
were all zero. This extra result is stored in the location pointed to by
`z2Ptr'. The value of `count' can be arbitrarily large.
(This routine makes more sense if `a0', `a1', and `a2' are considered
to form a fixed-point value with binary point between `a1' and `a2'. This
fixed-point value is shifted right by the number of bits given in `count',
and the integer part of the result is returned at the locations pointed to
by `z0Ptr' and `z1Ptr'. The fractional part of the result may be slightly
corrupted as described above, and is returned at the location pointed to by
`z2Ptr'.)
-------------------------------------------------------------------------------
*/
INLINE void
shift128ExtraRightJamming(
bits64 a0,
bits64 a1,
bits64 a2,
int16 count,
bits64 *z0Ptr,
bits64 *z1Ptr,
bits64 *z2Ptr
)
{
bits64 z0, z1, z2;
int8 negCount = ( - count ) & 63;
if ( count == 0 ) {
z2 = a2;
z1 = a1;
z0 = a0;
}
else {
if ( count < 64 ) {
z2 = a1<<negCount;
z1 = ( a0<<negCount ) | ( a1>>count );
z0 = a0>>count;
}
else {
if ( count == 64 ) {
z2 = a1;
z1 = a0;
}
else {
a2 |= a1;
if ( count < 128 ) {
z2 = a0<<negCount;
z1 = a0>>( count & 63 );
}
else {
z2 = ( count == 128 ) ? a0 : ( a0 != 0 );
z1 = 0;
}
}
z0 = 0;
}
z2 |= ( a2 != 0 );
}
*z2Ptr = z2;
*z1Ptr = z1;
*z0Ptr = z0;
}
/*
-------------------------------------------------------------------------------
Shifts the 128-bit value formed by concatenating `a0' and `a1' left by the
number of bits given in `count'. Any bits shifted off are lost. The value
of `count' must be less than 64. The result is broken into two 64-bit
pieces which are stored at the locations pointed to by `z0Ptr' and `z1Ptr'.
-------------------------------------------------------------------------------
*/
INLINE void
shortShift128Left(
bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr )
{
*z1Ptr = a1<<count;
*z0Ptr =
( count == 0 ) ? a0 : ( a0<<count ) | ( a1>>( ( - count ) & 63 ) );
}
/*
-------------------------------------------------------------------------------
Shifts the 192-bit value formed by concatenating `a0', `a1', and `a2' left
by the number of bits given in `count'. Any bits shifted off are lost.
The value of `count' must be less than 64. The result is broken into three
64-bit pieces which are stored at the locations pointed to by `z0Ptr',
`z1Ptr', and `z2Ptr'.
-------------------------------------------------------------------------------
*/
INLINE void
shortShift192Left(
bits64 a0,
bits64 a1,
bits64 a2,
int16 count,
bits64 *z0Ptr,
bits64 *z1Ptr,
bits64 *z2Ptr
)
{
bits64 z0, z1, z2;
int8 negCount;
z2 = a2<<count;
z1 = a1<<count;
z0 = a0<<count;
if ( 0 < count ) {
negCount = ( ( - count ) & 63 );
z1 |= a2>>negCount;
z0 |= a1>>negCount;
}
*z2Ptr = z2;
*z1Ptr = z1;
*z0Ptr = z0;
}
/*
-------------------------------------------------------------------------------
Adds the 128-bit value formed by concatenating `a0' and `a1' to the 128-bit
value formed by concatenating `b0' and `b1'. Addition is modulo 2^128, so
any carry out is lost. The result is broken into two 64-bit pieces which
are stored at the locations pointed to by `z0Ptr' and `z1Ptr'.
-------------------------------------------------------------------------------
*/
INLINE void
add128(
bits64 a0, bits64 a1, bits64 b0, bits64 b1, bits64 *z0Ptr, bits64 *z1Ptr )
{
bits64 z1;
z1 = a1 + b1;
*z1Ptr = z1;
*z0Ptr = a0 + b0 + ( z1 < a1 );
}
/*
-------------------------------------------------------------------------------
Adds the 192-bit value formed by concatenating `a0', `a1', and `a2' to the
192-bit value formed by concatenating `b0', `b1', and `b2'. Addition is
modulo 2^192, so any carry out is lost. The result is broken into three
64-bit pieces which are stored at the locations pointed to by `z0Ptr',
`z1Ptr', and `z2Ptr'.
-------------------------------------------------------------------------------
*/
INLINE void
add192(
bits64 a0,
bits64 a1,
bits64 a2,
bits64 b0,
bits64 b1,
bits64 b2,
bits64 *z0Ptr,
bits64 *z1Ptr,
bits64 *z2Ptr
)
{
bits64 z0, z1, z2;
int8 carry0, carry1;
z2 = a2 + b2;
carry1 = ( z2 < a2 );
z1 = a1 + b1;
carry0 = ( z1 < a1 );
z0 = a0 + b0;
z1 += carry1;
z0 += ( z1 < carry1 );
z0 += carry0;
*z2Ptr = z2;
*z1Ptr = z1;
*z0Ptr = z0;
}
/*
-------------------------------------------------------------------------------
Subtracts the 128-bit value formed by concatenating `b0' and `b1' from the
128-bit value formed by concatenating `a0' and `a1'. Subtraction is modulo
2^128, so any borrow out (carry out) is lost. The result is broken into two
64-bit pieces which are stored at the locations pointed to by `z0Ptr' and
`z1Ptr'.
-------------------------------------------------------------------------------
*/
INLINE void
sub128(
bits64 a0, bits64 a1, bits64 b0, bits64 b1, bits64 *z0Ptr, bits64 *z1Ptr )
{
*z1Ptr = a1 - b1;
*z0Ptr = a0 - b0 - ( a1 < b1 );
}
/*
-------------------------------------------------------------------------------
Subtracts the 192-bit value formed by concatenating `b0', `b1', and `b2'
from the 192-bit value formed by concatenating `a0', `a1', and `a2'.
Subtraction is modulo 2^192, so any borrow out (carry out) is lost. The
result is broken into three 64-bit pieces which are stored at the locations
pointed to by `z0Ptr', `z1Ptr', and `z2Ptr'.
-------------------------------------------------------------------------------
*/
INLINE void
sub192(
bits64 a0,
bits64 a1,
bits64 a2,
bits64 b0,
bits64 b1,
bits64 b2,
bits64 *z0Ptr,
bits64 *z1Ptr,
bits64 *z2Ptr
)
{
bits64 z0, z1, z2;
int8 borrow0, borrow1;
z2 = a2 - b2;
borrow1 = ( a2 < b2 );
z1 = a1 - b1;
borrow0 = ( a1 < b1 );
z0 = a0 - b0;
z0 -= ( z1 < borrow1 );
z1 -= borrow1;
z0 -= borrow0;
*z2Ptr = z2;
*z1Ptr = z1;
*z0Ptr = z0;
}
/*
-------------------------------------------------------------------------------
Multiplies `a' by `b' to obtain a 128-bit product. The product is broken
into two 64-bit pieces which are stored at the locations pointed to by
`z0Ptr' and `z1Ptr'.
-------------------------------------------------------------------------------
*/
INLINE void mul64To128( bits64 a, bits64 b, bits64 *z0Ptr, bits64 *z1Ptr )
{
bits32 aHigh, aLow, bHigh, bLow;
bits64 z0, zMiddleA, zMiddleB, z1;
aLow = a;
aHigh = a>>32;
bLow = b;
bHigh = b>>32;
z1 = ( (bits64) aLow ) * bLow;
zMiddleA = ( (bits64) aLow ) * bHigh;
zMiddleB = ( (bits64) aHigh ) * bLow;
z0 = ( (bits64) aHigh ) * bHigh;
zMiddleA += zMiddleB;
z0 += ( ( (bits64) ( zMiddleA < zMiddleB ) )<<32 ) + ( zMiddleA>>32 );
zMiddleA <<= 32;
z1 += zMiddleA;
z0 += ( z1 < zMiddleA );
*z1Ptr = z1;
*z0Ptr = z0;
}
/*
-------------------------------------------------------------------------------
Multiplies the 128-bit value formed by concatenating `a0' and `a1' by `b' to
obtain a 192-bit product. The product is broken into three 64-bit pieces
which are stored at the locations pointed to by `z0Ptr', `z1Ptr', and
`z2Ptr'.
-------------------------------------------------------------------------------
*/
INLINE void
mul128By64To192(
bits64 a0,
bits64 a1,
bits64 b,
bits64 *z0Ptr,
bits64 *z1Ptr,
bits64 *z2Ptr
)
{
bits64 z0, z1, z2, more1;
mul64To128( a1, b, &z1, &z2 );
mul64To128( a0, b, &z0, &more1 );
add128( z0, more1, 0, z1, &z0, &z1 );
*z2Ptr = z2;
*z1Ptr = z1;
*z0Ptr = z0;
}
/*
-------------------------------------------------------------------------------
Multiplies the 128-bit value formed by concatenating `a0' and `a1' to the
128-bit value formed by concatenating `b0' and `b1' to obtain a 256-bit
product. The product is broken into four 64-bit pieces which are stored at
the locations pointed to by `z0Ptr', `z1Ptr', `z2Ptr', and `z3Ptr'.
-------------------------------------------------------------------------------
*/
INLINE void
mul128To256(
bits64 a0,
bits64 a1,
bits64 b0,
bits64 b1,
bits64 *z0Ptr,
bits64 *z1Ptr,
bits64 *z2Ptr,
bits64 *z3Ptr
)
{
bits64 z0, z1, z2, z3;
bits64 more1, more2;
mul64To128( a1, b1, &z2, &z3 );
mul64To128( a1, b0, &z1, &more2 );
add128( z1, more2, 0, z2, &z1, &z2 );
mul64To128( a0, b0, &z0, &more1 );
add128( z0, more1, 0, z1, &z0, &z1 );
mul64To128( a0, b1, &more1, &more2 );
add128( more1, more2, 0, z2, &more1, &z2 );
add128( z0, z1, 0, more1, &z0, &z1 );
*z3Ptr = z3;
*z2Ptr = z2;
*z1Ptr = z1;
*z0Ptr = z0;
}
/*
-------------------------------------------------------------------------------
Returns an approximation to the 64-bit integer quotient obtained by dividing
`b' into the 128-bit value formed by concatenating `a0' and `a1'. The
divisor `b' must be at least 2^63. If q is the exact quotient truncated
toward zero, the approximation returned lies between q and q + 2 inclusive.
If the exact quotient q is larger than 64 bits, the maximum positive 64-bit
unsigned integer is returned.
-------------------------------------------------------------------------------
*/
static bits64 estimateDiv128To64( bits64 a0, bits64 a1, bits64 b )
{
bits64 b0, b1;
bits64 rem0, rem1, term0, term1;
bits64 z;
if ( b <= a0 ) return LIT64( 0xFFFFFFFFFFFFFFFF );
b0 = b>>32; /* hence b0 is 32 bits wide now */
if ( b0<<32 <= a0 ) {
z = LIT64( 0xFFFFFFFF00000000 );
} else {
z = a0;
do_div( z, b0 );
z <<= 32;
}
mul64To128( b, z, &term0, &term1 );
sub128( a0, a1, term0, term1, &rem0, &rem1 );
while ( ( (sbits64) rem0 ) < 0 ) {
z -= LIT64( 0x100000000 );
b1 = b<<32;
add128( rem0, rem1, b0, b1, &rem0, &rem1 );
}
rem0 = ( rem0<<32 ) | ( rem1>>32 );
if ( b0<<32 <= rem0 ) {
z |= 0xFFFFFFFF;
} else {
do_div( rem0, b0 );
z |= rem0;
}
return z;
}
/*
-------------------------------------------------------------------------------
Returns an approximation to the square root of the 32-bit significand given
by `a'. Considered as an integer, `a' must be at least 2^31. If bit 0 of
`aExp' (the least significant bit) is 1, the integer returned approximates
2^31*sqrt(`a'/2^31), where `a' is considered an integer. If bit 0 of `aExp'
is 0, the integer returned approximates 2^31*sqrt(`a'/2^30). In either
case, the approximation returned lies strictly within +/-2 of the exact
value.
-------------------------------------------------------------------------------
*/
static bits32 estimateSqrt32( int16 aExp, bits32 a )
{
static const bits16 sqrtOddAdjustments[] = {
0x0004, 0x0022, 0x005D, 0x00B1, 0x011D, 0x019F, 0x0236, 0x02E0,
0x039C, 0x0468, 0x0545, 0x0631, 0x072B, 0x0832, 0x0946, 0x0A67
};
static const bits16 sqrtEvenAdjustments[] = {
0x0A2D, 0x08AF, 0x075A, 0x0629, 0x051A, 0x0429, 0x0356, 0x029E,
0x0200, 0x0179, 0x0109, 0x00AF, 0x0068, 0x0034, 0x0012, 0x0002
};
int8 index;
bits32 z;
bits64 A;
index = ( a>>27 ) & 15;
if ( aExp & 1 ) {
z = 0x4000 + ( a>>17 ) - sqrtOddAdjustments[ index ];
z = ( ( a / z )<<14 ) + ( z<<15 );
a >>= 1;
}
else {
z = 0x8000 + ( a>>17 ) - sqrtEvenAdjustments[ index ];
z = a / z + z;
z = ( 0x20000 <= z ) ? 0xFFFF8000 : ( z<<15 );
if ( z <= a ) return (bits32) ( ( (sbits32) a )>>1 );
}
A = ( (bits64) a )<<31;
do_div( A, z );
return ( (bits32) A ) + ( z>>1 );
}
/*
-------------------------------------------------------------------------------
Returns the number of leading 0 bits before the most-significant 1 bit
of `a'. If `a' is zero, 32 is returned.
-------------------------------------------------------------------------------
*/
static int8 countLeadingZeros32( bits32 a )
{
static const int8 countLeadingZerosHigh[] = {
8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
int8 shiftCount;
shiftCount = 0;
if ( a < 0x10000 ) {
shiftCount += 16;
a <<= 16;
}
if ( a < 0x1000000 ) {
shiftCount += 8;
a <<= 8;
}
shiftCount += countLeadingZerosHigh[ a>>24 ];
return shiftCount;
}
/*
-------------------------------------------------------------------------------
Returns the number of leading 0 bits before the most-significant 1 bit
of `a'. If `a' is zero, 64 is returned.
-------------------------------------------------------------------------------
*/
static int8 countLeadingZeros64( bits64 a )
{
int8 shiftCount;
shiftCount = 0;
if ( a < ( (bits64) 1 )<<32 ) {
shiftCount += 32;
}
else {
a >>= 32;
}
shiftCount += countLeadingZeros32( a );
return shiftCount;
}
/*
-------------------------------------------------------------------------------
Returns 1 if the 128-bit value formed by concatenating `a0' and `a1'
is equal to the 128-bit value formed by concatenating `b0' and `b1'.
Otherwise, returns 0.
-------------------------------------------------------------------------------
*/
INLINE flag eq128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 )
{
return ( a0 == b0 ) && ( a1 == b1 );
}
/*
-------------------------------------------------------------------------------
Returns 1 if the 128-bit value formed by concatenating `a0' and `a1' is less
than or equal to the 128-bit value formed by concatenating `b0' and `b1'.
Otherwise, returns 0.
-------------------------------------------------------------------------------
*/
INLINE flag le128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 )
{
return ( a0 < b0 ) || ( ( a0 == b0 ) && ( a1 <= b1 ) );
}
/*
-------------------------------------------------------------------------------
Returns 1 if the 128-bit value formed by concatenating `a0' and `a1' is less
than the 128-bit value formed by concatenating `b0' and `b1'. Otherwise,
returns 0.
-------------------------------------------------------------------------------
*/
INLINE flag lt128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 )
{
return ( a0 < b0 ) || ( ( a0 == b0 ) && ( a1 < b1 ) );
}
/*
-------------------------------------------------------------------------------
Returns 1 if the 128-bit value formed by concatenating `a0' and `a1' is
not equal to the 128-bit value formed by concatenating `b0' and `b1'.
Otherwise, returns 0.
-------------------------------------------------------------------------------
*/
INLINE flag ne128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 )
{
return ( a0 != b0 ) || ( a1 != b1 );
}

View file

@ -0,0 +1,367 @@
/*
===============================================================================
This C source fragment is part of the SoftFloat IEC/IEEE Floating-point
Arithmetic Package, Release 2.
Written by John R. Hauser. This work was made possible in part by the
International Computer Science Institute, located at Suite 600, 1947 Center
Street, Berkeley, California 94704. Funding was partially provided by the
National Science Foundation under grant MIP-9311980. The original version
of this code was written as part of a project to build a fixed-point vector
processor in collaboration with the University of California at Berkeley,
overseen by Profs. Nelson Morgan and John Wawrzynek. More information
is available through the Web page
http://www.jhauser.us/arithmetic/SoftFloat-2b/SoftFloat-source.txt
THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort
has been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT
TIMES RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO
PERSONS AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ANY
AND ALL LOSSES, COSTS, OR OTHER PROBLEMS ARISING FROM ITS USE.
Derivative works are acceptable, even for commercial purposes, so long as
(1) they include prominent notice that the work is derivative, and (2) they
include prominent notice akin to these three paragraphs for those parts of
this code that are retained.
===============================================================================
*/
/*
-------------------------------------------------------------------------------
Underflow tininess-detection mode, statically initialized to default value.
(The declaration in `softfloat.h' must match the `int8' type here.)
-------------------------------------------------------------------------------
*/
int8 float_detect_tininess = float_tininess_after_rounding;
/*
-------------------------------------------------------------------------------
Raises the exceptions specified by `flags'. Floating-point traps can be
defined here if desired. It is currently not possible for such a trap to
substitute a result value. If traps are not implemented, this routine
should be simply `float_exception_flags |= flags;'.
ScottB: November 4, 1998
Moved this function out of softfloat-specialize into fpmodule.c.
This effectively isolates all the changes required for integrating with the
Linux kernel into fpmodule.c. Porting to NetBSD should only require modifying
fpmodule.c to integrate with the NetBSD kernel (I hope!).
-------------------------------------------------------------------------------
void float_raise( int8 flags )
{
float_exception_flags |= flags;
}
*/
/*
-------------------------------------------------------------------------------
Internal canonical NaN format.
-------------------------------------------------------------------------------
*/
typedef struct {
flag sign;
bits64 high, low;
} commonNaNT;
/*
-------------------------------------------------------------------------------
The pattern for a default generated single-precision NaN.
-------------------------------------------------------------------------------
*/
#define float32_default_nan 0xFFFFFFFF
/*
-------------------------------------------------------------------------------
Returns 1 if the single-precision floating-point value `a' is a NaN;
otherwise returns 0.
-------------------------------------------------------------------------------
*/
flag float32_is_nan( float32 a )
{
return ( 0xFF000000 < (bits32) ( a<<1 ) );
}
/*
-------------------------------------------------------------------------------
Returns 1 if the single-precision floating-point value `a' is a signaling
NaN; otherwise returns 0.
-------------------------------------------------------------------------------
*/
flag float32_is_signaling_nan( float32 a )
{
return ( ( ( a>>22 ) & 0x1FF ) == 0x1FE ) && ( a & 0x003FFFFF );
}
/*
-------------------------------------------------------------------------------
Returns the result of converting the single-precision floating-point NaN
`a' to the canonical NaN format. If `a' is a signaling NaN, the invalid
exception is raised.
-------------------------------------------------------------------------------
*/
static commonNaNT float32ToCommonNaN( float32 a )
{
commonNaNT z;
if ( float32_is_signaling_nan( a ) ) float_raise( float_flag_invalid );
z.sign = a>>31;
z.low = 0;
z.high = ( (bits64) a )<<41;
return z;
}
/*
-------------------------------------------------------------------------------
Returns the result of converting the canonical NaN `a' to the single-
precision floating-point format.
-------------------------------------------------------------------------------
*/
static float32 commonNaNToFloat32( commonNaNT a )
{
return ( ( (bits32) a.sign )<<31 ) | 0x7FC00000 | ( a.high>>41 );
}
/*
-------------------------------------------------------------------------------
Takes two single-precision floating-point values `a' and `b', one of which
is a NaN, and returns the appropriate NaN result. If either `a' or `b' is a
signaling NaN, the invalid exception is raised.
-------------------------------------------------------------------------------
*/
static float32 propagateFloat32NaN( float32 a, float32 b )
{
flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN;
aIsNaN = float32_is_nan( a );
aIsSignalingNaN = float32_is_signaling_nan( a );
bIsNaN = float32_is_nan( b );
bIsSignalingNaN = float32_is_signaling_nan( b );
a |= 0x00400000;
b |= 0x00400000;
if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid );
if ( aIsNaN ) {
return ( aIsSignalingNaN & bIsNaN ) ? b : a;
}
else {
return b;
}
}
/*
-------------------------------------------------------------------------------
The pattern for a default generated double-precision NaN.
-------------------------------------------------------------------------------
*/
#define float64_default_nan LIT64( 0xFFFFFFFFFFFFFFFF )
/*
-------------------------------------------------------------------------------
Returns 1 if the double-precision floating-point value `a' is a NaN;
otherwise returns 0.
-------------------------------------------------------------------------------
*/
flag float64_is_nan( float64 a )
{
return ( LIT64( 0xFFE0000000000000 ) < (bits64) ( a<<1 ) );
}
/*
-------------------------------------------------------------------------------
Returns 1 if the double-precision floating-point value `a' is a signaling
NaN; otherwise returns 0.
-------------------------------------------------------------------------------
*/
flag float64_is_signaling_nan( float64 a )
{
return
( ( ( a>>51 ) & 0xFFF ) == 0xFFE )
&& ( a & LIT64( 0x0007FFFFFFFFFFFF ) );
}
/*
-------------------------------------------------------------------------------
Returns the result of converting the double-precision floating-point NaN
`a' to the canonical NaN format. If `a' is a signaling NaN, the invalid
exception is raised.
-------------------------------------------------------------------------------
*/
static commonNaNT float64ToCommonNaN( float64 a )
{
commonNaNT z;
if ( float64_is_signaling_nan( a ) ) float_raise( float_flag_invalid );
z.sign = a>>63;
z.low = 0;
z.high = a<<12;
return z;
}
/*
-------------------------------------------------------------------------------
Returns the result of converting the canonical NaN `a' to the double-
precision floating-point format.
-------------------------------------------------------------------------------
*/
static float64 commonNaNToFloat64( commonNaNT a )
{
return
( ( (bits64) a.sign )<<63 )
| LIT64( 0x7FF8000000000000 )
| ( a.high>>12 );
}
/*
-------------------------------------------------------------------------------
Takes two double-precision floating-point values `a' and `b', one of which
is a NaN, and returns the appropriate NaN result. If either `a' or `b' is a
signaling NaN, the invalid exception is raised.
-------------------------------------------------------------------------------
*/
static float64 propagateFloat64NaN( float64 a, float64 b )
{
flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN;
aIsNaN = float64_is_nan( a );
aIsSignalingNaN = float64_is_signaling_nan( a );
bIsNaN = float64_is_nan( b );
bIsSignalingNaN = float64_is_signaling_nan( b );
a |= LIT64( 0x0008000000000000 );
b |= LIT64( 0x0008000000000000 );
if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid );
if ( aIsNaN ) {
return ( aIsSignalingNaN & bIsNaN ) ? b : a;
}
else {
return b;
}
}
#ifdef FLOATX80
/*
-------------------------------------------------------------------------------
The pattern for a default generated extended double-precision NaN. The
`high' and `low' values hold the most- and least-significant bits,
respectively.
-------------------------------------------------------------------------------
*/
#define floatx80_default_nan_high 0xFFFF
#define floatx80_default_nan_low LIT64( 0xFFFFFFFFFFFFFFFF )
/*
-------------------------------------------------------------------------------
Returns 1 if the extended double-precision floating-point value `a' is a
NaN; otherwise returns 0.
-------------------------------------------------------------------------------
*/
flag floatx80_is_nan( floatx80 a )
{
return ( ( a.high & 0x7FFF ) == 0x7FFF ) && (bits64) ( a.low<<1 );
}
/*
-------------------------------------------------------------------------------
Returns 1 if the extended double-precision floating-point value `a' is a
signaling NaN; otherwise returns 0.
-------------------------------------------------------------------------------
*/
flag floatx80_is_signaling_nan( floatx80 a )
{
//register int lr;
bits64 aLow;
//__asm__("mov %0, lr" : : "g" (lr));
//fp_printk("floatx80_is_signalling_nan() called from 0x%08x\n",lr);
aLow = a.low & ~ LIT64( 0x4000000000000000 );
return
( ( a.high & 0x7FFF ) == 0x7FFF )
&& (bits64) ( aLow<<1 )
&& ( a.low == aLow );
}
/*
-------------------------------------------------------------------------------
Returns the result of converting the extended double-precision floating-
point NaN `a' to the canonical NaN format. If `a' is a signaling NaN, the
invalid exception is raised.
-------------------------------------------------------------------------------
*/
static commonNaNT floatx80ToCommonNaN( floatx80 a )
{
commonNaNT z;
if ( floatx80_is_signaling_nan( a ) ) float_raise( float_flag_invalid );
z.sign = a.high>>15;
z.low = 0;
z.high = a.low<<1;
return z;
}
/*
-------------------------------------------------------------------------------
Returns the result of converting the canonical NaN `a' to the extended
double-precision floating-point format.
-------------------------------------------------------------------------------
*/
static floatx80 commonNaNToFloatx80( commonNaNT a )
{
floatx80 z;
z.low = LIT64( 0xC000000000000000 ) | ( a.high>>1 );
z.high = ( ( (bits16) a.sign )<<15 ) | 0x7FFF;
z.__padding = 0;
return z;
}
/*
-------------------------------------------------------------------------------
Takes two extended double-precision floating-point values `a' and `b', one
of which is a NaN, and returns the appropriate NaN result. If either `a' or
`b' is a signaling NaN, the invalid exception is raised.
-------------------------------------------------------------------------------
*/
static floatx80 propagateFloatx80NaN( floatx80 a, floatx80 b )
{
flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN;
aIsNaN = floatx80_is_nan( a );
aIsSignalingNaN = floatx80_is_signaling_nan( a );
bIsNaN = floatx80_is_nan( b );
bIsSignalingNaN = floatx80_is_signaling_nan( b );
a.low |= LIT64( 0xC000000000000000 );
b.low |= LIT64( 0xC000000000000000 );
if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid );
if ( aIsNaN ) {
return ( aIsSignalingNaN & bIsNaN ) ? b : a;
}
else {
return b;
}
}
#endif

3435
arch/arm/nwfpe/softfloat.c Normal file

File diff suppressed because it is too large Load diff

281
arch/arm/nwfpe/softfloat.h Normal file
View file

@ -0,0 +1,281 @@
/*
===============================================================================
This C header file is part of the SoftFloat IEC/IEEE Floating-point
Arithmetic Package, Release 2.
Written by John R. Hauser. This work was made possible in part by the
International Computer Science Institute, located at Suite 600, 1947 Center
Street, Berkeley, California 94704. Funding was partially provided by the
National Science Foundation under grant MIP-9311980. The original version
of this code was written as part of a project to build a fixed-point vector
processor in collaboration with the University of California at Berkeley,
overseen by Profs. Nelson Morgan and John Wawrzynek. More information
is available through the Web page
http://www.jhauser.us/arithmetic/SoftFloat-2b/SoftFloat-source.txt
THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort
has been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT
TIMES RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO
PERSONS AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ANY
AND ALL LOSSES, COSTS, OR OTHER PROBLEMS ARISING FROM ITS USE.
Derivative works are acceptable, even for commercial purposes, so long as
(1) they include prominent notice that the work is derivative, and (2) they
include prominent notice akin to these three paragraphs for those parts of
this code that are retained.
===============================================================================
*/
#ifndef __SOFTFLOAT_H__
#define __SOFTFLOAT_H__
/*
-------------------------------------------------------------------------------
The macro `FLOATX80' must be defined to enable the extended double-precision
floating-point format `floatx80'. If this macro is not defined, the
`floatx80' type will not be defined, and none of the functions that either
input or output the `floatx80' type will be defined.
-------------------------------------------------------------------------------
*/
#ifdef CONFIG_FPE_NWFPE_XP
#define FLOATX80
#endif
/*
-------------------------------------------------------------------------------
Software IEC/IEEE floating-point types.
-------------------------------------------------------------------------------
*/
typedef u32 float32;
typedef u64 float64;
typedef struct {
#ifdef __ARMEB__
u16 __padding;
u16 high;
#else
u16 high;
u16 __padding;
#endif
u64 low;
} __attribute__ ((packed,aligned(4))) floatx80;
/*
-------------------------------------------------------------------------------
Software IEC/IEEE floating-point underflow tininess-detection mode.
-------------------------------------------------------------------------------
*/
extern signed char float_detect_tininess;
enum {
float_tininess_after_rounding = 0,
float_tininess_before_rounding = 1
};
/*
-------------------------------------------------------------------------------
Software IEC/IEEE floating-point rounding mode.
-------------------------------------------------------------------------------
*/
//extern int8 float_rounding_mode;
enum {
float_round_nearest_even = 0,
float_round_to_zero = 1,
float_round_down = 2,
float_round_up = 3
};
/*
-------------------------------------------------------------------------------
Software IEC/IEEE floating-point exception flags.
-------------------------------------------------------------------------------
enum {
float_flag_inexact = 1,
float_flag_underflow = 2,
float_flag_overflow = 4,
float_flag_divbyzero = 8,
float_flag_invalid = 16
};
ScottB: November 4, 1998
Changed the enumeration to match the bit order in the FPA11.
*/
enum {
float_flag_invalid = 1,
float_flag_divbyzero = 2,
float_flag_overflow = 4,
float_flag_underflow = 8,
float_flag_inexact = 16
};
/*
-------------------------------------------------------------------------------
Routine to raise any or all of the software IEC/IEEE floating-point
exception flags.
-------------------------------------------------------------------------------
*/
void float_raise( signed char );
/*
-------------------------------------------------------------------------------
Software IEC/IEEE integer-to-floating-point conversion routines.
-------------------------------------------------------------------------------
*/
float32 int32_to_float32( struct roundingData *, signed int );
float64 int32_to_float64( signed int );
#ifdef FLOATX80
floatx80 int32_to_floatx80( signed int );
#endif
/*
-------------------------------------------------------------------------------
Software IEC/IEEE single-precision conversion routines.
-------------------------------------------------------------------------------
*/
signed int float32_to_int32( struct roundingData *, float32 );
signed int float32_to_int32_round_to_zero( float32 );
float64 float32_to_float64( float32 );
#ifdef FLOATX80
floatx80 float32_to_floatx80( float32 );
#endif
/*
-------------------------------------------------------------------------------
Software IEC/IEEE single-precision operations.
-------------------------------------------------------------------------------
*/
float32 float32_round_to_int( struct roundingData*, float32 );
float32 float32_add( struct roundingData *, float32, float32 );
float32 float32_sub( struct roundingData *, float32, float32 );
float32 float32_mul( struct roundingData *, float32, float32 );
float32 float32_div( struct roundingData *, float32, float32 );
float32 float32_rem( struct roundingData *, float32, float32 );
float32 float32_sqrt( struct roundingData*, float32 );
char float32_eq( float32, float32 );
char float32_le( float32, float32 );
char float32_lt( float32, float32 );
char float32_eq_signaling( float32, float32 );
char float32_le_quiet( float32, float32 );
char float32_lt_quiet( float32, float32 );
char float32_is_signaling_nan( float32 );
/*
-------------------------------------------------------------------------------
Software IEC/IEEE double-precision conversion routines.
-------------------------------------------------------------------------------
*/
signed int float64_to_int32( struct roundingData *, float64 );
signed int float64_to_int32_round_to_zero( float64 );
float32 float64_to_float32( struct roundingData *, float64 );
#ifdef FLOATX80
floatx80 float64_to_floatx80( float64 );
#endif
/*
-------------------------------------------------------------------------------
Software IEC/IEEE double-precision operations.
-------------------------------------------------------------------------------
*/
float64 float64_round_to_int( struct roundingData *, float64 );
float64 float64_add( struct roundingData *, float64, float64 );
float64 float64_sub( struct roundingData *, float64, float64 );
float64 float64_mul( struct roundingData *, float64, float64 );
float64 float64_div( struct roundingData *, float64, float64 );
float64 float64_rem( struct roundingData *, float64, float64 );
float64 float64_sqrt( struct roundingData *, float64 );
char float64_eq( float64, float64 );
char float64_le( float64, float64 );
char float64_lt( float64, float64 );
char float64_eq_signaling( float64, float64 );
char float64_le_quiet( float64, float64 );
char float64_lt_quiet( float64, float64 );
char float64_is_signaling_nan( float64 );
#ifdef FLOATX80
/*
-------------------------------------------------------------------------------
Software IEC/IEEE extended double-precision conversion routines.
-------------------------------------------------------------------------------
*/
signed int floatx80_to_int32( struct roundingData *, floatx80 );
signed int floatx80_to_int32_round_to_zero( floatx80 );
float32 floatx80_to_float32( struct roundingData *, floatx80 );
float64 floatx80_to_float64( struct roundingData *, floatx80 );
/*
-------------------------------------------------------------------------------
Software IEC/IEEE extended double-precision operations.
-------------------------------------------------------------------------------
*/
floatx80 floatx80_round_to_int( struct roundingData *, floatx80 );
floatx80 floatx80_add( struct roundingData *, floatx80, floatx80 );
floatx80 floatx80_sub( struct roundingData *, floatx80, floatx80 );
floatx80 floatx80_mul( struct roundingData *, floatx80, floatx80 );
floatx80 floatx80_div( struct roundingData *, floatx80, floatx80 );
floatx80 floatx80_rem( struct roundingData *, floatx80, floatx80 );
floatx80 floatx80_sqrt( struct roundingData *, floatx80 );
char floatx80_eq( floatx80, floatx80 );
char floatx80_le( floatx80, floatx80 );
char floatx80_lt( floatx80, floatx80 );
char floatx80_eq_signaling( floatx80, floatx80 );
char floatx80_le_quiet( floatx80, floatx80 );
char floatx80_lt_quiet( floatx80, floatx80 );
char floatx80_is_signaling_nan( floatx80 );
extern flag floatx80_is_nan(floatx80);
#endif
static inline flag extractFloat32Sign(float32 a)
{
return a >> 31;
}
static inline flag float32_eq_nocheck(float32 a, float32 b)
{
return (a == b) || ((bits32) ((a | b) << 1) == 0);
}
static inline flag float32_lt_nocheck(float32 a, float32 b)
{
flag aSign, bSign;
aSign = extractFloat32Sign(a);
bSign = extractFloat32Sign(b);
if (aSign != bSign)
return aSign && ((bits32) ((a | b) << 1) != 0);
return (a != b) && (aSign ^ (a < b));
}
static inline flag extractFloat64Sign(float64 a)
{
return a >> 63;
}
static inline flag float64_eq_nocheck(float64 a, float64 b)
{
return (a == b) || ((bits64) ((a | b) << 1) == 0);
}
static inline flag float64_lt_nocheck(float64 a, float64 b)
{
flag aSign, bSign;
aSign = extractFloat64Sign(a);
bSign = extractFloat64Sign(b);
if (aSign != bSign)
return aSign && ((bits64) ((a | b) << 1) != 0);
return (a != b) && (aSign ^ (a < b));
}
extern flag float32_is_nan( float32 a );
extern flag float64_is_nan( float64 a );
extern int32 float64_to_uint32( struct roundingData *roundData, float64 a );
extern int32 float64_to_uint32_round_to_zero( float64 a );
#endif