mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
357 lines
11 KiB
357 lines
11 KiB
// SPDX-License-Identifier: GPL-2.0-or-later |
|
/* |
|
* Linux/PA-RISC Project (http://www.parisc-linux.org/) |
|
* |
|
* Floating-point emulation code |
|
* Copyright (C) 2001 Hewlett-Packard (Paul Bame) <[email protected]> |
|
*/ |
|
/* |
|
* BEGIN_DESC |
|
* |
|
* File: |
|
* @(#) pa/fp/decode_exc.c $ Revision: $ |
|
* |
|
* Purpose: |
|
* <<please update with a synopsis of the functionality provided by this file>> |
|
* |
|
* External Interfaces: |
|
* <<the following list was autogenerated, please review>> |
|
* decode_fpu(Fpu_register, trap_counts) |
|
* |
|
* Internal Interfaces: |
|
* <<please update>> |
|
* |
|
* Theory: |
|
* <<please update with a overview of the operation of this file>> |
|
* |
|
* END_DESC |
|
*/ |
|
|
|
#include <linux/kernel.h> |
|
#include "float.h" |
|
#include "sgl_float.h" |
|
#include "dbl_float.h" |
|
#include "cnv_float.h" |
|
/* #include "types.h" */ |
|
#include <asm/signal.h> |
|
#include <asm/siginfo.h> |
|
/* #include <machine/sys/mdep_private.h> */ |
|
|
|
#undef Fpustatus_register |
|
#define Fpustatus_register Fpu_register[0] |
|
|
|
/* General definitions */ |
|
#define DOESTRAP 1 |
|
#define NOTRAP 0 |
|
#define SIGNALCODE(signal, code) ((signal) << 24 | (code)) |
|
#define copropbit 1<<31-2 /* bit position 2 */ |
|
#define opclass 9 /* bits 21 & 22 */ |
|
#define fmt 11 /* bits 19 & 20 */ |
|
#define df 13 /* bits 17 & 18 */ |
|
#define twobits 3 /* mask low-order 2 bits */ |
|
#define fivebits 31 /* mask low-order 5 bits */ |
|
#define MAX_EXCP_REG 7 /* number of excpeption registers to check */ |
|
|
|
/* Exception register definitions */ |
|
#define Excp_type(index) Exceptiontype(Fpu_register[index]) |
|
#define Excp_instr(index) Instructionfield(Fpu_register[index]) |
|
#define Clear_excp_register(index) Allexception(Fpu_register[index]) = 0 |
|
#define Excp_format() \ |
|
(current_ir >> ((current_ir>>opclass & twobits)==1 ? df : fmt) & twobits) |
|
|
|
/* Miscellaneous definitions */ |
|
#define Fpu_sgl(index) Fpu_register[index*2] |
|
|
|
#define Fpu_dblp1(index) Fpu_register[index*2] |
|
#define Fpu_dblp2(index) Fpu_register[(index*2)+1] |
|
|
|
#define Fpu_quadp1(index) Fpu_register[index*2] |
|
#define Fpu_quadp2(index) Fpu_register[(index*2)+1] |
|
#define Fpu_quadp3(index) Fpu_register[(index*2)+2] |
|
#define Fpu_quadp4(index) Fpu_register[(index*2)+3] |
|
|
|
/* Single precision floating-point definitions */ |
|
#ifndef Sgl_decrement |
|
# define Sgl_decrement(sgl_value) Sall(sgl_value)-- |
|
#endif |
|
|
|
/* Double precision floating-point definitions */ |
|
#ifndef Dbl_decrement |
|
# define Dbl_decrement(dbl_valuep1,dbl_valuep2) \ |
|
if ((Dallp2(dbl_valuep2)--) == 0) Dallp1(dbl_valuep1)-- |
|
#endif |
|
|
|
|
|
#define update_trap_counts(Fpu_register, aflags, bflags, trap_counts) { \ |
|
aflags=(Fpu_register[0])>>27; /* assumes zero fill. 32 bit */ \ |
|
Fpu_register[0] |= bflags; \ |
|
} |
|
|
|
u_int |
|
decode_fpu(unsigned int Fpu_register[], unsigned int trap_counts[]) |
|
{ |
|
unsigned int current_ir, excp; |
|
int target, exception_index = 1; |
|
boolean inexact; |
|
unsigned int aflags; |
|
unsigned int bflags; |
|
unsigned int excptype; |
|
|
|
|
|
/* Keep stats on how many floating point exceptions (based on type) |
|
* that happen. Want to keep this overhead low, but still provide |
|
* some information to the customer. All exits from this routine |
|
* need to restore Fpu_register[0] |
|
*/ |
|
|
|
bflags=(Fpu_register[0] & 0xf8000000); |
|
Fpu_register[0] &= 0x07ffffff; |
|
|
|
/* exception_index is used to index the exception register queue. It |
|
* always points at the last register that contains a valid exception. A |
|
* zero value implies no exceptions (also the initialized value). Setting |
|
* the T-bit resets the exception_index to zero. |
|
*/ |
|
|
|
/* |
|
* Check for reserved-op exception. A reserved-op exception does not |
|
* set any exception registers nor does it set the T-bit. If the T-bit |
|
* is not set then a reserved-op exception occurred. |
|
* |
|
* At some point, we may want to report reserved op exceptions as |
|
* illegal instructions. |
|
*/ |
|
|
|
if (!Is_tbit_set()) { |
|
update_trap_counts(Fpu_register, aflags, bflags, trap_counts); |
|
return SIGNALCODE(SIGILL, ILL_COPROC); |
|
} |
|
|
|
/* |
|
* Is a coprocessor op. |
|
* |
|
* Now we need to determine what type of exception occurred. |
|
*/ |
|
for (exception_index=1; exception_index<=MAX_EXCP_REG; exception_index++) { |
|
current_ir = Excp_instr(exception_index); |
|
/* |
|
* On PA89: there are 5 different unimplemented exception |
|
* codes: 0x1, 0x9, 0xb, 0x3, and 0x23. PA-RISC 2.0 adds |
|
* another, 0x2b. Only these have the low order bit set. |
|
*/ |
|
excptype = Excp_type(exception_index); |
|
if (excptype & UNIMPLEMENTEDEXCEPTION) { |
|
/* |
|
* Clear T-bit and exception register so that |
|
* we can tell if a trap really occurs while |
|
* emulating the instruction. |
|
*/ |
|
Clear_tbit(); |
|
Clear_excp_register(exception_index); |
|
/* |
|
* Now emulate this instruction. If a trap occurs, |
|
* fpudispatch will return a non-zero number |
|
*/ |
|
excp = fpudispatch(current_ir,excptype,0,Fpu_register); |
|
/* accumulate the status flags, don't lose them as in hpux */ |
|
if (excp) { |
|
/* |
|
* We now need to make sure that the T-bit and the |
|
* exception register contain the correct values |
|
* before continuing. |
|
*/ |
|
/* |
|
* Set t-bit since it might still be needed for a |
|
* subsequent real trap (I don't understand fully -PB) |
|
*/ |
|
Set_tbit(); |
|
/* some of the following code uses |
|
* Excp_type(exception_index) so fix that up */ |
|
Set_exceptiontype_and_instr_field(excp,current_ir, |
|
Fpu_register[exception_index]); |
|
if (excp == UNIMPLEMENTEDEXCEPTION) { |
|
/* |
|
* it is really unimplemented, so restore the |
|
* TIMEX extended unimplemented exception code |
|
*/ |
|
excp = excptype; |
|
update_trap_counts(Fpu_register, aflags, bflags, |
|
trap_counts); |
|
return SIGNALCODE(SIGILL, ILL_COPROC); |
|
} |
|
/* some of the following code uses excptype, so |
|
* fix that up too */ |
|
excptype = excp; |
|
} |
|
/* handle exceptions other than the real UNIMPLIMENTED the |
|
* same way as if the hardware had caused them */ |
|
if (excp == NOEXCEPTION) |
|
/* For now use 'break', should technically be 'continue' */ |
|
break; |
|
} |
|
|
|
/* |
|
* In PA89, the underflow exception has been extended to encode |
|
* additional information. The exception looks like pp01x0, |
|
* where x is 1 if inexact and pp represent the inexact bit (I) |
|
* and the round away bit (RA) |
|
*/ |
|
if (excptype & UNDERFLOWEXCEPTION) { |
|
/* check for underflow trap enabled */ |
|
if (Is_underflowtrap_enabled()) { |
|
update_trap_counts(Fpu_register, aflags, bflags, |
|
trap_counts); |
|
return SIGNALCODE(SIGFPE, FPE_FLTUND); |
|
} else { |
|
/* |
|
* Isn't a real trap; we need to |
|
* return the default value. |
|
*/ |
|
target = current_ir & fivebits; |
|
#ifndef lint |
|
if (Ibit(Fpu_register[exception_index])) inexact = TRUE; |
|
else inexact = FALSE; |
|
#endif |
|
switch (Excp_format()) { |
|
case SGL: |
|
/* |
|
* If ra (round-away) is set, will |
|
* want to undo the rounding done |
|
* by the hardware. |
|
*/ |
|
if (Rabit(Fpu_register[exception_index])) |
|
Sgl_decrement(Fpu_sgl(target)); |
|
|
|
/* now denormalize */ |
|
sgl_denormalize(&Fpu_sgl(target),&inexact,Rounding_mode()); |
|
break; |
|
case DBL: |
|
/* |
|
* If ra (round-away) is set, will |
|
* want to undo the rounding done |
|
* by the hardware. |
|
*/ |
|
if (Rabit(Fpu_register[exception_index])) |
|
Dbl_decrement(Fpu_dblp1(target),Fpu_dblp2(target)); |
|
|
|
/* now denormalize */ |
|
dbl_denormalize(&Fpu_dblp1(target),&Fpu_dblp2(target), |
|
&inexact,Rounding_mode()); |
|
break; |
|
} |
|
if (inexact) Set_underflowflag(); |
|
/* |
|
* Underflow can generate an inexact |
|
* exception. If inexact trap is enabled, |
|
* want to do an inexact trap, otherwise |
|
* set inexact flag. |
|
*/ |
|
if (inexact && Is_inexacttrap_enabled()) { |
|
/* |
|
* Set exception field of exception register |
|
* to inexact, parm field to zero. |
|
* Underflow bit should be cleared. |
|
*/ |
|
Set_exceptiontype(Fpu_register[exception_index], |
|
INEXACTEXCEPTION); |
|
Set_parmfield(Fpu_register[exception_index],0); |
|
update_trap_counts(Fpu_register, aflags, bflags, |
|
trap_counts); |
|
return SIGNALCODE(SIGFPE, FPE_FLTRES); |
|
} |
|
else { |
|
/* |
|
* Exception register needs to be cleared. |
|
* Inexact flag needs to be set if inexact. |
|
*/ |
|
Clear_excp_register(exception_index); |
|
if (inexact) Set_inexactflag(); |
|
} |
|
} |
|
continue; |
|
} |
|
switch(Excp_type(exception_index)) { |
|
case OVERFLOWEXCEPTION: |
|
case OVERFLOWEXCEPTION | INEXACTEXCEPTION: |
|
/* check for overflow trap enabled */ |
|
update_trap_counts(Fpu_register, aflags, bflags, |
|
trap_counts); |
|
if (Is_overflowtrap_enabled()) { |
|
update_trap_counts(Fpu_register, aflags, bflags, |
|
trap_counts); |
|
return SIGNALCODE(SIGFPE, FPE_FLTOVF); |
|
} else { |
|
/* |
|
* Isn't a real trap; we need to |
|
* return the default value. |
|
*/ |
|
target = current_ir & fivebits; |
|
switch (Excp_format()) { |
|
case SGL: |
|
Sgl_setoverflow(Fpu_sgl(target)); |
|
break; |
|
case DBL: |
|
Dbl_setoverflow(Fpu_dblp1(target),Fpu_dblp2(target)); |
|
break; |
|
} |
|
Set_overflowflag(); |
|
/* |
|
* Overflow always generates an inexact |
|
* exception. If inexact trap is enabled, |
|
* want to do an inexact trap, otherwise |
|
* set inexact flag. |
|
*/ |
|
if (Is_inexacttrap_enabled()) { |
|
/* |
|
* Set exception field of exception |
|
* register to inexact. Overflow |
|
* bit should be cleared. |
|
*/ |
|
Set_exceptiontype(Fpu_register[exception_index], |
|
INEXACTEXCEPTION); |
|
update_trap_counts(Fpu_register, aflags, bflags, |
|
trap_counts); |
|
return SIGNALCODE(SIGFPE, FPE_FLTRES); |
|
} |
|
else { |
|
/* |
|
* Exception register needs to be cleared. |
|
* Inexact flag needs to be set. |
|
*/ |
|
Clear_excp_register(exception_index); |
|
Set_inexactflag(); |
|
} |
|
} |
|
break; |
|
case INVALIDEXCEPTION: |
|
case OPC_2E_INVALIDEXCEPTION: |
|
update_trap_counts(Fpu_register, aflags, bflags, trap_counts); |
|
return SIGNALCODE(SIGFPE, FPE_FLTINV); |
|
case DIVISIONBYZEROEXCEPTION: |
|
update_trap_counts(Fpu_register, aflags, bflags, trap_counts); |
|
Clear_excp_register(exception_index); |
|
return SIGNALCODE(SIGFPE, FPE_FLTDIV); |
|
case INEXACTEXCEPTION: |
|
update_trap_counts(Fpu_register, aflags, bflags, trap_counts); |
|
return SIGNALCODE(SIGFPE, FPE_FLTRES); |
|
default: |
|
update_trap_counts(Fpu_register, aflags, bflags, trap_counts); |
|
printk("%s(%d) Unknown FPU exception 0x%x\n", __FILE__, |
|
__LINE__, Excp_type(exception_index)); |
|
return SIGNALCODE(SIGILL, ILL_COPROC); |
|
case NOEXCEPTION: /* no exception */ |
|
/* |
|
* Clear exception register in case |
|
* other fields are non-zero. |
|
*/ |
|
Clear_excp_register(exception_index); |
|
break; |
|
} |
|
} |
|
/* |
|
* No real exceptions occurred. |
|
*/ |
|
Clear_tbit(); |
|
update_trap_counts(Fpu_register, aflags, bflags, trap_counts); |
|
return(NOTRAP); |
|
}
|
|
|