patch-1.3.65 linux/arch/alpha/kernel/traps.c
Next file: linux/arch/i386/boot/compressed/Makefile
Previous file: linux/arch/alpha/kernel/signal.c
Back to the patch index
Back to the overall index
- Lines: 108
- Date:
Sat Feb 17 09:19:38 1996
- Orig file:
v1.3.64/linux/arch/alpha/kernel/traps.c
- Orig date:
Tue Dec 26 04:45:34 1995
diff -u --recursive --new-file v1.3.64/linux/arch/alpha/kernel/traps.c linux/arch/alpha/kernel/traps.c
@@ -11,7 +11,6 @@
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/tty.h>
-#include <linux/config.h>
#include <asm/gentrap.h>
#include <asm/segment.h>
@@ -220,6 +219,44 @@
}
/*
+ * Convert an s-floating point value in memory format to the
+ * corresponding value in register format. The exponent
+ * needs to be remapped to preserve non-finite values
+ * (infinities, not-a-numbers, denormals).
+ */
+static inline unsigned long s_mem_to_reg (unsigned long s_mem)
+{
+ unsigned long frac = (s_mem >> 0) & 0x7fffff;
+ unsigned long sign = (s_mem >> 31) & 0x1;
+ unsigned long exp_msb = (s_mem >> 30) & 0x1;
+ unsigned long exp_low = (s_mem >> 23) & 0x7f;
+ unsigned long exp;
+
+ exp = (exp_msb << 10) | exp_low; /* common case */
+ if (exp_msb) {
+ if (exp_low == 0x7f) {
+ exp = 0x3ff;
+ }
+ } else {
+ if (exp_low == 0x00) {
+ exp = 0x000;
+ } else {
+ exp |= (0x7 << 8);
+ }
+ }
+ return (sign << 63) | (exp << 52) | (frac << 29);
+}
+
+/*
+ * Convert an s-floating point value in register format to the
+ * corresponding value in memory format.
+ */
+static inline unsigned long s_reg_to_mem (unsigned long s_reg)
+{
+ return ((s_reg >> 62) << 30) | ((s_reg << 5) >> 34);
+}
+
+/*
* Handle user-level unaligned fault. Handling user-level unaligned
* faults is *extremely* slow and produces nasty messages. A user
* program *should* fix unaligned faults ASAP.
@@ -236,8 +273,7 @@
*
* Sigh. We *do* have to handle some FP operations, because GCC will
* uses them as temporary storage for integer memory to memory copies.
- * However, we need to deal with stt/ldt only as they are the only
- * fp load/stores that preserve the bit pattern.
+ * However, we need to deal with stt/ldt and sts/lds only.
*/
asmlinkage void do_entUnaUser(void * va, unsigned long opcode, unsigned long reg,
unsigned long * frame)
@@ -246,6 +282,8 @@
unsigned long *reg_addr, *pc_addr, usp, zero = 0;
static int cnt = 0;
static long last_time = 0;
+ extern void alpha_write_fp_reg (unsigned long reg, unsigned long val);
+ extern unsigned long alpha_read_fp_reg (unsigned long reg);
pc_addr = frame + 7 + 20 + 1; /* pc in PAL frame */
@@ -265,7 +303,7 @@
dir = VERIFY_READ;
if (opcode & 0x4) {
- /* it's a stl, stq, or stt */
+ /* it's a stl, stq, stt, or sts */
dir = VERIFY_WRITE;
}
size = 4;
@@ -307,13 +345,20 @@
}
switch (opcode) {
- case 0x23: alpha_write_fp_reg(reg, ldq_u(va)); break; /* ldt */
- case 0x27: stq_u(alpha_read_fp_reg(reg), va); break; /* stt */
+ case 0x22: /* lds */
+ alpha_write_fp_reg(reg, s_mem_to_reg(ldl_u(va)));
+ break;
+ case 0x26: /* lds */
+ alpha_write_fp_reg(reg, s_reg_to_mem(ldl_u(va)));
+ break;
+
+ case 0x23: alpha_write_fp_reg(reg, ldq_u(va)); break; /* ldt */
+ case 0x27: stq_u(alpha_read_fp_reg(reg), va); break; /* stt */
- case 0x28: *reg_addr = (int) ldl_u(va); break; /* ldl */
- case 0x29: *reg_addr = ldq_u(va); break; /* ldq */
- case 0x2c: stl_u(*reg_addr, va); break; /* stl */
- case 0x2d: stq_u(*reg_addr, va); break; /* stq */
+ case 0x28: *reg_addr = (int) ldl_u(va); break; /* ldl */
+ case 0x29: *reg_addr = ldq_u(va); break; /* ldq */
+ case 0x2c: stl_u(*reg_addr, va); break; /* stl */
+ case 0x2d: stq_u(*reg_addr, va); break; /* stq */
default:
*pc_addr -= 4; /* make pc point to faulting insn */
send_sig(SIGBUS, current, 1);
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov
with Sam's (original) version of this