NetBSD/sys/arch/pc532/fpu/ieee_invop.c

332 lines
7.8 KiB
C

/* $NetBSD: ieee_invop.c,v 1.2 1996/05/03 23:19:28 phil Exp $ */
/*
* IEEE floating point support for NS32081 and NS32381 fpus.
* Copyright (c) 1995 Ian Dall
* All Rights Reserved.
*
* Permission to use, copy, modify and distribute this software and its
* documentation is hereby granted, provided that both the copyright
* notice and this permission notice appear in all copies of the
* software, derivative works or modified versions, and any portions
* thereof, and that both notices appear in supporting documentation.
*
* IAN DALL ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" CONDITION.
* IAN DALL DISCLAIMS ANY LIABILITY OF ANY KIND FOR ANY DAMAGES
* WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
*/
/*
* File: ieee_invop.c
* Author: Ian Dall
* Date: November 1995
*
* Handle operations which generated reserved operand traps.
*
* HISTORY
* 23-Apr-96 Ian Dall (Ian.Dall@dsto.defence.gov.au)
* Don't change sign of NaN operands of NEGF and SUBF.
*
* 23-Apr-96 Ian Dall (Ian.Dall@dsto.defence.gov.au)
* Operations on signaling NaN's always produce a quiet NaN.
*
* 08-Apr-96 Ian Dall (Ian.Dall@dsto.defence.gov.au)
* Generate correct return value so flags get set properly.
*
* 05-Apr-96 Ian Dall (Ian.Dall@dsto.defence.gov.au)
* Get subnormal number divided by subnormal number right.
* Get infinty multiplied by zero right.
*
* 02-Apr-96 Ian Dall (Ian.Dall@dsto.defence.gov.au)
* Make 0/0 produce NaN.
*
* 14-Dec-95 Ian Dall (Ian.Dall@dsto.defence.gov.au)
* First release.
*
*/
#include "ieee_internal.h"
#include <machine/psl.h>
#include <limits.h>
static int nan_2(union t_conv data1, union t_conv *data2)
{
int nans = (ISNAN(data1)? 1: 0) + (ISNAN(*data2)? 2: 0);
switch (nans) {
case 0:
return 0;
case 1:
*data2 = data1;
break;
case 2:
break;
case 3:
if((data1.d_bits.mantissa > data2->d_bits.mantissa)
|| (data1.d_bits.mantissa == data1.d_bits.mantissa
&& data1.d_bits.mantissa2 > data2->d_bits.mantissa2)) {
*data2 = data1;
}
break;
}
data2->d_bits.mantissa |= 0x80000; /* Make sure it is a quiet NaN */
return 1;
}
static int add_iv (struct operand *op1, struct operand *op2)
{
int inftys;
int ret = FPC_TT_NONE;
if(nan_2(op1->data, &op2->data)) {
return ret;
}
inftys = (ISINFTY(op1->data)? 1: 0) + (ISINFTY(op2->data)? 2:0);
switch (inftys) {
case 0:
break;
case 1:
op2->data = op1->data;
return ret;
case 2:
return ret;
case 3:
if(op1->data.d_bits.sign != op2->data.d_bits.sign) {
op2->data = qnan;
return FPC_TT_INVOP;
}
return ret;
}
/* Must be subnormals */
return ieee_add(op1->data.d, &op2->data.d);
}
static int div_iv (struct operand *op1, struct operand *op2)
{
int inftys, sign1, sign2;
int ret = FPC_TT_NONE;
if(nan_2(op1->data, &op2->data)) {
return ret;
}
inftys = (ISINFTY(op1->data)? 1: 0) + (ISINFTY(op2->data)? 2:0);
sign1 = op1->data.d_bits.sign;
sign2 = op2->data.d_bits.sign;
switch (inftys) {
case 0:
break;
case 1: /* n/oo */
{
op2->data.d = 0.0;
op2->data.d_bits.sign = sign1 ^ sign2;
return ret;
}
case 2: /* oo/n */
op2->data.d_bits.sign = sign1 ^ sign2;
return ret;
case 3:
op2->data = qnan;
return FPC_TT_INVOP;
}
if(ISZERO(op1->data)) {
if (ISZERO(op2->data)) {
/* Must be 0/0 */
op2->data = qnan;
return FPC_TT_INVOP;
}
else {
/* Must be subnorm/0 */
op2->data = infty;
op2->data.d_bits.sign = sign1 ^ sign2;
return FPC_TT_DIV0;
}
}
/* Must be subnorm/subnorm */
return ieee_div(op1->data.d, &op2->data.d);
}
static int mul_iv (struct operand *op1, struct operand *op2)
{
int inftys, sign1, sign2;
int ret = FPC_TT_NONE;
if(nan_2(op1->data, &op2->data)) {
return ret;
}
inftys = (ISINFTY(op1->data)? 1: 0) + (ISINFTY(op2->data)? 2:0);
sign1 = op1->data.d_bits.sign;
sign2 = op2->data.d_bits.sign;
switch (inftys) {
case 0:
break;
case 1: /* oo * n */
if (ISZERO(op2->data)) {
op2->data = qnan;
return FPC_TT_INVOP;
}
else {
op2->data = op1->data;
op2->data.d_bits.sign = sign1 ^ sign2;
return ret;
}
case 2: /* n * oo */
if (ISZERO(op1->data)) {
op2->data = qnan;
return FPC_TT_INVOP;
}
/* Fall through */
case 3: /* oo * oo */
op2->data.d_bits.sign = sign1 ^ sign2;
return ret;
}
/* Must be subnormals */
return ieee_mul(op1->data.d, &op2->data.d);
}
static int dot_iv (struct operand *op1, struct operand *op2, struct operand *op3)
{
int inftys;
int ret = FPC_TT_NONE;
union t_conv t = op2->data;
if(nan_2(op1->data, &t)) {
if(nan_2(t, &op3->data))
return ret;
}
inftys = ((ISINFTY(op1->data)? 1: 0) + (ISINFTY(op2->data)? 2:0)
+ (ISINFTY(op3->data)? 4: 0));
switch (inftys) {
case 0:
break;
case 1: case 2: case 3: /* oo * n + m or n * oo + m or oo * oo + m */
op3->data = op2->data;
return mul_iv(op1, op3);
case 4: /* n * m + oo */
return ret;
case 5: case 6: case 7:
{
struct operand t = *op2;
mul_iv(op1, &t);
add_iv(&t, op3);
return ret;
}
}
/* Must be subnormals */
return ieee_dot(op1->data.d, op2->data.d, &op3->data.d);
}
static int poly_iv (struct operand *op1, struct operand *op2, struct operand *op3)
{
int ret;
struct operand t = *op2;
ret = dot_iv(op3, op1, &t);
*op3 = t;
return ret;
}
static int round_iv (struct operand *op1, struct operand *op2, int xopcode)
{
int ret = FPC_TT_NONE;
if(ISNAN(op1->data)) {
/* XXX */
op2->data.i = 0;
return FPC_TT_INVOP; /* Need a special code */
}
else if (ISINFTY(op1->data)) {
op2->data.i = op1->data.d_bits.sign? INT_MIN: INT_MAX;
} else {
/* Must be a denorm */
op2->data.i = 0;
}
return ret;
}
static int scalb_iv (struct operand *op1, struct operand *op2)
{
int ret = FPC_TT_NONE;
if(nan_2(op1->data, &op2->data)) {
return ret;
}
return ieee_scalb(op1->data.d, &op2->data.d);
}
static int logb_iv (struct operand *op1, struct operand *op2)
{
int ret = FPC_TT_NONE;
if (ISNAN(op1->data))
op2->data = op1->data;
else if(ISINFTY(op1->data))
op2->data = infty;
else {
/* Is a denormal */
union t_conv t = op1->data;
int norm = ieee_normalize(&t);
op2->data.d = t.d_bits.exp + norm;
}
return ret;
}
static void cmp_iv(struct operand *op1, struct operand *op2, state *state)
{
state->PSR &= ~(PSR_N | PSR_Z | PSR_L);
if (ISNAN(op1->data) || ISNAN(op2->data)) {
return;
}
ieee_cmp(op1->data.d, op2->data.d, state);
return;
}
int ieee_invop(struct operand *op1, struct operand *op2,
struct operand *f0_op, int xopcode, state *state)
{
int user_trap = FPC_TT_NONE;
unsigned int fsr = state->FSR;
/* Don't fiddle with fsr. The FPC_TT_INVOP bit should only be set
* if we attempt to *generate* a NaN. This is achieved by returning
* FPC_TT_INVOP when we generate a NaN.
*/
/*
* Figure out right thing to do.
*/
switch(xopcode) {
case NEGF:
if(! ISNAN(op1->data))
op1->data.d_bits.sign ^= 1;
/* Fall through */
case MOVF:
case MOVLF:
case MOVFL:
op2->data = op1->data;
break;
case CMPF:
cmp_iv(op1, op2, state);
break;
case SUBF:
if(! ISNAN(op1->data))
op1->data.d_bits.sign ^= 1;
/* Fall through */
case ADDF:
user_trap = add_iv(op1, op2);
break;
case MULF:
user_trap = mul_iv(op1, op2);
break;
case DIVF:
user_trap = div_iv(op1, op2);
break;
case ROUNDFI:
case TRUNCFI:
case FLOORFI:
user_trap = round_iv(op1, op2, xopcode);
break;
case SCALBF:
user_trap = scalb_iv(op1, op2);
break;
case LOGBF:
user_trap = logb_iv(op1, op2);
break;
case DOTF:
user_trap = dot_iv(op1, op2, f0_op);
break;
case POLYF:
user_trap = poly_iv(op1, op2, f0_op);
break;
}
return user_trap;
}