2 * Linux/PA-RISC Project (http://www.parisc-linux.org/)
4 * Floating-point emulation code
5 * Copyright (C) 2001 Hewlett-Packard (Paul Bame) <bame@debian.org>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2, or (at your option)
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 * @(#) pa/fp/decode_exc.c $ Revision: $
28 * <<please update with a synopsis of the functionality provided by this file>>
30 * External Interfaces:
31 * <<the following list was autogenerated, please review>>
32 * decode_fpu(Fpu_register, trap_counts)
34 * Internal Interfaces:
38 * <<please update with a overview of the operation of this file>>
43 #include <linux/kernel.h>
45 #include "sgl_float.h"
46 #include "dbl_float.h"
47 #include "cnv_float.h"
48 /* #include "types.h" */
49 #include <asm/signal.h>
50 #include <asm/siginfo.h>
51 /* #include <machine/sys/mdep_private.h> */
53 #undef Fpustatus_register
54 #define Fpustatus_register Fpu_register[0]
56 /* General definitions */
59 #define SIGNALCODE(signal, code) ((signal) << 24 | (code));
60 #define copropbit 1<<31-2 /* bit position 2 */
61 #define opclass 9 /* bits 21 & 22 */
62 #define fmt 11 /* bits 19 & 20 */
63 #define df 13 /* bits 17 & 18 */
64 #define twobits 3 /* mask low-order 2 bits */
65 #define fivebits 31 /* mask low-order 5 bits */
66 #define MAX_EXCP_REG 7 /* number of excpeption registers to check */
68 /* Exception register definitions */
69 #define Excp_type(index) Exceptiontype(Fpu_register[index])
70 #define Excp_instr(index) Instructionfield(Fpu_register[index])
71 #define Clear_excp_register(index) Allexception(Fpu_register[index]) = 0
72 #define Excp_format() \
73 (current_ir >> ((current_ir>>opclass & twobits)==1 ? df : fmt) & twobits)
75 /* Miscellaneous definitions */
76 #define Fpu_sgl(index) Fpu_register[index*2]
78 #define Fpu_dblp1(index) Fpu_register[index*2]
79 #define Fpu_dblp2(index) Fpu_register[(index*2)+1]
81 #define Fpu_quadp1(index) Fpu_register[index*2]
82 #define Fpu_quadp2(index) Fpu_register[(index*2)+1]
83 #define Fpu_quadp3(index) Fpu_register[(index*2)+2]
84 #define Fpu_quadp4(index) Fpu_register[(index*2)+3]
86 /* Single precision floating-point definitions */
88 # define Sgl_decrement(sgl_value) Sall(sgl_value)--
91 /* Double precision floating-point definitions */
93 # define Dbl_decrement(dbl_valuep1,dbl_valuep2) \
94 if ((Dallp2(dbl_valuep2)--) == 0) Dallp1(dbl_valuep1)--
98 #define update_trap_counts(Fpu_register, aflags, bflags, trap_counts) { \
99 aflags=(Fpu_register[0])>>27; /* assumes zero fill. 32 bit */ \
100 Fpu_register[0] |= bflags; \
104 decode_fpu(unsigned int Fpu_register
[], unsigned int trap_counts
[])
106 unsigned int current_ir
, excp
;
107 int target
, exception_index
= 1;
111 unsigned int excptype
;
114 /* Keep stats on how many floating point exceptions (based on type)
115 * that happen. Want to keep this overhead low, but still provide
116 * some information to the customer. All exits from this routine
117 * need to restore Fpu_register[0]
120 bflags
=(Fpu_register
[0] & 0xf8000000);
121 Fpu_register
[0] &= 0x07ffffff;
123 /* exception_index is used to index the exception register queue. It
124 * always points at the last register that contains a valid exception. A
125 * zero value implies no exceptions (also the initialized value). Setting
126 * the T-bit resets the exception_index to zero.
130 * Check for reserved-op exception. A reserved-op exception does not
131 * set any exception registers nor does it set the T-bit. If the T-bit
132 * is not set then a reserved-op exception occurred.
134 * At some point, we may want to report reserved op exceptions as
135 * illegal instructions.
138 if (!Is_tbit_set()) {
139 update_trap_counts(Fpu_register
, aflags
, bflags
, trap_counts
);
140 return SIGNALCODE(SIGILL
, ILL_COPROC
);
144 * Is a coprocessor op.
146 * Now we need to determine what type of exception occurred.
148 for (exception_index
=1; exception_index
<=MAX_EXCP_REG
; exception_index
++) {
149 current_ir
= Excp_instr(exception_index
);
151 * On PA89: there are 5 different unimplemented exception
152 * codes: 0x1, 0x9, 0xb, 0x3, and 0x23. PA-RISC 2.0 adds
153 * another, 0x2b. Only these have the low order bit set.
155 excptype
= Excp_type(exception_index
);
156 if (excptype
& UNIMPLEMENTEDEXCEPTION
) {
158 * Clear T-bit and exception register so that
159 * we can tell if a trap really occurs while
160 * emulating the instruction.
163 Clear_excp_register(exception_index
);
165 * Now emulate this instruction. If a trap occurs,
166 * fpudispatch will return a non-zero number
168 excp
= fpudispatch(current_ir
,excptype
,0,Fpu_register
);
169 /* accumulate the status flags, don't lose them as in hpux */
172 * We now need to make sure that the T-bit and the
173 * exception register contain the correct values
177 * Set t-bit since it might still be needed for a
178 * subsequent real trap (I don't understand fully -PB)
181 /* some of the following code uses
182 * Excp_type(exception_index) so fix that up */
183 Set_exceptiontype_and_instr_field(excp
,current_ir
,
184 Fpu_register
[exception_index
]);
185 if (excp
== UNIMPLEMENTEDEXCEPTION
) {
187 * it is really unimplemented, so restore the
188 * TIMEX extended unimplemented exception code
191 update_trap_counts(Fpu_register
, aflags
, bflags
,
193 return SIGNALCODE(SIGILL
, ILL_COPROC
);
195 /* some of the following code uses excptype, so
199 /* handle exceptions other than the real UNIMPLIMENTED the
200 * same way as if the hardware had caused them */
201 if (excp
== NOEXCEPTION
)
202 /* For now use 'break', should technically be 'continue' */
207 * In PA89, the underflow exception has been extended to encode
208 * additional information. The exception looks like pp01x0,
209 * where x is 1 if inexact and pp represent the inexact bit (I)
210 * and the round away bit (RA)
212 if (excptype
& UNDERFLOWEXCEPTION
) {
213 /* check for underflow trap enabled */
214 if (Is_underflowtrap_enabled()) {
215 update_trap_counts(Fpu_register
, aflags
, bflags
,
217 return SIGNALCODE(SIGFPE
, FPE_FLTUND
);
220 * Isn't a real trap; we need to
221 * return the default value.
223 target
= current_ir
& fivebits
;
225 if (Ibit(Fpu_register
[exception_index
])) inexact
= TRUE
;
226 else inexact
= FALSE
;
228 switch (Excp_format()) {
231 * If ra (round-away) is set, will
232 * want to undo the rounding done
235 if (Rabit(Fpu_register
[exception_index
]))
236 Sgl_decrement(Fpu_sgl(target
));
238 /* now denormalize */
239 sgl_denormalize(&Fpu_sgl(target
),&inexact
,Rounding_mode());
243 * If ra (round-away) is set, will
244 * want to undo the rounding done
247 if (Rabit(Fpu_register
[exception_index
]))
248 Dbl_decrement(Fpu_dblp1(target
),Fpu_dblp2(target
));
250 /* now denormalize */
251 dbl_denormalize(&Fpu_dblp1(target
),&Fpu_dblp2(target
),
252 &inexact
,Rounding_mode());
255 if (inexact
) Set_underflowflag();
257 * Underflow can generate an inexact
258 * exception. If inexact trap is enabled,
259 * want to do an inexact trap, otherwise
262 if (inexact
&& Is_inexacttrap_enabled()) {
264 * Set exception field of exception register
265 * to inexact, parm field to zero.
266 * Underflow bit should be cleared.
268 Set_exceptiontype(Fpu_register
[exception_index
],
270 Set_parmfield(Fpu_register
[exception_index
],0);
271 update_trap_counts(Fpu_register
, aflags
, bflags
,
273 return SIGNALCODE(SIGFPE
, FPE_FLTRES
);
277 * Exception register needs to be cleared.
278 * Inexact flag needs to be set if inexact.
280 Clear_excp_register(exception_index
);
281 if (inexact
) Set_inexactflag();
286 switch(Excp_type(exception_index
)) {
287 case OVERFLOWEXCEPTION
:
288 case OVERFLOWEXCEPTION
| INEXACTEXCEPTION
:
289 /* check for overflow trap enabled */
290 update_trap_counts(Fpu_register
, aflags
, bflags
,
292 if (Is_overflowtrap_enabled()) {
293 update_trap_counts(Fpu_register
, aflags
, bflags
,
295 return SIGNALCODE(SIGFPE
, FPE_FLTOVF
);
298 * Isn't a real trap; we need to
299 * return the default value.
301 target
= current_ir
& fivebits
;
302 switch (Excp_format()) {
304 Sgl_setoverflow(Fpu_sgl(target
));
307 Dbl_setoverflow(Fpu_dblp1(target
),Fpu_dblp2(target
));
312 * Overflow always generates an inexact
313 * exception. If inexact trap is enabled,
314 * want to do an inexact trap, otherwise
317 if (Is_inexacttrap_enabled()) {
319 * Set exception field of exception
320 * register to inexact. Overflow
321 * bit should be cleared.
323 Set_exceptiontype(Fpu_register
[exception_index
],
325 update_trap_counts(Fpu_register
, aflags
, bflags
,
327 return SIGNALCODE(SIGFPE
, FPE_FLTRES
);
331 * Exception register needs to be cleared.
332 * Inexact flag needs to be set.
334 Clear_excp_register(exception_index
);
339 case INVALIDEXCEPTION
:
340 case OPC_2E_INVALIDEXCEPTION
:
341 update_trap_counts(Fpu_register
, aflags
, bflags
, trap_counts
);
342 return SIGNALCODE(SIGFPE
, FPE_FLTINV
);
343 case DIVISIONBYZEROEXCEPTION
:
344 update_trap_counts(Fpu_register
, aflags
, bflags
, trap_counts
);
345 Clear_excp_register(exception_index
);
346 return SIGNALCODE(SIGFPE
, FPE_FLTDIV
);
347 case INEXACTEXCEPTION
:
348 update_trap_counts(Fpu_register
, aflags
, bflags
, trap_counts
);
349 return SIGNALCODE(SIGFPE
, FPE_FLTRES
);
351 update_trap_counts(Fpu_register
, aflags
, bflags
, trap_counts
);
352 printk("%s(%d) Unknown FPU exception 0x%x\n", __FILE__
,
353 __LINE__
, Excp_type(exception_index
));
354 return SIGNALCODE(SIGILL
, ILL_COPROC
);
355 case NOEXCEPTION
: /* no exception */
357 * Clear exception register in case
358 * other fields are non-zero.
360 Clear_excp_register(exception_index
);
365 * No real exceptions occurred.
368 update_trap_counts(Fpu_register
, aflags
, bflags
, trap_counts
);