Commit 58ada350 authored by Russell King's avatar Russell King

Merge flint.arm.linux.org.uk:/usr/src/linux-bk-2.5/linux-2.5-nwfpe

into flint.arm.linux.org.uk:/usr/src/linux-bk-2.5/linux-2.5-rmk
parents 8a0aa9f7 180699eb
...@@ -628,6 +628,18 @@ config FPE_NWFPE ...@@ -628,6 +628,18 @@ config FPE_NWFPE
You may say N here if you are going to load the Acorn FPEmulator You may say N here if you are going to load the Acorn FPEmulator
early in the bootup. early in the bootup.
config FPE_NWFPE_XP
bool "Support extended precision"
depends on FPE_NWFPE
help
Say Y to include 80-bit support in the kernel floating-point
emulator. Otherwise, only 32 and 64-bit support is compiled in.
Note that gcc does not generate 80-bit operations by default,
so in most cases this option only enlarges the size of the
floating point emulator without any good reason.
You almost surely want to say N here.
config FPE_FASTFPE config FPE_FASTFPE
tristate "FastFPE math emulation (EXPERIMENTAL)" tristate "FastFPE math emulation (EXPERIMENTAL)"
depends on !CPU_26 && !CPU_32v3 && EXPERIMENTAL depends on !CPU_26 && !CPU_32v3 && EXPERIMENTAL
......
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> 2002-01-19 Russell King <rmk@arm.linux.org.uk>
* fpa11.h - Add documentation * fpa11.h - Add documentation
......
...@@ -2,18 +2,12 @@ ...@@ -2,18 +2,12 @@
# Copyright (C) 1998, 1999, 2001 Philip Blundell # Copyright (C) 1998, 1999, 2001 Philip Blundell
# #
obj-y := obj-$(CONFIG_FPE_NWFPE) += nwfpe.o
obj-m :=
obj-n :=
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
nwfpe-objs := fpa11.o fpa11_cpdo.o fpa11_cpdt.o fpa11_cprt.o \ nwfpe-$(CONFIG_FPE_NWFPE_XP) += extended_cpdo.o
fpmodule.o fpopcode.o softfloat.o \ nwfpe-$(CONFIG_CPU_26) += entry26.o
single_cpdo.o double_cpdo.o extended_cpdo.o nwfpe-$(CONFIG_CPU_32) += entry.o
ifeq ($(CONFIG_CPU_26),y)
nwfpe-objs += entry26.o
else
nwfpe-objs += entry.o
endif
...@@ -23,6 +23,11 @@ ...@@ -23,6 +23,11 @@
#include "softfloat.h" #include "softfloat.h"
#include "fpopcode.h" #include "fpopcode.h"
union float64_components {
float64 f64;
unsigned int i[2];
};
float64 float64_exp(float64 Fm); float64 float64_exp(float64 Fm);
float64 float64_ln(float64 Fm); float64 float64_ln(float64 Fm);
float64 float64_sin(float64 rFm); float64 float64_sin(float64 rFm);
...@@ -32,257 +37,123 @@ float64 float64_arctan(float64 rFm); ...@@ -32,257 +37,123 @@ float64 float64_arctan(float64 rFm);
float64 float64_log(float64 rFm); float64 float64_log(float64 rFm);
float64 float64_tan(float64 rFm); float64 float64_tan(float64 rFm);
float64 float64_arccos(float64 rFm); float64 float64_arccos(float64 rFm);
float64 float64_pow(float64 rFn,float64 rFm); float64 float64_pow(float64 rFn, float64 rFm);
float64 float64_pol(float64 rFn,float64 rFm); float64 float64_pol(float64 rFn, float64 rFm);
unsigned int DoubleCPDO(const unsigned int opcode) static float64 float64_rsf(float64 rFn, float64 rFm)
{ {
FPA11 *fpa11 = GET_FPA11(); return float64_sub(rFm, rFn);
float64 rFm, rFn;
unsigned int Fd, Fm, Fn, nRc = 1;
//printk("DoubleCPDO(0x%08x)\n",opcode);
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;
case typeExtended:
// !! patb
//printk("not implemented! why not?\n");
//!! ScottB
// should never get here, if extended involved
// then other operand should be promoted then
// ExtendedCPDO called.
break;
default: return 0;
}
}
if (!MONADIC_INSTRUCTION(opcode))
{
Fn = getFn(opcode);
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;
}
}
Fd = getFd(opcode);
/* !! this switch isn't optimized; better (opcode & MASK_ARITHMETIC_OPCODE)>>24, sort of */
switch (opcode & MASK_ARITHMETIC_OPCODE)
{
/* dyadic opcodes */
case ADF_CODE:
fpa11->fpreg[Fd].fDouble = float64_add(rFn,rFm);
break;
case MUF_CODE:
case FML_CODE:
fpa11->fpreg[Fd].fDouble = float64_mul(rFn,rFm);
break;
case SUF_CODE:
fpa11->fpreg[Fd].fDouble = float64_sub(rFn,rFm);
break;
case RSF_CODE:
fpa11->fpreg[Fd].fDouble = float64_sub(rFm,rFn);
break;
case DVF_CODE:
case FDV_CODE:
fpa11->fpreg[Fd].fDouble = float64_div(rFn,rFm);
break;
case RDF_CODE:
case FRD_CODE:
fpa11->fpreg[Fd].fDouble = float64_div(rFm,rFn);
break;
#if 0
case POW_CODE:
fpa11->fpreg[Fd].fDouble = float64_pow(rFn,rFm);
break;
case RPW_CODE:
fpa11->fpreg[Fd].fDouble = float64_pow(rFm,rFn);
break;
#endif
case RMF_CODE:
fpa11->fpreg[Fd].fDouble = float64_rem(rFn,rFm);
break;
#if 0
case POL_CODE:
fpa11->fpreg[Fd].fDouble = float64_pol(rFn,rFm);
break;
#endif
/* monadic opcodes */
case MVF_CODE:
fpa11->fpreg[Fd].fDouble = rFm;
break;
case MNF_CODE:
{
unsigned int *p = (unsigned int*)&rFm;
p[1] ^= 0x80000000;
fpa11->fpreg[Fd].fDouble = rFm;
}
break;
case ABS_CODE:
{
unsigned int *p = (unsigned int*)&rFm;
p[1] &= 0x7fffffff;
fpa11->fpreg[Fd].fDouble = rFm;
}
break;
case RND_CODE:
case URD_CODE:
fpa11->fpreg[Fd].fDouble = float64_round_to_int(rFm);
break;
case SQT_CODE:
fpa11->fpreg[Fd].fDouble = float64_sqrt(rFm);
break;
#if 0
case LOG_CODE:
fpa11->fpreg[Fd].fDouble = float64_log(rFm);
break;
case LGN_CODE:
fpa11->fpreg[Fd].fDouble = float64_ln(rFm);
break;
case EXP_CODE:
fpa11->fpreg[Fd].fDouble = float64_exp(rFm);
break;
case SIN_CODE:
fpa11->fpreg[Fd].fDouble = float64_sin(rFm);
break;
case COS_CODE:
fpa11->fpreg[Fd].fDouble = float64_cos(rFm);
break;
case TAN_CODE:
fpa11->fpreg[Fd].fDouble = float64_tan(rFm);
break;
case ASN_CODE:
fpa11->fpreg[Fd].fDouble = float64_arcsin(rFm);
break;
case ACS_CODE:
fpa11->fpreg[Fd].fDouble = float64_arccos(rFm);
break;
case ATN_CODE:
fpa11->fpreg[Fd].fDouble = float64_arctan(rFm);
break;
#endif
case NRM_CODE:
break;
default:
{
nRc = 0;
}
}
if (0 != nRc) fpa11->fType[Fd] = typeDouble;
return nRc;
} }
#if 0 static float64 float64_rdv(float64 rFn, float64 rFm)
float64 float64_exp(float64 rFm)
{ {
return rFm; return float64_div(rFm, rFn);
//series
} }
float64 float64_ln(float64 rFm) static float64 (*const dyadic_double[16])(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(float64 rFm)
{ {
return rFm; return rFm;
//series
} }
float64 float64_sin(float64 rFm) static float64 float64_mnf(float64 rFm)
{ {
return rFm; union float64_components u;
//series
}
float64 float64_cos(float64 rFm) u.f64 = rFm;
{ u.i[1] ^= 0x80000000;
return rFm;
//series
}
#if 0 return u.f64;
float64 float64_arcsin(float64 rFm)
{
//series
} }
float64 float64_arctan(float64 rFm) static float64 float64_abs(float64 rFm)
{ {
//series union float64_components u;
}
#endif
float64 float64_log(float64 rFm) u.f64 = rFm;
{ u.i[1] &= 0x7fffffff;
return float64_div(float64_ln(rFm),getDoubleConstant(7));
}
float64 float64_tan(float64 rFm) return u.f64;
{
return float64_div(float64_sin(rFm),float64_cos(rFm));
}
float64 float64_arccos(float64 rFm)
{
return rFm;
//return float64_sub(halfPi,float64_arcsin(rFm));
}
float64 float64_pow(float64 rFn,float64 rFm)
{
return float64_exp(float64_mul(rFm,float64_ln(rFn)));
} }
float64 float64_pol(float64 rFn,float64 rFm) static float64 (*const monadic_double[16])(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(const unsigned int opcode, FPREG * rFd)
{ {
return float64_arctan(float64_div(rFn,rFm)); 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](rFn, rFm);
} else {
return 0;
}
} else {
if (monadic_double[opc_mask_shift]) {
rFd->fDouble = monadic_double[opc_mask_shift](rFm);
} else {
return 0;
}
}
return 1;
} }
#endif
...@@ -72,37 +72,37 @@ several floating point instructions. */ ...@@ -72,37 +72,37 @@ several floating point instructions. */
.globl nwfpe_enter .globl nwfpe_enter
nwfpe_enter: nwfpe_enter:
mov r4, lr @ save the failure-return addresses mov r4, lr @ save the failure-return addresses
mov sl, sp mov sl, sp @ we access the registers via 'sl'
ldr r5, [sp, #60] @ get contents of PC; ldr r5, [sp, #60] @ get contents of PC;
emulate: emulate:
bl EmulateAll @ emulate the instruction bl EmulateAll @ emulate the instruction
cmp r0, #0 @ was emulation successful cmp r0, #0 @ was emulation successful
moveq pc, r4 @ no, return failure moveq pc, r4 @ no, return failure
next: next:
.Lx1: ldrt r6, [r5], #4 @ get the next instruction and .Lx1: ldrt r6, [r5], #4 @ get the next instruction and
@ increment PC @ increment PC
and r2, r6, #0x0F000000 @ test for FP insns and r2, r6, #0x0F000000 @ test for FP insns
teq r2, #0x0C000000 teq r2, #0x0C000000
teqne r2, #0x0D000000 teqne r2, #0x0D000000
teqne r2, #0x0E000000 teqne r2, #0x0E000000
movne pc, r9 @ return ok if not a fp insn movne pc, r9 @ return ok if not a fp insn
str r5, [sp, #60] @ update PC copy in regs str r5, [sp, #60] @ update PC copy in regs
mov r0, r6 @ save a copy mov r0, r6 @ save a copy
ldr r1, [sp, #64] @ fetch the condition codes ldr r1, [sp, #64] @ fetch the condition codes
bl checkCondition @ check the condition bl checkCondition @ check the condition
cmp r0, #0 @ r0 = 0 ==> condition failed cmp r0, #0 @ r0 = 0 ==> condition failed
@ if condition code failed to match, next insn @ if condition code failed to match, next insn
beq next @ get the next instruction; beq next @ get the next instruction;
mov r0, r6 @ prepare for EmulateAll() mov r0, r6 @ prepare for EmulateAll()
b emulate @ if r0 != 0, goto EmulateAll b emulate @ if r0 != 0, goto EmulateAll
@ We need to be prepared for the instructions at .Lx1 and .Lx2 @ We need to be prepared for the instructions at .Lx1 and .Lx2
@ to fault. Emit the appropriate exception gunk to fix things up. @ to fault. Emit the appropriate exception gunk to fix things up.
......
...@@ -66,7 +66,6 @@ several floating point instructions. */ ...@@ -66,7 +66,6 @@ several floating point instructions. */
.globl nwfpe_enter .globl nwfpe_enter
nwfpe_enter: nwfpe_enter:
mov sl, sp mov sl, sp
ldr r5, [sp, #60] @ get contents of PC ldr r5, [sp, #60] @ get contents of PC
bic r5, r5, #0xfc000003 bic r5, r5, #0xfc000003
ldr r0, [r5, #-4] @ get actual instruction into r0 ldr r0, [r5, #-4] @ get actual instruction into r0
...@@ -96,7 +95,7 @@ next: ...@@ -96,7 +95,7 @@ next:
@ if condition code failed to match, next insn @ if condition code failed to match, next insn
beq next @ get the next instruction; beq next @ get the next instruction;
mov r0, r6 @ prepare for EmulateAll() mov r0, r6 @ prepare for EmulateAll()
adr lr, 1b adr lr, 1b
orr lr, lr, #3 orr lr, lr, #3
......
...@@ -32,242 +32,123 @@ floatx80 floatx80_arctan(floatx80 rFm); ...@@ -32,242 +32,123 @@ floatx80 floatx80_arctan(floatx80 rFm);
floatx80 floatx80_log(floatx80 rFm); floatx80 floatx80_log(floatx80 rFm);
floatx80 floatx80_tan(floatx80 rFm); floatx80 floatx80_tan(floatx80 rFm);
floatx80 floatx80_arccos(floatx80 rFm); floatx80 floatx80_arccos(floatx80 rFm);
floatx80 floatx80_pow(floatx80 rFn,floatx80 rFm); floatx80 floatx80_pow(floatx80 rFn, floatx80 rFm);
floatx80 floatx80_pol(floatx80 rFn,floatx80 rFm); floatx80 floatx80_pol(floatx80 rFn, floatx80 rFm);
unsigned int ExtendedCPDO(const unsigned int opcode) static floatx80 floatx80_rsf(floatx80 rFn, floatx80 rFm)
{ {
FPA11 *fpa11 = GET_FPA11(); return floatx80_sub(rFm, rFn);
floatx80 rFm, rFn;
unsigned int Fd, Fm, Fn, nRc = 1;
//printk("ExtendedCPDO(0x%08x)\n",opcode);
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;
}
}
if (!MONADIC_INSTRUCTION(opcode))
{
Fn = getFn(opcode);
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;
}
}
Fd = getFd(opcode);
switch (opcode & MASK_ARITHMETIC_OPCODE)
{
/* dyadic opcodes */
case ADF_CODE:
fpa11->fpreg[Fd].fExtended = floatx80_add(rFn,rFm);
break;
case MUF_CODE:
case FML_CODE:
fpa11->fpreg[Fd].fExtended = floatx80_mul(rFn,rFm);
break;
case SUF_CODE:
fpa11->fpreg[Fd].fExtended = floatx80_sub(rFn,rFm);
break;
case RSF_CODE:
fpa11->fpreg[Fd].fExtended = floatx80_sub(rFm,rFn);
break;
case DVF_CODE:
case FDV_CODE:
fpa11->fpreg[Fd].fExtended = floatx80_div(rFn,rFm);
break;
case RDF_CODE:
case FRD_CODE:
fpa11->fpreg[Fd].fExtended = floatx80_div(rFm,rFn);
break;
#if 0
case POW_CODE:
fpa11->fpreg[Fd].fExtended = floatx80_pow(rFn,rFm);
break;
case RPW_CODE:
fpa11->fpreg[Fd].fExtended = floatx80_pow(rFm,rFn);
break;
#endif
case RMF_CODE:
fpa11->fpreg[Fd].fExtended = floatx80_rem(rFn,rFm);
break;
#if 0
case POL_CODE:
fpa11->fpreg[Fd].fExtended = floatx80_pol(rFn,rFm);
break;
#endif
/* monadic opcodes */
case MVF_CODE:
fpa11->fpreg[Fd].fExtended = rFm;
break;
case MNF_CODE:
rFm.high ^= 0x8000;
fpa11->fpreg[Fd].fExtended = rFm;
break;
case ABS_CODE:
rFm.high &= 0x7fff;
fpa11->fpreg[Fd].fExtended = rFm;
break;
case RND_CODE:
case URD_CODE:
fpa11->fpreg[Fd].fExtended = floatx80_round_to_int(rFm);
break;
case SQT_CODE:
fpa11->fpreg[Fd].fExtended = floatx80_sqrt(rFm);
break;
#if 0
case LOG_CODE:
fpa11->fpreg[Fd].fExtended = floatx80_log(rFm);
break;
case LGN_CODE:
fpa11->fpreg[Fd].fExtended = floatx80_ln(rFm);
break;
case EXP_CODE:
fpa11->fpreg[Fd].fExtended = floatx80_exp(rFm);
break;
case SIN_CODE:
fpa11->fpreg[Fd].fExtended = floatx80_sin(rFm);
break;
case COS_CODE:
fpa11->fpreg[Fd].fExtended = floatx80_cos(rFm);
break;
case TAN_CODE:
fpa11->fpreg[Fd].fExtended = floatx80_tan(rFm);
break;
case ASN_CODE:
fpa11->fpreg[Fd].fExtended = floatx80_arcsin(rFm);
break;
case ACS_CODE:
fpa11->fpreg[Fd].fExtended = floatx80_arccos(rFm);
break;
case ATN_CODE:
fpa11->fpreg[Fd].fExtended = floatx80_arctan(rFm);
break;
#endif
case NRM_CODE:
break;
default:
{
nRc = 0;
}
}
if (0 != nRc) fpa11->fType[Fd] = typeExtended;
return nRc;
}
#if 0
floatx80 floatx80_exp(floatx80 Fm)
{
//series
}
floatx80 floatx80_ln(floatx80 Fm)
{
//series
}
floatx80 floatx80_sin(floatx80 rFm)
{
//series
}
floatx80 floatx80_cos(floatx80 rFm)
{
//series
}
floatx80 floatx80_arcsin(floatx80 rFm)
{
//series
}
floatx80 floatx80_arctan(floatx80 rFm)
{
//series
} }
floatx80 floatx80_log(floatx80 rFm) static floatx80 floatx80_rdv(floatx80 rFn, floatx80 rFm)
{ {
return floatx80_div(floatx80_ln(rFm),getExtendedConstant(7)); return floatx80_div(rFm, rFn);
} }
floatx80 floatx80_tan(floatx80 rFm) static floatx80 (*const dyadic_extended[16])(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(floatx80 rFm)
{ {
return floatx80_div(floatx80_sin(rFm),floatx80_cos(rFm)); return rFm;
} }
floatx80 floatx80_arccos(floatx80 rFm) static floatx80 floatx80_mnf(floatx80 rFm)
{ {
//return floatx80_sub(halfPi,floatx80_arcsin(rFm)); rFm.high ^= 0x8000;
return rFm;
} }
floatx80 floatx80_pow(floatx80 rFn,floatx80 rFm) static floatx80 floatx80_abs(floatx80 rFm)
{ {
return floatx80_exp(floatx80_mul(rFm,floatx80_ln(rFn))); rFm.high &= 0x7fff;
return rFm;
} }
floatx80 floatx80_pol(floatx80 rFn,floatx80 rFm) static floatx80 (*const monadic_extended[16])(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(const unsigned int opcode, FPREG * rFd)
{ {
return floatx80_arctan(floatx80_div(rFn,rFm)); 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](rFn, rFm);
} else {
return 0;
}
} else {
if (monadic_extended[opc_mask_shift]) {
rFd->fExtended = monadic_extended[opc_mask_shift](rFm);
} else {
return 0;
}
}
return 1;
} }
#endif
/* /*
NetWinder Floating Point Emulator NetWinder Floating Point Emulator
(c) Rebel.COM, 1998,1999 (c) Rebel.COM, 1998,1999
(c) Philip Blundell, 2001
Direct questions, comments to Scott Bambrough <scottb@netwinder.org> Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
...@@ -37,184 +38,105 @@ unsigned int EmulateCPRT(const unsigned int); ...@@ -37,184 +38,105 @@ unsigned int EmulateCPRT(const unsigned int);
/* Reset the FPA11 chip. Called to initialize and reset the emulator. */ /* Reset the FPA11 chip. Called to initialize and reset the emulator. */
static void resetFPA11(void) static void resetFPA11(void)
{ {
int i; int i;
FPA11 *fpa11 = GET_FPA11(); FPA11 *fpa11 = GET_FPA11();
/* initialize the register type array */ /* initialize the register type array */
for (i=0;i<=7;i++) for (i = 0; i <= 7; i++) {
{ fpa11->fType[i] = typeNone;
fpa11->fType[i] = typeNone; }
}
/* FPSR: set system id to FP_EMULATOR, set AC, clear all other bits */
/* FPSR: set system id to FP_EMULATOR, set AC, clear all other bits */ fpa11->fpsr = FP_EMULATOR | BIT_AC;
fpa11->fpsr = FP_EMULATOR | BIT_AC;
/* FPCR: set SB, AB and DA bits, clear all others */
#if MAINTAIN_FPCR
fpa11->fpcr = MASK_RESET;
#endif
} }
void SetRoundingMode(const unsigned int opcode) void SetRoundingMode(const unsigned int opcode)
{ {
#if MAINTAIN_FPCR switch (opcode & MASK_ROUNDING_MODE) {
FPA11 *fpa11 = GET_FPA11(); default:
fpa11->fpcr &= ~MASK_ROUNDING_MODE; case ROUND_TO_NEAREST:
#endif float_rounding_mode = float_round_nearest_even;
switch (opcode & MASK_ROUNDING_MODE) break;
{
default: case ROUND_TO_PLUS_INFINITY:
case ROUND_TO_NEAREST: float_rounding_mode = float_round_up;
float_rounding_mode = float_round_nearest_even; break;
#if MAINTAIN_FPCR
fpa11->fpcr |= ROUND_TO_NEAREST; case ROUND_TO_MINUS_INFINITY:
#endif float_rounding_mode = float_round_down;
break; break;
case ROUND_TO_PLUS_INFINITY: case ROUND_TO_ZERO:
float_rounding_mode = float_round_up; float_rounding_mode = float_round_to_zero;
#if MAINTAIN_FPCR break;
fpa11->fpcr |= ROUND_TO_PLUS_INFINITY; }
#endif
break;
case ROUND_TO_MINUS_INFINITY:
float_rounding_mode = float_round_down;
#if MAINTAIN_FPCR
fpa11->fpcr |= ROUND_TO_MINUS_INFINITY;
#endif
break;
case ROUND_TO_ZERO:
float_rounding_mode = float_round_to_zero;
#if MAINTAIN_FPCR
fpa11->fpcr |= ROUND_TO_ZERO;
#endif
break;
}
} }
void SetRoundingPrecision(const unsigned int opcode) void SetRoundingPrecision(const unsigned int opcode)
{ {
#if MAINTAIN_FPCR #ifdef CONFIG_FPE_NWFPE_XP
FPA11 *fpa11 = GET_FPA11(); switch (opcode & MASK_ROUNDING_PRECISION) {
fpa11->fpcr &= ~MASK_ROUNDING_PRECISION; case ROUND_SINGLE:
#endif floatx80_rounding_precision = 32;
switch (opcode & MASK_ROUNDING_PRECISION) break;
{
case ROUND_SINGLE: case ROUND_DOUBLE:
floatx80_rounding_precision = 32; floatx80_rounding_precision = 64;
#if MAINTAIN_FPCR break;
fpa11->fpcr |= ROUND_SINGLE;
#endif case ROUND_EXTENDED:
break; floatx80_rounding_precision = 80;
break;
case ROUND_DOUBLE:
floatx80_rounding_precision = 64; default:
#if MAINTAIN_FPCR floatx80_rounding_precision = 80;
fpa11->fpcr |= ROUND_DOUBLE; }
#endif #endif
break;
case ROUND_EXTENDED:
floatx80_rounding_precision = 80;
#if MAINTAIN_FPCR
fpa11->fpcr |= ROUND_EXTENDED;
#endif
break;
default: floatx80_rounding_precision = 80;
}
} }
void nwfpe_init(union fp_state *fp) void nwfpe_init_fpa(union fp_state *fp)
{ {
FPA11 *fpa11 = (FPA11 *)fp; FPA11 *fpa11 = (FPA11 *)fp;
memset(fpa11, 0, sizeof(FPA11)); #ifdef NWFPE_DEBUG
resetFPA11(); printk("NWFPE: setting up state.\n");
SetRoundingMode(ROUND_TO_NEAREST); #endif
SetRoundingPrecision(ROUND_EXTENDED); memset(fpa11, 0, sizeof(FPA11));
fpa11->initflag = 1; resetFPA11();
SetRoundingMode(ROUND_TO_NEAREST);
SetRoundingPrecision(ROUND_EXTENDED);
fpa11->initflag = 1;
} }
/* Emulate the instruction in the opcode. */ /* Emulate the instruction in the opcode. */
unsigned int EmulateAll(unsigned int opcode) unsigned int EmulateAll(unsigned int opcode)
{ {
unsigned int nRc = 1, code; unsigned int code;
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. */
nRc = EmulateCPRT(opcode);
}
else
{
/* Emulate monadic arithmetic opcodes. */
/* Emulate dyadic arithmetic opcodes. */
nRc = EmulateCPDO(opcode);
}
}
else if (code == 0x0c000000)
{
/* Emulate load/store opcodes. */
/* Emulate load/store multiple opcodes. */
nRc = EmulateCPDT(opcode);
}
else
{
/* Invalid instruction detected. Return FALSE. */
nRc = 0;
}
}
return(nRc);
}
#if 0 #ifdef NWFPE_DEBUG
unsigned int EmulateAll1(unsigned int opcode) printk("NWFPE: emulating opcode %08x\n", opcode);
{
switch ((opcode >> 24) & 0xf)
{
case 0xc:
case 0xd:
if ((opcode >> 20) & 0x1)
{
switch ((opcode >> 8) & 0xf)
{
case 0x1: return PerformLDF(opcode); break;
case 0x2: return PerformLFM(opcode); break;
default: return 0;
}
}
else
{
switch ((opcode >> 8) & 0xf)
{
case 0x1: return PerformSTF(opcode); break;
case 0x2: return PerformSFM(opcode); break;
default: return 0;
}
}
break;
case 0xe:
if (opcode & 0x10)
return EmulateCPDO(opcode);
else
return EmulateCPRT(opcode);
break;
default: return 0;
}
}
#endif #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;
}
...@@ -37,6 +37,7 @@ register unsigned int *user_registers asm("sl"); ...@@ -37,6 +37,7 @@ register unsigned int *user_registers asm("sl");
/* includes */ /* includes */
#include "fpsr.h" /* FP control and status register definitions */ #include "fpsr.h" /* FP control and status register definitions */
#include "milieu.h"
#include "softfloat.h" #include "softfloat.h"
#define typeNone 0x00 #define typeNone 0x00
...@@ -48,9 +49,13 @@ register unsigned int *user_registers asm("sl"); ...@@ -48,9 +49,13 @@ register unsigned int *user_registers asm("sl");
* This must be no more and no less than 12 bytes. * This must be no more and no less than 12 bytes.
*/ */
typedef union tagFPREG { typedef union tagFPREG {
floatx80 fExtended; float32 fSingle;
float64 fDouble; float64 fDouble;
float32 fSingle; #ifdef CONFIG_FPE_NWFPE_XP
floatx80 fExtended;
#else
int padding[3];
#endif
} FPREG; } FPREG;
/* /*
...@@ -67,21 +72,21 @@ typedef union tagFPREG { ...@@ -67,21 +72,21 @@ typedef union tagFPREG {
* not initialise. * not initialise.
*/ */
typedef struct tagFPA11 { typedef struct tagFPA11 {
/* 0 */ FPREG fpreg[8]; /* 8 floating point registers */ /* 0 */ FPREG fpreg[8]; /* 8 floating point registers */
/* 96 */ FPSR fpsr; /* floating point status register */ /* 96 */ FPSR fpsr; /* floating point status register */
/* 100 */ FPCR fpcr; /* floating point control register */ /* 100 */ FPCR fpcr; /* floating point control register */
/* 104 */ unsigned char fType[8]; /* type of floating point value held in /* 104 */ unsigned char fType[8]; /* type of floating point value held in
floating point registers. One of none floating point registers. One of
single, double or extended. */ none, single, double or extended. */
/* 112 */ int initflag; /* this is special. The kernel guarantees /* 112 */ int initflag; /* this is special. The kernel guarantees
to set it to 0 when a thread is launched, to set it to 0 when a thread is launched,
so we can use it to detect whether this so we can use it to detect whether this
instance of the emulator needs to be instance of the emulator needs to be
initialised. */ initialised. */
} FPA11; } FPA11;
extern void SetRoundingMode(const unsigned int); extern void SetRoundingMode(const unsigned int);
extern void SetRoundingPrecision(const unsigned int); extern void SetRoundingPrecision(const unsigned int);
extern void nwfpe_init(union fp_state *fp); extern void nwfpe_init_fpa(union fp_state *fp);
#endif #endif
...@@ -24,28 +24,28 @@ ...@@ -24,28 +24,28 @@
/* Read and write floating point status register */ /* Read and write floating point status register */
extern __inline__ unsigned int readFPSR(void) extern __inline__ unsigned int readFPSR(void)
{ {
FPA11 *fpa11 = GET_FPA11(); FPA11 *fpa11 = GET_FPA11();
return(fpa11->fpsr); return (fpa11->fpsr);
} }
extern __inline__ void writeFPSR(FPSR reg) extern __inline__ void writeFPSR(FPSR reg)
{ {
FPA11 *fpa11 = GET_FPA11(); FPA11 *fpa11 = GET_FPA11();
/* the sysid byte in the status register is readonly */ /* the sysid byte in the status register is readonly */
fpa11->fpsr = (fpa11->fpsr & MASK_SYSID) | (reg & ~MASK_SYSID); fpa11->fpsr = (fpa11->fpsr & MASK_SYSID) | (reg & ~MASK_SYSID);
} }
/* Read and write floating point control register */ /* Read and write floating point control register */
extern __inline__ FPCR readFPCR(void) extern __inline__ FPCR readFPCR(void)
{ {
FPA11 *fpa11 = GET_FPA11(); FPA11 *fpa11 = GET_FPA11();
/* clear SB, AB and DA bits before returning FPCR */ /* clear SB, AB and DA bits before returning FPCR */
return(fpa11->fpcr & ~MASK_RFC); return (fpa11->fpcr & ~MASK_RFC);
} }
extern __inline__ void writeFPCR(FPCR reg) extern __inline__ void writeFPCR(FPCR reg)
{ {
FPA11 *fpa11 = GET_FPA11(); FPA11 *fpa11 = GET_FPA11();
fpa11->fpcr &= ~MASK_WFC; /* clear SB, AB and DA bits */ fpa11->fpcr &= ~MASK_WFC; /* clear SB, AB and DA bits */
fpa11->fpcr |= (reg & MASK_WFC); /* write SB, AB and DA bits */ fpa11->fpcr |= (reg & MASK_WFC); /* write SB, AB and DA bits */
} }
/* /*
NetWinder Floating Point Emulator NetWinder Floating Point Emulator
(c) Rebel.COM, 1998,1999 (c) Rebel.COM, 1998,1999
(c) Philip Blundell, 2001
Direct questions, comments to Scott Bambrough <scottb@netwinder.org> Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
...@@ -22,96 +23,109 @@ ...@@ -22,96 +23,109 @@
#include "fpa11.h" #include "fpa11.h"
#include "fpopcode.h" #include "fpopcode.h"
unsigned int SingleCPDO(const unsigned int opcode); unsigned int SingleCPDO(const unsigned int opcode, FPREG * rFd);
unsigned int DoubleCPDO(const unsigned int opcode); unsigned int DoubleCPDO(const unsigned int opcode, FPREG * rFd);
unsigned int ExtendedCPDO(const unsigned int opcode); unsigned int ExtendedCPDO(const unsigned int opcode, FPREG * rFd);
unsigned int EmulateCPDO(const unsigned int opcode) unsigned int EmulateCPDO(const unsigned int opcode)
{ {
FPA11 *fpa11 = GET_FPA11(); FPA11 *fpa11 = GET_FPA11();
unsigned int Fd, nType, nDest, nRc = 1; FPREG *rFd;
unsigned int nType, nDest, nRc;
//printk("EmulateCPDO(0x%08x)\n",opcode);
/* Get the destination size. If not valid let Linux perform
/* Get the destination size. If not valid let Linux perform an invalid instruction trap. */
an invalid instruction trap. */ nDest = getDestinationSize(opcode);
nDest = getDestinationSize(opcode); if (typeNone == nDest)
if (typeNone == nDest) return 0; return 0;
SetRoundingMode(opcode); SetRoundingMode(opcode);
/* Compare the size of the operands in Fn and Fm. /* Compare the size of the operands in Fn and Fm.
Choose the largest size and perform operations in that size, Choose the largest size and perform operations in that size,
in order to make use of all the precision of the operands. 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 If Fm is a constant, we just grab a constant of a size
matching the size of the operand in Fn. */ matching the size of the operand in Fn. */
if (MONADIC_INSTRUCTION(opcode)) if (MONADIC_INSTRUCTION(opcode))
nType = nDest; nType = nDest;
else else
nType = fpa11->fType[getFn(opcode)]; nType = fpa11->fType[getFn(opcode)];
if (!CONSTANT_FM(opcode)) if (!CONSTANT_FM(opcode)) {
{ register unsigned int Fm = getFm(opcode);
register unsigned int Fm = getFm(opcode); if (nType < fpa11->fType[Fm]) {
if (nType < fpa11->fType[Fm]) nType = fpa11->fType[Fm];
{ }
nType = fpa11->fType[Fm]; }
}
} rFd = &fpa11->fpreg[getFd(opcode)];
switch (nType) switch (nType) {
{ case typeSingle:
case typeSingle : nRc = SingleCPDO(opcode); break; nRc = SingleCPDO(opcode, rFd);
case typeDouble : nRc = DoubleCPDO(opcode); break; break;
case typeExtended : nRc = ExtendedCPDO(opcode); break; case typeDouble:
default : nRc = 0; nRc = DoubleCPDO(opcode, rFd);
} break;
#ifdef CONFIG_FPE_NWFPE_XP
/* If the operation succeeded, check to see if the result in the case typeExtended:
destination register is the correct size. If not force it nRc = ExtendedCPDO(opcode, rFd);
to be. */ break;
Fd = getFd(opcode); #endif
nType = fpa11->fType[Fd]; default:
if ((0 != nRc) && (nDest != nType)) nRc = 0;
{ }
switch (nDest)
{ /* The CPDO functions used to always set the destination type
case typeSingle: to be the same as their working size. */
{
if (typeDouble == nType) if (nRc != 0) {
fpa11->fpreg[Fd].fSingle = /* If the operation succeeded, check to see if the result in the
float64_to_float32(fpa11->fpreg[Fd].fDouble); destination register is the correct size. If not force it
else to be. */
fpa11->fpreg[Fd].fSingle =
floatx80_to_float32(fpa11->fpreg[Fd].fExtended); fpa11->fType[getFd(opcode)] = nDest;
}
break; #ifdef CONFIG_FPE_NWFPE_XP
if (nDest != nType) {
case typeDouble: switch (nDest) {
{ case typeSingle:
if (typeSingle == nType) {
fpa11->fpreg[Fd].fDouble = if (typeDouble == nType)
float32_to_float64(fpa11->fpreg[Fd].fSingle); rFd->fSingle = float64_to_float32(rFd->fDouble);
else else
fpa11->fpreg[Fd].fDouble = rFd->fSingle = floatx80_to_float32(rFd->fExtended);
floatx80_to_float64(fpa11->fpreg[Fd].fExtended); }
} break;
break;
case typeDouble:
case typeExtended: {
{ if (typeSingle == nType)
if (typeSingle == nType) rFd->fDouble = float32_to_float64(rFd->fSingle);
fpa11->fpreg[Fd].fExtended = else
float32_to_floatx80(fpa11->fpreg[Fd].fSingle); rFd->fDouble = floatx80_to_float64(rFd->fExtended);
else }
fpa11->fpreg[Fd].fExtended = break;
float64_to_floatx80(fpa11->fpreg[Fd].fDouble);
} case typeExtended:
break; {
} if (typeSingle == nType)
rFd->fExtended = float32_to_floatx80(rFd->fSingle);
fpa11->fType[Fd] = nDest; else
} rFd->fExtended = float64_to_floatx80(rFd->fDouble);
}
return nRc; break;
}
}
#else
if (nDest != nType) {
if (nDest == typeSingle)
rFd->fSingle = float64_to_float32(rFd->fDouble);
else
rFd->fDouble = float32_to_float64(rFd->fSingle);
}
#endif
}
return nRc;
} }
/* /*
NetWinder Floating Point Emulator NetWinder Floating Point Emulator
(c) Rebel.com, 1998-1999 (c) Rebel.com, 1998-1999
(c) Philip Blundell, 1998 (c) Philip Blundell, 1998, 2001
Direct questions, comments to Scott Bambrough <scottb@netwinder.org> Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
...@@ -28,341 +28,354 @@ ...@@ -28,341 +28,354 @@
#include <asm/uaccess.h> #include <asm/uaccess.h>
static inline static inline void loadSingle(const unsigned int Fn, const unsigned int *pMem)
void loadSingle(const unsigned int Fn,const unsigned int *pMem)
{ {
FPA11 *fpa11 = GET_FPA11(); FPA11 *fpa11 = GET_FPA11();
fpa11->fType[Fn] = typeSingle; fpa11->fType[Fn] = typeSingle;
get_user(fpa11->fpreg[Fn].fSingle, pMem); get_user(fpa11->fpreg[Fn].fSingle, pMem);
} }
static inline static inline void loadDouble(const unsigned int Fn, const unsigned int *pMem)
void loadDouble(const unsigned int Fn,const unsigned int *pMem)
{ {
FPA11 *fpa11 = GET_FPA11(); FPA11 *fpa11 = GET_FPA11();
unsigned int *p; unsigned int *p;
p = (unsigned int*)&fpa11->fpreg[Fn].fDouble; p = (unsigned int *) &fpa11->fpreg[Fn].fDouble;
fpa11->fType[Fn] = typeDouble; fpa11->fType[Fn] = typeDouble;
get_user(p[0], &pMem[1]); get_user(p[0], &pMem[1]);
get_user(p[1], &pMem[0]); /* sign & exponent */ get_user(p[1], &pMem[0]); /* sign & exponent */
} }
static inline #ifdef CONFIG_FPE_NWFPE_XP
void loadExtended(const unsigned int Fn,const unsigned int *pMem) static inline void loadExtended(const unsigned int Fn, const unsigned int *pMem)
{ {
FPA11 *fpa11 = GET_FPA11(); FPA11 *fpa11 = GET_FPA11();
unsigned int *p; unsigned int *p;
p = (unsigned int*)&fpa11->fpreg[Fn].fExtended; p = (unsigned int *) &fpa11->fpreg[Fn].fExtended;
fpa11->fType[Fn] = typeExtended; fpa11->fType[Fn] = typeExtended;
get_user(p[0], &pMem[0]); /* sign & exponent */ get_user(p[0], &pMem[0]); /* sign & exponent */
get_user(p[1], &pMem[2]); /* ls bits */ get_user(p[1], &pMem[2]); /* ls bits */
get_user(p[2], &pMem[1]); /* ms bits */ get_user(p[2], &pMem[1]); /* ms bits */
} }
#endif
static inline static inline void loadMultiple(const unsigned int Fn, const unsigned int *pMem)
void loadMultiple(const unsigned int Fn,const unsigned int *pMem)
{ {
FPA11 *fpa11 = GET_FPA11(); FPA11 *fpa11 = GET_FPA11();
register unsigned int *p; register unsigned int *p;
unsigned long x; unsigned long x;
p = (unsigned int*)&(fpa11->fpreg[Fn]); p = (unsigned int *) &(fpa11->fpreg[Fn]);
get_user(x, &pMem[0]); get_user(x, &pMem[0]);
fpa11->fType[Fn] = (x >> 14) & 0x00000003; fpa11->fType[Fn] = (x >> 14) & 0x00000003;
switch (fpa11->fType[Fn]) switch (fpa11->fType[Fn]) {
{ case typeSingle:
case typeSingle: case typeDouble:
case typeDouble: {
{ get_user(p[0], &pMem[2]); /* Single */
get_user(p[0], &pMem[2]); /* Single */ get_user(p[1], &pMem[1]); /* double msw */
get_user(p[1], &pMem[1]); /* double msw */ p[2] = 0; /* empty */
p[2] = 0; /* empty */ }
} break;
break;
#ifdef CONFIG_FPE_NWFPE_XP
case typeExtended: case typeExtended:
{ {
get_user(p[1], &pMem[2]); get_user(p[1], &pMem[2]);
get_user(p[2], &pMem[1]); /* msw */ get_user(p[2], &pMem[1]); /* msw */
p[0] = (x & 0x80003fff); p[0] = (x & 0x80003fff);
} }
break; break;
} #endif
}
} }
static inline static inline void storeSingle(const unsigned int Fn, unsigned int *pMem)
void storeSingle(const unsigned int Fn,unsigned int *pMem)
{ {
FPA11 *fpa11 = GET_FPA11(); FPA11 *fpa11 = GET_FPA11();
union union {
{ float32 f;
float32 f; unsigned int i[1];
unsigned int i[1]; } val;
} val;
switch (fpa11->fType[Fn]) {
switch (fpa11->fType[Fn]) case typeDouble:
{ val.f = float64_to_float32(fpa11->fpreg[Fn].fDouble);
case typeDouble: break;
val.f = float64_to_float32(fpa11->fpreg[Fn].fDouble);
break; #ifdef CONFIG_FPE_NWFPE_XP
case typeExtended:
case typeExtended: val.f = floatx80_to_float32(fpa11->fpreg[Fn].fExtended);
val.f = floatx80_to_float32(fpa11->fpreg[Fn].fExtended); break;
break; #endif
default: val.f = fpa11->fpreg[Fn].fSingle; default:
} val.f = fpa11->fpreg[Fn].fSingle;
}
put_user(val.i[0], pMem);
put_user(val.i[0], pMem);
} }
static inline static inline void storeDouble(const unsigned int Fn, unsigned int *pMem)
void storeDouble(const unsigned int Fn,unsigned int *pMem)
{ {
FPA11 *fpa11 = GET_FPA11(); FPA11 *fpa11 = GET_FPA11();
union union {
{ float64 f;
float64 f; unsigned int i[2];
unsigned int i[2]; } val;
} val;
switch (fpa11->fType[Fn]) {
switch (fpa11->fType[Fn]) case typeSingle:
{ val.f = float32_to_float64(fpa11->fpreg[Fn].fSingle);
case typeSingle: break;
val.f = float32_to_float64(fpa11->fpreg[Fn].fSingle);
break; #ifdef CONFIG_FPE_NWFPE_XP
case typeExtended:
case typeExtended: val.f = floatx80_to_float64(fpa11->fpreg[Fn].fExtended);
val.f = floatx80_to_float64(fpa11->fpreg[Fn].fExtended); break;
break; #endif
default: val.f = fpa11->fpreg[Fn].fDouble; default:
} val.f = fpa11->fpreg[Fn].fDouble;
}
put_user(val.i[1], &pMem[0]); /* msw */
put_user(val.i[0], &pMem[1]); /* lsw */ put_user(val.i[1], &pMem[0]); /* msw */
put_user(val.i[0], &pMem[1]); /* lsw */
} }
static inline #ifdef CONFIG_FPE_NWFPE_XP
void storeExtended(const unsigned int Fn,unsigned int *pMem) static inline void storeExtended(const unsigned int Fn, unsigned int *pMem)
{ {
FPA11 *fpa11 = GET_FPA11(); FPA11 *fpa11 = GET_FPA11();
union union {
{ floatx80 f;
floatx80 f; unsigned int i[3];
unsigned int i[3]; } val;
} val;
switch (fpa11->fType[Fn]) {
switch (fpa11->fType[Fn]) case typeSingle:
{ val.f = float32_to_floatx80(fpa11->fpreg[Fn].fSingle);
case typeSingle: break;
val.f = float32_to_floatx80(fpa11->fpreg[Fn].fSingle);
break; case typeDouble:
val.f = float64_to_floatx80(fpa11->fpreg[Fn].fDouble);
case typeDouble: break;
val.f = float64_to_floatx80(fpa11->fpreg[Fn].fDouble);
break; default:
val.f = fpa11->fpreg[Fn].fExtended;
default: val.f = fpa11->fpreg[Fn].fExtended; }
}
put_user(val.i[0], &pMem[0]); /* sign & exp */
put_user(val.i[0], &pMem[0]); /* sign & exp */ put_user(val.i[1], &pMem[2]);
put_user(val.i[1], &pMem[2]); put_user(val.i[2], &pMem[1]); /* msw */
put_user(val.i[2], &pMem[1]); /* msw */
} }
#endif
static inline static inline void storeMultiple(const unsigned int Fn, unsigned int *pMem)
void storeMultiple(const unsigned int Fn,unsigned int *pMem)
{ {
FPA11 *fpa11 = GET_FPA11(); FPA11 *fpa11 = GET_FPA11();
register unsigned int nType, *p; register unsigned int nType, *p;
p = (unsigned int*)&(fpa11->fpreg[Fn]); p = (unsigned int *) &(fpa11->fpreg[Fn]);
nType = fpa11->fType[Fn]; nType = fpa11->fType[Fn];
switch (nType) switch (nType) {
{ case typeSingle:
case typeSingle: case typeDouble:
case typeDouble: {
{ put_user(p[0], &pMem[2]); /* single */
put_user(p[0], &pMem[2]); /* single */ put_user(p[1], &pMem[1]); /* double msw */
put_user(p[1], &pMem[1]); /* double msw */ put_user(nType << 14, &pMem[0]);
put_user(nType << 14, &pMem[0]); }
} break;
break;
#ifdef CONFIG_FPE_NWFPE_XP
case typeExtended: case typeExtended:
{ {
put_user(p[2], &pMem[1]); /* msw */ put_user(p[2], &pMem[1]); /* msw */
put_user(p[1], &pMem[2]); put_user(p[1], &pMem[2]);
put_user((p[0] & 0x80003fff) | (nType << 14), &pMem[0]); put_user((p[0] & 0x80003fff) | (nType << 14), &pMem[0]);
} }
break; break;
} #endif
}
} }
unsigned int PerformLDF(const unsigned int opcode) unsigned int PerformLDF(const unsigned int opcode)
{ {
unsigned int *pBase, *pAddress, *pFinal, nRc = 1, unsigned int *pBase, *pAddress, *pFinal, nRc = 1,
write_back = WRITE_BACK(opcode); write_back = WRITE_BACK(opcode);
//printk("PerformLDF(0x%08x), Fd = 0x%08x\n",opcode,getFd(opcode)); pBase = (unsigned int *) readRegister(getRn(opcode));
if (REG_PC == getRn(opcode)) {
pBase = (unsigned int*)readRegister(getRn(opcode)); pBase += 2;
if (REG_PC == getRn(opcode)) write_back = 0;
{ }
pBase += 2;
write_back = 0; pFinal = pBase;
} if (BIT_UP_SET(opcode))
pFinal += getOffset(opcode);
pFinal = pBase; else
if (BIT_UP_SET(opcode)) pFinal -= getOffset(opcode);
pFinal += getOffset(opcode);
else if (PREINDEXED(opcode))
pFinal -= getOffset(opcode); pAddress = pFinal;
else
if (PREINDEXED(opcode)) pAddress = pFinal; else pAddress = pBase; pAddress = pBase;
switch (opcode & MASK_TRANSFER_LENGTH) switch (opcode & MASK_TRANSFER_LENGTH) {
{ case TRANSFER_SINGLE:
case TRANSFER_SINGLE : loadSingle(getFd(opcode),pAddress); break; loadSingle(getFd(opcode), pAddress);
case TRANSFER_DOUBLE : loadDouble(getFd(opcode),pAddress); break; break;
case TRANSFER_EXTENDED: loadExtended(getFd(opcode),pAddress); break; case TRANSFER_DOUBLE:
default: nRc = 0; loadDouble(getFd(opcode), pAddress);
} break;
#ifdef CONFIG_FPE_NWFPE_XP
if (write_back) writeRegister(getRn(opcode),(unsigned int)pFinal); case TRANSFER_EXTENDED:
return nRc; loadExtended(getFd(opcode), pAddress);
break;
#endif
default:
nRc = 0;
}
if (write_back)
writeRegister(getRn(opcode), (unsigned int) pFinal);
return nRc;
} }
unsigned int PerformSTF(const unsigned int opcode) unsigned int PerformSTF(const unsigned int opcode)
{ {
unsigned int *pBase, *pAddress, *pFinal, nRc = 1, unsigned int *pBase, *pAddress, *pFinal, nRc = 1,
write_back = WRITE_BACK(opcode); write_back = WRITE_BACK(opcode);
//printk("PerformSTF(0x%08x), Fd = 0x%08x\n",opcode,getFd(opcode)); SetRoundingMode(ROUND_TO_NEAREST);
SetRoundingMode(ROUND_TO_NEAREST);
pBase = (unsigned int *) readRegister(getRn(opcode));
pBase = (unsigned int*)readRegister(getRn(opcode)); if (REG_PC == getRn(opcode)) {
if (REG_PC == getRn(opcode)) pBase += 2;
{ write_back = 0;
pBase += 2; }
write_back = 0;
} pFinal = pBase;
if (BIT_UP_SET(opcode))
pFinal = pBase; pFinal += getOffset(opcode);
if (BIT_UP_SET(opcode)) else
pFinal += getOffset(opcode); pFinal -= getOffset(opcode);
else
pFinal -= getOffset(opcode); if (PREINDEXED(opcode))
pAddress = pFinal;
if (PREINDEXED(opcode)) pAddress = pFinal; else pAddress = pBase; else
pAddress = pBase;
switch (opcode & MASK_TRANSFER_LENGTH)
{ switch (opcode & MASK_TRANSFER_LENGTH) {
case TRANSFER_SINGLE : storeSingle(getFd(opcode),pAddress); break; case TRANSFER_SINGLE:
case TRANSFER_DOUBLE : storeDouble(getFd(opcode),pAddress); break; storeSingle(getFd(opcode), pAddress);
case TRANSFER_EXTENDED: storeExtended(getFd(opcode),pAddress); break; break;
default: nRc = 0; case TRANSFER_DOUBLE:
} storeDouble(getFd(opcode), pAddress);
break;
if (write_back) writeRegister(getRn(opcode),(unsigned int)pFinal); #ifdef CONFIG_FPE_NWFPE_XP
return nRc; case TRANSFER_EXTENDED:
storeExtended(getFd(opcode), pAddress);
break;
#endif
default:
nRc = 0;
}
if (write_back)
writeRegister(getRn(opcode), (unsigned int) pFinal);
return nRc;
} }
unsigned int PerformLFM(const unsigned int opcode) unsigned int PerformLFM(const unsigned int opcode)
{ {
unsigned int i, Fd, *pBase, *pAddress, *pFinal, unsigned int i, Fd, *pBase, *pAddress, *pFinal,
write_back = WRITE_BACK(opcode); write_back = WRITE_BACK(opcode);
pBase = (unsigned int*)readRegister(getRn(opcode)); pBase = (unsigned int *) readRegister(getRn(opcode));
if (REG_PC == getRn(opcode)) if (REG_PC == getRn(opcode)) {
{ pBase += 2;
pBase += 2; write_back = 0;
write_back = 0; }
}
pFinal = pBase;
pFinal = pBase; if (BIT_UP_SET(opcode))
if (BIT_UP_SET(opcode)) pFinal += getOffset(opcode);
pFinal += getOffset(opcode); else
else pFinal -= getOffset(opcode);
pFinal -= getOffset(opcode);
if (PREINDEXED(opcode))
if (PREINDEXED(opcode)) pAddress = pFinal; else pAddress = pBase; pAddress = pFinal;
else
Fd = getFd(opcode); pAddress = pBase;
for (i=getRegisterCount(opcode);i>0;i--)
{ Fd = getFd(opcode);
loadMultiple(Fd,pAddress); for (i = getRegisterCount(opcode); i > 0; i--) {
pAddress += 3; Fd++; loadMultiple(Fd, pAddress);
if (Fd == 8) Fd = 0; pAddress += 3;
} Fd++;
if (Fd == 8)
if (write_back) writeRegister(getRn(opcode),(unsigned int)pFinal); Fd = 0;
return 1; }
if (write_back)
writeRegister(getRn(opcode), (unsigned int) pFinal);
return 1;
} }
unsigned int PerformSFM(const unsigned int opcode) unsigned int PerformSFM(const unsigned int opcode)
{ {
unsigned int i, Fd, *pBase, *pAddress, *pFinal, unsigned int i, Fd, *pBase, *pAddress, *pFinal,
write_back = WRITE_BACK(opcode); write_back = WRITE_BACK(opcode);
pBase = (unsigned int*)readRegister(getRn(opcode)); pBase = (unsigned int *) readRegister(getRn(opcode));
if (REG_PC == getRn(opcode)) if (REG_PC == getRn(opcode)) {
{ pBase += 2;
pBase += 2; write_back = 0;
write_back = 0; }
}
pFinal = pBase;
pFinal = pBase; if (BIT_UP_SET(opcode))
if (BIT_UP_SET(opcode)) pFinal += getOffset(opcode);
pFinal += getOffset(opcode); else
else pFinal -= getOffset(opcode);
pFinal -= getOffset(opcode);
if (PREINDEXED(opcode))
if (PREINDEXED(opcode)) pAddress = pFinal; else pAddress = pBase; pAddress = pFinal;
else
Fd = getFd(opcode); pAddress = pBase;
for (i=getRegisterCount(opcode);i>0;i--)
{ Fd = getFd(opcode);
storeMultiple(Fd,pAddress); for (i = getRegisterCount(opcode); i > 0; i--) {
pAddress += 3; Fd++; storeMultiple(Fd, pAddress);
if (Fd == 8) Fd = 0; pAddress += 3;
} Fd++;
if (Fd == 8)
if (write_back) writeRegister(getRn(opcode),(unsigned int)pFinal); Fd = 0;
return 1; }
if (write_back)
writeRegister(getRn(opcode), (unsigned int) pFinal);
return 1;
} }
#if 1
unsigned int EmulateCPDT(const unsigned int opcode) unsigned int EmulateCPDT(const unsigned int opcode)
{ {
unsigned int nRc = 0; unsigned int nRc = 0;
//printk("EmulateCPDT(0x%08x)\n",opcode); if (LDF_OP(opcode)) {
nRc = PerformLDF(opcode);
if (LDF_OP(opcode)) } else if (LFM_OP(opcode)) {
{ nRc = PerformLFM(opcode);
nRc = PerformLDF(opcode); } else if (STF_OP(opcode)) {
} nRc = PerformSTF(opcode);
else if (LFM_OP(opcode)) } else if (SFM_OP(opcode)) {
{ nRc = PerformSFM(opcode);
nRc = PerformLFM(opcode); } else {
} nRc = 0;
else if (STF_OP(opcode)) }
{
nRc = PerformSTF(opcode); return nRc;
}
else if (SFM_OP(opcode))
{
nRc = PerformSFM(opcode);
}
else
{
nRc = 0;
}
return nRc;
} }
#endif
/* /*
NetWinder Floating Point Emulator NetWinder Floating Point Emulator
(c) Rebel.COM, 1998,1999 (c) Rebel.COM, 1998,1999
(c) Philip Blundell, 1999 (c) Philip Blundell, 1999, 2001
Direct questions, comments to Scott Bambrough <scottb@netwinder.org> Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
...@@ -21,269 +21,348 @@ ...@@ -21,269 +21,348 @@
*/ */
#include "fpa11.h" #include "fpa11.h"
#include "milieu.h"
#include "softfloat.h"
#include "fpopcode.h" #include "fpopcode.h"
#include "fpa11.inl" #include "fpa11.inl"
#include "fpmodule.h" #include "fpmodule.h"
#include "fpmodule.inl" #include "fpmodule.inl"
#ifdef CONFIG_FPE_NWFPE_XP
extern flag floatx80_is_nan(floatx80); extern flag floatx80_is_nan(floatx80);
extern flag float64_is_nan( float64); #endif
extern flag float32_is_nan( float32); extern flag float64_is_nan(float64);
extern flag float32_is_nan(float32);
void SetRoundingMode(const unsigned int opcode); void SetRoundingMode(const unsigned int opcode);
unsigned int PerformFLT(const unsigned int opcode); unsigned int PerformFLT(const unsigned int opcode);
unsigned int PerformFIX(const unsigned int opcode); unsigned int PerformFIX(const unsigned int opcode);
static unsigned int static unsigned int PerformComparison(const unsigned int opcode);
PerformComparison(const unsigned int opcode);
unsigned int EmulateCPRT(const unsigned int opcode) unsigned int EmulateCPRT(const unsigned int opcode)
{ {
unsigned int nRc = 1;
//printk("EmulateCPRT(0x%08x)\n",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: nRc = PerformFLT(opcode); break;
case FIX_CODE >> 20: nRc = PerformFIX(opcode); break;
case WFS_CODE >> 20: writeFPSR(readRegister(getRd(opcode))); break;
case RFS_CODE >> 20: writeRegister(getRd(opcode),readFPSR()); break;
#if 0 /* We currently have no use for the FPCR, so there's no point
in emulating it. */
case WFC_CODE >> 20: writeFPCR(readRegister(getRd(opcode)));
case RFC_CODE >> 20: writeRegister(getRd(opcode),readFPCR()); break;
#endif
default: nRc = 0; if (opcode & 0x800000) {
} /* This is some variant of a comparison (PerformComparison
will sort out which one). Since most of the other CPRT
return nRc; 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) unsigned int PerformFLT(const unsigned int opcode)
{ {
FPA11 *fpa11 = GET_FPA11(); FPA11 *fpa11 = GET_FPA11();
SetRoundingMode(opcode);
unsigned int nRc = 1; SetRoundingPrecision(opcode);
SetRoundingMode(opcode);
switch (opcode & MASK_ROUNDING_PRECISION) {
switch (opcode & MASK_ROUNDING_PRECISION) case ROUND_SINGLE:
{ {
case ROUND_SINGLE: fpa11->fType[getFn(opcode)] = typeSingle;
{ fpa11->fpreg[getFn(opcode)].fSingle = int32_to_float32(readRegister(getRd(opcode)));
fpa11->fType[getFn(opcode)] = typeSingle; }
fpa11->fpreg[getFn(opcode)].fSingle = break;
int32_to_float32(readRegister(getRd(opcode)));
} case ROUND_DOUBLE:
break; {
fpa11->fType[getFn(opcode)] = typeDouble;
case ROUND_DOUBLE: fpa11->fpreg[getFn(opcode)].fDouble = int32_to_float64(readRegister(getRd(opcode)));
{ }
fpa11->fType[getFn(opcode)] = typeDouble; break;
fpa11->fpreg[getFn(opcode)].fDouble =
int32_to_float64(readRegister(getRd(opcode))); #ifdef CONFIG_FPE_NWFPE_XP
} case ROUND_EXTENDED:
break; {
fpa11->fType[getFn(opcode)] = typeExtended;
case ROUND_EXTENDED: fpa11->fpreg[getFn(opcode)].fExtended = int32_to_floatx80(readRegister(getRd(opcode)));
{ }
fpa11->fType[getFn(opcode)] = typeExtended; break;
fpa11->fpreg[getFn(opcode)].fExtended = #endif
int32_to_floatx80(readRegister(getRd(opcode)));
} default:
break; return 0;
}
default: nRc = 0;
} return 1;
return nRc;
} }
unsigned int PerformFIX(const unsigned int opcode) unsigned int PerformFIX(const unsigned int opcode)
{ {
FPA11 *fpa11 = GET_FPA11(); FPA11 *fpa11 = GET_FPA11();
unsigned int nRc = 1; unsigned int Fn = getFm(opcode);
unsigned int Fn = getFm(opcode);
SetRoundingMode(opcode);
SetRoundingMode(opcode);
switch (fpa11->fType[Fn]) {
switch (fpa11->fType[Fn]) case typeSingle:
{ {
case typeSingle: writeRegister(getRd(opcode), float32_to_int32(fpa11->fpreg[Fn].fSingle));
{ }
writeRegister(getRd(opcode), break;
float32_to_int32(fpa11->fpreg[Fn].fSingle));
} case typeDouble:
break; {
writeRegister(getRd(opcode), float64_to_int32(fpa11->fpreg[Fn].fDouble));
case typeDouble: }
{ break;
writeRegister(getRd(opcode),
float64_to_int32(fpa11->fpreg[Fn].fDouble)); #ifdef CONFIG_FPE_NWFPE_XP
} case typeExtended:
break; {
writeRegister(getRd(opcode), floatx80_to_int32(fpa11->fpreg[Fn].fExtended));
case typeExtended: }
{ break;
writeRegister(getRd(opcode), #endif
floatx80_to_int32(fpa11->fpreg[Fn].fExtended));
}
break;
default: nRc = 0;
}
return nRc;
}
default:
static unsigned int __inline__ return 0;
PerformComparisonOperation(floatx80 Fn, floatx80 Fm) }
{
unsigned int flags = 0; return 1;
/* test for less than condition */
if (floatx80_lt(Fn,Fm))
{
flags |= CC_NEGATIVE;
}
/* test for equal condition */
if (floatx80_eq(Fn,Fm))
{
flags |= CC_ZERO;
}
/* test for greater than or equal condition */
if (floatx80_lt(Fm,Fn))
{
flags |= CC_CARRY;
}
writeConditionCodes(flags);
return 1;
} }
/* This instruction sets the flags N, Z, C, V in the FPSR. */ /* This instruction sets the flags N, Z, C, V in the FPSR. */
static unsigned int PerformComparison(const unsigned int opcode) static unsigned int PerformComparison(const unsigned int opcode)
{ {
FPA11 *fpa11 = GET_FPA11(); FPA11 *fpa11 = GET_FPA11();
unsigned int Fn, Fm; unsigned int Fn = getFn(opcode), Fm = getFm(opcode);
floatx80 rFn, rFm; int e_flag = opcode & 0x400000; /* 1 if CxFE */
int e_flag = opcode & 0x400000; /* 1 if CxFE */ int n_flag = opcode & 0x200000; /* 1 if CNxx */
int n_flag = opcode & 0x200000; /* 1 if CNxx */ unsigned int flags = 0;
unsigned int flags = 0;
#ifdef CONFIG_FPE_NWFPE_XP
//printk("PerformComparison(0x%08x)\n",opcode); floatx80 rFn, rFm;
Fn = getFn(opcode); /* Check for unordered condition and convert all operands to 80-bit
Fm = getFm(opcode); format.
?? Might be some mileage in avoiding this conversion if possible.
/* Check for unordered condition and convert all operands to 80-bit Eg, if both operands are 32-bit, detect this and do a 32-bit
format. comparison (cheaper than an 80-bit one). */
?? Might be some mileage in avoiding this conversion if possible. switch (fpa11->fType[Fn]) {
Eg, if both operands are 32-bit, detect this and do a 32-bit case typeSingle:
comparison (cheaper than an 80-bit one). */ //printk("single.\n");
switch (fpa11->fType[Fn]) if (float32_is_nan(fpa11->fpreg[Fn].fSingle))
{ goto unordered;
case typeSingle: rFn = float32_to_floatx80(fpa11->fpreg[Fn].fSingle);
//printk("single.\n"); break;
if (float32_is_nan(fpa11->fpreg[Fn].fSingle))
goto unordered; case typeDouble:
rFn = float32_to_floatx80(fpa11->fpreg[Fn].fSingle); //printk("double.\n");
break; if (float64_is_nan(fpa11->fpreg[Fn].fDouble))
goto unordered;
case typeDouble: rFn = float64_to_floatx80(fpa11->fpreg[Fn].fDouble);
//printk("double.\n"); break;
if (float64_is_nan(fpa11->fpreg[Fn].fDouble))
goto unordered; case typeExtended:
rFn = float64_to_floatx80(fpa11->fpreg[Fn].fDouble); //printk("extended.\n");
break; if (floatx80_is_nan(fpa11->fpreg[Fn].fExtended))
goto unordered;
case typeExtended: rFn = fpa11->fpreg[Fn].fExtended;
//printk("extended.\n"); break;
if (floatx80_is_nan(fpa11->fpreg[Fn].fExtended))
goto unordered; default:
rFn = fpa11->fpreg[Fn].fExtended; return 0;
break; }
default: return 0; if (CONSTANT_FM(opcode)) {
} //printk("Fm is a constant: #%d.\n",Fm);
rFm = getExtendedConstant(Fm);
if (CONSTANT_FM(opcode)) if (floatx80_is_nan(rFm))
{ goto unordered;
//printk("Fm is a constant: #%d.\n",Fm); } else {
rFm = getExtendedConstant(Fm); //printk("Fm = r%d which contains a ",Fm);
if (floatx80_is_nan(rFm)) switch (fpa11->fType[Fm]) {
goto unordered; case typeSingle:
} //printk("single.\n");
else if (float32_is_nan(fpa11->fpreg[Fm].fSingle))
{ goto unordered;
//printk("Fm = r%d which contains a ",Fm); rFm = float32_to_floatx80(fpa11->fpreg[Fm].fSingle);
switch (fpa11->fType[Fm]) break;
{
case typeSingle: case typeDouble:
//printk("single.\n"); //printk("double.\n");
if (float32_is_nan(fpa11->fpreg[Fm].fSingle)) if (float64_is_nan(fpa11->fpreg[Fm].fDouble))
goto unordered; goto unordered;
rFm = float32_to_floatx80(fpa11->fpreg[Fm].fSingle); rFm = float64_to_floatx80(fpa11->fpreg[Fm].fDouble);
break; break;
case typeDouble: case typeExtended:
//printk("double.\n"); //printk("extended.\n");
if (float64_is_nan(fpa11->fpreg[Fm].fDouble)) if (floatx80_is_nan(fpa11->fpreg[Fm].fExtended))
goto unordered; goto unordered;
rFm = float64_to_floatx80(fpa11->fpreg[Fm].fDouble); rFm = fpa11->fpreg[Fm].fExtended;
break; break;
case typeExtended: default:
//printk("extended.\n"); return 0;
if (floatx80_is_nan(fpa11->fpreg[Fm].fExtended)) }
goto unordered; }
rFm = fpa11->fpreg[Fm].fExtended;
break; if (n_flag)
rFm.high ^= 0x8000;
default: return 0;
} /* test for less than condition */
} if (floatx80_lt(rFn, rFm))
flags |= CC_NEGATIVE;
if (n_flag)
{ /* test for equal condition */
rFm.high ^= 0x8000; if (floatx80_eq(rFn, rFm))
} flags |= CC_ZERO;
return PerformComparisonOperation(rFn,rFm); /* test for greater than or equal condition */
if (floatx80_lt(rFm, rFn))
unordered: flags |= CC_CARRY;
/* ?? The FPA data sheet is pretty vague about this, in particular
about whether the non-E comparisons can ever raise exceptions. #else
This implementation is based on a combination of what it says in if (CONSTANT_FM(opcode)) {
the data sheet, observation of how the Acorn emulator actually /* Fm is a constant. Do the comparison in whatever precision
behaves (and how programs expect it to) and guesswork. */ Fn happens to be stored in. */
flags |= CC_OVERFLOW; if (fpa11->fType[Fn] == typeSingle) {
flags &= ~(CC_ZERO | CC_NEGATIVE); float32 rFm = getSingleConstant(Fm);
float32 rFn = fpa11->fpreg[Fn].fSingle;
if (BIT_AC & readFPSR()) flags |= CC_CARRY;
if (float32_is_nan(rFn))
if (e_flag) float_raise(float_flag_invalid); goto unordered;
writeConditionCodes(flags); if (n_flag)
return 1; 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;
} }
...@@ -42,11 +42,17 @@ ...@@ -42,11 +42,17 @@
#include "fpa11.inl" #include "fpa11.inl"
/* kernel symbols required for signal handling */ /* kernel symbols required for signal handling */
#ifdef CONFIG_FPE_NWFPE_XP
#define NWFPE_BITS "extended"
#else
#define NWFPE_BITS "double"
#endif
#ifdef MODULE #ifdef MODULE
void fp_send_sig(unsigned long sig, struct task_struct *p, int priv); void fp_send_sig(unsigned long sig, struct task_struct *p, int priv);
#if LINUX_VERSION_CODE > 0x20115 #if LINUX_VERSION_CODE > 0x20115
MODULE_AUTHOR("Scott Bambrough <scottb@rebel.com>"); MODULE_AUTHOR("Scott Bambrough <scottb@rebel.com>");
MODULE_DESCRIPTION("NWFPE floating point emulator"); MODULE_DESCRIPTION("NWFPE floating point emulator (" NWFPE_BITS " precision)");
#endif #endif
#else #else
...@@ -63,63 +69,45 @@ void fp_setup(void); ...@@ -63,63 +69,45 @@ void fp_setup(void);
extern void (*kern_fp_enter)(void); extern void (*kern_fp_enter)(void);
extern void (*fp_init)(union fp_state *); extern void (*fp_init)(union fp_state *);
/* Original value of fp_enter from kernel before patched by fpe_init. */ /* Original value of fp_enter from kernel before patched by fpe_init. */
static void (*orig_fp_enter)(void); static void (*orig_fp_enter)(void);
static void (*orig_fp_init)(union fp_state *); static void (*orig_fp_init)(union fp_state *);
/* forward declarations */ /* forward declarations */
extern void nwfpe_enter(void); extern void nwfpe_enter(void);
#ifdef MODULE
/*
* Return 0 if we can be unloaded. This can only happen if
* kern_fp_enter is still pointing at nwfpe_enter
*/
static int fpe_unload(void)
{
return (kern_fp_enter == nwfpe_enter) ? 0 : 1;
}
#endif
static int __init fpe_init(void) static int __init fpe_init(void)
{ {
if (sizeof(FPA11) > sizeof(union fp_state)) { if (sizeof(FPA11) > sizeof(union fp_state)) {
printk(KERN_ERR "nwfpe: bad structure size\n"); printk(KERN_ERR "nwfpe: bad structure size\n");
return -EINVAL; return -EINVAL;
} }
if (sizeof(FPREG) != 12) { if (sizeof(FPREG) != 12) {
printk(KERN_ERR "nwfpe: bad register size\n"); printk(KERN_ERR "nwfpe: bad register size\n");
return -EINVAL; return -EINVAL;
} }
if (fpe_type[0] && strcmp(fpe_type, "nwfpe"))
#ifdef MODULE return 0;
if (!mod_member_present(&__this_module, can_unload))
return -EINVAL; /* Display title, version and copyright information. */
__this_module.can_unload = fpe_unload; printk(KERN_WARNING "NetWinder Floating Point Emulator V0.97 ("
#else NWFPE_BITS " precision)\n");
if (fpe_type[0] && strcmp(fpe_type, "nwfpe"))
return 0; /* Save pointer to the old FP handler and then patch ourselves in */
#endif orig_fp_enter = kern_fp_enter;
orig_fp_init = fp_init;
/* Display title, version and copyright information. */ kern_fp_enter = nwfpe_enter;
printk(KERN_WARNING "NetWinder Floating Point Emulator V0.95 " fp_init = nwfpe_init_fpa;
"(c) 1998-1999 Rebel.com\n");
return 0;
/* Save pointer to the old FP handler and then patch ourselves in */
orig_fp_enter = kern_fp_enter;
orig_fp_init = fp_init;
kern_fp_enter = nwfpe_enter;
fp_init = nwfpe_init;
return 0;
} }
static void __exit fpe_exit(void) static void __exit fpe_exit(void)
{ {
/* Restore the values we saved earlier. */ /* Restore the values we saved earlier. */
kern_fp_enter = orig_fp_enter; kern_fp_enter = orig_fp_enter;
fp_init = orig_fp_init; fp_init = orig_fp_init;
} }
/* /*
...@@ -144,41 +132,42 @@ cumulative exceptions flag byte are set and we return. ...@@ -144,41 +132,42 @@ cumulative exceptions flag byte are set and we return.
void float_raise(signed char flags) void float_raise(signed char flags)
{ {
register unsigned int fpsr, cumulativeTraps; register unsigned int fpsr, cumulativeTraps;
#ifdef CONFIG_DEBUG_USER #ifdef CONFIG_DEBUG_USER
printk(KERN_DEBUG "NWFPE: %s[%d] takes exception %08x at %p from %08x\n", printk(KERN_DEBUG
current->comm, current->pid, flags, "NWFPE: %s[%d] takes exception %08x at %p from %08x\n",
__builtin_return_address(0), GET_USERREG()[15]); current->comm, current->pid, flags,
__builtin_return_address(0), GET_USERREG()[15]);
#endif #endif
/* Keep SoftFloat exception flags up to date. */ /* Keep SoftFloat exception flags up to date. */
float_exception_flags |= flags; float_exception_flags |= flags;
/* Read fpsr and initialize the cumulativeTraps. */ /* Read fpsr and initialize the cumulativeTraps. */
fpsr = readFPSR(); fpsr = readFPSR();
cumulativeTraps = 0; cumulativeTraps = 0;
/* For each type of exception, the cumulative trap exception bit is only /* For each type of exception, the cumulative trap exception bit is only
set if the corresponding trap enable bit is not set. */ set if the corresponding trap enable bit is not set. */
if ((!(fpsr & BIT_IXE)) && (flags & BIT_IXC)) if ((!(fpsr & BIT_IXE)) && (flags & BIT_IXC))
cumulativeTraps |= BIT_IXC; cumulativeTraps |= BIT_IXC;
if ((!(fpsr & BIT_UFE)) && (flags & BIT_UFC)) if ((!(fpsr & BIT_UFE)) && (flags & BIT_UFC))
cumulativeTraps |= BIT_UFC; cumulativeTraps |= BIT_UFC;
if ((!(fpsr & BIT_OFE)) && (flags & BIT_OFC)) if ((!(fpsr & BIT_OFE)) && (flags & BIT_OFC))
cumulativeTraps |= BIT_OFC; cumulativeTraps |= BIT_OFC;
if ((!(fpsr & BIT_DZE)) && (flags & BIT_DZC)) if ((!(fpsr & BIT_DZE)) && (flags & BIT_DZC))
cumulativeTraps |= BIT_DZC; cumulativeTraps |= BIT_DZC;
if ((!(fpsr & BIT_IOE)) && (flags & BIT_IOC)) if ((!(fpsr & BIT_IOE)) && (flags & BIT_IOC))
cumulativeTraps |= BIT_IOC; cumulativeTraps |= BIT_IOC;
/* Set the cumulative exceptions flags. */ /* Set the cumulative exceptions flags. */
if (cumulativeTraps) if (cumulativeTraps)
writeFPSR(fpsr | cumulativeTraps); writeFPSR(fpsr | cumulativeTraps);
/* Raise an exception if necessary. */ /* Raise an exception if necessary. */
if (fpsr & (flags << 16)) if (fpsr & (flags << 16))
fp_send_sig(SIGFPE, current, 1); fp_send_sig(SIGFPE, current, 1);
} }
module_init(fpe_init); module_init(fpe_init);
......
...@@ -22,63 +22,64 @@ ...@@ -22,63 +22,64 @@
extern __inline__ extern __inline__
unsigned int readRegister(const unsigned int nReg) unsigned int readRegister(const unsigned int nReg)
{ {
/* Note: The CPU thinks it has dealt with the current instruction. As /* Note: The CPU thinks it has dealt with the current instruction.
a result the program counter has been advanced to the next As a result the program counter has been advanced to the next
instruction, and points 4 bytes beyond the actual instruction instruction, and points 4 bytes beyond the actual instruction
that caused the invalid instruction trap to occur. We adjust that caused the invalid instruction trap to occur. We adjust
for this in this routine. LDF/STF instructions with Rn = PC for this in this routine. LDF/STF instructions with Rn = PC
depend on the PC being correct, as they use PC+8 in their depend on the PC being correct, as they use PC+8 in their
address calculations. */ address calculations. */
unsigned int *userRegisters = GET_USERREG(); unsigned int *userRegisters = GET_USERREG();
unsigned int val = userRegisters[nReg]; unsigned int val = userRegisters[nReg];
if (REG_PC == nReg) val -= 4; if (REG_PC == nReg)
return val; val -= 4;
return val;
} }
extern __inline__ extern __inline__
void writeRegister(const unsigned int nReg, const unsigned int val) void writeRegister(const unsigned int nReg, const unsigned int val)
{ {
unsigned int *userRegisters = GET_USERREG(); unsigned int *userRegisters = GET_USERREG();
userRegisters[nReg] = val; userRegisters[nReg] = val;
} }
extern __inline__ extern __inline__
unsigned int readCPSR(void) unsigned int readCPSR(void)
{ {
return(readRegister(REG_CPSR)); return (readRegister(REG_CPSR));
} }
extern __inline__ extern __inline__
void writeCPSR(const unsigned int val) void writeCPSR(const unsigned int val)
{ {
writeRegister(REG_CPSR,val); writeRegister(REG_CPSR, val);
} }
extern __inline__ extern __inline__
unsigned int readConditionCodes(void) unsigned int readConditionCodes(void)
{ {
#ifdef __FPEM_TEST__ #ifdef __FPEM_TEST__
return(0); return (0);
#else #else
return(readCPSR() & CC_MASK); return (readCPSR() & CC_MASK);
#endif #endif
} }
extern __inline__ extern __inline__
void writeConditionCodes(const unsigned int val) void writeConditionCodes(const unsigned int val)
{ {
unsigned int *userRegisters = GET_USERREG(); unsigned int *userRegisters = GET_USERREG();
unsigned int rval; unsigned int rval;
/* /*
* Operate directly on userRegisters since * Operate directly on userRegisters since
* the CPSR may be the PC register itself. * the CPSR may be the PC register itself.
*/ */
rval = userRegisters[REG_CPSR] & ~CC_MASK; rval = userRegisters[REG_CPSR] & ~CC_MASK;
userRegisters[REG_CPSR] = rval | (val & CC_MASK); userRegisters[REG_CPSR] = rval | (val & CC_MASK);
} }
extern __inline__ extern __inline__
unsigned int readMemoryInt(unsigned int *pMem) unsigned int readMemoryInt(unsigned int *pMem)
{ {
return *pMem; return *pMem;
} }
...@@ -26,123 +26,64 @@ ...@@ -26,123 +26,64 @@
#include "fpmodule.h" #include "fpmodule.h"
#include "fpmodule.inl" #include "fpmodule.inl"
#ifdef CONFIG_FPE_NWFPE_XP
const floatx80 floatx80Constant[] = { const floatx80 floatx80Constant[] = {
{ 0x0000, 0x0000000000000000ULL}, /* extended 0.0 */ {0x0000, 0x0000000000000000ULL}, /* extended 0.0 */
{ 0x3fff, 0x8000000000000000ULL}, /* extended 1.0 */ {0x3fff, 0x8000000000000000ULL}, /* extended 1.0 */
{ 0x4000, 0x8000000000000000ULL}, /* extended 2.0 */ {0x4000, 0x8000000000000000ULL}, /* extended 2.0 */
{ 0x4000, 0xc000000000000000ULL}, /* extended 3.0 */ {0x4000, 0xc000000000000000ULL}, /* extended 3.0 */
{ 0x4001, 0x8000000000000000ULL}, /* extended 4.0 */ {0x4001, 0x8000000000000000ULL}, /* extended 4.0 */
{ 0x4001, 0xa000000000000000ULL}, /* extended 5.0 */ {0x4001, 0xa000000000000000ULL}, /* extended 5.0 */
{ 0x3ffe, 0x8000000000000000ULL}, /* extended 0.5 */ {0x3ffe, 0x8000000000000000ULL}, /* extended 0.5 */
{ 0x4002, 0xa000000000000000ULL} /* extended 10.0 */ {0x4002, 0xa000000000000000ULL} /* extended 10.0 */
}; };
#endif
const float64 float64Constant[] = { const float64 float64Constant[] = {
0x0000000000000000ULL, /* double 0.0 */ 0x0000000000000000ULL, /* double 0.0 */
0x3ff0000000000000ULL, /* double 1.0 */ 0x3ff0000000000000ULL, /* double 1.0 */
0x4000000000000000ULL, /* double 2.0 */ 0x4000000000000000ULL, /* double 2.0 */
0x4008000000000000ULL, /* double 3.0 */ 0x4008000000000000ULL, /* double 3.0 */
0x4010000000000000ULL, /* double 4.0 */ 0x4010000000000000ULL, /* double 4.0 */
0x4014000000000000ULL, /* double 5.0 */ 0x4014000000000000ULL, /* double 5.0 */
0x3fe0000000000000ULL, /* double 0.5 */ 0x3fe0000000000000ULL, /* double 0.5 */
0x4024000000000000ULL /* double 10.0 */ 0x4024000000000000ULL /* double 10.0 */
}; };
const float32 float32Constant[] = { const float32 float32Constant[] = {
0x00000000, /* single 0.0 */ 0x00000000, /* single 0.0 */
0x3f800000, /* single 1.0 */ 0x3f800000, /* single 1.0 */
0x40000000, /* single 2.0 */ 0x40000000, /* single 2.0 */
0x40400000, /* single 3.0 */ 0x40400000, /* single 3.0 */
0x40800000, /* single 4.0 */ 0x40800000, /* single 4.0 */
0x40a00000, /* single 5.0 */ 0x40a00000, /* single 5.0 */
0x3f000000, /* single 0.5 */ 0x3f000000, /* single 0.5 */
0x41200000 /* single 10.0 */ 0x41200000 /* single 10.0 */
}; };
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);
}
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);
}
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);
}
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);
}
/* condition code lookup table /* condition code lookup table
index into the table is test code: EQ, NE, ... LT, GT, AL, NV index into the table is test code: EQ, NE, ... LT, GT, AL, NV
bit position in short is condition code: NZCV */ bit position in short is condition code: NZCV */
static const unsigned short aCC[16] = { static const unsigned short aCC[16] = {
0xF0F0, // EQ == Z set 0xF0F0, // EQ == Z set
0x0F0F, // NE 0x0F0F, // NE
0xCCCC, // CS == C set 0xCCCC, // CS == C set
0x3333, // CC 0x3333, // CC
0xFF00, // MI == N set 0xFF00, // MI == N set
0x00FF, // PL 0x00FF, // PL
0xAAAA, // VS == V set 0xAAAA, // VS == V set
0x5555, // VC 0x5555, // VC
0x0C0C, // HI == C set && Z clear 0x0C0C, // HI == C set && Z clear
0xF3F3, // LS == C clear || Z set 0xF3F3, // LS == C clear || Z set
0xAA55, // GE == (N==V) 0xAA55, // GE == (N==V)
0x55AA, // LT == (N!=V) 0x55AA, // LT == (N!=V)
0x0A05, // GT == (!Z && (N==V)) 0x0A05, // GT == (!Z && (N==V))
0xF5FA, // LE == (Z || (N!=V)) 0xF5FA, // LE == (Z || (N!=V))
0xFFFF, // AL always 0xFFFF, // AL always
0 // NV 0 // NV
}; };
unsigned int checkCondition(const unsigned int opcode, const unsigned int ccodes) unsigned int checkCondition(const unsigned int opcode, const unsigned int ccodes)
{ {
return (aCC[opcode>>28] >> (ccodes>>28)) & 1; return (aCC[opcode >> 28] >> (ccodes >> 28)) & 1;
} }
/* /*
NetWinder Floating Point Emulator NetWinder Floating Point Emulator
(c) Rebel.COM, 1998,1999 (c) Rebel.COM, 1998,1999
(c) Philip Blundell, 2001
Direct questions, comments to Scott Bambrough <scottb@netwinder.org> Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
...@@ -186,7 +187,7 @@ TABLE 5 ...@@ -186,7 +187,7 @@ TABLE 5
#define BIT_LOAD 0x00100000 #define BIT_LOAD 0x00100000
/* masks for load/store */ /* masks for load/store */
#define MASK_CPDT 0x0c000000 /* data processing opcode */ #define MASK_CPDT 0x0c000000 /* data processing opcode */
#define MASK_OFFSET 0x000000ff #define MASK_OFFSET 0x000000ff
#define MASK_TRANSFER_LENGTH 0x00408000 #define MASK_TRANSFER_LENGTH 0x00408000
#define MASK_REGISTER_COUNT MASK_TRANSFER_LENGTH #define MASK_REGISTER_COUNT MASK_TRANSFER_LENGTH
...@@ -236,7 +237,7 @@ TABLE 5 ...@@ -236,7 +237,7 @@ TABLE 5
#define MONADIC_INSTRUCTION(opcode) ((opcode & BIT_MONADIC) != 0) #define MONADIC_INSTRUCTION(opcode) ((opcode & BIT_MONADIC) != 0)
/* instruction identification masks */ /* instruction identification masks */
#define MASK_CPDO 0x0e000000 /* arithmetic opcode */ #define MASK_CPDO 0x0e000000 /* arithmetic opcode */
#define MASK_ARITHMETIC_OPCODE 0x00f08000 #define MASK_ARITHMETIC_OPCODE 0x00f08000
#define MASK_DESTINATION_SIZE 0x00080080 #define MASK_DESTINATION_SIZE 0x00080080
...@@ -282,7 +283,7 @@ TABLE 5 ...@@ -282,7 +283,7 @@ TABLE 5
=== ===
*/ */
#define MASK_CPRT 0x0e000010 /* register transfer opcode */ #define MASK_CPRT 0x0e000010 /* register transfer opcode */
#define MASK_CPRT_CODE 0x00f00000 #define MASK_CPRT_CODE 0x00f00000
#define FLT_CODE 0x00000000 #define FLT_CODE 0x00000000
#define FIX_CODE 0x00100000 #define FIX_CODE 0x00100000
...@@ -366,25 +367,111 @@ TABLE 5 ...@@ -366,25 +367,111 @@ TABLE 5
/* Get the rounding mode from the opcode. */ /* Get the rounding mode from the opcode. */
#define getRoundingMode(opcode) ((opcode & MASK_ROUNDING_MODE) >> 5) #define getRoundingMode(opcode) ((opcode & MASK_ROUNDING_MODE) >> 5)
#ifdef CONFIG_FPE_NWFPE_XP
static inline const floatx80 getExtendedConstant(const unsigned int nIndex) static inline const floatx80 getExtendedConstant(const unsigned int nIndex)
{ {
extern const floatx80 floatx80Constant[]; extern const floatx80 floatx80Constant[];
return floatx80Constant[nIndex]; return floatx80Constant[nIndex];
} }
#endif
static inline const float64 getDoubleConstant(const unsigned int nIndex) static inline const float64 getDoubleConstant(const unsigned int nIndex)
{ {
extern const float64 float64Constant[]; extern const float64 float64Constant[];
return float64Constant[nIndex]; return float64Constant[nIndex];
} }
static inline const float32 getSingleConstant(const unsigned int nIndex) static inline const float32 getSingleConstant(const unsigned int nIndex)
{ {
extern const float32 float32Constant[]; extern const float32 float32Constant[];
return float32Constant[nIndex]; return float32Constant[nIndex];
} }
extern unsigned int getRegisterCount(const unsigned int opcode); static inline unsigned int getTransferLength(const unsigned int opcode)
extern unsigned int getDestinationSize(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);
}
#endif #endif
...@@ -38,12 +38,12 @@ The FPCR is a 32 bit register consisting of bit flags. ...@@ -38,12 +38,12 @@ The FPCR is a 32 bit register consisting of bit flags.
------------ ------------
Note: the system id byte is read only */ Note: the system id byte is read only */
typedef unsigned int FPSR; /* type for floating point status register */ typedef unsigned int FPSR; /* type for floating point status register */
typedef unsigned int FPCR; /* type for floating point control register */ typedef unsigned int FPCR; /* type for floating point control register */
#define MASK_SYSID 0xff000000 #define MASK_SYSID 0xff000000
#define BIT_HARDWARE 0x80000000 #define BIT_HARDWARE 0x80000000
#define FP_EMULATOR 0x01000000 /* System ID for emulator */ #define FP_EMULATOR 0x01000000 /* System ID for emulator */
#define FP_ACCELERATOR 0x81000000 /* System ID for FPA11 */ #define FP_ACCELERATOR 0x81000000 /* System ID for FPA11 */
/* EXCEPTION TRAP ENABLE BYTE /* EXCEPTION TRAP ENABLE BYTE
...@@ -51,11 +51,11 @@ typedef unsigned int FPCR; /* type for floating point control register */ ...@@ -51,11 +51,11 @@ typedef unsigned int FPCR; /* type for floating point control register */
#define MASK_TRAP_ENABLE 0x00ff0000 #define MASK_TRAP_ENABLE 0x00ff0000
#define MASK_TRAP_ENABLE_STRICT 0x001f0000 #define MASK_TRAP_ENABLE_STRICT 0x001f0000
#define BIT_IXE 0x00100000 /* inexact exception enable */ #define BIT_IXE 0x00100000 /* inexact exception enable */
#define BIT_UFE 0x00080000 /* underflow exception enable */ #define BIT_UFE 0x00080000 /* underflow exception enable */
#define BIT_OFE 0x00040000 /* overflow exception enable */ #define BIT_OFE 0x00040000 /* overflow exception enable */
#define BIT_DZE 0x00020000 /* divide by zero exception enable */ #define BIT_DZE 0x00020000 /* divide by zero exception enable */
#define BIT_IOE 0x00010000 /* invalid operation exception enable */ #define BIT_IOE 0x00010000 /* invalid operation exception enable */
/* SYSTEM CONTROL BYTE /* SYSTEM CONTROL BYTE
---------------------- */ ---------------------- */
......
/* /*
NetWinder Floating Point Emulator NetWinder Floating Point Emulator
(c) Rebel.COM, 1998,1999 (c) Rebel.COM, 1998,1999
(c) Philip Blundell, 2001
Direct questions, comments to Scott Bambrough <scottb@netwinder.org> Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
...@@ -32,224 +33,92 @@ float32 float32_arctan(float32 rFm); ...@@ -32,224 +33,92 @@ float32 float32_arctan(float32 rFm);
float32 float32_log(float32 rFm); float32 float32_log(float32 rFm);
float32 float32_tan(float32 rFm); float32 float32_tan(float32 rFm);
float32 float32_arccos(float32 rFm); float32 float32_arccos(float32 rFm);
float32 float32_pow(float32 rFn,float32 rFm); float32 float32_pow(float32 rFn, float32 rFm);
float32 float32_pol(float32 rFn,float32 rFm); float32 float32_pol(float32 rFn, float32 rFm);
unsigned int SingleCPDO(const unsigned int opcode) static float32 float32_rsf(float32 rFn, float32 rFm)
{ {
FPA11 *fpa11 = GET_FPA11(); return float32_sub(rFm, rFn);
float32 rFm, rFn;
unsigned int Fd, Fm, Fn, nRc = 1;
Fm = getFm(opcode);
if (CONSTANT_FM(opcode))
{
rFm = getSingleConstant(Fm);
}
else
{
switch (fpa11->fType[Fm])
{
case typeSingle:
rFm = fpa11->fpreg[Fm].fSingle;
break;
default: return 0;
}
}
if (!MONADIC_INSTRUCTION(opcode))
{
Fn = getFn(opcode);
switch (fpa11->fType[Fn])
{
case typeSingle:
rFn = fpa11->fpreg[Fn].fSingle;
break;
default: return 0;
}
}
Fd = getFd(opcode);
switch (opcode & MASK_ARITHMETIC_OPCODE)
{
/* dyadic opcodes */
case ADF_CODE:
fpa11->fpreg[Fd].fSingle = float32_add(rFn,rFm);
break;
case MUF_CODE:
case FML_CODE:
fpa11->fpreg[Fd].fSingle = float32_mul(rFn,rFm);
break;
case SUF_CODE:
fpa11->fpreg[Fd].fSingle = float32_sub(rFn,rFm);
break;
case RSF_CODE:
fpa11->fpreg[Fd].fSingle = float32_sub(rFm,rFn);
break;
case DVF_CODE:
case FDV_CODE:
fpa11->fpreg[Fd].fSingle = float32_div(rFn,rFm);
break;
case RDF_CODE:
case FRD_CODE:
fpa11->fpreg[Fd].fSingle = float32_div(rFm,rFn);
break;
#if 0
case POW_CODE:
fpa11->fpreg[Fd].fSingle = float32_pow(rFn,rFm);
break;
case RPW_CODE:
fpa11->fpreg[Fd].fSingle = float32_pow(rFm,rFn);
break;
#endif
case RMF_CODE:
fpa11->fpreg[Fd].fSingle = float32_rem(rFn,rFm);
break;
#if 0
case POL_CODE:
fpa11->fpreg[Fd].fSingle = float32_pol(rFn,rFm);
break;
#endif
/* monadic opcodes */
case MVF_CODE:
fpa11->fpreg[Fd].fSingle = rFm;
break;
case MNF_CODE:
rFm ^= 0x80000000;
fpa11->fpreg[Fd].fSingle = rFm;
break;
case ABS_CODE:
rFm &= 0x7fffffff;
fpa11->fpreg[Fd].fSingle = rFm;
break;
case RND_CODE:
case URD_CODE:
fpa11->fpreg[Fd].fSingle = float32_round_to_int(rFm);
break;
case SQT_CODE:
fpa11->fpreg[Fd].fSingle = float32_sqrt(rFm);
break;
#if 0
case LOG_CODE:
fpa11->fpreg[Fd].fSingle = float32_log(rFm);
break;
case LGN_CODE:
fpa11->fpreg[Fd].fSingle = float32_ln(rFm);
break;
case EXP_CODE:
fpa11->fpreg[Fd].fSingle = float32_exp(rFm);
break;
case SIN_CODE:
fpa11->fpreg[Fd].fSingle = float32_sin(rFm);
break;
case COS_CODE:
fpa11->fpreg[Fd].fSingle = float32_cos(rFm);
break;
case TAN_CODE:
fpa11->fpreg[Fd].fSingle = float32_tan(rFm);
break;
case ASN_CODE:
fpa11->fpreg[Fd].fSingle = float32_arcsin(rFm);
break;
case ACS_CODE:
fpa11->fpreg[Fd].fSingle = float32_arccos(rFm);
break;
case ATN_CODE:
fpa11->fpreg[Fd].fSingle = float32_arctan(rFm);
break;
#endif
case NRM_CODE:
break;
default:
{
nRc = 0;
}
}
if (0 != nRc) fpa11->fType[Fd] = typeSingle;
return nRc;
} }
#if 0 static float32 float32_rdv(float32 rFn, float32 rFm)
float32 float32_exp(float32 Fm)
{ {
//series return float32_div(rFm, rFn);
} }
float32 float32_ln(float32 Fm) static float32 (*const dyadic_single[16])(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(float32 rFm)
{ {
//series return rFm;
} }
float32 float32_sin(float32 rFm) static float32 float32_mnf(float32 rFm)
{ {
//series return rFm ^ 0x80000000;
} }
float32 float32_cos(float32 rFm) static float32 float32_abs(float32 rFm)
{ {
//series return rFm & 0x7fffffff;
} }
float32 float32_arcsin(float32 rFm) static float32 (*const monadic_single[16])(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(const unsigned int opcode, FPREG * rFd)
{ {
//series 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](rFn, rFm);
} else {
return 0;
}
} else {
if (monadic_single[opc_mask_shift]) {
rFd->fSingle = monadic_single[opc_mask_shift](rFm);
} else {
return 0;
}
}
return 1;
} }
float32 float32_arctan(float32 rFm)
{
//series
}
float32 float32_arccos(float32 rFm)
{
//return float32_sub(halfPi,float32_arcsin(rFm));
}
float32 float32_log(float32 rFm)
{
return float32_div(float32_ln(rFm),getSingleConstant(7));
}
float32 float32_tan(float32 rFm)
{
return float32_div(float32_sin(rFm),float32_cos(rFm));
}
float32 float32_pow(float32 rFn,float32 rFm)
{
return float32_exp(float32_mul(rFm,float32_ln(rFn)));
}
float32 float32_pol(float32 rFn,float32 rFm)
{
return float32_arctan(float32_div(rFn,rFm));
}
#endif
...@@ -29,8 +29,8 @@ this code that are retained. ...@@ -29,8 +29,8 @@ this code that are retained.
*/ */
#include "fpa11.h" #include "fpa11.h"
#include "milieu.h" //#include "milieu.h"
#include "softfloat.h" //#include "softfloat.h"
/* /*
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
...@@ -142,12 +142,14 @@ INLINE int16 extractFloat32Exp( float32 a ) ...@@ -142,12 +142,14 @@ INLINE int16 extractFloat32Exp( float32 a )
Returns the sign bit of the single-precision floating-point value `a'. Returns the sign bit of the single-precision floating-point value `a'.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
*/ */
#if 0 /* in softfloat.h */
INLINE flag extractFloat32Sign( float32 a ) INLINE flag extractFloat32Sign( float32 a )
{ {
return a>>31; return a>>31;
} }
#endif
/* /*
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
...@@ -184,9 +186,9 @@ INLINE float32 packFloat32( flag zSign, int16 zExp, bits32 zSig ) ...@@ -184,9 +186,9 @@ INLINE float32 packFloat32( flag zSign, int16 zExp, bits32 zSig )
{ {
#if 0 #if 0
float32 f; float32 f;
__asm__("@ packFloat32; \n\ __asm__("@ packFloat32 \n\
mov %0, %1, asl #31; \n\ mov %0, %1, asl #31 \n\
orr %0, %2, asl #23; \n\ orr %0, %2, asl #23 \n\
orr %0, %3" orr %0, %3"
: /* no outputs */ : /* no outputs */
: "g" (f), "g" (zSign), "g" (zExp), "g" (zSig) : "g" (f), "g" (zSign), "g" (zExp), "g" (zSig)
...@@ -321,12 +323,14 @@ INLINE int16 extractFloat64Exp( float64 a ) ...@@ -321,12 +323,14 @@ INLINE int16 extractFloat64Exp( float64 a )
Returns the sign bit of the double-precision floating-point value `a'. Returns the sign bit of the double-precision floating-point value `a'.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
*/ */
#if 0 /* in softfloat.h */
INLINE flag extractFloat64Sign( float64 a ) INLINE flag extractFloat64Sign( float64 a )
{ {
return a>>63; return a>>63;
} }
#endif
/* /*
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
......
...@@ -40,7 +40,9 @@ floating-point format `floatx80'. If this macro is not defined, the ...@@ -40,7 +40,9 @@ floating-point format `floatx80'. If this macro is not defined, the
input or output the `floatx80' type will be defined. input or output the `floatx80' type will be defined.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
*/ */
#ifdef CONFIG_FPE_NWFPE_XP
#define FLOATX80 #define FLOATX80
#endif
/* /*
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
...@@ -229,4 +231,46 @@ char floatx80_is_signaling_nan( floatx80 ); ...@@ -229,4 +231,46 @@ char floatx80_is_signaling_nan( floatx80 );
#endif #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));
}
#endif #endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment