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
You may say N here if you are going to load the Acorn FPEmulator
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
tristate "FastFPE math emulation (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>
* fpa11.h - Add documentation
......
......@@ -2,18 +2,12 @@
# Copyright (C) 1998, 1999, 2001 Philip Blundell
#
obj-y :=
obj-m :=
obj-n :=
obj-$(CONFIG_FPE_NWFPE) += nwfpe.o
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 \
fpmodule.o fpopcode.o softfloat.o \
single_cpdo.o double_cpdo.o extended_cpdo.o
ifeq ($(CONFIG_CPU_26),y)
nwfpe-objs += entry26.o
else
nwfpe-objs += entry.o
endif
nwfpe-$(CONFIG_FPE_NWFPE_XP) += extended_cpdo.o
nwfpe-$(CONFIG_CPU_26) += entry26.o
nwfpe-$(CONFIG_CPU_32) += entry.o
......@@ -23,6 +23,11 @@
#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);
......@@ -32,257 +37,123 @@ 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);
float64 float64_pow(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();
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;
return float64_sub(rFm, rFn);
}
#if 0
float64 float64_exp(float64 rFm)
static float64 float64_rdv(float64 rFn, float64 rFm)
{
return rFm;
//series
return float64_div(rFm, rFn);
}
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;
//series
return rFm;
}
float64 float64_sin(float64 rFm)
static float64 float64_mnf(float64 rFm)
{
return rFm;
//series
}
union float64_components u;
float64 float64_cos(float64 rFm)
{
return rFm;
//series
}
u.f64 = rFm;
u.i[1] ^= 0x80000000;
#if 0
float64 float64_arcsin(float64 rFm)
{
//series
return u.f64;
}
float64 float64_arctan(float64 rFm)
static float64 float64_abs(float64 rFm)
{
//series
}
#endif
union float64_components u;
float64 float64_log(float64 rFm)
{
return float64_div(float64_ln(rFm),getDoubleConstant(7));
}
u.f64 = rFm;
u.i[1] &= 0x7fffffff;
float64 float64_tan(float64 rFm)
{
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)));
return u.f64;
}
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. */
.globl nwfpe_enter
nwfpe_enter:
mov r4, lr @ save the failure-return addresses
mov sl, sp
mov r4, lr @ save the failure-return addresses
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:
bl EmulateAll @ emulate the instruction
cmp r0, #0 @ was emulation successful
moveq pc, r4 @ no, return failure
bl EmulateAll @ emulate the instruction
cmp r0, #0 @ was emulation successful
moveq pc, r4 @ no, return failure
next:
.Lx1: ldrt r6, [r5], #4 @ get the next instruction and
.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
movne pc, r9 @ return ok if not a fp insn
and r2, r6, #0x0F000000 @ test for FP insns
teq r2, #0x0C000000
teqne r2, #0x0D000000
teqne r2, #0x0E000000
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
ldr r1, [sp, #64] @ fetch the condition codes
bl checkCondition @ check the condition
cmp r0, #0 @ r0 = 0 ==> condition failed
mov r0, r6 @ save a copy
ldr r1, [sp, #64] @ fetch the condition codes
bl checkCondition @ check the condition
cmp r0, #0 @ r0 = 0 ==> condition failed
@ if condition code failed to match, next insn
beq next @ get the next instruction;
mov r0, r6 @ prepare for EmulateAll()
b emulate @ if r0 != 0, goto EmulateAll
@ if condition code failed to match, next insn
beq next @ get the next instruction;
mov r0, r6 @ prepare for EmulateAll()
b emulate @ if r0 != 0, goto EmulateAll
@ We need to be prepared for the instructions at .Lx1 and .Lx2
@ to fault. Emit the appropriate exception gunk to fix things up.
......
......@@ -66,7 +66,6 @@ several floating point instructions. */
.globl nwfpe_enter
nwfpe_enter:
mov sl, sp
ldr r5, [sp, #60] @ get contents of PC
bic r5, r5, #0xfc000003
ldr r0, [r5, #-4] @ get actual instruction into r0
......@@ -96,7 +95,7 @@ next:
@ if condition code failed to match, next insn
beq next @ get the next instruction;
mov r0, r6 @ prepare for EmulateAll()
adr lr, 1b
orr lr, lr, #3
......
......@@ -32,242 +32,123 @@ 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);
floatx80 floatx80_pow(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();
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
return floatx80_sub(rFm, rFn);
}
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
(c) Rebel.COM, 1998,1999
(c) Philip Blundell, 2001
Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
......@@ -37,184 +38,105 @@ unsigned int EmulateCPRT(const unsigned int);
/* 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;
/* FPCR: set SB, AB and DA bits, clear all others */
#if MAINTAIN_FPCR
fpa11->fpcr = MASK_RESET;
#endif
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;
}
void SetRoundingMode(const unsigned int opcode)
{
#if MAINTAIN_FPCR
FPA11 *fpa11 = GET_FPA11();
fpa11->fpcr &= ~MASK_ROUNDING_MODE;
#endif
switch (opcode & MASK_ROUNDING_MODE)
{
default:
case ROUND_TO_NEAREST:
float_rounding_mode = float_round_nearest_even;
#if MAINTAIN_FPCR
fpa11->fpcr |= ROUND_TO_NEAREST;
#endif
break;
case ROUND_TO_PLUS_INFINITY:
float_rounding_mode = float_round_up;
#if MAINTAIN_FPCR
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;
}
switch (opcode & MASK_ROUNDING_MODE) {
default:
case ROUND_TO_NEAREST:
float_rounding_mode = float_round_nearest_even;
break;
case ROUND_TO_PLUS_INFINITY:
float_rounding_mode = float_round_up;
break;
case ROUND_TO_MINUS_INFINITY:
float_rounding_mode = float_round_down;
break;
case ROUND_TO_ZERO:
float_rounding_mode = float_round_to_zero;
break;
}
}
void SetRoundingPrecision(const unsigned int opcode)
{
#if MAINTAIN_FPCR
FPA11 *fpa11 = GET_FPA11();
fpa11->fpcr &= ~MASK_ROUNDING_PRECISION;
#endif
switch (opcode & MASK_ROUNDING_PRECISION)
{
case ROUND_SINGLE:
floatx80_rounding_precision = 32;
#if MAINTAIN_FPCR
fpa11->fpcr |= ROUND_SINGLE;
#endif
break;
case ROUND_DOUBLE:
floatx80_rounding_precision = 64;
#if MAINTAIN_FPCR
fpa11->fpcr |= ROUND_DOUBLE;
#endif
break;
case ROUND_EXTENDED:
floatx80_rounding_precision = 80;
#if MAINTAIN_FPCR
fpa11->fpcr |= ROUND_EXTENDED;
#endif
break;
default: floatx80_rounding_precision = 80;
}
#ifdef CONFIG_FPE_NWFPE_XP
switch (opcode & MASK_ROUNDING_PRECISION) {
case ROUND_SINGLE:
floatx80_rounding_precision = 32;
break;
case ROUND_DOUBLE:
floatx80_rounding_precision = 64;
break;
case ROUND_EXTENDED:
floatx80_rounding_precision = 80;
break;
default:
floatx80_rounding_precision = 80;
}
#endif
}
void nwfpe_init(union fp_state *fp)
void nwfpe_init_fpa(union fp_state *fp)
{
FPA11 *fpa11 = (FPA11 *)fp;
memset(fpa11, 0, sizeof(FPA11));
resetFPA11();
SetRoundingMode(ROUND_TO_NEAREST);
SetRoundingPrecision(ROUND_EXTENDED);
fpa11->initflag = 1;
FPA11 *fpa11 = (FPA11 *)fp;
#ifdef NWFPE_DEBUG
printk("NWFPE: setting up state.\n");
#endif
memset(fpa11, 0, sizeof(FPA11));
resetFPA11();
SetRoundingMode(ROUND_TO_NEAREST);
SetRoundingPrecision(ROUND_EXTENDED);
fpa11->initflag = 1;
}
/* Emulate the instruction in the opcode. */
unsigned int EmulateAll(unsigned int opcode)
{
unsigned int nRc = 1, 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);
}
unsigned int code;
#if 0
unsigned int EmulateAll1(unsigned int 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;
}
}
#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;
}
......@@ -37,6 +37,7 @@ register unsigned int *user_registers asm("sl");
/* includes */
#include "fpsr.h" /* FP control and status register definitions */
#include "milieu.h"
#include "softfloat.h"
#define typeNone 0x00
......@@ -48,9 +49,13 @@ register unsigned int *user_registers asm("sl");
* This must be no more and no less than 12 bytes.
*/
typedef union tagFPREG {
floatx80 fExtended;
float64 fDouble;
float32 fSingle;
float32 fSingle;
float64 fDouble;
#ifdef CONFIG_FPE_NWFPE_XP
floatx80 fExtended;
#else
int padding[3];
#endif
} FPREG;
/*
......@@ -67,21 +72,21 @@ typedef union tagFPREG {
* 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. */
/* 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. */
} FPA11;
extern void SetRoundingMode(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
......@@ -24,28 +24,28 @@
/* Read and write floating point status register */
extern __inline__ unsigned int readFPSR(void)
{
FPA11 *fpa11 = GET_FPA11();
return(fpa11->fpsr);
FPA11 *fpa11 = GET_FPA11();
return (fpa11->fpsr);
}
extern __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);
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 */
extern __inline__ FPCR readFPCR(void)
{
FPA11 *fpa11 = GET_FPA11();
/* clear SB, AB and DA bits before returning FPCR */
return(fpa11->fpcr & ~MASK_RFC);
FPA11 *fpa11 = GET_FPA11();
/* clear SB, AB and DA bits before returning FPCR */
return (fpa11->fpcr & ~MASK_RFC);
}
extern __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 */
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 */
}
/*
NetWinder Floating Point Emulator
(c) Rebel.COM, 1998,1999
(c) Philip Blundell, 2001
Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
......@@ -22,96 +23,109 @@
#include "fpa11.h"
#include "fpopcode.h"
unsigned int SingleCPDO(const unsigned int opcode);
unsigned int DoubleCPDO(const unsigned int opcode);
unsigned int ExtendedCPDO(const unsigned int opcode);
unsigned int SingleCPDO(const unsigned int opcode, FPREG * rFd);
unsigned int DoubleCPDO(const unsigned int opcode, FPREG * rFd);
unsigned int ExtendedCPDO(const unsigned int opcode, FPREG * rFd);
unsigned int EmulateCPDO(const unsigned int opcode)
{
FPA11 *fpa11 = GET_FPA11();
unsigned int Fd, nType, nDest, nRc = 1;
//printk("EmulateCPDO(0x%08x)\n",opcode);
/* Get the destination size. If not valid let Linux perform
an invalid instruction trap. */
nDest = getDestinationSize(opcode);
if (typeNone == nDest) return 0;
SetRoundingMode(opcode);
/* 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];
}
}
switch (nType)
{
case typeSingle : nRc = SingleCPDO(opcode); break;
case typeDouble : nRc = DoubleCPDO(opcode); break;
case typeExtended : nRc = ExtendedCPDO(opcode); break;
default : 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. */
Fd = getFd(opcode);
nType = fpa11->fType[Fd];
if ((0 != nRc) && (nDest != nType))
{
switch (nDest)
{
case typeSingle:
{
if (typeDouble == nType)
fpa11->fpreg[Fd].fSingle =
float64_to_float32(fpa11->fpreg[Fd].fDouble);
else
fpa11->fpreg[Fd].fSingle =
floatx80_to_float32(fpa11->fpreg[Fd].fExtended);
}
break;
case typeDouble:
{
if (typeSingle == nType)
fpa11->fpreg[Fd].fDouble =
float32_to_float64(fpa11->fpreg[Fd].fSingle);
else
fpa11->fpreg[Fd].fDouble =
floatx80_to_float64(fpa11->fpreg[Fd].fExtended);
}
break;
case typeExtended:
{
if (typeSingle == nType)
fpa11->fpreg[Fd].fExtended =
float32_to_floatx80(fpa11->fpreg[Fd].fSingle);
else
fpa11->fpreg[Fd].fExtended =
float64_to_floatx80(fpa11->fpreg[Fd].fDouble);
}
break;
}
fpa11->fType[Fd] = nDest;
}
return nRc;
FPA11 *fpa11 = GET_FPA11();
FPREG *rFd;
unsigned int nType, nDest, nRc;
/* Get the destination size. If not valid let Linux perform
an invalid instruction trap. */
nDest = getDestinationSize(opcode);
if (typeNone == nDest)
return 0;
SetRoundingMode(opcode);
/* 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(opcode, rFd);
break;
case typeDouble:
nRc = DoubleCPDO(opcode, rFd);
break;
#ifdef CONFIG_FPE_NWFPE_XP
case typeExtended:
nRc = ExtendedCPDO(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(rFd->fDouble);
else
rFd->fSingle = floatx80_to_float32(rFd->fExtended);
}
break;
case typeDouble:
{
if (typeSingle == nType)
rFd->fDouble = float32_to_float64(rFd->fSingle);
else
rFd->fDouble = floatx80_to_float64(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(rFd->fDouble);
else
rFd->fDouble = float32_to_float64(rFd->fSingle);
}
#endif
}
return nRc;
}
/*
NetWinder Floating Point Emulator
(c) Rebel.com, 1998-1999
(c) Philip Blundell, 1998
(c) Philip Blundell, 1998, 2001
Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
......@@ -28,341 +28,354 @@
#include <asm/uaccess.h>
static inline
void loadSingle(const unsigned int Fn,const unsigned int *pMem)
static inline void loadSingle(const unsigned int Fn, const unsigned int *pMem)
{
FPA11 *fpa11 = GET_FPA11();
fpa11->fType[Fn] = typeSingle;
get_user(fpa11->fpreg[Fn].fSingle, 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 *pMem)
static inline void loadDouble(const unsigned int Fn, const unsigned int *pMem)
{
FPA11 *fpa11 = GET_FPA11();
unsigned int *p;
p = (unsigned int*)&fpa11->fpreg[Fn].fDouble;
fpa11->fType[Fn] = typeDouble;
get_user(p[0], &pMem[1]);
get_user(p[1], &pMem[0]); /* sign & exponent */
FPA11 *fpa11 = GET_FPA11();
unsigned int *p;
p = (unsigned int *) &fpa11->fpreg[Fn].fDouble;
fpa11->fType[Fn] = typeDouble;
get_user(p[0], &pMem[1]);
get_user(p[1], &pMem[0]); /* sign & exponent */
}
static inline
void loadExtended(const unsigned int Fn,const unsigned int *pMem)
#ifdef CONFIG_FPE_NWFPE_XP
static inline void loadExtended(const unsigned int Fn, const unsigned int *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 */
get_user(p[1], &pMem[2]); /* ls bits */
get_user(p[2], &pMem[1]); /* ms bits */
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 */
get_user(p[1], &pMem[2]); /* ls bits */
get_user(p[2], &pMem[1]); /* ms bits */
}
#endif
static inline
void loadMultiple(const unsigned int Fn,const unsigned int *pMem)
static inline void loadMultiple(const unsigned int Fn, const unsigned int *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;
case typeExtended:
{
get_user(p[1], &pMem[2]);
get_user(p[2], &pMem[1]); /* msw */
p[0] = (x & 0x80003fff);
}
break;
}
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(const unsigned int Fn,unsigned int *pMem)
static inline void storeSingle(const unsigned int Fn, unsigned int *pMem)
{
FPA11 *fpa11 = GET_FPA11();
union
{
float32 f;
unsigned int i[1];
} val;
switch (fpa11->fType[Fn])
{
case typeDouble:
val.f = float64_to_float32(fpa11->fpreg[Fn].fDouble);
break;
case typeExtended:
val.f = floatx80_to_float32(fpa11->fpreg[Fn].fExtended);
break;
default: val.f = fpa11->fpreg[Fn].fSingle;
}
put_user(val.i[0], pMem);
FPA11 *fpa11 = GET_FPA11();
union {
float32 f;
unsigned int i[1];
} val;
switch (fpa11->fType[Fn]) {
case typeDouble:
val.f = float64_to_float32(fpa11->fpreg[Fn].fDouble);
break;
#ifdef CONFIG_FPE_NWFPE_XP
case typeExtended:
val.f = floatx80_to_float32(fpa11->fpreg[Fn].fExtended);
break;
#endif
default:
val.f = fpa11->fpreg[Fn].fSingle;
}
put_user(val.i[0], pMem);
}
static inline
void storeDouble(const unsigned int Fn,unsigned int *pMem)
static inline void storeDouble(const unsigned int Fn, unsigned int *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;
case typeExtended:
val.f = floatx80_to_float64(fpa11->fpreg[Fn].fExtended);
break;
default: val.f = fpa11->fpreg[Fn].fDouble;
}
put_user(val.i[1], &pMem[0]); /* msw */
put_user(val.i[0], &pMem[1]); /* lsw */
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(fpa11->fpreg[Fn].fExtended);
break;
#endif
default:
val.f = fpa11->fpreg[Fn].fDouble;
}
put_user(val.i[1], &pMem[0]); /* msw */
put_user(val.i[0], &pMem[1]); /* lsw */
}
static inline
void storeExtended(const unsigned int Fn,unsigned int *pMem)
#ifdef CONFIG_FPE_NWFPE_XP
static inline void storeExtended(const unsigned int Fn, unsigned int *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 */
put_user(val.i[1], &pMem[2]);
put_user(val.i[2], &pMem[1]); /* msw */
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 */
put_user(val.i[1], &pMem[2]);
put_user(val.i[2], &pMem[1]); /* msw */
}
#endif
static inline
void storeMultiple(const unsigned int Fn,unsigned int *pMem)
static inline void storeMultiple(const unsigned int Fn, unsigned int *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;
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;
}
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 *pBase, *pAddress, *pFinal, nRc = 1,
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 += 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;
case TRANSFER_EXTENDED: loadExtended(getFd(opcode),pAddress); break;
default: nRc = 0;
}
if (write_back) writeRegister(getRn(opcode),(unsigned int)pFinal);
return nRc;
unsigned int *pBase, *pAddress, *pFinal, nRc = 1,
write_back = WRITE_BACK(opcode);
pBase = (unsigned int *) 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 int) pFinal);
return nRc;
}
unsigned int PerformSTF(const unsigned int opcode)
{
unsigned int *pBase, *pAddress, *pFinal, nRc = 1,
write_back = WRITE_BACK(opcode);
//printk("PerformSTF(0x%08x), Fd = 0x%08x\n",opcode,getFd(opcode));
SetRoundingMode(ROUND_TO_NEAREST);
pBase = (unsigned int*)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(getFd(opcode),pAddress); break;
case TRANSFER_DOUBLE : storeDouble(getFd(opcode),pAddress); break;
case TRANSFER_EXTENDED: storeExtended(getFd(opcode),pAddress); break;
default: nRc = 0;
}
if (write_back) writeRegister(getRn(opcode),(unsigned int)pFinal);
return nRc;
unsigned int *pBase, *pAddress, *pFinal, nRc = 1,
write_back = WRITE_BACK(opcode);
SetRoundingMode(ROUND_TO_NEAREST);
pBase = (unsigned int *) 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(getFd(opcode), pAddress);
break;
case TRANSFER_DOUBLE:
storeDouble(getFd(opcode), pAddress);
break;
#ifdef CONFIG_FPE_NWFPE_XP
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 i, Fd, *pBase, *pAddress, *pFinal,
write_back = WRITE_BACK(opcode);
pBase = (unsigned int*)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 int)pFinal);
return 1;
unsigned int i, Fd, *pBase, *pAddress, *pFinal,
write_back = WRITE_BACK(opcode);
pBase = (unsigned int *) 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 int) pFinal);
return 1;
}
unsigned int PerformSFM(const unsigned int opcode)
{
unsigned int i, Fd, *pBase, *pAddress, *pFinal,
write_back = WRITE_BACK(opcode);
pBase = (unsigned int*)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 int)pFinal);
return 1;
unsigned int i, Fd, *pBase, *pAddress, *pFinal,
write_back = WRITE_BACK(opcode);
pBase = (unsigned int *) 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 int) pFinal);
return 1;
}
#if 1
unsigned int EmulateCPDT(const unsigned int opcode)
{
unsigned int nRc = 0;
//printk("EmulateCPDT(0x%08x)\n",opcode);
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;
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;
}
#endif
/*
NetWinder Floating Point Emulator
(c) Rebel.COM, 1998,1999
(c) Philip Blundell, 1999
(c) Philip Blundell, 1999, 2001
Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
......@@ -21,269 +21,348 @@
*/
#include "fpa11.h"
#include "milieu.h"
#include "softfloat.h"
#include "fpopcode.h"
#include "fpa11.inl"
#include "fpmodule.h"
#include "fpmodule.inl"
#ifdef CONFIG_FPE_NWFPE_XP
extern flag floatx80_is_nan(floatx80);
extern flag float64_is_nan( float64);
extern flag float32_is_nan( float32);
#endif
extern flag float64_is_nan(float64);
extern flag float32_is_nan(float32);
void SetRoundingMode(const unsigned int opcode);
unsigned int PerformFLT(const unsigned int opcode);
unsigned int PerformFIX(const unsigned int opcode);
static unsigned int
PerformComparison(const unsigned int opcode);
static unsigned int PerformComparison(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;
}
return nRc;
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();
unsigned int nRc = 1;
SetRoundingMode(opcode);
switch (opcode & MASK_ROUNDING_PRECISION)
{
case ROUND_SINGLE:
{
fpa11->fType[getFn(opcode)] = typeSingle;
fpa11->fpreg[getFn(opcode)].fSingle =
int32_to_float32(readRegister(getRd(opcode)));
}
break;
case ROUND_DOUBLE:
{
fpa11->fType[getFn(opcode)] = typeDouble;
fpa11->fpreg[getFn(opcode)].fDouble =
int32_to_float64(readRegister(getRd(opcode)));
}
break;
case ROUND_EXTENDED:
{
fpa11->fType[getFn(opcode)] = typeExtended;
fpa11->fpreg[getFn(opcode)].fExtended =
int32_to_floatx80(readRegister(getRd(opcode)));
}
break;
default: nRc = 0;
}
return nRc;
FPA11 *fpa11 = GET_FPA11();
SetRoundingMode(opcode);
SetRoundingPrecision(opcode);
switch (opcode & MASK_ROUNDING_PRECISION) {
case ROUND_SINGLE:
{
fpa11->fType[getFn(opcode)] = typeSingle;
fpa11->fpreg[getFn(opcode)].fSingle = int32_to_float32(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;
}
return 1;
}
unsigned int PerformFIX(const unsigned int opcode)
{
FPA11 *fpa11 = GET_FPA11();
unsigned int nRc = 1;
unsigned int Fn = getFm(opcode);
SetRoundingMode(opcode);
switch (fpa11->fType[Fn])
{
case typeSingle:
{
writeRegister(getRd(opcode),
float32_to_int32(fpa11->fpreg[Fn].fSingle));
}
break;
case typeDouble:
{
writeRegister(getRd(opcode),
float64_to_int32(fpa11->fpreg[Fn].fDouble));
}
break;
case typeExtended:
{
writeRegister(getRd(opcode),
floatx80_to_int32(fpa11->fpreg[Fn].fExtended));
}
break;
default: nRc = 0;
}
return nRc;
}
FPA11 *fpa11 = GET_FPA11();
unsigned int Fn = getFm(opcode);
SetRoundingMode(opcode);
switch (fpa11->fType[Fn]) {
case typeSingle:
{
writeRegister(getRd(opcode), float32_to_int32(fpa11->fpreg[Fn].fSingle));
}
break;
case typeDouble:
{
writeRegister(getRd(opcode), float64_to_int32(fpa11->fpreg[Fn].fDouble));
}
break;
#ifdef CONFIG_FPE_NWFPE_XP
case typeExtended:
{
writeRegister(getRd(opcode), floatx80_to_int32(fpa11->fpreg[Fn].fExtended));
}
break;
#endif
static unsigned int __inline__
PerformComparisonOperation(floatx80 Fn, floatx80 Fm)
{
unsigned int flags = 0;
/* 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;
default:
return 0;
}
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, Fm;
floatx80 rFn, rFm;
int e_flag = opcode & 0x400000; /* 1 if CxFE */
int n_flag = opcode & 0x200000; /* 1 if CNxx */
unsigned int flags = 0;
//printk("PerformComparison(0x%08x)\n",opcode);
Fn = getFn(opcode);
Fm = getFm(opcode);
/* 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;
}
return PerformComparisonOperation(rFn,rFm);
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;
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;
}
......@@ -42,11 +42,17 @@
#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);
#if LINUX_VERSION_CODE > 0x20115
MODULE_AUTHOR("Scott Bambrough <scottb@rebel.com>");
MODULE_DESCRIPTION("NWFPE floating point emulator");
MODULE_DESCRIPTION("NWFPE floating point emulator (" NWFPE_BITS " precision)");
#endif
#else
......@@ -63,63 +69,45 @@ void fp_setup(void);
extern void (*kern_fp_enter)(void);
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_init)(union fp_state *);
/* forward declarations */
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)
{
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;
}
#ifdef MODULE
if (!mod_member_present(&__this_module, can_unload))
return -EINVAL;
__this_module.can_unload = fpe_unload;
#else
if (fpe_type[0] && strcmp(fpe_type, "nwfpe"))
return 0;
#endif
/* Display title, version and copyright information. */
printk(KERN_WARNING "NetWinder Floating Point Emulator V0.95 "
"(c) 1998-1999 Rebel.com\n");
/* 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;
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");
/* 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_fpa;
return 0;
}
static void __exit fpe_exit(void)
{
/* Restore the values we saved earlier. */
kern_fp_enter = orig_fp_enter;
fp_init = orig_fp_init;
/* Restore the values we saved earlier. */
kern_fp_enter = orig_fp_enter;
fp_init = orig_fp_init;
}
/*
......@@ -144,41 +132,42 @@ cumulative exceptions flag byte are set and we return.
void float_raise(signed char flags)
{
register unsigned int fpsr, cumulativeTraps;
register unsigned int fpsr, cumulativeTraps;
#ifdef CONFIG_DEBUG_USER
printk(KERN_DEBUG "NWFPE: %s[%d] takes exception %08x at %p from %08x\n",
current->comm, current->pid, flags,
__builtin_return_address(0), GET_USERREG()[15]);
printk(KERN_DEBUG
"NWFPE: %s[%d] takes exception %08x at %p from %08x\n",
current->comm, current->pid, flags,
__builtin_return_address(0), GET_USERREG()[15]);
#endif
/* Keep SoftFloat exception flags up to date. */
float_exception_flags |= flags;
/* 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);
/* Keep SoftFloat exception flags up to date. */
float_exception_flags |= flags;
/* 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);
......
......@@ -22,63 +22,64 @@
extern __inline__
unsigned int 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. */
unsigned int *userRegisters = GET_USERREG();
unsigned int val = userRegisters[nReg];
if (REG_PC == nReg) val -= 4;
return val;
/* 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. */
unsigned int *userRegisters = GET_USERREG();
unsigned int val = userRegisters[nReg];
if (REG_PC == nReg)
val -= 4;
return val;
}
extern __inline__
void writeRegister(const unsigned int nReg, const unsigned int val)
{
unsigned int *userRegisters = GET_USERREG();
userRegisters[nReg] = val;
unsigned int *userRegisters = GET_USERREG();
userRegisters[nReg] = val;
}
extern __inline__
unsigned int readCPSR(void)
{
return(readRegister(REG_CPSR));
return (readRegister(REG_CPSR));
}
extern __inline__
void writeCPSR(const unsigned int val)
{
writeRegister(REG_CPSR,val);
writeRegister(REG_CPSR, val);
}
extern __inline__
unsigned int readConditionCodes(void)
{
#ifdef __FPEM_TEST__
return(0);
return (0);
#else
return(readCPSR() & CC_MASK);
return (readCPSR() & CC_MASK);
#endif
}
extern __inline__
void writeConditionCodes(const unsigned int val)
{
unsigned int *userRegisters = GET_USERREG();
unsigned int rval;
/*
* Operate directly on userRegisters since
* the CPSR may be the PC register itself.
*/
rval = userRegisters[REG_CPSR] & ~CC_MASK;
userRegisters[REG_CPSR] = rval | (val & CC_MASK);
unsigned int *userRegisters = GET_USERREG();
unsigned int rval;
/*
* Operate directly on userRegisters since
* the CPSR may be the PC register itself.
*/
rval = userRegisters[REG_CPSR] & ~CC_MASK;
userRegisters[REG_CPSR] = rval | (val & CC_MASK);
}
extern __inline__
unsigned int readMemoryInt(unsigned int *pMem)
{
return *pMem;
return *pMem;
}
......@@ -26,123 +26,64 @@
#include "fpmodule.h"
#include "fpmodule.inl"
#ifdef CONFIG_FPE_NWFPE_XP
const floatx80 floatx80Constant[] = {
{ 0x0000, 0x0000000000000000ULL}, /* extended 0.0 */
{ 0x3fff, 0x8000000000000000ULL}, /* extended 1.0 */
{ 0x4000, 0x8000000000000000ULL}, /* extended 2.0 */
{ 0x4000, 0xc000000000000000ULL}, /* extended 3.0 */
{ 0x4001, 0x8000000000000000ULL}, /* extended 4.0 */
{ 0x4001, 0xa000000000000000ULL}, /* extended 5.0 */
{ 0x3ffe, 0x8000000000000000ULL}, /* extended 0.5 */
{ 0x4002, 0xa000000000000000ULL} /* extended 10.0 */
};
{0x0000, 0x0000000000000000ULL}, /* extended 0.0 */
{0x3fff, 0x8000000000000000ULL}, /* extended 1.0 */
{0x4000, 0x8000000000000000ULL}, /* extended 2.0 */
{0x4000, 0xc000000000000000ULL}, /* extended 3.0 */
{0x4001, 0x8000000000000000ULL}, /* extended 4.0 */
{0x4001, 0xa000000000000000ULL}, /* extended 5.0 */
{0x3ffe, 0x8000000000000000ULL}, /* extended 0.5 */
{0x4002, 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 */
};
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 */
};
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);
}
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 */
};
/* condition code lookup table
index into the table is test code: EQ, NE, ... LT, GT, AL, NV
bit position in short is condition code: NZCV */
static const unsigned short aCC[16] = {
0xF0F0, // EQ == Z set
0x0F0F, // NE
0xCCCC, // CS == C set
0x3333, // CC
0xFF00, // MI == N set
0x00FF, // PL
0xAAAA, // VS == V set
0x5555, // VC
0x0C0C, // HI == C set && Z clear
0xF3F3, // LS == C clear || Z set
0xAA55, // GE == (N==V)
0x55AA, // LT == (N!=V)
0x0A05, // GT == (!Z && (N==V))
0xF5FA, // LE == (Z || (N!=V))
0xFFFF, // AL always
0 // NV
0xF0F0, // EQ == Z set
0x0F0F, // NE
0xCCCC, // CS == C set
0x3333, // CC
0xFF00, // MI == N set
0x00FF, // PL
0xAAAA, // VS == V set
0x5555, // VC
0x0C0C, // HI == C set && Z clear
0xF3F3, // LS == C clear || Z set
0xAA55, // GE == (N==V)
0x55AA, // LT == (N!=V)
0x0A05, // GT == (!Z && (N==V))
0xF5FA, // LE == (Z || (N!=V))
0xFFFF, // AL always
0 // NV
};
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
(c) Rebel.COM, 1998,1999
(c) Philip Blundell, 2001
Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
......@@ -186,7 +187,7 @@ TABLE 5
#define BIT_LOAD 0x00100000
/* masks for load/store */
#define MASK_CPDT 0x0c000000 /* data processing opcode */
#define MASK_CPDT 0x0c000000 /* data processing opcode */
#define MASK_OFFSET 0x000000ff
#define MASK_TRANSFER_LENGTH 0x00408000
#define MASK_REGISTER_COUNT MASK_TRANSFER_LENGTH
......@@ -236,7 +237,7 @@ TABLE 5
#define MONADIC_INSTRUCTION(opcode) ((opcode & BIT_MONADIC) != 0)
/* instruction identification masks */
#define MASK_CPDO 0x0e000000 /* arithmetic opcode */
#define MASK_CPDO 0x0e000000 /* arithmetic opcode */
#define MASK_ARITHMETIC_OPCODE 0x00f08000
#define MASK_DESTINATION_SIZE 0x00080080
......@@ -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 FLT_CODE 0x00000000
#define FIX_CODE 0x00100000
......@@ -366,25 +367,111 @@ TABLE 5
/* Get the rounding mode from the opcode. */
#define getRoundingMode(opcode) ((opcode & MASK_ROUNDING_MODE) >> 5)
#ifdef CONFIG_FPE_NWFPE_XP
static inline const floatx80 getExtendedConstant(const unsigned int nIndex)
{
extern const floatx80 floatx80Constant[];
return floatx80Constant[nIndex];
}
extern const floatx80 floatx80Constant[];
return floatx80Constant[nIndex];
}
#endif
static inline const float64 getDoubleConstant(const unsigned int nIndex)
{
extern const float64 float64Constant[];
return float64Constant[nIndex];
}
extern const float64 float64Constant[];
return float64Constant[nIndex];
}
static inline const float32 getSingleConstant(const unsigned int nIndex)
{
extern const float32 float32Constant[];
return float32Constant[nIndex];
}
extern const float32 float32Constant[];
return float32Constant[nIndex];
}
extern unsigned int getRegisterCount(const unsigned int opcode);
extern unsigned int getDestinationSize(const unsigned int opcode);
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);
}
#endif
......@@ -38,12 +38,12 @@ The FPCR is a 32 bit register consisting of bit flags.
------------
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 */
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_EMULATOR 0x01000000 /* System ID for emulator */
#define FP_ACCELERATOR 0x81000000 /* System ID for FPA11 */
/* EXCEPTION TRAP ENABLE BYTE
......@@ -51,11 +51,11 @@ typedef unsigned int FPCR; /* type for floating point control register */
#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 */
#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
---------------------- */
......
/*
NetWinder Floating Point Emulator
(c) Rebel.COM, 1998,1999
(c) Philip Blundell, 2001
Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
......@@ -32,224 +33,92 @@ 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);
float32 float32_pow(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();
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;
return float32_sub(rFm, rFn);
}
#if 0
float32 float32_exp(float32 Fm)
static float32 float32_rdv(float32 rFn, float32 rFm)
{
//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.
*/
#include "fpa11.h"
#include "milieu.h"
#include "softfloat.h"
//#include "milieu.h"
//#include "softfloat.h"
/*
-------------------------------------------------------------------------------
......@@ -142,12 +142,14 @@ INLINE int16 extractFloat32Exp( float32 a )
Returns the sign bit of the single-precision floating-point value `a'.
-------------------------------------------------------------------------------
*/
#if 0 /* in softfloat.h */
INLINE flag extractFloat32Sign( float32 a )
{
return a>>31;
}
#endif
/*
-------------------------------------------------------------------------------
......@@ -184,9 +186,9 @@ INLINE float32 packFloat32( flag zSign, int16 zExp, bits32 zSig )
{
#if 0
float32 f;
__asm__("@ packFloat32; \n\
mov %0, %1, asl #31; \n\
orr %0, %2, asl #23; \n\
__asm__("@ packFloat32 \n\
mov %0, %1, asl #31 \n\
orr %0, %2, asl #23 \n\
orr %0, %3"
: /* no outputs */
: "g" (f), "g" (zSign), "g" (zExp), "g" (zSig)
......@@ -321,12 +323,14 @@ INLINE int16 extractFloat64Exp( float64 a )
Returns the sign bit of the double-precision floating-point value `a'.
-------------------------------------------------------------------------------
*/
#if 0 /* in softfloat.h */
INLINE flag extractFloat64Sign( float64 a )
{
return a>>63;
}
#endif
/*
-------------------------------------------------------------------------------
......
......@@ -40,7 +40,9 @@ floating-point format `floatx80'. If this macro is not defined, the
input or output the `floatx80' type will be defined.
-------------------------------------------------------------------------------
*/
#ifdef CONFIG_FPE_NWFPE_XP
#define FLOATX80
#endif
/*
-------------------------------------------------------------------------------
......@@ -229,4 +231,46 @@ char floatx80_is_signaling_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));
}
#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